summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-12-21 22:06:22 +0100
committerMichael Biebl <biebl@debian.org>2018-12-21 22:06:22 +0100
commit6e866b331d7cd4a5e0759dd160dea6edabd3678e (patch)
tree4d24c1ffe4ae946f04d8910956090e8d13aecd9a
parentb012e92123bdc9fa10c2f079ec5bd9313b23e21a (diff)
downloadsystemd-6e866b331d7cd4a5e0759dd160dea6edabd3678e.tar.gz
New upstream version 240
-rw-r--r--.dir-locals.el14
-rw-r--r--.github/ISSUE_TEMPLATE/Bug_report.md2
-rw-r--r--.gitignore3
-rw-r--r--.lgtm.yml13
-rw-r--r--.lgtm/cpp-queries/fgets.ql21
-rw-r--r--.mailmap303
-rw-r--r--.mkosi/mkosi.debian4
-rw-r--r--.mkosi/mkosi.fedora3
-rw-r--r--.mkosi/mkosi.ubuntu4
-rw-r--r--.travis.yml196
-rw-r--r--.vimrc6
-rw-r--r--NEWS621
-rw-r--r--README20
-rw-r--r--README.md16
-rw-r--r--TODO211
-rw-r--r--catalog/meson.build1
-rw-r--r--catalog/systemd.be.catalog.in8
-rw-r--r--catalog/systemd.be@latin.catalog.in8
-rw-r--r--catalog/systemd.bg.catalog.in6
-rw-r--r--catalog/systemd.catalog.in74
-rw-r--r--catalog/systemd.da.catalog.in6
-rw-r--r--catalog/systemd.fr.catalog.in6
-rw-r--r--catalog/systemd.hr.catalog.in6
-rw-r--r--catalog/systemd.hu.catalog.in6
-rw-r--r--catalog/systemd.it.catalog.in6
-rw-r--r--catalog/systemd.ko.catalog.in6
-rw-r--r--catalog/systemd.pl.catalog.in78
-rw-r--r--catalog/systemd.pt_BR.catalog.in6
-rw-r--r--catalog/systemd.ru.catalog.in6
-rw-r--r--catalog/systemd.sr.catalog.in6
-rw-r--r--catalog/systemd.zh_CN.catalog.in6
-rw-r--r--catalog/systemd.zh_TW.catalog.in6
-rw-r--r--coccinelle/cmp.cocci28
-rw-r--r--coccinelle/iovec-make.cocci29
-rw-r--r--coccinelle/redundant-if.cocci54
-rw-r--r--coccinelle/swap-two.cocci7
-rw-r--r--coccinelle/synthetic-errno.cocci42
-rw-r--r--doc/CODING_STYLE460
-rw-r--r--doc/DISTRO_PORTING71
-rw-r--r--doc/HACKING122
-rw-r--r--doc/TRANSLATORS27
-rw-r--r--docs/AUTOMATIC_BOOT_ASSESSMENT.md203
-rw-r--r--docs/BLOCK_DEVICE_LOCKING.md63
-rw-r--r--docs/BOOT_LOADER_INTERFACE.md114
-rw-r--r--docs/BOOT_LOADER_SPECIFICATION.md (renamed from doc/BOOT_LOADER_SPECIFICATION.md)127
-rw-r--r--docs/CGROUP_DELEGATION.md (renamed from doc/CGROUP_DELEGATION.md)27
-rw-r--r--docs/CNAME1
-rw-r--r--docs/CODE_OF_CONDUCT.md14
-rw-r--r--docs/CODE_QUALITY.md (renamed from doc/CODE_QUALITY.md)8
-rw-r--r--docs/CODING_STYLE.md520
-rw-r--r--docs/CONTRIBUTING.md (renamed from .github/CONTRIBUTING.md)4
-rw-r--r--docs/DISTRO_PORTING.md75
-rw-r--r--docs/ENVIRONMENT.md (renamed from doc/ENVIRONMENT.md)77
-rw-r--r--docs/HACKING.md123
-rw-r--r--docs/PORTABLE_SERVICES.md (renamed from doc/PORTABLE_SERVICES.md)31
-rw-r--r--docs/PREDICTABLE_INTERFACE_NAMES.md64
-rw-r--r--docs/RELEASE.md (renamed from .github/RELEASE.md)2
-rw-r--r--docs/TRANSIENT-SETTINGS.md (renamed from doc/TRANSIENT-SETTINGS.md)9
-rw-r--r--docs/TRANSLATORS.md74
-rw-r--r--docs/UIDS-GIDS.md (renamed from doc/UIDS-GIDS.md)2
-rw-r--r--docs/_config.yml1
-rw-r--r--docs/index.md20
-rw-r--r--docs/sysvinit/README.in (renamed from doc/sysvinit/README.in)0
-rw-r--r--docs/sysvinit/meson.build (renamed from doc/sysvinit/meson.build)0
-rw-r--r--docs/var-log/README.in (renamed from doc/var-log/README.in)0
-rw-r--r--docs/var-log/meson.build (renamed from doc/var-log/meson.build)0
-rw-r--r--factory/etc/nsswitch.conf17
-rw-r--r--hwdb/20-OUI.hwdb4281
-rw-r--r--hwdb/20-acpi-vendor.hwdb48
-rw-r--r--hwdb/20-acpi-vendor.hwdb.patch107
-rw-r--r--hwdb/20-pci-vendor-model.hwdb2379
-rw-r--r--hwdb/20-usb-vendor-model.hwdb301
-rw-r--r--hwdb/60-evdev.hwdb50
-rw-r--r--hwdb/60-input-id.hwdb4
-rw-r--r--hwdb/60-keyboard.hwdb60
-rw-r--r--hwdb/60-sensor.hwdb123
-rw-r--r--hwdb/70-mouse.hwdb266
-rw-r--r--hwdb/70-pointingstick.hwdb12
-rw-r--r--hwdb/acpi_id_registry.html6
-rwxr-xr-xhwdb/ids_parser.py1
-rw-r--r--hwdb/ma-large.txt9006
-rw-r--r--hwdb/ma-medium.txt2025
-rw-r--r--hwdb/ma-small.txt2012
-rw-r--r--hwdb/meson.build8
-rwxr-xr-xhwdb/parse_hwdb.py6
-rw-r--r--hwdb/pci.ids1060
-rw-r--r--hwdb/pnp_id_registry.html12
-rw-r--r--hwdb/usb.ids123
-rw-r--r--man/.dir-locals.el12
-rw-r--r--man/bootctl.xml75
-rw-r--r--man/busctl.xml29
-rw-r--r--man/coredump.conf.xml2
-rw-r--r--man/coredumpctl.xml3
-rw-r--r--man/crypttab.xml9
-rw-r--r--man/daemon.xml2
-rw-r--r--man/dnssec-trust-anchors.d.xml2
-rw-r--r--man/hwdb.xml2
-rw-r--r--man/id128-app-specific.c11
-rw-r--r--man/journal-iterate-unique.c25
-rw-r--r--man/journalctl.xml122
-rw-r--r--man/journald.conf.xml14
-rw-r--r--man/kernel-command-line.xml23
-rw-r--r--man/kernel-install.xml100
-rw-r--r--man/less-variables.xml8
-rw-r--r--man/libudev.xml8
-rw-r--r--man/loader.conf.xml8
-rw-r--r--man/locale.conf.xml2
-rw-r--r--man/logind.conf.xml13
-rw-r--r--man/machinectl.xml58
-rw-r--r--man/meson.build27
-rw-r--r--man/networkctl.xml84
-rw-r--r--man/networkd.conf.xml35
-rw-r--r--man/nss-myhostname.xml3
-rw-r--r--man/nss-mymachines.xml87
-rw-r--r--man/nss-resolve.xml5
-rw-r--r--man/nss-systemd.xml1
-rw-r--r--man/os-release.xml30
-rw-r--r--man/pam_systemd.xml109
-rw-r--r--man/portablectl.xml20
-rw-r--r--man/print-unit-path.c64
-rw-r--r--man/resolvectl.xml44
-rw-r--r--man/resolved.conf.xml25
-rw-r--r--man/rules/meson.build108
-rw-r--r--man/sd-bus-errors.xml15
-rw-r--r--man/sd-bus.xml81
-rw-r--r--man/sd-event.xml2
-rw-r--r--man/sd-id128.xml4
-rw-r--r--man/sd-journal.xml17
-rw-r--r--man/sd_bus_add_match.xml8
-rw-r--r--man/sd_bus_attach_event.xml122
-rw-r--r--man/sd_bus_close.xml101
-rw-r--r--man/sd_bus_creds_get_pid.xml2
-rw-r--r--man/sd_bus_creds_new_from_pid.xml2
-rw-r--r--man/sd_bus_default.xml40
-rw-r--r--man/sd_bus_error.xml26
-rw-r--r--man/sd_bus_error_add_map.xml15
-rw-r--r--man/sd_bus_get_fd.xml126
-rw-r--r--man/sd_bus_get_n_queued_read.xml2
-rw-r--r--man/sd_bus_is_open.xml13
-rw-r--r--man/sd_bus_message_append.xml15
-rw-r--r--man/sd_bus_message_append_array.xml2
-rw-r--r--man/sd_bus_message_append_basic.xml3
-rw-r--r--man/sd_bus_message_append_string_memfd.xml2
-rw-r--r--man/sd_bus_message_append_strv.xml2
-rw-r--r--man/sd_bus_message_copy.xml115
-rw-r--r--man/sd_bus_message_get_signature.xml111
-rw-r--r--man/sd_bus_message_get_type.xml129
-rw-r--r--man/sd_bus_message_new.xml189
-rw-r--r--man/sd_bus_message_new_method_call.xml166
-rw-r--r--man/sd_bus_message_new_method_error.xml190
-rw-r--r--man/sd_bus_message_new_signal.xml120
-rw-r--r--man/sd_bus_message_read.xml232
-rw-r--r--man/sd_bus_message_read_array.xml108
-rw-r--r--man/sd_bus_message_read_basic.xml165
-rw-r--r--man/sd_bus_message_rewind.xml88
-rw-r--r--man/sd_bus_message_set_destination.xml70
-rw-r--r--man/sd_bus_message_set_expect_reply.xml127
-rw-r--r--man/sd_bus_message_skip.xml108
-rw-r--r--man/sd_bus_message_verify_type.xml99
-rw-r--r--man/sd_bus_negotiate_fds.xml2
-rw-r--r--man/sd_bus_new.xml57
-rw-r--r--man/sd_bus_path_encode.xml16
-rw-r--r--man/sd_bus_process.xml105
-rw-r--r--man/sd_bus_reply_method_error.xml161
-rw-r--r--man/sd_bus_request_name.xml11
-rw-r--r--man/sd_bus_set_close_on_exit.xml105
-rw-r--r--man/sd_bus_set_connected_signal.xml13
-rw-r--r--man/sd_bus_set_description.xml188
-rw-r--r--man/sd_bus_set_sender.xml13
-rw-r--r--man/sd_bus_set_watch_bind.xml13
-rw-r--r--man/sd_bus_slot_ref.xml107
-rw-r--r--man/sd_bus_slot_set_description.xml109
-rw-r--r--man/sd_bus_slot_set_destroy_callback.xml6
-rw-r--r--man/sd_bus_slot_set_floating.xml2
-rw-r--r--man/sd_bus_slot_set_userdata.xml88
-rw-r--r--man/sd_bus_track_add_name.xml2
-rw-r--r--man/sd_bus_track_new.xml2
-rw-r--r--man/sd_bus_wait.xml113
-rw-r--r--man/sd_event_add_child.xml2
-rw-r--r--man/sd_event_add_defer.xml2
-rw-r--r--man/sd_event_add_inotify.xml2
-rw-r--r--man/sd_event_add_io.xml4
-rw-r--r--man/sd_event_add_signal.xml2
-rw-r--r--man/sd_event_add_time.xml2
-rw-r--r--man/sd_event_exit.xml2
-rw-r--r--man/sd_event_get_fd.xml4
-rw-r--r--man/sd_event_new.xml13
-rw-r--r--man/sd_event_now.xml2
-rw-r--r--man/sd_event_run.xml9
-rw-r--r--man/sd_event_set_watchdog.xml5
-rw-r--r--man/sd_event_source_get_event.xml5
-rw-r--r--man/sd_event_source_get_pending.xml5
-rw-r--r--man/sd_event_source_set_description.xml5
-rw-r--r--man/sd_event_source_set_destroy_callback.xml2
-rw-r--r--man/sd_event_source_set_enabled.xml19
-rw-r--r--man/sd_event_source_set_prepare.xml5
-rw-r--r--man/sd_event_source_set_priority.xml5
-rw-r--r--man/sd_event_source_set_userdata.xml5
-rw-r--r--man/sd_event_source_unref.xml5
-rw-r--r--man/sd_event_wait.xml6
-rw-r--r--man/sd_id128_get_machine.xml82
-rw-r--r--man/sd_id128_randomize.xml6
-rw-r--r--man/sd_journal_enumerate_fields.xml3
-rw-r--r--man/sd_journal_get_catalog.xml11
-rw-r--r--man/sd_journal_get_cursor.xml3
-rw-r--r--man/sd_journal_get_cutoff_realtime_usec.xml3
-rw-r--r--man/sd_journal_get_data.xml8
-rw-r--r--man/sd_journal_get_fd.xml11
-rw-r--r--man/sd_journal_get_realtime_usec.xml8
-rw-r--r--man/sd_journal_get_usage.xml3
-rw-r--r--man/sd_journal_has_runtime_files.xml3
-rw-r--r--man/sd_journal_next.xml3
-rw-r--r--man/sd_journal_open.xml13
-rw-r--r--man/sd_journal_print.xml3
-rw-r--r--man/sd_journal_query_unique.xml30
-rw-r--r--man/sd_journal_seek_head.xml3
-rw-r--r--man/sd_journal_stream_fd.xml5
-rw-r--r--man/sd_listen_fds.xml2
-rw-r--r--man/sd_login_monitor_new.xml26
-rw-r--r--man/sd_notify.xml2
-rw-r--r--man/sd_pid_get_owner_uid.xml2
-rw-r--r--man/send-unit-files-changed.c16
-rw-r--r--man/standard-conf.xml2
-rw-r--r--man/standard-options.xml2
-rw-r--r--man/systemctl.xml159
-rw-r--r--man/systemd-analyze.xml39
-rw-r--r--man/systemd-ask-password.xml2
-rw-r--r--man/systemd-bless-boot-generator.xml49
-rw-r--r--man/systemd-bless-boot.service.xml115
-rw-r--r--man/systemd-boot-check-no-failures.service.xml54
-rw-r--r--man/systemd-boot.xml273
-rw-r--r--man/systemd-cgtop.xml2
-rw-r--r--man/systemd-coredump.xml2
-rw-r--r--man/systemd-cryptsetup-generator.xml14
-rw-r--r--man/systemd-debug-generator.xml27
-rw-r--r--man/systemd-escape.xml33
-rw-r--r--man/systemd-hibernate-resume-generator.xml14
-rw-r--r--man/systemd-hwdb.xml2
-rw-r--r--man/systemd-id128.xml122
-rw-r--r--man/systemd-inhibit.xml1
-rw-r--r--man/systemd-journal-gatewayd.service.xml2
-rw-r--r--man/systemd-journal-remote.service.xml2
-rw-r--r--man/systemd-journal-upload.service.xml2
-rw-r--r--man/systemd-journald.service.xml8
-rw-r--r--man/systemd-logind.service.xml6
-rw-r--r--man/systemd-machine-id-setup.xml2
-rw-r--r--man/systemd-mount.xml4
-rw-r--r--man/systemd-networkd.service.xml2
-rw-r--r--man/systemd-nspawn.xml6
-rw-r--r--man/systemd-portabled.service.xml2
-rw-r--r--man/systemd-resolved.service.xml64
-rw-r--r--man/systemd-run-generator.xml82
-rw-r--r--man/systemd-run.xml38
-rw-r--r--man/systemd-sleep.conf.xml20
-rw-r--r--man/systemd-socket-activate.xml2
-rw-r--r--man/systemd-socket-proxyd.xml4
-rw-r--r--man/systemd-system.conf.xml45
-rw-r--r--man/systemd-sysusers.xml3
-rw-r--r--man/systemd-time-wait-sync.service.xml2
-rw-r--r--man/systemd-tmpfiles.xml8
-rw-r--r--man/systemd-udevd.service.xml22
-rw-r--r--man/systemd.dnssd.xml2
-rw-r--r--man/systemd.exec.xml152
-rw-r--r--man/systemd.generator.xml2
-rw-r--r--man/systemd.journal-fields.xml42
-rw-r--r--man/systemd.kill.xml35
-rw-r--r--man/systemd.link.xml107
-rw-r--r--man/systemd.mount.xml16
-rw-r--r--man/systemd.netdev.xml290
-rw-r--r--man/systemd.network.xml274
-rw-r--r--man/systemd.nspawn.xml14
-rw-r--r--man/systemd.offline-updates.xml2
-rw-r--r--man/systemd.resource-control.xml108
-rw-r--r--man/systemd.scope.xml2
-rw-r--r--man/systemd.service.xml200
-rw-r--r--man/systemd.slice.xml2
-rw-r--r--man/systemd.socket.xml34
-rw-r--r--man/systemd.special.xml1906
-rw-r--r--man/systemd.swap.xml24
-rw-r--r--man/systemd.syntax.xml35
-rw-r--r--man/systemd.target.xml3
-rw-r--r--man/systemd.time.xml6
-rw-r--r--man/systemd.timer.xml22
-rw-r--r--man/systemd.unit.xml216
-rw-r--r--man/systemd.xml49
-rw-r--r--man/threads-aware.xml17
-rw-r--r--man/timedatectl.xml7
-rw-r--r--man/tmpfiles.d.xml300
-rw-r--r--man/udev.conf.xml51
-rw-r--r--man/udev.xml4
-rw-r--r--man/udevadm.xml55
-rw-r--r--man/user-system-options.xml5
-rw-r--r--man/user@.service.xml190
-rw-r--r--meson.build763
-rw-r--r--meson_options.txt71
-rwxr-xr-xmkosi.build5
-rw-r--r--po/LINGUAS1
-rw-r--r--po/be.po1
-rw-r--r--po/be@latin.po1
-rw-r--r--po/bg.po1
-rw-r--r--po/ca.po2
-rw-r--r--po/cs.po527
-rw-r--r--po/da.po3
-rw-r--r--po/de.po1
-rw-r--r--po/el.po1
-rw-r--r--po/es.po1
-rw-r--r--po/fr.po25
-rw-r--r--po/gl.po1
-rw-r--r--po/hu.po1
-rw-r--r--po/id.po1
-rw-r--r--po/it.po86
-rw-r--r--po/ja.po20
-rw-r--r--po/ko.po1
-rw-r--r--po/lt.po691
-rw-r--r--po/pl.po22
-rw-r--r--po/pt_BR.po119
-rw-r--r--po/ro.po1
-rw-r--r--po/ru.po51
-rw-r--r--po/sk.po1
-rw-r--r--po/sv.po1
-rw-r--r--po/tr.po96
-rw-r--r--po/uk.po1
-rw-r--r--po/zh_CN.po1
-rw-r--r--po/zh_TW.po1
-rw-r--r--rules/60-block.rules2
-rw-r--r--rules/60-persistent-storage-tape.rules14
-rw-r--r--rules/60-persistent-storage.rules7
-rw-r--r--shell-completion/bash/bootctl37
-rw-r--r--shell-completion/bash/busctl8
-rw-r--r--shell-completion/bash/coredumpctl10
-rw-r--r--shell-completion/bash/journalctl11
-rw-r--r--shell-completion/bash/kernel-install1
-rw-r--r--shell-completion/bash/loginctl12
-rw-r--r--shell-completion/bash/machinectl10
-rw-r--r--shell-completion/bash/meson.build2
-rw-r--r--shell-completion/bash/resolvectl4
-rw-r--r--shell-completion/bash/systemctl.in110
-rw-r--r--shell-completion/bash/systemd-analyze34
-rw-r--r--shell-completion/bash/systemd-nspawn37
-rw-r--r--shell-completion/bash/systemd-resolve4
-rw-r--r--shell-completion/bash/systemd-run20
-rw-r--r--shell-completion/bash/udevadm178
-rw-r--r--shell-completion/zsh/_coredumpctl3
-rw-r--r--shell-completion/zsh/_machinectl4
-rw-r--r--shell-completion/zsh/_sd_outputmodes2
-rw-r--r--shell-completion/zsh/_systemctl.in3
-rw-r--r--shell-completion/zsh/_systemd-run4
-rw-r--r--shell-completion/zsh/meson.build2
-rw-r--r--src/ac-power/ac-power.c24
-rw-r--r--src/activate/activate.c65
-rw-r--r--src/analyze/analyze-security.c2087
-rw-r--r--src/analyze/analyze-security.h12
-rw-r--r--src/analyze/analyze-verify.c18
-rw-r--r--src/analyze/analyze.c605
-rw-r--r--src/analyze/meson.build2
-rw-r--r--src/ask-password/ask-password.c53
-rw-r--r--src/backlight/backlight.c408
-rw-r--r--src/basic/MurmurHash2.h5
-rw-r--r--src/basic/af-list.c5
-rw-r--r--src/basic/alloc-util.c2
-rw-r--r--src/basic/alloc-util.h23
-rw-r--r--src/basic/arphrd-list.c6
-rw-r--r--src/basic/async.c5
-rw-r--r--src/basic/blockdev-util.c22
-rw-r--r--src/basic/btrfs-ctree.h97
-rw-r--r--src/basic/btrfs-util.c226
-rw-r--r--src/basic/btrfs-util.h16
-rw-r--r--src/basic/cap-list.c4
-rw-r--r--src/basic/capability-util.c125
-rw-r--r--src/basic/capability-util.h29
-rw-r--r--src/basic/cgroup-util.c300
-rw-r--r--src/basic/cgroup-util.h36
-rw-r--r--src/basic/chattr-util.c16
-rw-r--r--src/basic/chattr-util.h4
-rw-r--r--src/basic/conf-files.c86
-rw-r--r--src/basic/conf-files.h5
-rw-r--r--src/basic/copy.c128
-rw-r--r--src/basic/copy.h50
-rw-r--r--src/basic/crypt-util.c10
-rw-r--r--src/basic/def.h2
-rw-r--r--src/basic/env-file.c574
-rw-r--r--src/basic/env-file.h17
-rw-r--r--src/basic/env-util.c159
-rw-r--r--src/basic/env-util.h4
-rw-r--r--src/basic/errno-list.c2
-rw-r--r--src/basic/escape.c1
-rw-r--r--src/basic/escape.h2
-rw-r--r--src/basic/ether-addr-util.c12
-rw-r--r--src/basic/ether-addr-util.h3
-rw-r--r--src/basic/fd-util.c31
-rw-r--r--src/basic/fd-util.h8
-rw-r--r--src/basic/fileio.c1057
-rw-r--r--src/basic/fileio.h46
-rw-r--r--src/basic/format-util.h1
-rw-r--r--src/basic/fs-util.c277
-rw-r--r--src/basic/fs-util.h8
-rwxr-xr-xsrc/basic/generate-af-list.sh2
-rwxr-xr-xsrc/basic/generate-arphrd-list.sh2
-rw-r--r--src/basic/hash-funcs.c53
-rw-r--r--src/basic/hash-funcs.h78
-rw-r--r--src/basic/hashmap.c157
-rw-r--r--src/basic/hashmap.h110
-rw-r--r--src/basic/hexdecoct.c10
-rw-r--r--src/basic/hostname-util.c57
-rw-r--r--src/basic/hostname-util.h1
-rw-r--r--src/basic/in-addr-util.c68
-rw-r--r--src/basic/in-addr-util.h23
-rw-r--r--src/basic/label.h1
-rw-r--r--src/basic/list.h12
-rw-r--r--src/basic/locale-util.c91
-rw-r--r--src/basic/locale-util.h35
-rw-r--r--src/basic/log.c102
-rw-r--r--src/basic/log.h16
-rw-r--r--src/basic/macro.h277
-rw-r--r--src/basic/mempool.c18
-rw-r--r--src/basic/mempool.h5
-rw-r--r--src/basic/meson.build115
-rw-r--r--src/basic/missing.h1422
-rw-r--r--src/basic/missing_audit.h24
-rw-r--r--src/basic/missing_btrfs.h22
-rw-r--r--src/basic/missing_btrfs_tree.h109
-rw-r--r--src/basic/missing_capability.h12
-rw-r--r--src/basic/missing_drm.h10
-rw-r--r--src/basic/missing_ethtool.h131
-rw-r--r--src/basic/missing_fcntl.h60
-rw-r--r--src/basic/missing_fib_rules.h45
-rw-r--r--src/basic/missing_fou.h55
-rw-r--r--src/basic/missing_fs.h63
-rw-r--r--src/basic/missing_if_bridge.h21
-rw-r--r--src/basic/missing_if_link.h390
-rw-r--r--src/basic/missing_if_tunnel.h59
-rw-r--r--src/basic/missing_input.h45
-rw-r--r--src/basic/missing_keyctl.h78
-rw-r--r--src/basic/missing_magic.h34
-rw-r--r--src/basic/missing_mman.h12
-rw-r--r--src/basic/missing_network.h155
-rw-r--r--src/basic/missing_prctl.h14
-rw-r--r--src/basic/missing_random.h16
-rw-r--r--src/basic/missing_resource.h11
-rw-r--r--src/basic/missing_sched.h21
-rw-r--r--src/basic/missing_securebits.h17
-rw-r--r--src/basic/missing_socket.h60
-rw-r--r--src/basic/missing_stat.h51
-rw-r--r--src/basic/missing_stdlib.h13
-rw-r--r--src/basic/missing_syscall.h17
-rw-r--r--src/basic/missing_timerfd.h8
-rw-r--r--src/basic/missing_type.h12
-rw-r--r--src/basic/missing_vxcan.h12
-rw-r--r--src/basic/mkdir-label.c17
-rw-r--r--src/basic/mkdir.c6
-rw-r--r--src/basic/mkdir.h2
-rw-r--r--src/basic/mountpoint-util.c444
-rw-r--r--src/basic/mountpoint-util.h24
-rw-r--r--src/basic/ordered-set.h6
-rw-r--r--src/basic/parse-util.c59
-rw-r--r--src/basic/parse-util.h1
-rw-r--r--src/basic/path-util.c157
-rw-r--r--src/basic/path-util.h47
-rw-r--r--src/basic/prioq.c24
-rw-r--r--src/basic/prioq.h1
-rw-r--r--src/basic/proc-cmdline.c176
-rw-r--r--src/basic/proc-cmdline.h17
-rw-r--r--src/basic/process-util.c210
-rw-r--r--src/basic/process-util.h32
-rw-r--r--src/basic/random-util.c209
-rw-r--r--src/basic/random-util.h16
-rw-r--r--src/basic/raw-clone.h2
-rw-r--r--src/basic/refcnt.h38
-rw-r--r--src/basic/rlimit-util.c52
-rw-r--r--src/basic/rlimit-util.h3
-rw-r--r--src/basic/rm-rf.c18
-rw-r--r--src/basic/rm-rf.h8
-rw-r--r--src/basic/securebits.h45
-rw-r--r--src/basic/selinux-util.c79
-rw-r--r--src/basic/selinux-util.h1
-rw-r--r--src/basic/set.h14
-rw-r--r--src/basic/sigbus.c4
-rw-r--r--src/basic/siphash24.c8
-rw-r--r--src/basic/siphash24.h5
-rw-r--r--src/basic/smack-util.c96
-rw-r--r--src/basic/smack-util.h1
-rw-r--r--src/basic/socket-label.c35
-rw-r--r--src/basic/socket-protocol-list.c39
-rw-r--r--src/basic/socket-protocol-list.h7
-rw-r--r--src/basic/socket-util.c350
-rw-r--r--src/basic/socket-util.h41
-rw-r--r--src/basic/sparse-endian.h9
-rw-r--r--src/basic/stat-util.c170
-rw-r--r--src/basic/stat-util.h27
-rw-r--r--src/basic/static-destruct.h56
-rw-r--r--src/basic/stdio-util.h4
-rw-r--r--src/basic/strbuf.c7
-rw-r--r--src/basic/strbuf.h1
-rw-r--r--src/basic/string-table.h2
-rw-r--r--src/basic/string-util.c56
-rw-r--r--src/basic/string-util.h49
-rw-r--r--src/basic/strv.c33
-rw-r--r--src/basic/strv.h26
-rw-r--r--src/basic/strxcpyx.c16
-rw-r--r--src/basic/strxcpyx.h1
-rw-r--r--src/basic/syslog-util.c20
-rw-r--r--src/basic/terminal-util.c357
-rw-r--r--src/basic/terminal-util.h18
-rw-r--r--src/basic/time-util.c307
-rw-r--r--src/basic/time-util.h4
-rw-r--r--src/basic/tmpfile-util.c329
-rw-r--r--src/basic/tmpfile-util.h19
-rw-r--r--src/basic/unaligned.h25
-rw-r--r--src/basic/unit-def.c2
-rw-r--r--src/basic/unit-def.h2
-rw-r--r--src/basic/unit-name.h2
-rw-r--r--src/basic/user-util.c173
-rw-r--r--src/basic/user-util.h15
-rw-r--r--src/basic/utf8.c123
-rw-r--r--src/basic/utf8.h17
-rw-r--r--src/basic/util.c76
-rw-r--r--src/basic/util.h67
-rw-r--r--src/basic/virt.c37
-rw-r--r--src/basic/xattr-util.c1
-rw-r--r--src/binfmt/binfmt.c96
-rw-r--r--src/boot/bless-boot-generator.c71
-rw-r--r--src/boot/bless-boot.c472
-rw-r--r--src/boot/boot-check-no-failures.c102
-rw-r--r--src/boot/bootctl.c383
-rw-r--r--src/boot/efi/boot.c567
-rw-r--r--src/boot/efi/console.c3
-rw-r--r--src/boot/efi/console.h10
-rw-r--r--src/boot/efi/disk.h5
-rw-r--r--src/boot/efi/graphics.c1
-rw-r--r--src/boot/efi/graphics.h6
-rw-r--r--src/boot/efi/linux.h5
-rw-r--r--src/boot/efi/measure.c7
-rw-r--r--src/boot/efi/measure.h7
-rw-r--r--src/boot/efi/meson.build35
-rw-r--r--src/boot/efi/pe.h5
-rw-r--r--src/boot/efi/shim.c32
-rw-r--r--src/boot/efi/shim.h8
-rw-r--r--src/boot/efi/splash.c5
-rw-r--r--src/boot/efi/splash.h8
-rw-r--r--src/boot/efi/stub.c8
-rw-r--r--src/boot/efi/util.c55
-rw-r--r--src/boot/efi/util.h44
-rw-r--r--src/busctl/busctl-introspect.c297
-rw-r--r--src/busctl/busctl.c652
-rw-r--r--src/cgls/cgls.c75
-rw-r--r--src/cgroups-agent/cgroups-agent.c4
-rw-r--r--src/cgtop/cgtop.c223
-rw-r--r--src/core/automount.c33
-rw-r--r--src/core/bpf-devices.c271
-rw-r--r--src/core/bpf-devices.h17
-rw-r--r--src/core/bpf-firewall.c42
-rw-r--r--src/core/bpf-firewall.h1
-rw-r--r--src/core/cgroup.c1218
-rw-r--r--src/core/cgroup.h28
-rw-r--r--src/core/chown-recursive.c160
-rw-r--r--src/core/dbus-cgroup.c158
-rw-r--r--src/core/dbus-execute.c120
-rw-r--r--src/core/dbus-job.c24
-rw-r--r--src/core/dbus-job.h1
-rw-r--r--src/core/dbus-kill.c10
-rw-r--r--src/core/dbus-manager.c89
-rw-r--r--src/core/dbus-mount.c2
-rw-r--r--src/core/dbus-scope.c6
-rw-r--r--src/core/dbus-scope.h2
-rw-r--r--src/core/dbus-service.c44
-rw-r--r--src/core/dbus-slice.c2
-rw-r--r--src/core/dbus-socket.c36
-rw-r--r--src/core/dbus-swap.c2
-rw-r--r--src/core/dbus-unit.c162
-rw-r--r--src/core/dbus-unit.h1
-rw-r--r--src/core/dbus.c63
-rw-r--r--src/core/dbus.h2
-rw-r--r--src/core/device.c291
-rw-r--r--src/core/device.h6
-rw-r--r--src/core/dynamic-user.c111
-rw-r--r--src/core/dynamic-user.h2
-rw-r--r--src/core/emergency-action.c96
-rw-r--r--src/core/emergency-action.h18
-rw-r--r--src/core/execute.c498
-rw-r--r--src/core/execute.h63
-rw-r--r--src/core/ima-setup.c21
-rw-r--r--src/core/ip-address-access.h1
-rw-r--r--src/core/job.c349
-rw-r--r--src/core/job.h9
-rw-r--r--src/core/kill.c4
-rw-r--r--src/core/kill.h2
-rw-r--r--src/core/killall.c21
-rw-r--r--src/core/kmod-setup.c31
-rw-r--r--src/core/load-dropin.c2
-rw-r--r--src/core/load-fragment-gperf.gperf.m413
-rw-r--r--src/core/load-fragment.c334
-rw-r--r--src/core/load-fragment.h6
-rw-r--r--src/core/locale-setup.c104
-rw-r--r--src/core/loopback-setup.c6
-rw-r--r--src/core/machine-id-setup.c17
-rw-r--r--src/core/macros.systemd.in8
-rw-r--r--src/core/main.c464
-rw-r--r--src/core/manager.c994
-rw-r--r--src/core/manager.h92
-rw-r--r--src/core/meson.build2
-rw-r--r--src/core/mount-setup.c281
-rw-r--r--src/core/mount-setup.h2
-rw-r--r--src/core/mount.c407
-rw-r--r--src/core/mount.h13
-rw-r--r--src/core/namespace.c291
-rw-r--r--src/core/path.c26
-rw-r--r--src/core/scope.c19
-rw-r--r--src/core/selinux-access.c3
-rw-r--r--src/core/selinux-access.h4
-rw-r--r--src/core/service.c581
-rw-r--r--src/core/service.h11
-rw-r--r--src/core/show-status.c52
-rw-r--r--src/core/show-status.h22
-rw-r--r--src/core/shutdown.c41
-rw-r--r--src/core/slice.c16
-rw-r--r--src/core/smack-setup.c85
-rw-r--r--src/core/socket.c271
-rw-r--r--src/core/swap.c269
-rw-r--r--src/core/swap.h6
-rw-r--r--src/core/system.conf.in6
-rw-r--r--src/core/target.c11
-rw-r--r--src/core/timer.c55
-rw-r--r--src/core/transaction.c22
-rw-r--r--src/core/umount.c135
-rw-r--r--src/core/unit-printf.c82
-rw-r--r--src/core/unit-printf.h1
-rw-r--r--src/core/unit.c1110
-rw-r--r--src/core/unit.h107
-rw-r--r--src/coredump/coredump.c95
-rw-r--r--src/coredump/coredumpctl.c100
-rw-r--r--src/coredump/meson.build6
-rw-r--r--src/cryptsetup/cryptsetup-generator.c247
-rw-r--r--src/cryptsetup/cryptsetup.c172
-rw-r--r--src/debug-generator/debug-generator.c42
-rw-r--r--src/delta/delta.c79
-rw-r--r--src/detect-virt/detect-virt.c78
-rw-r--r--src/dissect/dissect.c71
-rw-r--r--src/environment-d-generator/environment-d-generator.c2
-rw-r--r--src/escape/escape.c169
-rw-r--r--src/firstboot/firstboot.c145
-rw-r--r--src/fsck/fsck.c136
-rw-r--r--src/fstab-generator/fstab-generator.c51
-rw-r--r--src/fuzz/fuzz-bus-message.c47
-rw-r--r--src/fuzz/fuzz-catalog.c26
-rw-r--r--src/fuzz/fuzz-compress.c80
-rw-r--r--src/fuzz/fuzz-dhcp6-client.c59
-rw-r--r--src/fuzz/fuzz-journal-remote.c3
-rw-r--r--src/fuzz/fuzz-journald-audit.c15
-rw-r--r--src/fuzz/fuzz-journald-kmsg.c18
-rw-r--r--src/fuzz/fuzz-journald-native-fd.c47
-rw-r--r--src/fuzz/fuzz-journald-native.c10
-rw-r--r--src/fuzz/fuzz-journald-stream.c36
-rw-r--r--src/fuzz/fuzz-journald-syslog.c10
-rw-r--r--src/fuzz/fuzz-journald.c46
-rw-r--r--src/fuzz/fuzz-journald.h12
-rw-r--r--src/fuzz/fuzz-json.c30
-rw-r--r--src/fuzz/fuzz-lldp.c40
-rw-r--r--src/fuzz/fuzz-main.c14
-rw-r--r--src/fuzz/fuzz-ndisc-rs.c58
-rw-r--r--src/fuzz/fuzz-udev-rules.c107
-rw-r--r--src/fuzz/fuzz.h1
-rw-r--r--src/fuzz/meson.build85
-rw-r--r--src/getty-generator/getty-generator.c73
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c172
-rw-r--r--src/hibernate-resume/hibernate-resume-generator.c50
-rw-r--r--src/hibernate-resume/hibernate-resume.c6
-rw-r--r--src/hostname/hostnamectl.c37
-rw-r--r--src/hostname/hostnamed.c122
-rw-r--r--src/hostname/org.freedesktop.hostname1.policy10
-rw-r--r--src/hwdb/hwdb.c717
-rw-r--r--src/id128/id128.c168
-rw-r--r--src/import/curl-util.c70
-rw-r--r--src/import/curl-util.h1
-rw-r--r--src/import/export-raw.c22
-rw-r--r--src/import/export-tar.c41
-rw-r--r--src/import/export.c24
-rw-r--r--src/import/import-common.c110
-rw-r--r--src/import/import-common.h4
-rw-r--r--src/import/import-compress.c14
-rw-r--r--src/import/import-compress.h4
-rw-r--r--src/import/import-fs.c327
-rw-r--r--src/import/import-raw.c42
-rw-r--r--src/import/import-tar.c41
-rw-r--r--src/import/import.c17
-rw-r--r--src/import/importd.c228
-rw-r--r--src/import/meson.build6
-rw-r--r--src/import/org.freedesktop.import1.conf20
-rw-r--r--src/import/pull-common.c20
-rw-r--r--src/import/pull-job.c97
-rw-r--r--src/import/pull-job.h3
-rw-r--r--src/import/pull-raw.c44
-rw-r--r--src/import/pull-tar.c38
-rw-r--r--src/import/pull.c24
-rw-r--r--src/initctl/initctl.c25
-rw-r--r--src/journal-remote/browse.html21
-rw-r--r--src/journal-remote/journal-gatewayd.c219
-rw-r--r--src/journal-remote/journal-remote-main.c322
-rw-r--r--src/journal-remote/journal-remote-write.c16
-rw-r--r--src/journal-remote/journal-remote-write.h4
-rw-r--r--src/journal-remote/journal-remote.c5
-rw-r--r--src/journal-remote/journal-remote.conf.in13
-rw-r--r--src/journal-remote/journal-remote.h2
-rw-r--r--src/journal-remote/journal-upload-journal.c19
-rw-r--r--src/journal-remote/journal-upload.c263
-rw-r--r--src/journal-remote/journal-upload.conf.in13
-rw-r--r--src/journal-remote/microhttpd-util.c10
-rw-r--r--src/journal-remote/microhttpd-util.h2
-rw-r--r--src/journal/audit-type.c10
-rw-r--r--src/journal/audit-type.h3
-rw-r--r--src/journal/cat.c51
-rw-r--r--src/journal/catalog.c93
-rw-r--r--src/journal/compress.c43
-rw-r--r--src/journal/fsprg.h7
-rw-r--r--src/journal/journal-def.h1
-rw-r--r--src/journal/journal-file.c614
-rw-r--r--src/journal/journal-file.h8
-rw-r--r--src/journal/journal-send.c2
-rw-r--r--src/journal/journal-vacuum.c65
-rw-r--r--src/journal/journal-verify.c8
-rw-r--r--src/journal/journalctl.c263
-rw-r--r--src/journal/journald-audit.c12
-rw-r--r--src/journal/journald-audit.h4
-rw-r--r--src/journal/journald-context.c73
-rw-r--r--src/journal/journald-context.h3
-rw-r--r--src/journal/journald-kmsg.c37
-rw-r--r--src/journal/journald-kmsg.h2
-rw-r--r--src/journal/journald-native.c31
-rw-r--r--src/journal/journald-native.h2
-rw-r--r--src/journal/journald-rate-limit.c48
-rw-r--r--src/journal/journald-rate-limit.h4
-rw-r--r--src/journal/journald-server.c256
-rw-r--r--src/journal/journald-server.h2
-rw-r--r--src/journal/journald-stream.c35
-rw-r--r--src/journal/journald-stream.h2
-rw-r--r--src/journal/journald-syslog.c163
-rw-r--r--src/journal/journald-wall.c3
-rw-r--r--src/journal/journald-wall.h3
-rw-r--r--src/journal/journald.conf1
-rw-r--r--src/journal/lookup3.c6
-rw-r--r--src/journal/meson.build2
-rw-r--r--src/journal/mmap-cache.c28
-rw-r--r--src/journal/sd-journal.c87
-rw-r--r--src/journal/test-catalog.c25
-rw-r--r--src/journal/test-compress-benchmark.c20
-rw-r--r--src/journal/test-compress.c63
-rw-r--r--src/journal/test-journal-enum.c5
-rw-r--r--src/journal/test-journal-init.c3
-rw-r--r--src/journal/test-journal-interleaving.c15
-rw-r--r--src/journal/test-journal-match.c9
-rw-r--r--src/journal/test-journal-stream.c5
-rw-r--r--src/journal/test-journal-syslog.c42
-rw-r--r--src/journal/test-journal-verify.c9
-rw-r--r--src/journal/test-journal.c24
-rw-r--r--src/journal/test-mmap-cache.c2
-rw-r--r--src/kernel-install/50-depmod.install1
-rw-r--r--src/kernel-install/90-loaderentry.install16
-rw-r--r--src/kernel-install/kernel-install1
-rw-r--r--src/libsystemd-network/dhcp-identifier.c140
-rw-r--r--src/libsystemd-network/dhcp-identifier.h17
-rw-r--r--src/libsystemd-network/dhcp-network.c30
-rw-r--r--src/libsystemd-network/dhcp-packet.c82
-rw-r--r--src/libsystemd-network/dhcp-server-internal.h4
-rw-r--r--src/libsystemd-network/dhcp6-internal.h8
-rw-r--r--src/libsystemd-network/dhcp6-lease-internal.h2
-rw-r--r--src/libsystemd-network/dhcp6-network.c14
-rw-r--r--src/libsystemd-network/dhcp6-option.c171
-rw-r--r--src/libsystemd-network/icmp6-util.c37
-rw-r--r--src/libsystemd-network/lldp-internal.h4
-rw-r--r--src/libsystemd-network/lldp-neighbor.c54
-rw-r--r--src/libsystemd-network/lldp-neighbor.h3
-rw-r--r--src/libsystemd-network/lldp-network.c1
-rw-r--r--src/libsystemd-network/lldp-network.h1
-rw-r--r--src/libsystemd-network/ndisc-internal.h3
-rw-r--r--src/libsystemd-network/ndisc-router.c29
-rw-r--r--src/libsystemd-network/network-internal.c51
-rw-r--r--src/libsystemd-network/network-internal.h8
-rw-r--r--src/libsystemd-network/radv-internal.h1
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c324
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c46
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c66
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c435
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c56
-rw-r--r--src/libsystemd-network/sd-ipv4acd.c59
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c23
-rw-r--r--src/libsystemd-network/sd-lldp.c111
-rw-r--r--src/libsystemd-network/sd-ndisc.c126
-rw-r--r--src/libsystemd-network/sd-radv.c208
-rw-r--r--src/libsystemd-network/test-acd.c5
-rw-r--r--src/libsystemd-network/test-dhcp-client.c43
-rw-r--r--src/libsystemd-network/test-dhcp-server.c10
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c83
-rw-r--r--src/libsystemd-network/test-ipv4ll-manual.c5
-rw-r--r--src/libsystemd-network/test-ipv4ll.c7
-rw-r--r--src/libsystemd-network/test-lldp.c132
-rw-r--r--src/libsystemd-network/test-ndisc-ra.c9
-rw-r--r--src/libsystemd-network/test-ndisc-rs.c7
-rw-r--r--src/libsystemd/disable-mempool.c5
-rw-r--r--src/libsystemd/libsystemd.sym101
-rw-r--r--src/libsystemd/meson.build23
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c7
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h6
-rw-r--r--src/libsystemd/sd-bus/bus-container.c44
-rw-r--r--src/libsystemd/sd-bus/bus-container.h3
-rw-r--r--src/libsystemd/sd-bus/bus-control.c20
-rw-r--r--src/libsystemd/sd-bus/bus-control.h3
-rw-r--r--src/libsystemd/sd-bus/bus-convenience.c2
-rw-r--r--src/libsystemd/sd-bus/bus-creds.c24
-rw-r--r--src/libsystemd/sd-bus/bus-creds.h3
-rw-r--r--src/libsystemd/sd-bus/bus-dump.c14
-rw-r--r--src/libsystemd/sd-bus/bus-dump.h3
-rw-r--r--src/libsystemd/sd-bus/bus-error.c39
-rw-r--r--src/libsystemd/sd-bus/bus-error.h13
-rw-r--r--src/libsystemd/sd-bus/bus-gvariant.c2
-rw-r--r--src/libsystemd/sd-bus/bus-gvariant.h3
-rw-r--r--src/libsystemd/sd-bus/bus-internal.c22
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h22
-rw-r--r--src/libsystemd/sd-bus/bus-introspect.c4
-rw-r--r--src/libsystemd/sd-bus/bus-introspect.h3
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.c2
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.h3
-rw-r--r--src/libsystemd/sd-bus/bus-match.c119
-rw-r--r--src/libsystemd/sd-bus/bus-match.h5
-rw-r--r--src/libsystemd/sd-bus/bus-message.c373
-rw-r--r--src/libsystemd/sd-bus/bus-message.h5
-rw-r--r--src/libsystemd/sd-bus/bus-objects.c34
-rw-r--r--src/libsystemd/sd-bus/bus-objects.h3
-rw-r--r--src/libsystemd/sd-bus/bus-protocol.h3
-rw-r--r--src/libsystemd/sd-bus/bus-signature.c8
-rw-r--r--src/libsystemd/sd-bus/bus-signature.h3
-rw-r--r--src/libsystemd/sd-bus/bus-slot.c36
-rw-r--r--src/libsystemd/sd-bus/bus-slot.h5
-rw-r--r--src/libsystemd/sd-bus/bus-socket.c32
-rw-r--r--src/libsystemd/sd-bus/bus-socket.h3
-rw-r--r--src/libsystemd/sd-bus/bus-track.c35
-rw-r--r--src/libsystemd/sd-bus/bus-track.h3
-rw-r--r--src/libsystemd/sd-bus/bus-type.c28
-rw-r--r--src/libsystemd/sd-bus/bus-type.h4
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c284
-rw-r--r--src/libsystemd/sd-bus/test-bus-address.c69
-rw-r--r--src/libsystemd/sd-bus/test-bus-benchmark.c3
-rw-r--r--src/libsystemd/sd-bus/test-bus-chat.c15
-rw-r--r--src/libsystemd/sd-bus/test-bus-cleanup.c28
-rw-r--r--src/libsystemd/sd-bus/test-bus-creds.c13
-rw-r--r--src/libsystemd/sd-bus/test-bus-error.c12
-rw-r--r--src/libsystemd/sd-bus/test-bus-gvariant.c34
-rw-r--r--src/libsystemd/sd-bus/test-bus-introspect.c2
-rw-r--r--src/libsystemd/sd-bus/test-bus-marshal.c25
-rw-r--r--src/libsystemd/sd-bus/test-bus-match.c9
-rw-r--r--src/libsystemd/sd-bus/test-bus-objects.c6
-rw-r--r--src/libsystemd/sd-bus/test-bus-server.c8
-rw-r--r--src/libsystemd/sd-bus/test-bus-signature.c10
-rw-r--r--src/libsystemd/sd-bus/test-bus-track.c17
-rw-r--r--src/libsystemd/sd-bus/test-bus-watch-bind.c14
-rw-r--r--src/libsystemd/sd-daemon/sd-daemon.c33
-rw-r--r--src/libsystemd/sd-device/device-enumerator-private.h2
-rw-r--r--src/libsystemd/sd-device/device-enumerator.c241
-rw-r--r--src/libsystemd/sd-device/device-internal.h47
-rw-r--r--src/libsystemd/sd-device/device-monitor-private.h20
-rw-r--r--src/libsystemd/sd-device/device-monitor.c762
-rw-r--r--src/libsystemd/sd-device/device-private.c302
-rw-r--r--src/libsystemd/sd-device/device-private.h8
-rw-r--r--src/libsystemd/sd-device/device-util.h28
-rw-r--r--src/libsystemd/sd-device/sd-device.c295
-rw-r--r--src/libsystemd/sd-device/test-sd-device-monitor.c151
-rw-r--r--src/libsystemd/sd-device/test-sd-device-thread.c39
-rw-r--r--src/libsystemd/sd-device/test-sd-device.c156
-rw-r--r--src/libsystemd/sd-device/test-udev-device-thread.c36
-rw-r--r--src/libsystemd/sd-event/event-source.h206
-rw-r--r--src/libsystemd/sd-event/event-util.c99
-rw-r--r--src/libsystemd/sd-event/event-util.h13
-rw-r--r--src/libsystemd/sd-event/sd-event.c481
-rw-r--r--src/libsystemd/sd-event/test-event.c9
-rw-r--r--src/libsystemd/sd-hwdb/hwdb-internal.h2
-rw-r--r--src/libsystemd/sd-hwdb/hwdb-util.c686
-rw-r--r--src/libsystemd/sd-hwdb/hwdb-util.h5
-rw-r--r--src/libsystemd/sd-hwdb/sd-hwdb.c41
-rw-r--r--src/libsystemd/sd-id128/id128-util.c13
-rw-r--r--src/libsystemd/sd-id128/id128-util.h7
-rw-r--r--src/libsystemd/sd-id128/sd-id128.c44
-rw-r--r--src/libsystemd/sd-login/sd-login.c88
-rw-r--r--src/libsystemd/sd-login/test-login.c2
-rw-r--r--src/libsystemd/sd-netlink/generic-netlink.c1
-rw-r--r--src/libsystemd/sd-netlink/local-addresses.c30
-rw-r--r--src/libsystemd/sd-netlink/local-addresses.h3
-rw-r--r--src/libsystemd/sd-netlink/netlink-internal.h29
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c29
-rw-r--r--src/libsystemd/sd-netlink/netlink-slot.c202
-rw-r--r--src/libsystemd/sd-netlink/netlink-slot.h14
-rw-r--r--src/libsystemd/sd-netlink/netlink-socket.c10
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c93
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.h2
-rw-r--r--src/libsystemd/sd-netlink/netlink-util.h21
-rw-r--r--src/libsystemd/sd-netlink/rtnl-message.c26
-rw-r--r--src/libsystemd/sd-netlink/sd-netlink.c326
-rw-r--r--src/libsystemd/sd-netlink/test-local-addresses.c7
-rw-r--r--src/libsystemd/sd-netlink/test-netlink.c199
-rw-r--r--src/libsystemd/sd-network/network-util.c2
-rw-r--r--src/libsystemd/sd-network/network-util.h1
-rw-r--r--src/libsystemd/sd-network/sd-network.c31
-rw-r--r--src/libsystemd/sd-path/sd-path.c12
-rw-r--r--src/libsystemd/sd-resolve/resolve-private.h39
-rw-r--r--src/libsystemd/sd-resolve/sd-resolve.c274
-rw-r--r--src/libsystemd/sd-resolve/test-resolve.c6
-rw-r--r--src/libsystemd/sd-utf8/sd-utf8.c2
-rw-r--r--src/libudev/libudev-device-internal.h7
-rw-r--r--src/libudev/libudev-device-private.c393
-rw-r--r--src/libudev/libudev-device.c392
-rw-r--r--src/libudev/libudev-enumerate.c75
-rw-r--r--src/libudev/libudev-hwdb.c79
-rw-r--r--src/libudev/libudev-list-internal.h21
-rw-r--r--src/libudev/libudev-list.c94
-rw-r--r--src/libudev/libudev-monitor.c750
-rw-r--r--src/libudev/libudev-private.h129
-rw-r--r--src/libudev/libudev-queue.c109
-rw-r--r--src/libudev/libudev-util.c163
-rw-r--r--src/libudev/libudev-util.h24
-rw-r--r--src/libudev/libudev.c57
-rw-r--r--src/libudev/libudev.h22
-rw-r--r--src/libudev/meson.build12
-rw-r--r--src/locale/keymap-util.c85
-rw-r--r--src/locale/keymap-util.h6
-rw-r--r--src/locale/localectl.c128
-rw-r--r--src/locale/localed.c97
-rw-r--r--src/locale/test-keymap-util.c4
-rw-r--r--src/login/70-uaccess.rules.m4 (renamed from src/login/70-uaccess.rules)4
-rw-r--r--src/login/71-seat.rules.in2
-rw-r--r--src/login/inhibit.c161
-rw-r--r--src/login/loginctl.c216
-rw-r--r--src/login/logind-acl.c51
-rw-r--r--src/login/logind-acl.h8
-rw-r--r--src/login/logind-action.c16
-rw-r--r--src/login/logind-button.c9
-rw-r--r--src/login/logind-button.h2
-rw-r--r--src/login/logind-core.c349
-rw-r--r--src/login/logind-dbus.c185
-rw-r--r--src/login/logind-device.c4
-rw-r--r--src/login/logind-gperf.gperf1
-rw-r--r--src/login/logind-inhibit.c7
-rw-r--r--src/login/logind-seat-dbus.c3
-rw-r--r--src/login/logind-seat.c97
-rw-r--r--src/login/logind-seat.h12
-rw-r--r--src/login/logind-session-dbus.c32
-rw-r--r--src/login/logind-session-device.c96
-rw-r--r--src/login/logind-session-device.h3
-rw-r--r--src/login/logind-session.c348
-rw-r--r--src/login/logind-session.h30
-rw-r--r--src/login/logind-user-dbus.c3
-rw-r--r--src/login/logind-user.c368
-rw-r--r--src/login/logind-user.h22
-rw-r--r--src/login/logind-utmp.c8
-rw-r--r--src/login/logind.c317
-rw-r--r--src/login/logind.h34
-rw-r--r--src/login/meson.build14
-rw-r--r--src/login/org.freedesktop.login1.policy3
-rw-r--r--src/login/pam_systemd.c319
-rw-r--r--src/login/sysfs-show.c129
-rw-r--r--src/login/test-inhibit.c2
-rw-r--r--src/login/user-runtime-dir.c140
-rw-r--r--src/machine-id-setup/machine-id-setup-main.c51
-rw-r--r--src/machine/image-dbus.c11
-rw-r--r--src/machine/machine-dbus.c55
-rw-r--r--src/machine/machine.c75
-rw-r--r--src/machine/machine.h4
-rw-r--r--src/machine/machinectl.c415
-rw-r--r--src/machine/machined-core.c36
-rw-r--r--src/machine/machined-dbus.c86
-rw-r--r--src/machine/machined.c44
-rw-r--r--src/machine/machined.h5
-rw-r--r--src/machine/meson.build1
-rw-r--r--src/modules-load/modules-load.c132
-rw-r--r--src/mount/mount-tool.c370
-rw-r--r--src/network/fuzz-netdev-parser.c25
-rw-r--r--src/network/fuzz-network-parser.c25
-rw-r--r--src/network/meson.build25
-rw-r--r--src/network/netdev/bond.c141
-rw-r--r--src/network/netdev/bond.h28
-rw-r--r--src/network/netdev/bridge.c6
-rw-r--r--src/network/netdev/bridge.h1
-rw-r--r--src/network/netdev/dummy.h1
-rw-r--r--src/network/netdev/fou-tunnel.c129
-rw-r--r--src/network/netdev/fou-tunnel.h36
-rw-r--r--src/network/netdev/geneve.c12
-rw-r--r--src/network/netdev/geneve.h36
-rw-r--r--src/network/netdev/ipvlan.h7
-rw-r--r--src/network/netdev/macvlan.h3
-rw-r--r--src/network/netdev/netdev-gperf.gperf20
-rw-r--r--src/network/netdev/netdev.c156
-rw-r--r--src/network/netdev/netdev.h18
-rw-r--r--src/network/netdev/netdevsim.h1
-rw-r--r--src/network/netdev/tunnel.c193
-rw-r--r--src/network/netdev/tunnel.h55
-rw-r--r--src/network/netdev/tuntap.c20
-rw-r--r--src/network/netdev/tuntap.h1
-rw-r--r--src/network/netdev/vcan.c1
-rw-r--r--src/network/netdev/vcan.h1
-rw-r--r--src/network/netdev/veth.h1
-rw-r--r--src/network/netdev/vlan.h1
-rw-r--r--src/network/netdev/vrf.c5
-rw-r--r--src/network/netdev/vrf.h4
-rw-r--r--src/network/netdev/vxcan.c6
-rw-r--r--src/network/netdev/vxcan.h5
-rw-r--r--src/network/netdev/vxlan.c39
-rw-r--r--src/network/netdev/vxlan.h35
-rw-r--r--src/network/netdev/wireguard.c153
-rw-r--r--src/network/netdev/wireguard.h28
-rw-r--r--src/network/networkctl.c77
-rw-r--r--src/network/networkd-address-label.c87
-rw-r--r--src/network/networkd-address-label.h5
-rw-r--r--src/network/networkd-address-pool.c12
-rw-r--r--src/network/networkd-address.c135
-rw-r--r--src/network/networkd-address.h5
-rw-r--r--src/network/networkd-brvlan.c8
-rw-r--r--src/network/networkd-conf.c69
-rw-r--r--src/network/networkd-dhcp4.c185
-rw-r--r--src/network/networkd-dhcp6.c341
-rw-r--r--src/network/networkd-fdb.c62
-rw-r--r--src/network/networkd-fdb.h5
-rw-r--r--src/network/networkd-gperf.gperf2
-rw-r--r--src/network/networkd-ipv4ll.c34
-rw-r--r--src/network/networkd-ipv6-proxy-ndp.c42
-rw-r--r--src/network/networkd-ipv6-proxy-ndp.h4
-rw-r--r--src/network/networkd-link.c748
-rw-r--r--src/network/networkd-link.h34
-rw-r--r--src/network/networkd-lldp-tx.c5
-rw-r--r--src/network/networkd-lldp-tx.h3
-rw-r--r--src/network/networkd-manager-bus.c1
-rw-r--r--src/network/networkd-manager.c372
-rw-r--r--src/network/networkd-manager.h26
-rw-r--r--src/network/networkd-ndisc.c324
-rw-r--r--src/network/networkd-ndisc.h1
-rw-r--r--src/network/networkd-neighbor.c237
-rw-r--r--src/network/networkd-neighbor.h37
-rw-r--r--src/network/networkd-network-gperf.gperf19
-rw-r--r--src/network/networkd-network.c329
-rw-r--r--src/network/networkd-network.h34
-rw-r--r--src/network/networkd-radv.c60
-rw-r--r--src/network/networkd-route.c204
-rw-r--r--src/network/networkd-route.h6
-rw-r--r--src/network/networkd-routing-policy-rule.c492
-rw-r--r--src/network/networkd-routing-policy-rule.h27
-rw-r--r--src/network/networkd-util.h1
-rw-r--r--src/network/networkd.c106
-rw-r--r--src/network/networkd.conf16
-rw-r--r--src/network/systemd-networkd.pkla2
-rw-r--r--src/network/systemd-networkd.rules4
-rw-r--r--src/network/test-network-tables.c7
-rw-r--r--src/network/test-network.c29
-rw-r--r--src/network/test-networkd-conf.c70
-rw-r--r--src/network/test-routing-policy-rule.c12
-rw-r--r--src/network/wait-online/link.h1
-rw-r--r--src/network/wait-online/manager.c15
-rw-r--r--src/network/wait-online/manager.h1
-rw-r--r--src/network/wait-online/wait-online.c71
-rw-r--r--src/notify/notify.c100
-rw-r--r--src/nspawn/nspawn-cgroup.c428
-rw-r--r--src/nspawn/nspawn-cgroup.h3
-rw-r--r--src/nspawn/nspawn-expose-ports.c4
-rw-r--r--src/nspawn/nspawn-gperf.gperf1
-rw-r--r--src/nspawn/nspawn-mount.c500
-rw-r--r--src/nspawn/nspawn-mount.h8
-rw-r--r--src/nspawn/nspawn-network.c49
-rw-r--r--src/nspawn/nspawn-patch-uid.c4
-rw-r--r--src/nspawn/nspawn-patch-uid.h1
-rw-r--r--src/nspawn/nspawn-register.c117
-rw-r--r--src/nspawn/nspawn-register.h3
-rw-r--r--src/nspawn/nspawn-seccomp.c18
-rw-r--r--src/nspawn/nspawn-settings.c11
-rw-r--r--src/nspawn/nspawn-settings.h9
-rw-r--r--src/nspawn/nspawn-setuid.c77
-rw-r--r--src/nspawn/nspawn-stub-pid1.c5
-rw-r--r--src/nspawn/nspawn.c730
-rw-r--r--src/nspawn/test-patch-uid.c5
-rw-r--r--src/nss-myhostname/nss-myhostname.c16
-rw-r--r--src/nss-mymachines/nss-mymachines.c132
-rw-r--r--src/nss-resolve/nss-resolve.c116
-rw-r--r--src/nss-systemd/nss-systemd.c74
-rw-r--r--src/partition/growfs.c78
-rw-r--r--src/partition/makefs.c45
-rw-r--r--src/path/path.c40
-rw-r--r--src/portable/portable.c96
-rw-r--r--src/portable/portable.h3
-rw-r--r--src/portable/portablectl.c81
-rw-r--r--src/portable/portabled-bus.c5
-rw-r--r--src/portable/portabled-image-bus.c13
-rw-r--r--src/portable/portabled-image-bus.h1
-rw-r--r--src/portable/portabled-image.c5
-rw-r--r--src/portable/portabled-image.h1
-rw-r--r--src/portable/portabled.c35
-rw-r--r--src/quotacheck/quotacheck.c27
-rw-r--r--src/random-seed/random-seed.c148
-rw-r--r--src/rc-local-generator/rc-local-generator.c53
-rw-r--r--src/remount-fs/remount-fs.c173
-rw-r--r--src/reply-password/reply-password.c53
-rw-r--r--src/resolve/meson.build23
-rw-r--r--src/resolve/resolv.conf1
-rw-r--r--src/resolve/resolvconf-compat.c88
-rw-r--r--src/resolve/resolvconf-compat.h1
-rw-r--r--src/resolve/resolvectl.c922
-rw-r--r--src/resolve/resolvectl.h9
-rw-r--r--src/resolve/resolved-bus.c18
-rw-r--r--src/resolve/resolved-conf.h1
-rw-r--r--src/resolve/resolved-dns-answer.c94
-rw-r--r--src/resolve/resolved-dns-answer.h4
-rw-r--r--src/resolve/resolved-dns-cache.c19
-rw-r--r--src/resolve/resolved-dns-dnssec.c56
-rw-r--r--src/resolve/resolved-dns-packet.c38
-rw-r--r--src/resolve/resolved-dns-packet.h10
-rw-r--r--src/resolve/resolved-dns-query.c43
-rw-r--r--src/resolve/resolved-dns-question.c31
-rw-r--r--src/resolve/resolved-dns-rr.c83
-rw-r--r--src/resolve/resolved-dns-rr.h11
-rw-r--r--src/resolve/resolved-dns-scope.c257
-rw-r--r--src/resolve/resolved-dns-scope.h10
-rw-r--r--src/resolve/resolved-dns-search-domain.c25
-rw-r--r--src/resolve/resolved-dns-server.c128
-rw-r--r--src/resolve/resolved-dns-server.h21
-rw-r--r--src/resolve/resolved-dns-stream.c265
-rw-r--r--src/resolve/resolved-dns-stream.h22
-rw-r--r--src/resolve/resolved-dns-stub.c50
-rw-r--r--src/resolve/resolved-dns-synthesize.c1
-rw-r--r--src/resolve/resolved-dns-transaction.c156
-rw-r--r--src/resolve/resolved-dns-trust-anchor.c32
-rw-r--r--src/resolve/resolved-dnssd-bus.c1
-rw-r--r--src/resolve/resolved-dnssd-bus.h1
-rw-r--r--src/resolve/resolved-dnssd.c16
-rw-r--r--src/resolve/resolved-dnssd.h1
-rw-r--r--src/resolve/resolved-dnstls-gnutls.c205
-rw-r--r--src/resolve/resolved-dnstls-gnutls.h20
-rw-r--r--src/resolve/resolved-dnstls-openssl.c353
-rw-r--r--src/resolve/resolved-dnstls-openssl.h21
-rw-r--r--src/resolve/resolved-dnstls.h32
-rw-r--r--src/resolve/resolved-etc-hosts.c348
-rw-r--r--src/resolve/resolved-etc-hosts.h17
-rw-r--r--src/resolve/resolved-gperf.gperf1
-rw-r--r--src/resolve/resolved-link-bus.c52
-rw-r--r--src/resolve/resolved-link-bus.h1
-rw-r--r--src/resolve/resolved-link.c65
-rw-r--r--src/resolve/resolved-link.h6
-rw-r--r--src/resolve/resolved-llmnr.c389
-rw-r--r--src/resolve/resolved-manager.c182
-rw-r--r--src/resolve/resolved-manager.h18
-rw-r--r--src/resolve/resolved-mdns.c239
-rw-r--r--src/resolve/resolved-mdns.h1
-rw-r--r--src/resolve/resolved-resolv-conf.c74
-rw-r--r--src/resolve/resolved-resolv-conf.h2
-rw-r--r--src/resolve/resolved.c81
-rw-r--r--src/resolve/resolved.conf.in3
-rw-r--r--src/resolve/test-dns-packet.c5
-rw-r--r--src/resolve/test-dnssec.c2
-rw-r--r--src/resolve/test-resolve-tables.c7
-rw-r--r--src/resolve/test-resolved-etc-hosts.c151
-rw-r--r--src/rfkill/rfkill.c321
-rw-r--r--src/run-generator/run-generator.c136
-rw-r--r--src/run/run.c469
-rw-r--r--src/shared/acl-util.c9
-rw-r--r--src/shared/acpi-fpdt.h1
-rw-r--r--src/shared/ask-password-api.c166
-rw-r--r--src/shared/ask-password-api.h2
-rw-r--r--src/shared/barrier.c (renamed from src/basic/barrier.c)8
-rw-r--r--src/shared/barrier.h (renamed from src/basic/barrier.h)3
-rw-r--r--src/shared/base-filesystem.h1
-rw-r--r--src/shared/bitmap.c (renamed from src/basic/bitmap.c)2
-rw-r--r--src/shared/bitmap.h (renamed from src/basic/bitmap.h)1
-rw-r--r--src/shared/blkid-util.h (renamed from src/basic/blkid-util.h)6
-rw-r--r--src/shared/boot-timestamps.h1
-rw-r--r--src/shared/bootspec.c161
-rw-r--r--src/shared/bootspec.h19
-rw-r--r--src/shared/bpf-program.c (renamed from src/basic/bpf-program.c)23
-rw-r--r--src/shared/bpf-program.h (renamed from src/basic/bpf-program.h)0
-rw-r--r--src/shared/bus-unit-util.c222
-rw-r--r--src/shared/bus-util.c324
-rw-r--r--src/shared/bus-util.h7
-rw-r--r--src/shared/calendarspec.c (renamed from src/basic/calendarspec.c)27
-rw-r--r--src/shared/calendarspec.h (renamed from src/basic/calendarspec.h)0
-rw-r--r--src/shared/cgroup-show.c36
-rw-r--r--src/shared/cgroup-show.h1
-rw-r--r--src/shared/clean-ipc.c51
-rw-r--r--src/shared/clock-util.c (renamed from src/basic/clock-util.c)39
-rw-r--r--src/shared/clock-util.h (renamed from src/basic/clock-util.h)0
-rw-r--r--src/shared/condition.c30
-rw-r--r--src/shared/conf-parser.c152
-rw-r--r--src/shared/conf-parser.h1
-rw-r--r--src/shared/cpu-set-util.c (renamed from src/basic/cpu-set-util.c)3
-rw-r--r--src/shared/cpu-set-util.h (renamed from src/basic/cpu-set-util.h)4
-rw-r--r--src/shared/crypt-util.c28
-rw-r--r--src/shared/crypt-util.h (renamed from src/basic/crypt-util.h)1
-rw-r--r--src/shared/daemon-util.h22
-rw-r--r--src/shared/dev-setup.c59
-rw-r--r--src/shared/dev-setup.h2
-rw-r--r--src/shared/dissect-image.c333
-rw-r--r--src/shared/dissect-image.h1
-rw-r--r--src/shared/dns-domain.c117
-rw-r--r--src/shared/dns-domain.h35
-rw-r--r--src/shared/dropin.c8
-rw-r--r--src/shared/efivars.c306
-rw-r--r--src/shared/efivars.h29
-rw-r--r--src/shared/enable-mempool.c5
-rw-r--r--src/shared/env-file-label.c21
-rw-r--r--src/shared/env-file-label.h8
-rw-r--r--src/shared/exec-util.c (renamed from src/basic/exec-util.c)69
-rw-r--r--src/shared/exec-util.h (renamed from src/basic/exec-util.h)4
-rw-r--r--src/shared/exit-status.c (renamed from src/basic/exit-status.c)10
-rw-r--r--src/shared/exit-status.h (renamed from src/basic/exit-status.h)2
-rw-r--r--src/shared/fdset.c11
-rw-r--r--src/shared/fileio-label.c (renamed from src/basic/fileio-label.c)32
-rw-r--r--src/shared/fileio-label.h (renamed from src/basic/fileio-label.h)14
-rw-r--r--src/shared/firewall-util.c8
-rw-r--r--src/shared/format-table.c (renamed from src/basic/format-table.c)558
-rw-r--r--src/shared/format-table.h (renamed from src/basic/format-table.h)16
-rwxr-xr-xsrc/shared/generate-ip-protocol-list.sh (renamed from src/basic/generate-socket-protocol-list.sh)0
-rw-r--r--src/shared/generator.c50
-rw-r--r--src/shared/generator.h18
-rw-r--r--src/shared/id128-print.c65
-rw-r--r--src/shared/id128-print.h10
-rw-r--r--src/shared/import-util.c2
-rw-r--r--src/shared/initreq.h7
-rw-r--r--src/shared/install-printf.c15
-rw-r--r--src/shared/install-printf.h2
-rw-r--r--src/shared/install.c479
-rw-r--r--src/shared/ip-protocol-list.c67
-rw-r--r--src/shared/ip-protocol-list.h6
-rw-r--r--src/shared/ip-protocol-to-name.awk (renamed from src/basic/socket-protocol-to-name.awk)2
-rw-r--r--src/shared/journal-importer.c (renamed from src/basic/journal-importer.c)33
-rw-r--r--src/shared/journal-importer.h (renamed from src/basic/journal-importer.h)7
-rw-r--r--src/shared/journal-util.h1
-rw-r--r--src/shared/json-internal.h63
-rw-r--r--src/shared/json.c3481
-rw-r--r--src/shared/json.h286
-rw-r--r--src/shared/linux/libbpf.h10
-rw-r--r--src/shared/lockfile-util.c (renamed from src/basic/lockfile-util.c)0
-rw-r--r--src/shared/lockfile-util.h (renamed from src/basic/lockfile-util.h)5
-rw-r--r--src/shared/logs-show.c410
-rw-r--r--src/shared/logs-show.h2
-rw-r--r--src/shared/machine-image.c36
-rw-r--r--src/shared/machine-image.h9
-rw-r--r--src/shared/machine-pool.c361
-rw-r--r--src/shared/machine-pool.h6
-rw-r--r--src/shared/main-func.h34
-rw-r--r--src/shared/meson.build124
-rw-r--r--src/shared/module-util.c72
-rw-r--r--src/shared/module-util.h (renamed from src/basic/module-util.h)2
-rw-r--r--src/shared/mount-util.c (renamed from src/basic/mount-util.c)409
-rw-r--r--src/shared/mount-util.h (renamed from src/basic/mount-util.h)26
-rw-r--r--src/shared/nscd-flush.c151
-rw-r--r--src/shared/nscd-flush.h4
-rw-r--r--src/shared/nsflags.c2
-rw-r--r--src/shared/nsflags.h4
-rw-r--r--src/shared/os-util.c (renamed from src/basic/os-util.c)10
-rw-r--r--src/shared/os-util.h (renamed from src/basic/os-util.h)2
-rw-r--r--src/shared/output-mode.c19
-rw-r--r--src/shared/output-mode.h8
-rw-r--r--src/shared/pager.c (renamed from src/basic/pager.c)140
-rw-r--r--src/shared/pager.h (renamed from src/basic/pager.h)7
-rw-r--r--src/shared/path-lookup.c102
-rw-r--r--src/shared/path-lookup.h6
-rw-r--r--src/shared/pretty-print.c247
-rw-r--r--src/shared/pretty-print.h17
-rw-r--r--src/shared/ptyfwd.c64
-rw-r--r--src/shared/ptyfwd.h4
-rw-r--r--src/shared/reboot-util.c (renamed from src/basic/reboot-util.c)0
-rw-r--r--src/shared/reboot-util.h (renamed from src/basic/reboot-util.h)0
-rw-r--r--src/shared/seccomp-util.c101
-rw-r--r--src/shared/seccomp-util.h6
-rw-r--r--src/shared/securebits-util.c (renamed from src/basic/securebits-util.c)1
-rw-r--r--src/shared/securebits-util.h (renamed from src/basic/securebits-util.h)3
-rw-r--r--src/shared/serialize.c214
-rw-r--r--src/shared/serialize.h25
-rw-r--r--src/shared/sleep-config.c137
-rw-r--r--src/shared/sleep-config.h2
-rw-r--r--src/shared/specifier.c43
-rw-r--r--src/shared/specifier.h30
-rw-r--r--src/shared/switch-root.c4
-rw-r--r--src/shared/switch-root.h4
-rw-r--r--src/shared/sysctl-util.c2
-rw-r--r--src/shared/tests.c159
-rw-r--r--src/shared/tests.h11
-rw-r--r--src/shared/tmpfile-util-label.c26
-rw-r--r--src/shared/tmpfile-util-label.h10
-rw-r--r--src/shared/tomoyo-util.h1
-rw-r--r--src/shared/udev-util.c185
-rw-r--r--src/shared/udev-util.h36
-rw-r--r--src/shared/uid-range.c21
-rw-r--r--src/shared/verbs.c (renamed from src/basic/verbs.c)22
-rw-r--r--src/shared/verbs.h (renamed from src/basic/verbs.h)2
-rw-r--r--src/shared/vlan-util.c3
-rw-r--r--src/shared/web-util.c (renamed from src/basic/web-util.c)14
-rw-r--r--src/shared/web-util.h (renamed from src/basic/web-util.h)0
-rw-r--r--src/shared/xml.c (renamed from src/basic/xml.c)0
-rw-r--r--src/shared/xml.h (renamed from src/basic/xml.h)0
-rw-r--r--src/sleep/sleep.c246
-rw-r--r--src/socket-proxy/socket-proxyd.c146
-rw-r--r--src/stdio-bridge/stdio-bridge.c170
-rw-r--r--src/sulogin-shell/sulogin-shell.c21
-rw-r--r--src/sysctl/sysctl.c76
-rw-r--r--src/system-update-generator/system-update-generator.c36
-rw-r--r--src/systemctl/systemctl.c1108
-rw-r--r--src/systemd/_sd-common.h11
-rw-r--r--src/systemd/meson.build51
-rw-r--r--src/systemd/sd-bus-protocol.h1
-rw-r--r--src/systemd/sd-bus-vtable.h1
-rw-r--r--src/systemd/sd-bus.h12
-rw-r--r--src/systemd/sd-daemon.h1
-rw-r--r--src/systemd/sd-device.h31
-rw-r--r--src/systemd/sd-dhcp-client.h10
-rw-r--r--src/systemd/sd-dhcp-lease.h1
-rw-r--r--src/systemd/sd-dhcp6-client.h13
-rw-r--r--src/systemd/sd-event.h8
-rw-r--r--src/systemd/sd-id128.h16
-rw-r--r--src/systemd/sd-journal.h1
-rw-r--r--src/systemd/sd-lldp.h10
-rw-r--r--src/systemd/sd-login.h1
-rw-r--r--src/systemd/sd-messages.h25
-rw-r--r--src/systemd/sd-ndisc.h6
-rw-r--r--src/systemd/sd-netlink.h49
-rw-r--r--src/systemd/sd-network.h3
-rw-r--r--src/systemd/sd-path.h3
-rw-r--r--src/systemd/sd-radv.h9
-rw-r--r--src/systemd/sd-resolve.h17
-rw-r--r--src/systemd/sd-utf8.h1
-rw-r--r--src/sysusers/sysusers.c290
-rw-r--r--src/sysv-generator/sysv-generator.c60
-rw-r--r--src/test/meson.build99
-rw-r--r--src/test/test-acl-util.c4
-rw-r--r--src/test/test-af-list.c6
-rw-r--r--src/test/test-architecture.c5
-rw-r--r--src/test/test-arphrd-list.c8
-rw-r--r--src/test/test-ask-password-api.c6
-rw-r--r--src/test/test-async.c4
-rw-r--r--src/test/test-barrier.c15
-rw-r--r--src/test/test-boot-timestamps.c38
-rw-r--r--src/test/test-bpf.c31
-rw-r--r--src/test/test-bus-util.c39
-rw-r--r--src/test/test-capability.c36
-rw-r--r--src/test/test-cgroup-mask.c105
-rw-r--r--src/test/test-cgroup-util.c18
-rw-r--r--src/test/test-cgroup.c2
-rw-r--r--src/test/test-chown-rec.c158
-rw-r--r--src/test/test-clock.c14
-rw-r--r--src/test/test-condition.c11
-rw-r--r--src/test/test-conf-files.c66
-rw-r--r--src/test/test-conf-parser.c75
-rw-r--r--src/test/test-copy.c4
-rw-r--r--src/test/test-daemon.c2
-rw-r--r--src/test/test-date.c5
-rw-r--r--src/test/test-dev-setup.c62
-rw-r--r--src/test/test-device-nodes.c3
-rw-r--r--src/test/test-dissect-image.c3
-rw-r--r--src/test/test-dns-domain.c182
-rw-r--r--src/test/test-emergency-action.c51
-rw-r--r--src/test/test-engine.c18
-rw-r--r--src/test/test-env-util.c76
-rw-r--r--src/test/test-escape.c5
-rw-r--r--src/test/test-exec-util.c49
-rw-r--r--src/test/test-execute.c189
-rw-r--r--src/test/test-fd-util.c5
-rw-r--r--src/test/test-fdset.c2
-rw-r--r--src/test/test-fileio.c220
-rw-r--r--src/test/test-firewall-util.c3
-rw-r--r--src/test/test-format-table.c28
-rw-r--r--src/test/test-fs-util.c213
-rw-r--r--src/test/test-glob-util.c2
-rw-r--r--src/test/test-hash.c9
-rw-r--r--src/test/test-hashmap-plain.c170
-rw-r--r--src/test/test-hashmap.c20
-rw-r--r--src/test/test-helper.c6
-rw-r--r--src/test/test-helper.h2
-rw-r--r--src/test/test-hexdecoct.c2
-rw-r--r--src/test/test-hostname-util.c9
-rw-r--r--src/test/test-id128.c2
-rw-r--r--src/test/test-in-addr-util.c82
-rw-r--r--src/test/test-install-root.c63
-rw-r--r--src/test/test-install.c4
-rw-r--r--src/test/test-ip-protocol-list.c64
-rw-r--r--src/test/test-ipcrm.c12
-rw-r--r--src/test/test-job-type.c2
-rw-r--r--src/test/test-journal-importer.c13
-rw-r--r--src/test/test-json.c462
-rw-r--r--src/test/test-libudev.c146
-rw-r--r--src/test/test-locale-util.c31
-rw-r--r--src/test/test-log.c26
-rw-r--r--src/test/test-loopback.c5
-rw-r--r--src/test/test-mount-util.c247
-rw-r--r--src/test/test-mountpoint-util.c268
-rw-r--r--src/test/test-namespace.c22
-rw-r--r--src/test/test-netlink-manual.c15
-rw-r--r--src/test/test-ns.c3
-rw-r--r--src/test/test-nscd-flush.c20
-rw-r--r--src/test/test-nss.c43
-rw-r--r--src/test/test-os-util.c5
-rw-r--r--src/test/test-parse-util.c2
-rw-r--r--src/test/test-path-lookup.c7
-rw-r--r--src/test/test-path-util.c130
-rw-r--r--src/test/test-path.c42
-rw-r--r--src/test/test-pretty-print.c40
-rw-r--r--src/test/test-prioq.c102
-rw-r--r--src/test/test-proc-cmdline.c136
-rw-r--r--src/test/test-process-util.c42
-rw-r--r--src/test/test-random-util.c39
-rw-r--r--src/test/test-rlimit-util.c1
-rw-r--r--src/test/test-sched-prio.c16
-rw-r--r--src/test/test-sd-hwdb.c74
-rw-r--r--src/test/test-seccomp.c205
-rw-r--r--src/test/test-selinux.c4
-rw-r--r--src/test/test-serialize.c208
-rw-r--r--src/test/test-set-disable-mempool.c53
-rw-r--r--src/test/test-set.c19
-rw-r--r--src/test/test-sigbus.c18
-rw-r--r--src/test/test-sleep.c53
-rw-r--r--src/test/test-socket-util.c412
-rw-r--r--src/test/test-specifier.c3
-rw-r--r--src/test/test-stat-util.c95
-rw-r--r--src/test/test-static-destruct.c34
-rw-r--r--src/test/test-string-util.c86
-rw-r--r--src/test/test-strip-tab-ansi.c1
-rw-r--r--src/test/test-strv.c238
-rwxr-xr-xsrc/test/test-systemd-tmpfiles.py19
-rw-r--r--src/test/test-tables.c6
-rw-r--r--src/test/test-terminal-util.c39
-rw-r--r--src/test/test-time-util.c74
-rw-r--r--src/test/test-tmpfiles.c5
-rw-r--r--src/test/test-udev.c121
-rw-r--r--src/test/test-umount.c22
-rw-r--r--src/test/test-unit-file.c53
-rw-r--r--src/test/test-unit-name.c23
-rw-r--r--src/test/test-user-util.c6
-rw-r--r--src/test/test-utf8.c47
-rw-r--r--src/test/test-util.c81
-rw-r--r--src/test/test-watch-pid.c21
-rw-r--r--src/test/test-watchdog.c8
-rw-r--r--src/test/test-xattr-util.c7
-rw-r--r--src/time-wait-sync/time-wait-sync.c59
-rw-r--r--src/timedate/timedatectl.c108
-rw-r--r--src/timedate/timedated.c209
-rw-r--r--src/timesync/test-timesync.c4
-rw-r--r--src/timesync/timesyncd-bus.c8
-rw-r--r--src/timesync/timesyncd-conf.h1
-rw-r--r--src/timesync/timesyncd-manager.c33
-rw-r--r--src/timesync/timesyncd-manager.h5
-rw-r--r--src/timesync/timesyncd-server.h1
-rw-r--r--src/timesync/timesyncd.c72
-rw-r--r--src/tmpfiles/tmpfiles.c1553
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c136
-rw-r--r--src/udev/ata_id/ata_id.c80
-rw-r--r--src/udev/cdrom_id/cdrom_id.c359
-rw-r--r--src/udev/collect/collect.c479
-rw-r--r--src/udev/meson.build21
-rw-r--r--src/udev/mtd_probe/probe_smartmedia.c28
-rw-r--r--src/udev/net/ethtool-util.c130
-rw-r--r--src/udev/net/ethtool-util.h21
-rw-r--r--src/udev/net/link-config-gperf.gperf1
-rw-r--r--src/udev/net/link-config.c125
-rw-r--r--src/udev/net/link-config.h18
-rw-r--r--src/udev/scsi_id/scsi.h4
-rw-r--r--src/udev/scsi_id/scsi_id.c73
-rw-r--r--src/udev/scsi_id/scsi_id.h5
-rw-r--r--src/udev/scsi_id/scsi_serial.c166
-rw-r--r--src/udev/udev-builtin-blkid.c91
-rw-r--r--src/udev/udev-builtin-btrfs.c27
-rw-r--r--src/udev/udev-builtin-hwdb.c89
-rw-r--r--src/udev/udev-builtin-input_id.c77
-rw-r--r--src/udev/udev-builtin-keyboard.c182
-rw-r--r--src/udev/udev-builtin-kmod.c58
-rw-r--r--src/udev/udev-builtin-net_id.c612
-rw-r--r--src/udev/udev-builtin-net_setup_link.c33
-rw-r--r--src/udev/udev-builtin-path_id.c358
-rw-r--r--src/udev/udev-builtin-uaccess.c41
-rw-r--r--src/udev/udev-builtin-usb_id.c171
-rw-r--r--src/udev/udev-builtin.c92
-rw-r--r--src/udev/udev-builtin.h68
-rw-r--r--src/udev/udev-ctrl.c158
-rw-r--r--src/udev/udev-ctrl.h41
-rw-r--r--src/udev/udev-event.c1066
-rw-r--r--src/udev/udev-node.c519
-rw-r--r--src/udev/udev-node.h15
-rw-r--r--src/udev/udev-rules.c986
-rw-r--r--src/udev/udev-watch.c205
-rw-r--r--src/udev/udev-watch.h10
-rw-r--r--src/udev/udev.conf6
-rw-r--r--src/udev/udev.h212
-rw-r--r--src/udev/udevadm-control.c146
-rw-r--r--src/udev/udevadm-hwdb.c684
-rw-r--r--src/udev/udevadm-info.c448
-rw-r--r--src/udev/udevadm-monitor.c367
-rw-r--r--src/udev/udevadm-settle.c156
-rw-r--r--src/udev/udevadm-test-builtin.c125
-rw-r--r--src/udev/udevadm-test.c152
-rw-r--r--src/udev/udevadm-trigger.c360
-rw-r--r--src/udev/udevadm-util.c68
-rw-r--r--src/udev/udevadm-util.h13
-rw-r--r--src/udev/udevadm.c170
-rw-r--r--src/udev/udevadm.h18
-rw-r--r--src/udev/udevd.c1372
-rw-r--r--src/update-done/update-done.c7
-rw-r--r--src/update-utmp/update-utmp.c29
-rw-r--r--src/user-sessions/user-sessions.c32
-rw-r--r--src/vconsole/90-vconsole.rules.in2
-rw-r--r--src/vconsole/vconsole-setup.c67
-rw-r--r--src/veritysetup/veritysetup-generator.c61
-rw-r--r--src/veritysetup/veritysetup.c113
-rw-r--r--src/volatile-root/volatile-root.c71
-rw-r--r--sysctl.d/50-default.conf5
-rw-r--r--sysusers.d/systemd.conf.m49
-rw-r--r--test/README.testsuite73
-rw-r--r--test/TEST-01-BASIC/Makefile7
-rwxr-xr-xtest/TEST-01-BASIC/test.sh1
-rwxr-xr-xtest/TEST-02-CRYPTSETUP/test.sh8
-rwxr-xr-xtest/TEST-03-JOBS/test.sh8
-rwxr-xr-xtest/TEST-04-JOURNAL/test.sh9
-rwxr-xr-xtest/TEST-05-RLIMITS/test.sh9
-rwxr-xr-xtest/TEST-06-SELINUX/test.sh1
-rwxr-xr-xtest/TEST-07-ISSUE-1981/test.sh9
-rwxr-xr-xtest/TEST-08-ISSUE-2730/test.sh2
-rwxr-xr-xtest/TEST-09-ISSUE-2691/test.sh6
-rwxr-xr-xtest/TEST-10-ISSUE-2467/test.sh2
-rwxr-xr-xtest/TEST-11-ISSUE-3166/test.sh10
-rwxr-xr-xtest/TEST-12-ISSUE-3171/test.sh9
-rw-r--r--test/TEST-13-NSPAWN-SMOKE/Makefile5
-rwxr-xr-xtest/TEST-13-NSPAWN-SMOKE/test.sh18
-rwxr-xr-xtest/TEST-14-MACHINE-ID/test.sh3
-rw-r--r--test/TEST-15-DROPIN/testsuite.service1
l---------[-rw-r--r--]test/TEST-17-UDEV-WANTS/Makefile5
-rwxr-xr-xtest/TEST-17-UDEV-WANTS/test.sh2
l---------[-rw-r--r--]test/TEST-18-FAILUREACTION/Makefile5
-rwxr-xr-xtest/TEST-18-FAILUREACTION/test.sh7
l---------[-rw-r--r--]test/TEST-19-DELEGATE/Makefile5
-rwxr-xr-xtest/TEST-19-DELEGATE/test.sh10
-rwxr-xr-xtest/TEST-19-DELEGATE/testsuite.sh21
l---------[-rw-r--r--]test/TEST-20-MAINPIDGAMES/Makefile5
-rwxr-xr-xtest/TEST-20-MAINPIDGAMES/test.sh8
l---------[-rw-r--r--]test/TEST-21-SYSUSERS/Makefile5
-rwxr-xr-xtest/TEST-21-SYSUSERS/test.sh1
l---------[-rw-r--r--]test/TEST-22-TMPFILES/Makefile5
-rwxr-xr-xtest/TEST-22-TMPFILES/test-02.sh95
-rwxr-xr-xtest/TEST-22-TMPFILES/test-03.sh236
-rwxr-xr-xtest/TEST-22-TMPFILES/test-04.sh44
-rwxr-xr-xtest/TEST-22-TMPFILES/test-05.sh45
-rwxr-xr-xtest/TEST-22-TMPFILES/test-06.sh38
-rwxr-xr-xtest/TEST-22-TMPFILES/test-07.sh31
-rwxr-xr-xtest/TEST-22-TMPFILES/test.sh2
-rw-r--r--test/TEST-22-TMPFILES/testsuite.service4
l---------test/TEST-23-TYPE-EXEC/Makefile1
-rwxr-xr-xtest/TEST-23-TYPE-EXEC/test.sh50
-rwxr-xr-xtest/TEST-23-TYPE-EXEC/testsuite.sh28
l---------test/TEST-24-UNIT-TESTS/Makefile1
-rwxr-xr-xtest/TEST-24-UNIT-TESTS/test.sh106
-rwxr-xr-xtest/TEST-24-UNIT-TESTS/testsuite.sh34
l---------test/TEST-25-IMPORT/Makefile1
-rwxr-xr-xtest/TEST-25-IMPORT/test.sh43
-rwxr-xr-xtest/TEST-25-IMPORT/testsuite.sh128
l---------test/TEST-26-SETENV/Makefile1
-rwxr-xr-xtest/TEST-26-SETENV/test.sh42
-rwxr-xr-xtest/TEST-26-SETENV/testsuite.sh33
l---------test/TEST-27-STDOUTFILE/Makefile1
-rwxr-xr-xtest/TEST-27-STDOUTFILE/test.sh52
-rwxr-xr-xtest/TEST-27-STDOUTFILE/testsuite.sh40
-rw-r--r--test/daughter.service1
-rw-r--r--test/fuzz-regressions/.gitattributes1
-rw-r--r--test/fuzz-regressions/meson.build34
-rw-r--r--test/fuzz/.gitattributes (renamed from test/fuzz-corpus/.gitattributes)0
-rw-r--r--test/fuzz/fuzz-bus-message/crash-26bba7182dedc8848939931d9fcefcb7922f2e56bin0 -> 157 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/crash-29ed3c202e0ffade3cad42c8bbeb6cc68a21eb8ebin0 -> 51 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/crash-32bf69483cbd4f2e6d46c25a2f92a472109aee45bin0 -> 89 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/crash-37449529b1ad867f0c2671fa80aca5d7812a2b70bin0 -> 534 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/crash-4162a61a79e4c5a832ca5232212f75fa560a1f75bin0 -> 534 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/crash-4f0211eb269e28db941961061494bfdbf3345e54bin0 -> 143 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/crash-603dfd98252375ac7dbced53c2ec312671939a36bin0 -> 40 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/crash-b88ad9ecf4aacf4a0caca5b5543953265367f084bin0 -> 32 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/crash-c1b37b4729b42c0c05b23cba4eed5d8102498a1ebin0 -> 93 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/crash-d8f3941c74219b4c03532c9b244d5ea539c61af5bin0 -> 41 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/crash-e1b811da5ca494e494b77c6bd8e1c2f2989425c5bin0 -> 28 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/leak-c09c0e2256d43bc5e2d02748c8d8760e7bc25d20bin0 -> 534 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/message1bin0 -> 534 bytes
-rw-r--r--test/fuzz/fuzz-bus-message/timeout-08ee8f6446a4064db064e8e0b3d220147f7d0b5bbin0 -> 534 bytes
-rw-r--r--test/fuzz/fuzz-catalog/clusterfuzz-testcase-minimized-fuzz-catalog-56744752788275202
-rw-r--r--test/fuzz/fuzz-catalog/systemd.pl.catalog390
-rw-r--r--test/fuzz/fuzz-dhcp-server/discover-existing (renamed from test/fuzz-corpus/dhcp-server/discover-existing)bin248 -> 248 bytes
-rw-r--r--test/fuzz/fuzz-dhcp-server/discover-new (renamed from test/fuzz-corpus/dhcp-server/discover-new)bin247 -> 247 bytes
-rw-r--r--test/fuzz/fuzz-dhcp-server/release (renamed from test/fuzz-corpus/dhcp-server/release)bin248 -> 248 bytes
-rw-r--r--test/fuzz/fuzz-dhcp-server/request-existing (renamed from test/fuzz-corpus/dhcp-server/request-existing)bin260 -> 260 bytes
-rw-r--r--test/fuzz/fuzz-dhcp-server/request-new (renamed from test/fuzz-corpus/dhcp-server/request-new)bin259 -> 259 bytes
-rw-r--r--test/fuzz/fuzz-dhcp-server/request-reboot (renamed from test/fuzz-corpus/dhcp-server/request-reboot)bin254 -> 254 bytes
-rw-r--r--test/fuzz/fuzz-dhcp-server/request-renew (renamed from test/fuzz-corpus/dhcp-server/request-renew)bin248 -> 248 bytes
-rw-r--r--test/fuzz/fuzz-dhcp6-client/crash-4003c06fce43a11fbd22f02584df2807ac333eaebin0 -> 14 bytes
-rw-r--r--test/fuzz/fuzz-dhcp6-client/crash-6e88fcb6b85c9436bcbe05219aa8e550194645efbin0 -> 9 bytes
-rw-r--r--test/fuzz/fuzz-dhcp6-client/oss-fuzz-10746bin0 -> 99 bytes
-rw-r--r--test/fuzz/fuzz-dhcp6-client/oss-fuzz-11019bin0 -> 61198 bytes
-rw-r--r--test/fuzz/fuzz-dns-packet/issue-7888 (renamed from test/fuzz-regressions/fuzz-dns-packet/issue-7888)bin25 -> 25 bytes
-rw-r--r--test/fuzz/fuzz-dns-packet/oss-fuzz-5465 (renamed from test/fuzz-regressions/fuzz-dns-packet/oss-fuzz-5465)bin24 -> 24 bytes
-rw-r--r--test/fuzz/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76 (renamed from test/fuzz-regressions/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76)bin7675 -> 7675 bytes
-rw-r--r--test/fuzz/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45 (renamed from test/fuzz-regressions/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45)bin2490 -> 2490 bytes
-rw-r--r--test/fuzz/fuzz-journal-remote/invalid-ts.txt (renamed from test/fuzz-corpus/journal-remote/invalid-ts.txt)bin4657 -> 4657 bytes
-rw-r--r--test/fuzz/fuzz-journal-remote/oss-fuzz-8658bin0 -> 10 bytes
-rw-r--r--test/fuzz/fuzz-journal-remote/oss-fuzz-8659 (renamed from test/fuzz-regressions/fuzz-journal-remote/oss-fuzz-8659)0
-rw-r--r--test/fuzz/fuzz-journal-remote/oss-fuzz-8686 (renamed from test/fuzz-regressions/fuzz-journal-remote/oss-fuzz-8686)0
-rw-r--r--test/fuzz/fuzz-journal-remote/sample.txt (renamed from test/fuzz-corpus/journal-remote/sample.txt)0
-rw-r--r--test/fuzz/fuzz-journald-audit/basic1
-rw-r--r--test/fuzz/fuzz-journald-audit/crash1
-rw-r--r--test/fuzz/fuzz-journald-kmsg/basic1
-rw-r--r--test/fuzz/fuzz-journald-kmsg/crash-c6c04d83e73f3d1417bc0afce8fa81b99f955963bin0 -> 112 bytes
-rw-r--r--test/fuzz/fuzz-journald-kmsg/dev-null2
-rw-r--r--test/fuzz/fuzz-journald-kmsg/leak-ab161e601e82f1ec31d11e2cbae2747834ce9e43bin0 -> 1847 bytes
-rw-r--r--test/fuzz/fuzz-journald-kmsg/loopback2
-rw-r--r--test/fuzz/fuzz-journald-kmsg/subsystem-loopback2
-rw-r--r--test/fuzz/fuzz-journald-native-fd/basicbin0 -> 34 bytes
-rw-r--r--test/fuzz/fuzz-journald-stream/basic8
-rw-r--r--test/fuzz/fuzz-journald-syslog/github-97951
-rw-r--r--test/fuzz/fuzz-journald-syslog/github-98201
-rw-r--r--test/fuzz/fuzz-journald-syslog/github-98271
-rw-r--r--test/fuzz/fuzz-journald-syslog/github-98291
-rw-r--r--test/fuzz/fuzz-json/crash-56394414822522889
-rw-r--r--test/fuzz/fuzz-json/oss-fuzz-109089
-rw-r--r--test/fuzz/fuzz-lldp/basicbin0 -> 52 bytes
-rw-r--r--test/fuzz/fuzz-lldp/incompletebin0 -> 31 bytes
-rw-r--r--test/fuzz/fuzz-lldp/ouibin0 -> 86 bytes
-rw-r--r--test/fuzz/fuzz-ndisc-rs/oss-fuzz-10734bin0 -> 212961 bytes
-rw-r--r--test/fuzz/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1bin0 -> 53 bytes
-rw-r--r--test/fuzz/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5bbin0 -> 71 bytes
-rw-r--r--test/fuzz/fuzz-netdev-parser/11-dummy.netdev3
-rw-r--r--test/fuzz/fuzz-netdev-parser/12-dummy.netdev3
-rw-r--r--test/fuzz/fuzz-netdev-parser/21-macvlan.netdev3
-rw-r--r--test/fuzz/fuzz-netdev-parser/21-macvtap.netdev3
-rw-r--r--test/fuzz/fuzz-netdev-parser/21-vlan.netdev10
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-6rd-tunnel.netdev8
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-bond-active-backup-slave.netdev6
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-bond.netdev18
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-bridge.netdev13
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-erspan-tunnel.netdev11
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-geneve.netdev12
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-gre-tunnel.netdev7
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-gretap-tunnel.netdev7
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-ip6gre-tunnel.netdev7
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-ip6tnl-tunnel.netdev8
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-ipip-tunnel-independent.netdev9
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-ipip-tunnel.netdev8
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-ipvlan.netdev6
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-sit-tunnel.netdev7
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-tap.netdev7
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-tun.netdev7
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-vcan.netdev3
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-veth.netdev8
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-vrf.netdev6
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-vti-tunnel.netdev7
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-vti6-tunnel.netdev7
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-vxlan.netdev16
-rw-r--r--test/fuzz/fuzz-netdev-parser/25-wireguard.netdev12
-rw-r--r--test/fuzz/fuzz-netdev-parser/26-bridge.netdev3
-rw-r--r--test/fuzz/fuzz-netdev-parser/directives.netdev158
-rw-r--r--test/fuzz/fuzz-netdev-parser/github-106155
-rw-r--r--test/fuzz/fuzz-netdev-parser/github-1062910
-rw-r--r--test/fuzz/fuzz-netdev-parser/oss-fuzz-11279bin0 -> 60 bytes
-rw-r--r--test/fuzz/fuzz-netdev-parser/oss-fuzz-11280bin0 -> 76 bytes
-rw-r--r--test/fuzz/fuzz-netdev-parser/oss-fuzz-11286bin0 -> 65 bytes
-rw-r--r--test/fuzz/fuzz-netdev-parser/oss-fuzz-11287bin0 -> 60 bytes
-rw-r--r--test/fuzz/fuzz-netdev-parser/oss-fuzz-11296bin0 -> 318 bytes
-rw-r--r--test/fuzz/fuzz-netdev-parser/oss-fuzz-11297bin0 -> 77 bytes
-rw-r--r--test/fuzz/fuzz-netdev-parser/oss-fuzz-11299bin0 -> 54 bytes
-rw-r--r--test/fuzz/fuzz-netdev-parser/oss-fuzz-11324bin0 -> 79 bytes
-rw-r--r--test/fuzz/fuzz-netdev-parser/oss-fuzz-113446
-rw-r--r--test/fuzz/fuzz-network-parser/21-vlan.network5
-rw-r--r--test/fuzz/fuzz-network-parser/23-active-slave.network6
-rw-r--r--test/fuzz/fuzz-network-parser/23-bond199.network2
-rw-r--r--test/fuzz/fuzz-network-parser/23-emit-lldp.network5
-rw-r--r--test/fuzz/fuzz-network-parser/23-primary-slave.network6
-rw-r--r--test/fuzz/fuzz-network-parser/23-test1-bond199.network6
-rw-r--r--test/fuzz/fuzz-network-parser/24-lldp.network5
-rw-r--r--test/fuzz/fuzz-network-parser/24-search-domain.network7
-rw-r--r--test/fuzz/fuzz-network-parser/25-address-link-section.network5
-rw-r--r--test/fuzz/fuzz-network-parser/25-address-section-miscellaneous.network10
-rw-r--r--test/fuzz/fuzz-network-parser/25-address-section.network11
-rw-r--r--test/fuzz/fuzz-network-parser/25-fibrule-port-range.network11
-rw-r--r--test/fuzz/fuzz-network-parser/25-ipv6-address-label-section.network6
-rw-r--r--test/fuzz/fuzz-network-parser/25-link-section-unmanaged.network6
-rw-r--r--test/fuzz/fuzz-network-parser/25-neighbor-section.network6
-rw-r--r--test/fuzz/fuzz-network-parser/25-route-section.network8
-rw-r--r--test/fuzz/fuzz-network-parser/25-route-tcp-window-settings.network10
-rw-r--r--test/fuzz/fuzz-network-parser/25-route-type.network14
-rw-r--r--test/fuzz/fuzz-network-parser/25-sysctl.network10
-rw-r--r--test/fuzz/fuzz-network-parser/26-bridge-slave-interface-1.network12
-rw-r--r--test/fuzz/fuzz-network-parser/26-bridge-slave-interface-2.network5
-rw-r--r--test/fuzz/fuzz-network-parser/bridge99.network6
-rw-r--r--test/fuzz/fuzz-network-parser/configure-without-carrier.network7
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-anonymize.network16
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-critical-connection.network9
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-ipv4-dhcp-settings.network15
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-ipv4-only-ipv6-disabled.network6
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-ipv4-only.network5
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-ipv6-only.network5
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-ipv6-rapid-commit.network8
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-listen-port.network8
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-route-metric.network10
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-route-table.network10
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client-timezone-router.network8
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-client.network5
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-server-timezone-router.network13
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-server-veth-peer.network6
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-server.network12
-rw-r--r--test/fuzz/fuzz-network-parser/dhcp-v4-server-veth-peer.network6
-rw-r--r--test/fuzz/fuzz-network-parser/directives.network196
-rw-r--r--test/fuzz/fuzz-network-parser/github-106398
-rw-r--r--test/fuzz/fuzz-network-parser/gretap.network5
-rw-r--r--test/fuzz/fuzz-network-parser/gretun.network5
-rw-r--r--test/fuzz/fuzz-network-parser/ip6gretap.network5
-rw-r--r--test/fuzz/fuzz-network-parser/ip6tnl.network5
-rw-r--r--test/fuzz/fuzz-network-parser/ipip.network5
-rw-r--r--test/fuzz/fuzz-network-parser/ipv6-prefix-veth.network5
-rw-r--r--test/fuzz/fuzz-network-parser/ipv6-prefix.network10
-rw-r--r--test/fuzz/fuzz-network-parser/ipvlan.network5
-rw-r--r--test/fuzz/fuzz-network-parser/macvlan.network5
-rw-r--r--test/fuzz/fuzz-network-parser/macvtap.network5
-rw-r--r--test/fuzz/fuzz-network-parser/oss-fuzz-112852
-rw-r--r--test/fuzz/fuzz-network-parser/oss-fuzz-113022
-rw-r--r--test/fuzz/fuzz-network-parser/oss-fuzz-113145
-rw-r--r--test/fuzz/fuzz-network-parser/oss-fuzz-113452
-rw-r--r--test/fuzz/fuzz-network-parser/routing-policy-rule.network10
-rw-r--r--test/fuzz/fuzz-network-parser/sit.network5
-rw-r--r--test/fuzz/fuzz-network-parser/test-static.network6
-rw-r--r--test/fuzz/fuzz-network-parser/vti.network5
-rw-r--r--test/fuzz/fuzz-network-parser/vti6.network5
-rw-r--r--test/fuzz/fuzz-network-parser/vxlan.network5
-rw-r--r--test/fuzz/fuzz-udev-rules/50-udev-default.rules86
-rw-r--r--test/fuzz/fuzz-udev-rules/60-block.rules11
-rw-r--r--test/fuzz/fuzz-udev-rules/60-cdrom_id.rules29
-rw-r--r--test/fuzz/fuzz-udev-rules/60-drm.rules8
-rw-r--r--test/fuzz/fuzz-udev-rules/60-evdev.rules23
-rw-r--r--test/fuzz/fuzz-udev-rules/60-input-id.rules8
-rw-r--r--test/fuzz/fuzz-udev-rules/60-persistent-alsa.rules14
-rw-r--r--test/fuzz/fuzz-udev-rules/60-persistent-input.rules42
-rw-r--r--test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules36
-rw-r--r--test/fuzz/fuzz-udev-rules/60-persistent-storage.rules109
-rw-r--r--test/fuzz/fuzz-udev-rules/60-persistent-v4l.rules20
-rw-r--r--test/fuzz/fuzz-udev-rules/60-sensor.rules18
-rw-r--r--test/fuzz/fuzz-udev-rules/60-serial.rules26
-rw-r--r--test/fuzz/fuzz-udev-rules/64-btrfs.rules17
-rw-r--r--test/fuzz/fuzz-udev-rules/70-joystick.rules12
-rw-r--r--test/fuzz/fuzz-udev-rules/70-mouse.rules18
-rw-r--r--test/fuzz/fuzz-udev-rules/70-touchpad.rules13
-rw-r--r--test/fuzz/fuzz-udev-rules/75-net-description.rules14
-rw-r--r--test/fuzz/fuzz-udev-rules/75-probe_mtd.rules7
-rw-r--r--test/fuzz/fuzz-udev-rules/78-sound-card.rules96
-rw-r--r--test/fuzz/fuzz-udev-rules/80-drivers.rules13
-rw-r--r--test/fuzz/fuzz-udev-rules/80-net-setup-link.rules13
-rw-r--r--test/fuzz/fuzz-udev-rules/99-systemd.rules76
-rw-r--r--test/fuzz/fuzz-unit-file/dev-mapper-fedora_krowka\x2dswap.swap (renamed from test/fuzz-corpus/unit-file/dev-mapper-fedora_krowka\x2dswap.swap)0
-rw-r--r--test/fuzz/fuzz-unit-file/directives.service (renamed from test/fuzz-corpus/unit-file/directives.service)6
-rw-r--r--test/fuzz/fuzz-unit-file/empty.scope (renamed from test/fuzz-corpus/unit-file/empty.scope)0
-rw-r--r--test/fuzz/fuzz-unit-file/machine.slice (renamed from test/fuzz-corpus/unit-file/machine.slice)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-100076
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-11569bin0 -> 277466 bytes
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-6884 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6884)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-6885 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6885)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-6886 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6886)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-6892 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6892)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-6897 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6897)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-6897-evverx (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6897-evverx)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-6908 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6908)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-6917 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6917)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-6977 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6977)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-6977-unminimized (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6977-unminimized)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-7004 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-7004)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-8064 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-8064)0
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-8827 (renamed from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-8827)0
-rw-r--r--test/fuzz/fuzz-unit-file/proc-sys-fs-binfmt_misc.automount (renamed from test/fuzz-corpus/unit-file/proc-sys-fs-binfmt_misc.automount)0
-rw-r--r--test/fuzz/fuzz-unit-file/syslog.socket (renamed from test/fuzz-corpus/unit-file/syslog.socket)0
-rw-r--r--test/fuzz/fuzz-unit-file/systemd-ask-password-console.path (renamed from test/fuzz-corpus/unit-file/systemd-ask-password-console.path)0
-rw-r--r--test/fuzz/fuzz-unit-file/systemd-machined.service (renamed from test/fuzz-corpus/unit-file/systemd-machined.service)0
-rw-r--r--test/fuzz/fuzz-unit-file/systemd-resolved.service (renamed from test/fuzz-corpus/unit-file/systemd-resolved.service)0
-rw-r--r--test/fuzz/fuzz-unit-file/systemd-tmpfiles-clean.timer (renamed from test/fuzz-corpus/unit-file/systemd-tmpfiles-clean.timer)0
-rw-r--r--test/fuzz/fuzz-unit-file/timers.target (renamed from test/fuzz-corpus/unit-file/timers.target)0
-rw-r--r--test/fuzz/fuzz-unit-file/var-lib-machines.mount (renamed from test/fuzz-corpus/unit-file/var-lib-machines.mount)0
-rw-r--r--test/fuzz/meson.build35
-rw-r--r--test/meson.build47
-rwxr-xr-xtest/mkosi.build.networkd-test24
-rw-r--r--test/mkosi.default.networkd-test80
-rw-r--r--test/mkosi.nspawn.networkd-test2
-rwxr-xr-xtest/networkd-test.py102
-rw-r--r--test/nomem.slice5
-rw-r--r--test/nomemleaf.service9
-rwxr-xr-xtest/run-integration-tests.sh2
-rwxr-xr-xtest/run-unit-tests.py61
-rw-r--r--test/test-execute/exec-basic.service2
-rw-r--r--test/test-execute/exec-dynamicuser-fixeduser-adm.service11
-rw-r--r--test/test-execute/exec-dynamicuser-fixeduser-games.service11
-rw-r--r--test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service3
-rw-r--r--test/test-execute/exec-dynamicuser-fixeduser.service3
-rw-r--r--test/test-execute/exec-dynamicuser-statedir-migrate-step1.service1
-rw-r--r--test/test-execute/exec-dynamicuser-statedir-migrate-step2.service1
-rw-r--r--test/test-execute/exec-dynamicuser-statedir.service1
-rw-r--r--test/test-execute/exec-dynamicuser-supplementarygroups.service4
-rw-r--r--test/test-execute/exec-privatenetwork-yes.service2
-rw-r--r--test/test-execute/exec-readonlypaths.service6
-rw-r--r--test/test-execute/exec-runtimedirectory-mode.service1
-rw-r--r--test/test-execute/exec-runtimedirectory.service1
-rw-r--r--test/test-execute/exec-specifier.service8
-rw-r--r--test/test-execute/exec-specifier@.service8
-rw-r--r--test/test-execute/exec-standardoutput-append.service13
-rw-r--r--test/test-execute/exec-standardoutput-file.service13
-rw-r--r--test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service6
-rw-r--r--test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service5
-rw-r--r--test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service4
-rw-r--r--test/test-execute/exec-supplementarygroups-single-group-user.service3
-rw-r--r--test/test-execute/exec-supplementarygroups-single-group.service3
-rw-r--r--test/test-execute/exec-supplementarygroups.service3
-rw-r--r--test/test-execute/exec-systemcallfilter-with-errno-multi.service9
-rw-r--r--test/test-execute/exec-temporaryfilesystem-options.service3
-rw-r--r--test/test-execute/exec-temporaryfilesystem-ro.service3
-rw-r--r--test/test-execute/exec-temporaryfilesystem-rw.service3
-rw-r--r--test/test-execute/exec-umask-0177.service2
-rw-r--r--test/test-execute/exec-umask-default.service2
-rw-r--r--test/test-functions153
-rw-r--r--test/test-network/conf/10-dropin-test.netdev4
-rw-r--r--test/test-network/conf/10-dropin-test.netdev.d/mac.conf2
-rw-r--r--test/test-network/conf/10-dropin-test.netdev.d/name.conf2
-rw-r--r--test/test-network/conf/11-dummy.netdev3
-rw-r--r--test/test-network/conf/12-dummy.netdev3
-rw-r--r--test/test-network/conf/21-macvlan.netdev3
-rw-r--r--test/test-network/conf/21-macvtap.netdev3
-rw-r--r--test/test-network/conf/21-vlan.netdev3
-rw-r--r--test/test-network/conf/21-vlan.netdev.d/override.conf6
-rw-r--r--test/test-network/conf/21-vlan.network2
-rw-r--r--test/test-network/conf/21-vlan.network.d/override.conf2
-rw-r--r--test/test-network/conf/23-active-slave.network6
-rw-r--r--test/test-network/conf/23-bond199.network2
-rw-r--r--test/test-network/conf/23-emit-lldp.network5
-rw-r--r--test/test-network/conf/23-primary-slave.network6
-rw-r--r--test/test-network/conf/23-test1-bond199.network6
-rw-r--r--test/test-network/conf/24-lldp.network5
-rw-r--r--test/test-network/conf/24-search-domain.network7
-rw-r--r--test/test-network/conf/25-6rd-tunnel.netdev7
-rw-r--r--test/test-network/conf/25-address-link-section.network5
-rw-r--r--test/test-network/conf/25-address-section-miscellaneous.network10
-rw-r--r--test/test-network/conf/25-address-section.network11
-rw-r--r--test/test-network/conf/25-bond-active-backup-slave.netdev6
-rw-r--r--test/test-network/conf/25-bond-balanced-tlb.netdev7
-rw-r--r--test/test-network/conf/25-bond.netdev18
-rw-r--r--test/test-network/conf/25-bridge.netdev13
-rw-r--r--test/test-network/conf/25-erspan-tunnel.netdev11
-rw-r--r--test/test-network/conf/25-fibrule-invert.network10
-rw-r--r--test/test-network/conf/25-fibrule-port-range.network11
-rw-r--r--test/test-network/conf/25-geneve.netdev12
-rw-r--r--test/test-network/conf/25-gre-tunnel.netdev7
-rw-r--r--test/test-network/conf/25-gretap-tunnel.netdev7
-rw-r--r--test/test-network/conf/25-ip6gre-tunnel.netdev7
-rw-r--r--test/test-network/conf/25-ip6tnl-tunnel.netdev8
-rw-r--r--test/test-network/conf/25-ipip-tunnel-independent.netdev9
-rw-r--r--test/test-network/conf/25-ipip-tunnel.netdev8
-rw-r--r--test/test-network/conf/25-ipv6-address-label-section.network6
-rw-r--r--test/test-network/conf/25-ipvlan.netdev6
-rw-r--r--test/test-network/conf/25-isatap-tunnel.netdev8
-rw-r--r--test/test-network/conf/25-link-section-unmanaged.network6
-rw-r--r--test/test-network/conf/25-neighbor-section.network10
-rw-r--r--test/test-network/conf/25-route-gateway-on-link.network13
-rw-r--r--test/test-network/conf/25-route-gateway.network14
-rw-r--r--test/test-network/conf/25-route-reverse-order.network15
-rw-r--r--test/test-network/conf/25-route-section.network8
-rw-r--r--test/test-network/conf/25-route-tcp-window-settings.network10
-rw-r--r--test/test-network/conf/25-route-type.network14
-rw-r--r--test/test-network/conf/25-sit-tunnel.netdev7
-rw-r--r--test/test-network/conf/25-sysctl.network10
-rw-r--r--test/test-network/conf/25-tap.netdev7
-rw-r--r--test/test-network/conf/25-tun.netdev7
-rw-r--r--test/test-network/conf/25-vcan.netdev3
-rw-r--r--test/test-network/conf/25-veth.netdev8
-rw-r--r--test/test-network/conf/25-vrf.netdev6
-rw-r--r--test/test-network/conf/25-vti-tunnel.netdev7
-rw-r--r--test/test-network/conf/25-vti6-tunnel.netdev7
-rw-r--r--test/test-network/conf/25-vxlan.netdev16
-rw-r--r--test/test-network/conf/25-wireguard.netdev15
-rw-r--r--test/test-network/conf/26-bridge-slave-interface-1.network12
-rw-r--r--test/test-network/conf/26-bridge-slave-interface-2.network5
-rw-r--r--test/test-network/conf/26-bridge.netdev3
-rw-r--r--test/test-network/conf/6rd.network5
-rw-r--r--test/test-network/conf/bridge99.network6
-rw-r--r--test/test-network/conf/configure-without-carrier.network7
-rw-r--r--test/test-network/conf/dhcp-client-anonymize.network16
-rw-r--r--test/test-network/conf/dhcp-client-critical-connection.network9
-rw-r--r--test/test-network/conf/dhcp-client-ipv4-dhcp-settings.network15
-rw-r--r--test/test-network/conf/dhcp-client-ipv4-only-ipv6-disabled.network6
-rw-r--r--test/test-network/conf/dhcp-client-ipv4-only.network5
-rw-r--r--test/test-network/conf/dhcp-client-ipv6-only.network5
-rw-r--r--test/test-network/conf/dhcp-client-ipv6-rapid-commit.network8
-rw-r--r--test/test-network/conf/dhcp-client-listen-port.network8
-rw-r--r--test/test-network/conf/dhcp-client-route-metric.network10
-rw-r--r--test/test-network/conf/dhcp-client-route-table.network10
-rw-r--r--test/test-network/conf/dhcp-client-timezone-router.network10
-rw-r--r--test/test-network/conf/dhcp-client.network5
-rw-r--r--test/test-network/conf/dhcp-server-timezone-router.network13
-rw-r--r--test/test-network/conf/dhcp-server-veth-peer.network6
-rw-r--r--test/test-network/conf/dhcp-server.network12
-rw-r--r--test/test-network/conf/dhcp-v4-server-veth-peer.network6
-rw-r--r--test/test-network/conf/gretap.network5
-rw-r--r--test/test-network/conf/gretun.network5
-rw-r--r--test/test-network/conf/ip6gretap.network5
-rw-r--r--test/test-network/conf/ip6tnl.network5
-rw-r--r--test/test-network/conf/ipip.network5
-rw-r--r--test/test-network/conf/ipv6-prefix-veth.network5
-rw-r--r--test/test-network/conf/ipv6-prefix.network10
-rw-r--r--test/test-network/conf/ipvlan.network5
-rw-r--r--test/test-network/conf/isatap.network5
-rw-r--r--test/test-network/conf/macvlan.network5
-rw-r--r--test/test-network/conf/macvtap.network5
-rw-r--r--test/test-network/conf/routing-policy-rule.network10
-rw-r--r--test/test-network/conf/sit.network5
-rw-r--r--test/test-network/conf/test-static.network6
-rw-r--r--test/test-network/conf/vti.network5
-rw-r--r--test/test-network/conf/vti6.network5
-rw-r--r--test/test-network/conf/vxlan.network5
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py1179
-rwxr-xr-xtest/udev-test.pl37
-rw-r--r--tmpfiles.d/systemd.conf.m46
-rwxr-xr-xtools/catalog-report.py1
-rwxr-xr-xtools/check-directives.sh21
-rwxr-xr-xtools/check-includes.pl2
-rwxr-xr-xtools/coverity.sh6
-rw-r--r--tools/gdb-sd_dump_hashmaps.py20
-rwxr-xr-xtools/generate-gperfs.py (renamed from src/basic/generate-gperfs.py)4
-rwxr-xr-xtools/make-directive-index.py1
-rwxr-xr-xtools/make-index-md.sh32
-rwxr-xr-xtools/make-man-index.py1
-rwxr-xr-xtools/make-man-rules.py3
-rwxr-xr-xtools/meson-build.sh4
-rwxr-xr-xtools/meson-check-api-docs.sh23
-rw-r--r--tools/meson-link-test.c1
-rwxr-xr-xtools/oss-fuzz.sh13
-rwxr-xr-xtools/xml_helper.py3
-rwxr-xr-xtravis-ci/managers/debian.sh83
-rwxr-xr-xtravis-ci/managers/fedora.sh85
-rw-r--r--travis-ci/managers/travis_wait.bash61
-rw-r--r--units/boot-complete.target (renamed from units/busnames.target)4
-rwxr-xr-xunits/meson-add-wants.sh4
-rw-r--r--units/meson.build9
-rw-r--r--units/systemd-ask-password-console.path2
-rw-r--r--units/systemd-ask-password-console.service.in2
-rw-r--r--units/systemd-ask-password-wall.path2
-rw-r--r--units/systemd-bless-boot.service.in22
-rw-r--r--units/systemd-boot-check-no-failures.service.in24
-rw-r--r--units/systemd-coredump@.service.in27
-rw-r--r--units/systemd-exit.service16
-rw-r--r--units/systemd-fsck-root.service.in1
-rw-r--r--units/systemd-fsck@.service.in1
-rw-r--r--units/systemd-hostnamed.service.in29
-rw-r--r--units/systemd-initctl.service.in3
-rw-r--r--units/systemd-journal-flush.service.in5
-rw-r--r--units/systemd-journal-gatewayd.service.in25
-rw-r--r--units/systemd-journal-remote.service.in27
-rw-r--r--units/systemd-journal-upload.service.in29
-rw-r--r--units/systemd-journald.service.in35
-rw-r--r--units/systemd-localed.service.in29
-rw-r--r--units/systemd-logind.service.in29
-rw-r--r--units/systemd-machined.service.in15
-rw-r--r--units/systemd-networkd.service.in35
-rw-r--r--units/systemd-portabled.service.in2
-rw-r--r--units/systemd-poweroff.service (renamed from units/systemd-poweroff.service.in)5
-rw-r--r--units/systemd-reboot.service (renamed from units/systemd-reboot.service.in)5
-rw-r--r--units/systemd-resolved.service.in38
-rw-r--r--units/systemd-rfkill.service.in5
-rw-r--r--units/systemd-rfkill.socket3
-rw-r--r--units/systemd-timedated.service.in27
-rw-r--r--units/systemd-timesyncd.service.in38
-rw-r--r--units/systemd-tmpfiles-setup.service.in2
-rw-r--r--units/user-.slice.d/10-defaults.conf2
-rw-r--r--units/user-runtime-dir@.service.in9
-rw-r--r--[l---------]units/user/bluetooth.target14
l---------units/user/busnames.target1
-rw-r--r--units/user/meson.build2
-rw-r--r--[l---------]units/user/paths.target13
-rw-r--r--[l---------]units/user/printer.target14
-rw-r--r--[l---------]units/user/shutdown.target15
-rw-r--r--[l---------]units/user/smartcard.target14
-rw-r--r--[l---------]units/user/sockets.target13
-rw-r--r--[l---------]units/user/sound.target14
-rw-r--r--units/user/systemd-exit.service (renamed from units/systemd-exit.service.in)5
-rw-r--r--units/user/systemd-exit.service.in19
-rw-r--r--[l---------]units/user/timers.target16
-rw-r--r--units/user@.service.in5
-rw-r--r--units/var-lib-machines.mount7
1975 files changed, 100131 insertions, 46456 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
index 5ef7e11634..676d755231 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -2,17 +2,14 @@
; A list of (major-mode . ((var1 . value1) (var2 . value2)))
; Mode can be nil, which gives default values.
-; Note that we set a line width of 119 for .c and XML files, but for everything
+; Note that we set a line width of 109 for .c and XML files, but for everything
; else (such as journal catalog files, unit files, README files) we stick to a
; more conservative 79 characters.
; NOTE: If you update this file make sure to update .vimrc and .editorconfig,
; too.
-((nil . ((indent-tabs-mode . nil)
- (tab-width . 8)
- (fill-column . 79)))
- (c-mode . ((fill-column . 119)
+((c-mode . ((fill-column . 109)
(c-basic-offset . 8)
(eval . (c-set-offset 'substatement-open 0))
(eval . (c-set-offset 'statement-case-open 0))
@@ -20,8 +17,11 @@
(eval . (c-set-offset 'arglist-intro '++))
(eval . (c-set-offset 'arglist-close 0))))
(nxml-mode . ((nxml-child-indent . 2)
- (fill-column . 119)))
+ (fill-column . 109)))
(meson-mode . ((meson-indent-basic . 8)))
(sh-mode . ((sh-basic-offset . 8)
(sh-indentation . 8)))
- (awk-mode . ((c-basic-offset . 8))))
+ (awk-mode . ((c-basic-offset . 8)))
+ (nil . ((indent-tabs-mode . nil)
+ (tab-width . 8)
+ (fill-column . 79))) )
diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md
index cea1c655b9..6a0b5527d0 100644
--- a/.github/ISSUE_TEMPLATE/Bug_report.md
+++ b/.github/ISSUE_TEMPLATE/Bug_report.md
@@ -8,7 +8,7 @@ about: A report of an error in a recent systemd version
> ...
<!-- **NOTE:** Do not submit bug reports about anything but the two most recently released systemd versions upstream! -->
-<!-- For older version please use distribution trackers (see https://github.com/systemd/systemd/blob/master/.github/CONTRIBUTING.md#filing-issues). -->
+<!-- For older version please use distribution trackers (see https://github.com/systemd/systemd/blob/master/docs/CONTRIBUTING.md#filing-issues). -->
**Used distribution**
> …
diff --git a/.gitignore b/.gitignore
index 1970002350..5d18705531 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@
*.trs
*~
.config.args
+.gdb_history
.deps/
/*.gcda
/*.gcno
@@ -25,9 +26,11 @@
/build*
/coverage/
/image.raw
+/.#image.raw.lck
/image.raw.cache-pre-dev
/image.raw.cache-pre-inst
/install-tree
+/.mkosi-*
/mkosi.builddir/
/mkosi.output/
/tags
diff --git a/.lgtm.yml b/.lgtm.yml
new file mode 100644
index 0000000000..5948d8c2bc
--- /dev/null
+++ b/.lgtm.yml
@@ -0,0 +1,13 @@
+extraction:
+ cpp:
+ prepare:
+ packages:
+ - python3-pip
+ - python3-setuptools
+ - python3-wheel
+ after_prepare:
+ - pip3 install meson
+ - export PATH="$HOME/.local/bin/:$PATH"
+ python:
+ python_setup:
+ version: 3
diff --git a/.lgtm/cpp-queries/fgets.ql b/.lgtm/cpp-queries/fgets.ql
new file mode 100644
index 0000000000..a4181e4f3d
--- /dev/null
+++ b/.lgtm/cpp-queries/fgets.ql
@@ -0,0 +1,21 @@
+/**
+ * @name Use of fgets()
+ * @description fgets() is dangerous to call. Use read_line() instead.
+ * @kind problem
+ * @problem.severity error
+ * @precision high
+ * @id cpp/fgets
+ * @tags reliability
+ * security
+ */
+import cpp
+
+predicate dangerousFunction(Function function) {
+ exists (string name | name = function.getQualifiedName() |
+ name = "fgets")
+}
+
+from FunctionCall call, Function target
+where call.getTarget() = target
+ and dangerousFunction(target)
+select call, target.getQualifiedName() + " is potentially dangerous"
diff --git a/.mailmap b/.mailmap
index 83b17bfb2b..f3809eac4c 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,163 +1,194 @@
-Kay Sievers <kay@vrfy.org>
-Kay Sievers <kay@vrfy.org> <kay.sievers@vrfy.org>
-Kay Sievers <kay@vrfy.org> <kay.sievers@suse.de>
-Kay Sievers <kay@vrfy.org> <kay@pim.off.vrfy.org>
-Kay Sievers <kay@vrfy.org> <kay@pim>
-Kay Sievers <kay@vrfy.org> <kay@yik.fritz.box>
-Greg Kroah-Hartman <greg@kroah.com>
-Greg Kroah-Hartman <greg@kroah.com> <greg@kroah.com>
-Greg Kroah-Hartman <greg@kroah.com> <greg@press.(none)>
-Greg Kroah-Hartman <greg@kroah.com> <gregkh@suse.de>
-Greg Kroah-Hartman <greg@kroah.com> <greg@bucket.kroah.org>
-Greg Kroah-Hartman <greg@kroah.com> <gregkh@linuxfoundation.org>
-Harald Hoyer <harald@redhat.com>
-David Zeuthen <david@fubar.dk>
-David Zeuthen <david@fubar.dk> <davidz@redhat.com>
-David Zeuthen <david@fubar.dk> <zeuthen@gmail.com>
-Hannes Reinecke <hare@suse.de>
-Scott James Remnant <scott@netsplit.com>
-Scott James Remnant <scott@netsplit.com> <scott@ubuntu.com>
Alan Jenkins <alan.christopher.jenkins@googlemail.com>
Alan Jenkins <alan.christopher.jenkins@googlemail.com> <alan-jenkins@tuffmail.co.uk>
-Marco d'Itri <md@linux.it> <md@Linux.IT>
-Robert Gerus <ar@bash.org.pl> Robert "arachnist" Gerus <ar@bash.org.pl>
-Fabiano Fidêncio <fabianofidencio@gmail.com> Fabiano Fidencio <fidencio@profusion.mobi>
-Martin Pitt <martinpitt@gnome.org>
-Martin Pitt <martinpitt@gnome.org> <martin.pitt@ubuntu.com>
+Alan Robertson <aroberts@zen.iomart.com> <alanjrobertson@gmail.com>
+Alexander Kochetkov <al.kochet@gmail.com>
+Alexander Kuleshov <kuleshovmail@gmail.com> <0xAX@users.noreply.github.com>
+Alexander Kurtz <alexander@kurtz.be>
+Alexandros Frantzis <alexandros.frantzis@canonical.com>
+Ananth N Mavinakayanahalli <ananth@in.ibm.com>
+Ananth N Mavinakayanahalli <ananth@in.ibm.com> <ananthmg@rediffmail.com>
+Andreas Henriksson <andreas@fatal.se>
+Anthony Parsons <flussence@users.noreply.github.com>
+Antoine Eiche <lewo@abesis.fr>
+Arnd Bergmann <arnd@arndb.de>
+Atul Sabharwal <atul.sabharwal@intel.com>
+Bart Rulon <barron@lexmark.com>
+Bastien Nocera <hadess@hadess.net> <hadess@users.noreply.github.com>
+Beniamino Galvani <bgalvani@redhat.com> <bengal@users.noreply.github.com>
+Bill Yodlowsky <bill@redhat.com> <itsbill@users.noreply.github.com>
+Brian Boylston <brian.boylston@hpe.com>
+Charles (Chas) Williams <ciwillia@brocade.com>
+Chen Qi <Qi.Chen@windriver.com> <40684930+ChenQi1989@users.noreply.github.com>
+Christophe Varoqui <christophe.varoqui@free.fr>
+Colin Guthrie <ColinGuthrie@web>
+Daniel Elstner <daniel.kitta@gmail.com> <danielk@openismus.com>
Daniel J Walsh <dwalsh@redhat.com>
+Daniel Kahn Gillmor <dkg@fifthhorseman.net>
+Daniel Machon <Danielmachon@live.dk>
+Daniel Rusek <mail@asciiwolf.com>
+Daniel Stekloff <dsteklof@us.ibm.com>
+Daniel Șerbănescu <dasj19@users.noreply.github.com>
Dave Reisner <dreisner@archlinux.org> <d@falconindy.com>
+David Zeuthen <david@fubar.dk>
+David Zeuthen <david@fubar.dk> <davidz@redhat.com>
+David Zeuthen <david@fubar.dk> <zeuthen@gmail.com>
+Davide Cavalca <dcavalca@fb.com> <davide125@tiscali.it>
+Dennis Wassenberg <dennis.wassenberg@secunet.com>
Diego Elio Pettenò <flameeyes@gmail.com>
-Daniel Elstner <daniel.kitta@gmail.com> <danielk@openismus.com>
+Dmitriy Geels <dmitriy.geels@gmail.com>
+Dmitry Khlebnikov <dmitry.khlebnikov@rea-group.com> <galaxy4public@users.noreply.github.com>
+Douglas Christman <DouglasChristman@gmail.com>
+Emil Soleyman <emil@soleyman.com>
+Eric Cook <llua@users.noreply.github.com>
+Evgeny Vereshchagin <evvers@ya.ru>
+Fabiano Fidêncio <fabianofidencio@gmail.com> Fabiano Fidencio <fidencio@profusion.mobi>
+Faizal Luthfi <zalluth@gmail.com>
+Federico Di Pierro <nierro92@gmail.com>
+Fionn Cleary <clearyf@tcd.ie>
Frederic Crozat <fcrozat@suse.com> <fcrozat@mandriva.com>
+Gautier Husson <admin_github@liberasys.com>
+George Gaydarov <git@gg7.io> <gg7@users.noreply.github.com>
+Gianluca Boiano <morf3089@gmail.com>
+Greg Kroah-Hartman <greg@kroah.com>
+Greg Kroah-Hartman <greg@kroah.com> <greg@bucket.kroah.org>
+Greg Kroah-Hartman <greg@kroah.com> <greg@kroah.com>
+Greg Kroah-Hartman <greg@kroah.com> <greg@press.(none)>
+Greg Kroah-Hartman <greg@kroah.com> <gregkh@linuxfoundation.org>
+Greg Kroah-Hartman <greg@kroah.com> <gregkh@suse.de>
+Gwendal Grignou <gwendal@chromium.org>
+Hannes Reinecke <hare@suse.de>
+Harald Hoyer <harald@redhat.com>
+Harald Hoyer <harald@redhat.com> <harald@hoyer.xyz>
+Heikki Kemppainen <heikki.kemppainen@nokia.com>
+Hendrik Brueckner <hbrueckner@users.noreply.github.com>
+Hendrik Westerberg <hendrik@gestorf.com>
Ian Campbell <ijc@hellion.org.uk> <Ian.Campbell@citrix.com>
+Insun Pyo <insun.pyo@samsung.com>
+Insun Pyo <insun.pyo@samsung.com> <iplayinsun@gmail.com>
Jerone Young <jyoung@redhat.com> <jerone.young@canonical.com>
+Jiuyang Liu <liujiuyang1994@gmail.com>
+John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
+John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> <glaubitz@suse.com>
+Jon Ringle <jringle@gridpoint.com> <ringlej@users.noreply.github.com>
+Jonas Dorel <jonas.dorel@laposte.net>
+Josef Andersson <josef.andersson@fripost.org>
+Josef Andersson <l10nl18nsweja@gmail.com>
+José Bollo <jose.bollo@iot.bzh> <jobol@nonadev.net>
+Jun Bo Bi <jambonmcyeah@gmail.com>
+Justin Capella <justincapella@gmail.com> <b1tninja@users.noreply.github.com>
+Jérémy Rosen <jeremy.rosen@enst-bretagne.fr>
+Jürg Billeter <j@bitron.ch>
+Karl Kraus <karl.kraus@tum.de> <laqueray@gmail.com>
+Kay Sievers <kay@vrfy.org>
+Kay Sievers <kay@vrfy.org> <kay.sievers@suse.de>
+Kay Sievers <kay@vrfy.org> <kay.sievers@vrfy.org>
+Kay Sievers <kay@vrfy.org> <kay@pim.off.vrfy.org>
+Kay Sievers <kay@vrfy.org> <kay@pim>
+Kay Sievers <kay@vrfy.org> <kay@yik.fritz.box>
+Krzysztof Jackiewicz <k.jackiewicz@samsung.com> <kjackiewicz@users.noreply.github.com>
+Larry Bernstone <lbernstone@gmail.com>
+Lennart Poettering <lennart@poettering.net> <LennartPoettering@web>
+Lennart Poettering <lennart@poettering.net> <lennart@bf9bc1cc-28ce-0310-abfb-9041aa761afb>
+Lennart Poettering <lennart@poettering.net> <mzninuv@0pointer.de>
+Leonard König <leonard.r.koenig@googlemail.com>
Luis Felipe Strano Moraes <luis.strano@gmail.com> <lfelipe@profusion.mobi>
+Lukáš Nykrýn <lnykryn@redhat.com>
+Lukáš Říha <cedel@centrum.cz>
+Mao Huang <littlecvr@gmail.com>
+Marco d'Itri <md@linux.it> <md@Linux.IT>
+Marcus Cooper <marcusc@axis.com> <codekipper@gmail.com>
Mario Limonciello <mario_limonciello@dell.com> <Mario_Limonciello@dell.com>
+Martin Pitt <martin@piware.de> <martinpitt@users.noreply.github.com>
+Martin Pitt <martinpitt@gnome.org>
+Martin Pitt <martinpitt@gnome.org> <martin.pitt@ubuntu.com>
+Martin Steuer <martinsteuer@gmx.de>
+Marty Plummer <ntzrmtthihu777@gmail.com>
+Matthew Leeds <matthew.leeds@endlessm.com> <mwl458@gmail.com>
+Matthew McGinn <mamcgi@gmail.com> <xginn8@users.noreply.github.com>
Matthias Clasen <mclasen@redhat.com> <matthias.clasen@gmail.com>
+Matthias-Christian Ott <ott@mirix.org> <ott@users.noreply.github.com>
+Michael Biebl <biebl@debian.org> <mbiebl@gmail.com>
+Michael Buesch <mbuesch@freenet.de>
+Michael Hoy <rimmington@gmail.com>
+Michael Olbrich <m.olbrich@pengutronix.de>
Michal Soltys <soltys@ziu.info> <nozo@ziu.info>
+Michal Suchanek <msuchanek@suse.de>
+Michal Suchanek <msuchanek@suse.de> <hramrach@gmail.com>
+Michał Szczepański <skrzatu@hotmail.com> <skrzatu@gmail.com>
+Michel Kraus <github@demonsphere.de> <27o@users.noreply.github.com>
+Miklos Vajna <vmiklos@frugalware.org> <vmiklos@gmail.com>
+Milan Pässler <me@petabyteboy.de>
+Neil Brown <neil@brown.name>
+Neil Brown <neilb@suse.com>
+Niklas Tibbling <niklasti@axis.com> <45659916+tibbling@users.noreply.github.com>
+Nikolas Nyby <nnyby@columbia.edu>
+Nogisaka Sadata <ngsksdt@gmail.com>
+Olaf Hering <olh@suse.de>
+Otto Wallenius <otto_026@hotmail.com>
+Pablo Lezaeta Reyes <prflr88@gmail.com>
+Paolo Giangrandi <paolo@luccalug.it>
+Patrick Mansfield <patmans@us.ibm.com>
+Patryk Kocielnik <longer44@gmail.com>
+Paul Mundt <lethal@linux-sh.org>
+Pavel Hrdina <phrdina@redhat.com>
+Peter D'Hoye <peter.dhoye@gmail.com>
+Piotr Szydełko <wiertel@users.sourceforge.net>
Piter PUNK <piterpunk@slackware.com> <piterpk@terra.com.br>
+Reid Price <reid.price@gmail.com>
+Reinhold Mueller <mueller.reinhold@web.de>
Richard Hughes <richard@hughsie.com> <hughsient@gmail.com>
+Richard W.M. Jones <rjones@redhat.com>
Robby Workman <rw@rlworkman.net> <rworkman@slackware.com>
+Robert Gerus <ar@bash.org.pl> Robert "arachnist" Gerus <ar@bash.org.pl>
+Robert Kolchmeyer <rkolchmeyer@google.com> <rkolchmeyer@users.noreply.github.com>
+Robert Love <rml@tech9.net>
+Roman Stingler <coolx67@gmx.at>
+Rubén Suárez Alvarez <rubensa@tluportatil082> <rubensa@gmail.com>
+Salvo Tomaselli <ltworf@users.noreply.github.com>
+Sandy Carter <bwrsandman@gmail.com>
+Scott James Remnant <scott@netsplit.com>
+Scott James Remnant <scott@netsplit.com> <scott@ubuntu.com>
+Seraphime Kirkovski <kirkseraph@gmail.com>
Shawn Landden <shawnlandden@gmail.com> <shawn@churchofgit.com>
+Shawn Landden <slandden@gmail.com> <shawn@git.icu>
+Silvio Knizek <killermoehre@gmx.net>
Simon Peeters <peeters.simon@gmail.com>
-Tobias Klauser <tklauser@distanz.ch> <tklauser@nuerscht.ch>
-Miklos Vajna <vmiklos@frugalware.org> <vmiklos@gmail.com>
-William Jon McCann <jmccann@redhat.com> <william.jon.mccann@gmail.com>
-Yin Kangkai <kangkai.yin@intel.com> <kangkai.yin@linux.intel.com>
-Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
-Lennart Poettering <lennart@poettering.net> <lennart@bf9bc1cc-28ce-0310-abfb-9041aa761afb>
-Ananth N Mavinakayanahalli <ananth@in.ibm.com>
-Ananth N Mavinakayanahalli <ananth@in.ibm.com> <ananthmg@rediffmail.com>
+Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+Stanislav AngeloviÄ <angelovic.s@gmail.com>
+Stasiek Michalski <hellcp@opensuse.org>
+Stefan Pietsch <mail.ipv4v6@gmail.com>
+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>
+Sébastien Bacher <seb128@ubuntu.com>
+Tanu Kaskinen <TanuKaskinen@web>
Ted Ts'o <tytso@mit.edu>
+Ted Wood <ted.l.wood@gmail.com>
+Ted Wood <ted@mailchimp.com>
+Thomas Blume <Thomas.Blume@suse.com>
+Thomas H. P. Andersen <phomes@gmail.com>
+Tiago Levit <liamgliam@gmail.com>
+Tibor Nagy <xnagytibor@gmail.com>
+Tinu Weber <takeya@bluewin.ch>
+Tobias Jungel <tobias.jungel@bisdn.de> Tobias Jungel <Tobias.Jungel@gmail.com>
Tobias Klauser <tklauser@access.unizh.ch>
-Tobias Klauser <tklauser@access.unizh.ch> <tklauser@access.unizh.chbk>
Tobias Klauser <tklauser@access.unizh.ch> <klauser@access.unizh.ch>
+Tobias Klauser <tklauser@access.unizh.ch> <tklauser@access.unizh.chbk>
Tobias Klauser <tklauser@access.unizh.ch> <tklauser@distanz.ch>
-Patrick Mansfield <patmans@us.ibm.com>
-Christophe Varoqui <christophe.varoqui@free.fr>
-Daniel Stekloff <dsteklof@us.ibm.com>
-Michael Buesch <mbuesch@freenet.de>
-Olaf Hering <olh@suse.de>
-Robert Love <rml@tech9.net>
-Arnd Bergmann <arnd@arndb.de>
+Tobias Klauser <tklauser@distanz.ch> <tklauser@nuerscht.ch>
Tom Rini <trini@kernel.crashing.org>
-Paul Mundt <lethal@linux-sh.org>
-Atul Sabharwal <atul.sabharwal@intel.com>
-Daniel Machon <Danielmachon@live.dk>
-Thomas Blume <Thomas.Blume@suse.com>
-Pablo Lezaeta Reyes <prflr88@gmail.com>
-Otto Wallenius <otto_026@hotmail.com>
Tom Yan <tom.ty89@gmail.com>
-Marty Plummer <ntzrmtthihu777@gmail.com>
-Brian Boylston <brian.boylston@hpe.com>
-Thomas H. P. Andersen <phomes@gmail.com>
-Michael Olbrich <m.olbrich@pengutronix.de>
-Douglas Christman <DouglasChristman@gmail.com>
-Alexander Kuleshov <kuleshovmail@gmail.com> <0xAX@users.noreply.github.com>
-Andreas Henriksson <andreas@fatal.se>
-Daniel Rusek <mail@asciiwolf.com>
-Dennis Wassenberg <dennis.wassenberg@secunet.com>
-Reid Price <reid.price@gmail.com>
-Stefan Schweter <stefan@schweter.it>
-Seraphime Kirkovski <kirkseraph@gmail.com>
-Bart Rulon <barron@lexmark.com>
-Richard W.M. Jones <rjones@redhat.com>
-Roman Stingler <coolx67@gmx.at>
-Michael Hoy <rimmington@gmail.com>
-Tiago Levit <liamgliam@gmail.com>
-Eric Cook <llua@users.noreply.github.com>
-Lukáš Nykrýn <lnykryn@redhat.com>
-Heikki Kemppainen <heikki.kemppainen@nokia.com>
-Hendrik Brueckner <hbrueckner@users.noreply.github.com>
-Alexandros Frantzis <alexandros.frantzis@canonical.com>
-Alexander Kochetkov <al.kochet@gmail.com>
-Fionn Cleary <clearyf@tcd.ie>
-Michel Kraus <github@demonsphere.de> <27o@users.noreply.github.com>
-Charles (Chas) Williams <ciwillia@brocade.com>
-Emil Soleyman <emil@soleyman.com>
-Dmitry Khlebnikov <dmitry.khlebnikov@rea-group.com> <galaxy4public@users.noreply.github.com>
-Antoine Eiche <lewo@abesis.fr>
-Gianluca Boiano <morf3089@gmail.com>
-Paolo Giangrandi <paolo@luccalug.it>
-Karl Kraus <karl.kraus@tum.de> <laqueray@gmail.com>
-Tibor Nagy <xnagytibor@gmail.com>
-Stuart McLaren <stuart.mclaren@hp.com>
-John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
-John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> <glaubitz@suse.com>
-Sjoerd Simons <sjoerd.simons@collabora.co.uk>
-Neil Brown <neil@brown.name>
-Michal Suchanek <msuchanek@suse.de> <hramrach@gmail.com>
-Michal Suchanek <msuchanek@suse.de>
-Bastien Nocera <hadess@hadess.net> <hadess@users.noreply.github.com>
-Umut Tezduyar Lindskog <umut@tezduyar.com>
-Alexander Kurtz <alexander@kurtz.be>
-Piotr Szydełko <wiertel@users.sourceforge.net>
-Åukasz Stelmach <l.stelmach@samsung.com> <stlman@poczta.fm>
-Krzysztof Jackiewicz <k.jackiewicz@samsung.com> <kjackiewicz@users.noreply.github.com>
-Marcus Cooper <marcusc@axis.com> <codekipper@gmail.com>
-Insun Pyo <insun.pyo@samsung.com> <iplayinsun@gmail.com>
-Ted Wood <ted.l.wood@gmail.com>
-Ted Wood <ted@mailchimp.com>
-Anthony Parsons <flussence@users.noreply.github.com>
-Federico Di Pierro <nierro92@gmail.com>
-Josef Andersson <josef.andersson@fripost.org>
-Josef Andersson <l10nl18nsweja@gmail.com>
-Hendrik Westerberg <hendrik@gestorf.com>
-Stefan Pietsch <mail.ipv4v6@gmail.com>
-Jérémy Rosen <jeremy.rosen@enst-bretagne.fr>
-Vasilis Liaskovitis <vliaskov@gmail.com>
-Daniel Kahn Gillmor <dkg@fifthhorseman.net>
+Tomasz Bachorski <tomasz.bachorski@x7f.io> <34866781+nulsoh@users.noreply.github.com>
Tomasz Pala <gotar@polanet.pl>
-Dmitriy Geels <dmitriy.geels@gmail.com>
-Beniamino Galvani <bgalvani@redhat.com> <bengal@users.noreply.github.com>
-Justin Capella <justincapella@gmail.com> <b1tninja@users.noreply.github.com>
-Daniel Șerbănescu <dasj19@users.noreply.github.com>
-Stanislav AngeloviÄ <angelovic.s@gmail.com>
Torsten Hilbrich <torsten.hilbrich@gmx.net>
-Tinu Weber <takeya@bluewin.ch>
-Gwendal Grignou <gwendal@chromium.org>
-José Bollo <jose.bollo@iot.bzh> <jobol@nonadev.net>
-Patryk Kocielnik <longer44@gmail.com>
-Lukáš Říha <cedel@centrum.cz>
-Alan Robertson <aroberts@zen.iomart.com> <alanjrobertson@gmail.com>
-Martin Steuer <martinsteuer@gmx.de>
-Matthias-Christian Ott <ott@mirix.org> <ott@users.noreply.github.com>
-Larry Bernstone <lbernstone@gmail.com>
-Michał Szczepański <skrzatu@hotmail.com> <skrzatu@gmail.com>
-Tomasz Bachorski <tomasz.bachorski@x7f.io> <34866781+nulsoh@users.noreply.github.com>
-Zachary Winnerman <33329648+winnerman-pythian@users.noreply.github.com>
+Umut Tezduyar Lindskog <umut@tezduyar.com>
+Vasilis Liaskovitis <vliaskov@gmail.com>
Vladislav Vishnyakov <split7fire@yandex.ru>
-Robert Kolchmeyer <rkolchmeyer@google.com> <rkolchmeyer@users.noreply.github.com>
-George Gaydarov <git@gg7.io> <gg7@users.noreply.github.com>
-Bill Yodlowsky <bill@redhat.com> <itsbill@users.noreply.github.com>
-Mao Huang <littlecvr@gmail.com>
-Pavel Hrdina <phrdina@redhat.com>
-Jürg Billeter <j@bitron.ch>
-Leonard König <leonard.r.koenig@googlemail.com>
-Milan Pässler <me@petabyteboy.de>
+William Jon McCann <jmccann@redhat.com> <william.jon.mccann@gmail.com>
Wim van Mourik <wvanmourik@computest.nl> <githubw@use.startmail.com>
-Rubén Suárez Alvarez <rubensa@tluportatil082> <rubensa@gmail.com>
-Salvo Tomaselli <ltworf@users.noreply.github.com>
-Matthew McGinn <mamcgi@gmail.com> <xginn8@users.noreply.github.com>
-Evgeny Vereshchagin <evvers@ya.ru>
-Peter D'Hoye <peter.dhoye@gmail.com>
+Yann E. Morin <yann.morin.1998@free.fr>
+Yin Kangkai <kangkai.yin@intel.com> <kangkai.yin@linux.intel.com>
+Zachary Winnerman <33329648+winnerman-pythian@users.noreply.github.com>
+Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
+Åukasz Stelmach <l.stelmach@samsung.com> <stlman@poczta.fm>
diff --git a/.mkosi/mkosi.debian b/.mkosi/mkosi.debian
index d30a592bc4..e85612bef1 100644
--- a/.mkosi/mkosi.debian
+++ b/.mkosi/mkosi.debian
@@ -1,6 +1,4 @@
# SPDX-License-Identifier: LGPL-2.1+
-#
-# Copyright © 2016 Daniel Rusek
# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
# Simply invoke "mkosi" in the project directory to build an OS image.
@@ -63,6 +61,6 @@ BuildPackages=
xz-utils
Packages=
- libqrencode3
+ libqrencode4
locales
libidn2-0
diff --git a/.mkosi/mkosi.fedora b/.mkosi/mkosi.fedora
index 63027d9fc7..ff43ecdfe6 100644
--- a/.mkosi/mkosi.fedora
+++ b/.mkosi/mkosi.fedora
@@ -5,11 +5,12 @@
[Distribution]
Distribution=fedora
-Release=27
+Release=29
[Output]
Format=raw_btrfs
Bootable=yes
+KernelCommandLine=printk.devkmsg=on
[Partitions]
RootSize=3G
diff --git a/.mkosi/mkosi.ubuntu b/.mkosi/mkosi.ubuntu
index bbda5dd054..1e4005f070 100644
--- a/.mkosi/mkosi.ubuntu
+++ b/.mkosi/mkosi.ubuntu
@@ -1,8 +1,4 @@
# SPDX-License-Identifier: LGPL-2.1+
-#
-# This file is part of systemd.
-#
-# Copyright 2016 Daniel Rusek
# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
# Simply invoke "mkosi" in the project directory to build an OS image.
diff --git a/.travis.yml b/.travis.yml
index d980038181..f50731fe3f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,77 +1,151 @@
sudo: required
-
services:
- docker
-language: c
+env:
+ global:
+ - AUTHOR_EMAIL="$(git log -1 $TRAVIS_COMMIT --pretty=\"%aE\")"
+ - CI_MANAGERS="$TRAVIS_BUILD_DIR/travis-ci/managers"
+ - CI_TOOLS="$TRAVIS_BUILD_DIR/travis-ci/tools"
+ - REPO_ROOT="$TRAVIS_BUILD_DIR"
+
+stages:
+ # Run Coverity periodically instead of for each commit/PR
+ - name: Coverity
+ if: type = cron
jobs:
include:
- - stage: coverity scan
- before_script:
- - sudo apt-get update
+ - stage: Build & test
+ name: Fedora Latest
+ language: bash
+ env:
+ - FEDORA_RELEASE="latest"
+ - CONT_NAME="systemd-fedora-$FEDORA_RELEASE"
+ - DOCKER_EXEC="docker exec -ti $CONT_NAME"
+ before_install:
- sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
- docker --version
- - env > .env
- env:
- - COVERITY_SCAN_PROJECT_NAME="$TRAVIS_REPO_SLUG"
- - COVERITY_SCAN_NOTIFICATION_EMAIL="${AUTHOR_EMAIL}"
- - COVERITY_SCAN_BRANCH_PATTERN="$TRAVIS_BRANCH"
- # Encrypted token for systemd/systemd Coverity Scan Analysis,
- # generated by "travis encrypt -r systemd/systemd COVERITY_SCAN_TOKEN=<TOKEN>"
- - secure: "lM0IVP2zOG5Ywk3YCbDCQL4WioyzzwtdtpZ+hKDy4BWCZDBJ/FVwIeBsXdMDvlTa3xi+GQ1b7kS2OmTfmG4aSlhU7isuH8SMq1Y4GR5AxfhkR+irUA1A1fntlvhbjIumDGW5wjs0Dt8KogMWS+ZD4eGE59lrVO/TrhMzIe1eHENVLFQJdNq+ZJXU8wxMfHf8lXk0xA8SJTid0XvZBNc4JN6pjJRA8LaOrMNhQYfygFmVQ598kwlu7gf5vbCKFPnIgJAxdIhz12XS9utGohV28IYj9d1DdUGUT+ar3OfADj3X8KFBP4Ymc02pcln3wVgdPtrDbFZh1R9jbmfdXGAH/6tTOJVn8aFySS2Vq9QiBiprWdPsAOLcWMNhnp0lMkASxs9/W26nU7Czo8VbAVWXM1w35plDpnDGR6lk/06dmOZpqu5p3AYr5xIKACIAdPDn0rNpnSWqC750WZ8ZWbHnKuZC5TWML7scVaPiEi7D7rbwqML2rdwx4ZoTZmCHiGByXCIWTfhf0JNQAix5WW3znl+BmDesumPgPj2mX+y6J1WYJrIz12m7qh7KhV/a1ODKM+I91A9rkOA/bPnmhmSSUR7CwgvZt1fC/VwBnaFFtAz9/70kN9Q8tDBXtXidExZwh1e3t5vDG72k3lXwNqpKRvdW3LOxK6lFvqEdMWVUJls="
+ install:
+ - $CI_MANAGERS/fedora.sh SETUP
script:
- # Copy content of CI_DIR into WORKDIR
- - find $CI_DIR -maxdepth 1 -type f -exec cp -t . {} +
- # Build container for current user
- - $CI_SCRIPT_DIR/build-docker-image.sh
-
- # For kernel version 4.8+
- - sudo sysctl vsyscall=emulate || true
- # Prepare environment for Coverity tool
- - |
- PLATFORM=`uname`
- export TOOL_BASE="/tmp/coverity-scan-analysis"
- export SCAN_URL="https://scan.coverity.com"
- export UPLOAD_URL="https://scan.coverity.com/builds"
- export TOOL_ARCHIVE="/tmp/cov-analysis-${PLATFORM}.tgz"
-
- # Get Coverity tool
- - $CI_TOOL_DIR/get-coverity.sh
- - TOOL_DIR="$(find $TOOL_BASE -type d -name 'cov-analysis*')"
+ - set -e
+ # Build systemd
+ - $CI_MANAGERS/fedora.sh RUN
+ - set +e
+ after_script:
+ - $CI_MANAGERS/fedora.sh CLEANUP
- # Export env variables for Coverity scan
- - env | grep -E "TRAVIS|COV|TOOL|URL" > .cov-env
- - |
- docker run -dit --env-file .cov-env \
- -v ${TOOL_BASE}:${TOOL_BASE}:ro \
- --name travis_coverity_scan coverity-${TRAVIS_COMMIT}:latest bash
- # Make sure Coverity script is executable
- - docker cp tools/coverity.sh travis_coverity_scan:/usr/local/bin
- # Preconfigure with meson to prevent Coverity from capturing meson metadata
- # Set compiler flag to prevent emit failure
- - docker exec -it travis_coverity_scan sh -c "CFLAGS='-D_Float128=long\ double -D_Float64=double -D_Float64x=long\ double -D_Float32=float -D_Float32x=double' meson cov-build -Dman=false"
- # Run Coverity Analysis
- - docker exec -it travis_coverity_scan coverity.sh build
- - docker exec -it travis_coverity_scan coverity.sh upload
+ - name: Fedora Latest (ASan+UBSan)
+ language: bash
+ env:
+ - FEDORA_RELEASE="latest"
+ - CONT_NAME="systemd-fedora-$FEDORA_RELEASE"
+ - DOCKER_EXEC="docker exec -ti $CONT_NAME"
+ before_install:
+ - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
+ - docker --version
+ install:
+ - $CI_MANAGERS/fedora.sh SETUP
+ script:
+ - set -e
+ - $CI_MANAGERS/fedora.sh RUN_ASAN
+ - set +e
+ after_script:
+ - $CI_MANAGERS/fedora.sh CLEANUP
-# Specify the order of stages and conditions
-stages:
- - name: coverity scan
- if: type = cron
+ - name: Fedora Latest (clang)
+ language: bash
+ env:
+ - FEDORA_RELEASE="latest"
+ - CONT_NAME="systemd-fedora-$FEDORA_RELEASE"
+ - DOCKER_EXEC="docker exec -ti $CONT_NAME"
+ before_install:
+ - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
+ - docker --version
+ install:
+ - $CI_MANAGERS/fedora.sh SETUP
+ script:
+ - set -e
+ - $CI_MANAGERS/fedora.sh RUN_CLANG
+ - set +e
+ after_script:
+ - $CI_MANAGERS/fedora.sh CLEANUP
-env:
- global:
- - ADMIN_EMAIL=macermak@redhat.com
+ - name: Fedora Latest (clang ASan+UBSan)
+ language: bash
+ env:
+ - FEDORA_RELEASE="latest"
+ - CONT_NAME="systemd-fedora-$FEDORA_RELEASE"
+ - DOCKER_EXEC="docker exec -ti $CONT_NAME"
+ before_install:
+ - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
+ - docker --version
+ install:
+ - $CI_MANAGERS/fedora.sh SETUP
+ script:
+ - set -e
+ - $CI_MANAGERS/fedora.sh RUN_CLANG_ASAN
+ - set +e
+ after_script:
+ - $CI_MANAGERS/fedora.sh CLEANUP
- - AUTHOR_NAME="$(git log -1 $TRAVIS_COMMIT --pretty=\"%aN\")"
- - AUTHOR_EMAIL="$(git log -1 $TRAVIS_COMMIT --pretty=\"%aE\")"
+ - name: Debian Testing
+ language: bash
+ env:
+ - DEBIAN_RELEASE="testing"
+ - CONT_NAME="systemd-debian-$DEBIAN_RELEASE"
+ - DOCKER_EXEC="docker exec -ti $CONT_NAME"
+ before_install:
+ - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
+ - docker --version
+ install:
+ - $CI_MANAGERS/debian.sh SETUP
+ script:
+ - set -e
+ - $CI_MANAGERS/debian.sh RUN
+ - set +e
+ after_script:
+ - $CI_MANAGERS/debian.sh CLEANUP
- - CI_DIR="$TRAVIS_BUILD_DIR/travis-ci"
- - CI_TOOL_DIR="$CI_DIR/tools"
- - CI_SCRIPT_DIR="$CI_DIR/scripts"
+ - stage: Coverity
+ language: bash
+ env:
+ - FEDORA_RELEASE="latest"
+ - CONT_NAME="coverity-fedora-$FEDORA_RELEASE"
+ - DOCKER_EXEC="docker exec -ti $CONT_NAME"
+ - TOOL_BASE="/var/tmp/coverity-scan-analysis"
+ - DOCKER_RUN="docker run -v $TOOL_BASE:$TOOL_BASE:rw --env-file .cov-env"
+ # Coverity env variables
+ - PLATFORM="$(uname)"
+ - TOOL_ARCHIVE="/var/tmp/cov-analysis-$PLATFORM.tgz
+ - SCAN_URL="https://scan.coverity.com"
+ - UPLOAD_URL="https://scan.coverity.com/builds"
+ - COVERITY_SCAN_PROJECT_NAME="$TRAVIS_REPO_SLUG"
+ - COVERITY_SCAN_NOTIFICATION_EMAIL="${AUTHOR_EMAIL}"
+ - COVERITY_SCAN_BRANCH_PATTERN="$TRAVIS_BRANCH"
+ # Encrypted COVERITY_SCAN_TOKEN env variable
+ # Generated using `travis encrypt -r systemd/systemd COVERITY_SCAN_TOKEN=xxxx`
+ - secure: "jKSz+Y1Mv8xMpQHh7g5lzW7E6HQGndFz/vKDJQ1CVShwFoyjV3Zu+MFS3UYKlh1236zL0Z4dvsYFx/b3Hq8nxZWCrWeZs2NdXgy/wh8LZhxwzcGYigp3sIA/cYdP5rDjFJO0MasNkl25/rml8+eZWz+8/xQic98UQHjSco/EOWtssoRcg0J0c4eDM7bGLfIQWE73NNY1Q1UtWjKmx1kekVrM8dPmHXJ9aERka7bmcbJAcKd6vabs6DQ5AfWccUPIn/EsRYqIJTRxJrFYU6XizANZ1a7Vwk/DWHZUEn2msxcZw5BbAMDTMx0TbfrNkKSHMHuvQUCu6KCBAq414i+LgkMfmQ2SWwKiIUsud1kxXX3ZPl9bxDv1HkvVdcniC/EM7lNEEVwm4meOnjuhI2lhOyOjmP3FTSlMHGP7xlK8DS2k9fqL58vn0BaSjwWgd+2+HuL2+nJmxcK1eLGzKqaostFxrk2Xs2vPZkUdV2nWY/asUrcWHml6YlWDn2eP83pfwxHYsMiEHY/rTKvxeVY+iirO/AphoO+eaYu7LvjKZU1Yx5Z4u/SnGWAiCH0yhMis0bWmgi7SCbw+sDd2uya+aoiLIGiB2ChW7hXHXCue/dif6/gLU7b+L8R00pQwnWdvKUPoIJCmZJYCluTeib4jpW+EmARB2+nR8wms2K9FGKM="
+ before_install:
+ - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
+ - docker --version
+ install:
+ # Install Coverity on the host
+ - $CI_TOOLS/get-coverity.sh
+ # Export necessary env variables for Coverity
+ - env | grep -E "TRAVIS|COV|TOOL|URL" > .cov-env
+ # Pull a Docker image and start a new container
+ - $CI_MANAGERS/fedora.sh SETUP
+ script:
+ - set -e
+ # Preconfigure with meson to prevent Coverity from capturing meson metadata
+ # Set compiler flag to prevent emit failure
+ - $DOCKER_EXEC sh -c "CFLAGS='-D_Float128=long\ double -D_Float64=double -D_Float64x=long\ double -D_Float32=float -D_Float32x=double' meson cov-build -Dman=false"
+ # Run Coverity
+ - $DOCKER_EXEC tools/coverity.sh build
+ - $DOCKER_EXEC tools/coverity.sh upload
-notifications:
- email:
- recipients:
- - ${ADMIN_EMAIL}
+ - set +e
+ after_script:
+ - $CI_MANAGERS/fedora.sh CLEANUP
diff --git a/.vimrc b/.vimrc
index 284bf88494..a62546d8e2 100644
--- a/.vimrc
+++ b/.vimrc
@@ -4,7 +4,7 @@
" You should consider setting 'set secure' as well, which is highly
" recommended!
-" Note that we set a line width of 119 for .c and XML files, but for everything
+" Note that we set a line width of 109 for .c and XML files, but for everything
" else (such as journal catalog files, unit files, README files) we stick to a
" more conservative 79 characters.
@@ -16,5 +16,5 @@ set shiftwidth=8
set expandtab
set makeprg=GCC_COLORS=\ make
set tw=79
-au BufRead,BufNewFile *.xml set tw=119 shiftwidth=2 smarttab
-au FileType c set tw=119
+au BufRead,BufNewFile *.xml set tw=109 shiftwidth=2 smarttab
+au FileType c set tw=109
diff --git a/NEWS b/NEWS
index fb00f4ba63..c67b5b068a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,506 @@
systemd System and Service Manager
+CHANGES WITH 240:
+
+ * NoNewPrivileges=yes has been set for all long-running services
+ implemented by systemd. Previously, this was problematic due to
+ SELinux (as this would also prohibit the transition from PID1's label
+ to the service's label). This restriction has since been lifted, but
+ an SELinux policy update is required.
+ (See e.g. https://github.com/fedora-selinux/selinux-policy/pull/234.)
+
+ * DynamicUser=yes is dropped from systemd-networkd.service,
+ systemd-resolved.service and systemd-timesyncd.service, which was
+ enabled in v239 for systemd-networkd.service and systemd-resolved.service,
+ and since v236 for systemd-timesyncd.service. The users and groups
+ systemd-network, systemd-resolve and systemd-timesync are created
+ by systemd-sysusers again. Distributors or system administrators
+ may need to create these users and groups if they not exist (or need
+ to re-enable DynamicUser= for those units) while upgrading systemd.
+
+ * When unit files are loaded from disk, previously systemd would
+ sometimes (depending on the unit loading order) load units from the
+ target path of symlinks in .wants/ or .requires/ directories of other
+ units. This meant that unit could be loaded from different paths
+ depending on whether the unit was requested explicitly or as a
+ dependency of another unit, not honouring the priority of directories
+ in search path. It also meant that it was possible to successfully
+ load and start units which are not found in the unit search path, as
+ long as they were requested as a dependency and linked to from
+ .wants/ or .requires/. The target paths of those symlinks are not
+ used for loading units anymore and the unit file must be found in
+ the search path.
+
+ * A new service type has been added: Type=exec. It's very similar to
+ Type=simple but ensures the service manager will wait for both fork()
+ and execve() of the main service binary to complete before proceeding
+ with follow-up units. This is primarily useful so that the manager
+ propagates any errors in the preparation phase of service execution
+ back to the job that requested the unit to be started. For example,
+ consider a service that has ExecStart= set to a file system binary
+ that doesn't exist. With Type=simple starting the unit would be
+ considered instantly successful, as only fork() has to complete
+ successfully and the manager does not wait for execve(), and hence
+ its failure is seen "too late". With the new Type=exec service type
+ starting the unit will fail, as the manager will wait for the
+ execve() and notice its failure, which is then propagated back to the
+ start job.
+
+ NOTE: with the next release 241 of systemd we intend to change the
+ systemd-run tool to default to Type=exec for transient services
+ started by it. This should be mostly safe, but in specific corner
+ cases might result in problems, as the systemd-run tool will then
+ block on NSS calls (such as user name look-ups due to User=) done
+ between the fork() and execve(), which under specific circumstances
+ might cause problems. It is recommended to specify "-p Type=simple"
+ explicitly in the few cases where this applies. For regular,
+ non-transient services (i.e. those defined with unit files on disk)
+ we will continue to default to Type=simple.
+
+ * The Linux kernel's current default RLIMIT_NOFILE resource limit for
+ userspace processes is set to 1024 (soft) and 4096
+ (hard). Previously, systemd passed this on unmodified to all
+ processes it forked off. With this systemd release the hard limit
+ systemd passes on is increased to 512K, overriding the kernel's
+ defaults and substantially increasing the number of simultaneous file
+ descriptors unprivileged userspace processes can allocate. Note that
+ the soft limit remains at 1024 for compatibility reasons: the
+ traditional UNIX select() call cannot deal with file descriptors >=
+ 1024 and increasing the soft limit globally might thus result in
+ programs unexpectedly allocating a high file descriptor and thus
+ failing abnormally when attempting to use it with select() (of
+ course, programs shouldn't use select() anymore, and prefer
+ poll()/epoll, but the call unfortunately remains undeservedly popular
+ at this time). This change reflects the fact that file descriptor
+ handling in the Linux kernel has been optimized in more recent
+ kernels and allocating large numbers of them should be much cheaper
+ both in memory and in performance than it used to be. Programs that
+ want to take benefit of the increased limit have to "opt-in" into
+ high file descriptors explicitly by raising their soft limit. Of
+ course, when they do that they must acknowledge that they cannot use
+ select() anymore (and neither can any shared library they use — or
+ any shared library used by any shared library they use and so on).
+ Which default hard limit is most appropriate is of course hard to
+ decide. However, given reports that ~300K file descriptors are used
+ in real-life applications we believe 512K is sufficiently high as new
+ default for now. Note that there are also reports that using very
+ high hard limits (e.g. 1G) is problematic: some software allocates
+ large arrays with one element for each potential file descriptor
+ (Java, …) — a high hard limit thus triggers excessively large memory
+ allocations in these applications. Hopefully, the new default of 512K
+ is a good middle ground: higher than what real-life applications
+ currently need, and low enough for avoid triggering excessively large
+ allocations in problematic software. (And yes, somebody should fix
+ Java.)
+
+ * The fs.nr_open and fs.file-max sysctls are now automatically bumped
+ to the highest possible values, as separate accounting of file
+ descriptors is no longer necessary, as memcg tracks them correctly as
+ part of the memory accounting anyway. Thus, from the four limits on
+ file descriptors currently enforced (fs.file-max, fs.nr_open,
+ RLIMIT_NOFILE hard, RLIMIT_NOFILE soft) we turn off the first two,
+ and keep only the latter two. A set of build-time options
+ (-Dbump-proc-sys-fs-file-max=no and -Dbump-proc-sys-fs-nr-open=no)
+ has been added to revert this change in behaviour, which might be
+ an option for systems that turn off memcg in the kernel.
+
+ * When no /etc/locale.conf file exists (and hence no locale settings
+ are in place), systemd will now use the "C.UTF-8" locale by default,
+ and set LANG= to it. This locale is supported by various
+ distributions including Fedora, with clear indications that upstream
+ glibc is going to make it available too. This locale enables UTF-8
+ mode by default, which appears appropriate for 2018.
+
+ * The "net.ipv4.conf.all.rp_filter" sysctl will now be set to 2 by
+ default. This effectively switches the RFC3704 Reverse Path filtering
+ from Strict mode to Loose mode. This is more appropriate for hosts
+ that have multiple links with routes to the same networks (e.g.
+ a client with a Wi-Fi and Ethernet both connected to the internet).
+
+ Consult the kernel documentation for details on this sysctl:
+ https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
+
+ * CPUAccounting=yes no longer enables the CPU controller when using
+ kernel 4.15+ and the unified cgroup hierarchy, as required accounting
+ statistics are now provided independently from the CPU controller.
+
+ * Support for disabling a particular cgroup controller within a sub-tree
+ has been added through the DisableControllers= directive.
+
+ * cgroup_no_v1=all on the kernel command line now also implies
+ using the unified cgroup hierarchy, unless one explicitly passes
+ systemd.unified_cgroup_hierarchy=0 on the kernel command line.
+
+ * The new "MemoryMin=" unit file property may now be used to set the
+ memory usage protection limit of processes invoked by the unit. This
+ controls the cgroupsv2 memory.min attribute. Similarly, the new
+ "IODeviceLatencyTargetSec=" property has been added, wrapping the new
+ cgroupsv2 io.latency cgroup property for configuring per-service I/O
+ latency.
+
+ * systemd now supports the cgroupsv2 devices BPF logic, as counterpart
+ to the cgroupsv1 "devices" cgroup controller.
+
+ * systemd-escape now is able to combine --unescape with --template. It
+ also learnt a new option --instance for extracting and unescaping the
+ instance part of a unit name.
+
+ * sd-bus now provides the sd_bus_message_readv() which is similar to
+ sd_bus_message_read() but takes a va_list object. The pair
+ sd_bus_set_method_call_timeout() and sd_bus_get_method_call_timeout()
+ has been added for configuring the default method call timeout to
+ use. sd_bus_error_move() may be used to efficiently move the contents
+ from one sd_bus_error structure to another, invalidating the
+ source. sd_bus_set_close_on_exit() and sd_bus_get_close_on_exit() may
+ be used to control whether a bus connection object is automatically
+ flushed when an sd-event loop is exited.
+
+ * When processing classic BSD syslog log messages, journald will now
+ save the original time-stamp string supplied in the new
+ SYSLOG_TIMESTAMP= journal field. This permits consumers to
+ reconstruct the original BSD syslog message more correctly.
+
+ * StandardOutput=/StandardError= in service files gained support for
+ new "append:…" parameters, for connecting STDOUT/STDERR of a service
+ to a file, and appending to it.
+
+ * The signal to use as last step of killing of unit processes is now
+ configurable. Previously it was hard-coded to SIGKILL, which may now
+ be overridden with the new KillSignal= setting. Note that this is the
+ signal used when regular termination (i.e. SIGTERM) does not suffice.
+ Similarly, the signal used when aborting a program in case of a
+ watchdog timeout may now be configured too (WatchdogSignal=).
+
+ * The XDG_SESSION_DESKTOP environment variable may now be configured in
+ the pam_systemd argument line, using the new desktop= switch. This is
+ useful to initialize it properly from a display manager without
+ having to touch C code.
+
+ * Most configuration options that previously accepted percentage values
+ now also accept permille values with the '‰' suffix (instead of '%').
+
+ * systemd-resolved may now optionally use OpenSSL instead of GnuTLS for
+ DNS-over-TLS.
+
+ * systemd-resolved's configuration file resolved.conf gained a new
+ option ReadEtcHosts= which may be used to turn off processing and
+ honoring /etc/hosts entries.
+
+ * The "--wait" switch may now be passed to "systemctl
+ is-system-running", in which case the tool will synchronously wait
+ until the system finished start-up.
+
+ * hostnamed gained a new bus call to determine the DMI product UUID.
+
+ * On x86-64 systemd will now prefer using the RDRAND processor
+ instruction over /dev/urandom whenever it requires randomness that
+ neither has to be crypto-grade nor should be reproducible. This
+ should substantially reduce the amount of entropy systemd requests
+ from the kernel during initialization on such systems, though not
+ reduce it to zero. (Why not zero? systemd still needs to allocate
+ UUIDs and such uniquely, which require high-quality randomness.)
+
+ * networkd gained support for Foo-Over-UDP, ERSPAN and ISATAP
+ tunnels. It also gained a new option ForceDHCPv6PDOtherInformation=
+ for forcing the "Other Information" bit in IPv6 RA messages. The
+ bonding logic gained four new options AdActorSystemPriority=,
+ AdUserPortKey=, AdActorSystem= for configuring various 802.3ad
+ aspects, and DynamicTransmitLoadBalancing= for enabling dynamic
+ shuffling of flows. The tunnel logic gained a new
+ IPv6RapidDeploymentPrefix= option for configuring IPv6 Rapid
+ Deployment. The policy rule logic gained four new options IPProtocol=,
+ SourcePort= and DestinationPort=, InvertRule=. The bridge logic gained
+ support for the MulticastToUnicast= option. networkd also gained
+ support for configuring static IPv4 ARP or IPv6 neighbor entries.
+
+ * .preset files (as read by 'systemctl preset') may now be used to
+ instantiate services.
+
+ * /etc/crypttab now understands the sector-size= option to configure
+ the sector size for an encrypted partition.
+
+ * Key material for encrypted disks may now be placed on a formatted
+ medium, and referenced from /etc/crypttab by the UUID of the file
+ system, followed by "=" suffixed by the path to the key file.
+
+ * The "collect" udev component has been removed without replacement, as
+ it is neither used nor maintained.
+
+ * When the RuntimeDirectory=, StateDirectory=, CacheDirectory=,
+ LogsDirectory=, ConfigurationDirectory= settings are used in a
+ service the executed processes will now receive a set of environment
+ variables containing the full paths of these directories.
+ Specifically, RUNTIME_DIRECTORY=, STATE_DIRECTORY, CACHE_DIRECTORY,
+ LOGS_DIRECTORY, CONFIGURATION_DIRECTORY are now set if these options
+ are used. Note that these options may be used multiple times per
+ service in which case the resulting paths will be concatenated and
+ separated by colons.
+
+ * Predictable interface naming has been extended to cover InfiniBand
+ NICs. They will be exposed with an "ib" prefix.
+
+ * tmpfiles.d/ line types may now be suffixed with a '-' character, in
+ which case the respective line failing is ignored.
+
+ * .link files may now be used to configure the equivalent to the
+ "ethtool advertise" commands.
+
+ * The sd-device.h and sd-hwdb.h APIs are now exported, as an
+ alternative to libudev.h. Previously, the latter was just an internal
+ wrapper around the former, but now these two APIs are exposed
+ directly.
+
+ * sd-id128.h gained a new function sd_id128_get_boot_app_specific()
+ which calculates an app-specific boot ID similar to how
+ sd_id128_get_machine_app_specific() generates an app-specific machine
+ ID.
+
+ * A new tool systemd-id128 has been added that can be used to determine
+ and generate various 128bit IDs.
+
+ * /etc/os-release gained two new standardized fields DOCUMENTATION_URL=
+ and LOGO=.
+
+ * systemd-hibernate-resume-generator will now honor the "noresume"
+ kernel command line option, in which case it will bypass resuming
+ from any hibernated image.
+
+ * The systemd-sleep.conf configuration file gained new options
+ AllowSuspend=, AllowHibernation=, AllowSuspendThenHibernate=,
+ AllowHybridSleep= for prohibiting specific sleep modes even if the
+ kernel exports them.
+
+ * portablectl is now officially supported and has thus moved to
+ /usr/bin/.
+
+ * bootctl learnt the two new commands "set-default" and "set-oneshot"
+ for setting the default boot loader item to boot to (either
+ persistently or only for the next boot). This is currently only
+ compatible with sd-boot, but may be implemented on other boot loaders
+ too, that follow the boot loader interface. The updated interface is
+ now documented here:
+
+ https://systemd.io/BOOT_LOADER_INTERFACE
+
+ * A new kernel command line option systemd.early_core_pattern= is now
+ understood which may be used to influence the core_pattern PID 1
+ installs during early boot.
+
+ * busctl learnt two new options -j and --json= for outputting method
+ call replies, properties and monitoring output in JSON.
+
+ * journalctl's JSON output now supports simple ANSI coloring as well as
+ a new "json-seq" mode for generating RFC7464 output.
+
+ * Unit files now support the %g/%G specifiers that resolve to the UNIX
+ group/GID of the service manager runs as, similar to the existing
+ %u/%U specifiers that resolve to the UNIX user/UID.
+
+ * systemd-logind learnt a new global configuration option
+ UserStopDelaySec= that may be set in logind.conf. It specifies how
+ long the systemd --user instance shall remain started after a user
+ logs out. This is useful to speed up repetitive re-connections of the
+ same user, as it means the user's service manager doesn't have to be
+ stopped/restarted on each iteration, but can be reused between
+ subsequent options. This setting defaults to 10s. systemd-logind also
+ exports two new properties on its Manager D-Bus objects indicating
+ whether the system's lid is currently closed, and whether the system
+ is on AC power.
+
+ * systemd gained support for a generic boot counting logic, which
+ generically permits automatic reverting to older boot loader entries
+ if newer updated ones don't work. The boot loader side is implemented
+ in sd-boot, but is kept open for other boot loaders too. For details
+ see:
+
+ https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT
+
+ * The SuccessAction=/FailureAction= unit file settings now learnt two
+ new parameters: "exit" and "exit-force", which result in immediate
+ exiting of the service manager, and are only useful in systemd --user
+ and container environments.
+
+ * Unit files gained support for a pair of options
+ FailureActionExitStatus=/SuccessActionExitStatus= for configuring the
+ exit status to use as service manager exit status when
+ SuccessAction=/FailureAction= is set to exit or exit-force.
+
+ * A pair of LogRateLimitIntervalSec=/LogRateLimitBurst= per-service
+ options may now be used to configure the log rate limiting applied by
+ journald per-service.
+
+ * systemd-analyze gained a new verb "timespan" for parsing and
+ normalizing time span values (i.e. strings like "5min 7s 8us").
+
+ * systemd-analyze also gained a new verb "security" for analyzing the
+ security and sand-boxing settings of services in order to determine an
+ "exposure level" for them, indicating whether a service would benefit
+ from more sand-boxing options turned on for them.
+
+ * "systemd-analyze syscall-filter" will now also show system calls
+ supported by the local kernel but not included in any of the defined
+ groups.
+
+ * .nspawn files now understand the Ephemeral= setting, matching the
+ --ephemeral command line switch.
+
+ * sd-event gained the new APIs sd_event_source_get_floating() and
+ sd_event_source_set_floating() for controlling whether a specific
+ event source is "floating", i.e. destroyed along with the even loop
+ object itself.
+
+ * Unit objects on D-Bus gained a new "Refs" property that lists all
+ clients that currently have a reference on the unit (to ensure it is
+ not unloaded).
+
+ * The JoinControllers= option in system.conf is no longer supported, as
+ it didn't work correctly, is hard to support properly, is legacy (as
+ the concept only exists on cgroupsv1) and apparently wasn't used.
+
+ * Journal messages that are generated whenever a unit enters the failed
+ state are now tagged with a unique MESSAGE_ID. Similarly, messages
+ generated whenever a service process exits are now made recognizable,
+ too. A taged message is also emitted whenever a unit enters the
+ "dead" state on success.
+
+ * systemd-run gained a new switch --working-directory= for configuring
+ the working directory of the service to start. A shortcut -d is
+ equivalent, setting the working directory of the service to the
+ current working directory of the invoking program. The new --shell
+ (or just -S) option has been added for invoking the $SHELL of the
+ caller as a service, and implies --pty --same-dir --wait --collect
+ --service-type=exec. Or in other words, "systemd-run -S" is now the
+ quickest way to quickly get an interactive in a fully clean and
+ well-defined system service context.
+
+ * machinectl gained a new verb "import-fs" for importing an OS tree
+ from a directory. Moreover, when a directory or tarball is imported
+ and single top-level directory found with the OS itself below the OS
+ tree is automatically mangled and moved one level up.
+
+ * systemd-importd will no longer set up an implicit btrfs loop-back
+ file system on /var/lib/machines. If one is already set up, it will
+ continue to be used.
+
+ * A new generator "systemd-run-generator" has been added. It will
+ synthesize a unit from one or more program command lines included in
+ the kernel command line. This is very useful in container managers
+ for example:
+
+ # systemd-nspawn -i someimage.raw -b systemd.run='"some command line"'
+
+ This will run "systemd-nspawn" on an image, invoke the specified
+ command line and immediately shut down the container again, returning
+ the command line's exit code.
+
+ * The block device locking logic is now documented:
+
+ https://systemd.io/BLOCK_DEVICE_LOCKING
+
+ * loginctl and machinectl now optionally output the various tables in
+ JSON using the --output= switch. It is our intention to add similar
+ support to systemctl and all other commands.
+
+ * udevadm's query and trigger verb now optionally take a .device unit
+ name as argument.
+
+ * systemd-udevd's network naming logic now understands a new
+ net.naming-scheme= kernel command line switch, which may be used to
+ pick a specific version of the naming scheme. This helps stabilizing
+ interface names even as systemd/udev are updated and the naming logic
+ is improved.
+
+ * sd-id128.h learnt two new auxiliary helpers: sd_id128_is_allf() and
+ SD_ID128_ALLF to test if a 128bit ID is set to all 0xFF bytes, and to
+ initialize one to all 0xFF.
+
+ * After loading the SELinux policy systemd will now recursively relabel
+ all files and directories listed in
+ /run/systemd/relabel-extra.d/*.relabel (which should be simple
+ newline separated lists of paths) in addition to the ones it already
+ implicitly relabels in /run, /dev and /sys. After the relabelling is
+ completed the *.relabel files (and /run/systemd/relabel-extra.d/) are
+ removed. This is useful to permit initrds (i.e. code running before
+ the SELinux policy is in effect) to generate files in the host
+ filesystem safely and ensure that the correct label is applied during
+ the transition to the host OS.
+
+ * KERNEL API BREAKAGE: Linux kernel 4.18 changed behaviour regarding
+ mknod() handling in user namespaces. Previously mknod() would always
+ fail with EPERM in user namespaces. Since 4.18 mknod() will succeed
+ but device nodes generated that way cannot be opened, and attempts to
+ open them result in EPERM. This breaks the "graceful fallback" logic
+ in systemd's PrivateDevices= sand-boxing option. This option is
+ implemented defensively, so that when systemd detects it runs in a
+ restricted environment (such as a user namespace, or an environment
+ where mknod() is blocked through seccomp or absence of CAP_SYS_MKNOD)
+ where device nodes cannot be created the effect of PrivateDevices= is
+ bypassed (following the logic that 2nd-level sand-boxing is not
+ essential if the system systemd runs in is itself already sand-boxed
+ as a whole). This logic breaks with 4.18 in container managers where
+ user namespacing is used: suddenly PrivateDevices= succeeds setting
+ up a private /dev/ file system containing devices nodes — but when
+ these are opened they don't work.
+
+ At this point is is recommended that container managers utilizing
+ user namespaces that intend to run systemd in the payload explicitly
+ block mknod() with seccomp or similar, so that the graceful fallback
+ logic works again.
+
+ We are very sorry for the breakage and the requirement to change
+ container configurations for newer kernels. It's purely caused by an
+ incompatible kernel change. The relevant kernel developers have been
+ notified about this userspace breakage quickly, but they chose to
+ ignore it.
+
+ Contributions from: afg, Alan Jenkins, Aleksei Timofeyev, Alexander
+ Filippov, Alexander Kurtz, Alexey Bogdanenko, Andreas Henriksson,
+ Andrew Jorgensen, Anita Zhang, apnix-uk, Arkan49, Arseny Maslennikov,
+ asavah, Asbjørn Apeland, aszlig, Bastien Nocera, Ben Boeckel, Benedikt
+ Morbach, Benjamin Berg, Bruce Zhang, Carlo Caione, Cedric Viou, Chen
+ Qi, Chris Chiu, Chris Down, Chris Morin, Christian Rebischke, Claudius
+ Ellsel, Colin Guthrie, dana, Daniel, Daniele Medri, Daniel Kahn
+ Gillmor, Daniel Rusek, Daniel van Vugt, Dariusz Gadomski, Dave Reisner,
+ David Anderson, Davide Cavalca, David Leeds, David Malcolm, David
+ Strauss, David Tardon, Dimitri John Ledkov, Dmitry Torokhov, dj-kaktus,
+ Dongsu Park, Elias Probst, Emil Soleyman, Erik Kooistra, Ervin Peters,
+ Evgeni Golov, Evgeny Vereshchagin, Fabrice Fontaine, Faheel Ahmad,
+ Faizal Luthfi, Felix Yan, Filipe Brandenburger, Franck Bui, Frank
+ Schaefer, Frantisek Sumsal, Gautier Husson, Gianluca Boiano, Giuseppe
+ Scrivano, glitsj16, Hans de Goede, Harald Hoyer, Harry Mallon, Harshit
+ Jain, Helmut Grohne, Henry Tung, Hui Yiqun, imayoda, Insun Pyo, Iwan
+ Timmer, Jan Janssen, Jan Pokorný, Jan Synacek, Jason A. Donenfeld,
+ javitoom, Jérémy Nouhaud, Jeremy Su, Jiuyang Liu, João Paulo Rechi
+ Vita, Joe Hershberger, Joe Rayhawk, Joerg Behrmann, Joerg Steffens,
+ Jonas Dorel, Jon Ringle, Josh Soref, Julian Andres Klode, Jun Bo Bi,
+ Jürg Billeter, Keith Busch, Khem Raj, Kirill Marinushkin, Larry
+ Bernstone, Lennart Poettering, Lion Yang, Li Song, Lorenz
+ Hübschle-Schneider, Lubomir Rintel, Lucas Werkmeister, Ludwin Janvier,
+ Lukáš Nykrýn, Luke Shumaker, mal, Marc-Antoine Perennou, Marcin
+ Skarbek, Marco Trevisan (Treviño), Marian Cepok, Mario Hros, Marko
+ Myllynen, Markus Grimm, Martin Pitt, Martin Sobotka, Martin Wilck,
+ Mathieu Trudel-Lapierre, Matthew Leeds, Michael Biebl, Michael Olbrich,
+ Michael 'pbone' Pobega, Michael Scherer, Michal Koutný, Michal
+ Sekletar, Michal Soltys, Mike Gilbert, Mike Palmer, Muhammet Kara, Neal
+ Gompa, Neil Brown, Network Silence, Niklas Tibbling, Nikolas Nyby,
+ Nogisaka Sadata, Oliver Smith, Patrik Flykt, Pavel Hrdina, Paweł
+ Szewczyk, Peter Hutterer, Piotr DrÄ…g, Ray Strode, Reinhold Mueller,
+ Renaud Métrich, Roman Gushchin, Ronny Chevalier, Rubén Suárez Alvarez,
+ Ruixin Bao, RussianNeuroMancer, Ryutaroh Matsumoto, Saleem Rashid, Sam
+ Morris, Samuel Morris, Sandy Carter, scootergrisen, Sébastien Bacher,
+ Sergey Ptashnick, Shawn Landden, Shengyao Xue, Shih-Yuan Lee
+ (FourDollars), Silvio Knizek, Sjoerd Simons, Stasiek Michalski, Stephen
+ Gallagher, Steven Allen, Steve Ramage, Susant Sahani, Sven Joachim,
+ Sylvain Plantefève, Tanu Kaskinen, Tejun Heo, Thiago Macieira, Thomas
+ Blume, Thomas Haller, Thomas H. P. Andersen, Tim Ruffing, TJ, Tobias
+ Jungel, Todd Walton, Tommi Rantala, Tomsod M, Tony Novak, Tore
+ Anderson, Trevonn, Victor Laskurain, Victor Tapia, Violet Halo, Vojtech
+ Trefny, welaq, William A. Kennington III, William Douglas, Wyatt Ward,
+ Xiang Fan, Xi Ruoyao, Xuanwo, Yann E. Morin, YmrDtnJu, Yu Watanabe,
+ Zbigniew Jędrzejewski-Szmek, Zhang Xianwei, Zsolt Dollenstein
+
+ — Warsaw, 2018-12-21
+
CHANGES WITH 239:
* NETWORK INTERFACE DEVICE NAMING CHANGES: systemd-udevd's "net_id"
@@ -82,7 +583,28 @@ CHANGES WITH 239:
* systemd-resolved.service and systemd-networkd.service now set
DynamicUser=yes. The users systemd-resolve and systemd-network are
- not created by systemd-sysusers.
+ not created by systemd-sysusers anymore.
+
+ NOTE: This has a chance of breaking nss-ldap and similar NSS modules
+ that embedd a network facing module into any process using getpwuid()
+ or related call: the dynamic allocation of the user ID for
+ systemd-resolved.service means the service manager has to check NSS
+ if the user name is already taken when forking off the service. Since
+ the user in the common case won't be defined in /etc/passwd the
+ lookup is likely to trigger nss-ldap which in turn might use NSS to
+ ask systemd-resolved for hostname lookups. This will hence result in
+ a deadlock: a user name lookup in order to start
+ systemd-resolved.service will result in a host name lookup for which
+ systemd-resolved.service needs to be started already. There are
+ multiple ways to work around this problem: pre-allocate the
+ "systemd-resolve" user on such systems, so that nss-ldap won't be
+ triggered; or use a different NSS package that doesn't do networking
+ in-process but provides a local asynchronous name cache; or configure
+ the NSS package to avoid lookups for UIDs in the range `pkg-config
+ systemd --variable=dynamicuidmin` … `pkg-config systemd
+ --variable=dynamicuidmax`, so that it does not consider itself
+ authoritative for the same UID range systemd allocates dynamic users
+ from.
* The systemd-resolve tool has been renamed to resolvectl (it also
remains available under the old name, for compatibility), and its
@@ -95,7 +617,7 @@ CHANGES WITH 239:
Debian and FreeBSD resolvconf tool.
* Support for suspend-then-hibernate has been added, i.e. a sleep mode
- where the system initially suspends, and after a time-out resumes and
+ where the system initially suspends, and after a timeout resumes and
hibernates again.
* networkd's ClientIdentifier= now accepts a new option "duid-only". If
@@ -300,13 +822,13 @@ CHANGES WITH 239:
* New documentation has been added to document cgroups delegation,
portable services and the various code quality tools we have set up:
- https://github.com/systemd/systemd/blob/master/doc/CGROUP_DELEGATION.md
- https://github.com/systemd/systemd/blob/master/doc/PORTABLE_SERVICES.md
- https://github.com/systemd/systemd/blob/master/doc/CODE_QUALITY.md
+ https://github.com/systemd/systemd/blob/master/docs/CGROUP_DELEGATION.md
+ https://github.com/systemd/systemd/blob/master/docs/PORTABLE_SERVICES.md
+ https://github.com/systemd/systemd/blob/master/docs/CODE_QUALITY.md
* The Boot Loader Specification has been added to the source tree.
- https://github.com/systemd/systemd/blob/master/doc/BOOT_LOADER_SPECIFICATION.md
+ https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md
While moving it into our source tree we have updated it and further
changes are now accepted through the usual github PR workflow.
@@ -546,10 +1068,9 @@ CHANGES WITH 237:
different from what the documentation said, and not particularly
useful, as repeated systemd-tmpfiles invocations would not be
idempotent and grow such files without bounds. With this release
- behaviour has been altered slightly, to match what the documentation
- says: lines of this type only have an effect if the indicated files
- don't exist yet, and only then the argument string is written to the
- file.
+ behaviour has been altered to match what the documentation says:
+ lines of this type only have an effect if the indicated files don't
+ exist yet, and only then the argument string is written to the file.
* FUTURE INCOMPATIBILITY: In systemd v238 we intend to slightly change
systemd-tmpfiles behaviour: previously, read-only files owned by root
@@ -1622,7 +2143,7 @@ CHANGES WITH 233:
* Documentation has been added that lists all of systemd's low-level
environment variables:
- https://github.com/systemd/systemd/blob/master/doc/ENVIRONMENT.md
+ https://github.com/systemd/systemd/blob/master/docs/ENVIRONMENT.md
* sd-daemon gained a new API sd_is_socket_sockaddr() for determining
whether a specific socket file descriptor matches a specified socket
@@ -3327,11 +3848,10 @@ CHANGES WITH 226:
correct dequeuing of real-time signals, without losing
signal events.
- * When systemd requests a PolicyKit decision when managing
- units it will now add additional fields to the request,
- including unit name and desired operation. This enables more
- powerful PolicyKit policies, that make decisions depending
- on these parameters.
+ * When systemd requests a polkit decision when managing units it
+ will now add additional fields to the request, including unit
+ name and desired operation. This enables more powerful polkit
+ policies, that make decisions depending on these parameters.
* nspawn learnt support for .nspawn settings files, that may
accompany the image files or directories of containers, and
@@ -3366,13 +3886,12 @@ CHANGES WITH 225:
options and allows other programs to query the values.
* SELinux access control when enabling/disabling units is no
- longer enforced with this release. The previous
- implementation was incorrect, and a new corrected
- implementation is not yet available. As unit file operations
- are still protected via PolicyKit and D-Bus policy this is
- not a security problem. Yet, distributions which care about
- optimal SELinux support should probably not stabilize on
- this release.
+ longer enforced with this release. The previous implementation
+ was incorrect, and a new corrected implementation is not yet
+ available. As unit file operations are still protected via
+ polkit and D-Bus policy this is not a security problem. Yet,
+ distributions which care about optimal SELinux support should
+ probably not stabilize on this release.
* sd-bus gained support for matches of type "arg0has=", that
test for membership of strings in string arrays sent in bus
@@ -3744,11 +4263,10 @@ CHANGES WITH 220:
* systemd-importd gained support for verifying downloaded
images with gpg2 (previously only gpg1 was supported).
- * systemd-machined, systemd-logind, systemd: most bus calls
- are now accessible to unprivileged processes via
- PolicyKit. Also, systemd-logind will now allow users to kill
- their own sessions without further privileges or
- authorization.
+ * systemd-machined, systemd-logind, systemd: most bus calls are
+ now accessible to unprivileged processes via polkit. Also,
+ systemd-logind will now allow users to kill their own sessions
+ without further privileges or authorization.
* systemd-shutdownd has been removed. This service was
previously responsible for implementing scheduled shutdowns
@@ -4403,7 +4921,7 @@ CHANGES WITH 217:
/run/systemd/user directory that was already previously
supported, but is under the control of the user.
- * Job timeouts (i.e. time-outs on the time a job that is
+ * Job timeouts (i.e. timeouts on the time a job that is
queued stays in the run queue) can now optionally result in
immediate reboot or power-off actions (JobTimeoutAction= and
JobTimeoutRebootArgument=). This is useful on ".target"
@@ -4530,11 +5048,11 @@ CHANGES WITH 217:
directly from now on, again.
* Support for the new ALLOW_INTERACTIVE_AUTHORIZATION D-Bus
- message flag has been added for all of systemd's PolicyKit
- authenticated method calls has been added. In particular
- this now allows optional interactive authorization via
- PolicyKit for many of PID1's privileged operations such as
- unit file enabling and disabling.
+ message flag has been added for all of systemd's polkit
+ authenticated method calls has been added. In particular this
+ now allows optional interactive authorization via polkit for
+ many of PID1's privileged operations such as unit file
+ enabling and disabling.
* "udevadm hwdb --update" learnt a new switch "--usr" for
placing the rebuilt hardware database in /usr instead of
@@ -4613,11 +5131,11 @@ CHANGES WITH 216:
well as the user/group databases, which should enhance
compatibility with certain tools like grpck.
- * A number of bus APIs of PID 1 now optionally consult
- PolicyKit to permit access for otherwise unprivileged
- clients under certain conditions. Note that this currently
- doesn't support interactive authentication yet, but this is
- expected to be added eventually, too.
+ * A number of bus APIs of PID 1 now optionally consult polkit to
+ permit access for otherwise unprivileged clients under certain
+ conditions. Note that this currently doesn't support
+ interactive authentication yet, but this is expected to be
+ added eventually, too.
* /etc/machine-info now has new fields for configuring the
deployment environment of the machine, as well as the
@@ -7090,8 +7608,8 @@ CHANGES WITH 198:
the rest of the package. It also has been updated to work
correctly in initrds.
- * Policykit previously has been runtime optional, and is now
- also compile time optional via a configure switch.
+ * polkit previously has been runtime optional, and is now also
+ compile time optional via a configure switch.
* systemd-analyze has been reimplemented in C. Also "systemctl
dot" has moved into systemd-analyze.
@@ -7259,9 +7777,9 @@ CHANGES WITH 197:
user/vendor or is automatically determined from ACPI and DMI
information if possible.
- * A number of PolicyKit actions are now bound together with
- "imply" rules. This should simplify creating UIs because
- many actions will now authenticate similar ones as well.
+ * A number of polkit actions are now bound together with "imply"
+ rules. This should simplify creating UIs because many actions
+ will now authenticate similar ones as well.
* Unit files learnt a new condition ConditionACPower= which
may be used to conditionalize a unit depending on whether an
@@ -7400,14 +7918,13 @@ CHANGES WITH 196:
to maintain the necessary patches downstream, or find a
different solution. (Talk to us if you have questions!)
- * Various systemd components will now bypass PolicyKit checks
- for root and otherwise handle properly if PolicyKit is not
- found to be around. This should fix most issues for
- PolicyKit-less systems. Quite frankly this should have been
- this way since day one. It is absolutely our intention to
- make systemd work fine on PolicyKit-less systems, and we
- consider it a bug if something does not work as it should if
- PolicyKit is not around.
+ * Various systemd components will now bypass polkit checks for
+ root and otherwise handle properly if polkit is not found to
+ be around. This should fix most issues for polkit-less
+ systems. Quite frankly this should have been this way since
+ day one. It is absolutely our intention to make systemd work
+ fine on polkit-less systems, and we consider it a bug if
+ something does not work as it should if polkit is not around.
* For embedded systems it is now possible to build udev and
systemd without blkid and/or kmod support.
diff --git a/README b/README
index 2cde08c37e..4439be11f0 100644
--- a/README
+++ b/README
@@ -148,12 +148,14 @@ REQUIREMENTS:
libacl (optional)
libselinux (optional)
liblzma (optional)
- liblz4 >= 119 (optional)
+ liblz4 >= 1.3.0 / 130 (optional)
libgcrypt (optional)
libqrencode (optional)
libmicrohttpd (optional)
libpython (optional)
libidn2 or libidn (optional)
+ gnutls >= 3.1.4 (optional, >= 3.5.3 is required to support DNS-over-TLS with gnutls)
+ openssl >= 1.1.0 (optional, required to support DNS-over-TLS with openssl)
elfutils >= 158 (optional)
polkit (optional)
pkg-config
@@ -161,18 +163,18 @@ REQUIREMENTS:
docbook-xsl (optional, required for documentation)
xsltproc (optional, required for documentation)
python-lxml (optional, required to build the indices)
- python >= 3.4, meson >= 0.44, ninja
+ python >= 3.5, meson >= 0.46, ninja
gcc, awk, sed, grep, m4, and similar tools
During runtime, you need the following additional
dependencies:
util-linux >= v2.27.1 required
- dbus >= 1.4.0 (strictly speaking optional, but recommended)
+ dbus >= 1.9.14 (strictly speaking optional, but recommended)
NOTE: If using dbus < 1.9.18, you should override the default
policy directory (--with-dbuspolicydir=/etc/dbus-1/system.d).
dracut (optional)
- PolicyKit (optional)
+ polkit (optional)
To build in directory build/:
meson build/ && ninja -C build
@@ -204,10 +206,12 @@ REQUIREMENTS:
make use of DynamicUser= now, hence enabling nss-systemd is not
optional.
- Note that the build prefix for systemd must be /usr. -Dsplit-usr=false
- (which is the default and does not need to be specified) is the
- recommended setting, and -Dsplit-usr=true should be used on systems
- which have /usr on a separate partition.
+ Note that the build prefix for systemd must be /usr. (Moreover,
+ packages systemd relies on — such as D-Bus — really should use the same
+ prefix, otherwise you are on your own.) -Dsplit-usr=false (which is the
+ default and does not need to be specified) is the recommended setting,
+ and -Dsplit-usr=true should be used on systems which have /usr on a
+ separate partition.
Additional packages are necessary to run some tests:
- busybox (used by test/TEST-13-NSPAWN-SMOKE)
diff --git a/README.md b/README.md
index a57566834c..72484f620c 100644
--- a/README.md
+++ b/README.md
@@ -2,24 +2,26 @@
<a href="https://in.waw.pl/systemd-github-state/systemd-systemd-issues.svg"><img align="right" src="https://in.waw.pl/systemd-github-state/systemd-systemd-issues-small.svg" alt="Count of open issues over time"></a>
<a href="https://in.waw.pl/systemd-github-state/systemd-systemd-pull-requests.svg"><img align="right" src="https://in.waw.pl/systemd-github-state/systemd-systemd-pull-requests-small.svg" alt="Count of open pull requests over time"></a>
-[![Build Status](https://semaphoreci.com/api/v1/projects/28a5a3ca-3c56-4078-8b5e-7ed6ef912e14/443470/shields_badge.svg)](https://semaphoreci.com/systemd/systemd)<br/>
+[![Semaphore CI Build Status](https://semaphoreci.com/api/v1/projects/28a5a3ca-3c56-4078-8b5e-7ed6ef912e14/443470/shields_badge.svg)](https://semaphoreci.com/systemd/systemd)<br/>
[![Coverity Scan Status](https://scan.coverity.com/projects/350/badge.svg)](https://scan.coverity.com/projects/350)<br/>
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)<br/>
-[![Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)
+[![Travis CI Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)<br/>
+[![Language Grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/systemd/systemd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/systemd/systemd/context:cpp)<br/>
+[![CentOS CI Build Status](https://ci.centos.org/buildStatus/icon?job=systemd-pr-build)](https://ci.centos.org/job/systemd-pr-build/)
## Details
General information about systemd can be found in the [systemd Wiki](https://www.freedesktop.org/wiki/Software/systemd).
-Information about build requirements are provided in the [README file](../master/README).
+Information about build requirements is provided in the [README file](README).
-Consult our [NEWS file](../master/NEWS) for information about what's new in the most recent systemd versions.
+Consult our [NEWS file](NEWS) for information about what's new in the most recent systemd versions.
-Please see the [HACKING file](../master/doc/HACKING) for information how to hack on systemd and test your modifications.
+Please see the [Hacking guide](docs/HACKING.md) for information on how to hack on systemd and test your modifications.
-Please see our [Contribution Guidelines](../master/.github/CONTRIBUTING.md) for more information about filing GitHub Issues and posting GitHub Pull Requests.
+Please see our [Contribution Guidelines](docs/CONTRIBUTING.md) for more information about filing GitHub Issues and posting GitHub Pull Requests.
-When preparing patches for systemd, please follow our [Coding Style Guidelines](../master/doc/CODING_STYLE).
+When preparing patches for systemd, please follow our [Coding Style Guidelines](docs/CODING_STYLE.md).
If you are looking for support, please contact our [mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) or join our [IRC channel](irc://irc.freenode.org/%23systemd).
diff --git a/TODO b/TODO
index ff1008accf..24ec98416a 100644
--- a/TODO
+++ b/TODO
@@ -1,9 +1,13 @@
Bugfixes:
-* the error paths in usbffs_dispatch_ep() leak memory
-
* copy.c: set the right chattrs before copying files and others after
+* Many manager configuration settings that are only applicable to user
+ manager or system manager can be always set. It would be better to reject
+ them when parsing config.
+
+* Clarify what IPAddress* matches (source, destination, both?)
+
External:
* Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros.
@@ -14,16 +18,134 @@ Janitorial Clean-ups:
* Rearrange tests so that the various test-xyz.c match a specific src/basic/xyz.c again
-* copy.c: set the right chattrs before copying files and others after
-
* rework mount.c and swap.c to follow proper state enumeration/deserialization
semantics, like we do for device.c now
Features:
-* the stop-when-unneded feature should be reworked: there should be a queue of
- units, and we should only enqeueu stop jobs from a defer event that processes
- queue instead of right-away when we assume that a unit is now unneeded.
+* make MAINPID= message reception checks even stricter: if service uses User=,
+ then check sending UID and ignore message if it doesn't match the user or
+ root.
+
+* maybe trigger a uevent "change" on a device if "systemctl reload xyz.device"
+ is issued.
+
+* when importing an fs tree with machined, optionally apply userns-rec-chown
+
+* when importing an fs tree with machined, complain if image is not an OS
+
+* Maybe introduce a helper safe_exec() or so, which is to execve() which
+ safe_fork() is to fork(). And then make revert the RLIMIT_NOFILE soft limit
+ to 1K implicitly, unless explicitly opted-out.
+
+* rework seccomp/nnp logic that that even if User= is used in combination with
+ a seccomp option we don't have to set NNP. For that, change uid first whil
+ keeping CAP_SYS_ADMIN, then apply seccomp, the drop cap.
+
+* add a concept for automatically loading per-unit secrets off disk and
+ inserting them into the kernel keyring. Maybe SecretsDirectory= similar to
+ ConfigurationDirectory=.
+
+* when no locale is configured, default to UEFI's PlatformLang variable
+
+* When logind.conf contains HandleLidSwitch=suspend-then-hibernate and we can't
+ hibernate because the swap partition isn't large enough, still suspend
+
+* bootctl: implement Type #2 boot loader entry discovery
+
+* bootctl,sd-boot: actually honour the "architecture" key
+
+* when a socket unit is spawned with an AF_UNIX path in /var/run, complain and
+ patch it to use /run instead
+
+* set memory.oom.group in cgroupsv2 for all leaf cgroups (kernel v4.19+)
+
+* add a new syscall group "@esoteric" for more esoteric stuff such as bpf() and
+ usefaultd() and make systemd-analyze check for it.
+
+* paranoia: whenever we process passwords, call mlock() on the memory
+ first. i.e. look for all places we use string_erase()/string_free_erase() and
+ augment them with mlock()
+
+* whenever oom_kill memory.event event is triggered print a nice log message
+
+* Move RestrictAddressFamily= to the new cgroup create socket
+
+* support the bind/connect/sendmsg cgroup stuff for sandboxing, and possibly
+ patching around
+
+* maybe implicitly attach monotonic+realtime timestamps to outgoing messages in
+ log.c and sd-journal-send
+
+* chown() tty a service is attached to after the service goes down
+
+* optionally: turn on cgroup delegation for per-session scope units
+
+* 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: search drop-ins in $BOOT, too
+
+* sd-boot: add "oneshot boot timeout" variable support
+
+* sd-boot: automatically load EFI modules from some drop-in dir, so that people
+ can add in file system drivers and such
+
+* esp generator: also mount $BOOT if found
+
+* sd-boot: optionally, show boot menu when previous default boot item has
+ non-zero "tries done" count
+
+* logind: add "boot into bootmenu" API, and possibly even "boot into windows"
+ and "boot into macos".
+
+* bootspec.c: also enumerate EFI unified kernel images.
+
+* maybe set a special xattr on cgroups that have delegate=yes set, to make it
+ easy to mark cut points
+
+* introduce an option (or replacement) for "systemctl show" that outputs all
+ properties as JSON, similar to busctl's new JSON output. In contrast to that
+ it should skip the variant type string though.
+
+* augment CODE_FILE=, CODE_LINE= with something like CODE_BASE= or so which
+ contains some identifier for the project, which allows us to include
+ clickable links to source files generating these log messages. The identifier
+ could be some abberviated URL prefix or so (taking inspiration from Go
+ imports). For example, for systemd we could use
+ CODE_BASE=github.com/systemd/systemd/blob/98b0b1123cc or so which is
+ sufficient to build a link by prefixing "http://" and suffixing the
+ CODE_FILE.
+
+* when outputting log data with journalctl and the log data includes references
+ to configuration files (CONFIG_FILE=), create a clickable link for it.
+
+* Augment MESSAGE_ID with MESSAGE_BASE, in a similar fashion so that we can
+ make clickable links from log messages carrying a MESSAGE_ID, that lead to
+ some explanatory text online.
+
+* maybe extend .path units to expose fanotify() per-mount change events
+
+* Add a "systemctl list-units --by-slice" mode or so, which rearranges the
+ output of "systemctl list-units" slightly by showing the tree structure of
+ the slices, and the units attached to them.
+
+* 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
+ 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)
+
+* nspawn: make nspawn suitable for shell pipelines: instead of triggering a
+ hangup when input is finished, send ^D, which synthesizes an EOF. Then wait
+ for hangup or ^D before passing on the EOF.
* When reloading configuration PID 1 should reset all its properties to the
original defaults before calling parse_config()
@@ -33,15 +155,13 @@ Features:
* nspawn: greater control over selinux label?
-* cgroups: figure out if we can somehow communicate in a cleaner way whether a
- systemd instance not running in the cgroup root shall or shall not manage the
- attributes of its top-level cgroup. Currently it assumes it manages all, but
- then might get EPERM due to permission porblems/userns, which is OK, but this
- should be revisited to make clearer and also work if the payload systemd runs
- with full privs and without userns.
+* hibernate/s2h: make this robust and safe to enable in Fedora by default.
+ Specifically:
-* portables: introduce a new unit file directory /etc/systemd/system.attached/
- or so, where we attach portable services to
+ 1. add resume_offset support to the resume code (i.e. support swap files
+ properly)
+ 2. check if swap is on weird storage and refuse if so
+ 3. add autodetection of hibernation images
* cgroups: use inotify to get notified when somebody else modifies cgroups
owned by us, then log a friendly warning.
@@ -51,10 +171,6 @@ Features:
that our log messages could contain clickable links for example for unit
files and suchlike we operate on.
-* introduce a new SystemCallFilters= group called "@system-service" with a
- sensible default set for system services, then make use of them in portable
- profiles
-
* add support for "portablectl attach http://foobar.com/waaa.raw (i.e. importd integration)
* add attach --enable and attach --now (for attach+enable+start)
@@ -73,7 +189,7 @@ Features:
zero and is not open anymore, while the latter happens when a file is
unlinked from any dir.
-* port systemctl, systemd-inhibit, busctl, … over to format-table.[ch]'s table formatters
+* port systemctl, busctl, … over to format-table.[ch]'s table formatters
* pid1: lock image configured with RootDirectory=/RootImage= using the usual nspawn semantics while the unit is up
@@ -130,13 +246,6 @@ Features:
* calenderspec: add support for week numbers and day numbers within a
year. This would allow us to define "bi-weekly" triggers safely.
-* add bpf-based implementation of devices cgroup controller logic for compat
- with cgroupsv2 as supported by newest kernel
-
-* introduce sd_id128_get_boot_app_specific() which is like
- sd_id128_get_machine_app_specific(). After all on long-running systems both
- IDs have similar properties.
-
* sd-bus: add vtable flag, that may be used to request client creds implicitly
and asynchronously before dispatching the operation
@@ -151,14 +260,13 @@ Features:
* taint systemd if there are fewer than 65536 users assigned (userns) to the system.
-* deprecate PermissionsStartOnly= and RootDirectoryStartOnly= in favour of the ExecStart= prefix chars
+* deprecate RootDirectoryStartOnly= in favour of a new ExecStart= prefix char
* add a new RuntimeDirectoryPreserve= mode that defines a similar lifecycle for
the runtime dir as we maintain for the fdstore: i.e. keep it around as long
as the unit is running or has a job queued.
-* support projid-based quota in machinectl for containers, and then drop
- implicit btrfs loopback magic in machined
+* support projid-based quota in machinectl for containers
* Add NetworkNamespacePath= to specify a path to a network namespace
@@ -187,9 +295,6 @@ Features:
* beef up pam_systemd to take unit file settings such as cgroups properties as
parameters
-* a new "systemd-analyze security" tool outputting a checklist of security
- features a service does and does not implement
-
* maybe hook of xfs/ext4 quotactl() with services? i.e. automatically manage
the quota of a the user indicated in User= via unit file settings, like the
other resource management concepts. Would mix nicely with DynamicUser=1. Or
@@ -198,11 +303,6 @@ Features:
StateDirectory=, LogsDirectory=, CacheDirectory=, as well as RootDirectory= if it
is set, plus the whole disk space any image configured with RootImage=.
-* Introduce "exit" as an EmergencyAction value, and allow to configure a
- per-unit success/failure exit code to configure. This would be useful for
- running commands inside of services inside of containers, which could then
- propagate their failure state all the way up.
-
* In DynamicUser= mode: before selecting a UID, use disk quota APIs on relevant
disks to see if the UID is already in use.
@@ -210,15 +310,6 @@ Features:
for all units. It should be both a way to pin units into memory as well as a
wait to retrieve their exit data.
-* maybe set a new set of env vars for services, based on RuntimeDirectory=,
- StateDirectory=, LogsDirectory=, CacheDirectory= and ConfigurationDirectory=
- automatically. For example, there could be $RUNTIME_DIRECTORY,
- $STATE_DIRECTORY, $LOGS_DIRECTORY=, $CACHE_DIRECTORY and
- $CONFIGURATION_DIRECTORY or so. This could be useful to write services that
- can adapt to varying directories for these purposes. Special care has to be
- taken if multiple dirs are configured. Maybe avoid setting the env vars in
- that case?
-
* expose IO accounting data on the bus, show it in systemd-run --wait and log
about it in the resource log message
@@ -228,7 +319,7 @@ Features:
* show whether a service has out-of-date configuration in "systemctl status" by
using mtime data of ConfigurationDirectory=.
-* replace all uses of fgets() + LINE_MAX by read_line()
+* replace all remaining uses of fgets() + LINE_MAX by read_line()
* Add AddUser= setting to unit files, similar to DynamicUser=1 which however
creates a static, persistent user rather than a dynamic, transient user. We
@@ -326,8 +417,6 @@ Features:
on PID 1 with the relevant signals, and makes relevant files in /sys and
/proc (such as the sysrq stuff) unavailable
-* DeviceAllow= should also generate seccomp filters for mknod()
-
* make sure the ratelimit object can deal with USEC_INFINITY as way to turn off things
* journalctl: make sure -f ends when the container indicated by -M terminates
@@ -396,9 +485,6 @@ Features:
state.
http://lists.freedesktop.org/archives/systemd-devel/2015-April/030229.html
-* Maybe add support for the equivalent of "ethtool advertise" to .link files?
- http://lists.freedesktop.org/archives/systemd-devel/2015-April/030112.html
-
* The udev blkid built-in should expose a property that reflects
whether media was sensed in USB CF/SD card readers. This should then
be used to control SYSTEMD_READY=1/0 so that USB card readers aren't
@@ -410,8 +496,6 @@ Features:
* hostnamectl: show root image uuid
-* sysfs set api in libudev is not const
-
* Find a solution for SMACK capabilities stuff:
http://lists.freedesktop.org/archives/systemd-devel/2014-December/026188.html
@@ -439,8 +523,6 @@ Features:
* maybe add support for specifier expansion in user.conf, specifically DefaultEnvironment=
-* introduce systemd-timesync-wait.service or so to sync on an NTP fix?
-
* consider showing the unit names during boot up in the status output, not just the unit descriptions
* maybe allow timer units with an empty Units= setting, so that they
@@ -528,7 +610,6 @@ Features:
* cgroups:
- implement per-slice CPUFairScheduling=1 switch
- - handle jointly mounted controllers correctly
- introduce high-level settings for RT budget, swappiness
- how to reset dynamically changed unit cgroup attributes sanely?
- when reloading configuration, apply new cgroup configuration
@@ -582,7 +663,6 @@ Features:
- document chaining of signal handler for SIGCHLD and child handlers
- define more intervals where we will shift wakeup intervals around in, 1h, 6h, 24h, ...
- generate a failure of a default event loop is executed out-of-thread
- - maybe add support for inotify events (which we can do safely now, with O_PATH)
* investigate endianness issues of UUID vs. GUID
@@ -593,8 +673,6 @@ Features:
* add a pam module that on password changes updates any LUKS slot where the password matches
-* maybe add a generator that looks for "systemd.run=" on the kernel cmdline for container usercases...
-
* test/:
- add unit tests for config_parse_device_allow()
@@ -641,11 +719,9 @@ Features:
* logind:
- logind: optionally, ignore idle-hint logic for autosuspend, block suspend as long as a session is around
- - When we update the kernel all kind of hibernation should be prohibited until shutdown/reboot
- logind: wakelock/opportunistic suspend support
- Add pretty name for seats in logind
- logind: allow showing logout dialog from system?
- - session scopes/user unit: add RequiresMountsFor for the home directory of the user
- add Suspend() bus calls which take timestamps to fix double suspend issues when somebody hits suspend and closes laptop quickly.
- if pam_systemd is invoked by su from a process that is outside of a
any session we should probably just become a NOP, since that's
@@ -802,8 +878,6 @@ Features:
PID 1...
- optionally automatically add FORWARD rules to iptables whenever nspawn is
running, remove them when shut down.
- - maybe make copying of /etc/resolv.conf optional, and skip it if --read-only
- is used
* dissect
- refuse mounting over a mount point
@@ -818,16 +892,11 @@ Features:
"machinectl start" with a new --ephemeral switch
- "machinectl status" should also show internal logs of the container in
question
- - "machinectl list-images" should show os-release data, as well as
- machine-info data (including deployment level)
- "machinectl history"
- "machinectl diff"
- "machinectl commit" that takes a writable snapshot of a tree, invokes a
shell in it, and marks it read-only after use
-* importd:
- - generate a nice warning if mkfs.btrfs is missing
-
* cryptsetup:
- cryptsetup-generator: allow specification of passwords in crypttab itself
- support rd.luks.allow-discards= kernel cmdline params in cryptsetup generator
@@ -1015,8 +1084,6 @@ External:
* kernel: add device_type = "fb", "fbcon" to class "graphics"
-* drop accountsservice's StandardOutput=syslog and Type=dbus fields
-
* /usr/bin/service should actually show the new command line
* fedora: suggest auto-restart on failure, but not on success and not on coredump. also, ask people to think about changing the start limit logic. Also point people to RestartPreventExitStatus=, SuccessExitStatus=
diff --git a/catalog/meson.build b/catalog/meson.build
index 1b13150894..3db8e390f2 100644
--- a/catalog/meson.build
+++ b/catalog/meson.build
@@ -17,7 +17,6 @@ in_files = '''
support_url = get_option('support-url')
support_sed = 's~%SUPPORT_URL%~@0@~'.format(support_url)
-build_catalog_dir = meson.current_build_dir()
foreach file : in_files
custom_target(
diff --git a/catalog/systemd.be.catalog.in b/catalog/systemd.be.catalog.in
index 5011ea268d..c5786c4012 100644
--- a/catalog/systemd.be.catalog.in
+++ b/catalog/systemd.be.catalog.in
@@ -1,7 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1+
#
-# Copyright © 2015, 2016 Viktar VaÅ­ÄkieviÄ
-
# Message catalog for systemd's own messages
# Belarusian translation
@@ -175,7 +173,7 @@ Support: %SUPPORT_URL%
ПрацÑÑ Ð·Ð°Ð¿ÑƒÑку юніта @UNIT@ завершаны.
-Вынік: @RESULT@.
+Вынік: @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Юніт @UNIT@ ÑпынÑецца
@@ -198,7 +196,7 @@ Support: %SUPPORT_URL%
Збой юніта @UNIT@.
-Вынік: @RESULT@.
+Вынік: @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Юніт @UNIT@ перачытвае Ñваю канфігурацыю
@@ -214,7 +212,7 @@ Support: %SUPPORT_URL%
Юніт @UNIT@ перачытаў Ñваю канфігурацыю.
-Вынік: @RESULT@.
+Вынік: @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: ПрацÑÑ @EXECUTABLE@ не можа быць выкананы
diff --git a/catalog/systemd.be@latin.catalog.in b/catalog/systemd.be@latin.catalog.in
index 6a8b092669..bec5c6f048 100644
--- a/catalog/systemd.be@latin.catalog.in
+++ b/catalog/systemd.be@latin.catalog.in
@@ -1,7 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1+
#
-# Copyright © 2015, 2016 Viktar VaÅ­ÄkieviÄ
-
# Message catalog for systemd's own messages
# Belarusian Latin translation
@@ -178,7 +176,7 @@ Support: %SUPPORT_URL%
Praces zapusku junita @UNIT@ zavieršany.
-Vynik: @RESULT@.
+Vynik: @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Junit @UNIT@ spyniajecca
@@ -201,7 +199,7 @@ Support: %SUPPORT_URL%
Zboj junita @UNIT@.
-Vynik: @RESULT@.
+Vynik: @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Junit @UNIT@ pieraÄytvaje svaju kanfihuracyju
@@ -217,7 +215,7 @@ Support: %SUPPORT_URL%
Junit @UNIT@ pieraÄytaÅ­ svaju kanfihuracyju.
-Vynik: @RESULT@.
+Vynik: @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Praces @EXECUTABLE@ nie moža być vykanany
diff --git a/catalog/systemd.bg.catalog.in b/catalog/systemd.bg.catalog.in
index 64d616f381..41f7b21bce 100644
--- a/catalog/systemd.bg.catalog.in
+++ b/catalog/systemd.bg.catalog.in
@@ -178,7 +178,7 @@ Support: %SUPPORT_URL%
Стартирането на модул „@UNIT@“ завърши.
-Резултатът е: @RESULT@
+Резултатът е: @JOB_RESULT@
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Модул „@UNIT@“ Ñе Ñпира
@@ -201,7 +201,7 @@ Support: %SUPPORT_URL%
Модулът „@UNIT@“ не уÑÐ¿Ñ Ð´Ð° Ñтартира.
-Резултатът е: @RESULT@
+Резултатът е: @JOB_RESULT@
-- d34d037fff1847e6ae669a370e694725
Subject: Модулът „@UNIT@“ започна презареждане на наÑтройките Ñи
@@ -217,7 +217,7 @@ Support: %SUPPORT_URL%
Модулът „@UNIT@“ завърши презареждането на наÑтройките Ñи.
-Резултатът e: @RESULT@
+Резултатът e: @JOB_RESULT@
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Програмата „@EXECUTABLE@“ не уÑÐ¿Ñ Ð´Ð° Ñе Ñтартира
diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in
index f1bddc6f7d..5c6799ee5d 100644
--- a/catalog/systemd.catalog.in
+++ b/catalog/systemd.catalog.in
@@ -52,7 +52,8 @@ dropped, other services' messages are unaffected.
The limits controlling when messages are dropped may be configured
with RateLimitIntervalSec= and RateLimitBurst= in
-/etc/systemd/journald.conf. See journald.conf(5) for details.
+/etc/systemd/journald.conf or LogRateLimitIntervalSec= and LogRateLimitBurst=
+in the unit file. See journald.conf(5) and systemd.exec(5) for details.
-- e9bf28e6e834481bb6f48f548ad13606
Subject: Journal messages have been missed
@@ -188,59 +189,67 @@ System shutdown has been initiated. The shutdown has now begun and
all system services are terminated and all file systems unmounted.
-- 7d4958e842da4a758f6c1cdc7b36dcc5
-Subject: Unit @UNIT@ has begun start-up
+Subject: A start job for unit @UNIT@ has begun execution
Defined-By: systemd
Support: %SUPPORT_URL%
-Unit @UNIT@ has begun starting up.
+A start job for unit @UNIT@ has begun execution.
+
+The job identifier is @JOB_ID@.
-- 39f53479d3a045ac8e11786248231fbf
-Subject: Unit @UNIT@ has finished start-up
+Subject: A start job for unit @UNIT@ has finished successfully
Defined-By: systemd
Support: %SUPPORT_URL%
-Unit @UNIT@ has finished starting up.
+A start job for unit @UNIT@ has finished successfully.
-The start-up result is @RESULT@.
+The job identifier is @JOB_ID@.
--- de5b426a63be47a7b6ac3eaac82e2f6f
-Subject: Unit @UNIT@ has begun shutting down
+-- be02cf6855d2428ba40df7e9d022f03d
+Subject: A start job for unit @UNIT@ has failed
Defined-By: systemd
Support: %SUPPORT_URL%
-Unit @UNIT@ has begun shutting down.
+A start job for unit @UNIT@ has finished with a failure.
--- 9d1aaa27d60140bd96365438aad20286
-Subject: Unit @UNIT@ has finished shutting down
+The job identifier is @JOB_ID@ and the job result is @JOB_RESULT@.
+
+-- de5b426a63be47a7b6ac3eaac82e2f6f
+Subject: A stop job for unit @UNIT@ has begun execution
Defined-By: systemd
Support: %SUPPORT_URL%
-Unit @UNIT@ has finished shutting down.
+A stop job for unit @UNIT@ has begun execution.
--- be02cf6855d2428ba40df7e9d022f03d
-Subject: Unit @UNIT@ has failed
+The job identifier is @JOB_ID@.
+
+-- 9d1aaa27d60140bd96365438aad20286
+Subject: A stop job for unit @UNIT@ has finished
Defined-By: systemd
Support: %SUPPORT_URL%
-Unit @UNIT@ has failed.
+A stop job for unit @UNIT@ has finished.
-The result is @RESULT@.
+The job identifier is @JOB_ID@ and the job result is @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
-Subject: Unit @UNIT@ has begun reloading its configuration
+Subject: A reload job for unit @UNIT@ has begun execution
Defined-By: systemd
Support: %SUPPORT_URL%
-Unit @UNIT@ has begun reloading its configuration
+A reload job for unit @UNIT@ has begun execution.
+
+The job identifier is @JOB_ID@.
-- 7b05ebc668384222baa8881179cfda54
-Subject: Unit @UNIT@ has finished reloading its configuration
+Subject: A reload job for unit @UNIT@ has finished
Defined-By: systemd
Support: %SUPPORT_URL%
-Unit @UNIT@ has finished reloading its configuration
+A reload job for unit @UNIT@ has finished.
-The result is @RESULT@.
+The job identifier is @JOB_ID@ and the job result is @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Process @EXECUTABLE@ could not be executed
@@ -343,6 +352,29 @@ Support: %SUPPORT_URL%
The unit @UNIT@ completed and consumed the indicated resources.
+-- 7ad2d189f7e94e70a38c781354912448
+Subject: Unit succeeded
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+The unit @UNIT@ has successfully entered the 'dead' state.
+
+-- d9b373ed55a64feb8242e02dbe79a49c
+Subject: Unit failed
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+The unit @UNIT@ has entered the 'failed' state with result '@UNIT_RESULT@'.
+
+-- 98e322203f7a4ed290d09fe03c09fe15
+Subject: Unit process exited
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+An @COMMAND@= process belonging to unit @UNIT@ has exited.
+
+The process' exit code is '@EXIT_CODE@' and its exit status is @EXIT_STATUS@.
+
-- 50876a9db00f4c40bde1a2ad381c3a1b
Subject: The system is configured in a way that might cause problems
Defined-By: systemd
diff --git a/catalog/systemd.da.catalog.in b/catalog/systemd.da.catalog.in
index 4e2bec8a0f..aecfafa05f 100644
--- a/catalog/systemd.da.catalog.in
+++ b/catalog/systemd.da.catalog.in
@@ -159,7 +159,7 @@ Support: %SUPPORT_URL%
Enhed @UNIT@ er færdig med at starte op.
-Resultat for opstart er @RESULT@.
+Resultat for opstart er @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Enhed @UNIT@ har påbegyndt nedlukning
@@ -182,7 +182,7 @@ Support: %SUPPORT_URL%
Enhed @UNIT@ har fejlet.
-Resultatet er @RESULT@
+Resultatet er @JOB_RESULT@
-- d34d037fff1847e6ae669a370e694725
Subject: Enhed @UNIT@ har påbegyndt genindlæsning af sin konfiguration
@@ -198,7 +198,7 @@ Support: %SUPPORT_URL%
Enhed @UNIT@ er færdig med at genindlæse sin konfiguration
-Resultatet er: @RESULT@.
+Resultatet er: @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Process @EXECUTABLE@ kunne ikke eksekveres
diff --git a/catalog/systemd.fr.catalog.in b/catalog/systemd.fr.catalog.in
index 156b1a37dc..13edd083cb 100644
--- a/catalog/systemd.fr.catalog.in
+++ b/catalog/systemd.fr.catalog.in
@@ -191,7 +191,7 @@ Subject: L'unité (unit) @UNIT@ a terminé son démarrage
Defined-By: systemd
Support: %SUPPORT_URL%
-L'unité (unit) @UNIT@ a terminé son démarrage, avec le résultat @RESULT@.
+L'unité (unit) @UNIT@ a terminé son démarrage, avec le résultat @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: L'unité (unit) @UNIT@ a commencé à s'arrêter
@@ -212,7 +212,7 @@ Subject: L'unité (unit) @UNIT@ a échoué
Defined-By: systemd
Support: %SUPPORT_URL%
-L'unité (unit) @UNIT@ a échoué, avec le résultat @RESULT@.
+L'unité (unit) @UNIT@ a échoué, avec le résultat @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: L'unité (unit) @UNIT@ a commencé à recharger sa configuration
@@ -227,7 +227,7 @@ Defined-By: systemd
Support: %SUPPORT_URL%
L'unité (unit) @UNIT@ a terminé de recharger configuration,
-avec le résultat @RESULT@.
+avec le résultat @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Le processus @EXECUTABLE@ n'a pas pu être exécuté
diff --git a/catalog/systemd.hr.catalog.in b/catalog/systemd.hr.catalog.in
index c4808b4c7d..4526ae2a8c 100644
--- a/catalog/systemd.hr.catalog.in
+++ b/catalog/systemd.hr.catalog.in
@@ -173,7 +173,7 @@ Support: %SUPPORT_URL%
Jedinica @UNIT@ je završila pokretanje.
-Rezultat pokretanja je @RESULT@.
+Rezultat pokretanja je @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Jedinica @UNIT@ je zapoÄela iskljuÄivanje
@@ -196,7 +196,7 @@ Support: %SUPPORT_URL%
Jedinica @UNIT@ nije uspjela.
-Rezultat je @RESULT@.
+Rezultat je @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Jedinica @UNIT@ je zapoÄela ponovno uÄitavati podeÅ¡avanja
@@ -212,7 +212,7 @@ Support: %SUPPORT_URL%
Jedinica @UNIT@ je zavrÅ¡ila ponovno uÄitavati podeÅ¡avanja
-Rezultat je @RESULT@.
+Rezultat je @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Proces @EXECUTABLE@ se ne može pokrenuti
diff --git a/catalog/systemd.hu.catalog.in b/catalog/systemd.hu.catalog.in
index 6c6d7e7934..5565b80b2a 100644
--- a/catalog/systemd.hu.catalog.in
+++ b/catalog/systemd.hu.catalog.in
@@ -161,7 +161,7 @@ Support: %SUPPORT_URL%
A(z) @UNIT@ egység befejezte az indulást
-Az indítás eredménye: @RESULT@.
+Az indítás eredménye: @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: A(z) @UNIT@ egység megkezdte a leállást
@@ -184,7 +184,7 @@ Support: %SUPPORT_URL%
A(z) @UNIT@ egység hibát jelzett.
-Az eredmény: @RESULT@.
+Az eredmény: @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: A(z) @UNIT@ egység megkezdte a beállításainak újratöltését
@@ -200,7 +200,7 @@ Support: %SUPPORT_URL%
A(z) @UNIT@ egység befejezte a beállításainak újratöltését.
-Az eredmény: @RESULT@.
+Az eredmény: @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: A folyamat végrehajtása sikertelen: @EXECUTABLE@
diff --git a/catalog/systemd.it.catalog.in b/catalog/systemd.it.catalog.in
index 4fd1f2a933..8ce4fa5d92 100644
--- a/catalog/systemd.it.catalog.in
+++ b/catalog/systemd.it.catalog.in
@@ -191,7 +191,7 @@ Support: %SUPPORT_URL%
L'unità @UNIT@ ha terminato la fase di avvio.
-La fase di avvio è @RESULT@.
+La fase di avvio è @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: L'unità @UNIT@ inizia la fase di spegnimento
@@ -214,7 +214,7 @@ Support: %SUPPORT_URL%
L'unità @UNIT@ è fallita.
-Il risultato è @RESULT@.
+Il risultato è @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: L'unità @UNIT@ inizia a caricare la propria configurazione
@@ -230,7 +230,7 @@ Support: %SUPPORT_URL%
L'unità @UNIT@ è terminata ricaricando la propria configurazione
-Il risultato è @RESULT@.
+Il risultato è @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Il processo @EXECUTABLE@ non può essere eseguito
diff --git a/catalog/systemd.ko.catalog.in b/catalog/systemd.ko.catalog.in
index fc0faad02c..59fbde8b62 100644
--- a/catalog/systemd.ko.catalog.in
+++ b/catalog/systemd.ko.catalog.in
@@ -182,7 +182,7 @@ Support: %SUPPORT_URL%
@UNIT@ 유닛 ì‹œë™ì„ 마쳤습니다.
-ì‹œë™ ê²°ê³¼ëŠ” @RESULT@ 입니다.
+ì‹œë™ ê²°ê³¼ëŠ” @JOB_RESULT@ 입니다.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: @UNIT@ 유닛 ë내기 ë™ìž‘ 시작
@@ -205,7 +205,7 @@ Support: %SUPPORT_URL%
@UNIT@ 유닛 ë™ìž‘ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.
-결과는 @RESULT@ 입니다.
+결과는 @JOB_RESULT@ 입니다.
-- d34d037fff1847e6ae669a370e694725
Subject: @UNIT@ 유닛 설정 다시 ì½ê¸° 시작
@@ -221,7 +221,7 @@ Support: %SUPPORT_URL%
@UNIT@ ìœ ë‹›ì˜ ì„¤ì • 다시 ì½ê¸° ë™ìž‘ì„ ë냈습니다.
-결과는 @RESULT@ 입니다.
+결과는 @JOB_RESULT@ 입니다.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: @EXECUTABLE@ 프로세스 시작할 수 ì—†ìŒ
diff --git a/catalog/systemd.pl.catalog.in b/catalog/systemd.pl.catalog.in
index 998894bd0a..b6c6b54205 100644
--- a/catalog/systemd.pl.catalog.in
+++ b/catalog/systemd.pl.catalog.in
@@ -1,6 +1,4 @@
# SPDX-License-Identifier: LGPL-2.1+
-#
-# Copyright © 2014-2017 Piotr Drąg
# Message catalog for systemd's own messages
# Polish translation
@@ -56,7 +54,9 @@ Nie ma to wpływu na komunikaty innych usług.
Ograniczenia kontrolujące pomijanie komunikatów mogą być konfigurowane
za pomocą opcji RateLimitIntervalSec= i RateLimitBurst= w pliku
-/etc/systemd/journald.conf. Strona journald.conf(5) zawiera więcej informacji.
+/etc/systemd/journald.conf lub LogRateLimitIntervalSec= i LogRateLimitBurst=
+w pliku jednostki. Strony journald.conf(5) i systemd.exec(5) zawierają więcej
+informacji.
-- e9bf28e6e834481bb6f48f548ad13606
Subject: Utracono komunikaty dziennika
@@ -188,59 +188,67 @@ Zainicjowano wyłączenie systemu. Wyłączenie zostało rozpoczęte i wszystki
usługi systemowe zostały zakończone, a wszystkie systemy plików odmontowane.
-- 7d4958e842da4a758f6c1cdc7b36dcc5
-Subject: Rozpoczęto uruchamianie jednostki @UNIT@
+Subject: Rozpoczęto wykonywanie zadania uruchamiania dla jednostki @UNIT@
Defined-By: systemd
Support: %SUPPORT_URL%
-Jednostka @UNIT@ rozpoczęła uruchamianie.
+Rozpoczęto wykonywanie zadania uruchamiania dla jednostki @UNIT@.
+
+Identyfikator zadania: @JOB_ID@.
-- 39f53479d3a045ac8e11786248231fbf
-Subject: Ukończono uruchamianie jednostki @UNIT@
+Subject: Pomyślnie ukończono zadanie uruchamiania dla jednostki @UNIT@
Defined-By: systemd
Support: %SUPPORT_URL%
-Jednostka @UNIT@ ukończyła uruchamianie.
+Pomyślnie ukończono zadanie uruchamiania dla jednostki @UNIT@.
-Wynik uruchamiania: @RESULT@.
+Identyfikator zadania: @JOB_ID@.
--- de5b426a63be47a7b6ac3eaac82e2f6f
-Subject: Rozpoczęto wyłączanie jednostki @UNIT@
+-- be02cf6855d2428ba40df7e9d022f03d
+Subject: Zadanie uruchamiania dla jednostki @UNIT@ się nie powiodło
Defined-By: systemd
Support: %SUPPORT_URL%
-Jednostka @UNIT@ rozpoczęła wyłączanie.
+Zadanie uruchamiania dla jednostki @UNIT@ zostało ukończone z niepowodzeniem.
--- 9d1aaa27d60140bd96365438aad20286
-Subject: Ukończono wyłączanie jednostki @UNIT@
+Identyfikator zadania: @JOB_ID@, wynik zadania: @JOB_RESULT@.
+
+-- de5b426a63be47a7b6ac3eaac82e2f6f
+Subject: Rozpoczęto wykonywanie zadania zatrzymania dla jednostki @UNIT@
Defined-By: systemd
Support: %SUPPORT_URL%
-Jednostka @UNIT@ ukończyła wyłączanie.
+Rozpoczęto wykonywanie zadania zatrzymania dla jednostki @UNIT@.
--- be02cf6855d2428ba40df7e9d022f03d
-Subject: Jednostka @UNIT@ się nie powiodła
+Identyfikator zadania: @JOB_ID@.
+
+-- 9d1aaa27d60140bd96365438aad20286
+Subject: Ukończono zadanie zatrzymania dla jednostki @UNIT@
Defined-By: systemd
Support: %SUPPORT_URL%
-Jednostka @UNIT@ się nie powiodła.
+Ukończono zadanie zatrzymania dla jednostki @UNIT@.
-Wynik: @RESULT@.
+Identyfikator zadania: @JOB_ID@, wynik zadania: @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
-Subject: Rozpoczęto ponowne wczytywanie konfiguracji jednostki @UNIT@
+Subject: Rozpoczęto wykonywanie zadania ponownego wczytania dla jednostki @UNIT@
Defined-By: systemd
Support: %SUPPORT_URL%
-Jednostka @UNIT@ rozpoczęła ponowne wczytywanie swojej konfiguracji.
+Rozpoczęto wykonywanie zadania ponownego wczytania dla jednostki @UNIT@.
+
+Identyfikator zadania: @JOB_ID@.
-- 7b05ebc668384222baa8881179cfda54
-Subject: Ukończono ponowne wczytywanie konfiguracji jednostki @UNIT@
+Subject: Ukończono zadanie ponownego wczytania dla jednostki @UNIT@
Defined-By: systemd
Support: %SUPPORT_URL%
-Jednostka @UNIT@ ukończyła ponowne wczytywanie swojej konfiguracji.
+Ukończono zadanie ponownego wczytania dla jednostki @UNIT@.
-Wynik: @RESULT@.
+Identyfikator zadania: @JOB_ID@, wynik zadania: @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Nie można wykonać procesu @EXECUTABLE@
@@ -338,6 +346,30 @@ Support: %SUPPORT_URL%
Jednostka @UNIT@ została ukończona, zużywając wskazane zasoby.
+-- 7ad2d189f7e94e70a38c781354912448
+Subject: Jednostka się powiodła
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+Jednostka @UNIT@ pomyślnie przeszła do stanu „dead†(martwego).
+
+-- d9b373ed55a64feb8242e02dbe79a49c
+Subject: Jednostka się nie powiodła
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+Jednostka @UNIT@ przeszła do stanu „failed†(niepowodzenia)
+z wynikiem „@UNIT_RESULT@â€.
+
+-- 98e322203f7a4ed290d09fe03c09fe15
+Subject: Proces jednostki zakończył działanie
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+Proces @COMMAND@= należący do jednostki @UNIT@ zakończył działanie.
+
+Kod wyjÅ›cia procesu: „@EXIT_CODE@â€, jego stan wyjÅ›cia: @EXIT_STATUS@.
+
-- 50876a9db00f4c40bde1a2ad381c3a1b
Subject: System jest skonfigurowany w sposób, który może powodować problemy
Defined-By: systemd
diff --git a/catalog/systemd.pt_BR.catalog.in b/catalog/systemd.pt_BR.catalog.in
index db1cb03198..edaefb7164 100644
--- a/catalog/systemd.pt_BR.catalog.in
+++ b/catalog/systemd.pt_BR.catalog.in
@@ -162,7 +162,7 @@ Support: %SUPPORT_URL%
A unidade @UNIT@ concluiu a inicialização.
-The start-up result is @RESULT@.
+The start-up result is @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Unidade @UNIT@ sendo desligado
@@ -185,7 +185,7 @@ Support: %SUPPORT_URL%
A unidade @UNIT@ falhou.
-O resultado é @RESULT@.
+O resultado é @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Unidade @UNIT@ iniciou recarregamento de sua configuração
@@ -201,7 +201,7 @@ Support: %SUPPORT_URL%
A unidade @UNIT@ concluiu o recarregamento de sua configuração.
-O resultado é @RESULT@.
+O resultado é @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: Processo @EXECUTABLE@ não pôde ser executado
diff --git a/catalog/systemd.ru.catalog.in b/catalog/systemd.ru.catalog.in
index 645edaa922..ccdc685037 100644
--- a/catalog/systemd.ru.catalog.in
+++ b/catalog/systemd.ru.catalog.in
@@ -227,7 +227,7 @@ Support: %SUPPORT_URL%
ПроцеÑÑ Ð·Ð°Ð¿ÑƒÑка юнита @UNIT@ был завершен.
-Результат: @RESULT@.
+Результат: @JOB_RESULT@.
# Subject: Unit @UNIT@ has begun shutting down
-- de5b426a63be47a7b6ac3eaac82e2f6f
@@ -253,7 +253,7 @@ Support: %SUPPORT_URL%
Произошел Ñбой юнита @UNIT@.
-Результат: @RESULT@.
+Результат: @JOB_RESULT@.
# Subject: Unit @UNIT@ has begun with reloading its configuration
-- d34d037fff1847e6ae669a370e694725
@@ -271,7 +271,7 @@ Support: %SUPPORT_URL%
Юнит @UNIT@ завершил процеÑÑ Ð¿ÐµÑ€ÐµÑ‡Ð¸Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ñвоей конфигурации.
-Результат: @RESULT@.
+Результат: @JOB_RESULT@.
# Subject: Process @EXECUTABLE@ could not be executed
-- 641257651c1b4ec9a8624d7a40a9e1e7
diff --git a/catalog/systemd.sr.catalog.in b/catalog/systemd.sr.catalog.in
index f5746715a4..7cb6546d43 100644
--- a/catalog/systemd.sr.catalog.in
+++ b/catalog/systemd.sr.catalog.in
@@ -158,7 +158,7 @@ Support: %SUPPORT_URL%
Јединица @UNIT@ је завршила Ñа покретањем.
-ИÑход покретања је @RESULT@.
+ИÑход покретања је @JOB_RESULT@.
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: Јединица @UNIT@ је почела Ñа гашењем
@@ -181,7 +181,7 @@ Support: %SUPPORT_URL%
Јединица @UNIT@ је пукла.
-ИÑход је @RESULT@.
+ИÑход је @JOB_RESULT@.
-- d34d037fff1847e6ae669a370e694725
Subject: Јединица @UNIT@ је почела Ñа поновним учитавањем Ñвог подешавања
@@ -197,7 +197,7 @@ Support: %SUPPORT_URL%
Јединица @UNIT@ је завршила Ñа поновним учитавањем Ñвог подешавања
-ИÑход је @RESULT@.
+ИÑход је @JOB_RESULT@.
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: ÐŸÑ€Ð¾Ñ†ÐµÑ @EXECUTABLE@ није могао бити извршен
diff --git a/catalog/systemd.zh_CN.catalog.in b/catalog/systemd.zh_CN.catalog.in
index fa58448acf..d6ac2592b8 100644
--- a/catalog/systemd.zh_CN.catalog.in
+++ b/catalog/systemd.zh_CN.catalog.in
@@ -156,7 +156,7 @@ Support: %SUPPORT_URL%
@UNIT@ å•å…ƒå·²ç»“æŸå¯åŠ¨ã€‚
-å¯åŠ¨ç»“果为“@RESULT@â€ã€‚
+å¯åŠ¨ç»“果为“@JOB_RESULT@â€ã€‚
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: @UNIT@ å•å…ƒå·²å¼€å§‹åœæ­¢æ“作
@@ -179,7 +179,7 @@ Support: %SUPPORT_URL%
@UNIT@ å•å…ƒå·²å¤±è´¥ã€‚
-结果为“@RESULT@â€ã€‚
+结果为“@JOB_RESULT@â€ã€‚
-- d34d037fff1847e6ae669a370e694725
Subject: @UNIT@ å•å…ƒå·²å¼€å§‹é‡æ–°è½½å…¥å…¶é…ç½®
@@ -195,7 +195,7 @@ Support: %SUPPORT_URL%
@UNIT@ å•å…ƒå·²ç»“æŸé…ç½®é‡è½½å…¥æ“作。
-结果为“@RESULT@â€ã€‚
+结果为“@JOB_RESULT@â€ã€‚
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: 进程 @EXECUTABLE@ 无法执行
diff --git a/catalog/systemd.zh_TW.catalog.in b/catalog/systemd.zh_TW.catalog.in
index 17bd2bc9af..a468c2f6bf 100644
--- a/catalog/systemd.zh_TW.catalog.in
+++ b/catalog/systemd.zh_TW.catalog.in
@@ -160,7 +160,7 @@ Support: %SUPPORT_URL%
å–®ä½ @UNIT@ å•Ÿå‹•å·²çµæŸã€‚
-å•Ÿå‹•çµæžœç‚º @RESULT@。
+å•Ÿå‹•çµæžœç‚º @JOB_RESULT@。
-- de5b426a63be47a7b6ac3eaac82e2f6f
Subject: å–®ä½ @UNIT@ 已開始關閉
@@ -183,7 +183,7 @@ Support: %SUPPORT_URL%
å–®ä½ @UNIT@ 已失敗。
-çµæžœç‚º @RESULT@。
+çµæžœç‚º @JOB_RESULT@。
-- d34d037fff1847e6ae669a370e694725
Subject: å–®ä½ @UNIT@ 已開始é‡æ–°è¼‰å…¥å…¶è¨­å®š
@@ -199,7 +199,7 @@ Support: %SUPPORT_URL%
å–®ä½ @UNIT@ å·²çµæŸé‡æ–°è¼‰å…¥å…¶è¨­å®š
-çµæžœç‚º @RESULT@。
+çµæžœç‚º @JOB_RESULT@。
-- 641257651c1b4ec9a8624d7a40a9e1e7
Subject: 行程 @EXECUTABLE@ 無法執行
diff --git a/coccinelle/cmp.cocci b/coccinelle/cmp.cocci
new file mode 100644
index 0000000000..a34cbe5bf6
--- /dev/null
+++ b/coccinelle/cmp.cocci
@@ -0,0 +1,28 @@
+@@
+expression x, y;
+@@
+- if (x < y)
+- return -1;
+- if (x > y)
+- return 1;
+- return 0;
++ return CMP(x, y);
+@@
+expression x, y;
+@@
+- if (x < y)
+- return -1;
+- else if (x > y)
+- return 1;
+- return 0;
++ return CMP(x, y);
+@@
+expression x, y;
+@@
+- if (x < y)
+- return -1;
+- else if (x > y)
+- return 1;
+- else
+- return 0;
++ return CMP(x, y);
diff --git a/coccinelle/iovec-make.cocci b/coccinelle/iovec-make.cocci
new file mode 100644
index 0000000000..7a0d4ced9b
--- /dev/null
+++ b/coccinelle/iovec-make.cocci
@@ -0,0 +1,29 @@
+@@
+expression x, y, p, l;
+@@
+- x[y].iov_base = p;
+- x[y].iov_len = l;
+- y++;
++ x[y++] = IOVEC_MAKE(p, l);
+@@
+expression x, p, l;
+@@
+- x.iov_base = p;
+- x.iov_len = l;
++ x = IOVEC_MAKE(p, l);
+@@
+expression x, p, l;
+@@
+- x->iov_base = p;
+- x->iov_len = l;
++ *x = IOVEC_MAKE(p, l);
+@@
+expression s;
+@@
+- IOVEC_MAKE(s, strlen(s));
++ IOVEC_MAKE_STRING(s);
+@@
+expression x, y, z;
+@@
+- x = (struct iovec) { .iov_base = y, .iov_len = z };
++ x = IOVEC_MAKE(y, z);
diff --git a/coccinelle/redundant-if.cocci b/coccinelle/redundant-if.cocci
new file mode 100644
index 0000000000..515e36e151
--- /dev/null
+++ b/coccinelle/redundant-if.cocci
@@ -0,0 +1,54 @@
+@@
+expression r;
+@@
+- if (r < 0)
+- return r;
+- if (r == 0)
+- return 0;
++ if (r <= 0)
++ return r;
+@@
+expression r;
+@@
+- if (r == 0)
+- return 0;
+- if (r < 0)
+- return r;
++ if (r <= 0)
++ return r;
+@@
+expression r;
+@@
+- if (r < 0)
+- return r;
+- if (r == 0)
+- return r;
++ if (r <= 0)
++ return r;
+@@
+expression r;
+@@
+- if (r == 0)
+- return r;
+- if (r < 0)
+- return r;
++ if (r <= 0)
++ return r;
+@@
+expression r;
+@@
+- if (r < 0)
+- return r;
+- if (r > 0)
+- return r;
++ if (r != 0)
++ return r;
+@@
+expression r;
+@@
+- if (r > 0)
+- return r;
+- if (r < 0)
+- return r;
++ if (r != 0)
++ return r;
diff --git a/coccinelle/swap-two.cocci b/coccinelle/swap-two.cocci
new file mode 100644
index 0000000000..edf7d32403
--- /dev/null
+++ b/coccinelle/swap-two.cocci
@@ -0,0 +1,7 @@
+@@
+expression x, y, z;
+@@
+- z = x;
+- x = y;
+- y = z;
++ SWAP_TWO(x, y);
diff --git a/coccinelle/synthetic-errno.cocci b/coccinelle/synthetic-errno.cocci
new file mode 100644
index 0000000000..645bfc945f
--- /dev/null
+++ b/coccinelle/synthetic-errno.cocci
@@ -0,0 +1,42 @@
+@@
+expression e;
+expression list args;
+@@
+- log_debug(args);
+- return -e;
++ return log_debug_errno(SYNTHETIC_ERRNO(e), args);
+@@
+expression e;
+expression list args;
+@@
+- log_info(args);
+- return -e;
++ return log_info_errno(SYNTHETIC_ERRNO(e), args);
+@@
+expression e;
+expression list args;
+@@
+- log_notice(args);
+- return -e;
++ return log_notice_errno(SYNTHETIC_ERRNO(e), args);
+@@
+expression e;
+expression list args;
+@@
+- log_error(args);
+- return -e;
++ return log_error_errno(SYNTHETIC_ERRNO(e), args);
+@@
+expression e;
+expression list args;
+@@
+- log_emergency(args);
+- return -e;
++ return log_emergency_errno(SYNTHETIC_ERRNO(e), args);
+@@
+identifier log_LEVEL_errno =~ "^log_(debug|info|notice|warning|error|emergency)_errno$";
+identifier ERRNO =~ "^E[A-Z]+$";
+expression list args;
+@@
+- return log_LEVEL_errno(ERRNO, args);
++ return log_LEVEL_errno(SYNTHETIC_ERRNO(ERRNO), args);
diff --git a/doc/CODING_STYLE b/doc/CODING_STYLE
deleted file mode 100644
index 26928d2e2d..0000000000
--- a/doc/CODING_STYLE
+++ /dev/null
@@ -1,460 +0,0 @@
-- 8ch indent, no tabs, except for files in man/ which are 2ch indent,
- and still no tabs
-
-- We prefer /* comments */ over // comments in code you commit, please. This
- way // comments are left for developers to use for local, temporary
- commenting of code for debug purposes (i.e. uncommittable stuff), making such
- comments easily discernable from explanatory, documenting code comments
- (i.e. committable stuff).
-
-- Don't break code lines too eagerly. We do *not* force line breaks at 80ch,
- all of today's screens should be much larger than that. But then again, don't
- overdo it, ~119ch should be enough really. The .editorconfig, .vimrc and
- .dir-locals.el files contained in the repository will set this limit up for
- you automatically, if you let them (as well as a few other things).
-
-- Variables and functions *must* be static, unless they have a
- prototype, and are supposed to be exported.
-
-- structs in MixedCase (with exceptions, such as public API structs),
- variables + functions in lower_case.
-
-- The destructors always unregister the object from the next bigger
- object, not the other way around
-
-- To minimize strict aliasing violations, we prefer unions over casting
-
-- For robustness reasons, destructors should be able to destruct
- half-initialized objects, too
-
-- Error codes are returned as negative Exxx. e.g. return -EINVAL. There
- are some exceptions: for constructors, it is OK to return NULL on
- OOM. For lookup functions, NULL is fine too for "not found".
-
- Be strict with this. When you write a function that can fail due to
- more than one cause, it *really* should have "int" as return value
- for the error code.
-
-- Do not bother with error checking whether writing to stdout/stderr
- worked.
-
-- Do not log errors from "library" code, only do so from "main
- program" code. (With one exception: it is OK to log with DEBUG level
- from any code, with the exception of maybe inner loops).
-
-- Always check OOM. There is no excuse. In program code, you can use
- "log_oom()" for then printing a short message, but not in "library" code.
-
-- Do not issue NSS requests (that includes user name and host name
- lookups) from PID 1 as this might trigger deadlocks when those
- lookups involve synchronously talking to services that we would need
- to start up
-
-- Do not synchronously talk to any other service from PID 1, due to
- risk of deadlocks
-
-- Avoid fixed-size string buffers, unless you really know the maximum
- size and that maximum size is small. They are a source of errors,
- since they possibly result in truncated strings. It is often nicer
- to use dynamic memory, alloca() or VLAs. If you do allocate fixed-size
- strings on the stack, then it is probably only OK if you either
- use a maximum size such as LINE_MAX, or count in detail the maximum
- size a string can have. (DECIMAL_STR_MAX and DECIMAL_STR_WIDTH
- macros are your friends for this!)
-
- Or in other words, if you use "char buf[256]" then you are likely
- doing something wrong!
-
-- Stay uniform. For example, always use "usec_t" for time
- values. Do not mix usec and msec, and usec and whatnot.
-
-- Make use of _cleanup_free_ and friends. It makes your code much
- nicer to read (and shorter)!
-
-- Be exceptionally careful when formatting and parsing floating point
- numbers. Their syntax is locale dependent (i.e. "5.000" in en_US is
- generally understood as 5, while on de_DE as 5000.).
-
-- Try to use this:
-
- void foo() {
- }
-
- instead of this:
-
- void foo()
- {
- }
-
- But it is OK if you do not.
-
-- Single-line "if" blocks should not be enclosed in {}. Use this:
-
- if (foobar)
- waldo();
-
- instead of this:
-
- if (foobar) {
- waldo();
- }
-
-- Do not write "foo ()", write "foo()".
-
-- Please use streq() and strneq() instead of strcmp(), strncmp() where
- applicable (i.e. wherever you just care about equality/inequality, not about
- the sorting order).
-
-- Preferably allocate stack variables on the top of the block:
-
- {
- int a, b;
-
- a = 5;
- b = a;
- }
-
-- Unless you allocate an array, "double" is always the better choice
- than "float". Processors speak "double" natively anyway, so this is
- no speed benefit, and on calls like printf() "float"s get promoted
- to "double"s anyway, so there is no point.
-
-- Do not mix function invocations with variable definitions in one
- line. Wrong:
-
- {
- int a = foobar();
- uint64_t x = 7;
- }
-
- Right:
-
- {
- int a;
- uint64_t x = 7;
-
- a = foobar();
- }
-
-- Use "goto" for cleaning up, and only use it for that. i.e. you may
- only jump to the end of a function, and little else. Never jump
- backwards!
-
-- Think about the types you use. If a value cannot sensibly be
- negative, do not use "int", but use "unsigned".
-
-- Use "char" only for actual characters. Use "uint8_t" or "int8_t"
- when you actually mean a byte-sized signed or unsigned
- integers. When referring to a generic byte, we generally prefer the
- unsigned variant "uint8_t". Do not use types based on "short". They
- *never* make sense. Use ints, longs, long longs, all in
- unsigned+signed fashion, and the fixed size types
- uint8_t/uint16_t/uint32_t/uint64_t/int8_t/int16_t/int32_t and so on,
- as well as size_t, but nothing else. Do not use kernel types like
- u32 and so on, leave that to the kernel.
-
-- Public API calls (i.e. functions exported by our shared libraries)
- must be marked "_public_" and need to be prefixed with "sd_". No
- other functions should be prefixed like that.
-
-- In public API calls, you *must* validate all your input arguments for
- programming error with assert_return() and return a sensible return
- code. In all other calls, it is recommended to check for programming
- errors with a more brutal assert(). We are more forgiving to public
- users than for ourselves! Note that assert() and assert_return()
- really only should be used for detecting programming errors, not for
- runtime errors. assert() and assert_return() by usage of _likely_()
- inform the compiler that he should not expect these checks to fail,
- and they inform fellow programmers about the expected validity and
- range of parameters.
-
-- Never use strtol(), atoi() and similar calls. Use safe_atoli(),
- safe_atou32() and suchlike instead. They are much nicer to use in
- most cases and correctly check for parsing errors.
-
-- For every function you add, think about whether it is a "logging"
- function or a "non-logging" function. "Logging" functions do logging
- on their own, "non-logging" function never log on their own and
- expect their callers to log. All functions in "library" code,
- i.e. in src/shared/ and suchlike must be "non-logging". Every time a
- "logging" function calls a "non-logging" function, it should log
- about the resulting errors. If a "logging" function calls another
- "logging" function, then it should not generate log messages, so
- that log messages are not generated twice for the same errors.
-
-- Avoid static variables, except for caches and very few other
- cases. Think about thread-safety! While most of our code is never
- used in threaded environments, at least the library code should make
- sure it works correctly in them. Instead of doing a lot of locking
- for that, we tend to prefer using TLS to do per-thread caching (which
- only works for small, fixed-size cache objects), or we disable
- caching for any thread that is not the main thread. Use
- is_main_thread() to detect whether the calling thread is the main
- thread.
-
-- Command line option parsing:
- - Do not print full help() on error, be specific about the error.
- - Do not print messages to stdout on error.
- - Do not POSIX_ME_HARDER unless necessary, i.e. avoid "+" in option string.
-
-- Do not write functions that clobber call-by-reference variables on
- failure. Use temporary variables for these cases and change the
- passed in variables only on success.
-
-- When you allocate a file descriptor, it should be made O_CLOEXEC
- right from the beginning, as none of our files should leak to forked
- binaries by default. Hence, whenever you open a file, O_CLOEXEC must
- be specified, right from the beginning. This also applies to
- sockets. Effectively this means that all invocations to:
-
- a) open() must get O_CLOEXEC passed
- b) socket() and socketpair() must get SOCK_CLOEXEC passed
- c) recvmsg() must get MSG_CMSG_CLOEXEC set
- d) F_DUPFD_CLOEXEC should be used instead of F_DUPFD, and so on
- f) invocations of fopen() should take "e"
-
-- We never use the POSIX version of basename() (which glibc defines it in
- libgen.h), only the GNU version (which glibc defines in string.h).
- The only reason to include libgen.h is because dirname()
- is needed. Every time you need that please immediately undefine
- basename(), and add a comment about it, so that no code ever ends up
- using the POSIX version!
-
-- Use the bool type for booleans, not integers. One exception: in public
- headers (i.e those in src/systemd/sd-*.h) use integers after all, as "bool"
- is C99 and in our public APIs we try to stick to C89 (with a few extension).
-
-- When you invoke certain calls like unlink(), or mkdir_p() and you
- know it is safe to ignore the error it might return (because a later
- call would detect the failure anyway, or because the error is in an
- error path and you thus couldn't do anything about it anyway), then
- make this clear by casting the invocation explicitly to (void). Code
- checks like Coverity understand that, and will not complain about
- ignored error codes. Hence, please use this:
-
- (void) unlink("/foo/bar/baz");
-
- instead of just this:
-
- unlink("/foo/bar/baz");
-
- Don't cast function calls to (void) that return no error
- conditions. Specifically, the various xyz_unref() calls that return a NULL
- object shouldn't be cast to (void), since not using the return value does not
- hide any errors.
-
-- Don't invoke exit(), ever. It is not replacement for proper error
- handling. Please escalate errors up your call chain, and use normal
- "return" to exit from the main function of a process. If you
- fork()ed off a child process, please use _exit() instead of exit(),
- so that the exit handlers are not run.
-
-- Please never use dup(). Use fcntl(fd, F_DUPFD_CLOEXEC, 3)
- instead. For two reason: first, you want O_CLOEXEC set on the new fd
- (see above). Second, dup() will happily duplicate your fd as 0, 1,
- 2, i.e. stdin, stdout, stderr, should those fds be closed. Given the
- special semantics of those fds, it's probably a good idea to avoid
- them. F_DUPFD_CLOEXEC with "3" as parameter avoids them.
-
-- When you define a destructor or unref() call for an object, please
- accept a NULL object and simply treat this as NOP. This is similar
- to how libc free() works, which accepts NULL pointers and becomes a
- NOP for them. By following this scheme a lot of if checks can be
- removed before invoking your destructor, which makes the code
- substantially more readable and robust.
-
-- Related to this: when you define a destructor or unref() call for an
- object, please make it return the same type it takes and always
- return NULL from it. This allows writing code like this:
-
- p = foobar_unref(p);
-
- which will always work regardless if p is initialized or not, and
- guarantees that p is NULL afterwards, all in just one line.
-
-- Use alloca(), but never forget that it is not OK to invoke alloca()
- within a loop or within function call parameters. alloca() memory is
- released at the end of a function, and not at the end of a {}
- block. Thus, if you invoke it in a loop, you keep increasing the
- stack pointer without ever releasing memory again. (VLAs have better
- behaviour in this case, so consider using them as an alternative.)
- Regarding not using alloca() within function parameters, see the
- BUGS section of the alloca(3) man page.
-
-- Use memzero() or even better zero() instead of memset(..., 0, ...)
-
-- Instead of using memzero()/memset() to initialize structs allocated
- on the stack, please try to use c99 structure initializers. It's
- short, prettier and actually even faster at execution. Hence:
-
- struct foobar t = {
- .foo = 7,
- .bar = "bazz",
- };
-
- instead of:
-
- struct foobar t;
- zero(t);
- t.foo = 7;
- t.bar = "bazz";
-
-- When returning a return code from main(), please preferably use
- EXIT_FAILURE and EXIT_SUCCESS as defined by libc.
-
-- The order in which header files are included doesn't matter too
- much. systemd-internal headers must not rely on an include order, so
- it is safe to include them in any order possible.
- However, to not clutter global includes, and to make sure internal
- definitions will not affect global headers, please always include the
- headers of external components first (these are all headers enclosed
- in <>), followed by our own exported headers (usually everything
- that's prefixed by "sd-"), and then followed by internal headers.
- Furthermore, in all three groups, order all includes alphabetically
- so duplicate includes can easily be detected.
-
-- To implement an endless loop, use "for (;;)" rather than "while
- (1)". The latter is a bit ugly anyway, since you probably really
- meant "while (true)"... To avoid the discussion what the right
- always-true expression for an infinite while() loop is our
- recommendation is to simply write it without any such expression by
- using "for (;;)".
-
-- Never use the "off_t" type, and particularly avoid it in public
- APIs. It's really weirdly defined, as it usually is 64bit and we
- don't support it any other way, but it could in theory also be
- 32bit. Which one it is depends on a compiler switch chosen by the
- compiled program, which hence corrupts APIs using it unless they can
- also follow the program's choice. Moreover, in systemd we should
- parse values the same way on all architectures and cannot expose
- off_t values over D-Bus. To avoid any confusion regarding conversion
- and ABIs, always use simply uint64_t directly.
-
-- Commit message subject lines should be prefixed with an appropriate
- component name of some kind. For example "journal: ", "nspawn: " and
- so on.
-
-- Do not use "Signed-Off-By:" in your commit messages. That's a kernel
- thing we don't do in the systemd project.
-
-- Avoid leaving long-running child processes around, i.e. fork()s that
- are not followed quickly by an execv() in the child. Resource
- management is unclear in this case, and memory CoW will result in
- unexpected penalties in the parent much, much later on.
-
-- Don't block execution for arbitrary amounts of time using usleep()
- or a similar call, unless you really know what you do. Just "giving
- something some time", or so is a lazy excuse. Always wait for the
- proper event, instead of doing time-based poll loops.
-
-- To determine the length of a constant string "foo", don't bother
- with sizeof("foo")-1, please use STRLEN() instead.
-
-- If you want to concatenate two or more strings, consider using
- strjoin() rather than asprintf(), as the latter is a lot
- slower. This matters particularly in inner loops.
-
-- Please avoid using global variables as much as you can. And if you
- do use them make sure they are static at least, instead of
- exported. Especially in library-like code it is important to avoid
- global variables. Why are global variables bad? They usually hinder
- generic reusability of code (since they break in threaded programs,
- and usually would require locking there), and as the code using them
- has side-effects make programs non-transparent. That said, there are
- many cases where they explicitly make a lot of sense, and are OK to
- use. For example, the log level and target in log.c is stored in a
- global variable, and that's OK and probably expected by most. Also
- in many cases we cache data in global variables. If you add more
- caches like this, please be careful however, and think about
- threading. Only use static variables if you are sure that
- thread-safety doesn't matter in your case. Alternatively consider
- using TLS, which is pretty easy to use with gcc's "thread_local"
- concept. It's also OK to store data that is inherently global in
- global variables, for example data parsed from command lines, see
- below.
-
-- If you parse a command line, and want to store the parsed parameters
- in global variables, please consider prefixing their names with
- "arg_". We have been following this naming rule in most of our
- tools, and we should continue to do so, as it makes it easy to
- identify command line parameter variables, and makes it clear why it
- is OK that they are global variables.
-
-- When exposing public C APIs, be careful what function parameters you make
- "const". For example, a parameter taking a context object should probably not
- be "const", even if you are writing an otherwise read-only accessor function
- for it. The reason is that making it "const" fixates the contract that your
- call won't alter the object ever, as part of the API. However, that's often
- quite a promise, given that this even prohibits object-internal caching or
- lazy initialization of object variables. Moreover it's usually not too useful
- for client applications. Hence: please be careful and avoid "const" on object
- parameters, unless you are very sure "const" is appropriate.
-
-- Make sure to enforce limits on every user controllable resource. If the user
- can allocate resources in your code, your code must enforce some form of
- limits after which it will refuse operation. It's fine if it is hard-coded (at
- least initially), but it needs to be there. This is particularly important
- for objects that unprivileged users may allocate, but also matters for
- everything else any user may allocated.
-
-- htonl()/ntohl() and htons()/ntohs() are weird. Please use htobe32() and
- htobe16() instead, it's much more descriptive, and actually says what really
- is happening, after all htonl() and htons() don't operate on longs and
- shorts as their name would suggest, but on uint32_t and uint16_t. Also,
- "network byte order" is just a weird name for "big endian", hence we might
- want to call it "big endian" right-away.
-
-- You might wonder what kind of common code belongs in src/shared/ and what
- belongs in src/basic/. The split is like this: anything that uses public APIs
- we expose (i.e. any of the sd-bus, sd-login, sd-id128, ... APIs) must be
- located in src/shared/. All stuff that only uses external libraries from
- other projects (such as glibc's APIs), or APIs from src/basic/ itself should
- be placed in src/basic/. Conversely, src/libsystemd/ may only use symbols
- from src/basic, but not from src/shared/. To summarize:
-
- src/basic/ → may be used by all code in the tree
- → may not use any code outside of src/basic/
-
- src/libsystemd/ → may be used by all code in the tree, except for code in src/basic/
- → may not use any code outside of src/basic/, src/libsystemd/
-
- src/shared/ → may be used by all code in the tree, except for code in src/basic/, src/libsystemd/
- → may not use any code outside of src/basic/, src/libsystemd/, src/shared/
-
-- Our focus is on the GNU libc (glibc), not any other libcs. If other libcs are
- incompatible with glibc it's on them. However, if there are equivalent POSIX
- and Linux/GNU-specific APIs, we generally prefer the POSIX APIs. If there
- aren't, we are happy to use GNU or Linux APIs, and expect non-GNU
- implementations of libc to catch up with glibc.
-
-- Whenever installing a signal handler, make sure to set SA_RESTART for it, so
- that interrupted system calls are automatically restarted, and we minimize
- hassles with handling EINTR (in particular as EINTR handling is pretty broken
- on Linux).
-
-- When applying C-style unescaping as well as specifier expansion on the same
- string, always apply the C-style unescaping fist, followed by the specifier
- expansion. When doing the reverse, make sure to escape '%' in specifier-style
- first (i.e. '%' → '%%'), and then do C-style escaping where necessary.
-
-- It's a good idea to use O_NONBLOCK when opening 'foreign' regular files, i.e
- file system objects that are supposed to be regular files whose paths where
- specified by the user and hence might actually refer to other types of file
- system objects. This is a good idea so that we don't end up blocking on
- 'strange' file nodes, for example if the user pointed us to a FIFO or device
- node which may block when opening. Moreover even for actual regular files
- O_NONBLOCK has a benefit: it bypasses any mandatory lock that might be in
- effect on the regular file. If in doubt consider turning off O_NONBLOCK again
- after opening.
-
-- When referring to a configuration file option in the documentation and such,
- please always suffix it with "=", to indicate that it is a configuration file
- setting.
-
-- When referring to a command line option in the documentation and such, please
- always prefix with "--" or "-" (as appropriate), to indicate that it is a
- command line option.
-
-- When referring to a file system path that is a directory, please always
- suffix it with "/", to indicate that it is a directory, not a regular file
- (or other file system object).
diff --git a/doc/DISTRO_PORTING b/doc/DISTRO_PORTING
deleted file mode 100644
index d1a187aa41..0000000000
--- a/doc/DISTRO_PORTING
+++ /dev/null
@@ -1,71 +0,0 @@
-Porting systemd To New Distributions
-
-HOWTO:
- You need to make the follow changes to adapt systemd to your
- distribution:
-
- 1) Find the right configure parameters for:
-
- -D rootprefix=
- -D sysvinit-path=
- -D sysvrcnd-path=
- -D rc-local=
- -D halt-local=
- -D loadkeys-path=
- -D setfont-path=
- -D tty-gid=
- -D ntp-servers=
- -D dns-servers=
- -D support-url=
-
- 2) Try it out. Play around (as an ordinary user) with
- '/usr/lib/systemd/systemd --test --system' for a test run
- of systemd without booting. This will read the unit files and
- print the initial transaction it would execute during boot-up.
- This will also inform you about ordering loops and suchlike.
-
-NTP POOL:
- By default, systemd-timesyncd uses the Google Public NTP servers
- time[1-4].google.com, if no other NTP configuration is available. They
- serve time that uses a leap second smear, and can be up to .5s off from
- servers that use stepped leap seconds.
-
- https://developers.google.com/time/smear
-
- If you prefer to use leap second steps, please register your own
- vendor pool at ntp.org and make it the built-in default by
- passing --with-ntp-servers= to configure. Registering vendor
- pools is free:
-
- http://www.pool.ntp.org/en/vendors.html
-
- Use -D ntp-servers= to direct systemd-timesyncd to different fallback
- NTP servers.
-
-DNS SERVERS:
- By default, systemd-resolved uses the Google Public DNS servers
- 8.8.8.8, 8.8.4.4, 2001:4860:4860::8888, 2001:4860:4860::8844 as
- fallback, if no other DNS configuration is available.
-
- Use -D dns-servers= to direct systemd-resolved to different fallback
- DNS servers.
-
-PAM:
- The default PAM config shipped by systemd is really bare bones.
- It does not include many modules your distro might want to enable
- to provide a more seamless experience. For example, limits set in
- /etc/security/limits.conf will not be read unless you load pam_limits.
- Make sure you add modules your distro expects from user services.
-
- Pass -D pamconfdir=no to meson to avoid installing this file and
- instead install your own.
-
-CONTRIBUTING UPSTREAM:
- We generally do no longer accept distribution-specific patches to
- systemd upstream. If you have to make changes to systemd's source code
- to make it work on your distribution, unless your code is generic
- enough to be generally useful, we are unlikely to merge it. Please
- always consider adopting the upstream defaults. If that is not
- possible, please maintain the relevant patches downstream.
-
- Thank you for understanding.
diff --git a/doc/HACKING b/doc/HACKING
deleted file mode 100644
index 182b0bac8c..0000000000
--- a/doc/HACKING
+++ /dev/null
@@ -1,122 +0,0 @@
-HACKING ON SYSTEMD
-
-We welcome all contributions to systemd. If you notice a bug or a missing
-feature, please feel invited to fix it, and submit your work as a github Pull
-Request (PR):
-
- https://github.com/systemd/systemd/pull/new
-
-Please make sure to follow our Coding Style when submitting patches. See
-doc/CODING_STYLE for details. Also have a look at our Contribution Guidelines:
-
- https://github.com/systemd/systemd/blob/master/.github/CONTRIBUTING.md
-
-When adding new functionality, tests should be added. For shared functionality
-(in src/basic and src/shared) unit tests should be sufficient. The general
-policy is to keep tests in matching files underneath src/test,
-e.g. src/test/test-path-util.c contains tests for any functions in
-src/basic/path-util.c. If adding a new source file, consider adding a matching
-test executable. For features at a higher level, tests in src/test/ are very
-strongly recommended. If that is no possible, integration tests in test/ are
-encouraged.
-
-Please also have a look at our list of code quality tools we have setup for systemd,
-to ensure our codebase stays in good shape:
-
- https://github.com/systemd/systemd/blob/master/doc/CODE_QUALITY.md
-
-Please always test your work before submitting a PR. For many of the components
-of systemd testing is straight-forward as you can simply compile systemd and
-run the relevant tool from the build directory.
-
-For some components (most importantly, systemd/PID1 itself) this is not
-possible, however. In order to simplify testing for cases like this we provide
-a set of "mkosi" build files directly in the source tree. "mkosi" is a tool for
-building clean OS images from an upstream distribution in combination with a
-fresh build of the project in the local working directory. To make use of this,
-please acquire "mkosi" from https://github.com/systemd/mkosi first, unless your
-distribution has packaged it already and you can get it from there. After the
-tool is installed it is sufficient to type "mkosi" in the systemd project
-directory to generate a disk image "image.raw" you can boot either in
-systemd-nspawn or in an UEFI-capable VM:
-
- # systemd-nspawn -bi image.raw
-
-or:
-
- # qemu-system-x86_64 -enable-kvm -m 512 -smp 2 -bios /usr/share/edk2/ovmf/OVMF_CODE.fd -hda image.raw
-
-Every time you rerun the "mkosi" command a fresh image is built, incorporating
-all current changes you made to the project tree.
-
-Alternatively, you may install the systemd version from your git check-out
-directly on top of your host system's directory tree. This mostly works fine,
-but of course you should know what you are doing as you might make your system
-unbootable in case of a bug in your changes. Also, you might step into your
-package manager's territory with this. Be careful!
-
-And never forget: most distributions provide very simple and convenient ways to
-install all development packages necessary to build systemd. For example, on
-Fedora the following command line should be sufficient to install all of
-systemd's build dependencies:
-
- # dnf builddep systemd
-
-Putting this all together, here's a series of commands for preparing a patch
-for systemd (this example is for Fedora):
-
- $ sudo dnf builddep systemd # install build dependencies
- $ sudo dnf install mkosi # install tool to quickly build images
- $ git clone https://github.com/systemd/systemd.git
- $ cd systemd
- $ vim src/core/main.c # or wherever you'd like to make your changes
- $ meson build # configure the build
- $ ninja -C build # build it locally, see if everything compiles fine
- $ ninja -C build test # run some simple regression tests
- $ (umask 077; echo 123 > mkosi.rootpw) # set root password used by mkosi
- $ sudo mkosi # build a test image
- $ sudo systemd-nspawn -bi image.raw # boot up the test image
- $ git add -p # interactively put together your patch
- $ git commit # commit it
- $ git push REMOTE HEAD:refs/heads/BRANCH
- # where REMOTE is your "fork" on github
- # and BRANCH is a branch name.
-
-And after that, head over to your repo on github and click "Compare & pull request"
-
-Happy hacking!
-
-
-FUZZERS
-
-systemd includes fuzzers in src/fuzz that use libFuzzer and are automatically
-run by OSS-Fuzz (https://github.com/google/oss-fuzz) with sanitizers. To add a
-fuzz target, create a new src/fuzz/fuzz-foo.c file with a LLVMFuzzerTestOneInput
-function and add it to the list in src/fuzz/meson.build.
-
-Whenever possible, a seed corpus and a dictionary should also be added with new
-fuzz targets. The dictionary should be named src/fuzz/fuzz-foo.dict and the seed
-corpus should be built and exported as $OUT/fuzz-foo_seed_corpus.zip in
-tools/oss-fuzz.sh.
-
-The fuzzers can be built locally if you have libFuzzer installed by running
-tools/oss-fuzz.sh. You should also confirm that the fuzzer runs in the
-OSS-Fuzz environment by checking out the OSS-Fuzz repo, and then running
-commands like this:
-
- python infra/helper.py build_image systemd
- python infra/helper.py build_fuzzers --sanitizer memory systemd ../systemd
- python infra/helper.py run_fuzzer systemd fuzz-foo
-
-If you find a bug that impacts the security of systemd, please follow the
-guidance in .github/CONTRIBUTING.md on how to report a security vulnerability.
-
-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
diff --git a/doc/TRANSLATORS b/doc/TRANSLATORS
deleted file mode 100644
index 873ec7b01c..0000000000
--- a/doc/TRANSLATORS
+++ /dev/null
@@ -1,27 +0,0 @@
-Notes for translators
-=====================
-
-systemd depends on gettext for multilingual support.
-In po/ directory you'll find the needed files.
-
-POT (Portable Object Template)
-------------------------------
-A text file with .pot extension, with all the extracted labels from code.
-
-To update the template:
-
-$ cd systemd/
-$ ninja -C build systemd-pot
-
-To start a new translation:
-
-$ cd po/
-$ cp systemd.pot <YOUR-LANG-CODE>.po
-
-Replace <YOUR-LANG-CODE> with the two-letters codes of ISO 639 standard.
-
-PO (Portable Object)
---------------------
-A text file with .po extension, with all the available labels and some additional
-metadata fields. Any editor is ok, but a good standard is 'poedit', a graphical
-application specifically designed for this kind of task.
diff --git a/docs/AUTOMATIC_BOOT_ASSESSMENT.md b/docs/AUTOMATIC_BOOT_ASSESSMENT.md
new file mode 100644
index 0000000000..83acdab446
--- /dev/null
+++ b/docs/AUTOMATIC_BOOT_ASSESSMENT.md
@@ -0,0 +1,203 @@
+# Automatic Boot Assessment
+
+systemd provides support for automatically reverting back to the previous
+version of the OS or kernel in case the system consistently fails to boot. This
+support is built into various of its components. When used together these
+components provide a complete solution on UEFI systems, built as add-on to the
+[Boot Loader
+Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION). However, the
+different components may also be used independently, and in combination with
+other software, to implement similar schemes, for example with other boot
+loaders or for non-UEFI systems. Here's a brief overview of the complete set of
+components:
+
+* The
+ [`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
+ boot loader optionally maintains a per-boot-loader-entry counter that is
+ decreased by one on each attempt to boot the entry, prioritizing entries that
+ have non-zero counters over those which already reached a counter of zero
+ when choosing the entry to boot.
+
+* The
+ [`systemd-bless-boot.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-bless-boot.service.html)
+ service automatically marks a boot loader entry, for which boot counting as
+ mentioned above is enabled, as "good" when a boot has been determined to be
+ successful, thus turning off boot counting for it.
+
+* The
+ [`systemd-bless-boot-generator(8)`](https://www.freedesktop.org/software/systemd/man/systemd-bless-boot-generator.html)
+ generator automatically pulls in `systemd-bless-boot.service` when use of
+ `systemd-boot` with boot counting enabled is detected.
+
+* The
+ [`systemd-boot-check-no-failures.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-boot-check-no-failures.service.html)
+ service is a simple health check tool that determines whether the boot
+ completed successfully. When enabled it becomes an indirect dependency of
+ `systemd-bless-boot.service` (by means of `boot-complete.target`, see
+ below), ensuring that the boot will not be considered successful if there are
+ any failed services.
+
+* The `boot-complete.target` target unit (see
+ [`systemd.special(7)`](https://www.freedesktop.org/software/systemd/man/systemd.special.html))
+ serves as a generic extension point both for units that shall be considered
+ necessary to consider a boot successful on one side (example:
+ `systemd-boot-check-no-failures.service` as described above), and units that
+ want to act only if the boot is successful on the other (example:
+ `systemd-bless-boot.service` as described above).
+
+* The
+ [`kernel-install(8)`](https://www.freedesktop.org/software/systemd/man/kernel-install.html)
+ script can optionally create boot loader entries that carry an initial boot
+ counter (the initial counter is configurable in `/etc/kernel/tries`).
+
+# Details
+
+The boot counting data `systemd-boot` and `systemd-bless-boot.service`
+manage is stored in the name of the boot loader entries. If a boot loader entry
+file name contains `+` followed by one or two numbers (if two numbers, then
+those need to be separated by `-`) right before the `.conf` suffix, then boot
+counting is enabled for it. The first number is the "tries left" counter
+encoding how many attempts to boot this entry shall still be made. The second
+number is the "tries done" counter, encoding how many failed attempts to boot
+it have already been made. Each time a boot loader entry marked this way is
+booted the first counter is decreased by one, and the second one increased by
+one. (If the second counter is missing, then it is assumed to be equivalent to
+zero.) If the "tries left" counter is above zero the entry is still considered
+for booting (the entry's state is considered to be "indeterminate"), as soon as
+it reached zero the entry is not tried anymore (entry state "bad"). If the boot
+attempt completed successfully the entry's counters are removed from the name
+(entry state "good"), thus turning off boot counting for the future.
+
+## Walkthrough
+
+Here's an example walkthrough of how this all fits together.
+
+1. The user runs `echo 3 > /etc/kernel/tries` to enable boot counting.
+
+2. A new kernel is installed. `kernel-install` is used to generate a new boot
+ loader entry file for it. Let's say the version string for the new kernel is
+ `4.14.11-300.fc27.x86_64`, a new boot loader entry
+ `/boot/loader/entries/4.14.11-300.fc27.x86_64+3.conf` is hence created.
+
+3. The system is booted for the first time after the new kernel is
+ installed. The boot loader now sees the `+3` counter in the entry file
+ name. It hence renames the file to `4.14.11-300.fc27.x86_64+2-1.conf`
+ indicating that at this point one attempt has started and thus only one less
+ is left. After the rename completed the entry is booted as usual.
+
+4. Let's say this attempt to boot fails. On the following boot the boot loader
+ will hence see the `+2-1` tag in the name, and hence rename the entry file to
+ `4.14.11-300.fc27.x86_64+1-2.conf`, and boot it.
+
+5. Let's say the boot fails again. On the subsequent boot the loader hence will
+ see the `+1-2` tag, and rename the file to
+ `4.14.11-300.fc27.x86_64+0-3.conf` and boot it.
+
+6. If this boot also fails, on the next boot the boot loader will see the the
+ tag `+0-3`, i.e. the counter reached zero. At this point the entry will be
+ considered "bad", and ordered to the end of the list of entries. The next
+ newest boot entry is now tried, i.e. the system automatically reverted back
+ to an earlier version.
+
+The above describes the walkthrough when the selected boot entry continuously
+fails. Let's have a look at an alternative ending to this walkthrough. In this
+scenario the first 4 steps are the same as above:
+
+1. *as above*
+
+2. *as above*
+
+3. *as above*
+
+4. *as above*
+
+5. Let's say the second boot succeeds. The kernel initializes properly, systemd
+ is started and invokes all generators.
+
+6. One of the generators started is `systemd-bless-boot-generator` which
+ detects that boot counting is used. It hence pulls
+ `systemd-bless-boot.service` into the initial transaction.
+
+7. `systemd-bless-boot.service` is ordered after and `Requires=` the generic
+ `boot-complete.target` unit. This unit is hence also pulled into the initial
+ transaction.
+
+8. The `boot-complete.target` unit is ordered after and pulls in various units
+ that are required to succeed for the boot process to be considered
+ successful. One such unit is `systemd-boot-check-no-failures.service`.
+
+9. `systemd-boot-check-no-failures.service` is run after all its own
+ dependencies completed, and assesses that the boot completed
+ successfully. It hence exits cleanly.
+
+10. This allows `boot-complete.target` to be reached. This signifies to the
+ system that this boot attempt shall be considered successful.
+
+11. Which in turn permits `systemd-bless-boot.service` to run. It now
+ determines which boot loader entry file was used to boot the system, and
+ renames it dropping the counter tag. Thus
+ `4.14.11-300.fc27.x86_64+1-2.conf` is renamed to
+ `4.14.11-300.fc27.x86_64.conf`. From this moment boot counting is turned
+ off.
+
+12. On the following boot (and all subsequent boots after that) the entry is
+ now seen with boot counting turned off, no further renaming takes place.
+
+# How to adapt this scheme to other setups
+
+Of the stack described above many components may be replaced or augmented. Here
+are a couple of recommendations.
+
+1. To support alternative boot loaders in place of `systemd-boot` two scenarios
+ are recommended:
+
+ a. Boot loaders already implementing the Boot Loader Specification can simply
+ implement an equivalent file rename based logic, and thus integrate fully
+ with the rest of the stack.
+
+ b. Boot loaders that want to implement boot counting and store the counters
+ elsewhere can provide their own replacements for
+ `systemd-bless-boot.service` and `systemd-bless-boot-generator`, but should
+ continue to use `boot-complete.target` and thus support any services
+ ordered before that.
+
+2. To support additional components that shall succeed before the boot is
+ considered successful, simply place them in units (if they aren't already)
+ and order them before the generic `boot-complete.target` target unit,
+ combined with `Requires=` dependencies from the target, so that the target
+ cannot be reached when any of the units fail. You may add any number of
+ units like this, and only if they all succeed the boot entry is marked as
+ good. Note that the target unit shall pull in these boot checking units, not
+ the other way around.
+
+3. To support additional components that shall only run on boot success, simply
+ wrap them in a unit and order them after `boot-complete.target`, pulling it
+ in.
+
+# FAQ
+
+1. *Why do you use file renames to store the counter? Why not a regular file?*
+ — Mainly two reasons: it's relatively likely that renames can be implemented
+ atomically even in simpler file systems, while writing to file contents has
+ a much bigger chance to be result in incomplete or corrupt data, as renaming
+ generally avoids allocating or releasing data blocks. Moreover it has the
+ benefit that the boot count metadata is directly attached to the boot loader
+ entry file, and thus the lifecycle of the metadata and the entry itself are
+ bound together. This means no additional clean-up needs to take place to
+ drop the boot loader counting information for an entry when it is removed.
+
+2. *Why not use EFI variables for storing the boot counter?* — The memory chips
+ used to back the persistent EFI variables are generally not of the highest
+ quality, hence shouldn't be written to more than necessary. This means we
+ can't really use it for changes made regularly during boot, but can use it
+ only for seldom made configuration changes.
+
+3. *I have a service which — when it fails — should immediately cause a
+ reboot. How does that fit in with the above?* — Well, that's orthogonal to
+ the above, please use `FailureAction=` in the unit file for this.
+
+4. *Under some condition I want to mark the current boot loader entry as bad
+ right-away, so that it never is tried again, how do I do that?* — You may
+ invoke `/usr/lib/systemd/systemd-bless-boot bad` at any time to mark the
+ current boot loader entry as "bad" right-away so that it isn't tried again
+ on later boots.
diff --git a/docs/BLOCK_DEVICE_LOCKING.md b/docs/BLOCK_DEVICE_LOCKING.md
new file mode 100644
index 0000000000..ceb060c0c9
--- /dev/null
+++ b/docs/BLOCK_DEVICE_LOCKING.md
@@ -0,0 +1,63 @@
+# Locking Block Device Access
+
+*TL;DR: Use BSD file locks
+[(`flock(2)`)](http://man7.org/linux/man-pages/man2/flock.2.html) on block
+device nodes to synchronize access for partitioning and file system formatting
+tools.*
+
+`systemd-udevd` probes all block devices showing up for file system superblock
+and partition table information (utilizing `libblkid`). If another program
+concurrently modifies a superblock or partition table this probing might be
+affected, which is bad in itself, but also might in turn result in undesired
+effects in programs subscribing to `udev` events.
+
+Applications manipulating a block device can temporarily stop `systemd-udevd`
+from processing rules on it — and thus bar it from probing the device — by
+taking a BSD file lock on the block device node. Specifically, whenever
+`systemd-udevd` starts processing a block device it takes a `LOCK_SH|LOCK_NB`
+lock using [`flock(2)`](http://man7.org/linux/man-pages/man2/flock.2.html) on
+the main block device (i.e. never on any partition block device, but on the
+device the partition belongs to). If this lock cannot be taken (i.e. `flock()`
+returns `EBUSY`), it refrains from processing the device. If it manages to take
+the lock it is kept for the entire time the device is processed.
+
+Note that `systemd-udevd` also watches all block device nodes it manages for
+`inotify()` `IN_CLOSE` events: whenever such an event is seen, this is used as
+trigger to re-run the rule-set for the device.
+
+These two concepts allow tools such as disk partitioners or file system
+formatting tools to safely and easily take exclusive ownership of a block
+device while operating: before starting work on the block device, they should
+take an `LOCK_EX` lock on it. This has two effects: first of all, in case
+`systemd-udevd` is still processing the device the tool will wait for it to
+finish. Second, after the lock is taken, it can be sure that that
+`systemd-udevd` will refrain from processing the block device, and thus all
+other client applications subscribed to it won't get device notifications from
+potentially half-written data either. After the operation is complete the
+partitioner/formatter can simply close the device node. This has two effects:
+it implicitly releases the lock, so that `systemd-udevd` can process events on
+the device node again. Secondly, it results an `IN_CLOSE` event, which causes
+`systemd-udevd` to immediately re-process the device — seeing all changes the
+tool made — and notify subscribed clients about it.
+
+Besides synchronizing block device access between `systemd-udevd` and such
+tools this scheme may also be used to synchronize access between those tools
+themselves. However, do note that `flock()` locks are advisory only. This means
+if one tool honours this scheme and another tool does not, they will of course
+not be synchronized properly, and might interfere with each other's work.
+
+Note that the file locks follow the usual access semantics of BSD locks: since
+`systemd-udevd` never writes to such block devices it only takes a `LOCK_SH`
+*shared* lock. A program intending to make changes to the block device should
+take a `LOCK_EX` *exclusive* lock instead. For further details, see the
+`flock(2)` man page.
+
+And please keep in mind: BSD file locks (`flock()`) and POSIX file locks
+(`lockf()`, `F_SETLK`, …) are different concepts, and in their effect
+orthogonal. The scheme discussed above uses the former and not the latter,
+because these types of locks more closely match the required semantics.
+
+Summarizing: it is recommended to take `LOCK_EX` BSD file locks when
+manipulating block devices in all tools that change file system block devices
+(`mkfs`, `fsck`, …) or partition tables (`fdisk`, `parted`, …), right after
+opening the node.
diff --git a/docs/BOOT_LOADER_INTERFACE.md b/docs/BOOT_LOADER_INTERFACE.md
new file mode 100644
index 0000000000..a0c7f39970
--- /dev/null
+++ b/docs/BOOT_LOADER_INTERFACE.md
@@ -0,0 +1,114 @@
+# The Boot Loader Interface
+
+systemd can interface with the boot loader to receive performance data and
+other information, and pass control information. This is only supported on EFI
+systems. Data is transferred between the boot loader and systemd in EFI
+variables. All EFI variables use the vendor UUID
+`4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`.
+
+* The EFI Variable `LoaderTimeInitUSec` contains the timestamp in microseconds
+ when the loader was initialized. This value is the time spent in the firmware
+ for initialization, it is formatted as numeric, NUL-terminated, decimal
+ string, in UTF-16.
+
+* The EFI Variable `LoaderTimeExecUSec` contains the timestamp in microseconds
+ when the loader finished its work and is about to execute the kernel. The
+ time spent in the loader is the difference between `LoaderTimeExecUSec` and
+ `LoaderTimeInitUSec`. This value is formatted the same way as
+ `LoaderTimeInitUSec`.
+
+* The EFI variable `LoaderDevicePartUUID` contains the partition GUID of the
+ ESP the boot loader was run from formatted as NUL-terminated UTF16 string, in
+ normal GUID syntax.
+
+* The EFI variable `LoaderConfigTimeout` contains the boot menu timeout
+ currently in use. It may be modified both by the boot loader and by the
+ host. The value should be formatted as numeric, NUL-terminated, decimal
+ string, in UTF-16. The time is specified in µs.
+
+* Similarly, the EFI variable `LoaderConfigTimeoutOneShot` contains a boot menu
+ timeout for a single following boot. It is set by the OS in order to request
+ display of the boot menu on the following boot. When set overrides
+ `LoaderConfigTimeout`. It is removed automatically after being read by the
+ boot loader, to ensure it only takes effect a single time. This value is
+ formatted the same way as `LoaderConfigTimeout`. If set to `0` the boot menu
+ timeout is turned off, and the menu is shown indefinitely.
+
+* The EFI variable `LoaderEntries` may contain a series of boot loader entry
+ identifiers, one after the other, each individually NUL terminated. This may
+ be used to let the OS know which boot menu entries were discovered by the
+ boot loader. A boot loader entry identifier should be a short, non-empty
+ alphanumeric string (possibly containing `-`, too). The list should be in the
+ order the entries are shown on screen during boot. See below regarding a
+ recommended vocabulary for boot loader entry identifiers.
+
+* The EFI variable `LoaderEntryDefault` contains the default boot loader entry
+ to use. It contains a NUL-terminated boot loader entry identifier.
+
+* Similarly, the EFI variable `LoaderEntryOneShot` contains the default boot
+ loader entry to use for a single following boot. It is set by the OS in order
+ to request booting into a specific menu entry on the following boot. When set
+ overrides `LoaderEntryDefault`. It is removed automatically after being read
+ by the boot loader, to ensure it only takes effect a single time. This value
+ is formatted the same way as `LoaderEntryDefault`.
+
+* The EFI variable `LoaderEntrySelected` contains the boot loader entry
+ identifier that was booted. It is set by the boot loader and read by
+ the OS in order to identify which entry has been used for the current boot.
+
+* The EFI variable `LoaderFeatures` contains a 64bit unsigned integer with a
+ number of flags bits that are set by the boot loader and passed to the OS and
+ indicate the features the boot loader supports. Specifically, the following
+ bits are defined:
+
+ * `1 << 0` → The boot loader honours `LoaderConfigTimeout` when set.
+ * `1 << 1` → The boot loader honours `LoaderConfigTimeoutOneShot` when set.
+ * `1 << 2` → The boot loader honours `LoaderEntryDefault` when set.
+ * `1 << 3` → The boot loader honours `LoaderEntryOneShot` when set.
+ * `1 << 4` → The boot loader supports boot counting as described in [Automatic Boot Assessment](https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT).
+
+If `LoaderTimeInitUSec` and `LoaderTimeExecUSec` are set, `systemd-analyze`
+will include them in its boot-time analysis. If `LoaderDevicePartUUID` is set,
+systemd will mount the ESP that was used for the boot to `/boot`, but only if
+that directory is empty, and only if no other file systems are mounted
+there. The `systemctl reboot --boot-loader-entry=…` and `systemctl reboot
+--boot-loader-menu=…` commands rely on the `LoaderFeatures` ,
+`LoaderConfigTimeoutOneShot`, `LoaderEntries`, `LoaderEntryOneShot` variables.
+
+## Boot Loader Entry Identifiers
+
+While boot loader entries may be named relatively freely, it's highly
+recommended to follow the following rules when picking identifiers for the
+entries, so that programs (and users) can derive basic context and meaning from
+the identifiers as passed in `LoaderEntries`, `LoaderEntryDefault`,
+`LoaderEntryOneShot`, `LoaderEntrySelected`, and possibly show nicely localized
+names for them in UIs.
+
+1. When boot loader entries are defined through [Boot Loader
+ Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION) drop-in files
+ the identifier should be derived directly from the drop-in snippet name, but
+ with the `.conf` (or `.efi` in case of Type #2 entries) suffix removed.
+
+2. Entries automatically discovered by the boot loader (as opposed to being
+ configured in configuration files) should generally have an identifier
+ prefixed with `auto-`.
+
+3. Boot menu entries referring to Microsoft Windows installations should either
+ use the identifier `windows` or use the `windows-` prefix for the
+ identifier. If a menu entry is automatically discovered, it should be
+ prefixed with `auto-`, see above (Example: this means an automatically
+ discovered Windows installation might have the identifier `auto-windows` or
+ `auto-windows-10` or so.).
+
+4. Similar, boot menu entries referring to Apple MacOS X installations should
+ use the identifier `osx` or one that is prefixed with `osx-`. If such an
+ entry is automatically discovered by the boot loader use `auto-osx` as
+ identifier, or `auto-osx-` as prefix for the identifier, see above.
+
+5. If a boot menu entry encapsulates the EFI shell program, it should use the
+ identifier `efi-shell` (or when automatically discovered: `auto-efi-shell`,
+ see above).
+
+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).
diff --git a/doc/BOOT_LOADER_SPECIFICATION.md b/docs/BOOT_LOADER_SPECIFICATION.md
index dc67f2ffc8..3724f78a80 100644
--- a/doc/BOOT_LOADER_SPECIFICATION.md
+++ b/docs/BOOT_LOADER_SPECIFICATION.md
@@ -15,9 +15,9 @@ Of course, without this specification things already work mostly fine. But here'
* To make the boot more robust, as no explicit rewriting of configuration files is required any more
* To improve dual-boot scenarios. Currently, multiple Linux installations tend to fight over which boot loader becomes the primary one in possession of the MBR, and only that one installation can then update the boot loader configuration of it freely. Other Linux installs have to be manually configured to never touch the MBR and instead install a chain-loaded boot loader in their own partition headers. In this new scheme as all installations share a loader directory no manual configuration has to take place, and all participants implicitly cooperate due to removal of name collisions and can install/remove their own boot menu entries at free will, without interfering with the entries of other installed operating systems.
-* Drop-in directories are otherwise now pretty ubiquitous on Linux as an easy way to extend configuration without having to edit, regenerate or manipulate configuration files. For the sake of uniformity we should do the same for extending the boot menu.
+* Drop-in directories are otherwise now pretty ubiquitous on Linux as an easy way to extend configuration without having to edit, regenerate or manipulate configuration files. For the sake of uniformity, we should do the same for extending the boot menu.
* Userspace code can sanely parse boot loader configuration which is essential with modern BIOSes which do not necessarily initialize USB keyboards anymore during boot, which makes boot menus hard to reach for the user. If userspace code can parse the boot loader configuration, too, this allows for UIs that can select a boot menu item to boot into, before rebooting the machine, thus not requiring interactivity during early boot.
-* To unify and thus simplify configuration of the various boot loaders around, which makes configuration of the boot loading process easier for users, administrators and developers alike
+* To unify and thus simplify configuration of the various boot loaders around, which makes configuration of the boot loading process easier for users, administrators and developers alike.
* For boot loaders with configuration _scripts_ such as grub2, adopting this spec allows for mostly static scripts that are generated only once at first installation, but then do not need to be updated anymore as that is done via drop-in files exclusively.
## Why not simply rely on the EFI boot menu logic?
@@ -25,7 +25,7 @@ Of course, without this specification things already work mostly fine. But here'
The EFI specification provides a boot options logic that can offer similar functionality. Here's why we think that it is not enough for our uses:
* The various EFI implementations implement the boot order/boot item logic to different levels. Some firmware implementations do not offer a boot menu at all and instead unconditionally follow the EFI boot order, booting the first item that is working.
-* If the firmware setup is used to reset all data usually all EFI boot entries are lost, making the system entirely unbootable, as the firmware setups generally do not offer a UI to define additional boot items. By placing the menu item information on disk it is always available, regardless if the BIOS setup data is lost.
+* If the firmware setup is used to reset all data usually all EFI boot entries are lost, making the system entirely unbootable, as the firmware setups generally do not offer a UI to define additional boot items. By placing the menu item information on disk, it is always available, regardless if the BIOS setup data is lost.
* Harddisk images should be moveable between machines and be bootable without requiring explicit EFI variables to be set. This also requires that the list of boot options is defined on disk, and not in EFI variables alone.
* EFI is not universal yet (especially on non-x86 platforms), this specification is useful both for EFI and non-EFI boot loaders.
* Many EFI systems disable USB support during early boot to optimize boot times, thus making keyboard input unavailable in the EFI menu. It is thus useful if the OS UI has a standardized way to discover available boot options which can be booted to.
@@ -36,61 +36,89 @@ Everything described below is located on a placeholder file system `$BOOT`. The
* 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 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`.
+ * 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`.
-This placeholder file system shall be determined during _installation time_, and an fstab entry maybe 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.
+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.
-**Note:** _`$BOOT` should be considered **shared** among all OS installations of a system. Instead of maintaining one `$BOOT` per installed OS (as `/boot` was traditionally handled), all installed OS share the same place to drop in their boot-time configuration._
+**Note:** _`$BOOT` should be considered **shared** among all OS installations of a system. Instead of maintaining one `$BOOT` per installed OS (as `/boot/` was traditionally handled), all installed OS share the same place to drop in their boot-time configuration._
-`$BOOT` must be a VFAT (16 or 32) file system. Other file system types should not be used. Applications accessing `$BOOT` should hence not assume that fancier file system features such as symlinks, hardlinks, access control or case sensitivity are supported.
+For systems where the firmware is able to read file systems directly, `$BOOT` must be a file system readable by the firmware. For other systems, `$BOOT` must be a VFAT (16 or 32) file system. Applications accessing `$BOOT` should hence not assume that fancier file system features such as symlinks, hardlinks, access control or case sensitivity are supported.
-### Boot loader specification entries
+This specification defines two types of boot loader entries. The first type is
+text based, very simple and suitable for a variety of firmware, architecture
+and image types ("Type #1"). The second type is specific to EFI, but allows
+single-file images that embed all metadata in the kernel binary itself, which
+is useful to cryptographically sign them as one file for the purpose of
+SecureBoot ("Type #2").
+
+Not all boot loader entries will apply to all systems. For example, Type #1
+entries that use the `efi` key and all Type #2 entries only apply to EFI
+systems. Entries using the `architecture` key might specify an architecture that
+doesn't match the local one. Boot loaders should ignore all entries that don't
+match the local platform and what the boot loader can support, and hide them
+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.
+
+### Type #1 Boot Loader Specification Entries
We define two directories below `$BOOT`:
-* `$BOOT/loader/` is the directory containing all files defined by this specification
+* `$BOOT/loader/` is the directory containing all files needed for Type #1 entries
* `$BOOT/loader/entries/` is the directory containing the drop-in snippets. This directory contains one `.conf` file for each boot menu item.
-**Note:** _In all cases the /loader directory should be located directly in the root of the file system. Specifically, if `$BOOT` is the ESP, then /loader directory should be located directly in the root directory of the ESP, and not in the EFI/ subdirectory._
+**Note:** _In all cases the `/loader/` directory should be located directly in the root of the file system. Specifically, if `$BOOT` is the ESP, then `/loader/` directory should be located directly in the root directory of the ESP, and not in the `/EFI/` subdirectory._
-Inside the `$BOOT/loader/entries/` directory each OS vendor may drop one or more configuration snippets with the suffix ".conf", one for each boot menu item. The file name of the file is used for identification of the boot item, but shall never be presented to the user in the UI. The file name may be chosen freely but should be unique enough to avoid clashes between OS installations. More specifically it is suggested to include the machine ID (`/etc/machine-id` or the D-Bus machine ID for OSes that lack `/etc/machine-id`), the kernel version (as returned by `uname -r`) and an OS identifier (The ID field of `/etc/os-release`). Example: `$BOOT/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf`.
+Inside the `$BOOT/loader/entries/` directory each OS vendor may drop one or more configuration snippets with the suffix ".conf", one for each boot menu item. The file name of the file is used for identification of the boot item but shall never be presented to the user in the UI. The file name may be chosen freely but should be unique enough to avoid clashes between OS installations. More specifically it is suggested to include the machine ID (`/etc/machine-id` or the D-Bus machine ID for OSes that lack `/etc/machine-id`), the kernel version (as returned by `uname -r`) and an OS identifier (The ID field of `/etc/os-release`). Example: `$BOOT/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf`.
-These configuration snippets shall be Unix-style text files (i.e. line separation with a single newline character), in the UTF-8 encoding. The configuration snippets are loosely inspired on Grub1's configuration syntax. Lines beginning with '#' shall be ignored and used for commenting. The first word of a line is used as key, and shall be separated by a space from its value. The following keys are known:
+These configuration snippets shall be Unix-style text files (i.e. line separation with a single newline character), in the UTF-8 encoding. The configuration snippets are loosely inspired on Grub1's configuration syntax. Lines beginning with '#' shall be ignored and used for commenting. The first word of a line is used as key and shall be separated by a space from its value. The following keys are known:
* `title` shall contain a human readable title string for this menu item. This will be displayed in the boot menu for the item. It is a good idea to initialize this from the `PRETTY_NAME` of `/etc/os-release`. This name should be descriptive and does not have to be unique. If a boot loader discovers two entries with the same title it is a good idea to show more than just the raw title in the UI, for example by appending the `version` field. This field is optional. Example: "Fedora 18 (Spherical Cow)".
-* `version` shall contain a human readable version string for this menu item. This is usually the kernel version and is intended for use by OSes to install multiple kernel versions at the same time with the same `title` field. This field shall be in a syntax that is useful for Debian-style version sorts, so that the boot loader UI can determine the newest version easily and show it first or preselect it automatically. This field is optional. Example: `3.7.2-201.fc18.x86_64`
-* `machine-id` shall contain the machine ID of the OS `/etc/machine-id`. This is useful for boot loaders and applications to filter out boot entries, for example to show only a single newest kernel per OS, or to group items by OS, or to maybe filter out the currently booted OS in UIs that want to show only other installed operating systems. This ID shall be formatted as 32 lower case hexadecimal characters (i.e. without any UUID formatting). This key is optional. Example: `4098b3f648d74c13b1f04ccfba7798e8`
-* `linux` refers to the kernel to spawn, and shall be a path relative to the `$BOOT` directory. It is recommended that every distribution creates a machine id and version specific subdirectory below `$BOOT` and places its kernels and initial RAM disk images there. Example: `/6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux`.
-* `initrd` refers to the initrd to use when executing the kernel. This also shall be a path relative to the `$BOOT` directory. This key is optional. This key may appear more than once in which case all specified images are used, in the order they are listed. Example: `6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd`
-* `efi` to spawn arbitrary EFI programs. This also takes a path relative to `$BOOT`. This key is only available on EFI systems.
-* `options` shall contain kernel parameters to pass to the Linux kernel to spawn. This key is optional.
+* `version` shall contain a human readable version string for this menu item. This is usually the kernel version and is intended for use by OSes to install multiple kernel versions at the same time with the same `title` field. This field shall be in a syntax that is useful for Debian-style version sorts, so that the boot loader UI can determine the newest version easily and show it first or preselect it automatically. This field is optional. Example: `3.7.2-201.fc18.x86_64`.
+* `machine-id` shall contain the machine ID of the OS `/etc/machine-id`. This is useful for boot loaders and applications to filter out boot entries, for example to show only a single newest kernel per OS, or to group items by OS, or to maybe filter out the currently booted OS in UIs that want to show only other installed operating systems. This ID shall be formatted as 32 lower case hexadecimal characters (i.e. without any UUID formatting). This key is optional. Example: `4098b3f648d74c13b1f04ccfba7798e8`.
+* `linux` refers to the Linux kernel to spawn and shall be a path relative to the `$BOOT` directory. It is recommended that every distribution creates a machine id and version specific subdirectory below `$BOOT` and places its kernels and initial RAM disk images there. Example: `/6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux`.
+* `initrd` refers to the initrd to use when executing the kernel. This also shall be a path relative to the `$BOOT` directory. This key is optional. This key may appear more than once in which case all specified images are used, in the order they are listed. Example: `6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd`.
+* `efi` refers to an arbitrary EFI program. This also takes a path relative to `$BOOT`. If this key is set, and the system is not an EFI system this entry should be hidden.
+* `options` shall contain kernel parameters to pass to the Linux kernel to spawn. This key is optional and may appear more than once in which case all specified parameters are used in the order they are listed.
* `devicetree` refers to the binary device tree to use when executing the
kernel. This also shall be a path relative to the `$BOOT` directory. This
-key is optional. Example: `6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.armv7hl/tegra20-paz00.dtb`
-* `architecture` refers to the UEFI architecture this entry is defined for. If specified and this does not match the local UEFI system architecture the entry is hidden.
+key is optional. Example: `6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.armv7hl/tegra20-paz00.dtb`.
+* `architecture` refers to the architecture this entry is defined for. The argument should be an architecture identifier, using the architecture vocabulary defined by the EFI specification (i.e. `IA32`, `x64`, `IA64`, `ARM`, `AA64`, …). If specified and this does not match (case insensitively) the local system architecture this entry should be hidden.
-Each configuration drop-in snippet must include at least a `linux` or an `efi` key, and is otherwise not valid. Here's an example for a complete drop-in file:
+Each configuration drop-in snippet must include at least a `linux` or an `efi` key and is otherwise not valid. Here's an example for a complete drop-in file:
# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf
- title Fedora 19 (Rawhide)
- version 3.8.0-2.fc19.x86_64
- machine-id 6a9857a393724b7a981ebb5b8495b9ea
- options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2
- linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux
- initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd
+ title Fedora 19 (Rawhide)
+ version 3.8.0-2.fc19.x86_64
+ machine-id 6a9857a393724b7a981ebb5b8495b9ea
+ options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2
+ architecture x64
+ linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux
+ initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd
-On EFI systems all kernel images shall be EFI images. In order to be compatible with EFI systems it is highly recommended only to install EFI kernel images, even on non-EFI systems, if that's applicable and supported on the specific architecture.
+On EFI systems all Linux kernel images should be EFI images. In order to increase compatibility with EFI systems it is highly recommended only to install EFI kernel images, even on non-EFI systems, if that's applicable and supported on the specific architecture.
Note that these configuration snippets may only reference kernels (and EFI programs) that reside on the same file system as the configuration snippets, i.e. everything referenced must be contained in the same file system. This is by design, as referencing other partitions or devices would require a non-trivial language for denoting device paths. If kernels/initrds are to be read from other partitions/disks the boot loader can do this in its own native configuration, using its own specific device path language, and this is out of focus for this specification. More specifically, on non-EFI systems configuration snippets following this specification cannot be used to spawn other operating systems (such as Windows).
-### Unified kernel images
+### Type #2 EFI Unified Kernel Images
+
+A unified kernel image is a single EFI PE executable combining an EFI stub
+loader, a kernel image, an initramfs image, and the kernel command line. See
+the description of the `--uefi` option in
+[dracut(8)](http://man7.org/linux/man-pages/man8/dracut.8.html). Such unified
+images will be searched for under `$BOOT/EFI/Linux/` and must have the
+extension `.efi`. Support for images of this type is of course specific to
+systems with EFI firmware. Ignore this section if you work on systems not
+supporting EFI.
-A unified kernel image is a single UEFI executable combining an UEFI stub loader, a kernel image, an initramfs image, and the kernel command line. See the description of the `--uefi` option in [dracut(8)](http://man7.org/linux/man-pages/man8/dracut.8.html). Such images will be searched for under `$BOOT/EFI/Linux`, and must have the extension `.efi`.
+Images of this type have the advantage that all metadata and payload that makes
+up the boot entry is monopolized in a single PE file that can be signed
+cryptographically as one for the purpose of EFI SecureBoot.
A valid unified kernel image must contain two PE sections:
@@ -99,7 +127,7 @@ A valid unified kernel image must contain two PE sections:
The `PRETTY_NAME=` and `VERSION_ID=` fields in the embedded os-release file are used the same as `title` and `version` in the "boot loader specification" entries. The `.cmdline` section is used instead of the `options` field. `linux` and `initrd` fields are not necessary, and there is no counterpart for the `machine-id` field.
-Any such images shall be added to the list of valid boot entries.
+On EFI, any such images shall be added to the list of valid boot entries.
### Additional notes
@@ -112,23 +140,40 @@ Note that all paths used in the configuration snippets use a Unix-style "/" as p
## Logic
-A _boot loader_ needs a file system driver to discover and read `$BOOT`, then simply reads all files `$BOOT/loader/entries/*.conf`, and populates its boot menu with this. It then extends this with any unified kernel images found in `$BOOT/EFI/Linux`. It may also add additional entries, for example "Reboot into firmware". Optionally it may sort the menu based on the `machine-id` and `version` fields, and possibly others. It uses the file name to identify specific items, for example in case it supports storing away default entry information somewhere. A boot loader should generally not modify these files.
-
-For "boot loader specification" entries, the _kernel package installer_ installs the kernel and initrd images to `$BOOT` (it is recommended to place these files in a vendor and OS and installation specific directory) and then generates a configuration snippet for it, placing this in `$BOOT/loader/entries/xyz.conf`, with xyz as concatenation of machine id and version information (see above). The files created by a kernel package are private property of the kernel package, and should be removed along with it.
-
-For "unified kernel images", the _kernel install_ creates the combined image and drops it into `$BOOT/EFI/Linux`. This file is also private property of the kernel package, and should be removed along with it.
+A _boot loader_ needs a file system driver to discover and read `$BOOT`, then
+simply reads all files `$BOOT/loader/entries/*.conf`, and populates its boot
+menu with this. On EFI, it then extends this with any unified kernel images
+found in `$BOOT/EFI/Linux/*.efi`. It may also add additional entries, for
+example a "Reboot into firmware" option. Optionally it may sort the menu based
+on the `machine-id` and `version` fields, and possibly others. It uses the file
+name to identify specific items, for example in case it supports storing away
+default entry information somewhere. A boot loader should generally not modify
+these files.
+
+For "Boot Loader Specification Entries" (Type #1), the _kernel package
+installer_ installs the kernel and initrd images to `$BOOT` (it is recommended
+to place these files in a vendor and OS and installation specific directory)
+and then generates a configuration snippet for it, placing this in
+`$BOOT/loader/entries/xyz.conf`, with xyz as concatenation of machine id and
+version information (see above). The files created by a kernel package are
+private property of the kernel package and should be removed along with it.
+
+For "EFI Unified Kernel Images" (Type #2), the vendor or kernel package
+installer creates the combined image and drops it into `$BOOT/EFI/Linux/`. This
+file is also private property of the kernel package and should be removed along
+with it.
A _UI application_ intended to show available boot options shall operate similar to a boot loader, but might apply additional filters, for example by filtering out the booted OS via the machine ID, or by suppressing all but the newest kernel versions.
-An _OS installer_ picks the right place for `$BOOT` as defined above (possibly creating a partition and file system for it), and pre-creates the `/loader/entries/` directory in it. It then installs an appropriate boot loader that can read these snippets. Finally it installs one or more kernel packages.
+An _OS installer_ picks the right place for `$BOOT` as defined above (possibly creating a partition and file system for it) and pre-creates the `/loader/entries/` directory in it. It then installs an appropriate boot loader that can read these snippets. Finally, it installs one or more kernel packages.
## Out of Focus
-There are a couple of items that are out of focus for this specifications:
+There are a couple of items that are out of focus for this specification:
-* If userspace can figure out the available boot options, then this is only useful so much: we'd still need to come up with a way how userspace could communicate to the boot loader the default boot loader entry temporarily or persistently. Defining a common scheme for this is certainly a good idea, but out of focus for this specifications.
-* This specifications is just about "Free" Operating systems. Hooking in other operating systems (like Windows, MacOS) into the boot menu is a different story, and should probably happen outside of this specification. For example, boot loaders might choose to detect other available OSes dynamically at runtime without explicit configuration (like <strike>Gummiboot</strike> systemd-boot does it), or via native configuration (for example via explicit Grub2 configuration generated once at installation).
+* If userspace can figure out the available boot options, then this is only useful so much: we'd still need to come up with a way how userspace could communicate to the boot loader the default boot loader entry temporarily or persistently. Defining a common scheme for this is certainly a good idea, but out of focus for this specification.
+* This specification is just about "Free" Operating systems. Hooking in other operating systems (like Windows and macOS) into the boot menu is a different story and should probably happen outside of this specification. For example, boot loaders might choose to detect other available OSes dynamically at runtime without explicit configuration (like `systemd-boot` does it), or via native configuration (for example via explicit Grub2 configuration generated once at installation).
* This specification leaves undefined what to do about systems which are upgraded from an OS that does not implement this specification. As the previous boot loader logic was largely handled by in distribution-specific ways we probably should leave the upgrade path (and whether there actually is one) to the distributions. The simplest solution might be to simply continue with the old scheme for old installations and use this new scheme only for new installations.
@@ -136,5 +181,3 @@ There are a couple of items that are out of focus for this specifications:
[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)
-
-[Obsolete patch adding Boot Loader Specification support to GNU grub 2](http://pkgs.fedoraproject.org/cgit/grub2.git/tree/0460-blscfg-add-blscfg-module-to-parse-Boot-Loader-Specif.patch?h=f20)
diff --git a/doc/CGROUP_DELEGATION.md b/docs/CGROUP_DELEGATION.md
index 63d9d41b10..ed23a0a142 100644
--- a/doc/CGROUP_DELEGATION.md
+++ b/docs/CGROUP_DELEGATION.md
@@ -91,10 +91,14 @@ hierarchy for managing purposes as `/sys/fs/cgroup/systemd/`.
3. **Hybrid** — this is a hybrid between the unified and legacy mode. It's set
up mostly like legacy, except that there's also an additional hierarchy
-`/sys/fs/cgroup/unified/` that contains the cgroupsv2 hierarchy. In this mode
-compatibility with cgroupsv1 is retained while some cgroupsv2 features are
-available too. This mode is a stopgap. Don't bother with this too much unless
-you have too much free time.
+`/sys/fs/cgroup/unified/` that contains the cgroupsv2 hierarchy. (Note that in
+this mode the unified hierarchy won't have controllers attached, the
+controllers are all mounted as separate hierarchies as in legacy mode,
+i.e. `/sys/fs/cgroup/unified/` is purely and exclusively about core cgroupsv2
+functionality and not about resource management.) In this mode compatibility
+with cgroupsv1 is retained while some cgroupsv2 features are available
+too. This mode is a stopgap. Don't bother with this too much unless you have
+too much free time.
To say this clearly, legacy and hybrid modes have no future. If you develop
software today and don't focus on the unified mode, then you are writing
@@ -118,7 +122,7 @@ thinking about them as orthogonal won't help you in the long run anyway.
If you wonder how to detect which of these three modes is currently used, use
`statfs()` on `/sys/fs/cgroup/`. If it reports `CGROUP2_SUPER_MAGIC` in its
`.f_type` field, then you are in unified mode. If it reports `TMPFS_MAGIC` then
-you are either in legacy or hybrid mode. To distuingish these two cases, run
+you are either in legacy or hybrid mode. To distinguish these two cases, run
`statfs()` again on `/sys/fs/cgroup/unified/`. If that succeeds and reports
`CGROUP2_SUPER_MAGIC` you are in hybrid mode, otherwise not.
@@ -240,6 +244,19 @@ So, if you want to do your own raw cgroups kernel level access, then allocate a
scope unit, or a service unit (or just use the service unit you already have
for your service code), and turn on delegation for it.
+(OK, here's one caveat: if you turn on delegation for a service, and that
+service has `ExecStartPost=`, `ExecReload=`, `ExecStop=` or `ExecStopPost=`
+set, then these commands will be executed within the `.control/` sub-cgroup of
+your service's cgroup. This is necessary because by turning on delegation we
+have to assume that the cgroup delegated to your service is now an *inner*
+cgroup, which means that it may not directly contain any processes. Hence, if
+your service has any of these four settings set, you must be prepared that a
+`.control/` subcgroup might appear, managed by the service manager. This also
+means that your service code should have moved itself further down the cgroup
+tree by the time it notifies the service manager about start-up readiness, so
+that the service's main cgroup is definitely an inner node by the time the
+service manager might start `ExecStartPost=`.)
+
## Three Scenarios
Let's say you write a container manager, and you wonder what to do regarding
diff --git a/docs/CNAME b/docs/CNAME
new file mode 100644
index 0000000000..cdcf4d9a52
--- /dev/null
+++ b/docs/CNAME
@@ -0,0 +1 @@
+systemd.io \ No newline at end of file
diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000..b3b46cd6d8
--- /dev/null
+++ b/docs/CODE_OF_CONDUCT.md
@@ -0,0 +1,14 @@
+# The systemd Community Conduct Guidelines
+
+This document provides community guidelines for a safe, respectful, productive, and collaborative place for any person who is willing to contribute to systemd. It applies to all “collaborative spacesâ€, which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.).
+
+- Participants will be tolerant of opposing views.
+- Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
+- When interpreting the words and actions of others, participants should always assume good intentions.
+- Behaviour which can be reasonably considered harassment will not be tolerated.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at systemd-conduct@googlegroups.com. This team currently consists of David Strauss <<systemd-conduct@davidstrauss.net>>, Ekaterina Gerasimova (Kat) <<Kittykat3756@gmail.com>>, and Zbigniew Jędrzejewski-Szmek <<zbyszek@in.waw.pl>>. In the unfortunate event that you wish to make a complaint against one of the members, you may instead contact any of the other members individually.
+
+All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
diff --git a/doc/CODE_QUALITY.md b/docs/CODE_QUALITY.md
index d020b19c07..be0e13f653 100644
--- a/doc/CODE_QUALITY.md
+++ b/docs/CODE_QUALITY.md
@@ -56,5 +56,9 @@ available functionality:
13. When building systemd from a git checkout the build scripts will
automatically enable a git commit hook that ensures whitespace cleanliness.
-Access to Coverity and oss-fuzz reports is limited. Please reach out the the
-maintainers is you need access.
+14. [LGTM](https://lgtm.com/) analyzes every commit pushed to master. The list
+ of active alerts can be found
+ [here](https://lgtm.com/projects/g/systemd/systemd/alerts/?mode=list).
+
+Access to Coverity and oss-fuzz reports is limited. Please reach out to the
+maintainers if you need access.
diff --git a/docs/CODING_STYLE.md b/docs/CODING_STYLE.md
new file mode 100644
index 0000000000..a91e119c4a
--- /dev/null
+++ b/docs/CODING_STYLE.md
@@ -0,0 +1,520 @@
+# Coding Style
+
+- 8ch indent, no tabs, except for files in `man/` which are 2ch indent,
+ and still no tabs.
+
+- We prefer `/* comments */` over `// comments` in code you commit, please. This
+ way `// comments` are left for developers to use for local, temporary
+ commenting of code for debug purposes (i.e. uncommittable stuff), making such
+ comments easily discernible from explanatory, documenting code comments
+ (i.e. committable stuff).
+
+- Don't break code lines too eagerly. We do **not** force line breaks at 80ch,
+ all of today's screens should be much larger than that. But then again, don't
+ overdo it, ~109ch should be enough really. The `.editorconfig`, `.vimrc` and
+ `.dir-locals.el` files contained in the repository will set this limit up for
+ you automatically, if you let them (as well as a few other things).
+
+- Variables and functions **must** be static, unless they have a
+ prototype, and are supposed to be exported.
+
+- structs in `PascalCase` (with exceptions, such as public API structs),
+ variables and functions in `snake_case`.
+
+- The destructors always deregister the object from the next bigger
+ object, not the other way around.
+
+- To minimize strict aliasing violations, we prefer unions over casting.
+
+- For robustness reasons, destructors should be able to destruct
+ half-initialized objects, too.
+
+- Error codes are returned as negative `Exxx`. e.g. `return -EINVAL`. There
+ are some exceptions: for constructors, it is OK to return `NULL` on
+ OOM. For lookup functions, `NULL` is fine too for "not found".
+
+ Be strict with this. When you write a function that can fail due to
+ more than one cause, it *really* should have an `int` as the return value
+ for the error code.
+
+- Do not bother with error checking whether writing to stdout/stderr
+ worked.
+
+- Do not log errors from "library" code, only do so from "main
+ program" code. (With one exception: it is OK to log with DEBUG level
+ from any code, with the exception of maybe inner loops).
+
+- Always check OOM. There is no excuse. In program code, you can use
+ `log_oom()` for then printing a short message, but not in "library" code.
+
+- Do not issue NSS requests (that includes user name and host name
+ lookups) from PID 1 as this might trigger deadlocks when those
+ lookups involve synchronously talking to services that we would need
+ to start up.
+
+- Do not synchronously talk to any other service from PID 1, due to
+ risk of deadlocks.
+
+- Avoid fixed-size string buffers, unless you really know the maximum
+ size and that maximum size is small. They are a source of errors,
+ since they possibly result in truncated strings. It is often nicer
+ to use dynamic memory, `alloca()` or VLAs. If you do allocate fixed-size
+ strings on the stack, then it is probably only OK if you either
+ use a maximum size such as `LINE_MAX`, or count in detail the maximum
+ size a string can have. (`DECIMAL_STR_MAX` and `DECIMAL_STR_WIDTH`
+ macros are your friends for this!)
+
+ Or in other words, if you use `char buf[256]` then you are likely
+ doing something wrong!
+
+- Stay uniform. For example, always use `usec_t` for time
+ values. Do not mix `usec` and `msec`, and `usec` and whatnot.
+
+- Make use of `_cleanup_free_` and friends. It makes your code much
+ nicer to read (and shorter)!
+
+- Be exceptionally careful when formatting and parsing floating point
+ numbers. Their syntax is locale dependent (i.e. `5.000` in en_US is
+ generally understood as 5, while in de_DE as 5000.).
+
+- Try to use this:
+
+ ```c
+ void foo() {
+ }
+ ```
+
+ instead of this:
+
+ ```c
+ void foo()
+ {
+ }
+ ```
+
+ But it is OK if you do not.
+
+- Single-line `if` blocks should not be enclosed in `{}`. Use this:
+
+ ```c
+ if (foobar)
+ waldo();
+ ```
+
+ instead of this:
+
+ ```c
+ if (foobar) {
+ waldo();
+ }
+ ```
+
+- Do not write `foo ()`, write `foo()`.
+
+- Please use `streq()` and `strneq()` instead of `strcmp()`, `strncmp()` where
+ applicable (i.e. wherever you just care about equality/inequality, not about
+ the sorting order).
+
+- Preferably allocate stack variables on the top of the block:
+
+ ```c
+ {
+ int a, b;
+
+ a = 5;
+ b = a;
+ }
+ ```
+
+- Unless you allocate an array, `double` is always the better choice
+ than `float`. Processors speak `double` natively anyway, so this is
+ no speed benefit, and on calls like `printf()` `float`s get promoted
+ to `double`s anyway, so there is no point.
+
+- Do not mix function invocations with variable definitions in one
+ line. Wrong:
+
+ ```c
+ {
+ int a = foobar();
+ uint64_t x = 7;
+ }
+ ```
+
+ Right:
+
+ ```c
+ {
+ int a;
+ uint64_t x = 7;
+
+ a = foobar();
+ }
+ ```
+
+- Use `goto` for cleaning up, and only use it for that. i.e. you may
+ only jump to the end of a function, and little else. Never jump
+ backwards!
+
+- Think about the types you use. If a value cannot sensibly be
+ negative, do not use `int`, but use `unsigned`.
+
+- Use `char` only for actual characters. Use `uint8_t` or `int8_t`
+ when you actually mean a byte-sized signed or unsigned
+ integers. When referring to a generic byte, we generally prefer the
+ unsigned variant `uint8_t`. Do not use types based on `short`. They
+ *never* make sense. Use `int`, `long`, `long long`, all in
+ unsigned and signed fashion, and the fixed-size types
+ `uint8_t`, `uint16_t`, `uint32_t`, `uint64_t`, `int8_t`, `int16_t`, `int32_t` and so on,
+ as well as `size_t`, but nothing else. Do not use kernel types like
+ `u32` and so on, leave that to the kernel.
+
+- Public API calls (i.e. functions exported by our shared libraries)
+ must be marked `_public_` and need to be prefixed with `sd_`. No
+ other functions should be prefixed like that.
+
+- In public API calls, you **must** validate all your input arguments for
+ programming error with `assert_return()` and return a sensible return
+ code. In all other calls, it is recommended to check for programming
+ errors with a more brutal `assert()`. We are more forgiving to public
+ users than for ourselves! Note that `assert()` and `assert_return()`
+ really only should be used for detecting programming errors, not for
+ runtime errors. `assert()` and `assert_return()` by usage of `_likely_()`
+ inform the compiler that he should not expect these checks to fail,
+ and they inform fellow programmers about the expected validity and
+ range of parameters.
+
+- Never use `strtol()`, `atoi()` and similar calls. Use `safe_atoli()`,
+ `safe_atou32()` and suchlike instead. They are much nicer to use in
+ most cases and correctly check for parsing errors.
+
+- For every function you add, think about whether it is a "logging"
+ function or a "non-logging" function. "Logging" functions do logging
+ on their own, "non-logging" function never log on their own and
+ expect their callers to log. All functions in "library" code,
+ i.e. in `src/shared/` and suchlike must be "non-logging". Every time a
+ "logging" function calls a "non-logging" function, it should log
+ about the resulting errors. If a "logging" function calls another
+ "logging" function, then it should not generate log messages, so
+ that log messages are not generated twice for the same errors.
+
+- If possible, do a combined log & return operation:
+
+ ```c
+ r = operation(...);
+ if (r < 0)
+ return log_(error|warning|notice|...)_errno(r, "Failed to ...: %m");
+ ```
+
+ If the error value is "synthetic", i.e. it was not received from
+ the called function, use `SYNTHETIC_ERRNO` wrapper to tell the logging
+ system to not log the errno value, but still return it:
+
+ ```c
+ n = read(..., s, sizeof s);
+ if (n != sizeof s)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read ...");
+ ```
+
+- Avoid static variables, except for caches and very few other
+ cases. Think about thread-safety! While most of our code is never
+ used in threaded environments, at least the library code should make
+ sure it works correctly in them. Instead of doing a lot of locking
+ for that, we tend to prefer using TLS to do per-thread caching (which
+ only works for small, fixed-size cache objects), or we disable
+ caching for any thread that is not the main thread. Use
+ `is_main_thread()` to detect whether the calling thread is the main
+ thread.
+
+- Command line option parsing:
+ - Do not print full `help()` on error, be specific about the error.
+ - Do not print messages to stdout on error.
+ - Do not POSIX_ME_HARDER unless necessary, i.e. avoid `+` in option string.
+
+- Do not write functions that clobber call-by-reference variables on
+ failure. Use temporary variables for these cases and change the
+ passed in variables only on success.
+
+- When you allocate a file descriptor, it should be made `O_CLOEXEC`
+ right from the beginning, as none of our files should leak to forked
+ binaries by default. Hence, whenever you open a file, `O_CLOEXEC` must
+ be specified, right from the beginning. This also applies to
+ sockets. Effectively, this means that all invocations to:
+
+ - `open()` must get `O_CLOEXEC` passed,
+ - `socket()` and `socketpair()` must get `SOCK_CLOEXEC` passed,
+ - `recvmsg()` must get `MSG_CMSG_CLOEXEC` set,
+ - `F_DUPFD_CLOEXEC` should be used instead of `F_DUPFD`, and so on,
+ - invocations of `fopen()` should take `e`.
+
+- We never use the POSIX version of `basename()` (which glibc defines it in
+ `libgen.h`), only the GNU version (which glibc defines in `string.h`).
+ The only reason to include `libgen.h` is because `dirname()`
+ is needed. Every time you need that please immediately undefine
+ `basename()`, and add a comment about it, so that no code ever ends up
+ using the POSIX version!
+
+- Use the bool type for booleans, not integers. One exception: in public
+ headers (i.e those in `src/systemd/sd-*.h`) use integers after all, as `bool`
+ is C99 and in our public APIs we try to stick to C89 (with a few extension).
+
+- When you invoke certain calls like `unlink()`, or `mkdir_p()` and you
+ know it is safe to ignore the error it might return (because a later
+ call would detect the failure anyway, or because the error is in an
+ error path and you thus couldn't do anything about it anyway), then
+ make this clear by casting the invocation explicitly to `(void)`. Code
+ checks like Coverity understand that, and will not complain about
+ ignored error codes. Hence, please use this:
+
+ ```c
+ (void) unlink("/foo/bar/baz");
+ ```
+
+ instead of just this:
+
+ ```c
+ unlink("/foo/bar/baz");
+ ```
+
+ Don't cast function calls to `(void)` that return no error
+ conditions. Specifically, the various `xyz_unref()` calls that return a `NULL`
+ object shouldn't be cast to `(void)`, since not using the return value does not
+ hide any errors.
+
+- Don't invoke `exit()`, ever. It is not replacement for proper error
+ handling. Please escalate errors up your call chain, and use normal
+ `return` to exit from the main function of a process. If you
+ `fork()`ed off a child process, please use `_exit()` instead of `exit()`,
+ so that the exit handlers are not run.
+
+- Please never use `dup()`. Use `fcntl(fd, F_DUPFD_CLOEXEC, 3)`
+ instead. For two reason: first, you want `O_CLOEXEC` set on the new `fd`
+ (see above). Second, `dup()` will happily duplicate your `fd` as 0, 1,
+ 2, i.e. stdin, stdout, stderr, should those `fd`s be closed. Given the
+ special semantics of those `fd`s, it's probably a good idea to avoid
+ them. `F_DUPFD_CLOEXEC` with `3` as parameter avoids them.
+
+- When you define a destructor or `unref()` call for an object, please
+ accept a `NULL` object and simply treat this as NOP. This is similar
+ to how libc `free()` works, which accepts `NULL` pointers and becomes a
+ NOP for them. By following this scheme a lot of `if` checks can be
+ removed before invoking your destructor, which makes the code
+ substantially more readable and robust.
+
+- Related to this: when you define a destructor or `unref()` call for an
+ object, please make it return the same type it takes and always
+ return `NULL` from it. This allows writing code like this:
+
+ ```c
+ p = foobar_unref(p);
+ ```
+
+ which will always work regardless if `p` is initialized or not, and
+ guarantees that `p` is `NULL` afterwards, all in just one line.
+
+- Use `alloca()`, but never forget that it is not OK to invoke `alloca()`
+ within a loop or within function call parameters. `alloca()` memory is
+ released at the end of a function, and not at the end of a `{}`
+ block. Thus, if you invoke it in a loop, you keep increasing the
+ stack pointer without ever releasing memory again. (VLAs have better
+ behavior in this case, so consider using them as an alternative.)
+ Regarding not using `alloca()` within function parameters, see the
+ BUGS section of the `alloca(3)` man page.
+
+- Use `memzero()` or even better `zero()` instead of `memset(..., 0, ...)`
+
+- Instead of using `memzero()`/`memset()` to initialize structs allocated
+ on the stack, please try to use c99 structure initializers. It's
+ short, prettier and actually even faster at execution. Hence:
+
+ ```c
+ struct foobar t = {
+ .foo = 7,
+ .bar = "bazz",
+ };
+ ```
+
+ instead of:
+
+ ```c
+ struct foobar t;
+ zero(t);
+ t.foo = 7;
+ t.bar = "bazz";
+ ```
+
+- When returning a return code from `main()`, please preferably use
+ `EXIT_FAILURE` and `EXIT_SUCCESS` as defined by libc.
+
+- The order in which header files are included doesn't matter too
+ much. systemd-internal headers must not rely on an include order, so
+ it is safe to include them in any order possible.
+ However, to not clutter global includes, and to make sure internal
+ definitions will not affect global headers, please always include the
+ headers of external components first (these are all headers enclosed
+ in <>), followed by our own exported headers (usually everything
+ that's prefixed by `sd-`), and then followed by internal headers.
+ Furthermore, in all three groups, order all includes alphabetically
+ so duplicate includes can easily be detected.
+
+- To implement an endless loop, use `for (;;)` rather than `while (1)`.
+ The latter is a bit ugly anyway, since you probably really
+ meant `while (true)`. To avoid the discussion what the right
+ always-true expression for an infinite while loop is, our
+ recommendation is to simply write it without any such expression by
+ using `for (;;)`.
+
+- Never use the `off_t` type, and particularly avoid it in public
+ APIs. It's really weirdly defined, as it usually is 64-bit and we
+ don't support it any other way, but it could in theory also be
+ 32-bit. Which one it is depends on a compiler switch chosen by the
+ compiled program, which hence corrupts APIs using it unless they can
+ also follow the program's choice. Moreover, in systemd we should
+ parse values the same way on all architectures and cannot expose
+ `off_t` values over D-Bus. To avoid any confusion regarding conversion
+ and ABIs, always use simply `uint64_t` directly.
+
+- Commit message subject lines should be prefixed with an appropriate
+ component name of some kind. For example "journal: ", "nspawn: " and
+ so on.
+
+- Do not use "Signed-Off-By:" in your commit messages. That's a kernel
+ thing we don't do in the systemd project.
+
+- Avoid leaving long-running child processes around, i.e. `fork()`s that
+ are not followed quickly by an `execv()` in the child. Resource
+ management is unclear in this case, and memory CoW will result in
+ unexpected penalties in the parent much, much later on.
+
+- Don't block execution for arbitrary amounts of time using `usleep()`
+ or a similar call, unless you really know what you do. Just "giving
+ something some time", or so is a lazy excuse. Always wait for the
+ proper event, instead of doing time-based poll loops.
+
+- To determine the length of a constant string `"foo"`, don't bother with
+ `sizeof("foo")-1`, please use `strlen()` instead (both gcc and clang optimize
+ the call away for fixed strings). The only exception is when declaring an
+ array. In that case use STRLEN, which evaluates to a static constant and
+ doesn't force the compiler to create a VLA.
+
+- If you want to concatenate two or more strings, consider using `strjoina()`
+ or `strjoin()` rather than `asprintf()`, as the latter is a lot slower. This
+ matters particularly in inner loops (but note that `strjoina()` cannot be
+ used there).
+
+- Please avoid using global variables as much as you can. And if you
+ do use them make sure they are static at least, instead of
+ exported. Especially in library-like code it is important to avoid
+ global variables. Why are global variables bad? They usually hinder
+ generic reusability of code (since they break in threaded programs,
+ and usually would require locking there), and as the code using them
+ has side-effects make programs non-transparent. That said, there are
+ many cases where they explicitly make a lot of sense, and are OK to
+ use. For example, the log level and target in `log.c` is stored in a
+ global variable, and that's OK and probably expected by most. Also
+ in many cases we cache data in global variables. If you add more
+ caches like this, please be careful however, and think about
+ threading. Only use static variables if you are sure that
+ thread-safety doesn't matter in your case. Alternatively, consider
+ using TLS, which is pretty easy to use with gcc's `thread_local`
+ concept. It's also OK to store data that is inherently global in
+ global variables, for example data parsed from command lines, see
+ below.
+
+- If you parse a command line, and want to store the parsed parameters
+ in global variables, please consider prefixing their names with
+ `arg_`. We have been following this naming rule in most of our
+ tools, and we should continue to do so, as it makes it easy to
+ identify command line parameter variables, and makes it clear why it
+ is OK that they are global variables.
+
+- When exposing public C APIs, be careful what function parameters you make
+ `const`. For example, a parameter taking a context object should probably not
+ be `const`, even if you are writing an otherwise read-only accessor function
+ for it. The reason is that making it `const` fixates the contract that your
+ call won't alter the object ever, as part of the API. However, that's often
+ quite a promise, given that this even prohibits object-internal caching or
+ lazy initialization of object variables. Moreover, it's usually not too useful
+ for client applications. Hence, please be careful and avoid `const` on object
+ parameters, unless you are very sure `const` is appropriate.
+
+- Make sure to enforce limits on every user controllable resource. If the user
+ can allocate resources in your code, your code must enforce some form of
+ limits after which it will refuse operation. It's fine if it is hard-coded (at
+ least initially), but it needs to be there. This is particularly important
+ for objects that unprivileged users may allocate, but also matters for
+ everything else any user may allocated.
+
+- `htonl()`/`ntohl()` and `htons()`/`ntohs()` are weird. Please use `htobe32()` and
+ `htobe16()` instead, it's much more descriptive, and actually says what really
+ is happening, after all `htonl()` and `htons()` don't operate on `long`s and
+ `short`s as their name would suggest, but on `uint32_t` and `uint16_t`. Also,
+ "network byte order" is just a weird name for "big endian", hence we might
+ want to call it "big endian" right-away.
+
+- You might wonder what kind of common code belongs in `src/shared/` and what
+ belongs in `src/basic/`. The split is like this: anything that is used to
+ implement the public shared object we provide (sd-bus, sd-login, sd-id128,
+ nss-systemd, nss-mymachines, nss-resolve, nss-myhostname, pam_systemd), must
+ be located in `src/basic` (those objects are not allowed to link to
+ libsystemd-shared.so). Conversely, anything which is shared between multiple
+ components and does not need to be in `src/basic/`, should be in
+ `src/shared/`.
+
+ To summarize:
+
+ `src/basic/`
+ - may be used by all code in the tree
+ - may not use any code outside of `src/basic/`
+
+ `src/libsystemd/`
+ - may be used by all code in the tree, except for code in `src/basic/`
+ - may not use any code outside of `src/basic/`, `src/libsystemd/`
+
+ `src/shared/`
+ - may be used by all code in the tree, except for code in `src/basic/`,
+ `src/libsystemd/`, `src/nss-*`, `src/login/pam_systemd.*`, and files under
+ `src/journal/` that end up in `libjournal-client.a` convenience library.
+ - may not use any code outside of `src/basic/`, `src/libsystemd/`, `src/shared/`
+
+- Our focus is on the GNU libc (glibc), not any other libcs. If other libcs are
+ incompatible with glibc it's on them. However, if there are equivalent POSIX
+ and Linux/GNU-specific APIs, we generally prefer the POSIX APIs. If there
+ aren't, we are happy to use GNU or Linux APIs, and expect non-GNU
+ implementations of libc to catch up with glibc.
+
+- Whenever installing a signal handler, make sure to set `SA_RESTART` for it, so
+ that interrupted system calls are automatically restarted, and we minimize
+ hassles with handling `EINTR` (in particular as `EINTR` handling is pretty broken
+ on Linux).
+
+- When applying C-style unescaping as well as specifier expansion on the same
+ string, always apply the C-style unescaping fist, followed by the specifier
+ expansion. When doing the reverse, make sure to escape `%` in specifier-style
+ first (i.e. `%` → `%%`), and then do C-style escaping where necessary.
+
+- It's a good idea to use `O_NONBLOCK` when opening 'foreign' regular files, i.e.
+ file system objects that are supposed to be regular files whose paths where
+ specified by the user and hence might actually refer to other types of file
+ system objects. This is a good idea so that we don't end up blocking on
+ 'strange' file nodes, for example if the user pointed us to a FIFO or device
+ node which may block when opening. Moreover even for actual regular files
+ `O_NONBLOCK` has a benefit: it bypasses any mandatory lock that might be in
+ effect on the regular file. If in doubt consider turning off `O_NONBLOCK` again
+ after opening.
+
+- When referring to a configuration file option in the documentation and such,
+ please always suffix it with `=`, to indicate that it is a configuration file
+ setting.
+
+- When referring to a command line option in the documentation and such, please
+ always prefix with `--` or `-` (as appropriate), to indicate that it is a
+ command line option.
+
+- When referring to a file system path that is a directory, please always
+ suffix it with `/`, to indicate that it is a directory, not a regular file
+ (or other file system object).
+
+- Don't use `fgets()`, it's too hard to properly handle errors such as overly
+ long lines. Use `read_line()` instead, which is our own function that handles
+ this much nicer.
diff --git a/.github/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 2f266f2934..6cfdd6f179 100644
--- a/.github/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -24,8 +24,8 @@ If you discover a security vulnerability, we'd appreciate a non-public disclosur
## Posting Pull Requests
* Make sure to post PRs only relative to a very recent git master.
-* Follow our [Coding Style](https://raw.githubusercontent.com/systemd/systemd/master/doc/CODING_STYLE) when contributing code. This is a requirement for all code we merge.
-* Please make sure to test your change before submitting the PR. See [HACKING](https://raw.githubusercontent.com/systemd/systemd/master/doc/HACKING) for details how to do this.
+* Follow our [Coding Style](CODING_STYLE.md) when contributing code. This is a requirement for all code we merge.
+* Please make sure to test your change before submitting the PR. See the [Hacking guide](HACKING.md) for details on how to do this.
* Make sure to run the test suite locally, before posting your PR. We use a CI system, meaning we don't even look at your PR, if the build and tests don't pass.
* If you need to update the code in an existing PR, force-push into the same branch, overriding old commits with new versions.
* After you have pushed a new version, add a comment about the new version (no notification is sent just for the commits, so it's easy to miss the update without an explicit comment). If you are a member of the systemd project on GitHub, remove the `reviewed/needs-rework` label.
diff --git a/docs/DISTRO_PORTING.md b/docs/DISTRO_PORTING.md
new file mode 100644
index 0000000000..bcb093fbdc
--- /dev/null
+++ b/docs/DISTRO_PORTING.md
@@ -0,0 +1,75 @@
+# Porting systemd To New Distributions
+
+## HOWTO
+
+You need to make the follow changes to adapt systemd to your
+distribution:
+
+1. Find the right configure parameters for:
+
+ * `-Drootprefix=`
+ * `-Dsysvinit-path=`
+ * `-Dsysvrcnd-path=`
+ * `-Drc-local=`
+ * `-Dhalt-local=`
+ * `-Dloadkeys-path=`
+ * `-Dsetfont-path=`
+ * `-Dtty-gid=`
+ * `-Dntp-servers=`
+ * `-Ddns-servers=`
+ * `-Dsupport-url=`
+
+2. Try it out.
+
+ Play around (as an ordinary user) with
+ `/usr/lib/systemd/systemd --test --system` for a test run
+ of systemd without booting. This will read the unit files and
+ print the initial transaction it would execute during boot-up.
+ This will also inform you about ordering loops and suchlike.
+
+## NTP Pool
+
+By default, systemd-timesyncd uses the Google Public NTP servers
+`time[1-4].google.com`, if no other NTP configuration is available.
+They serve time that uses a
+[leap second smear](https://developers.google.com/time/smear)
+and can be up to .5s off from servers that use stepped leap seconds.
+
+If you prefer to use leap second steps, please register your own
+vendor pool at ntp.org and make it the built-in default by
+passing `-Dntp-servers=` to meson. Registering vendor
+pools is [free](http://www.pool.ntp.org/en/vendors.html).
+
+Use `-Dntp-servers=` to direct systemd-timesyncd to different fallback
+NTP servers.
+
+## DNS Servers
+
+By default, systemd-resolved uses the Google Public DNS servers
+`8.8.8.8`, `8.8.4.4`, `2001:4860:4860::8888`, `2001:4860:4860::8844`
+as fallback, if no other DNS configuration is available.
+
+Use `-Ddns-servers=` to direct systemd-resolved to different fallback
+DNS servers.
+
+## PAM
+
+The default PAM config shipped by systemd is really bare bones.
+It does not include many modules your distro might want to enable
+to provide a more seamless experience. For example, limits set in
+`/etc/security/limits.conf` will not be read unless you load `pam_limits`.
+Make sure you add modules your distro expects from user services.
+
+Pass `-Dpamconfdir=no` to meson to avoid installing this file and
+instead install your own.
+
+## Contributing Upstream
+
+We generally do no longer accept distribution-specific patches to
+systemd upstream. If you have to make changes to systemd's source code
+to make it work on your distribution, unless your code is generic
+enough to be generally useful, we are unlikely to merge it. Please
+always consider adopting the upstream defaults. If that is not
+possible, please maintain the relevant patches downstream.
+
+Thank you for understanding.
diff --git a/doc/ENVIRONMENT.md b/docs/ENVIRONMENT.md
index 85d26fe28c..619a57eb3f 100644
--- a/doc/ENVIRONMENT.md
+++ b/docs/ENVIRONMENT.md
@@ -37,6 +37,23 @@ All tools:
useful for debugging, in order to test generators and other code against
specific kernel command lines.
+* `$SYSTEMD_IN_INITRD` — takes a boolean. If set, overrides initrd detection.
+ This is useful for debugging and testing initrd-only programs in the main
+ system.
+
+* `$SYSTEMD_BUS_TIMEOUT=SECS` — specifies the maximum time to wait for method call
+ completion. If no time unit is specified, assumes seconds. The usual other units
+ are understood, too (us, ms, s, min, h, d, w, month, y). If it is not set or set
+ to 0, then the built-in default is used.
+
+* `$SYSTEMD_MEMPOOL=0` — if set, the internal memory caching logic employed by
+ hash tables is turned off, and libc malloc() is used for all allocations.
+
+* `$SYSTEMD_EMOJI=0` — if set, tools such as "systemd-analyze security" will
+ not output graphical smiley emojis, but ASCII alternatives instead. Note that
+ this only controls use of Unicode emoji glyphs, and has no effect on other
+ Unicode glyphs.
+
systemctl:
* `$SYSTEMCTL_FORCE_BUS=1` — if set, do not connect to PID1's private D-Bus
@@ -65,12 +82,27 @@ systemd-nspawn:
* `$SYSTEMD_NSPAWN_LOCK=0` — if set, do not lock container images when running.
+* `$SYSTEMD_NSPAWN_TMPFS_TMP=0` — if set, do not overmount /tmp in the
+ container with a tmpfs, but leave the directory from the image in place.
+
systemd-logind:
* `$SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1` — if set, report that
hibernation is available even if the swap devices do not provide enough room
for it.
+systemd-udevd:
+
+* `$NET_NAMING_SCHEME=` – if set, takes a network naming scheme (i.e. one of
+ "v238", "v239", "v240"…, or the special value "latest") as parameter. If
+ specified udev's net_id builtin will follow the specified naming scheme when
+ determining stable network interface names. This may be used to revert to
+ naming schemes of older udev versions, in order to provide more stable naming
+ across updates. This environment variable takes precedence over the kernel
+ command line option `net.naming-scheme=`, except if the value is prefixed
+ with `:` in which case the kernel command line option takes precedence, if it
+ is specified as well.
+
installed systemd tests:
* `$SYSTEMD_TEST_DATA` — override the location of test data. This is useful if
@@ -96,3 +128,48 @@ systemd-timedated:
NTP client services. If set, `timedatectl set-ntp on` enables and starts the
first existing unit listed in the environment variable, and
`timedatectl set-ntp off` disables and stops all listed units.
+
+systemd-sulogin-shell:
+
+* `$SYSTEMD_SULOGIN_FORCE=1` — This skips asking for the root password if the
+ root password is not available (such as when the root account is locked).
+ See `sulogin(8)` for more details.
+
+bootctl and other tools that access the EFI System Partition (ESP):
+
+* `$SYSTEMD_RELAX_ESP_CHECKS=1` — if set, the ESP validation checks are
+ relaxed. Specifically, validation checks that ensure the specified ESP path
+ is a FAT file system are turned off, as are checks that the path is located
+ on a GPT partition with the correct type UUID.
+
+* `$SYSTEMD_ESP_PATH=…` — override the path to the EFI System Partition. This
+ may be used to override ESP path auto detection, and redirect any accesses to
+ the ESP to the specified directory. Not that unlike with bootctl's --path=
+ switch only very superficial validation of the specified path is done when
+ this environment variable is used.
+
+systemd itself:
+
+* `$SYSTEMD_ACTIVATION_UNIT` — set for all NSS and PAM module invocations that
+ are done by the service manager on behalf of a specific unit, in child
+ processes that are later (after execve()) going to become unit
+ processes. Contains the full unit name (e.g. "foobar.service"). NSS and PAM
+ modules can use this information to determine in which context and on whose
+ behalf they are being called, which may be useful to avoid deadlocks, for
+ example to bypass IPC calls to the very service that is about to be
+ started. Note that NSS and PAM modules should be careful to only rely on this
+ data when invoked privileged, or possibly only when getppid() returns 1, as
+ setting environment variables is of course possible in any even unprivileged
+ contexts.
+
+* `$SYSTEMD_ACTIVATION_SCOPE` — closely related to `$SYSTEMD_ACTIVATION_UNIT`,
+ it is either set to `system` or `user` depending on whether the NSS/PAM
+ module is called by systemd in `--system` or `--user` mode.
+
+systemd-remount-fs:
+
+* `$SYSTEMD_REMOUNT_ROOT_RW=1` — if set and and no entry for the root directory
+ exists in /etc/fstab (this file always takes precedence), then the root
+ directory is remounted writable. This is primarily used by
+ systemd-gpt-auto-generator to ensure the root partition is mounted writable
+ in accordance to the GPT partition flags.
diff --git a/docs/HACKING.md b/docs/HACKING.md
new file mode 100644
index 0000000000..17136060ce
--- /dev/null
+++ b/docs/HACKING.md
@@ -0,0 +1,123 @@
+# Hacking on systemd
+
+We welcome all contributions to systemd. If you notice a bug or a missing
+feature, please feel invited to fix it, and submit your work as a GitHub Pull
+Request (PR) at https://github.com/systemd/systemd/pull/new.
+
+Please make sure to follow our [Coding Style](CODING_STYLE.md) when submitting patches.
+Also have a look at our [Contribution Guidelines](CONTRIBUTING.md).
+
+When adding new functionality, tests should be added. For shared functionality
+(in `src/basic/` and `src/shared/`) unit tests should be sufficient. The general
+policy is to keep tests in matching files underneath `src/test/`,
+e.g. `src/test/test-path-util.c` contains tests for any functions in
+`src/basic/path-util.c`. If adding a new source file, consider adding a matching
+test executable. For features at a higher level, tests in `src/test/` are very
+strongly recommended. If that is not possible, integration tests in `test/` are
+encouraged.
+
+Please also have a look at our list of [code quality tools](CODE_QUALITY.md) we have setup for systemd,
+to ensure our codebase stays in good shape.
+
+Please always test your work before submitting a PR. For many of the components
+of systemd testing is straight-forward as you can simply compile systemd and
+run the relevant tool from the build directory.
+
+For some components (most importantly, systemd/PID1 itself) this is not
+possible, however. In order to simplify testing for cases like this we provide
+a set of `mkosi` build files directly in the source tree. `mkosi` is a tool for
+building clean OS images from an upstream distribution in combination with a
+fresh build of the project in the local working directory. To make use of this,
+please acquire `mkosi` from https://github.com/systemd/mkosi first, unless your
+distribution has packaged it already and you can get it from there. After the
+tool is installed it is sufficient to type `mkosi` in the systemd project
+directory to generate a disk image `image.raw` you can boot either in
+`systemd-nspawn` or in an UEFI-capable VM:
+
+```
+# systemd-nspawn -bi image.raw
+```
+
+or:
+
+```
+# qemu-system-x86_64 -enable-kvm -m 512 -smp 2 -bios /usr/share/edk2/ovmf/OVMF_CODE.fd -hda image.raw
+```
+
+Every time you rerun the `mkosi` command a fresh image is built, incorporating
+all current changes you made to the project tree.
+
+Alternatively, you may install the systemd version from your git check-out
+directly on top of your host system's directory tree. This mostly works fine,
+but of course you should know what you are doing as you might make your system
+unbootable in case of a bug in your changes. Also, you might step into your
+package manager's territory with this. Be careful!
+
+And never forget: most distributions provide very simple and convenient ways to
+install all development packages necessary to build systemd. For example, on
+Fedora the following command line should be sufficient to install all of
+systemd's build dependencies:
+
+```
+# dnf builddep systemd
+```
+
+Putting this all together, here's a series of commands for preparing a patch
+for systemd (this example is for Fedora):
+
+```sh
+$ sudo dnf builddep systemd # install build dependencies
+$ sudo dnf install mkosi # install tool to quickly build images
+$ git clone https://github.com/systemd/systemd.git
+$ cd systemd
+$ vim src/core/main.c # or wherever you'd like to make your changes
+$ meson build # configure the build
+$ ninja -C build # build it locally, see if everything compiles fine
+$ ninja -C build test # run some simple regression tests
+$ (umask 077; echo 123 > mkosi.rootpw) # set root password used by mkosi
+$ sudo mkosi # build a test image
+$ sudo systemd-nspawn -bi image.raw # boot up the test image
+$ git add -p # interactively put together your patch
+$ git commit # commit it
+$ git push REMOTE HEAD:refs/heads/BRANCH
+ # where REMOTE is your "fork" on GitHub
+ # and BRANCH is a branch name.
+```
+
+And after that, head over to your repo on GitHub and click "Compare & pull request"
+
+Happy hacking!
+
+
+## Fuzzers
+
+systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically
+run by [OSS-Fuzz](https://github.com/google/oss-fuzz) with sanitizers. To add a
+fuzz target, create a new `src/fuzz/fuzz-foo.c` file with a `LLVMFuzzerTestOneInput`
+function and add it to the list in `src/fuzz/meson.build`.
+
+Whenever possible, a seed corpus and a dictionary should also be added with new
+fuzz targets. The dictionary should be named `src/fuzz/fuzz-foo.dict` and the seed
+corpus should be built and exported as `$OUT/fuzz-foo_seed_corpus.zip` in
+`tools/oss-fuzz.sh`.
+
+The fuzzers can be built locally if you have libFuzzer installed by running
+`tools/oss-fuzz.sh`. You should also confirm that the fuzzer runs in the
+OSS-Fuzz environment by checking out the OSS-Fuzz repo, and then running
+commands like this:
+
+```
+python infra/helper.py build_image systemd
+python infra/helper.py build_fuzzers --sanitizer memory systemd ../systemd
+python infra/helper.py run_fuzzer systemd fuzz-foo
+```
+
+If you find a bug that impacts the security of systemd, please follow the
+guidance in [CONTRIBUTING.md](CONTRIBUTING.md) on how to report a security vulnerability.
+
+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
diff --git a/doc/PORTABLE_SERVICES.md b/docs/PORTABLE_SERVICES.md
index 1833244447..4b37a19455 100644
--- a/doc/PORTABLE_SERVICES.md
+++ b/docs/PORTABLE_SERVICES.md
@@ -20,7 +20,7 @@ Portable services don't bring anything inherently new to the table. All they do
is put together known concepts in a slightly nicer way to cover a specific set
of use-cases in a nicer way.
-# So, what *is* a "Portable Service"?
+## So, what *is* a "Portable Service"?
A portable service is ultimately just an OS tree, either inside of a directory
tree, or inside a raw disk image containing a Linux file system. This tree is
@@ -43,7 +43,7 @@ do too.
If you so will, "Portable Services" are a nicer way to manage chroot()
environments, with better security, tooling and behavior.
-# Where's the difference to a "Container"?
+## Where's the difference to a "Container"?
"Container" is a very vague term, after all it is used for
systemd-nspawn/LXC-type OS containers, for Docker/rkt-like micro service
@@ -74,7 +74,7 @@ Note that portable services are only available for system services, not for
user services. i.e. the functionality cannot be used for the stuff
bubblewrap/flatpak is focusing on.
-# Mode of Operation
+## Mode of Operation
If you have portable service image, maybe in a raw disk image called
`foobar_0.7.23.raw`, then attaching the services to the host is as easy as:
@@ -98,16 +98,17 @@ This command does the following:
`foobar@.{service|socket|target|timer|path}` as well as
`foobar.*.{service|socket|target|timer|path}` and
`foobar.{service|socket|target|timer|path}` are copied out. These unit files
- are placed in `/etc/systemd/system/` like regular unit files. Within the
- images the unit files are looked for at the usual locations, i.e. in
- `/usr/lib/systemd/system/` and `/etc/systemd/system/` and so on, relative to
- the image's root.
+ are placed in `/etc/systemd/system.attached/` (which is part of the normal
+ unit file search path of PID 1, and thus loaded exactly like regular unit
+ files). Within the images the unit files are looked for at the usual
+ locations, i.e. in `/usr/lib/systemd/system/` and `/etc/systemd/system/` and
+ so on, relative to the image's root.
3. For each such unit file a drop-in file is created. Let's say
`foobar-waldo.service` was one of the unit files copied to
- `/etc/systemd/system/`, then a drop-in file
- `/etc/systemd/system/foobar-waldo.service.d/20-portable.conf` is created,
- containing a few lines of additional configuration:
+ `/etc/systemd/system.attached/`, then a drop-in file
+ `/etc/systemd/system.attached/foobar-waldo.service.d/20-portable.conf` is
+ created, containing a few lines of additional configuration:
```
[Service]
@@ -140,7 +141,7 @@ Note that `portable attach` won't enable or start any of the units it copies
out. This still has to take place in a second, separate step. (That said We
might add options to do this automatically later on.).
-# Requirements on Images
+## Requirements on Images
Note that portable services don't introduce any new image format, but most OS
images should just work the way they are. Specifically, the following
@@ -207,14 +208,14 @@ image. To facility 3 and 4 you also need to include a boot loader in the
image. As mentioned `mkosi -b` takes care of all of that for you, but any other
image generator should work too.
-# Execution Environment
+## Execution Environment
Note that the code in portable service images is run exactly like regular
services. Hence there's no new execution environment to consider. Oh, unlike
Docker would do it, as these are regular system services they aren't run as PID
1 either, but with regular PID values.
-# Access to host resources
+## Access to host resources
If services shipped with this mechanism shall be able to access host resources
(such as files or AF_UNIX sockets for IPC), use the normal `BindPaths=` and
@@ -223,7 +224,7 @@ If services shipped with this mechanism shall be able to access host resources
`/etc/resolv.conf`, the D-Bus system bus socket or write access to the logging
subsystem are available to the service.
-# Instantiation
+## Instantiation
Sometimes it makes sense to instantiate the same set of services multiple
times. The portable service concept does not introduce a new logic for this. It
@@ -241,7 +242,7 @@ simple as:
The benefit of this approach is that templating works exactly the same for
units shipped with the OS itself as for attached portable services.
-# Immutable images with local data
+## Immutable images with local data
It's a good idea to keep portable service images read-only during normal
operation. In fact all but the `trusted` profile will default to this kind of
diff --git a/docs/PREDICTABLE_INTERFACE_NAMES.md b/docs/PREDICTABLE_INTERFACE_NAMES.md
new file mode 100644
index 0000000000..73d60477cd
--- /dev/null
+++ b/docs/PREDICTABLE_INTERFACE_NAMES.md
@@ -0,0 +1,64 @@
+# Predictable Network Interface Names
+
+Starting with v197 systemd/udev will automatically assign predictable, stable network interface names for all local Ethernet, WLAN and WWAN interfaces. This is a departure from the traditional interface naming scheme ("eth0", "eth1", "wlan0", ...), but should fix real problems.
+
+
+## Why?
+
+The classic naming scheme for network interfaces applied by the kernel is to simply assign names beginning with "eth0", "eth1", ... to all interfaces as they are probed by the drivers. As the driver probing is generally not predictable for modern technology this means that as soon as multiple network interfaces are available the assignment of the names "eth0", "eth1" and so on is generally not fixed anymore and it might very well happen that "eth0" on one boot ends up being "eth1" on the next. This can have serious security implications, for example in firewall rules which are coded for certain naming schemes, and which are hence very sensitive to unpredictable changing names.
+
+To fix this problem multiple solutions have been proposed and implemented. For a longer time udev shipped support for assigning permanent "ethX" names to certain interfaces based on their MAC addresses. This turned out to have a multitude of problems, among them: this required a writable root directory which is generally not available; the statelessness of the system is lost as booting an OS image on a system will result in changed configuration of the image; on many systems MAC addresses are not actually fixed, such as on a lot of embedded hardware and particularly on all kinds of virtualization solutions. The biggest of all however is that the userspace components trying to assign the interface name raced against the kernel assigning new names from the same "ethX" namespace, a race condition with all kinds of weird effects, among them that assignment of names sometimes failed. As a result support for this has been removed from systemd/udev a while back.
+
+Another solution that has been implemented is "biosdevname" which tries to find fixed slot topology information in certain firmware interfaces and uses them to assign fixed names to interfaces which incorporate their physical location on the mainboard. In a way this naming scheme is similar to what is already done natively in udev for various device nodes via /dev/*/by-path/ symlinks. In many cases, biosdevname departs from the low-level kernel device identification schemes that udev generally uses for these symlinks, and instead invents its own enumeration schemes.
+
+Finally, many distributions support renaming interfaces to user-chosen names (think: "internet0", "dmz0", ...) keyed off their MAC addresses or physical locations as part of their networking scripts. This is a very good choice but does have the problem that it implies that the user is willing and capable of choosing and assigning these names.
+
+We believe it is a good default choice to generalize the scheme pioneered by "biosdevname". Assigning fixed names based on firmware/topology/location information has the big advantage that the names are fully automatic, fully predictable, that they stay fixed even if hardware is added or removed (i.e. no reenumeration takes place) and that broken hardware can be replaced seamlessly. That said, they admittedly are sometimes harder to read than the "eth0" or "wlan0" everybody is used to. Example: "enp5s0"
+
+
+## What precisely has changed in v197?
+
+With systemd 197 we have added native support for a number of different naming policies into systemd/udevd proper and made a scheme similar to biosdevname's (but generally more powerful, and closer to kernel-internal device identification schemes) the default. The following different naming schemes for network interfaces are now supported by udev natively:
+
+1. Names incorporating Firmware/BIOS provided index numbers for on-board devices (example: `eno1`)
+1. Names incorporating Firmware/BIOS provided PCI Express hotplug slot index numbers (example: `ens1`)
+1. Names incorporating physical/geographical location of the connector of the hardware (example: `enp2s0`)
+1. Names incorporating the interfaces's MAC address (example: `enx78e7d1ea46da`)
+1. Classic, unpredictable kernel-native ethX naming (example: `eth0`)
+
+By default, systemd v197 will now name interfaces following policy 1) if that information from the firmware is applicable and available, falling back to 2) if that information from the firmware is applicable and available, falling back to 3) if applicable, falling back to 5) in all other cases. Policy 4) is not used by default, but is available if the user chooses so.
+
+This combined policy is only applied as last resort. That means, if the system has biosdevname installed, it will take precedence. If the user has added udev rules which change the name of the kernel devices these will take precedence too. Also, any distribution specific naming schemes generally take precedence.
+
+
+## Come again, what good does this do?
+
+With this new scheme you now get:
+
+* Stable interface names across reboots
+* Stable interface names even when hardware is added or removed, i.e. no re-enumeration takes place (to the level the firmware permits this)
+* Stable interface names when kernels or drivers are updated/changed
+* Stable interface names even if you have to replace broken ethernet cards by new ones
+* The names are automatically determined without user configuration, they just work
+* The interface names are fully predictable, i.e. just by looking at lspci you can figure out what the interface is going to be called
+* Fully stateless operation, changing the hardware configuration will not result in changes in /etc
+* Compatibility with read-only root
+* The network interface naming now follows more closely the scheme used for aliasing block device nodes and other device nodes in /dev via symlinks
+* Applicability to both x86 and non-x86 machines
+* The same on all distributions that adopted systemd/udev
+* It's easy to opt out of the scheme (see below)
+
+Does this have any drawbacks? Yes, it does. Previously it was practically guaranteed that hosts equipped with a single ethernet card only had a single "eth0" interface. With this new scheme in place, an administrator now has to check first what the local interface name is before he can invoke commands on it where previously he had a good chance that "eth0" was the right name.
+
+
+## I don't like this, how do I disable this?
+
+You basically have three options:
+
+1. You disable the assignment of fixed names, so that the unpredictable kernel names are used again. For this, simply mask udev's .link file for the default policy: `ln -s /dev/null /etc/systemd/network/99-default.link`
+1. You create your own manual naming scheme, for example by naming your interfaces "internet0", "dmz0" or "lan0". For that create your own .link files in /etc/systemd/network/, that choose an explicit name or a better naming scheme for one, some, or all of your interfaces. See [[systemd.link(5)|http://www.freedesktop.org/software/systemd/man/systemd.link.html]] for more information.
+1. You pass the net.ifnames=0 on the kernel command line
+
+## How does the new naming scheme look like, precisely?
+
+That's documented in detail in a comment block [[the sources of the net_id built-in|https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-net_id.c#L20]]. Please refer to this in case you are wondering how to decode the new interface names.
diff --git a/.github/RELEASE.md b/docs/RELEASE.md
index 2807667a30..11794aae63 100644
--- a/.github/RELEASE.md
+++ b/docs/RELEASE.md
@@ -1,4 +1,4 @@
-# Steps to a successful release
+# Steps to a Successful Release
1. Add all items to NEWS
2. Update the contributors list in NEWS ("make git-contrib")
diff --git a/doc/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md
index ca9e8387b7..89a185b527 100644
--- a/doc/TRANSIENT-SETTINGS.md
+++ b/docs/TRANSIENT-SETTINGS.md
@@ -45,6 +45,8 @@ Most generic unit settings are available for transient units.
✓ StartLimitAction=ACTION
✓ FailureAction=
✓ SuccessAction=
+✓ FailureActionExitStatus=
+✓ SuccessActionExitStatus=
✓ AddRef=
✓ RebootArgument=STRING
✓ ConditionPathExists=
@@ -135,6 +137,8 @@ All execution-related settings are available for transient units.
✓ SyslogLevelPrefix=
✓ LogLevelMax=
✓ LogExtraFields=
+✓ LogRateLimitIntervalSec=
+✓ LogRateLimitBurst=
✓ SecureBits=
✓ CapabilityBoundingSet=
✓ AmbientCapabilities=
@@ -173,6 +177,7 @@ All execution-related settings are available for transient units.
✓ TemporaryFileSystem=
✓ PrivateTmp=
✓ PrivateDevices=
+✓ PrivateMounts=
✓ ProtectKernelTunables=
✓ ProtectKernelModules=
✓ ProtectControlGroups=
@@ -216,6 +221,7 @@ All cgroup/resource control settings are available for transient units
✓ StartupCPUShares=
✓ CPUQuota=
✓ MemoryAccounting=
+✓ MemoryMin=
✓ MemoryLow=
✓ MemoryHigh=
✓ MemoryMax=
@@ -254,6 +260,8 @@ All process killing settings are available for transient units:
✓ SendSIGHUP=
✓ KillMode=
✓ KillSignal=
+✓ FinalKillSignal=
+✓ WatchdogSignal=
```
## Service Unit Settings
@@ -276,7 +284,6 @@ Most service unit settings are available for transient units.
✓ WatchdogSec=
✓ Type=
✓ Restart=
-✓ PermissionsStartOnly=
✓ RootDirectoryStartOnly=
✓ RemainAfterExit=
✓ GuessMainPID=
diff --git a/docs/TRANSLATORS.md b/docs/TRANSLATORS.md
new file mode 100644
index 0000000000..9c45453083
--- /dev/null
+++ b/docs/TRANSLATORS.md
@@ -0,0 +1,74 @@
+# Notes for Translators
+
+systemd depends on the `gettext` package for multilingual support.
+
+You'll find the i18n files in the `po/` directory.
+
+The build system (meson/ninja) can be used to generate a template (`*.pot`),
+which can be used to create new translations.
+
+It can also merge the template into the existing translations (`*.po`), to pick
+up new strings in need of translation.
+
+Finally, it is able to compile the translations (to `*.gmo` files), so that
+they can be used by systemd software. (This step is also useful to confirm the
+syntax of the `*.po` files is correct.)
+
+## Creating a New Translation
+
+To create a translation to a language not yet available, start by creating the
+initial template:
+
+```
+$ ninja -C build/ systemd-pot
+```
+
+This will generate file `po/systemd.pot` in the source tree.
+
+Then simply copy it to a new <code><i>${lang_code}</i>.po</code> file, where
+<code><i>${lang_code}</i></code> is the two-letter code for a language
+(possibly followed by a two-letter uppercase country code), according to the
+ISO 639 standard.
+
+In short:
+
+<pre>
+$ cp po/systemd.pot po/<i>${lang_code}</i>.po
+</pre>
+
+Then edit the new <code>po/<i>${lang_code}</i>.po</code> file (for example,
+using the `poedit` GUI editor.)
+
+## Updating an Existing Translation
+
+Start by updating the `*.po` files from the latest template:
+
+```
+$ ninja -C build/ systemd-update-po
+```
+
+This will touch all the `*.po` files, so you'll want to pay attention when
+creating a git commit from this change, to only include the one translation
+you're actually updating.
+
+Edit the `*.po` file, looking for empty translations and translations marked as
+"fuzzy" (which means the merger found a similar message that needs to be
+reviewed as it's expected not to match exactly.)
+
+You can use any text editor to update the `*.po` files, but a good choice is
+the `poedit` editor, a graphical application specifically designed for this
+purpose.
+
+Once you're done, create a git commit for the update of the `po/*.po` file you
+touched. Remember to undo the changes to the other `*.po` files (for instance,
+using `git checkout -- po/` after you commit the changes you do want to keep.)
+
+# Recompiling Translations
+
+You can recompile the `*.po` files using the following command:
+
+```
+$ ninja -C build/ systemd-gmo
+```
+
+The resulting files will be saved in the `build/po/` directory.
diff --git a/doc/UIDS-GIDS.md b/docs/UIDS-GIDS.md
index 7755491319..c59fefc5cd 100644
--- a/doc/UIDS-GIDS.md
+++ b/docs/UIDS-GIDS.md
@@ -1,4 +1,4 @@
-# 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.
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000000..ee845eec80
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-primer
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000000..2140253674
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,20 @@
+# systemd Documentation
+
+* [Automatic Boot Assessment](https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT)
+* [Locking Block Device Access](https://systemd.io/BLOCK_DEVICE_LOCKING)
+* [The Boot Loader Interface](https://systemd.io/BOOT_LOADER_INTERFACE)
+* [The Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION)
+* [Control Group APIs and Delegation](https://systemd.io/CGROUP_DELEGATION)
+* [The systemd Community Conduct Guidelines](https://github.com/systemd/systemd/blob/master/docs/CODE_OF_CONDUCT.md)
+* [Code Quality Tools](https://systemd.io/CODE_QUALITY)
+* [Coding Style](https://systemd.io/CODING_STYLE)
+* [Contributing](https://github.com/systemd/systemd/blob/master/docs/CONTRIBUTING.md)
+* [Porting systemd To New Distributions](https://systemd.io/DISTRO_PORTING)
+* [Predictable Network Interface Names](https://systemd.io/PREDICTABLE_INTERFACE_NAMES)
+* [Known Environment Variables](https://systemd.io/ENVIRONMENT)
+* [Hacking on systemd](https://systemd.io/HACKING)
+* [Portable Services Introduction](https://systemd.io/PORTABLE_SERVICES)
+* [Steps to a Successful Release](https://systemd.io/RELEASE)
+* [What settings are currently available for transient units?](https://systemd.io/TRANSIENT-SETTINGS)
+* [Notes for Translators](https://systemd.io/TRANSLATORS)
+* [Users, Groups, UIDs and GIDs on `systemd` Systems](https://systemd.io/UIDS-GIDS)
diff --git a/doc/sysvinit/README.in b/docs/sysvinit/README.in
index de5d80d902..de5d80d902 100644
--- a/doc/sysvinit/README.in
+++ b/docs/sysvinit/README.in
diff --git a/doc/sysvinit/meson.build b/docs/sysvinit/meson.build
index fbac59ae49..fbac59ae49 100644
--- a/doc/sysvinit/meson.build
+++ b/docs/sysvinit/meson.build
diff --git a/doc/var-log/README.in b/docs/var-log/README.in
index 2e64fb196a..2e64fb196a 100644
--- a/doc/var-log/README.in
+++ b/docs/var-log/README.in
diff --git a/doc/var-log/meson.build b/docs/var-log/meson.build
index 0ddff20ce5..0ddff20ce5 100644
--- a/doc/var-log/meson.build
+++ b/docs/var-log/meson.build
diff --git a/factory/etc/nsswitch.conf b/factory/etc/nsswitch.conf
index 5f2984e77f..5470993e34 100644
--- a/factory/etc/nsswitch.conf
+++ b/factory/etc/nsswitch.conf
@@ -1,6 +1,15 @@
# This file is part of systemd.
-passwd: files
-shadow: files
-group: files
-hosts: files mymachines resolve myhostname
+passwd: compat mymachines systemd
+group: compat mymachines systemd
+shadow: compat
+
+hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname
+networks: files
+
+protocols: db files
+services: db files
+ethers: db files
+rpc: db files
+
+netgroup: nis
diff --git a/hwdb/20-OUI.hwdb b/hwdb/20-OUI.hwdb
index 1f24cd6016..c1f3e30e90 100644
--- a/hwdb/20-OUI.hwdb
+++ b/hwdb/20-OUI.hwdb
@@ -573,7 +573,7 @@ OUI:0000BC*
ID_OUI_FROM_DATABASE=Rockwell Automation
OUI:0000BD*
- ID_OUI_FROM_DATABASE=MITSUBISHI CABLE COMPANY
+ ID_OUI_FROM_DATABASE=Mitsubishi Cable Industries, Ltd. / Ryosei Systems
OUI:0000BE*
ID_OUI_FROM_DATABASE=THE NTI GROUP
@@ -2139,7 +2139,7 @@ OUI:0002C6*
ID_OUI_FROM_DATABASE=Data Track Technology PLC
OUI:0002C7*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:0002C8*
ID_OUI_FROM_DATABASE=Technocom Communications Technology (pte) Ltd
@@ -3747,7 +3747,7 @@ OUI:0004DE*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:0004DF*
- ID_OUI_FROM_DATABASE=Teracom Telematica Ltda.
+ ID_OUI_FROM_DATABASE=TERACOM TELEMATICA S.A
OUI:0004E0*
ID_OUI_FROM_DATABASE=Procket Networks
@@ -5349,13 +5349,13 @@ OUI:0006F4*
ID_OUI_FROM_DATABASE=Prime Electronics & Satellitics Inc.
OUI:0006F5*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:0006F6*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:0006F7*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:0006F8*
ID_OUI_FROM_DATABASE=The Boeing Company
@@ -5388,13 +5388,13 @@ OUI:000701*
ID_OUI_FROM_DATABASE=RACAL-DATACOM
OUI:000702*
- ID_OUI_FROM_DATABASE=Varian Medical Systems
+ ID_OUI_FROM_DATABASE=Varex Imaging
OUI:000703*
ID_OUI_FROM_DATABASE=CSEE Transport
OUI:000704*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:000705*
ID_OUI_FROM_DATABASE=Endress & Hauser GmbH & Co
@@ -7011,7 +7011,7 @@ OUI:000939*
ID_OUI_FROM_DATABASE=ShibaSoku Co.,Ltd.
OUI:00093A*
- ID_OUI_FROM_DATABASE=Molex
+ ID_OUI_FROM_DATABASE=Molex CMS
OUI:00093B*
ID_OUI_FROM_DATABASE=HYUNDAI NETWORKS INC.
@@ -8772,7 +8772,7 @@ OUI:000B85*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:000B86*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:000B87*
ID_OUI_FROM_DATABASE=American Reliance Inc.
@@ -10296,7 +10296,7 @@ OUI:000D81*
ID_OUI_FROM_DATABASE=Pepperl+Fuchs GmbH
OUI:000D82*
- ID_OUI_FROM_DATABASE=PHSNET SRLS
+ ID_OUI_FROM_DATABASE=PHSNET
OUI:000D83*
ID_OUI_FROM_DATABASE=Sanmina-SCI Hungary Ltd.
@@ -11094,7 +11094,7 @@ OUI:000E8B*
ID_OUI_FROM_DATABASE=Astarte Technology Co, Ltd.
OUI:000E8C*
- ID_OUI_FROM_DATABASE=Siemens AG A&D ET
+ ID_OUI_FROM_DATABASE=Siemens AG
OUI:000E8D*
ID_OUI_FROM_DATABASE=Systems in Progress Holding GmbH
@@ -14604,7 +14604,7 @@ OUI:00131D*
ID_OUI_FROM_DATABASE=Scanvaegt International A/S
OUI:00131E*
- ID_OUI_FROM_DATABASE=Peiker acustic GmbH & Co. KG
+ ID_OUI_FROM_DATABASE=peiker acustic GmbH
OUI:00131F*
ID_OUI_FROM_DATABASE=NxtPhase T&D, Corp.
@@ -15369,7 +15369,7 @@ OUI:00141C*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:00141D*
- ID_OUI_FROM_DATABASE=LTi DRIVES GmbH
+ ID_OUI_FROM_DATABASE=LTI-Motion GmbH
OUI:00141E*
ID_OUI_FROM_DATABASE=P.A. Semi, Inc.
@@ -17580,7 +17580,7 @@ OUI:0016FD*
ID_OUI_FROM_DATABASE=Jaty Electronics
OUI:0016FE*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:0016FF*
ID_OUI_FROM_DATABASE=Wamin Optocomm Mfg Corp
@@ -19008,7 +19008,7 @@ OUI:0018D9*
ID_OUI_FROM_DATABASE=Santosha Internatonal, Inc
OUI:0018DA*
- ID_OUI_FROM_DATABASE=AMBER wireless GmbH
+ ID_OUI_FROM_DATABASE=Würth Elektronik eiSos GmbH & Co. KG
OUI:0018DB*
ID_OUI_FROM_DATABASE=EPL Technology Ltd
@@ -19299,7 +19299,7 @@ OUI:00193A*
ID_OUI_FROM_DATABASE=OESOLUTIONS
OUI:00193B*
- ID_OUI_FROM_DATABASE=Wilibox Deliberant Group LLC
+ ID_OUI_FROM_DATABASE=LigoWave
OUI:00193C*
ID_OUI_FROM_DATABASE=HighPoint Technologies Incorporated
@@ -19701,7 +19701,7 @@ OUI:0019C0*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
OUI:0019C1*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:0019C2*
ID_OUI_FROM_DATABASE=Equustek Solutions, Inc.
@@ -19980,7 +19980,7 @@ OUI:001A1D*
ID_OUI_FROM_DATABASE=PChome Online Inc.
OUI:001A1E*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:001A1F*
ID_OUI_FROM_DATABASE=Coastal Environmental Systems
@@ -20079,7 +20079,7 @@ OUI:001A3E*
ID_OUI_FROM_DATABASE=Faster Technology LLC
OUI:001A3F*
- ID_OUI_FROM_DATABASE=intelbras
+ ID_OUI_FROM_DATABASE=Intelbras
OUI:001A40*
ID_OUI_FROM_DATABASE=A-FOUR TECH CO., LTD.
@@ -21312,7 +21312,7 @@ OUI:001BC5014*
ID_OUI_FROM_DATABASE=Private
OUI:001BC5015*
- ID_OUI_FROM_DATABASE=Private
+ ID_OUI_FROM_DATABASE=Corporate Systems Engineering
OUI:001BC5016*
ID_OUI_FROM_DATABASE=Energotechnica OOO NPP Ltd
@@ -22011,7 +22011,7 @@ OUI:001BFA*
ID_OUI_FROM_DATABASE=G.i.N. mbH
OUI:001BFB*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:001BFC*
ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
@@ -23745,7 +23745,7 @@ OUI:001E3C*
ID_OUI_FROM_DATABASE=Lyngbox Media AB
OUI:001E3D*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:001E3E*
ID_OUI_FROM_DATABASE=KMW Inc.
@@ -23898,7 +23898,7 @@ OUI:001E6F*
ID_OUI_FROM_DATABASE=Magna-Power Electronics, Inc.
OUI:001E70*
- ID_OUI_FROM_DATABASE=Cobham Defence Communications Ltd
+ ID_OUI_FROM_DATABASE=Cobham Antenna Systems
OUI:001E71*
ID_OUI_FROM_DATABASE=MIrcom Group of Companies
@@ -24114,7 +24114,7 @@ OUI:001EB7*
ID_OUI_FROM_DATABASE=TBTech, Co., Ltd.
OUI:001EB8*
- ID_OUI_FROM_DATABASE=Fortis, Inc.
+ ID_OUI_FROM_DATABASE=Aloys, Inc
OUI:001EB9*
ID_OUI_FROM_DATABASE=Sing Fai Technology Limited
@@ -26103,7 +26103,7 @@ OUI:00214E*
ID_OUI_FROM_DATABASE=GS Yuasa Power Supply Ltd.
OUI:00214F*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:002150*
ID_OUI_FROM_DATABASE=EYEVIEW ELECTRONICS
@@ -27420,7 +27420,7 @@ OUI:002305*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:002306*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:002307*
ID_OUI_FROM_DATABASE=FUTURE INNOVATION TECH CO.,LTD
@@ -27642,7 +27642,7 @@ OUI:00234F*
ID_OUI_FROM_DATABASE=Luminous Power Technologies Pvt. Ltd.
OUI:002350*
- ID_OUI_FROM_DATABASE=LynTec
+ ID_OUI_FROM_DATABASE=RDC, Inc. dba LynTec
OUI:002351*
ID_OUI_FROM_DATABASE=2Wire Inc
@@ -28320,7 +28320,7 @@ OUI:002432*
ID_OUI_FROM_DATABASE=Neostar Technology Co.,LTD
OUI:002433*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:002434*
ID_OUI_FROM_DATABASE=Lectrosonics, Inc.
@@ -28485,7 +28485,7 @@ OUI:00246B*
ID_OUI_FROM_DATABASE=Covia, Inc.
OUI:00246C*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:00246D*
ID_OUI_FROM_DATABASE=Weinzierl Engineering GmbH
@@ -29889,7 +29889,7 @@ OUI:002642*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
OUI:002643*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:002644*
ID_OUI_FROM_DATABASE=Thomson Telecom Belgium
@@ -30572,6 +30572,9 @@ OUI:0028F8*
OUI:002926*
ID_OUI_FROM_DATABASE=Applied Optoelectronics, Inc Taiwan Branch
+OUI:0029C2*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:002A10*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -30590,6 +30593,9 @@ OUI:002D76*
OUI:002EC7*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:002F5C*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:002FD9*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
@@ -31364,6 +31370,9 @@ OUI:0030FF*
OUI:003146*
ID_OUI_FROM_DATABASE=Juniper Networks
+OUI:003217*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:00323A*
ID_OUI_FROM_DATABASE=so-logic
@@ -32228,11 +32237,14 @@ OUI:00425A*
OUI:004268*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:004279*
+ ID_OUI_FROM_DATABASE=Sunitec Enterprise Co.,Ltd
+
OUI:0043FF*
ID_OUI_FROM_DATABASE=KETRON S.R.L.
OUI:004501*
- ID_OUI_FROM_DATABASE=Versus Technology, Inc.
+ ID_OUI_FROM_DATABASE=Midmark RTLS
OUI:00451D*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -32249,6 +32261,9 @@ OUI:004BF3*
OUI:004D32*
ID_OUI_FROM_DATABASE=Andon Health Co.,Ltd.
+OUI:004E35*
+ ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
+
OUI:005000*
ID_OUI_FROM_DATABASE=NEXO COMMUNICATIONS, INC.
@@ -32975,6 +32990,9 @@ OUI:0051ED*
OUI:005218*
ID_OUI_FROM_DATABASE=Wuxi Keboda Electron Co.Ltd
+OUI:0052C2*
+ ID_OUI_FROM_DATABASE=peiker acustic GmbH
+
OUI:00549F*
ID_OUI_FROM_DATABASE=Avaya Inc
@@ -33065,6 +33083,9 @@ OUI:005A13*
OUI:005A39*
ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD
+OUI:005B94*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:005BA1*
ID_OUI_FROM_DATABASE=shanghai huayuan chuangxin software CO., LTD.
@@ -33866,6 +33887,9 @@ OUI:0064A6*
OUI:00664B*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:006762*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:006B8E*
ID_OUI_FROM_DATABASE=Shanghai Feixun Communication Co.,Ltd.
@@ -33893,6 +33917,9 @@ OUI:006DFB*
OUI:006F64*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:006FF2*
+ ID_OUI_FROM_DATABASE=MITSUMI ELECTRIC CO.,LTD.
+
OUI:0070B0*
ID_OUI_FROM_DATABASE=M/A-COM INC. COMPANIES
@@ -33938,6 +33965,9 @@ OUI:007686*
OUI:0076B1*
ID_OUI_FROM_DATABASE=Somfy-Protect By Myfox SAS
+OUI:00778D*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:007888*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -33950,6 +33980,9 @@ OUI:0078CD*
OUI:007B18*
ID_OUI_FROM_DATABASE=SENTRY Co., LTD.
+OUI:007C2D*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:007DFA*
ID_OUI_FROM_DATABASE=Volkswagen Group of America
@@ -34751,6 +34784,9 @@ OUI:008701*
OUI:008731*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:008764*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:008865*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -36234,7 +36270,7 @@ OUI:00A0D4*
ID_OUI_FROM_DATABASE=RADIOLAN, INC.
OUI:00A0D5*
- ID_OUI_FROM_DATABASE=Sierra Wireless Inc
+ ID_OUI_FROM_DATABASE=Sierra Wireless
OUI:00A0D6*
ID_OUI_FROM_DATABASE=SBE, Inc.
@@ -36422,6 +36458,9 @@ OUI:00AA70*
OUI:00ACE0*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:00AD24*
+ ID_OUI_FROM_DATABASE=D-Link International
+
OUI:00AECD*
ID_OUI_FROM_DATABASE=Pensando Systems
@@ -36542,6 +36581,9 @@ OUI:00B0F0*
OUI:00B0F5*
ID_OUI_FROM_DATABASE=NetWorth Technologies, Inc.
+OUI:00B1E3*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:00B338*
ID_OUI_FROM_DATABASE=Kontron Asia Pacific Design Sdn. Bhd
@@ -36551,6 +36593,9 @@ OUI:00B342*
OUI:00B362*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:00B4F5*
+ ID_OUI_FROM_DATABASE=DongGuan Siyoto Electronics Co., Ltd
+
OUI:00B56D*
ID_OUI_FROM_DATABASE=David Electronics Co., LTD.
@@ -36560,15 +36605,24 @@ OUI:00B5D0*
OUI:00B5D6*
ID_OUI_FROM_DATABASE=Omnibit Inc.
+OUI:00B600*
+ ID_OUI_FROM_DATABASE=VOIM Co., Ltd.
+
OUI:00B670*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:00B69F*
ID_OUI_FROM_DATABASE=Latch
+OUI:00B771*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:00B78D*
ID_OUI_FROM_DATABASE=Nanjing Shining Electric Automation Co., Ltd
+OUI:00B8B3*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:00B8C2*
ID_OUI_FROM_DATABASE=Heights Telecom T ltd
@@ -37424,6 +37478,9 @@ OUI:00CAE5*
OUI:00CB00*
ID_OUI_FROM_DATABASE=Private
+OUI:00CB51*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
OUI:00CBB4*
ID_OUI_FROM_DATABASE=SHENZHEN ATEKO PHOTOELECTRICITY CO.,LTD
@@ -37584,7 +37641,7 @@ OUI:00D02C*
ID_OUI_FROM_DATABASE=CAMPBELL SCIENTIFIC, INC.
OUI:00D02D*
- ID_OUI_FROM_DATABASE=ADEMCO
+ ID_OUI_FROM_DATABASE=Resideo
OUI:00D02E*
ID_OUI_FROM_DATABASE=COMMUNICATION AUTOMATION CORP.
@@ -37689,7 +37746,7 @@ OUI:00D04F*
ID_OUI_FROM_DATABASE=BITRONICS, INC.
OUI:00D050*
- ID_OUI_FROM_DATABASE=ISKRATEL
+ ID_OUI_FROM_DATABASE=Iskratel d.o.o.
OUI:00D051*
ID_OUI_FROM_DATABASE=O2 MICRO, INC.
@@ -38228,9 +38285,15 @@ OUI:00D38D*
OUI:00D632*
ID_OUI_FROM_DATABASE=GE Energy
+OUI:00D6FE*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:00D78F*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:00D861*
+ ID_OUI_FROM_DATABASE=Micro-Star INTL CO., LTD.
+
OUI:00D9D1*
ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc.
@@ -39095,6 +39158,9 @@ OUI:00E6E8*
OUI:00E8AB*
ID_OUI_FROM_DATABASE=Meggitt Training Systems, Inc.
+OUI:00EABD*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:00EB2D*
ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
@@ -39104,6 +39170,9 @@ OUI:00EBD5*
OUI:00EC0A*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+OUI:00EEAB*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:00EEBD*
ID_OUI_FROM_DATABASE=HTC Corporation
@@ -39125,6 +39194,9 @@ OUI:00F403*
OUI:00F46F*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:00F48D*
+ ID_OUI_FROM_DATABASE=Liteon Technology Corporation
+
OUI:00F4B9*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -39164,6 +39236,9 @@ OUI:00FC8D*
OUI:00FCBA*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:00FD22*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:00FD45*
ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
@@ -39224,6 +39299,9 @@ OUI:0403D6*
OUI:0404EA*
ID_OUI_FROM_DATABASE=Valens Semiconductor Ltd.
+OUI:04072E*
+ ID_OUI_FROM_DATABASE=VTech Electronics Ltd.
+
OUI:040973*
ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
@@ -39281,6 +39359,9 @@ OUI:041E64*
OUI:041E7A*
ID_OUI_FROM_DATABASE=DSPWorks
+OUI:041EFA*
+ ID_OUI_FROM_DATABASE=BISSELL Homecare, Inc.
+
OUI:04209A*
ID_OUI_FROM_DATABASE=Panasonic Corporation AVC Networks Company
@@ -39320,6 +39401,9 @@ OUI:043110*
OUI:0432F4*
ID_OUI_FROM_DATABASE=Partron
+OUI:043385*
+ ID_OUI_FROM_DATABASE=Nanchang BlackShark Co.,Ltd.
+
OUI:043389*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -39449,6 +39533,9 @@ OUI:0469F8*
OUI:046B1B*
ID_OUI_FROM_DATABASE=SYSDINE Co., Ltd.
+OUI:046B25*
+ ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+
OUI:046C9D*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -39519,7 +39606,7 @@ OUI:0475F5*
ID_OUI_FROM_DATABASE=CSST
OUI:04766E*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:047863*
ID_OUI_FROM_DATABASE=Shanghai MXCHIP Information Technology Co., Ltd.
@@ -39566,6 +39653,12 @@ OUI:048C03*
OUI:048D38*
ID_OUI_FROM_DATABASE=Netcore Technology Inc.
+OUI:049162*
+ ID_OUI_FROM_DATABASE=Microchip Technology Inc.
+
+OUI:049226*
+ ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
+
OUI:0492EE*
ID_OUI_FROM_DATABASE=iway AG
@@ -39588,7 +39681,7 @@ OUI:049790*
ID_OUI_FROM_DATABASE=Lartech telecom LLC
OUI:0498F3*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:0499E6*
ID_OUI_FROM_DATABASE=Shenzhen Yoostar Technology Co., Ltd
@@ -39644,6 +39737,9 @@ OUI:04B648*
OUI:04BA36*
ID_OUI_FROM_DATABASE=Li Seng Technology Ltd
+OUI:04BA8D*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:04BBF9*
ID_OUI_FROM_DATABASE=Pavilion Data Systems Inc
@@ -39654,7 +39750,7 @@ OUI:04BD70*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:04BD88*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:04BF6D*
ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
@@ -39746,9 +39842,15 @@ OUI:04CB1D*
OUI:04CE14*
ID_OUI_FROM_DATABASE=Wilocity LTD.
+OUI:04CE7E*
+ ID_OUI_FROM_DATABASE=NXP France Semiconductors France
+
OUI:04CF25*
ID_OUI_FROM_DATABASE=MANYCOLORS, INC.
+OUI:04CF8C*
+ ID_OUI_FROM_DATABASE=XIAOMI Electronics,CO.,LTD
+
OUI:04D13A*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
@@ -39761,12 +39863,18 @@ OUI:04D3CF*
OUI:04D437*
ID_OUI_FROM_DATABASE=ZNV
+OUI:04D4C4*
+ ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
+
OUI:04D6AA*
ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
OUI:04D783*
ID_OUI_FROM_DATABASE=Y&H E&C Co.,LTD.
+OUI:04D7A5*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
OUI:04DAD2*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -39812,6 +39920,9 @@ OUI:04E536*
OUI:04E548*
ID_OUI_FROM_DATABASE=Cohda Wireless Pty Ltd
+OUI:04E598*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
OUI:04E662*
ID_OUI_FROM_DATABASE=Acroname Inc.
@@ -39821,6 +39932,9 @@ OUI:04E676*
OUI:04E9E5*
ID_OUI_FROM_DATABASE=PJRC.COM, LLC
+OUI:04EB40*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:04ECBB*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
@@ -39851,6 +39965,9 @@ OUI:04F8C2*
OUI:04F938*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:04F9D9*
+ ID_OUI_FROM_DATABASE=Speaker Electronic(Jiashan) Co.,Ltd
+
OUI:04FA3F*
ID_OUI_FROM_DATABASE=Opticore Inc.
@@ -40415,12 +40532,18 @@ OUI:082E5F*
OUI:08306B*
ID_OUI_FROM_DATABASE=Palo Alto Networks
+OUI:08351B*
+ ID_OUI_FROM_DATABASE=Shenzhen Jialihua Electronic Technology Co., Ltd
+
OUI:083571*
ID_OUI_FROM_DATABASE=CASwell INC.
OUI:0835B2*
ID_OUI_FROM_DATABASE=CoreEdge Networks Co., Ltd
+OUI:0836C9*
+ ID_OUI_FROM_DATABASE=NETGEAR
+
OUI:08373D*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -40532,6 +40655,9 @@ OUI:0868EA*
OUI:086A0A*
ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP
+OUI:086BD7*
+ ID_OUI_FROM_DATABASE=Silicon Laboratories
+
OUI:086D41*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -40577,6 +40703,9 @@ OUI:087CBE*
OUI:087D21*
ID_OUI_FROM_DATABASE=Altasec technology corporation
+OUI:087F98*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
OUI:088039*
ID_OUI_FROM_DATABASE=Cisco SPVTG
@@ -40628,9 +40757,15 @@ OUI:089734*
OUI:089758*
ID_OUI_FROM_DATABASE=Shenzhen Strong Rising Electronics Co.,Ltd DongGuan Subsidiary
+OUI:089798*
+ ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD.
+
OUI:089B4B*
ID_OUI_FROM_DATABASE=iKuai Networks
+OUI:089C86*
+ ID_OUI_FROM_DATABASE=Nokia Shanghai Bell Co. Ltd.)
+
OUI:089E01*
ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC.
@@ -40679,6 +40814,9 @@ OUI:08B7EC*
OUI:08BA22*
ID_OUI_FROM_DATABASE=Swaive Corporation
+OUI:08BA5F*
+ ID_OUI_FROM_DATABASE=Qingdao Hisense Electronics Co.,Ltd.
+
OUI:08BBCC*
ID_OUI_FROM_DATABASE=AK-NORD EDV VERTRIEBSGES. mbH
@@ -40784,6 +40922,9 @@ OUI:08EBED*
OUI:08ECA9*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:08ECF5*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:08ED020*
ID_OUI_FROM_DATABASE=D2SLink Systems
@@ -40844,6 +40985,9 @@ OUI:08EFAB*
OUI:08F1B7*
ID_OUI_FROM_DATABASE=Towerstream Corpration
+OUI:08F1EA*
+ ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
+
OUI:08F2F4*
ID_OUI_FROM_DATABASE=Net One Partners Co.,Ltd.
@@ -40887,7 +41031,7 @@ OUI:0C08B4*
ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
OUI:0C1105*
- ID_OUI_FROM_DATABASE=Ringslink (Xiamen) Network Communication Technologies Co., Ltd
+ ID_OUI_FROM_DATABASE=AKUVOX (XIAMEN) NETWORKS CO., LTD
OUI:0C1167*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -40916,6 +41060,9 @@ OUI:0C191F*
OUI:0C1A10*
ID_OUI_FROM_DATABASE=Acoustic Stream
+OUI:0C1C19*
+ ID_OUI_FROM_DATABASE=LONGCONN ELECTRONICS(SHENZHEN) CO.,LTD
+
OUI:0C1C20*
ID_OUI_FROM_DATABASE=Kakao Corp
@@ -40985,6 +41132,9 @@ OUI:0C3CCD*
OUI:0C3E9F*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:0C4101*
+ ID_OUI_FROM_DATABASE=Ruichi Auto Technology (Guangzhou) Co., Ltd.
+
OUI:0C413E*
ID_OUI_FROM_DATABASE=Microsoft Corporation
@@ -41162,6 +41312,9 @@ OUI:0C73EBE*
OUI:0C74C2*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:0C7512*
+ ID_OUI_FROM_DATABASE=Shenzhen Kunlun TongTai Technology Co.,Ltd.
+
OUI:0C7523*
ID_OUI_FROM_DATABASE=BEIJING GEHUA CATV NETWORK CO.,LTD
@@ -41252,6 +41405,9 @@ OUI:0C9301*
OUI:0C93FB*
ID_OUI_FROM_DATABASE=BNS Solutions
+OUI:0C9541*
+ ID_OUI_FROM_DATABASE=CHIPSEA TECHNOLOGIES (SHENZHEN) CORP.
+
OUI:0C96BF*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -41276,6 +41432,9 @@ OUI:0C9D92*
OUI:0C9E91*
ID_OUI_FROM_DATABASE=Sankosha Corporation
+OUI:0CA06C*
+ ID_OUI_FROM_DATABASE=Industrial Cyber Sensing Inc.
+
OUI:0CA138*
ID_OUI_FROM_DATABASE=Blinq Wireless Inc.
@@ -41315,6 +41474,9 @@ OUI:0CB34F*
OUI:0CB459*
ID_OUI_FROM_DATABASE=Marketech International Corp.
+OUI:0CB4A4*
+ ID_OUI_FROM_DATABASE=Xintai Automobile Intelligent Network Technology
+
OUI:0CB4EF*
ID_OUI_FROM_DATABASE=Digience Co.,Ltd.
@@ -41345,6 +41507,9 @@ OUI:0CBF15*
OUI:0CBF3F*
ID_OUI_FROM_DATABASE=Shenzhen Lencotion Technology Co.,Ltd
+OUI:0CBF74*
+ ID_OUI_FROM_DATABASE=Morse Micro
+
OUI:0CC0C0*
ID_OUI_FROM_DATABASE=MAGNETI MARELLI SISTEMAS ELECTRONICOS MEXICO
@@ -41399,6 +41564,9 @@ OUI:0CCEF6*
OUI:0CCFD1*
ID_OUI_FROM_DATABASE=SPRINGWAVE Co., Ltd
+OUI:0CD0F8*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:0CD292*
ID_OUI_FROM_DATABASE=Intel Corporate
@@ -41441,6 +41609,9 @@ OUI:0CDDEF*
OUI:0CDFA4*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:0CE041*
+ ID_OUI_FROM_DATABASE=iDruide
+
OUI:0CE0DC*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -41465,6 +41636,9 @@ OUI:0CE936*
OUI:0CEAC9*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:0CEC84*
+ ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp.
+
OUI:0CEEE6*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
@@ -41537,6 +41711,9 @@ OUI:0CF3EE*
OUI:0CF405*
ID_OUI_FROM_DATABASE=Beijing Signalway Technologies Co.,Ltd
+OUI:0CF475*
+ ID_OUI_FROM_DATABASE=Zliide Technologies ApS
+
OUI:0CF4D5*
ID_OUI_FROM_DATABASE=Ruckus Wireless
@@ -41558,6 +41735,51 @@ OUI:0CFD37*
OUI:0CFE45*
ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc.
+OUI:0CFE5D0*
+ ID_OUI_FROM_DATABASE=Chengdu Ledong Information & Technology Co., Ltd.
+
+OUI:0CFE5D1*
+ ID_OUI_FROM_DATABASE=Fender Musical Instrument
+
+OUI:0CFE5D2*
+ ID_OUI_FROM_DATABASE=Dspread International Co.,Limited
+
+OUI:0CFE5D3*
+ ID_OUI_FROM_DATABASE=Beijing WayClouds Technology Co., Ltd.
+
+OUI:0CFE5D4*
+ ID_OUI_FROM_DATABASE=Yantai Dongfang Wisdom Electic Co.,Ltd.
+
+OUI:0CFE5D5*
+ ID_OUI_FROM_DATABASE=SELECTRIC Nachrichten-Systeme GmbH
+
+OUI:0CFE5D6*
+ ID_OUI_FROM_DATABASE=Antailiye Technology Co.,Ltd
+
+OUI:0CFE5D7*
+ ID_OUI_FROM_DATABASE=Vermes Microdispensing GmbH
+
+OUI:0CFE5D8*
+ ID_OUI_FROM_DATABASE=CTK Contact Electronics co., Ltd.
+
+OUI:0CFE5D9*
+ ID_OUI_FROM_DATABASE=Celerway Communication AS
+
+OUI:0CFE5DA*
+ ID_OUI_FROM_DATABASE=Fujian Jieyu Computer Technology Co., Ltd.
+
+OUI:0CFE5DB*
+ ID_OUI_FROM_DATABASE=YINUO-LINK LIMITED
+
+OUI:0CFE5DC*
+ ID_OUI_FROM_DATABASE=Bepal Technology Co.,Ltd.
+
+OUI:0CFE5DD*
+ ID_OUI_FROM_DATABASE=Maksat Technologies P Ltd
+
+OUI:0CFE5DE*
+ ID_OUI_FROM_DATABASE=NEWGREEN TECH CO., LTD.
+
OUI:100000*
ID_OUI_FROM_DATABASE=Private
@@ -41645,6 +41867,9 @@ OUI:100BA9*
OUI:100C24*
ID_OUI_FROM_DATABASE=pomdevices, LLC
+OUI:100C6B*
+ ID_OUI_FROM_DATABASE=NETGEAR
+
OUI:100D2F*
ID_OUI_FROM_DATABASE=Online Security Pty. Ltd.
@@ -41678,6 +41903,9 @@ OUI:101248*
OUI:101250*
ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+OUI:1012B4*
+ ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+
OUI:101331*
ID_OUI_FROM_DATABASE=Technicolor
@@ -41714,6 +41942,9 @@ OUI:102831*
OUI:102AB3*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+OUI:102C6B*
+ ID_OUI_FROM_DATABASE=AMPAK Technology, Inc.
+
OUI:102C83*
ID_OUI_FROM_DATABASE=XIMEA
@@ -41726,6 +41957,9 @@ OUI:102EAF*
OUI:102F6B*
ID_OUI_FROM_DATABASE=Microsoft Corporation
+OUI:103025*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:103034*
ID_OUI_FROM_DATABASE=Cara Systems
@@ -41741,6 +41975,9 @@ OUI:103711*
OUI:103B59*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:103D0A*
+ ID_OUI_FROM_DATABASE=Hui Zhou Gaoshengda Technology Co.,LTD
+
OUI:103DEA*
ID_OUI_FROM_DATABASE=HFC Technology (Beijing) Ltd. Co.
@@ -41813,6 +42050,9 @@ OUI:105917*
OUI:105AF7*
ID_OUI_FROM_DATABASE=ADB Italia
+OUI:105BAD*
+ ID_OUI_FROM_DATABASE=Mega Well Limited
+
OUI:105C3B*
ID_OUI_FROM_DATABASE=Perma-Pipe, Inc.
@@ -41873,6 +42113,9 @@ OUI:107223*
OUI:10768A*
ID_OUI_FROM_DATABASE=EoCell
+OUI:107717*
+ ID_OUI_FROM_DATABASE=SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD
+
OUI:1077B0*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
@@ -41906,6 +42149,12 @@ OUI:107BEF*
OUI:107D1A*
ID_OUI_FROM_DATABASE=Dell Inc.
+OUI:1081B4*
+ ID_OUI_FROM_DATABASE=Hunan Greatwall Galaxy Science and Technology Co.,Ltd.
+
+OUI:108286*
+ ID_OUI_FROM_DATABASE=Luxshare Precision Industry Co.,Ltd
+
OUI:1083D2*
ID_OUI_FROM_DATABASE=Microseven Systems, LLC
@@ -41924,6 +42173,9 @@ OUI:108A1B*
OUI:108CCF*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:108EBA*
+ ID_OUI_FROM_DATABASE=Molekule
+
OUI:108EE0*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -41942,12 +42194,21 @@ OUI:10954B*
OUI:109836*
ID_OUI_FROM_DATABASE=Dell Inc.
+OUI:1098C3*
+ ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
+
OUI:109AB9*
ID_OUI_FROM_DATABASE=Tosibox Oy
OUI:109ADD*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:109C70*
+ ID_OUI_FROM_DATABASE=Prusa Research s.r.o.
+
+OUI:109E3A*
+ ID_OUI_FROM_DATABASE=Zhejiang Tmall Technology Co., Ltd.
+
OUI:109FA9*
ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc
@@ -41957,6 +42218,9 @@ OUI:10A13B*
OUI:10A24E*
ID_OUI_FROM_DATABASE=GOLD3LINK ELECTRONICS CO., LTD
+OUI:10A3B8*
+ ID_OUI_FROM_DATABASE=Iskratel d.o.o.
+
OUI:10A4B9*
ID_OUI_FROM_DATABASE=Baidu Online Network Technology (Beijing) Co., Ltd
@@ -41996,6 +42260,9 @@ OUI:10B713*
OUI:10B7F6*
ID_OUI_FROM_DATABASE=Plastoform Industries Ltd.
+OUI:10B9F7*
+ ID_OUI_FROM_DATABASE=Niko-Servodan
+
OUI:10B9FE*
ID_OUI_FROM_DATABASE=Lika srl
@@ -42020,6 +42287,9 @@ OUI:10C07C*
OUI:10C172*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:10C22F*
+ ID_OUI_FROM_DATABASE=China Entropy Co., Ltd.
+
OUI:10C25A*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
@@ -42032,6 +42302,9 @@ OUI:10C37B*
OUI:10C586*
ID_OUI_FROM_DATABASE=BIO SOUND LAB CO., LTD.
+OUI:10C595*
+ ID_OUI_FROM_DATABASE=Lenovo
+
OUI:10C60C*
ID_OUI_FROM_DATABASE=Domino UK Ltd
@@ -42047,6 +42320,9 @@ OUI:10C6FC*
OUI:10C73F*
ID_OUI_FROM_DATABASE=Midas Klark Teknik Ltd
+OUI:10C753*
+ ID_OUI_FROM_DATABASE=Qingdao Intelligent&Precise Electronics Co.,Ltd.
+
OUI:10CA81*
ID_OUI_FROM_DATABASE=PRECIA
@@ -42098,6 +42374,9 @@ OUI:10DEE4*
OUI:10DF8B*
ID_OUI_FROM_DATABASE=Shenzhen CareDear Communication Technology Co.,Ltd
+OUI:10DFFC*
+ ID_OUI_FROM_DATABASE=Siemens AG
+
OUI:10E2D5*
ID_OUI_FROM_DATABASE=Qi Hardware Inc.
@@ -42206,6 +42485,9 @@ OUI:140D4F*
OUI:14109F*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:141114*
+ ID_OUI_FROM_DATABASE=TECNO MOBILE LIMITED
+
OUI:141330*
ID_OUI_FROM_DATABASE=Anakreon UK LLP
@@ -42407,6 +42689,9 @@ OUI:144C1A*
OUI:144D67*
ID_OUI_FROM_DATABASE=Zioncom Electronics (Shenzhen) Ltd.
+OUI:144E2A*
+ ID_OUI_FROM_DATABASE=Ciena Corporation
+
OUI:144E34*
ID_OUI_FROM_DATABASE=Remote Solution
@@ -42414,7 +42699,7 @@ OUI:144F8A*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:144FD70*
- ID_OUI_FROM_DATABASE=annapurnalabs
+ ID_OUI_FROM_DATABASE=Annapurna labs
OUI:144FD71*
ID_OUI_FROM_DATABASE=Zehnder Group AG
@@ -42506,6 +42791,9 @@ OUI:14612F*
OUI:146308*
ID_OUI_FROM_DATABASE=JABIL CIRCUIT (SHANGHAI) LTD.
+OUI:1469A2*
+ ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+
OUI:146A0B*
ID_OUI_FROM_DATABASE=Cypress Electronics Limited
@@ -42533,6 +42821,9 @@ OUI:14780B*
OUI:1479F3*
ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
+OUI:147BAC*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:147DB3*
ID_OUI_FROM_DATABASE=JOA TELECOM.CO.,LTD
@@ -42596,6 +42887,9 @@ OUI:149B2F*
OUI:149D09*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:149D99*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:149ECF*
ID_OUI_FROM_DATABASE=Dell Inc.
@@ -42603,7 +42897,7 @@ OUI:149F3C*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:149FB6*
- ID_OUI_FROM_DATABASE=GUANGDONG GENIUS TECHNOLOGY CO.,LTD.
+ ID_OUI_FROM_DATABASE=GUANGDONG GENIUS TECHNOLOGY CO., LTD.
OUI:149FE8*
ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd.
@@ -42683,6 +42977,9 @@ OUI:14C126*
OUI:14C1FF*
ID_OUI_FROM_DATABASE=ShenZhen QianHai Comlan communication Co.,LTD
+OUI:14C213*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:14C21D*
ID_OUI_FROM_DATABASE=Sabtech Industries
@@ -42710,6 +43007,9 @@ OUI:14CF92*
OUI:14CFE2*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:14D00D*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:14D11F*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -42764,6 +43064,9 @@ OUI:14EDE4*
OUI:14EE9D*
ID_OUI_FROM_DATABASE=AirNav Systems LLC
+OUI:14EFCF*
+ ID_OUI_FROM_DATABASE=SCHREDER
+
OUI:14F0C5*
ID_OUI_FROM_DATABASE=Xtremio Ltd.
@@ -42797,12 +43100,18 @@ OUI:18017D*
OUI:1801E3*
ID_OUI_FROM_DATABASE=Bittium Wireless Ltd
+OUI:1801F1*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
OUI:180373*
ID_OUI_FROM_DATABASE=Dell Inc.
OUI:1803FA*
ID_OUI_FROM_DATABASE=IBT Interfaces
+OUI:1804ED*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:180675*
ID_OUI_FROM_DATABASE=Dilax Intelcom GmbH
@@ -42821,6 +43130,9 @@ OUI:180C77*
OUI:180CAC*
ID_OUI_FROM_DATABASE=CANON INC.
+OUI:180D2C*
+ ID_OUI_FROM_DATABASE=Intelbras
+
OUI:180F76*
ID_OUI_FROM_DATABASE=D-Link International
@@ -42857,6 +43169,9 @@ OUI:181DEA*
OUI:181E78*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+OUI:181E95*
+ ID_OUI_FROM_DATABASE=AuVerte
+
OUI:181EB0*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -42884,6 +43199,9 @@ OUI:182666*
OUI:182861*
ID_OUI_FROM_DATABASE=AirTies Wireless Networks
+OUI:182A44*
+ ID_OUI_FROM_DATABASE=HIROSE ELECTRONIC SYSTEM
+
OUI:182A7B*
ID_OUI_FROM_DATABASE=Nintendo Co., Ltd.
@@ -42926,6 +43244,9 @@ OUI:183825*
OUI:183864*
ID_OUI_FROM_DATABASE=CAP-TECH INTERNATIONAL CO., LTD.
+OUI:1838AE*
+ ID_OUI_FROM_DATABASE=CONSPIN SOLUTION
+
OUI:183919*
ID_OUI_FROM_DATABASE=Unicoi Systems
@@ -42971,6 +43292,12 @@ OUI:1848D8*
OUI:184A6F*
ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd
+OUI:184B0D*
+ ID_OUI_FROM_DATABASE=Ruckus Wireless
+
+OUI:184BDF*
+ ID_OUI_FROM_DATABASE=Caavo Inc
+
OUI:184C08*
ID_OUI_FROM_DATABASE=Rockwell Automation
@@ -43029,7 +43356,7 @@ OUI:1862E4*
ID_OUI_FROM_DATABASE=Texas Instruments
OUI:186472*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:186571*
ID_OUI_FROM_DATABASE=Top Victory Electronics (Taiwan) Co., Ltd.
@@ -43079,12 +43406,18 @@ OUI:18742E*
OUI:187532*
ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO., LTD
+OUI:1878D4*
+ ID_OUI_FROM_DATABASE=Verizon
+
OUI:1879A2*
ID_OUI_FROM_DATABASE=GMJ ELECTRIC LIMITED
OUI:187A93*
ID_OUI_FROM_DATABASE=AMICCOM Electronics Corporation
+OUI:187C0B*
+ ID_OUI_FROM_DATABASE=Ruckus Wireless
+
OUI:187C81*
ID_OUI_FROM_DATABASE=Valeo Vision Systems
@@ -43100,6 +43433,9 @@ OUI:1880CE*
OUI:1880F5*
ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd
+OUI:18810E*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:188219*
ID_OUI_FROM_DATABASE=Alibaba Cloud Computing Ltd.
@@ -43235,6 +43571,9 @@ OUI:18A3E8*
OUI:18A6F7*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+OUI:18A7F1*
+ ID_OUI_FROM_DATABASE=Qingdao Haier Technology Co.,Ltd
+
OUI:18A905*
ID_OUI_FROM_DATABASE=Hewlett Packard
@@ -43289,12 +43628,21 @@ OUI:18B79E*
OUI:18B81F*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:18B905*
+ ID_OUI_FROM_DATABASE=Hong Kong Bouffalo Lab Limited
+
+OUI:18BB26*
+ ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED
+
OUI:18BC5A*
ID_OUI_FROM_DATABASE=Zhejiang Tmall Technology Co., Ltd.
OUI:18BDAD*
ID_OUI_FROM_DATABASE=L-TECH CORPORATION
+OUI:18BE92*
+ ID_OUI_FROM_DATABASE=Delta Networks, Inc.
+
OUI:18C086*
ID_OUI_FROM_DATABASE=Broadcom
@@ -43361,6 +43709,9 @@ OUI:18DC56*
OUI:18DED7*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:18DFB4*
+ ID_OUI_FROM_DATABASE=BOSUNG POWERTEC CO.,LTD.
+
OUI:18E288*
ID_OUI_FROM_DATABASE=STT Condigi
@@ -43466,6 +43817,9 @@ OUI:1C11E1*
OUI:1C129D*
ID_OUI_FROM_DATABASE=IEEE PES PSRC/SUB
+OUI:1C12B0*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:1C1448*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
@@ -43565,12 +43919,21 @@ OUI:1C232C*
OUI:1C234F*
ID_OUI_FROM_DATABASE=EDMI Europe Ltd
+OUI:1C24CD*
+ ID_OUI_FROM_DATABASE=Askey Computer Corp.
+
+OUI:1C24EB*
+ ID_OUI_FROM_DATABASE=Burlywood
+
OUI:1C25E1*
ID_OUI_FROM_DATABASE=China Mobile IOT Company Limited
OUI:1C27DD*
ID_OUI_FROM_DATABASE=Datang Gohighsec(zhejiang)Information Technology Co.,Ltd.
+OUI:1C2E1B*
+ ID_OUI_FROM_DATABASE=Suzhou Tremenet Communication Technology Co., Ltd.
+
OUI:1C330E*
ID_OUI_FROM_DATABASE=PernixData
@@ -43601,6 +43964,9 @@ OUI:1C3A4F*
OUI:1C3ADE*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:1C3B8F*
+ ID_OUI_FROM_DATABASE=Selve GmbH & Co. KG
+
OUI:1C3DE7*
ID_OUI_FROM_DATABASE=Sigma Koki Co.,Ltd.
@@ -43616,6 +43982,9 @@ OUI:1C40E8*
OUI:1C4158*
ID_OUI_FROM_DATABASE=Gemalto M2M GmbH
+OUI:1C427D*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:1C43EC*
ID_OUI_FROM_DATABASE=JAPAN CIRCUIT CO.,LTD
@@ -43658,6 +44027,9 @@ OUI:1C5216*
OUI:1C52D6*
ID_OUI_FROM_DATABASE=FLAT DISPLAY TECHNOLOGY CORPORATION
+OUI:1C549E*
+ ID_OUI_FROM_DATABASE=Universal Electronics, Inc.
+
OUI:1C553A*
ID_OUI_FROM_DATABASE=QianGua Corp.
@@ -43667,6 +44039,9 @@ OUI:1C56FE*
OUI:1C57D8*
ID_OUI_FROM_DATABASE=Kraftway Corporation PLC
+OUI:1C599B*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:1C5A0B*
ID_OUI_FROM_DATABASE=Tegile Systems
@@ -44315,6 +44690,9 @@ OUI:1CF03E*
OUI:1CF061*
ID_OUI_FROM_DATABASE=SCAPS GmbH
+OUI:1CF29A*
+ ID_OUI_FROM_DATABASE=Google, Inc.
+
OUI:1CF4CA*
ID_OUI_FROM_DATABASE=Private
@@ -44327,6 +44705,51 @@ OUI:1CFA68*
OUI:1CFCBB*
ID_OUI_FROM_DATABASE=Realfiction ApS
+OUI:1CFD080*
+ ID_OUI_FROM_DATABASE=InSeat Solutions, LLC
+
+OUI:1CFD081*
+ ID_OUI_FROM_DATABASE=Shenzhen SEWO Technology Co.,Ltd.
+
+OUI:1CFD082*
+ ID_OUI_FROM_DATABASE=HiHi Ltd
+
+OUI:1CFD083*
+ ID_OUI_FROM_DATABASE=Umeox Innovations Co.,Ltd
+
+OUI:1CFD084*
+ ID_OUI_FROM_DATABASE=SABIK Offshore GmbH
+
+OUI:1CFD085*
+ ID_OUI_FROM_DATABASE=Beijing Hengxin Rainbow Information Technology Co.,Ltd
+
+OUI:1CFD086*
+ ID_OUI_FROM_DATABASE=A&B Technology
+
+OUI:1CFD087*
+ ID_OUI_FROM_DATABASE=sunweit industrial limited
+
+OUI:1CFD088*
+ ID_OUI_FROM_DATABASE=ShenZhen DeLippo Technology Co., LTD
+
+OUI:1CFD089*
+ ID_OUI_FROM_DATABASE=LABEL
+
+OUI:1CFD08A*
+ ID_OUI_FROM_DATABASE=Banmak Technogies Co.,Ltd
+
+OUI:1CFD08B*
+ ID_OUI_FROM_DATABASE=guangzhou huiqun intelligent technology co. LTD
+
+OUI:1CFD08C*
+ ID_OUI_FROM_DATABASE=Shanghai YottaTech Co Ltd (上海尧它科技有é™å…¬å¸ï¼‰
+
+OUI:1CFD08D*
+ ID_OUI_FROM_DATABASE=Tianjin Keyvia Electric Co.,Ltd
+
+OUI:1CFD08E*
+ ID_OUI_FROM_DATABASE=MESHBOX FOUNDATION PTE. LTD.
+
OUI:1CFEA7*
ID_OUI_FROM_DATABASE=IDentytech Solutins Ltd.
@@ -44357,6 +44780,9 @@ OUI:200BC7*
OUI:200CC8*
ID_OUI_FROM_DATABASE=NETGEAR
+OUI:200DB0*
+ ID_OUI_FROM_DATABASE=Shenzhen Four Seas Global Link Network Technology Co., Ltd.
+
OUI:200E95*
ID_OUI_FROM_DATABASE=IEC – TC9 WG43
@@ -44393,6 +44819,9 @@ OUI:201A06*
OUI:201D03*
ID_OUI_FROM_DATABASE=Elatec GmbH
+OUI:201F31*
+ ID_OUI_FROM_DATABASE=Inteno Broadband Technology AB
+
OUI:2021A5*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
@@ -44402,9 +44831,15 @@ OUI:202564*
OUI:202598*
ID_OUI_FROM_DATABASE=Teleview
+OUI:20283E*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:2028BC*
ID_OUI_FROM_DATABASE=Visionscape Co,. Ltd.
+OUI:202AC5*
+ ID_OUI_FROM_DATABASE=Petite-En
+
OUI:202BC1*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -44423,6 +44858,15 @@ OUI:202DF8*
OUI:2031EB*
ID_OUI_FROM_DATABASE=HDSN
+OUI:203233*
+ ID_OUI_FROM_DATABASE=SHENZHEN BILIAN ELECTRONIC CO.,LTD
+
+OUI:20326C*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
+OUI:2034FB*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
OUI:20365B*
ID_OUI_FROM_DATABASE=Megafone Limited
@@ -44481,7 +44925,7 @@ OUI:204AAA*
ID_OUI_FROM_DATABASE=Hanscan Spain S.A.
OUI:204C03*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:204C6D*
ID_OUI_FROM_DATABASE=Hugo Brennenstuhl Gmbh & Co. KG.
@@ -44603,6 +45047,9 @@ OUI:207852*
OUI:2078F0*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:207918*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:207C8F*
ID_OUI_FROM_DATABASE=Quanta Microsystems,Inc.
@@ -44696,6 +45143,12 @@ OUI:20AA4B*
OUI:20AB37*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:20AD56*
+ ID_OUI_FROM_DATABASE=Continental Automotive Systems Inc.
+
+OUI:20B001*
+ ID_OUI_FROM_DATABASE=Technicolor
+
OUI:20B0F7*
ID_OUI_FROM_DATABASE=Enclustra GmbH
@@ -44705,6 +45158,9 @@ OUI:20B399*
OUI:20B5C6*
ID_OUI_FROM_DATABASE=Mimosa Networks
+OUI:20B780*
+ ID_OUI_FROM_DATABASE=Toshiba Visual Solutions Corporation Co.,Ltd
+
OUI:20B7C0*
ID_OUI_FROM_DATABASE=OMICRON electronics GmbH
@@ -44951,6 +45407,9 @@ OUI:241B13*
OUI:241B44*
ID_OUI_FROM_DATABASE=Hangzhou Tuners Electronics Co., Ltd
+OUI:241B7A*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:241C04*
ID_OUI_FROM_DATABASE=SHENZHEN JEHE TECHNOLOGY DEVELOPMENT CO., LTD.
@@ -45011,6 +45470,9 @@ OUI:243A82*
OUI:243C20*
ID_OUI_FROM_DATABASE=Dynamode Group
+OUI:243F30*
+ ID_OUI_FROM_DATABASE=Oxygen Broadband s.a.
+
OUI:2442BC*
ID_OUI_FROM_DATABASE=Alinco,incorporated
@@ -45086,6 +45548,9 @@ OUI:244E7BE*
OUI:244F1D*
ID_OUI_FROM_DATABASE=iRule LLC
+OUI:2453BF*
+ ID_OUI_FROM_DATABASE=Enernet
+
OUI:245880*
ID_OUI_FROM_DATABASE=VIZEO
@@ -45167,9 +45632,15 @@ OUI:247703*
OUI:24792A*
ID_OUI_FROM_DATABASE=Ruckus Wireless
+OUI:2479F8*
+ ID_OUI_FROM_DATABASE=KUPSON spol. s r.o.
+
OUI:247C4C*
ID_OUI_FROM_DATABASE=Herman Miller
+OUI:247D4D*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:247E12*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -45191,6 +45662,9 @@ OUI:2481AA*
OUI:24828A*
ID_OUI_FROM_DATABASE=Prowave Technologies Ltd.
+OUI:248498*
+ ID_OUI_FROM_DATABASE=Beijing Jiaoda Microunion Tech.Co.,Ltd.
+
OUI:2486F4*
ID_OUI_FROM_DATABASE=Ctek, Inc.
@@ -45210,7 +45684,7 @@ OUI:24920E*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:2493CA*
- ID_OUI_FROM_DATABASE=Voxtronic Technology Computer-Systeme GmbH
+ ID_OUI_FROM_DATABASE=Voxtronic Austria
OUI:249442*
ID_OUI_FROM_DATABASE=OPEN ROAD SOLUTIONS , INC.
@@ -45302,6 +45776,9 @@ OUI:24BCF8*
OUI:24BE05*
ID_OUI_FROM_DATABASE=Hewlett Packard
+OUI:24BE18*
+ ID_OUI_FROM_DATABASE=DADOUTEK COMPANY LIMITED
+
OUI:24BF74*
ID_OUI_FROM_DATABASE=Private
@@ -45381,7 +45858,7 @@ OUI:24DBED*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:24DEC6*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:24DF6A*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -45536,6 +46013,9 @@ OUI:282246*
OUI:282373*
ID_OUI_FROM_DATABASE=Digita
+OUI:2823F5*
+ ID_OUI_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co., Ltd.
+
OUI:2824FF*
ID_OUI_FROM_DATABASE=Wistron Neweb Corporation
@@ -45689,6 +46169,9 @@ OUI:28385C*
OUI:2838CF*
ID_OUI_FROM_DATABASE=Gen2wave
+OUI:283926*
+ ID_OUI_FROM_DATABASE=CyberTAN Technology Inc.
+
OUI:28395E*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -45815,6 +46298,9 @@ OUI:2872C5*
OUI:2872F0*
ID_OUI_FROM_DATABASE=ATHENA
+OUI:2875D8*
+ ID_OUI_FROM_DATABASE=FUJIAN STAR-NET COMMUNICATION CO.,LTD
+
OUI:287610*
ID_OUI_FROM_DATABASE=IgniteNet
@@ -45836,6 +46322,9 @@ OUI:287CDB*
OUI:288023*
ID_OUI_FROM_DATABASE=Hewlett Packard
+OUI:288088*
+ ID_OUI_FROM_DATABASE=NETGEAR
+
OUI:2880A2*
ID_OUI_FROM_DATABASE=Novatel Wireless Solutions, Inc.
@@ -45900,7 +46389,7 @@ OUI:28A02B*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:28A183*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:28A186*
ID_OUI_FROM_DATABASE=enblink
@@ -45953,6 +46442,9 @@ OUI:28B3AB*
OUI:28B448*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:28B4FB*
+ ID_OUI_FROM_DATABASE=Sprocomm Technologies CO.,LTD.
+
OUI:28B9D9*
ID_OUI_FROM_DATABASE=Radisys Corporation
@@ -46103,6 +46595,12 @@ OUI:28E794*
OUI:28E7CF*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:28E98E*
+ ID_OUI_FROM_DATABASE=Mitsubishi Electric Corporation
+
+OUI:28EC9A*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:28ED58*
ID_OUI_FROM_DATABASE=JAG Jakob AG
@@ -46164,7 +46662,7 @@ OUI:28F5377*
ID_OUI_FROM_DATABASE=Shenzhen Modern Cowboy Technology Co.,Ltd.
OUI:28F5378*
- ID_OUI_FROM_DATABASE=1MORE, INC.
+ ID_OUI_FROM_DATABASE=1MORE
OUI:28F5379*
ID_OUI_FROM_DATABASE=Herbert Waldmann GmbH & Co. KG
@@ -46271,6 +46769,9 @@ OUI:2C00F7*
OUI:2C010B*
ID_OUI_FROM_DATABASE=NASCENT Technology, LLC - RemKon
+OUI:2C01B5*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:2C029F*
ID_OUI_FROM_DATABASE=3ALogics
@@ -46319,6 +46820,9 @@ OUI:2C1A31*
OUI:2C1BC8*
ID_OUI_FROM_DATABASE=Hunan Topview Network System CO.,LTD
+OUI:2C1CF6*
+ ID_OUI_FROM_DATABASE=Alien Green LLC
+
OUI:2C1DB8*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
@@ -46454,6 +46958,9 @@ OUI:2C28B7*
OUI:2C2997*
ID_OUI_FROM_DATABASE=Microsoft Corporation
+OUI:2C2BF9*
+ ID_OUI_FROM_DATABASE=LG Innotek
+
OUI:2C2D48*
ID_OUI_FROM_DATABASE=bct electronic GesmbH
@@ -46514,6 +47021,9 @@ OUI:2C3BFD*
OUI:2C3ECF*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:2C3F0B*
+ ID_OUI_FROM_DATABASE=Cisco Meraki
+
OUI:2C3F38*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -46619,6 +47129,9 @@ OUI:2C54CF*
OUI:2C553C*
ID_OUI_FROM_DATABASE=Gainspeed, Inc.
+OUI:2C557C*
+ ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd
+
OUI:2C55D3*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -46655,6 +47168,9 @@ OUI:2C5BB8*
OUI:2C5BE1*
ID_OUI_FROM_DATABASE=Centripetal Networks, Inc
+OUI:2C5D34*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:2C5D93*
ID_OUI_FROM_DATABASE=Ruckus Wireless
@@ -46664,6 +47180,9 @@ OUI:2C5FF3*
OUI:2C600C*
ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC.
+OUI:2C6104*
+ ID_OUI_FROM_DATABASE=SHENZHEN FENGLIAN TECHNOLOGY CO., LTD.
+
OUI:2C61F6*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -46754,12 +47273,18 @@ OUI:2C72C3*
OUI:2C7360*
ID_OUI_FROM_DATABASE=Earda Technologies co Ltd
+OUI:2C73A0*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:2C750F*
ID_OUI_FROM_DATABASE=Shanghai Dongzhou-Lawton Communication Technology Co. Ltd.
OUI:2C768A*
ID_OUI_FROM_DATABASE=Hewlett Packard
+OUI:2C79D7*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
OUI:2C7B5A*
ID_OUI_FROM_DATABASE=Milper Ltd
@@ -46832,6 +47357,9 @@ OUI:2C9EEC*
OUI:2C9EFC*
ID_OUI_FROM_DATABASE=CANON INC.
+OUI:2CA02F*
+ ID_OUI_FROM_DATABASE=Veroguard Systems Pty Ltd
+
OUI:2CA157*
ID_OUI_FROM_DATABASE=acromate, Inc.
@@ -46853,6 +47381,12 @@ OUI:2CA780*
OUI:2CA835*
ID_OUI_FROM_DATABASE=RIM
+OUI:2CA9F0*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
+OUI:2CAA8E*
+ ID_OUI_FROM_DATABASE=Wyze Labs Inc
+
OUI:2CAB00*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -46910,15 +47444,27 @@ OUI:2CBE97*
OUI:2CC260*
ID_OUI_FROM_DATABASE=Oracle Corporation
+OUI:2CC407*
+ ID_OUI_FROM_DATABASE=machineQ
+
OUI:2CC548*
ID_OUI_FROM_DATABASE=IAdea Corporation
OUI:2CC5D3*
ID_OUI_FROM_DATABASE=Ruckus Wireless
+OUI:2CCA0C*
+ ID_OUI_FROM_DATABASE=WITHUS PLANET
+
OUI:2CCC15*
ID_OUI_FROM_DATABASE=Nokia Corporation
+OUI:2CCC44*
+ ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc.
+
+OUI:2CCCE6*
+ ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd
+
OUI:2CCD27*
ID_OUI_FROM_DATABASE=Precor Inc
@@ -47036,6 +47582,9 @@ OUI:2CF0EE*
OUI:2CF203*
ID_OUI_FROM_DATABASE=EMKO ELEKTRONIK SAN VE TIC AS
+OUI:2CF432*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:2CF4C5*
ID_OUI_FROM_DATABASE=Avaya Inc
@@ -47114,6 +47663,51 @@ OUI:3009F9D*
OUI:3009F9E*
ID_OUI_FROM_DATABASE=ZhongLi HengFeng (Shenzhen) Technology co.,Ltd.
+OUI:300A600*
+ ID_OUI_FROM_DATABASE=KAZUtechnica Co.,Ltd.
+
+OUI:300A601*
+ ID_OUI_FROM_DATABASE=Beijing Ruiteng Zhongtian TECH Ltd.,Co
+
+OUI:300A602*
+ ID_OUI_FROM_DATABASE=Advanced Electronic Designs, Inc.
+
+OUI:300A603*
+ ID_OUI_FROM_DATABASE=Private
+
+OUI:300A604*
+ ID_OUI_FROM_DATABASE=AVIC JONHON OPTRONIC TECHNOLOGY CO., LTD.
+
+OUI:300A605*
+ ID_OUI_FROM_DATABASE=A9
+
+OUI:300A606*
+ ID_OUI_FROM_DATABASE=Realtime biometrics India pvt ltd
+
+OUI:300A607*
+ ID_OUI_FROM_DATABASE=Newtons4th Ltd
+
+OUI:300A608*
+ ID_OUI_FROM_DATABASE=Bronkhorst High-Tech BV
+
+OUI:300A609*
+ ID_OUI_FROM_DATABASE=WINTEK System Co., Ltd
+
+OUI:300A60A*
+ ID_OUI_FROM_DATABASE=Ampetronic Ltd
+
+OUI:300A60B*
+ ID_OUI_FROM_DATABASE=Giax GmbH
+
+OUI:300A60C*
+ ID_OUI_FROM_DATABASE=Thermo Process Instruments, LP
+
+OUI:300A60D*
+ ID_OUI_FROM_DATABASE=Sixth Energy Technologies Private Limited
+
+OUI:300A60E*
+ ID_OUI_FROM_DATABASE=Imageo s.r.o.
+
OUI:300AC5*
ID_OUI_FROM_DATABASE=Ruio telecommunication technologies Co., Limited
@@ -47141,6 +47735,9 @@ OUI:3010B3*
OUI:3010E4*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:301389*
+ ID_OUI_FROM_DATABASE=Siemens AG, Automations & Drives,
+
OUI:30142D*
ID_OUI_FROM_DATABASE=Piciorgros GmbH
@@ -47219,6 +47816,12 @@ OUI:302303*
OUI:302432*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:302478*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
+OUI:302952*
+ ID_OUI_FROM_DATABASE=Hillstone Networks Inc
+
OUI:3029BE*
ID_OUI_FROM_DATABASE=Shanghai MRDcom Co.,Ltd
@@ -47294,6 +47897,9 @@ OUI:30469A*
OUI:30493B*
ID_OUI_FROM_DATABASE=Nanjing Z-Com Wireless Co.,Ltd
+OUI:304A26*
+ ID_OUI_FROM_DATABASE=Shenzhen Trolink Technology CO, LTD
+
OUI:304B07*
ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
@@ -47303,6 +47909,9 @@ OUI:304C7E*
OUI:304EC3*
ID_OUI_FROM_DATABASE=Tianjin Techua Technology Co., Ltd.
+OUI:304F75*
+ ID_OUI_FROM_DATABASE=DASAN Network Solutions
+
OUI:3051F8*
ID_OUI_FROM_DATABASE=BYK-Gardner GmbH
@@ -47526,7 +48135,7 @@ OUI:30C01B*
ID_OUI_FROM_DATABASE=Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd
OUI:30C3D9*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:30C507*
ID_OUI_FROM_DATABASE=ECI Telecom Ltd.
@@ -47579,6 +48188,9 @@ OUI:30D9D9*
OUI:30DE86*
ID_OUI_FROM_DATABASE=Cedac Software S.r.l.
+OUI:30DF8D*
+ ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
+
OUI:30E090*
ID_OUI_FROM_DATABASE=Linctronix Ltd,
@@ -47588,6 +48200,9 @@ OUI:30E171*
OUI:30E37A*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:30E3D6*
+ ID_OUI_FROM_DATABASE=Spotify USA Inc.
+
OUI:30E48E*
ID_OUI_FROM_DATABASE=Vodafone UK
@@ -47600,6 +48215,9 @@ OUI:30EB1F*
OUI:30EB25*
ID_OUI_FROM_DATABASE=INTEK DIGITAL
+OUI:30EB5A*
+ ID_OUI_FROM_DATABASE=LANDIS + GYR
+
OUI:30EFD1*
ID_OUI_FROM_DATABASE=Alstom Strongwish (Shenzhen) Co., Ltd.
@@ -47709,7 +48327,7 @@ OUI:340286*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:34029B*
- ID_OUI_FROM_DATABASE=CloudBerry Technologies Private Limited
+ ID_OUI_FROM_DATABASE=Plexonics Technologies LImited
OUI:3403DE*
ID_OUI_FROM_DATABASE=Texas Instruments
@@ -47828,6 +48446,9 @@ OUI:341E6B*
OUI:341FE4*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:342003*
+ ID_OUI_FROM_DATABASE=Shenzhen Feitengyun Technology Co.,LTD
+
OUI:342109*
ID_OUI_FROM_DATABASE=Jensen Scandinavia AS
@@ -47948,6 +48569,12 @@ OUI:3440B5*
OUI:34415D*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:3441A8*
+ ID_OUI_FROM_DATABASE=ER-Telecom
+
+OUI:344262*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:34466F*
ID_OUI_FROM_DATABASE=HiTEM Engineering
@@ -48137,6 +48764,9 @@ OUI:348B75*
OUI:348F27*
ID_OUI_FROM_DATABASE=Ruckus Wireless
+OUI:349342*
+ ID_OUI_FROM_DATABASE=TTE Corporation
+
OUI:3495DB*
ID_OUI_FROM_DATABASE=Logitec Corporation
@@ -48203,6 +48833,9 @@ OUI:34A843*
OUI:34A84E*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:34A8EB*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:34AA8B*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -48288,7 +48921,7 @@ OUI:34C69A*
ID_OUI_FROM_DATABASE=Enecsys Ltd
OUI:34C731*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:34C803*
ID_OUI_FROM_DATABASE=Nokia Corporation
@@ -48377,6 +49010,15 @@ OUI:34D7B4*
OUI:34D954*
ID_OUI_FROM_DATABASE=WiBotic Inc.
+OUI:34DAB7*
+ ID_OUI_FROM_DATABASE=zte corporation
+
+OUI:34DAC1*
+ ID_OUI_FROM_DATABASE=SAE Technologies Development(Dongguan) Co., Ltd.
+
+OUI:34DB9C*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
OUI:34DBFD*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -48458,6 +49100,9 @@ OUI:34F64B*
OUI:34F6D2*
ID_OUI_FROM_DATABASE=Panasonic Taiwan Co.,Ltd.
+OUI:34F8E7*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:34F968*
ID_OUI_FROM_DATABASE=ATEK Products, LLC
@@ -48476,6 +49121,9 @@ OUI:34FCB9*
OUI:34FCEF*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+OUI:380025*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:380195*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -48512,6 +49160,9 @@ OUI:380A94*
OUI:380AAB*
ID_OUI_FROM_DATABASE=Formlabs
+OUI:380B3C*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:380B40*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -48569,6 +49220,9 @@ OUI:3820A8*
OUI:382187*
ID_OUI_FROM_DATABASE=Midea Group Co., Ltd.
+OUI:3821C7*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
OUI:38229D*
ID_OUI_FROM_DATABASE=ADB Broadband Italia
@@ -48605,6 +49259,9 @@ OUI:382DD1*
OUI:382DE8*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:3830F9*
+ ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+
OUI:3831AC*
ID_OUI_FROM_DATABASE=WEG
@@ -48662,6 +49319,9 @@ OUI:383A21E*
OUI:383BC8*
ID_OUI_FROM_DATABASE=2Wire Inc
+OUI:383C9C*
+ ID_OUI_FROM_DATABASE=Fujian Newland Payment Technology Co.,Ltd.
+
OUI:383F10*
ID_OUI_FROM_DATABASE=DBL Technology Ltd.
@@ -48707,6 +49367,9 @@ OUI:384FF0*
OUI:38521A*
ID_OUI_FROM_DATABASE=Nokia
+OUI:38539C*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:385610*
ID_OUI_FROM_DATABASE=CANDY HOUSE, Inc.
@@ -48819,7 +49482,7 @@ OUI:3873EAC*
ID_OUI_FROM_DATABASE=LG Electronics
OUI:3873EAD*
- ID_OUI_FROM_DATABASE=annapurnalabs
+ ID_OUI_FROM_DATABASE=Annapurna labs
OUI:3873EAE*
ID_OUI_FROM_DATABASE=Shenzhen Jixian Technology Co., Ltd.
@@ -48839,9 +49502,15 @@ OUI:387B47*
OUI:3880DF*
ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+OUI:3881D7*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:388345*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+OUI:38839A*
+ ID_OUI_FROM_DATABASE=SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.
+
OUI:388602*
ID_OUI_FROM_DATABASE=Flexoptix GmbH
@@ -48938,15 +49607,63 @@ OUI:38ADBE*
OUI:38AF29*
ID_OUI_FROM_DATABASE=Zhejiang Dahua Technology Co., Ltd.
+OUI:38AFD0*
+ ID_OUI_FROM_DATABASE=Private
+
OUI:38AFD7*
ID_OUI_FROM_DATABASE=FUJITSU LIMITED
OUI:38B12D*
ID_OUI_FROM_DATABASE=Sonotronic Nagel GmbH
+OUI:38B19E0*
+ ID_OUI_FROM_DATABASE=Triple Jump Medical
+
+OUI:38B19E1*
+ ID_OUI_FROM_DATABASE=Freedompro Srl
+
+OUI:38B19E2*
+ ID_OUI_FROM_DATABASE=HDANYWHERE
+
+OUI:38B19E3*
+ ID_OUI_FROM_DATABASE=AVO DEVELOPMENT LTD
+
+OUI:38B19E5*
+ ID_OUI_FROM_DATABASE=Star Electronics GmbH & CoKG
+
+OUI:38B19E6*
+ ID_OUI_FROM_DATABASE=Thrust Networks
+
+OUI:38B19E7*
+ ID_OUI_FROM_DATABASE=Beijing Memblaze Technology Co Ltd
+
+OUI:38B19E8*
+ ID_OUI_FROM_DATABASE=BoCo Inc.
+
+OUI:38B19E9*
+ ID_OUI_FROM_DATABASE=Doepke Schaltgeräte GmbH
+
+OUI:38B19EA*
+ ID_OUI_FROM_DATABASE=Aeroespacial Guosheng Technology Co., Ltd
+
+OUI:38B19EB*
+ ID_OUI_FROM_DATABASE=System Q Ltd
+
+OUI:38B19EC*
+ ID_OUI_FROM_DATABASE=Gesellschaft industrieller Technologien
+
+OUI:38B19ED*
+ ID_OUI_FROM_DATABASE=Dallas Delta Corporation
+
+OUI:38B19EE*
+ ID_OUI_FROM_DATABASE=ShenZhen ShuaiXian Electronic Equipment Co.Ltd
+
OUI:38B1DB*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:38B4D3*
+ ID_OUI_FROM_DATABASE=BSH Hausgeraete GmbH
+
OUI:38B54D*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -49026,7 +49743,10 @@ OUI:38BF33*
ID_OUI_FROM_DATABASE=NEC CASIO Mobile Communications
OUI:38C096*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
+
+OUI:38C2BA*
+ ID_OUI_FROM_DATABASE=CCTV NEOTECH
OUI:38C70A*
ID_OUI_FROM_DATABASE=WiFiSong
@@ -49073,6 +49793,9 @@ OUI:38D7CA*
OUI:38D82F*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:38D9A5*
+ ID_OUI_FROM_DATABASE=Mikotek Information Inc.
+
OUI:38DBBB*
ID_OUI_FROM_DATABASE=Sunbow Telecom Co., Ltd.
@@ -49088,6 +49811,9 @@ OUI:38E08E*
OUI:38E1AA*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:38E26E*
+ ID_OUI_FROM_DATABASE=ShenZhen Sweet Rain Electronics Co.,Ltd.
+
OUI:38E2DD*
ID_OUI_FROM_DATABASE=zte corporation
@@ -49157,6 +49883,9 @@ OUI:38F73D*
OUI:38F7B2*
ID_OUI_FROM_DATABASE=SEOJUN ELECTRIC
+OUI:38F85E*
+ ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
+
OUI:38F889*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -49166,6 +49895,9 @@ OUI:38F8B7*
OUI:38F8CA*
ID_OUI_FROM_DATABASE=OWIN Inc.
+OUI:38F9D3*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:38FACA*
ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd
@@ -49220,6 +49952,9 @@ OUI:38FEC5*
OUI:38FF36*
ID_OUI_FROM_DATABASE=Ruckus Wireless
+OUI:3C01EF*
+ ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
+
OUI:3C02B1*
ID_OUI_FROM_DATABASE=Creation Technologies LP
@@ -49316,6 +50051,9 @@ OUI:3C1E04*
OUI:3C1E13*
ID_OUI_FROM_DATABASE=HANGZHOU SUNRISE TECHNOLOGY CO., LTD
+OUI:3C20F6*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:3C24F00*
ID_OUI_FROM_DATABASE=SHENZHEN PINSIDA TECHNOLOGY CO.,LTD.
@@ -49370,9 +50108,15 @@ OUI:3C26D5*
OUI:3C2763*
ID_OUI_FROM_DATABASE=SLE quality engineering GmbH & Co. KG
+OUI:3C286D*
+ ID_OUI_FROM_DATABASE=Google, Inc.
+
OUI:3C2AF4*
ID_OUI_FROM_DATABASE=Brother Industries, LTD.
+OUI:3C2C30*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:3C2C94*
ID_OUI_FROM_DATABASE=æ­å·žå¾·æ¾œç§‘技有é™å…¬å¸ï¼ˆHangZhou Delan Technology Co.,Ltd)
@@ -49409,6 +50153,9 @@ OUI:3C363D*
OUI:3C36E4*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:3C3786*
+ ID_OUI_FROM_DATABASE=NETGEAR
+
OUI:3C3888*
ID_OUI_FROM_DATABASE=ConnectQuest, llc
@@ -49568,6 +50315,9 @@ OUI:3C5AB4*
OUI:3C5CC3*
ID_OUI_FROM_DATABASE=Shenzhen First Blue Chip Technology Ltd
+OUI:3C5CC4*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:3C5EC3*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -49595,6 +50345,51 @@ OUI:3C678C*
OUI:3C6816*
ID_OUI_FROM_DATABASE=VXi Corporation
+OUI:3C6A2C0*
+ ID_OUI_FROM_DATABASE=Rio Lago Technologies LLC
+
+OUI:3C6A2C1*
+ ID_OUI_FROM_DATABASE=Olibra LLC
+
+OUI:3C6A2C2*
+ ID_OUI_FROM_DATABASE=Bosch Automotive Products (Suzhou) Co., Ltd.
+
+OUI:3C6A2C3*
+ ID_OUI_FROM_DATABASE=figur8, Inc.
+
+OUI:3C6A2C4*
+ ID_OUI_FROM_DATABASE=XI'AN YEP TELECOM TECHNOLOGY CO.,LTD
+
+OUI:3C6A2C5*
+ ID_OUI_FROM_DATABASE=Qingdao iGuan Technology Co., Ltd.
+
+OUI:3C6A2C6*
+ ID_OUI_FROM_DATABASE=La Barrière Automatique
+
+OUI:3C6A2C7*
+ ID_OUI_FROM_DATABASE=Homegear GmbH
+
+OUI:3C6A2C8*
+ ID_OUI_FROM_DATABASE=TP Radio
+
+OUI:3C6A2C9*
+ ID_OUI_FROM_DATABASE=WICKS Co., Ltd.
+
+OUI:3C6A2CA*
+ ID_OUI_FROM_DATABASE=Metro
+
+OUI:3C6A2CB*
+ ID_OUI_FROM_DATABASE=Phytium Technology Co., Ltd.
+
+OUI:3C6A2CC*
+ ID_OUI_FROM_DATABASE=Eltov System
+
+OUI:3C6A2CD*
+ ID_OUI_FROM_DATABASE=Xiamen Smarttek CO., Ltd.
+
+OUI:3C6A2CE*
+ ID_OUI_FROM_DATABASE=Beijing Donghua Hongtai Polytron Technologies Inc
+
OUI:3C6A7D*
ID_OUI_FROM_DATABASE=Niigata Power Systems Co., Ltd.
@@ -49691,6 +50486,9 @@ OUI:3C8C40*
OUI:3C8CF8*
ID_OUI_FROM_DATABASE=TRENDnet, Inc.
+OUI:3C8D20*
+ ID_OUI_FROM_DATABASE=Google, Inc.
+
OUI:3C9066*
ID_OUI_FROM_DATABASE=SmartRG, Inc.
@@ -49703,6 +50501,9 @@ OUI:3C9157*
OUI:3C9174*
ID_OUI_FROM_DATABASE=ALONG COMMUNICATION TECHNOLOGY
+OUI:3C9180*
+ ID_OUI_FROM_DATABASE=Liteon Technology Corporation
+
OUI:3C92DC*
ID_OUI_FROM_DATABASE=Octopod Technology Co. Ltd.
@@ -49730,6 +50531,9 @@ OUI:3C99F7*
OUI:3C9A77*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+OUI:3C9BD6*
+ ID_OUI_FROM_DATABASE=Vizio, Inc
+
OUI:3C9F81*
ID_OUI_FROM_DATABASE=Shenzhen CATIC Bit Communications Technology Co.,Ltd
@@ -49922,6 +50726,9 @@ OUI:3CEAFB*
OUI:3CEF8C*
ID_OUI_FROM_DATABASE=Zhejiang Dahua Technology Co., Ltd.
+OUI:3CF011*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:3CF392*
ID_OUI_FROM_DATABASE=Virtualtek. Co. Ltd
@@ -50039,6 +50846,9 @@ OUI:4022ED*
OUI:4025C2*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:402619*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:40270B*
ID_OUI_FROM_DATABASE=Mobileeco Co., Ltd
@@ -50436,7 +51246,7 @@ OUI:40A8F0*
ID_OUI_FROM_DATABASE=Hewlett Packard
OUI:40A93F*
- ID_OUI_FROM_DATABASE=Private
+ ID_OUI_FROM_DATABASE=Pivotal Commware, Inc.
OUI:40AC8D*
ID_OUI_FROM_DATABASE=Data Management, Inc.
@@ -50444,12 +51254,18 @@ OUI:40AC8D*
OUI:40B034*
ID_OUI_FROM_DATABASE=Hewlett Packard
+OUI:40B076*
+ ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
+
OUI:40B0FA*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
OUI:40B2C8*
ID_OUI_FROM_DATABASE=Nortel Networks
+OUI:40B30E*
+ ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+
OUI:40B395*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -50486,6 +51302,9 @@ OUI:40B93C*
OUI:40BA61*
ID_OUI_FROM_DATABASE=ARIMA Communications Corp.
+OUI:40BC60*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:40BC73*
ID_OUI_FROM_DATABASE=Cronoplast S.L.
@@ -50567,11 +51386,14 @@ OUI:40D855*
OUI:40DC9D*
ID_OUI_FROM_DATABASE=HAJEN
+OUI:40DF02*
+ ID_OUI_FROM_DATABASE=LINE BIZ Plus
+
OUI:40E230*
ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
OUI:40E3D6*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:40E730*
ID_OUI_FROM_DATABASE=DEY Storage Systems, Inc.
@@ -50648,6 +51470,9 @@ OUI:40F14C*
OUI:40F201*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+OUI:40F21C*
+ ID_OUI_FROM_DATABASE=DASAN Zhone Solutions
+
OUI:40F2E9*
ID_OUI_FROM_DATABASE=IBM
@@ -50714,6 +51539,9 @@ OUI:40F4EC*
OUI:40F52E*
ID_OUI_FROM_DATABASE=Leica Microsystems (Schweiz) AG
+OUI:40F9D5*
+ ID_OUI_FROM_DATABASE=Tecore Networks
+
OUI:40FA7F*
ID_OUI_FROM_DATABASE=Preh Car Connect GmbH
@@ -50726,6 +51554,9 @@ OUI:40FE0D*
OUI:440010*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:440049*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:44032C*
ID_OUI_FROM_DATABASE=Intel Corporate
@@ -50735,6 +51566,9 @@ OUI:4403A7*
OUI:440444*
ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+OUI:44070B*
+ ID_OUI_FROM_DATABASE=Google, Inc.
+
OUI:4409B8*
ID_OUI_FROM_DATABASE=Salcomp (Shenzhen) CO., LTD.
@@ -50756,9 +51590,18 @@ OUI:441441*
OUI:44184F*
ID_OUI_FROM_DATABASE=Fitview
+OUI:4418FD*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:4419B6*
ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd.
+OUI:441AFA*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
+OUI:441C12*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
OUI:441CA8*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
@@ -50780,6 +51623,9 @@ OUI:4423AA*
OUI:4425BB*
ID_OUI_FROM_DATABASE=Bamboo Entertainment Corporation
+OUI:4428A3*
+ ID_OUI_FROM_DATABASE=Jiangsu fulian Communication Technology Co., Ltd.
+
OUI:442938*
ID_OUI_FROM_DATABASE=NietZsche enterprise Co.Ltd.
@@ -50834,18 +51680,27 @@ OUI:443839*
OUI:4439C4*
ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd.
+OUI:443C88*
+ ID_OUI_FROM_DATABASE=FICOSA MAROC INTERNATIONAL
+
OUI:443C9C*
ID_OUI_FROM_DATABASE=Pintsch Tiefenbach GmbH
OUI:443D21*
ID_OUI_FROM_DATABASE=Nuvolt
+OUI:443E07*
+ ID_OUI_FROM_DATABASE=Electrolux
+
OUI:443EB2*
ID_OUI_FROM_DATABASE=DEOTRON Co., LTD.
OUI:444450*
ID_OUI_FROM_DATABASE=OttoQ
+OUI:4447CC*
+ ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd.
+
OUI:444891*
ID_OUI_FROM_DATABASE=HDMI Licensing, LLC
@@ -50897,6 +51752,9 @@ OUI:445829*
OUI:44599F*
ID_OUI_FROM_DATABASE=Criticare Systems, Inc
+OUI:445D5E*
+ ID_OUI_FROM_DATABASE=SHENZHEN Coolkit Technology CO.,LTD
+
OUI:445ECD*
ID_OUI_FROM_DATABASE=Razer Inc
@@ -51101,6 +51959,9 @@ OUI:44AD19*
OUI:44ADD9*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:44B295*
+ ID_OUI_FROM_DATABASE=Sichuan AI-Link Technology Co., Ltd.
+
OUI:44B32D*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
@@ -51110,6 +51971,15 @@ OUI:44B382*
OUI:44B412*
ID_OUI_FROM_DATABASE=SIUS AG
+OUI:44B433*
+ ID_OUI_FROM_DATABASE=tide.co.,ltd
+
+OUI:44B462*
+ ID_OUI_FROM_DATABASE=Flextronics Tech.(Ind) Pvt Ltd
+
+OUI:44B994*
+ ID_OUI_FROM_DATABASE=Douglas Lighting Controls
+
OUI:44BA46*
ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD
@@ -51164,6 +52034,9 @@ OUI:44D244*
OUI:44D2CA*
ID_OUI_FROM_DATABASE=Anvia TV Oy
+OUI:44D3AD*
+ ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp.
+
OUI:44D3CA*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -51212,6 +52085,9 @@ OUI:44E4D9*
OUI:44E4EE*
ID_OUI_FROM_DATABASE=Wistron Neweb Corporation
+OUI:44E66E*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:44E8A5*
ID_OUI_FROM_DATABASE=Myreka Technologies Sdn. Bhd.
@@ -51224,6 +52100,9 @@ OUI:44EA4B*
OUI:44EAD8*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:44ECCE*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
OUI:44ED57*
ID_OUI_FROM_DATABASE=Longicorn, inc.
@@ -51257,6 +52136,9 @@ OUI:44FB42*
OUI:44FDA3*
ID_OUI_FROM_DATABASE=Everysight LTD.
+OUI:44FE3B*
+ ID_OUI_FROM_DATABASE=Arcadyan Corporation
+
OUI:44FFBA*
ID_OUI_FROM_DATABASE=zte corporation
@@ -51272,6 +52154,9 @@ OUI:48022A*
OUI:480362*
ID_OUI_FROM_DATABASE=DESAY ELECTRONICS(HUIZHOU)CO.,LTD
+OUI:48049F*
+ ID_OUI_FROM_DATABASE=ELECOM CO., LTD
+
OUI:48066A*
ID_OUI_FROM_DATABASE=Tempered Networks, Inc.
@@ -51294,7 +52179,7 @@ OUI:480BB25*
ID_OUI_FROM_DATABASE=Solaredge LTD.
OUI:480BB26*
- ID_OUI_FROM_DATABASE=annapurnalabs
+ ID_OUI_FROM_DATABASE=Annapurna labs
OUI:480BB27*
ID_OUI_FROM_DATABASE=Beijing Dragon Resources Limited.
@@ -51383,6 +52268,9 @@ OUI:4833DD*
OUI:48343D*
ID_OUI_FROM_DATABASE=IEP GmbH
+OUI:48352E*
+ ID_OUI_FROM_DATABASE=Shenzhen Wolck Network Product Co.,LTD
+
OUI:48365F*
ID_OUI_FROM_DATABASE=Wintecronics Ltd.
@@ -51413,6 +52301,9 @@ OUI:4844F7*
OUI:484520*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:4846C1*
+ ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED
+
OUI:4846F1*
ID_OUI_FROM_DATABASE=Uros Oy
@@ -51422,6 +52313,9 @@ OUI:4846FB*
OUI:4849C7*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:484A30*
+ ID_OUI_FROM_DATABASE=George Robotics Limited
+
OUI:484AE9*
ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
@@ -51587,12 +52481,18 @@ OUI:488244*
OUI:4882F2*
ID_OUI_FROM_DATABASE=Appel Elektronik GmbH
+OUI:4883B4*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:4883C7*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
OUI:4886E8*
ID_OUI_FROM_DATABASE=Microsoft Corporation
+OUI:48872D*
+ ID_OUI_FROM_DATABASE=SHEN ZHEN DA XIA LONG QUE TECHNOLOGY CO.,LTD
+
OUI:488803*
ID_OUI_FROM_DATABASE=ManTechnology Inc.
@@ -51602,6 +52502,9 @@ OUI:48881E*
OUI:4888CA*
ID_OUI_FROM_DATABASE=Motorola (Wuhan) Mobility Technologies Communication Co., Ltd.
+OUI:4889E7*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:488AD2*
ID_OUI_FROM_DATABASE=MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.
@@ -51620,6 +52523,9 @@ OUI:489153*
OUI:4891F6*
ID_OUI_FROM_DATABASE=Shenzhen Reach software technology CO.,LTD
+OUI:489507*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:4898CA*
ID_OUI_FROM_DATABASE=Sichuan AI-Link Technology Co., Ltd.
@@ -51635,6 +52541,9 @@ OUI:489D18*
OUI:489D24*
ID_OUI_FROM_DATABASE=BlackBerry RTS
+OUI:489DD1*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:48A0F8*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
@@ -51653,6 +52562,12 @@ OUI:48A380*
OUI:48A472*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:48A493*
+ ID_OUI_FROM_DATABASE=TAIYO YUDEN CO.,LTD
+
+OUI:48A6B8*
+ ID_OUI_FROM_DATABASE=Sonos, Inc.
+
OUI:48A6D2*
ID_OUI_FROM_DATABASE=GJsun Optical Science and Tech Co.,Ltd.
@@ -51695,6 +52610,9 @@ OUI:48BA4E*
OUI:48BCA6*
ID_OUI_FROM_DATABASE=​ASUNG TECHNO CO.,Ltd
+OUI:48BD0E*
+ ID_OUI_FROM_DATABASE=Quanta Storage Inc.
+
OUI:48BD3D*
ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
@@ -51716,6 +52634,9 @@ OUI:48C093*
OUI:48C1AC*
ID_OUI_FROM_DATABASE=PLANTRONICS, INC.
+OUI:48C3B0*
+ ID_OUI_FROM_DATABASE=Pharos Co.Ltd
+
OUI:48C58D*
ID_OUI_FROM_DATABASE=Lear Corporation GmbH
@@ -51764,6 +52685,9 @@ OUI:48D705*
OUI:48D7FF*
ID_OUI_FROM_DATABASE=BLANKOM Antennentechnik GmbH
+OUI:48D845*
+ ID_OUI_FROM_DATABASE=Shenzhen Mainuoke Electronics Co., Ltd
+
OUI:48D855*
ID_OUI_FROM_DATABASE=Telvent
@@ -51794,6 +52718,15 @@ OUI:48E1AF*
OUI:48E244*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:48E3C3*
+ ID_OUI_FROM_DATABASE=JENOPTIK Advanced Systems GmbH
+
+OUI:48E695*
+ ID_OUI_FROM_DATABASE=Insigma Inc
+
+OUI:48E6C0*
+ ID_OUI_FROM_DATABASE=SIMCom Wireless Solutions Co.,Ltd.
+
OUI:48E9F1*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -51822,7 +52755,10 @@ OUI:48F027*
ID_OUI_FROM_DATABASE=Chengdu newifi Co.,Ltd
OUI:48F07B*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
+
+OUI:48F17F*
+ ID_OUI_FROM_DATABASE=Intel Corporate
OUI:48F230*
ID_OUI_FROM_DATABASE=Ubizcore Co.,LTD
@@ -51860,6 +52796,9 @@ OUI:48FCB8*
OUI:48FD8E*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:48FDA3*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
OUI:48FEEA*
ID_OUI_FROM_DATABASE=HOMA B.V.
@@ -51950,6 +52889,9 @@ OUI:4C1B86*
OUI:4C1FCC*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:4C218C*
+ ID_OUI_FROM_DATABASE=Panasonic India Private limited
+
OUI:4C21D0*
ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
@@ -52022,6 +52964,9 @@ OUI:4C49E3*
OUI:4C4B68*
ID_OUI_FROM_DATABASE=Mobile Device, Inc.
+OUI:4C4D66*
+ ID_OUI_FROM_DATABASE=Nanjing Jiahao Technology Co., Ltd.
+
OUI:4C4E03*
ID_OUI_FROM_DATABASE=TCT mobile ltd
@@ -52046,6 +52991,9 @@ OUI:4C55B8*
OUI:4C55CC*
ID_OUI_FROM_DATABASE=Zentri Pty Ltd
+OUI:4C569D*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:4C57CA*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -52121,6 +53069,9 @@ OUI:4C65A8E*
OUI:4C6641*
ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
+OUI:4C6AF6*
+ ID_OUI_FROM_DATABASE=HMD Global Oy
+
OUI:4C6E6E*
ID_OUI_FROM_DATABASE=Comnect Technology CO.,LTD
@@ -52202,9 +53153,39 @@ OUI:4C8FA5*
OUI:4C910C*
ID_OUI_FROM_DATABASE=Lanix Internacional, S.A. de C.V.
+OUI:4C917A0*
+ ID_OUI_FROM_DATABASE=Shenzhen Dangs Science & Technology CO.,LTD
+
+OUI:4C917A2*
+ ID_OUI_FROM_DATABASE=Chongqing Unisinsight Technology Co.,Ltd.
+
+OUI:4C917A3*
+ ID_OUI_FROM_DATABASE=Smart Access
+
+OUI:4C917A4*
+ ID_OUI_FROM_DATABASE=LumiGrow Inc.
+
+OUI:4C917A6*
+ ID_OUI_FROM_DATABASE=Openeye
+
+OUI:4C917A9*
+ ID_OUI_FROM_DATABASE=Hangzhou Hangtu Technology Co.,Ltd.
+
+OUI:4C917AA*
+ ID_OUI_FROM_DATABASE=Erlab DFS SAS
+
+OUI:4C917AB*
+ ID_OUI_FROM_DATABASE=AvertX
+
+OUI:4C917AE*
+ ID_OUI_FROM_DATABASE=Annapurna labs
+
OUI:4C9614*
ID_OUI_FROM_DATABASE=Juniper Networks
+OUI:4C962D*
+ ID_OUI_FROM_DATABASE=Fresh AB
+
OUI:4C98EF*
ID_OUI_FROM_DATABASE=Zeo
@@ -52253,6 +53234,9 @@ OUI:4CAE1C*
OUI:4CAE31*
ID_OUI_FROM_DATABASE=ShengHai Electronics (Shenzhen) Ltd
+OUI:4CAEA3*
+ ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
+
OUI:4CB008*
ID_OUI_FROM_DATABASE=Shenzhen Gwelltimes Technology Co.,Ltd
@@ -52298,6 +53282,51 @@ OUI:4CBB58*
OUI:4CBC42*
ID_OUI_FROM_DATABASE=Shenzhen Hangsheng Electronics Co.,Ltd.
+OUI:4CBC980*
+ ID_OUI_FROM_DATABASE=Charge-Amps AB
+
+OUI:4CBC981*
+ ID_OUI_FROM_DATABASE=JSC NIC
+
+OUI:4CBC982*
+ ID_OUI_FROM_DATABASE=Quake Global Inc
+
+OUI:4CBC983*
+ ID_OUI_FROM_DATABASE=Machine Max
+
+OUI:4CBC984*
+ ID_OUI_FROM_DATABASE=Nemon Co., Ltd.
+
+OUI:4CBC985*
+ ID_OUI_FROM_DATABASE=Gronic Systems GmbH
+
+OUI:4CBC986*
+ ID_OUI_FROM_DATABASE=Humanplus Intelligent Robotics Technology Co.,Ltd.
+
+OUI:4CBC987*
+ ID_OUI_FROM_DATABASE=Voegtlin Instruments GmbH
+
+OUI:4CBC988*
+ ID_OUI_FROM_DATABASE=Shenzhen Shanling Digital Technology Development Co.,Ltd.
+
+OUI:4CBC989*
+ ID_OUI_FROM_DATABASE=Airtex Manufacturing Partnership
+
+OUI:4CBC98A*
+ ID_OUI_FROM_DATABASE=Shenzhen Cogitation Technology Co.,Ltd.
+
+OUI:4CBC98B*
+ ID_OUI_FROM_DATABASE=Dongguan SmartAction Technology Co.,Ltd
+
+OUI:4CBC98C*
+ ID_OUI_FROM_DATABASE=Heliotis AG
+
+OUI:4CBC98D*
+ ID_OUI_FROM_DATABASE=Elink Technology (Shenzhen) Co., Limited
+
+OUI:4CBC98E*
+ ID_OUI_FROM_DATABASE=Wonder Workshop
+
OUI:4CBCA5*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -52319,6 +53348,12 @@ OUI:4CC602*
OUI:4CC681*
ID_OUI_FROM_DATABASE=Shenzhen Aisat Electronic Co., Ltd.
+OUI:4CC7D6*
+ ID_OUI_FROM_DATABASE=FLEXTRONICS MANUFACTURING(ZHUHAI)CO.,LTD.
+
+OUI:4CC8A1*
+ ID_OUI_FROM_DATABASE=Cisco Meraki
+
OUI:4CC94F*
ID_OUI_FROM_DATABASE=Nokia
@@ -52349,12 +53384,18 @@ OUI:4CD637*
OUI:4CD7B6*
ID_OUI_FROM_DATABASE=Helmer Scientific
+OUI:4CD98F*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:4CD9C4*
ID_OUI_FROM_DATABASE=Magneti Marelli Automotive Electronics (Guangzhou) Co. Ltd
OUI:4CDD31*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:4CDD7D*
+ ID_OUI_FROM_DATABASE=LHP Telematics LLC
+
OUI:4CDF3D*
ID_OUI_FROM_DATABASE=TEAM ENGINEERS ADVANCE TECHNOLOGIES INDIA PVT LTD
@@ -52403,12 +53444,18 @@ OUI:4CE173D*
OUI:4CE173E*
ID_OUI_FROM_DATABASE=Plus One Japan Limited
+OUI:4CE19E*
+ ID_OUI_FROM_DATABASE=TECNO MOBILE LIMITED
+
OUI:4CE1BB*
ID_OUI_FROM_DATABASE=Zhuhai HiFocus Technology Co., Ltd.
OUI:4CE2F1*
ID_OUI_FROM_DATABASE=sclak srl
+OUI:4CE5AE*
+ ID_OUI_FROM_DATABASE=Tianjin Beebox Intelligent Technology Co.,Ltd.
+
OUI:4CE676*
ID_OUI_FROM_DATABASE=BUFFALO.INC
@@ -52460,6 +53507,9 @@ OUI:4CFB45*
OUI:4CFF12*
ID_OUI_FROM_DATABASE=Fuze Entertainment Co., ltd
+OUI:500084*
+ ID_OUI_FROM_DATABASE=Siemens Canada
+
OUI:50008C*
ID_OUI_FROM_DATABASE=Hong Kong Telecommunications (HKT) Limited
@@ -52497,7 +53547,7 @@ OUI:500B911*
ID_OUI_FROM_DATABASE=SPD Development Company Ltd
OUI:500B912*
- ID_OUI_FROM_DATABASE=annapurnalabs
+ ID_OUI_FROM_DATABASE=Annapurna labs
OUI:500B913*
ID_OUI_FROM_DATABASE=EWIN TECHNOLOGY LIMITED
@@ -52595,6 +53645,9 @@ OUI:5027C7*
OUI:50294D*
ID_OUI_FROM_DATABASE=NANJING IOT SENSOR TECHNOLOGY CO,LTD
+OUI:5029F5*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:502A7E*
ID_OUI_FROM_DATABASE=Smart electronic GmbH
@@ -52604,6 +53657,9 @@ OUI:502A8B*
OUI:502B73*
ID_OUI_FROM_DATABASE=Tenda Technology Co.,Ltd.Dongguan branch
+OUI:502B98*
+ ID_OUI_FROM_DATABASE=Es-tech International
+
OUI:502D1D*
ID_OUI_FROM_DATABASE=Nokia Corporation
@@ -52658,6 +53714,9 @@ OUI:503DA1*
OUI:503DE5*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:503E7C*
+ ID_OUI_FROM_DATABASE=LeiShen Intelligent System Co.Ltd
+
OUI:503EAA*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
@@ -52745,6 +53804,9 @@ OUI:505BC2*
OUI:505DAC*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:505FB5*
+ ID_OUI_FROM_DATABASE=Askey Computer Corp.
+
OUI:506028*
ID_OUI_FROM_DATABASE=Xirrus Inc.
@@ -52823,6 +53885,9 @@ OUI:507691*
OUI:5076A6*
ID_OUI_FROM_DATABASE=Ecil Informatica Ind. Com. Ltda
+OUI:5076AF*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:507705*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -52871,6 +53936,9 @@ OUI:508C77*
OUI:508CB1*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:508CF5*
+ ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
+
OUI:508D6F*
ID_OUI_FROM_DATABASE=CHAHOO Limited
@@ -52913,6 +53981,9 @@ OUI:50A009*
OUI:50A054*
ID_OUI_FROM_DATABASE=Actineon
+OUI:50A0A4*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:50A0BF*
ID_OUI_FROM_DATABASE=Alba Fiber Systems Inc.
@@ -52991,6 +54062,12 @@ OUI:50AB3E*
OUI:50ABBF*
ID_OUI_FROM_DATABASE=Hoseo Telecom
+OUI:50AD71*
+ ID_OUI_FROM_DATABASE=Tessolve Semiconductor Private Limited
+
+OUI:50AD92*
+ ID_OUI_FROM_DATABASE=NX Technologies
+
OUI:50ADD5*
ID_OUI_FROM_DATABASE=Dynalec Corporation
@@ -53072,6 +54149,9 @@ OUI:50D753*
OUI:50DA00*
ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited
+OUI:50DB3F*
+ ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
+
OUI:50DCE7*
ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
@@ -53108,6 +54188,9 @@ OUI:50EAD6*
OUI:50EB1A*
ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+OUI:50EC50*
+ ID_OUI_FROM_DATABASE=Beijing Xiaomi Mobile Software Co., Ltd
+
OUI:50ED78*
ID_OUI_FROM_DATABASE=Changzhou Yongse Infotech Co.,Ltd
@@ -53225,6 +54308,9 @@ OUI:54055F*
OUI:540593*
ID_OUI_FROM_DATABASE=WOORI ELEC Co.,Ltd
+OUI:54068B*
+ ID_OUI_FROM_DATABASE=Ningbo Deli Kebei Technology Co.LTD
+
OUI:540955*
ID_OUI_FROM_DATABASE=zte corporation
@@ -53354,6 +54440,12 @@ OUI:544408*
OUI:54466B*
ID_OUI_FROM_DATABASE=Shenzhen CZTIC Electronic Technology Co., Ltd
+OUI:544741*
+ ID_OUI_FROM_DATABASE=XCHENG HOLDING
+
+OUI:5447D3*
+ ID_OUI_FROM_DATABASE=TSAT AS
+
OUI:544810*
ID_OUI_FROM_DATABASE=Dell Inc.
@@ -53411,6 +54503,9 @@ OUI:546172*
OUI:5461EA*
ID_OUI_FROM_DATABASE=Zaplox AB
+OUI:5462E2*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:5464D9*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
@@ -53424,7 +54519,7 @@ OUI:546751*
ID_OUI_FROM_DATABASE=Compal Broadband Networks, Inc.
OUI:546AD8*
- ID_OUI_FROM_DATABASE=Elster Water Metering Limited
+ ID_OUI_FROM_DATABASE=Elster Water Metering
OUI:546C0E*
ID_OUI_FROM_DATABASE=Texas Instruments
@@ -53471,12 +54566,18 @@ OUI:547FA8*
OUI:547FEE*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:548028*
+ ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
+
OUI:54812D*
ID_OUI_FROM_DATABASE=PAX Computer Technology(Shenzhen) Ltd.
OUI:5481AD*
ID_OUI_FROM_DATABASE=Eagle Research Corporation
+OUI:54833A*
+ ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
+
OUI:54847B*
ID_OUI_FROM_DATABASE=Digital Devices GmbH
@@ -53561,6 +54662,9 @@ OUI:549A4C*
OUI:549B12*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:549B72*
+ ID_OUI_FROM_DATABASE=Ericsson AB
+
OUI:549D85*
ID_OUI_FROM_DATABASE=EnerAccess inc
@@ -53570,6 +54674,9 @@ OUI:549F13*
OUI:549F35*
ID_OUI_FROM_DATABASE=Dell Inc.
+OUI:549FAE*
+ ID_OUI_FROM_DATABASE=iBASE Gaming Inc
+
OUI:54A04F*
ID_OUI_FROM_DATABASE=t-mac Technologies Ltd
@@ -53597,6 +54704,9 @@ OUI:54A619*
OUI:54A65C*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+OUI:54A703*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
OUI:54A9D4*
ID_OUI_FROM_DATABASE=Minibar Systems
@@ -54011,6 +55121,9 @@ OUI:5869F9*
OUI:586AB1*
ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited
+OUI:586B14*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:586D8F*
ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC
@@ -54122,6 +55235,9 @@ OUI:589B0B*
OUI:589CFC*
ID_OUI_FROM_DATABASE=FreeBSD Foundation
+OUI:589EC6*
+ ID_OUI_FROM_DATABASE=Gigaset Communications GmbH
+
OUI:58A0CB*
ID_OUI_FROM_DATABASE=TrackNet, Inc
@@ -54200,12 +55316,18 @@ OUI:58C583*
OUI:58C5CB*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:58C6F0*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:58C876*
ID_OUI_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co., Ltd.
OUI:58C935*
ID_OUI_FROM_DATABASE=Chiun Mai Communication Systems, Inc
+OUI:58CB52*
+ ID_OUI_FROM_DATABASE=Google, Inc.
+
OUI:58CF4B*
ID_OUI_FROM_DATABASE=Lufkin Industries
@@ -54227,6 +55349,9 @@ OUI:58D6D3*
OUI:58D759*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:58D9C3*
+ ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+
OUI:58D9D5*
ID_OUI_FROM_DATABASE=Tenda Technology Co.,Ltd.Dongguan branch
@@ -54257,6 +55382,9 @@ OUI:58E476*
OUI:58E636*
ID_OUI_FROM_DATABASE=EVRsafe Technologies
+OUI:58E6BA*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:58E747*
ID_OUI_FROM_DATABASE=Deltanet AG
@@ -54297,7 +55425,7 @@ OUI:58E876A*
ID_OUI_FROM_DATABASE=SHENZHEN DIGISSIN TECHNOLOGY
OUI:58E876B*
- ID_OUI_FROM_DATABASE=annapurnalabs
+ ID_OUI_FROM_DATABASE=Annapurna labs
OUI:58E876C*
ID_OUI_FROM_DATABASE=KUSTOM SIGNALS INC
@@ -54314,6 +55442,9 @@ OUI:58EB14*
OUI:58ECE1*
ID_OUI_FROM_DATABASE=Newport Corporation
+OUI:58ECED*
+ ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+
OUI:58EECE*
ID_OUI_FROM_DATABASE=Icon Time Systems
@@ -54530,6 +55661,9 @@ OUI:5C3C27*
OUI:5C4058*
ID_OUI_FROM_DATABASE=Jefferson Audio Video Systems, Inc.
+OUI:5C415A*
+ ID_OUI_FROM_DATABASE=Amazon.com, LLC
+
OUI:5C41E7*
ID_OUI_FROM_DATABASE=Wiatec International Ltd.
@@ -54668,6 +55802,9 @@ OUI:5C86C1*
OUI:5C8778*
ID_OUI_FROM_DATABASE=Cybertelbridge co.,ltd
+OUI:5C8816*
+ ID_OUI_FROM_DATABASE=Rockwell Automation
+
OUI:5C899A*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
@@ -54809,6 +55946,9 @@ OUI:5CCA1A*
OUI:5CCA32*
ID_OUI_FROM_DATABASE=Theben AG
+OUI:5CCBCA*
+ ID_OUI_FROM_DATABASE=FUJIAN STAR-NET COMMUNICATION CO.,LTD
+
OUI:5CCCA0*
ID_OUI_FROM_DATABASE=Gridwiz Inc.
@@ -55019,6 +56159,9 @@ OUI:600308*
OUI:600347*
ID_OUI_FROM_DATABASE=Billion Electric Co. Ltd.
+OUI:6003A6*
+ ID_OUI_FROM_DATABASE=Inteno Broadband Technology AB
+
OUI:600417*
ID_OUI_FROM_DATABASE=POSBANK CO.,LTD
@@ -55079,6 +56222,9 @@ OUI:601971*
OUI:601D0F*
ID_OUI_FROM_DATABASE=Midnite Solar
+OUI:601D91*
+ ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+
OUI:601E02*
ID_OUI_FROM_DATABASE=EltexAlatau
@@ -55134,7 +56280,7 @@ OUI:6036DD*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:60380E*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:6038E0*
ID_OUI_FROM_DATABASE=Belkin International Inc.
@@ -55217,6 +56363,9 @@ OUI:605F8D*
OUI:60601F*
ID_OUI_FROM_DATABASE=SZ DJI TECHNOLOGY CO.,LTD
+OUI:6061DF*
+ ID_OUI_FROM_DATABASE=Z-meta Research LLC
+
OUI:6063F9*
ID_OUI_FROM_DATABASE=Ciholas, Inc.
@@ -55256,6 +56405,9 @@ OUI:606D3C*
OUI:606DC7*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:606ED0*
+ ID_OUI_FROM_DATABASE=SEAL AG
+
OUI:60720B*
ID_OUI_FROM_DATABASE=BLU Products Inc
@@ -55310,6 +56462,9 @@ OUI:6089B7*
OUI:608C2B*
ID_OUI_FROM_DATABASE=Hanson Technology
+OUI:608C4A*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:608CE6*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
@@ -55373,6 +56528,9 @@ OUI:60A44C*
OUI:60A4D0*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:60A730*
+ ID_OUI_FROM_DATABASE=Shenzhen Yipinfang Internet Technology Co.,Ltd
+
OUI:60A8FE*
ID_OUI_FROM_DATABASE=Nokia
@@ -55460,6 +56618,9 @@ OUI:60CDA9*
OUI:60CDC5*
ID_OUI_FROM_DATABASE=Taiwan Carol Electronics., Ltd
+OUI:60CE92*
+ ID_OUI_FROM_DATABASE=The Refined Industry Company Limited
+
OUI:60D02C*
ID_OUI_FROM_DATABASE=Ruckus Wireless
@@ -55742,9 +56903,15 @@ OUI:642216*
OUI:642400*
ID_OUI_FROM_DATABASE=Xorcom Ltd.
+OUI:64255E*
+ ID_OUI_FROM_DATABASE=Observint Technologies, Inc.
+
OUI:642737*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:6429ED*
+ ID_OUI_FROM_DATABASE=AO PKK Milandr
+
OUI:642B8A*
ID_OUI_FROM_DATABASE=ALL BEST Industrial Co., Ltd.
@@ -55790,6 +56957,9 @@ OUI:644BC3*
OUI:644BF0*
ID_OUI_FROM_DATABASE=CalDigit, Inc
+OUI:644C36*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:644D70*
ID_OUI_FROM_DATABASE=dSPACE GmbH
@@ -55829,6 +56999,9 @@ OUI:6455B1*
OUI:645601*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+OUI:6458AD*
+ ID_OUI_FROM_DATABASE=China Mobile IOT Company Limited
+
OUI:6459F8*
ID_OUI_FROM_DATABASE=Vodafone Omnitel B.V.
@@ -55853,6 +57026,9 @@ OUI:645EBE*
OUI:645FFF*
ID_OUI_FROM_DATABASE=Nicolet Neuro
+OUI:646038*
+ ID_OUI_FROM_DATABASE=Hirschmann Automation and Control GmbH
+
OUI:646184*
ID_OUI_FROM_DATABASE=VELUX
@@ -55910,6 +57086,9 @@ OUI:647033*
OUI:6472D8*
ID_OUI_FROM_DATABASE=GooWi Technology Co.,Limited
+OUI:647366*
+ ID_OUI_FROM_DATABASE=Shenzhen Siera Technology Ltd
+
OUI:6473E2*
ID_OUI_FROM_DATABASE=Arbiter Systems, Inc.
@@ -55931,6 +57110,9 @@ OUI:647791*
OUI:6479A7*
ID_OUI_FROM_DATABASE=Phison Electronics Corp.
+OUI:647BCE*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:647BD4*
ID_OUI_FROM_DATABASE=Texas Instruments
@@ -55964,6 +57146,9 @@ OUI:6488FF*
OUI:64899A*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+OUI:6489F1*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:648D9E*
ID_OUI_FROM_DATABASE=IVT Electronic Co.,Ltd
@@ -55997,6 +57182,9 @@ OUI:649C81*
OUI:649C8E*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:649D99*
+ ID_OUI_FROM_DATABASE=FS COM INC
+
OUI:649EF3*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -56078,6 +57266,9 @@ OUI:64BC0C*
OUI:64BC11*
ID_OUI_FROM_DATABASE=CombiQ AB
+OUI:64C2DE*
+ ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+
OUI:64C354*
ID_OUI_FROM_DATABASE=Avaya Inc
@@ -56093,6 +57284,9 @@ OUI:64C667*
OUI:64C6AF*
ID_OUI_FROM_DATABASE=AXERRA Networks Ltd
+OUI:64C753*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:64C944*
ID_OUI_FROM_DATABASE=LARK Technologies, Inc
@@ -56121,7 +57315,7 @@ OUI:64D241*
ID_OUI_FROM_DATABASE=Keith & Koep GmbH
OUI:64D4BD*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:64D4DA*
ID_OUI_FROM_DATABASE=Intel Corporate
@@ -56201,6 +57395,9 @@ OUI:64ED57*
OUI:64ED62*
ID_OUI_FROM_DATABASE=WOORI SYSTEMS Co., Ltd
+OUI:64EEB7*
+ ID_OUI_FROM_DATABASE=Netcore Technology Inc
+
OUI:64F242*
ID_OUI_FROM_DATABASE=Gerdes Aktiengesellschaft
@@ -56210,6 +57407,9 @@ OUI:64F50E*
OUI:64F69D*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:64F81C*
+ ID_OUI_FROM_DATABASE=Huawei Technologies Co., Ltd.
+
OUI:64F88A*
ID_OUI_FROM_DATABASE=China Mobile IOT Company Limited
@@ -56351,6 +57551,9 @@ OUI:6828BA*
OUI:6828F6*
ID_OUI_FROM_DATABASE=Vubiq Networks, Inc.
+OUI:6829DC*
+ ID_OUI_FROM_DATABASE=Ficosa Electronics S.L.U.
+
OUI:682C7B*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -56393,6 +57596,9 @@ OUI:684352*
OUI:6843D7*
ID_OUI_FROM_DATABASE=Agilecom Photonics Solutions Guangdong Limited
+OUI:6845F1*
+ ID_OUI_FROM_DATABASE=TOSHIBA CLIENT SOLUTIONS CO., LTD.
+
OUI:684749*
ID_OUI_FROM_DATABASE=Texas Instruments
@@ -56405,6 +57611,9 @@ OUI:684B88*
OUI:684CA8*
ID_OUI_FROM_DATABASE=Shenzhen Herotel Tech. Co., Ltd.
+OUI:684F64*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:6851B7*
ID_OUI_FROM_DATABASE=PowerCloud Systems, Inc.
@@ -56534,9 +57743,15 @@ OUI:688AB5*
OUI:688AF0*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:688B0F*
+ ID_OUI_FROM_DATABASE=China Mobile IOT Company Limited
+
OUI:688DB6*
ID_OUI_FROM_DATABASE=AETEK INC.
+OUI:688F2E*
+ ID_OUI_FROM_DATABASE=Hitron Technologies. Inc
+
OUI:688F84*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -56609,6 +57824,9 @@ OUI:689861*
OUI:6899CD*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:689A87*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:689AB7*
ID_OUI_FROM_DATABASE=Atelier Vision Corporation
@@ -56627,6 +57845,9 @@ OUI:689E19*
OUI:689FF0*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:68A03E*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:68A0F6*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -56642,6 +57863,9 @@ OUI:68A3C4*
OUI:68A40E*
ID_OUI_FROM_DATABASE=BSH Hausgeräte GmbH
+OUI:68A47D*
+ ID_OUI_FROM_DATABASE=Sun Cupid Technology (HK) LTD
+
OUI:68A682*
ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd
@@ -56834,6 +58058,12 @@ OUI:68FEDA*
OUI:68FEF7*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:68FF7B*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
+OUI:6C006B*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:6C0273*
ID_OUI_FROM_DATABASE=Shenzhen Jin Yun Video Equipment Co., Ltd.
@@ -56912,9 +58142,15 @@ OUI:6C2995*
OUI:6C2ACB*
ID_OUI_FROM_DATABASE=Paxton Access Ltd
+OUI:6C2B59*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:6C2C06*
ID_OUI_FROM_DATABASE=OOO NPP Systemotechnika-NN
+OUI:6C2CDC*
+ ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd
+
OUI:6C2E33*
ID_OUI_FROM_DATABASE=Accelink Technologies Co.,Ltd.
@@ -56936,6 +58172,9 @@ OUI:6C33A9*
OUI:6C3838*
ID_OUI_FROM_DATABASE=Marking System Technology Co., Ltd.
+OUI:6C3845*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:6C38A1*
ID_OUI_FROM_DATABASE=Ubee Interactive Co., Limited
@@ -57020,6 +58259,51 @@ OUI:6C5AB5*
OUI:6C5C14*
ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+OUI:6C5C3D0*
+ ID_OUI_FROM_DATABASE=ShenZhen Hugsun Technology Co.,Ltd.
+
+OUI:6C5C3D1*
+ ID_OUI_FROM_DATABASE=Shenzhen Justek Technology Co., Ltd
+
+OUI:6C5C3D2*
+ ID_OUI_FROM_DATABASE=Vertiv Industrial Systems
+
+OUI:6C5C3D3*
+ ID_OUI_FROM_DATABASE=KWONG MING ELECTRICAL MANUFACTORY LIMITED
+
+OUI:6C5C3D4*
+ ID_OUI_FROM_DATABASE=HTI Co., LTD.
+
+OUI:6C5C3D5*
+ ID_OUI_FROM_DATABASE=Unitel Engineering
+
+OUI:6C5C3D6*
+ ID_OUI_FROM_DATABASE=Hangzhou Netease Yanxuan Trading Co.,Ltd
+
+OUI:6C5C3D7*
+ ID_OUI_FROM_DATABASE=SOUNDKING ELECTRONICS&SOUND CO., LTD.
+
+OUI:6C5C3D8*
+ ID_OUI_FROM_DATABASE=GUANGZHOU GUANGRI ELEVATOR INDUSTRY CO.,LTD
+
+OUI:6C5C3D9*
+ ID_OUI_FROM_DATABASE=IskraUralTEL
+
+OUI:6C5C3DA*
+ ID_OUI_FROM_DATABASE=krtkl inc.
+
+OUI:6C5C3DB*
+ ID_OUI_FROM_DATABASE=Reconova Technologies
+
+OUI:6C5C3DC*
+ ID_OUI_FROM_DATABASE=choyang powertech
+
+OUI:6C5C3DD*
+ ID_OUI_FROM_DATABASE=Syowatsusinkougyo Co.,Ltd.
+
+OUI:6C5C3DE*
+ ID_OUI_FROM_DATABASE=Clinton Electronics Corporation
+
OUI:6C5CDE*
ID_OUI_FROM_DATABASE=SunReports, Inc.
@@ -57137,6 +58421,9 @@ OUI:6C9AC9*
OUI:6C9B02*
ID_OUI_FROM_DATABASE=Nokia Corporation
+OUI:6C9BC0*
+ ID_OUI_FROM_DATABASE=Chemoptics Inc.
+
OUI:6C9CE9*
ID_OUI_FROM_DATABASE=Nimble Storage
@@ -57146,6 +58433,9 @@ OUI:6C9CED*
OUI:6CA100*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:6CA604*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:6CA682*
ID_OUI_FROM_DATABASE=EDAM information & communications
@@ -57167,6 +58457,9 @@ OUI:6CA858*
OUI:6CA906*
ID_OUI_FROM_DATABASE=Telefield Ltd
+OUI:6CA928*
+ ID_OUI_FROM_DATABASE=HMD Global Oy
+
OUI:6CA96F*
ID_OUI_FROM_DATABASE=TransPacket AS
@@ -57254,6 +58547,9 @@ OUI:6CC374*
OUI:6CC4D5*
ID_OUI_FROM_DATABASE=HMD Global Oy
+OUI:6CC7EC*
+ ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
+
OUI:6CCA08*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
@@ -57275,6 +58571,45 @@ OUI:6CDC6A*
OUI:6CDD30*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:6CDFFB0*
+ ID_OUI_FROM_DATABASE=Shenzhen HDCVT Technology
+
+OUI:6CDFFB1*
+ ID_OUI_FROM_DATABASE=Chongqing Baoli Yota Technologies Limited
+
+OUI:6CDFFB2*
+ ID_OUI_FROM_DATABASE=Sercomm Corporation.
+
+OUI:6CDFFB3*
+ ID_OUI_FROM_DATABASE=Beijing Ainemo Co Ltd
+
+OUI:6CDFFB4*
+ ID_OUI_FROM_DATABASE=Lineable Inc
+
+OUI:6CDFFB5*
+ ID_OUI_FROM_DATABASE=Greenbird Vertriebs GmbH
+
+OUI:6CDFFB7*
+ ID_OUI_FROM_DATABASE=Hashtrend AG
+
+OUI:6CDFFB8*
+ ID_OUI_FROM_DATABASE=Hardmeier
+
+OUI:6CDFFB9*
+ ID_OUI_FROM_DATABASE=YongTechs Electric Co. Ltd
+
+OUI:6CDFFBA*
+ ID_OUI_FROM_DATABASE=Guilin Zhishen Information TechonlogyCO.,Ltd
+
+OUI:6CDFFBB*
+ ID_OUI_FROM_DATABASE=CELL System Co.,Ltd.
+
+OUI:6CDFFBC*
+ ID_OUI_FROM_DATABASE=Toucan Systems Ltd
+
+OUI:6CDFFBD*
+ ID_OUI_FROM_DATABASE=Nanjing Buruike Electronics Technology Co., Ltd.
+
OUI:6CE01E*
ID_OUI_FROM_DATABASE=Modcam AB
@@ -57290,6 +58625,9 @@ OUI:6CE4CE*
OUI:6CE4DA*
ID_OUI_FROM_DATABASE=NEC Platforms, Ltd.
+OUI:6CE85C*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:6CE873*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
@@ -57324,7 +58662,7 @@ OUI:6CF373*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:6CF37F*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:6CF5E8*
ID_OUI_FROM_DATABASE=Mooredoll Inc.
@@ -57371,6 +58709,9 @@ OUI:7006AC*
OUI:700B01*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+OUI:700B4F*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:700BC0*
ID_OUI_FROM_DATABASE=Dewav Technology Company
@@ -57407,12 +58748,21 @@ OUI:70169F*
OUI:70188B*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:7018A7*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
+OUI:70192F*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:701A04*
ID_OUI_FROM_DATABASE=Liteon Technology Corporation
OUI:701AED*
ID_OUI_FROM_DATABASE=ADVAS CO., LTD.
+OUI:701BFB*
+ ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+
OUI:701CE7*
ID_OUI_FROM_DATABASE=Intel Corporate
@@ -57425,6 +58775,9 @@ OUI:701D7F*
OUI:701DC4*
ID_OUI_FROM_DATABASE=NorthStar Battery Company, LLC
+OUI:701E68*
+ ID_OUI_FROM_DATABASE=Hanna Instruments, Inc.
+
OUI:701F53*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -57470,6 +58823,9 @@ OUI:702DD1*
OUI:702E22*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:702ED9*
+ ID_OUI_FROM_DATABASE=Guangzhou Shiyuan Electronics Co., Ltd.
+
OUI:702F4B*
ID_OUI_FROM_DATABASE=PolyVision Inc.
@@ -57491,6 +58847,9 @@ OUI:703187*
OUI:7032D5*
ID_OUI_FROM_DATABASE=Athena Wireless Communications Inc
+OUI:703509*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:703811*
ID_OUI_FROM_DATABASE=Invensys Rail
@@ -57501,7 +58860,10 @@ OUI:7038EE*
ID_OUI_FROM_DATABASE=Avaya Inc
OUI:703A0E*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
+OUI:703A51*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
OUI:703A73*
ID_OUI_FROM_DATABASE=Shenzhen Sundray Technologies Company Limited
@@ -57518,6 +58880,9 @@ OUI:703C03*
OUI:703C39*
ID_OUI_FROM_DATABASE=SEAWING Kft
+OUI:703C69*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:703D15*
ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited
@@ -57590,6 +58955,9 @@ OUI:7055F8*
OUI:705681*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:7057BF*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
OUI:705812*
ID_OUI_FROM_DATABASE=Panasonic Corporation AVC Networks Company
@@ -57620,6 +58988,12 @@ OUI:705B2E*
OUI:705CAD*
ID_OUI_FROM_DATABASE=Konami Gaming Inc
+OUI:705DCC*
+ ID_OUI_FROM_DATABASE=EFM Networks
+
+OUI:705E55*
+ ID_OUI_FROM_DATABASE=Realme Chongqing MobileTelecommunications Corp Ltd
+
OUI:705EAA*
ID_OUI_FROM_DATABASE=Action Target, Inc.
@@ -57653,6 +59027,9 @@ OUI:70695A*
OUI:706BB9*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:706D15*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:706DEC*
ID_OUI_FROM_DATABASE=Wifi-soft LLC
@@ -57749,6 +59126,9 @@ OUI:70820E*
OUI:70828E*
ID_OUI_FROM_DATABASE=OleumTech Corporation
+OUI:708540*
+ ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd
+
OUI:7085C2*
ID_OUI_FROM_DATABASE=ASRock Incorporation
@@ -57872,6 +59252,9 @@ OUI:70A8E3*
OUI:70AAB2*
ID_OUI_FROM_DATABASE=BlackBerry RTS
+OUI:70ACD7*
+ ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd
+
OUI:70AD54*
ID_OUI_FROM_DATABASE=Malvern Instruments Ltd
@@ -57896,15 +59279,27 @@ OUI:70B14E*
OUI:70B265*
ID_OUI_FROM_DATABASE=Hiltron s.r.l.
+OUI:70B317*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:70B3D5001*
ID_OUI_FROM_DATABASE=SOREDI touch systems GmbH
OUI:70B3D5002*
ID_OUI_FROM_DATABASE=Gogo BA
+OUI:70B3D5003*
+ ID_OUI_FROM_DATABASE=ANYROAM
+
+OUI:70B3D5006*
+ ID_OUI_FROM_DATABASE=Piranha EMS Inc.
+
OUI:70B3D5007*
ID_OUI_FROM_DATABASE=SENSONEO
+OUI:70B3D5008*
+ ID_OUI_FROM_DATABASE=ESYSE GmbH Embedded Systems Engineering
+
OUI:70B3D5009*
ID_OUI_FROM_DATABASE=HolidayCoro
@@ -57926,6 +59321,9 @@ OUI:70B3D5011*
OUI:70B3D5012*
ID_OUI_FROM_DATABASE=KST technology
+OUI:70B3D5013*
+ ID_OUI_FROM_DATABASE=Sportsbeams Lighting, Inc.
+
OUI:70B3D5014*
ID_OUI_FROM_DATABASE=FRAKO Kondensatoren und Anlagenbau GmbH
@@ -57938,6 +59336,9 @@ OUI:70B3D5016*
OUI:70B3D501A*
ID_OUI_FROM_DATABASE=Cubro Acronet GesmbH
+OUI:70B3D501B*
+ ID_OUI_FROM_DATABASE=AUDI AG
+
OUI:70B3D501C*
ID_OUI_FROM_DATABASE=Kumu Networks
@@ -57950,6 +59351,9 @@ OUI:70B3D501E*
OUI:70B3D501F*
ID_OUI_FROM_DATABASE=SPX Flow Technology BV
+OUI:70B3D5020*
+ ID_OUI_FROM_DATABASE=MICRO DEBUG, Y.K.
+
OUI:70B3D5022*
ID_OUI_FROM_DATABASE=Ravelin Ltd
@@ -58034,6 +59438,9 @@ OUI:70B3D5044*
OUI:70B3D5046*
ID_OUI_FROM_DATABASE=Shenzhen Rihuida Electronics Co,. Ltd
+OUI:70B3D5048*
+ ID_OUI_FROM_DATABASE=AvMap srlu
+
OUI:70B3D5049*
ID_OUI_FROM_DATABASE=APP Engineering, Inc.
@@ -58070,6 +59477,9 @@ OUI:70B3D5059*
OUI:70B3D505A*
ID_OUI_FROM_DATABASE=Uni Control System Sp. z o. o.
+OUI:70B3D505C*
+ ID_OUI_FROM_DATABASE=Amber Kinetics Inc
+
OUI:70B3D505D*
ID_OUI_FROM_DATABASE=KOMS Co.,Ltd.
@@ -58106,6 +59516,9 @@ OUI:70B3D506F*
OUI:70B3D5070*
ID_OUI_FROM_DATABASE=Lumiplan Duhamel
+OUI:70B3D5072*
+ ID_OUI_FROM_DATABASE=Lightdrop
+
OUI:70B3D5073*
ID_OUI_FROM_DATABASE=Liteon Technology Corporation
@@ -58127,6 +59540,9 @@ OUI:70B3D507A*
OUI:70B3D507B*
ID_OUI_FROM_DATABASE=wallbe GmbH
+OUI:70B3D507C*
+ ID_OUI_FROM_DATABASE=ISAC SRL
+
OUI:70B3D507D*
ID_OUI_FROM_DATABASE=PANORAMIC POWER
@@ -58160,6 +59576,12 @@ OUI:70B3D5088*
OUI:70B3D508A*
ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
+OUI:70B3D508B*
+ ID_OUI_FROM_DATABASE=Peter Huber Kaeltemaschinenbau AG
+
+OUI:70B3D508C*
+ ID_OUI_FROM_DATABASE=Airmar Technology Corp
+
OUI:70B3D508D*
ID_OUI_FROM_DATABASE=Clover Electronics Technology Co., Ltd.
@@ -58187,6 +59609,9 @@ OUI:70B3D5096*
OUI:70B3D5097*
ID_OUI_FROM_DATABASE=Avant Technologies
+OUI:70B3D5098*
+ ID_OUI_FROM_DATABASE=Alcodex Technologies Private Limited
+
OUI:70B3D5099*
ID_OUI_FROM_DATABASE=Schwer+Kopka GmbH
@@ -58226,6 +59651,9 @@ OUI:70B3D50A5*
OUI:70B3D50A6*
ID_OUI_FROM_DATABASE=PA CONSULTING SERVICES
+OUI:70B3D50A7*
+ ID_OUI_FROM_DATABASE=Traffic and Parking Control Co, Inc.
+
OUI:70B3D50A8*
ID_OUI_FROM_DATABASE=Symetrics Industries d.b.a. Extant Aerospace
@@ -58259,6 +59687,9 @@ OUI:70B3D50B3*
OUI:70B3D50B4*
ID_OUI_FROM_DATABASE=AVER
+OUI:70B3D50B5*
+ ID_OUI_FROM_DATABASE=Capgemini Netherlands
+
OUI:70B3D50B6*
ID_OUI_FROM_DATABASE=Landis Gyr
@@ -58301,6 +59732,9 @@ OUI:70B3D50C5*
OUI:70B3D50C6*
ID_OUI_FROM_DATABASE=Embedded Arts Co., Ltd.
+OUI:70B3D50C7*
+ ID_OUI_FROM_DATABASE=PEEK TRAFFIC
+
OUI:70B3D50C8*
ID_OUI_FROM_DATABASE=Fin Robotics Inc
@@ -58313,6 +59747,12 @@ OUI:70B3D50CD*
OUI:70B3D50CE*
ID_OUI_FROM_DATABASE=Innominds Software Inc
+OUI:70B3D50CF*
+ ID_OUI_FROM_DATABASE=sohonet ltd
+
+OUI:70B3D50D0*
+ ID_OUI_FROM_DATABASE=ProHound Controles Eirelli
+
OUI:70B3D50D1*
ID_OUI_FROM_DATABASE=Common Sense Monitoring Solutions Ltd.
@@ -58367,6 +59807,12 @@ OUI:70B3D50E6*
OUI:70B3D50E8*
ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG
+OUI:70B3D50E9*
+ ID_OUI_FROM_DATABASE=VNT electronics s.r.o.
+
+OUI:70B3D50EA*
+ ID_OUI_FROM_DATABASE=AEV Broadcast Srl
+
OUI:70B3D50EC*
ID_OUI_FROM_DATABASE=ACS MOTION CONTROL
@@ -58421,6 +59867,9 @@ OUI:70B3D5105*
OUI:70B3D5106*
ID_OUI_FROM_DATABASE=Aplex Technology Inc.
+OUI:70B3D5107*
+ ID_OUI_FROM_DATABASE=OOO Alyans
+
OUI:70B3D5108*
ID_OUI_FROM_DATABASE=TEX COMPUTER SRL
@@ -58466,6 +59915,9 @@ OUI:70B3D511F*
OUI:70B3D5120*
ID_OUI_FROM_DATABASE=GSP Sprachtechnologie GmbH
+OUI:70B3D5121*
+ ID_OUI_FROM_DATABASE=Shenzhen Luxurite Smart Home Ltd
+
OUI:70B3D5122*
ID_OUI_FROM_DATABASE=Henri Systems Holland bv
@@ -58568,6 +60020,9 @@ OUI:70B3D5149*
OUI:70B3D514A*
ID_OUI_FROM_DATABASE=ExSens Technology (Pty) Ltd.
+OUI:70B3D514B*
+ ID_OUI_FROM_DATABASE=C21 Systems Ltd
+
OUI:70B3D514C*
ID_OUI_FROM_DATABASE=CRDE
@@ -58580,6 +60035,9 @@ OUI:70B3D514E*
OUI:70B3D514F*
ID_OUI_FROM_DATABASE=Mobile Devices Unlimited
+OUI:70B3D5150*
+ ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
+
OUI:70B3D5152*
ID_OUI_FROM_DATABASE=Xped Corporation Pty Ltd
@@ -58589,6 +60047,9 @@ OUI:70B3D5153*
OUI:70B3D5154*
ID_OUI_FROM_DATABASE=Walk Horizon Technology (Beijing) Co., Ltd.
+OUI:70B3D515B*
+ ID_OUI_FROM_DATABASE=Armstrong International, Inc.
+
OUI:70B3D515C*
ID_OUI_FROM_DATABASE=Woods Hole Oceanographic Institution
@@ -58625,6 +60086,9 @@ OUI:70B3D5168*
OUI:70B3D5169*
ID_OUI_FROM_DATABASE=Service Plus LLC
+OUI:70B3D516A*
+ ID_OUI_FROM_DATABASE=4Jtech s.r.o.
+
OUI:70B3D516B*
ID_OUI_FROM_DATABASE=IOT Engineering
@@ -58697,6 +60161,9 @@ OUI:70B3D5187*
OUI:70B3D5188*
ID_OUI_FROM_DATABASE=Birket Engineering
+OUI:70B3D5189*
+ ID_OUI_FROM_DATABASE=DAVE SRL
+
OUI:70B3D518B*
ID_OUI_FROM_DATABASE=Aplex Technology Inc.
@@ -58721,6 +60188,12 @@ OUI:70B3D5194*
OUI:70B3D5197*
ID_OUI_FROM_DATABASE=Lattech Systems Pty Ltd
+OUI:70B3D5199*
+ ID_OUI_FROM_DATABASE=Smart Controls LLC
+
+OUI:70B3D519A*
+ ID_OUI_FROM_DATABASE=WiSuite USA
+
OUI:70B3D519B*
ID_OUI_FROM_DATABASE=Global Technical Systems
@@ -58766,6 +60239,9 @@ OUI:70B3D51AD*
OUI:70B3D51AF*
ID_OUI_FROM_DATABASE=Teenage Engineering AB
+OUI:70B3D51B3*
+ ID_OUI_FROM_DATABASE=Graphcore Ltd
+
OUI:70B3D51B4*
ID_OUI_FROM_DATABASE=5nines
@@ -58784,9 +60260,15 @@ OUI:70B3D51B9*
OUI:70B3D51BB*
ID_OUI_FROM_DATABASE=EFENTO T P SZYDÅOWSKI K ZARĘBA SPÓÅKA JAWNA
+OUI:70B3D51BD*
+ ID_OUI_FROM_DATABASE=Shenzhen Siera Technology Ltd
+
OUI:70B3D51BE*
ID_OUI_FROM_DATABASE=Potter Electric Signal Co. LLC
+OUI:70B3D51C0*
+ ID_OUI_FROM_DATABASE=W. H. Leary Co., Inc.
+
OUI:70B3D51C4*
ID_OUI_FROM_DATABASE=Smeg S.p.A.
@@ -58799,6 +60281,9 @@ OUI:70B3D51C7*
OUI:70B3D51C8*
ID_OUI_FROM_DATABASE=LDA audio video profesional S.L.
+OUI:70B3D51C9*
+ ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
+
OUI:70B3D51CB*
ID_OUI_FROM_DATABASE=MatchX GmbH
@@ -58823,21 +60308,36 @@ OUI:70B3D51D4*
OUI:70B3D51D7*
ID_OUI_FROM_DATABASE=Private
+OUI:70B3D51D8*
+ ID_OUI_FROM_DATABASE=Blue Skies Global LLC
+
+OUI:70B3D51D9*
+ ID_OUI_FROM_DATABASE=MondeF
+
OUI:70B3D51DA*
ID_OUI_FROM_DATABASE=Promess Inc.
OUI:70B3D51DB*
ID_OUI_FROM_DATABASE=Hudson Robotics
+OUI:70B3D51DC*
+ ID_OUI_FROM_DATABASE=TEKVEL Ltd.
+
OUI:70B3D51DD*
ID_OUI_FROM_DATABASE=RF CREATIONS LTD
OUI:70B3D51DE*
ID_OUI_FROM_DATABASE=DYCEC, S.A.
+OUI:70B3D51DF*
+ ID_OUI_FROM_DATABASE=ENTEC Electric & Electronic Co., LTD.
+
OUI:70B3D51E0*
ID_OUI_FROM_DATABASE=TOPROOTTechnology Corp. Ltd.
+OUI:70B3D51E1*
+ ID_OUI_FROM_DATABASE=TEX COMPUTER SRL
+
OUI:70B3D51E3*
ID_OUI_FROM_DATABASE=Hatel Elektronik LTD. STI.
@@ -58850,12 +60350,24 @@ OUI:70B3D51E5*
OUI:70B3D51E6*
ID_OUI_FROM_DATABASE=Sanmina Israel
+OUI:70B3D51E8*
+ ID_OUI_FROM_DATABASE=Gogo BA
+
OUI:70B3D51E9*
ID_OUI_FROM_DATABASE=comtime GmbH
+OUI:70B3D51EA*
+ ID_OUI_FROM_DATABASE=Sense For Innovation
+
+OUI:70B3D51EE*
+ ID_OUI_FROM_DATABASE=MEGGITT
+
OUI:70B3D51EF*
ID_OUI_FROM_DATABASE=ADTEK
+OUI:70B3D51F1*
+ ID_OUI_FROM_DATABASE=DIEHL Connectivity Solutions
+
OUI:70B3D51F3*
ID_OUI_FROM_DATABASE=Smart Energy Code Company Limited
@@ -58874,6 +60386,9 @@ OUI:70B3D51FD*
OUI:70B3D51FE*
ID_OUI_FROM_DATABASE=MobiPromo
+OUI:70B3D51FF*
+ ID_OUI_FROM_DATABASE=Audiodo AB
+
OUI:70B3D5200*
ID_OUI_FROM_DATABASE=NextEV Co., Ltd.
@@ -58970,12 +60485,18 @@ OUI:70B3D5228*
OUI:70B3D5229*
ID_OUI_FROM_DATABASE=CONTROL SYSTEMS Srl
+OUI:70B3D522A*
+ ID_OUI_FROM_DATABASE=Shishido Electrostatic, Ltd.
+
OUI:70B3D522B*
ID_OUI_FROM_DATABASE=VITEC
OUI:70B3D522C*
ID_OUI_FROM_DATABASE=Hiquel Elektronik- und Anlagenbau GmbH
+OUI:70B3D522D*
+ ID_OUI_FROM_DATABASE=Leder Elektronik Design
+
OUI:70B3D522E*
ID_OUI_FROM_DATABASE=Private
@@ -58991,6 +60512,9 @@ OUI:70B3D5231*
OUI:70B3D5232*
ID_OUI_FROM_DATABASE=UCONSYS
+OUI:70B3D5234*
+ ID_OUI_FROM_DATABASE=EDFelectronics JRMM Sp z o.o. sp.k.
+
OUI:70B3D5235*
ID_OUI_FROM_DATABASE=CAMEON S.A.
@@ -59000,6 +60524,9 @@ OUI:70B3D5236*
OUI:70B3D5238*
ID_OUI_FROM_DATABASE=Arete Associates
+OUI:70B3D5239*
+ ID_OUI_FROM_DATABASE=Applied Silver
+
OUI:70B3D523A*
ID_OUI_FROM_DATABASE=Mesa Labs, Inc.
@@ -59033,6 +60560,9 @@ OUI:70B3D5246*
OUI:70B3D5248*
ID_OUI_FROM_DATABASE=GL TECH CO.,LTD
+OUI:70B3D524A*
+ ID_OUI_FROM_DATABASE=Unmukti Technology Pvt Ltd
+
OUI:70B3D524B*
ID_OUI_FROM_DATABASE=TOSEI ENGINEERING CORP.
@@ -59048,6 +60578,9 @@ OUI:70B3D524F*
OUI:70B3D5250*
ID_OUI_FROM_DATABASE=Datum Electronics Limited
+OUI:70B3D5251*
+ ID_OUI_FROM_DATABASE=PixelApps s.r.o.
+
OUI:70B3D5252*
ID_OUI_FROM_DATABASE=Sierra Nevada Corporation
@@ -59075,12 +60608,18 @@ OUI:70B3D525B*
OUI:70B3D525D*
ID_OUI_FROM_DATABASE=Mimo Networks
+OUI:70B3D525F*
+ ID_OUI_FROM_DATABASE=COPPERNIC SAS
+
OUI:70B3D5260*
ID_OUI_FROM_DATABASE=ModuSystems, Inc
OUI:70B3D5261*
ID_OUI_FROM_DATABASE=Potter Electric Signal Co. LLC
+OUI:70B3D5264*
+ ID_OUI_FROM_DATABASE=ifak technology + service GmbH
+
OUI:70B3D5266*
ID_OUI_FROM_DATABASE=Spectra Displays Ltd
@@ -59126,6 +60665,9 @@ OUI:70B3D5279*
OUI:70B3D527A*
ID_OUI_FROM_DATABASE=TD ECOPHISIKA
+OUI:70B3D527C*
+ ID_OUI_FROM_DATABASE=MOTION LIB,Inc.
+
OUI:70B3D527D*
ID_OUI_FROM_DATABASE=Telenor Connexion AB
@@ -59150,6 +60692,9 @@ OUI:70B3D5285*
OUI:70B3D5286*
ID_OUI_FROM_DATABASE=Pedax Danmark
+OUI:70B3D5287*
+ ID_OUI_FROM_DATABASE=Hypex Electronics BV
+
OUI:70B3D5288*
ID_OUI_FROM_DATABASE=Bresslergroup
@@ -59177,6 +60722,9 @@ OUI:70B3D528F*
OUI:70B3D5292*
ID_OUI_FROM_DATABASE=Private
+OUI:70B3D5293*
+ ID_OUI_FROM_DATABASE=Solar RIg Technologies
+
OUI:70B3D5295*
ID_OUI_FROM_DATABASE=Cello Electronics (UK) Ltd
@@ -59216,6 +60764,9 @@ OUI:70B3D52A5*
OUI:70B3D52A7*
ID_OUI_FROM_DATABASE=Plasmability, LLC
+OUI:70B3D52A8*
+ ID_OUI_FROM_DATABASE=Dynamic Perspective GmbH
+
OUI:70B3D52A9*
ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L.
@@ -59321,9 +60872,15 @@ OUI:70B3D52DB*
OUI:70B3D52DC*
ID_OUI_FROM_DATABASE=Bolide Technology Group, Inc.
+OUI:70B3D52DE*
+ ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
+
OUI:70B3D52E0*
ID_OUI_FROM_DATABASE=Peter Huber
+OUI:70B3D52E1*
+ ID_OUI_FROM_DATABASE=hiSky S.C.S LTD
+
OUI:70B3D52E2*
ID_OUI_FROM_DATABASE=Spark Lasers
@@ -59333,9 +60890,15 @@ OUI:70B3D52E3*
OUI:70B3D52E5*
ID_OUI_FROM_DATABASE=Fläkt Woods AB
+OUI:70B3D52E6*
+ ID_OUI_FROM_DATABASE=IPG Photonics Corporation
+
OUI:70B3D52E7*
ID_OUI_FROM_DATABASE=Atos spa
+OUI:70B3D52E8*
+ ID_OUI_FROM_DATABASE=Telefire
+
OUI:70B3D52EA*
ID_OUI_FROM_DATABASE=Schneider Electric Motion
@@ -59345,6 +60908,9 @@ OUI:70B3D52EB*
OUI:70B3D52EC*
ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
+OUI:70B3D52ED*
+ ID_OUI_FROM_DATABASE=Signals and systems india pvt ltd
+
OUI:70B3D52EE*
ID_OUI_FROM_DATABASE=Aplex Technology Inc.
@@ -59366,6 +60932,9 @@ OUI:70B3D52F3*
OUI:70B3D52F4*
ID_OUI_FROM_DATABASE=Radixon s.r.o.
+OUI:70B3D52F5*
+ ID_OUI_FROM_DATABASE=eze System, Inc.
+
OUI:70B3D52F6*
ID_OUI_FROM_DATABASE=TATTILE SRL
@@ -59384,9 +60953,15 @@ OUI:70B3D52FD*
OUI:70B3D52FE*
ID_OUI_FROM_DATABASE=Yaham Optoelectronics Co., Ltd
+OUI:70B3D52FF*
+ ID_OUI_FROM_DATABASE=Sunstone Engineering
+
OUI:70B3D5300*
ID_OUI_FROM_DATABASE=Novo DR Ltd.
+OUI:70B3D5302*
+ ID_OUI_FROM_DATABASE=DogWatch Inc
+
OUI:70B3D5303*
ID_OUI_FROM_DATABASE=Fuchu Giken, Inc.
@@ -59414,6 +60989,9 @@ OUI:70B3D530D*
OUI:70B3D530E*
ID_OUI_FROM_DATABASE=Private
+OUI:70B3D530F*
+ ID_OUI_FROM_DATABASE=Cardinal Scales Manufacturing Co
+
OUI:70B3D5313*
ID_OUI_FROM_DATABASE=DIEHL Controls
@@ -59432,6 +61010,9 @@ OUI:70B3D531C*
OUI:70B3D531E*
ID_OUI_FROM_DATABASE=GILLAM-FEI S.A.
+OUI:70B3D531F*
+ ID_OUI_FROM_DATABASE=Elcoma
+
OUI:70B3D5323*
ID_OUI_FROM_DATABASE=TATTILE SRL
@@ -59459,12 +61040,21 @@ OUI:70B3D532D*
OUI:70B3D532F*
ID_OUI_FROM_DATABASE=Movidius SRL
+OUI:70B3D5330*
+ ID_OUI_FROM_DATABASE=iOne
+
OUI:70B3D5332*
ID_OUI_FROM_DATABASE=InnoSenT
+OUI:70B3D5334*
+ ID_OUI_FROM_DATABASE=Dokuen Co. Ltd.
+
OUI:70B3D5336*
ID_OUI_FROM_DATABASE=Synaccess Networks Inc.
+OUI:70B3D5338*
+ ID_OUI_FROM_DATABASE=Opti-Sciences, Inc.
+
OUI:70B3D5339*
ID_OUI_FROM_DATABASE=Sierra Nevada Corporation
@@ -59492,6 +61082,9 @@ OUI:70B3D5343*
OUI:70B3D5344*
ID_OUI_FROM_DATABASE=IHI Inspection & Instrumentation Co., Ltd.
+OUI:70B3D5345*
+ ID_OUI_FROM_DATABASE=AT-Automation Technology GmbH
+
OUI:70B3D5346*
ID_OUI_FROM_DATABASE=Ultamation Limited
@@ -59525,6 +61118,12 @@ OUI:70B3D5352*
OUI:70B3D5353*
ID_OUI_FROM_DATABASE=Digital Outfit
+OUI:70B3D5354*
+ ID_OUI_FROM_DATABASE=IMP-Computer Systems
+
+OUI:70B3D5355*
+ ID_OUI_FROM_DATABASE=Hongin., Ltd
+
OUI:70B3D5357*
ID_OUI_FROM_DATABASE=Movimento Group AB
@@ -59579,6 +61178,9 @@ OUI:70B3D536C*
OUI:70B3D536D*
ID_OUI_FROM_DATABASE=Cyberteam Sp z o o
+OUI:70B3D536E*
+ ID_OUI_FROM_DATABASE=Electrónica Falcón S.A.U
+
OUI:70B3D536F*
ID_OUI_FROM_DATABASE=BuddyGuard GmbH
@@ -59645,9 +61247,15 @@ OUI:70B3D538B*
OUI:70B3D538C*
ID_OUI_FROM_DATABASE=MiraeSignal Co., Ltd
+OUI:70B3D538D*
+ ID_OUI_FROM_DATABASE=IMP-TELEKOMUNIKACIJE DOO
+
OUI:70B3D538F*
ID_OUI_FROM_DATABASE=Sorynorydotcom Inc
+OUI:70B3D5391*
+ ID_OUI_FROM_DATABASE=Changshu Ruite Electric Co.,Ltd.
+
OUI:70B3D5392*
ID_OUI_FROM_DATABASE=Contec DTx
@@ -59672,6 +61280,15 @@ OUI:70B3D539D*
OUI:70B3D539E*
ID_OUI_FROM_DATABASE=Lanmark Controls Inc.
+OUI:70B3D53A0*
+ ID_OUI_FROM_DATABASE=chiconypower
+
+OUI:70B3D53A1*
+ ID_OUI_FROM_DATABASE=Reckeen HDP Media sp. z o.o. sp. k.
+
+OUI:70B3D53A4*
+ ID_OUI_FROM_DATABASE=Ascenix Corporation
+
OUI:70B3D53A5*
ID_OUI_FROM_DATABASE=KMtronic ltd
@@ -59765,6 +61382,9 @@ OUI:70B3D53CF*
OUI:70B3D53D2*
ID_OUI_FROM_DATABASE=Imagine Inc.
+OUI:70B3D53D4*
+ ID_OUI_FROM_DATABASE=Sanmina Israel
+
OUI:70B3D53D5*
ID_OUI_FROM_DATABASE=oxynet Solutions
@@ -59783,6 +61403,9 @@ OUI:70B3D53DA*
OUI:70B3D53DB*
ID_OUI_FROM_DATABASE=KST technology
+OUI:70B3D53DD*
+ ID_OUI_FROM_DATABASE=Kniggendorf + Kögler Security GmbH
+
OUI:70B3D53DE*
ID_OUI_FROM_DATABASE=ELOMAC Elektronik GmbH
@@ -59807,6 +61430,9 @@ OUI:70B3D53E5*
OUI:70B3D53E6*
ID_OUI_FROM_DATABASE=machineQ
+OUI:70B3D53E7*
+ ID_OUI_FROM_DATABASE=JNR Sports Holdings, LLC
+
OUI:70B3D53E8*
ID_OUI_FROM_DATABASE=COSMOS web Co., Ltd.
@@ -59846,6 +61472,9 @@ OUI:70B3D53F7*
OUI:70B3D53F9*
ID_OUI_FROM_DATABASE=Herrick Tech Labs
+OUI:70B3D53FA*
+ ID_OUI_FROM_DATABASE=Zaklad Energoelektroniki Twerd
+
OUI:70B3D53FE*
ID_OUI_FROM_DATABASE=Mentor Graphics
@@ -59858,6 +61487,12 @@ OUI:70B3D5400*
OUI:70B3D5401*
ID_OUI_FROM_DATABASE=Private
+OUI:70B3D5402*
+ ID_OUI_FROM_DATABASE=AKIS technologies
+
+OUI:70B3D5403*
+ ID_OUI_FROM_DATABASE=Mighty Cube Co., Ltd.
+
OUI:70B3D5404*
ID_OUI_FROM_DATABASE=RANIX,Inc.
@@ -59876,6 +61511,9 @@ OUI:70B3D5408*
OUI:70B3D540A*
ID_OUI_FROM_DATABASE=Monroe Electronics, Inc.
+OUI:70B3D540E*
+ ID_OUI_FROM_DATABASE=Liaoyun Information Technology Co., Ltd.
+
OUI:70B3D5410*
ID_OUI_FROM_DATABASE=Avant Technologies, Inc
@@ -59897,6 +61535,9 @@ OUI:70B3D5418*
OUI:70B3D541A*
ID_OUI_FROM_DATABASE=HYOSUNG Power & Industrial Systems
+OUI:70B3D541B*
+ ID_OUI_FROM_DATABASE=SYS TEC electronic GmbH
+
OUI:70B3D541E*
ID_OUI_FROM_DATABASE=Redler Computers
@@ -59924,6 +61565,9 @@ OUI:70B3D542C*
OUI:70B3D542D*
ID_OUI_FROM_DATABASE=RCH Italia SpA
+OUI:70B3D542E*
+ ID_OUI_FROM_DATABASE=Dr. Zinngrebe GmbH
+
OUI:70B3D542F*
ID_OUI_FROM_DATABASE=SINTOKOGIO, LTD
@@ -59948,6 +61592,9 @@ OUI:70B3D5435*
OUI:70B3D5436*
ID_OUI_FROM_DATABASE=Henrich Electronics Corporation
+OUI:70B3D5437*
+ ID_OUI_FROM_DATABASE=Digital Way
+
OUI:70B3D5439*
ID_OUI_FROM_DATABASE=TriLED
@@ -59957,9 +61604,18 @@ OUI:70B3D543B*
OUI:70B3D543D*
ID_OUI_FROM_DATABASE=Veryx Technologies Private Limited
+OUI:70B3D543E*
+ ID_OUI_FROM_DATABASE=Peloton Technology
+
+OUI:70B3D543F*
+ ID_OUI_FROM_DATABASE=biosilver .co.,ltd
+
OUI:70B3D5440*
ID_OUI_FROM_DATABASE=Discover Video
+OUI:70B3D5441*
+ ID_OUI_FROM_DATABASE=Videoport S.A.
+
OUI:70B3D5442*
ID_OUI_FROM_DATABASE=Blair Companies
@@ -59972,6 +61628,9 @@ OUI:70B3D5445*
OUI:70B3D5446*
ID_OUI_FROM_DATABASE=Santa Barbara Imaging Systems
+OUI:70B3D5447*
+ ID_OUI_FROM_DATABASE=Avid Controls Inc
+
OUI:70B3D5448*
ID_OUI_FROM_DATABASE=B/E Aerospace, Inc.
@@ -59990,6 +61649,9 @@ OUI:70B3D5457*
OUI:70B3D5459*
ID_OUI_FROM_DATABASE=Protium Technologies, Inc.
+OUI:70B3D545B*
+ ID_OUI_FROM_DATABASE=KOMZ - IZMERENIYA
+
OUI:70B3D545C*
ID_OUI_FROM_DATABASE=AlyTech
@@ -60050,6 +61712,9 @@ OUI:70B3D5479*
OUI:70B3D547C*
ID_OUI_FROM_DATABASE=Par-Tech, Inc.
+OUI:70B3D547E*
+ ID_OUI_FROM_DATABASE=Fiber Optika Technologies Pvt. Ltd.
+
OUI:70B3D547F*
ID_OUI_FROM_DATABASE=ASE GmbH
@@ -60119,6 +61784,9 @@ OUI:70B3D54A0*
OUI:70B3D54A1*
ID_OUI_FROM_DATABASE=Herholdt Controls srl
+OUI:70B3D54A2*
+ ID_OUI_FROM_DATABASE=DEVAU Lemppenau GmbH
+
OUI:70B3D54A5*
ID_OUI_FROM_DATABASE=Intermind Inc.
@@ -60134,6 +61802,12 @@ OUI:70B3D54A9*
OUI:70B3D54AA*
ID_OUI_FROM_DATABASE=Twoway Communications, Inc.
+OUI:70B3D54AB*
+ ID_OUI_FROM_DATABASE=TruTeq Wireless (Pty) Ltd
+
+OUI:70B3D54AC*
+ ID_OUI_FROM_DATABASE=Microsoft Research
+
OUI:70B3D54AD*
ID_OUI_FROM_DATABASE=GACI
@@ -60152,6 +61826,9 @@ OUI:70B3D54B1*
OUI:70B3D54B2*
ID_OUI_FROM_DATABASE=Certus Operations Ltd
+OUI:70B3D54B3*
+ ID_OUI_FROM_DATABASE=Bacsoft
+
OUI:70B3D54B4*
ID_OUI_FROM_DATABASE=Hi Tech Systems Ltd
@@ -60194,6 +61871,9 @@ OUI:70B3D54C4*
OUI:70B3D54C5*
ID_OUI_FROM_DATABASE=Moving iMage Technologies LLC
+OUI:70B3D54C6*
+ ID_OUI_FROM_DATABASE=BlueBox Video Limited
+
OUI:70B3D54C7*
ID_OUI_FROM_DATABASE=SOLVERIS sp. z o.o.
@@ -60212,6 +61892,9 @@ OUI:70B3D54CF*
OUI:70B3D54D1*
ID_OUI_FROM_DATABASE=Contraves Advanced Devices Sdn. Bhd.
+OUI:70B3D54D2*
+ ID_OUI_FROM_DATABASE=Biotage Sweden AB
+
OUI:70B3D54D4*
ID_OUI_FROM_DATABASE=Nortek Global HVAC
@@ -60236,6 +61919,9 @@ OUI:70B3D54DE*
OUI:70B3D54DF*
ID_OUI_FROM_DATABASE=Nidec Avtron Automation Corp
+OUI:70B3D54E0*
+ ID_OUI_FROM_DATABASE=Microvideo
+
OUI:70B3D54E1*
ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
@@ -60248,21 +61934,36 @@ OUI:70B3D54E7*
OUI:70B3D54E9*
ID_OUI_FROM_DATABASE=ADETEC SAS
+OUI:70B3D54EA*
+ ID_OUI_FROM_DATABASE=Vocality international T/A Cubic
+
OUI:70B3D54EB*
ID_OUI_FROM_DATABASE=INFOSOFT DIGITAL DESIGN & SERVICES PRIVATE LIMITED
OUI:70B3D54EC*
ID_OUI_FROM_DATABASE=Hangzhou Youshi Industry Co., Ltd.
+OUI:70B3D54EE*
+ ID_OUI_FROM_DATABASE=NOA Co., Ltd.
+
OUI:70B3D54EF*
ID_OUI_FROM_DATABASE=CMI, Inc.
OUI:70B3D54F0*
ID_OUI_FROM_DATABASE=Li Seng Technology Ltd.,
+OUI:70B3D54F1*
+ ID_OUI_FROM_DATABASE=LG Electronics
+
+OUI:70B3D54F2*
+ ID_OUI_FROM_DATABASE=COMPAL ELECTRONICS, INC.
+
OUI:70B3D54F4*
ID_OUI_FROM_DATABASE=WiTagg, Inc
+OUI:70B3D54F6*
+ ID_OUI_FROM_DATABASE=DORLET SAU
+
OUI:70B3D54F8*
ID_OUI_FROM_DATABASE=Private
@@ -60302,18 +62003,30 @@ OUI:70B3D5508*
OUI:70B3D550E*
ID_OUI_FROM_DATABASE=Micro Trend Automation Co., LTD
+OUI:70B3D5510*
+ ID_OUI_FROM_DATABASE=PSL ELEKTRONÄ°K SANAYÄ° VE TÄ°CARET A.S.
+
OUI:70B3D5511*
ID_OUI_FROM_DATABASE=Next Sight srl
+OUI:70B3D5512*
+ ID_OUI_FROM_DATABASE=Techno Broad,Inc
+
OUI:70B3D5513*
ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
OUI:70B3D5515*
ID_OUI_FROM_DATABASE=PCSC
+OUI:70B3D5516*
+ ID_OUI_FROM_DATABASE=LINEAGE POWER PVT LTD.,
+
OUI:70B3D5517*
ID_OUI_FROM_DATABASE=ISPHER
+OUI:70B3D5518*
+ ID_OUI_FROM_DATABASE=CRUXELL Corp.
+
OUI:70B3D551B*
ID_OUI_FROM_DATABASE=Vitrea Smart Home Technologies
@@ -60365,6 +62078,9 @@ OUI:70B3D5531*
OUI:70B3D5532*
ID_OUI_FROM_DATABASE=Talleres de Escoriaza SA
+OUI:70B3D5533*
+ ID_OUI_FROM_DATABASE=Nippon Marine Enterprises, Ltd.
+
OUI:70B3D5538*
ID_OUI_FROM_DATABASE=sydetion UG (h.b.)
@@ -60386,6 +62102,9 @@ OUI:70B3D5542*
OUI:70B3D5544*
ID_OUI_FROM_DATABASE=Silicon Safe Ltd
+OUI:70B3D5545*
+ ID_OUI_FROM_DATABASE=Airity Technologies Inc.
+
OUI:70B3D5546*
ID_OUI_FROM_DATABASE=Sensefarm AB
@@ -60419,6 +62138,9 @@ OUI:70B3D5550*
OUI:70B3D5551*
ID_OUI_FROM_DATABASE=infrachip
+OUI:70B3D5553*
+ ID_OUI_FROM_DATABASE=TAALEX Systemtechnik GmbH
+
OUI:70B3D5554*
ID_OUI_FROM_DATABASE=Teletypes Manufacturing Plant
@@ -60461,12 +62183,18 @@ OUI:70B3D556A*
OUI:70B3D556B*
ID_OUI_FROM_DATABASE=S.E.I. CO.,LTD.
+OUI:70B3D556C*
+ ID_OUI_FROM_DATABASE=Telensa Ltd
+
OUI:70B3D5570*
ID_OUI_FROM_DATABASE=Bayern Engineering GmbH & Co. KG
OUI:70B3D5572*
ID_OUI_FROM_DATABASE=CRDE
+OUI:70B3D5574*
+ ID_OUI_FROM_DATABASE=Cloud Intelligence Pty Ltd
+
OUI:70B3D5576*
ID_OUI_FROM_DATABASE=Shandong Hospot IOT Technology Co.,Ltd.
@@ -60476,6 +62204,9 @@ OUI:70B3D5578*
OUI:70B3D5579*
ID_OUI_FROM_DATABASE=Chelsea Technologies Group Ltd
+OUI:70B3D557A*
+ ID_OUI_FROM_DATABASE=Rhythm Engineering, LLC.
+
OUI:70B3D557B*
ID_OUI_FROM_DATABASE=ELAMAKATO GmbH
@@ -60518,6 +62249,9 @@ OUI:70B3D558E*
OUI:70B3D558F*
ID_OUI_FROM_DATABASE=LSL systems
+OUI:70B3D5590*
+ ID_OUI_FROM_DATABASE=812th AITS
+
OUI:70B3D5591*
ID_OUI_FROM_DATABASE=Private
@@ -60539,9 +62273,15 @@ OUI:70B3D5596*
OUI:70B3D5597*
ID_OUI_FROM_DATABASE=VAPE RAIL INTERNATIONAL
+OUI:70B3D5598*
+ ID_OUI_FROM_DATABASE=Ruag Defence France SAS
+
OUI:70B3D5599*
ID_OUI_FROM_DATABASE=LECO Corporation
+OUI:70B3D559B*
+ ID_OUI_FROM_DATABASE=AUTOMATIZACION Y CONECTIVIDAD SA DE CV
+
OUI:70B3D559C*
ID_OUI_FROM_DATABASE=DAVE SRL
@@ -60557,6 +62297,12 @@ OUI:70B3D55A2*
OUI:70B3D55A3*
ID_OUI_FROM_DATABASE=CT Company
+OUI:70B3D55A5*
+ ID_OUI_FROM_DATABASE=Rehwork GmbH
+
+OUI:70B3D55A7*
+ ID_OUI_FROM_DATABASE=ABB S.p.A.
+
OUI:70B3D55A8*
ID_OUI_FROM_DATABASE=Farmobile
@@ -60623,6 +62369,9 @@ OUI:70B3D55D1*
OUI:70B3D55D3*
ID_OUI_FROM_DATABASE=Supracon AG
+OUI:70B3D55D4*
+ ID_OUI_FROM_DATABASE=RCH ITALIA SPA
+
OUI:70B3D55D5*
ID_OUI_FROM_DATABASE=CT Company
@@ -60662,6 +62411,9 @@ OUI:70B3D55E5*
OUI:70B3D55E6*
ID_OUI_FROM_DATABASE=Mechatronics Systems Private Limited
+OUI:70B3D55E7*
+ ID_OUI_FROM_DATABASE=Heroic Technologies Inc.
+
OUI:70B3D55E8*
ID_OUI_FROM_DATABASE=VITEC
@@ -60671,6 +62423,9 @@ OUI:70B3D55E9*
OUI:70B3D55EA*
ID_OUI_FROM_DATABASE=KYS,INC
+OUI:70B3D55EC*
+ ID_OUI_FROM_DATABASE=Creative Electronics Ltd
+
OUI:70B3D55ED*
ID_OUI_FROM_DATABASE=EA Elektroautomatik GmbH & Co. KG
@@ -60722,6 +62477,9 @@ OUI:70B3D5600*
OUI:70B3D5602*
ID_OUI_FROM_DATABASE=Quantum Opus, LLC
+OUI:70B3D5603*
+ ID_OUI_FROM_DATABASE=EGISTECH CO.,LTD.
+
OUI:70B3D5605*
ID_OUI_FROM_DATABASE=Aplex Technology Inc.
@@ -60788,6 +62546,9 @@ OUI:70B3D561E*
OUI:70B3D561F*
ID_OUI_FROM_DATABASE=Labotect Labor-Technik-Göttingen GmbH
+OUI:70B3D5623*
+ ID_OUI_FROM_DATABASE=Beijing HuaLian Technology Co, Ltd.
+
OUI:70B3D5625*
ID_OUI_FROM_DATABASE=VX Instruments GmbH
@@ -60848,6 +62609,9 @@ OUI:70B3D5645*
OUI:70B3D5647*
ID_OUI_FROM_DATABASE=KZTA
+OUI:70B3D5648*
+ ID_OUI_FROM_DATABASE=Magnamed Tecnologia Medica S/A
+
OUI:70B3D5649*
ID_OUI_FROM_DATABASE=swissled technologies AG
@@ -60917,6 +62681,9 @@ OUI:70B3D5665*
OUI:70B3D5666*
ID_OUI_FROM_DATABASE=Aplex Technology Inc.
+OUI:70B3D5669*
+ ID_OUI_FROM_DATABASE=Pano0ramic Power
+
OUI:70B3D566A*
ID_OUI_FROM_DATABASE=Private
@@ -60932,6 +62699,9 @@ OUI:70B3D5671*
OUI:70B3D5672*
ID_OUI_FROM_DATABASE=KLEIBER Infrared GmbH
+OUI:70B3D5673*
+ ID_OUI_FROM_DATABASE=ACD Elekronik GmbH
+
OUI:70B3D5674*
ID_OUI_FROM_DATABASE=Fortress Cyber Security
@@ -60962,6 +62732,12 @@ OUI:70B3D5680*
OUI:70B3D5682*
ID_OUI_FROM_DATABASE=Rosslare Enterprises Limited
+OUI:70B3D5686*
+ ID_OUI_FROM_DATABASE=Access Protocol Pty Ltd
+
+OUI:70B3D5688*
+ ID_OUI_FROM_DATABASE=MG s.r.l.
+
OUI:70B3D5689*
ID_OUI_FROM_DATABASE=Prisma Telecom Testing Srl
@@ -61007,6 +62783,12 @@ OUI:70B3D56A0*
OUI:70B3D56A1*
ID_OUI_FROM_DATABASE=GLIAL TECHNOLOGY
+OUI:70B3D56A2*
+ ID_OUI_FROM_DATABASE=Root Automation
+
+OUI:70B3D56A3*
+ ID_OUI_FROM_DATABASE=OutdoorLink
+
OUI:70B3D56A5*
ID_OUI_FROM_DATABASE=Akenori PTE LTD
@@ -61046,6 +62828,9 @@ OUI:70B3D56B6*
OUI:70B3D56B7*
ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG
+OUI:70B3D56B8*
+ ID_OUI_FROM_DATABASE=BT9
+
OUI:70B3D56BB*
ID_OUI_FROM_DATABASE=LUCEO
@@ -61079,6 +62864,9 @@ OUI:70B3D56D0*
OUI:70B3D56D1*
ID_OUI_FROM_DATABASE=Visual Engineering Technologies Ltd
+OUI:70B3D56D2*
+ ID_OUI_FROM_DATABASE=Ahrens & Birner Company GmbH
+
OUI:70B3D56D3*
ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH
@@ -61160,6 +62948,9 @@ OUI:70B3D56FA*
OUI:70B3D56FB*
ID_OUI_FROM_DATABASE=Shachihata Inc.
+OUI:70B3D56FC*
+ ID_OUI_FROM_DATABASE=MI Inc.
+
OUI:70B3D56FD*
ID_OUI_FROM_DATABASE=Core Akıllı Ev Sistemleri
@@ -61196,6 +62987,9 @@ OUI:70B3D5709*
OUI:70B3D570A*
ID_OUI_FROM_DATABASE=PULLNET TECHNOLOGY, SA DE CV SSC1012302S73
+OUI:70B3D570E*
+ ID_OUI_FROM_DATABASE=Wuhan Xingtuxinke ELectronic Co.,Ltd
+
OUI:70B3D570F*
ID_OUI_FROM_DATABASE=Alion Science & Technology
@@ -61220,6 +63014,9 @@ OUI:70B3D5716*
OUI:70B3D5717*
ID_OUI_FROM_DATABASE=Secure Systems & Services
+OUI:70B3D5718*
+ ID_OUI_FROM_DATABASE=PEEK TRAFFIC
+
OUI:70B3D571B*
ID_OUI_FROM_DATABASE=elsys
@@ -61280,6 +63077,9 @@ OUI:70B3D5735*
OUI:70B3D5737*
ID_OUI_FROM_DATABASE=SD Biosensor
+OUI:70B3D5738*
+ ID_OUI_FROM_DATABASE=Private
+
OUI:70B3D5739*
ID_OUI_FROM_DATABASE=Zigencorp, Inc
@@ -61289,6 +63089,9 @@ OUI:70B3D573A*
OUI:70B3D573B*
ID_OUI_FROM_DATABASE=S-I-C
+OUI:70B3D573C*
+ ID_OUI_FROM_DATABASE=Centro de Ingenieria y Desarrollo industrial
+
OUI:70B3D573D*
ID_OUI_FROM_DATABASE=NETWAYS GmbH
@@ -61313,6 +63116,9 @@ OUI:70B3D5745*
OUI:70B3D5747*
ID_OUI_FROM_DATABASE=Eva Automation
+OUI:70B3D5748*
+ ID_OUI_FROM_DATABASE=KDT
+
OUI:70B3D5749*
ID_OUI_FROM_DATABASE=Granite River Labs Inc
@@ -61346,6 +63152,9 @@ OUI:70B3D5755*
OUI:70B3D5758*
ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG
+OUI:70B3D5759*
+ ID_OUI_FROM_DATABASE=AML
+
OUI:70B3D575A*
ID_OUI_FROM_DATABASE=Standard Backhaul Communications
@@ -61391,6 +63200,9 @@ OUI:70B3D576A*
OUI:70B3D576B*
ID_OUI_FROM_DATABASE=EMPELOR GmbH
+OUI:70B3D576C*
+ ID_OUI_FROM_DATABASE=Aural Ltd
+
OUI:70B3D576D*
ID_OUI_FROM_DATABASE=Trimble
@@ -61451,6 +63263,9 @@ OUI:70B3D5784*
OUI:70B3D5785*
ID_OUI_FROM_DATABASE=Density Inc.
+OUI:70B3D5788*
+ ID_OUI_FROM_DATABASE=Slan
+
OUI:70B3D5789*
ID_OUI_FROM_DATABASE=SEMEX-EngCon GmbH
@@ -61475,6 +63290,9 @@ OUI:70B3D5790*
OUI:70B3D5791*
ID_OUI_FROM_DATABASE=Romteck Australia
+OUI:70B3D5793*
+ ID_OUI_FROM_DATABASE=Gastech Australia Pty Ltd
+
OUI:70B3D5794*
ID_OUI_FROM_DATABASE=Shadin Avionics
@@ -61619,6 +63437,12 @@ OUI:70B3D57D7*
OUI:70B3D57D9*
ID_OUI_FROM_DATABASE=ATOM GIKEN Co.,Ltd.
+OUI:70B3D57DA*
+ ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
+
+OUI:70B3D57DC*
+ ID_OUI_FROM_DATABASE=Software Systems Plus
+
OUI:70B3D57DD*
ID_OUI_FROM_DATABASE=Excel Medical Electronics LLC
@@ -61695,7 +63519,7 @@ OUI:70B3D57F8*
ID_OUI_FROM_DATABASE=Solvera Lynx d.d.
OUI:70B3D57F9*
- ID_OUI_FROM_DATABASE=CSS Inc.
+ ID_OUI_FROM_DATABASE=Communication Systems Solutions
OUI:70B3D57FB*
ID_OUI_FROM_DATABASE=db Broadcast Products Ltd
@@ -61706,6 +63530,15 @@ OUI:70B3D57FD*
OUI:70B3D57FE*
ID_OUI_FROM_DATABASE=RCH Italia SpA
+OUI:70B3D5800*
+ ID_OUI_FROM_DATABASE=HeadsafeIP PTY LTD
+
+OUI:70B3D5802*
+ ID_OUI_FROM_DATABASE=Qingdao CNR HITACH Railway Signal&communication co.,ltd
+
+OUI:70B3D5803*
+ ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG
+
OUI:70B3D5804*
ID_OUI_FROM_DATABASE=PMT Corporation
@@ -61802,6 +63635,9 @@ OUI:70B3D5833*
OUI:70B3D5835*
ID_OUI_FROM_DATABASE=CommBox P/L
+OUI:70B3D5836*
+ ID_OUI_FROM_DATABASE=Authenticdata
+
OUI:70B3D5837*
ID_OUI_FROM_DATABASE=HiDes, Inc.
@@ -61847,6 +63683,9 @@ OUI:70B3D5849*
OUI:70B3D584A*
ID_OUI_FROM_DATABASE=MOG Laboratories Pty Ltd
+OUI:70B3D584B*
+ ID_OUI_FROM_DATABASE=QuestHouse, Inc.
+
OUI:70B3D584C*
ID_OUI_FROM_DATABASE=CoreKinect
@@ -61859,6 +63698,9 @@ OUI:70B3D584E*
OUI:70B3D5850*
ID_OUI_FROM_DATABASE=REO AG
+OUI:70B3D5851*
+ ID_OUI_FROM_DATABASE=EXASCEND (Wuhan) Co., Ltd
+
OUI:70B3D5852*
ID_OUI_FROM_DATABASE=NetBoxSC, LLC
@@ -61892,18 +63734,27 @@ OUI:70B3D585E*
OUI:70B3D585F*
ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
+OUI:70B3D5860*
+ ID_OUI_FROM_DATABASE=KBS Industrieelektronik GmbH
+
OUI:70B3D5861*
ID_OUI_FROM_DATABASE=KST technology
OUI:70B3D5862*
ID_OUI_FROM_DATABASE=TripleOre
+OUI:70B3D5863*
+ ID_OUI_FROM_DATABASE=Shenzhen Wesion Technology Co., Ltd
+
OUI:70B3D5866*
ID_OUI_FROM_DATABASE=MEPS Realtime
OUI:70B3D5868*
ID_OUI_FROM_DATABASE=U-JIN Mesco Co., Ltd.
+OUI:70B3D586B*
+ ID_OUI_FROM_DATABASE=AVL DiTEST
+
OUI:70B3D586C*
ID_OUI_FROM_DATABASE=eeas gmbh
@@ -61949,6 +63800,9 @@ OUI:70B3D587D*
OUI:70B3D587E*
ID_OUI_FROM_DATABASE=Septentrio NV
+OUI:70B3D587F*
+ ID_OUI_FROM_DATABASE=NAC Planning Co., Ltd.
+
OUI:70B3D5880*
ID_OUI_FROM_DATABASE=Skopei B.V.
@@ -61979,6 +63833,9 @@ OUI:70B3D588D*
OUI:70B3D588F*
ID_OUI_FROM_DATABASE=Quaesta Instruments, LLC
+OUI:70B3D5890*
+ ID_OUI_FROM_DATABASE=EIDOS s.r.l.
+
OUI:70B3D5891*
ID_OUI_FROM_DATABASE=neocontrol soluções em automação
@@ -62000,6 +63857,9 @@ OUI:70B3D5896*
OUI:70B3D5897*
ID_OUI_FROM_DATABASE=EFG CZ spol. s r.o.
+OUI:70B3D589A*
+ ID_OUI_FROM_DATABASE=Algodue Elettronica Srl
+
OUI:70B3D589B*
ID_OUI_FROM_DATABASE=ControlWorks, Inc.
@@ -62030,6 +63890,9 @@ OUI:70B3D58AC*
OUI:70B3D58AD*
ID_OUI_FROM_DATABASE=Global Communications Technology LLC
+OUI:70B3D58AE*
+ ID_OUI_FROM_DATABASE=FARECO
+
OUI:70B3D58B0*
ID_OUI_FROM_DATABASE=IES S.r.l.
@@ -62051,6 +63914,9 @@ OUI:70B3D58B7*
OUI:70B3D58B9*
ID_OUI_FROM_DATABASE=Toptech Systems, Inc.
+OUI:70B3D58BB*
+ ID_OUI_FROM_DATABASE=KST technology
+
OUI:70B3D58BE*
ID_OUI_FROM_DATABASE=Connoiseur Electronics Private Limited
@@ -62078,6 +63944,9 @@ OUI:70B3D58CA*
OUI:70B3D58CB*
ID_OUI_FROM_DATABASE=WELT Corporation
+OUI:70B3D58CD*
+ ID_OUI_FROM_DATABASE=EA Elektroautomatik GmbH & Co. KG
+
OUI:70B3D58CE*
ID_OUI_FROM_DATABASE=CORES Corporation
@@ -62090,12 +63959,18 @@ OUI:70B3D58D0*
OUI:70B3D58D3*
ID_OUI_FROM_DATABASE=PERFORMANCE CONTROLS, INC.
+OUI:70B3D58D7*
+ ID_OUI_FROM_DATABASE=Schneider Electric Motion USA
+
OUI:70B3D58D8*
ID_OUI_FROM_DATABASE=VNG Corporation
OUI:70B3D58D9*
ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
+OUI:70B3D58DA*
+ ID_OUI_FROM_DATABASE=MicroElectronics System Co.Ltd
+
OUI:70B3D58DB*
ID_OUI_FROM_DATABASE=Kratos Analytical Ltd
@@ -62108,6 +63983,9 @@ OUI:70B3D58E0*
OUI:70B3D58E1*
ID_OUI_FROM_DATABASE=WoKa-Elektronik GmbH
+OUI:70B3D58E2*
+ ID_OUI_FROM_DATABASE=Zhiye Electronics Co., Ltd.
+
OUI:70B3D58E3*
ID_OUI_FROM_DATABASE=DORLET SAU
@@ -62216,6 +64094,9 @@ OUI:70B3D591A*
OUI:70B3D591B*
ID_OUI_FROM_DATABASE=Dolotron d.o.o.
+OUI:70B3D591C*
+ ID_OUI_FROM_DATABASE=Alere Technologies AS
+
OUI:70B3D591E*
ID_OUI_FROM_DATABASE=Creotech Instruments S.A.
@@ -62234,6 +64115,9 @@ OUI:70B3D5924*
OUI:70B3D5925*
ID_OUI_FROM_DATABASE=Diamante Lighting Srl
+OUI:70B3D5926*
+ ID_OUI_FROM_DATABASE=Advice
+
OUI:70B3D5927*
ID_OUI_FROM_DATABASE=LG Electronics
@@ -62270,6 +64154,9 @@ OUI:70B3D5935*
OUI:70B3D5936*
ID_OUI_FROM_DATABASE=FARO TECHNOLOGIES, INC.
+OUI:70B3D5937*
+ ID_OUI_FROM_DATABASE=TATTILE SRL
+
OUI:70B3D5938*
ID_OUI_FROM_DATABASE=JETI Technische Instrumente GmbH
@@ -62300,12 +64187,21 @@ OUI:70B3D5947*
OUI:70B3D5948*
ID_OUI_FROM_DATABASE=VISION SYSTEMS AURTOMOTIVE (SAFETY TECH)
+OUI:70B3D5949*
+ ID_OUI_FROM_DATABASE=National Radio & Telecommunication Corporation - NRTC
+
OUI:70B3D594A*
ID_OUI_FROM_DATABASE=SHENZHEN WISEWING INTERNET TECHNOLOGY CO.,LTD
+OUI:70B3D594B*
+ ID_OUI_FROM_DATABASE=RF Code
+
OUI:70B3D594D*
ID_OUI_FROM_DATABASE=SEASON DESIGN TECHNOLOGY
+OUI:70B3D594E*
+ ID_OUI_FROM_DATABASE=BP Lubricants USA, Inc.
+
OUI:70B3D594F*
ID_OUI_FROM_DATABASE=MART NETWORK SOLUTIONS LTD
@@ -62357,6 +64253,9 @@ OUI:70B3D5961*
OUI:70B3D5963*
ID_OUI_FROM_DATABASE=Triax A/S
+OUI:70B3D5966*
+ ID_OUI_FROM_DATABASE=dA Tomato Limited
+
OUI:70B3D5967*
ID_OUI_FROM_DATABASE=TATTILE SRL
@@ -62372,9 +64271,15 @@ OUI:70B3D596B*
OUI:70B3D596D*
ID_OUI_FROM_DATABASE=MSB Elektronik und Gerätebau GmbH
+OUI:70B3D596E*
+ ID_OUI_FROM_DATABASE=Myostat Motion Control Inc
+
OUI:70B3D596F*
ID_OUI_FROM_DATABASE=4CAM GmbH
+OUI:70B3D5972*
+ ID_OUI_FROM_DATABASE=AixControl GmbH
+
OUI:70B3D5973*
ID_OUI_FROM_DATABASE=Autonomic Controls, Inc.
@@ -62387,6 +64292,15 @@ OUI:70B3D5975*
OUI:70B3D5976*
ID_OUI_FROM_DATABASE=Atonarp Micro-Systems India Pvt. Ltd.
+OUI:70B3D5977*
+ ID_OUI_FROM_DATABASE=Engage Technologies
+
+OUI:70B3D5978*
+ ID_OUI_FROM_DATABASE=Satixfy Israel Ltd.
+
+OUI:70B3D597A*
+ ID_OUI_FROM_DATABASE=Orion Corporation
+
OUI:70B3D597C*
ID_OUI_FROM_DATABASE=Nu-Tek Power Controls and Automation
@@ -62411,6 +64325,9 @@ OUI:70B3D5987*
OUI:70B3D5989*
ID_OUI_FROM_DATABASE=DCNS
+OUI:70B3D598A*
+ ID_OUI_FROM_DATABASE=vision systems safety tech
+
OUI:70B3D598B*
ID_OUI_FROM_DATABASE=Richard Paul Russell Ltd
@@ -62423,6 +64340,9 @@ OUI:70B3D598E*
OUI:70B3D598F*
ID_OUI_FROM_DATABASE=Spaceflight Industries
+OUI:70B3D5990*
+ ID_OUI_FROM_DATABASE=Energy Wall
+
OUI:70B3D5991*
ID_OUI_FROM_DATABASE=Javasparrow Inc.
@@ -62462,6 +64382,9 @@ OUI:70B3D59A0*
OUI:70B3D59A1*
ID_OUI_FROM_DATABASE=ITS Industrial Turbine Services GmbH
+OUI:70B3D59A2*
+ ID_OUI_FROM_DATABASE=O-Net Communications(Shenzhen)Limited
+
OUI:70B3D59A7*
ID_OUI_FROM_DATABASE=Honeywell
@@ -62498,6 +64421,9 @@ OUI:70B3D59B6*
OUI:70B3D59B8*
ID_OUI_FROM_DATABASE=Loma Systems
+OUI:70B3D59B9*
+ ID_OUI_FROM_DATABASE=Aethera Technologies
+
OUI:70B3D59BA*
ID_OUI_FROM_DATABASE=ATIM Radiocommunication
@@ -62510,6 +64436,12 @@ OUI:70B3D59C0*
OUI:70B3D59C1*
ID_OUI_FROM_DATABASE=Zeroplus Technology Co.,Ltd.
+OUI:70B3D59C4*
+ ID_OUI_FROM_DATABASE=aelettronica group srl
+
+OUI:70B3D59C5*
+ ID_OUI_FROM_DATABASE=LINEAGE POWER PVT LTD.,
+
OUI:70B3D59C6*
ID_OUI_FROM_DATABASE=Overspeed SARL
@@ -62549,6 +64481,9 @@ OUI:70B3D59D4*
OUI:70B3D59D5*
ID_OUI_FROM_DATABASE=Southern Tier Technologies
+OUI:70B3D59D6*
+ ID_OUI_FROM_DATABASE=Crown Solar Power Fencing Systems
+
OUI:70B3D59D7*
ID_OUI_FROM_DATABASE=KM OptoElektronik GmbH
@@ -62567,6 +64502,9 @@ OUI:70B3D59DD*
OUI:70B3D59DE*
ID_OUI_FROM_DATABASE=System 11 Sp. z o.o.
+OUI:70B3D59DF*
+ ID_OUI_FROM_DATABASE=DOBE Computing
+
OUI:70B3D59E0*
ID_OUI_FROM_DATABASE=ES Industrial Systems Co., Ltd.
@@ -62591,6 +64529,9 @@ OUI:70B3D59EC*
OUI:70B3D59ED*
ID_OUI_FROM_DATABASE=Benchmark Electronics BV
+OUI:70B3D59EE*
+ ID_OUI_FROM_DATABASE=Private
+
OUI:70B3D59EF*
ID_OUI_FROM_DATABASE=Cottonwood Creek Technologies, Inc.
@@ -62618,12 +64559,18 @@ OUI:70B3D59F6*
OUI:70B3D59F8*
ID_OUI_FROM_DATABASE=Asymmetric Technologies
+OUI:70B3D59F9*
+ ID_OUI_FROM_DATABASE=Fluid Components Intl
+
OUI:70B3D59FA*
ID_OUI_FROM_DATABASE=Ideas srl
OUI:70B3D59FB*
ID_OUI_FROM_DATABASE=Unicom Global, Inc.
+OUI:70B3D59FD*
+ ID_OUI_FROM_DATABASE=amakidenki
+
OUI:70B3D5A00*
ID_OUI_FROM_DATABASE=ATX NETWORKS LTD
@@ -62720,6 +64667,9 @@ OUI:70B3D5A2A*
OUI:70B3D5A2C*
ID_OUI_FROM_DATABASE=TLV CO., LTD.
+OUI:70B3D5A2D*
+ ID_OUI_FROM_DATABASE=Project Service S.r.l.
+
OUI:70B3D5A2E*
ID_OUI_FROM_DATABASE=Kokam Co., Ltd
@@ -62825,9 +64775,15 @@ OUI:70B3D5A5B*
OUI:70B3D5A5C*
ID_OUI_FROM_DATABASE=Molekule
+OUI:70B3D5A5D*
+ ID_OUI_FROM_DATABASE=Position Imaging
+
OUI:70B3D5A5E*
ID_OUI_FROM_DATABASE=ConectaIP Tecnologia S.L.
+OUI:70B3D5A5F*
+ ID_OUI_FROM_DATABASE=Daatrics LTD
+
OUI:70B3D5A62*
ID_OUI_FROM_DATABASE=Environexus
@@ -62849,12 +64805,18 @@ OUI:70B3D5A6E*
OUI:70B3D5A6F*
ID_OUI_FROM_DATABASE=8Cups
+OUI:70B3D5A71*
+ ID_OUI_FROM_DATABASE=Samwell International Inc
+
OUI:70B3D5A72*
ID_OUI_FROM_DATABASE=Business Marketers Group, Inc.
OUI:70B3D5A73*
ID_OUI_FROM_DATABASE=MobiPromo
+OUI:70B3D5A75*
+ ID_OUI_FROM_DATABASE=Taejin InforTech
+
OUI:70B3D5A76*
ID_OUI_FROM_DATABASE=Pietro Fiorentini
@@ -62864,6 +64826,9 @@ OUI:70B3D5A78*
OUI:70B3D5A7A*
ID_OUI_FROM_DATABASE=Fluid Management Technology
+OUI:70B3D5A7B*
+ ID_OUI_FROM_DATABASE=SmartSafe
+
OUI:70B3D5A7C*
ID_OUI_FROM_DATABASE=Transelektronik Messgeräte GmbH
@@ -62876,6 +64841,9 @@ OUI:70B3D5A81*
OUI:70B3D5A82*
ID_OUI_FROM_DATABASE=Telefrank GmbH
+OUI:70B3D5A84*
+ ID_OUI_FROM_DATABASE=SOREL GmbH Mikroelektronik
+
OUI:70B3D5A85*
ID_OUI_FROM_DATABASE=exceet electronics GesmbH
@@ -62930,6 +64898,12 @@ OUI:70B3D5A99*
OUI:70B3D5A9A*
ID_OUI_FROM_DATABASE=Amphenol Advanced Sensors
+OUI:70B3D5A9B*
+ ID_OUI_FROM_DATABASE=OSMOZIS
+
+OUI:70B3D5A9C*
+ ID_OUI_FROM_DATABASE=Veo Technologies
+
OUI:70B3D5A9D*
ID_OUI_FROM_DATABASE=VITEC MULTIMEDIA
@@ -62954,6 +64928,9 @@ OUI:70B3D5AA3*
OUI:70B3D5AA4*
ID_OUI_FROM_DATABASE=Pullnet Technology,S.L.
+OUI:70B3D5AA5*
+ ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
+
OUI:70B3D5AA6*
ID_OUI_FROM_DATABASE=Proximus
@@ -62963,6 +64940,9 @@ OUI:70B3D5AA7*
OUI:70B3D5AA8*
ID_OUI_FROM_DATABASE=West-Com Nurse Call Systems, Inc.
+OUI:70B3D5AA9*
+ ID_OUI_FROM_DATABASE=Datamars SA
+
OUI:70B3D5AAA*
ID_OUI_FROM_DATABASE=Xemex NV
@@ -62981,6 +64961,9 @@ OUI:70B3D5AAF*
OUI:70B3D5AB0*
ID_OUI_FROM_DATABASE=OSR R&D ISRAEL LTD
+OUI:70B3D5AB2*
+ ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L.
+
OUI:70B3D5AB3*
ID_OUI_FROM_DATABASE=MICAS AG
@@ -63002,6 +64985,9 @@ OUI:70B3D5AB9*
OUI:70B3D5ABA*
ID_OUI_FROM_DATABASE=CL International
+OUI:70B3D5ABB*
+ ID_OUI_FROM_DATABASE=David Horn Communications Ltd
+
OUI:70B3D5ABC*
ID_OUI_FROM_DATABASE=BKM-Micronic Richtfunkanlagen GmbH
@@ -63035,12 +65021,18 @@ OUI:70B3D5AC8*
OUI:70B3D5AC9*
ID_OUI_FROM_DATABASE=Trinity Solutions LLC
+OUI:70B3D5ACA*
+ ID_OUI_FROM_DATABASE=Tecnint HTE SRL
+
OUI:70B3D5ACB*
ID_OUI_FROM_DATABASE=TATTILE SRL
OUI:70B3D5ACD*
ID_OUI_FROM_DATABASE=CRDE
+OUI:70B3D5ACF*
+ ID_OUI_FROM_DATABASE=APG Cash Drawer, LLC
+
OUI:70B3D5AD1*
ID_OUI_FROM_DATABASE=Sensile Technologies SA
@@ -63062,6 +65054,9 @@ OUI:70B3D5ADA*
OUI:70B3D5ADB*
ID_OUI_FROM_DATABASE=RF Code
+OUI:70B3D5ADC*
+ ID_OUI_FROM_DATABASE=SODAQ
+
OUI:70B3D5ADD*
ID_OUI_FROM_DATABASE=GHL Systems Berhad
@@ -63086,6 +65081,9 @@ OUI:70B3D5AE3*
OUI:70B3D5AE5*
ID_OUI_FROM_DATABASE=BeatCraft, Inc.
+OUI:70B3D5AE6*
+ ID_OUI_FROM_DATABASE=Ya Batho Trading (Pty) Ltd
+
OUI:70B3D5AE7*
ID_OUI_FROM_DATABASE=E-T-A Elektrotechnische Apparate GmbH
@@ -63098,6 +65096,9 @@ OUI:70B3D5AEA*
OUI:70B3D5AEB*
ID_OUI_FROM_DATABASE=Association Romandix
+OUI:70B3D5AEC*
+ ID_OUI_FROM_DATABASE=Paratec Ltd.
+
OUI:70B3D5AEE*
ID_OUI_FROM_DATABASE=DiTEST Fahrzeugdiagnose GmbH
@@ -63128,6 +65129,9 @@ OUI:70B3D5AF6*
OUI:70B3D5AF7*
ID_OUI_FROM_DATABASE=DimoSystems BV
+OUI:70B3D5AF8*
+ ID_OUI_FROM_DATABASE=boekel
+
OUI:70B3D5AF9*
ID_OUI_FROM_DATABASE=Critical Link LLC
@@ -63167,6 +65171,12 @@ OUI:70B3D5B0B*
OUI:70B3D5B0C*
ID_OUI_FROM_DATABASE=Vigilate srl
+OUI:70B3D5B0F*
+ ID_OUI_FROM_DATABASE=merkur Funksysteme AG
+
+OUI:70B3D5B10*
+ ID_OUI_FROM_DATABASE=Zumbach Electronic AG
+
OUI:70B3D5B11*
ID_OUI_FROM_DATABASE=CAB S.R.L.
@@ -63221,6 +65231,9 @@ OUI:70B3D5B2A*
OUI:70B3D5B2B*
ID_OUI_FROM_DATABASE=Vtron Pty Ltd
+OUI:70B3D5B2D*
+ ID_OUI_FROM_DATABASE=Plexus
+
OUI:70B3D5B2E*
ID_OUI_FROM_DATABASE=Green Access Ltd
@@ -63242,6 +65255,9 @@ OUI:70B3D5B35*
OUI:70B3D5B37*
ID_OUI_FROM_DATABASE=CODEC Co., Ltd.
+OUI:70B3D5B38*
+ ID_OUI_FROM_DATABASE=GoTrustID Inc.
+
OUI:70B3D5B39*
ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
@@ -63272,15 +65288,24 @@ OUI:70B3D5B43*
OUI:70B3D5B44*
ID_OUI_FROM_DATABASE=ENTEC Electric & Electronic Co., LTD.
+OUI:70B3D5B46*
+ ID_OUI_FROM_DATABASE=FAS Electronics (Fujian) Co.,LTD.
+
OUI:70B3D5B47*
ID_OUI_FROM_DATABASE=DSIT Solutions LTD
OUI:70B3D5B48*
ID_OUI_FROM_DATABASE=DWQ Informatikai Tanacsado es Vezerlestechnikai KFT
+OUI:70B3D5B49*
+ ID_OUI_FROM_DATABASE=ANALOGICS TECH INDIA LTD
+
OUI:70B3D5B4A*
ID_OUI_FROM_DATABASE=MEDEX
+OUI:70B3D5B4D*
+ ID_OUI_FROM_DATABASE=Avidbots Corporation
+
OUI:70B3D5B51*
ID_OUI_FROM_DATABASE=Critical Link LLC
@@ -63299,30 +65324,51 @@ OUI:70B3D5B59*
OUI:70B3D5B5C*
ID_OUI_FROM_DATABASE=Prozess Technologie
+OUI:70B3D5B5F*
+ ID_OUI_FROM_DATABASE=CRDMDEVEOPPEMENTS
+
+OUI:70B3D5B60*
+ ID_OUI_FROM_DATABASE=ZAO ZEO
+
OUI:70B3D5B62*
ID_OUI_FROM_DATABASE=Sakura Seiki Co.,Ltd.
OUI:70B3D5B64*
ID_OUI_FROM_DATABASE=OSUNG LST CO.,LTD.
+OUI:70B3D5B66*
+ ID_OUI_FROM_DATABASE=Silent Gliss International Ltd
+
OUI:70B3D5B67*
ID_OUI_FROM_DATABASE=RedWave Labs Ltd
OUI:70B3D5B6A*
ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
+OUI:70B3D5B6B*
+ ID_OUI_FROM_DATABASE=Cambria Corporation
+
OUI:70B3D5B6C*
ID_OUI_FROM_DATABASE=GHM-Messtechnik GmbH (Standort IMTRON)
OUI:70B3D5B6D*
ID_OUI_FROM_DATABASE=Movis
+OUI:70B3D5B6E*
+ ID_OUI_FROM_DATABASE=Edgeware AB
+
+OUI:70B3D5B71*
+ ID_OUI_FROM_DATABASE=Private
+
OUI:70B3D5B72*
ID_OUI_FROM_DATABASE=UB330.net d.o.o.
OUI:70B3D5B74*
ID_OUI_FROM_DATABASE=OnYield Inc Ltd
+OUI:70B3D5B76*
+ ID_OUI_FROM_DATABASE=ATL-SD
+
OUI:70B3D5B77*
ID_OUI_FROM_DATABASE=Motec Pty Ltd
@@ -63339,11 +65385,14 @@ OUI:70B3D5B7D*
ID_OUI_FROM_DATABASE=LOGIX ITS Inc
OUI:70B3D5B7E*
- ID_OUI_FROM_DATABASE=Elbit Systems of America - Fort Worth Operations
+ ID_OUI_FROM_DATABASE=Elbit Systems of America
OUI:70B3D5B7F*
ID_OUI_FROM_DATABASE=JSK System
+OUI:70B3D5B80*
+ ID_OUI_FROM_DATABASE=BIGHOUSE.,INC.
+
OUI:70B3D5B81*
ID_OUI_FROM_DATABASE=Instro Precision Limited
@@ -63371,6 +65420,9 @@ OUI:70B3D5B8C*
OUI:70B3D5B8D*
ID_OUI_FROM_DATABASE=JungwooEng Co., Ltd
+OUI:70B3D5B8E*
+ ID_OUI_FROM_DATABASE=UR FOG S.R.L.
+
OUI:70B3D5B8F*
ID_OUI_FROM_DATABASE=Assembly Contracts Ltd
@@ -63392,6 +65444,9 @@ OUI:70B3D5B98*
OUI:70B3D5B99*
ID_OUI_FROM_DATABASE=DomoSafety S.A.
+OUI:70B3D5B9A*
+ ID_OUI_FROM_DATABASE=Potter Electric Signal Co. LLC
+
OUI:70B3D5B9B*
ID_OUI_FROM_DATABASE=Elektronik Art
@@ -63437,6 +65492,9 @@ OUI:70B3D5BAE*
OUI:70B3D5BAF*
ID_OUI_FROM_DATABASE=SYS TEC electronic GmbH
+OUI:70B3D5BB0*
+ ID_OUI_FROM_DATABASE=WICELL TECHNOLOGY
+
OUI:70B3D5BB2*
ID_OUI_FROM_DATABASE=Mettler Toledo Hi Speed
@@ -63446,6 +65504,9 @@ OUI:70B3D5BB3*
OUI:70B3D5BB4*
ID_OUI_FROM_DATABASE=Integritech
+OUI:70B3D5BB6*
+ ID_OUI_FROM_DATABASE=Franke Aquarotter GmbH
+
OUI:70B3D5BB7*
ID_OUI_FROM_DATABASE=Innoflight, Inc.
@@ -63455,6 +65516,9 @@ OUI:70B3D5BB8*
OUI:70B3D5BB9*
ID_OUI_FROM_DATABASE=KOSMEK.Ltd
+OUI:70B3D5BBA*
+ ID_OUI_FROM_DATABASE=Samriddi Automations Pvt. Ltd.
+
OUI:70B3D5BBD*
ID_OUI_FROM_DATABASE=Providius Corp
@@ -63470,6 +65534,12 @@ OUI:70B3D5BC1*
OUI:70B3D5BC2*
ID_OUI_FROM_DATABASE=DWEWOONG ELECTRIC Co., Ltd.
+OUI:70B3D5BC3*
+ ID_OUI_FROM_DATABASE=eWireless
+
+OUI:70B3D5BC4*
+ ID_OUI_FROM_DATABASE=Digital Media Professionals
+
OUI:70B3D5BC6*
ID_OUI_FROM_DATABASE=Hatteland Display AS
@@ -63482,9 +65552,15 @@ OUI:70B3D5BCB*
OUI:70B3D5BCC*
ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
+OUI:70B3D5BCD*
+ ID_OUI_FROM_DATABASE=Sasken Technologies Ltd
+
OUI:70B3D5BCE*
ID_OUI_FROM_DATABASE=YAWATA ELECTRIC INDUSTRIAL CO.,LTD.
+OUI:70B3D5BCF*
+ ID_OUI_FROM_DATABASE=APG Cash Drawer, LLC
+
OUI:70B3D5BD1*
ID_OUI_FROM_DATABASE=CableLabs
@@ -63497,6 +65573,12 @@ OUI:70B3D5BD3*
OUI:70B3D5BD5*
ID_OUI_FROM_DATABASE=Synics AG
+OUI:70B3D5BD6*
+ ID_OUI_FROM_DATABASE=Consarc Corporation
+
+OUI:70B3D5BD8*
+ ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
+
OUI:70B3D5BD9*
ID_OUI_FROM_DATABASE=SolwayTech
@@ -63626,6 +65708,9 @@ OUI:70B3D5C1C*
OUI:70B3D5C1D*
ID_OUI_FROM_DATABASE=Kranze Technology Solutions
+OUI:70B3D5C1F*
+ ID_OUI_FROM_DATABASE=Behr Technologies Inc
+
OUI:70B3D5C20*
ID_OUI_FROM_DATABASE=Mipot S.p.a.
@@ -63636,7 +65721,7 @@ OUI:70B3D5C22*
ID_OUI_FROM_DATABASE=Skyriver Communications Inc.
OUI:70B3D5C24*
- ID_OUI_FROM_DATABASE=Elbit Systems of America - Fort Worth Operations
+ ID_OUI_FROM_DATABASE=Elbit Systems of America
OUI:70B3D5C25*
ID_OUI_FROM_DATABASE=speedsignal GmbH
@@ -63647,12 +65732,18 @@ OUI:70B3D5C26*
OUI:70B3D5C27*
ID_OUI_FROM_DATABASE=GD Mission Systems
+OUI:70B3D5C29*
+ ID_OUI_FROM_DATABASE=SOFTLAND INDIA LTD
+
OUI:70B3D5C2A*
ID_OUI_FROM_DATABASE=Array Telepresence
OUI:70B3D5C2C*
ID_OUI_FROM_DATABASE=Dromont S.p.A.
+OUI:70B3D5C2D*
+ ID_OUI_FROM_DATABASE=Ensotech Limited
+
OUI:70B3D5C2E*
ID_OUI_FROM_DATABASE=Triax A/S
@@ -63695,6 +65786,9 @@ OUI:70B3D5C3E*
OUI:70B3D5C3F*
ID_OUI_FROM_DATABASE=Code Blue Corporation
+OUI:70B3D5C40*
+ ID_OUI_FROM_DATABASE=HongSeok Ltd.
+
OUI:70B3D5C41*
ID_OUI_FROM_DATABASE=Merlin CSI
@@ -63710,9 +65804,15 @@ OUI:70B3D5C45*
OUI:70B3D5C49*
ID_OUI_FROM_DATABASE=BTG Instruments AB
+OUI:70B3D5C4A*
+ ID_OUI_FROM_DATABASE=TIAMA
+
OUI:70B3D5C4B*
ID_OUI_FROM_DATABASE=ANKER-EAST
+OUI:70B3D5C4C*
+ ID_OUI_FROM_DATABASE=VTC Digicom
+
OUI:70B3D5C4D*
ID_OUI_FROM_DATABASE=RADA Electronics Industries Ltd.
@@ -63785,6 +65885,9 @@ OUI:70B3D5C6E*
OUI:70B3D5C6F*
ID_OUI_FROM_DATABASE=nyantec GmbH
+OUI:70B3D5C70*
+ ID_OUI_FROM_DATABASE=Magnetek
+
OUI:70B3D5C73*
ID_OUI_FROM_DATABASE=C.D.N.CORPORATION
@@ -63839,6 +65942,9 @@ OUI:70B3D5C8C*
OUI:70B3D5C8D*
ID_OUI_FROM_DATABASE=KST technology
+OUI:70B3D5C8E*
+ ID_OUI_FROM_DATABASE=Coral Telecom Limited
+
OUI:70B3D5C8F*
ID_OUI_FROM_DATABASE=TRIDENT INFOSOL PVT LTD
@@ -63848,6 +65954,9 @@ OUI:70B3D5C91*
OUI:70B3D5C92*
ID_OUI_FROM_DATABASE=Unitro Fleischmann
+OUI:70B3D5C93*
+ ID_OUI_FROM_DATABASE=GMI Ltd
+
OUI:70B3D5C96*
ID_OUI_FROM_DATABASE=UNI DIMENXI SDN BHD
@@ -63858,7 +65967,7 @@ OUI:70B3D5C9B*
ID_OUI_FROM_DATABASE=Tieto Sweden AB
OUI:70B3D5C9D*
- ID_OUI_FROM_DATABASE=APG Cash Drawer
+ ID_OUI_FROM_DATABASE=APG Cash Drawer, LLC
OUI:70B3D5C9E*
ID_OUI_FROM_DATABASE=FUKUDA SANGYO CO., LTD.
@@ -63866,6 +65975,9 @@ OUI:70B3D5C9E*
OUI:70B3D5C9F*
ID_OUI_FROM_DATABASE=Triax A/S
+OUI:70B3D5CA1*
+ ID_OUI_FROM_DATABASE=Waldo System
+
OUI:70B3D5CA2*
ID_OUI_FROM_DATABASE=De Haardt bv
@@ -63911,6 +66023,9 @@ OUI:70B3D5CB7*
OUI:70B3D5CB8*
ID_OUI_FROM_DATABASE=Verti Tecnologia
+OUI:70B3D5CBA*
+ ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
+
OUI:70B3D5CBC*
ID_OUI_FROM_DATABASE=Procon Electronics Pty Ltd
@@ -63920,6 +66035,9 @@ OUI:70B3D5CBE*
OUI:70B3D5CC1*
ID_OUI_FROM_DATABASE=BEEcube Inc.
+OUI:70B3D5CC2*
+ ID_OUI_FROM_DATABASE=LSC Lighting Systems (Aust) Pty Ltd
+
OUI:70B3D5CC3*
ID_OUI_FROM_DATABASE=Fidalia Networks Inc
@@ -63974,6 +66092,9 @@ OUI:70B3D5CD9*
OUI:70B3D5CDA*
ID_OUI_FROM_DATABASE=VITEC
+OUI:70B3D5CDC*
+ ID_OUI_FROM_DATABASE=Dat-Con d.o.o.
+
OUI:70B3D5CDE*
ID_OUI_FROM_DATABASE=Multipure International
@@ -63989,6 +66110,9 @@ OUI:70B3D5CE2*
OUI:70B3D5CE3*
ID_OUI_FROM_DATABASE=Dalcnet srl
+OUI:70B3D5CE4*
+ ID_OUI_FROM_DATABASE=WAVES SYSTEM
+
OUI:70B3D5CE5*
ID_OUI_FROM_DATABASE=GridBridge Inc
@@ -64004,6 +66128,9 @@ OUI:70B3D5CEA*
OUI:70B3D5CED*
ID_OUI_FROM_DATABASE=Advanced Products Corporation Pte Ltd
+OUI:70B3D5CF0*
+ ID_OUI_FROM_DATABASE=SHENZHEN WITLINK CO.,LTD.
+
OUI:70B3D5CF1*
ID_OUI_FROM_DATABASE=LightDec GmbH & Co. KG
@@ -64031,6 +66158,12 @@ OUI:70B3D5CFE*
OUI:70B3D5CFF*
ID_OUI_FROM_DATABASE=DTECH Labs, Inc.
+OUI:70B3D5D00*
+ ID_OUI_FROM_DATABASE=DKI Technology Co., Ltd
+
+OUI:70B3D5D01*
+ ID_OUI_FROM_DATABASE=Vision4ce Ltd
+
OUI:70B3D5D05*
ID_OUI_FROM_DATABASE=Colmek
@@ -64064,6 +66197,9 @@ OUI:70B3D5D11*
OUI:70B3D5D12*
ID_OUI_FROM_DATABASE=FIDELTRONIK POLAND SP. Z O.O.
+OUI:70B3D5D1A*
+ ID_OUI_FROM_DATABASE=Monnit Corporation
+
OUI:70B3D5D1B*
ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
@@ -64091,6 +66227,9 @@ OUI:70B3D5D25*
OUI:70B3D5D26*
ID_OUI_FROM_DATABASE=MI Inc.
+OUI:70B3D5D28*
+ ID_OUI_FROM_DATABASE=Toshiba Electron Tubes & Devices Co., Ltd.
+
OUI:70B3D5D29*
ID_OUI_FROM_DATABASE=Sportzcast
@@ -64112,6 +66251,9 @@ OUI:70B3D5D32*
OUI:70B3D5D34*
ID_OUI_FROM_DATABASE=G-PHILOS CO.,LTD
+OUI:70B3D5D36*
+ ID_OUI_FROM_DATABASE=Insitu Inc.
+
OUI:70B3D5D37*
ID_OUI_FROM_DATABASE=Sicon srl
@@ -64166,6 +66308,12 @@ OUI:70B3D5D4D*
OUI:70B3D5D4E*
ID_OUI_FROM_DATABASE=FLSmidth
+OUI:70B3D5D4F*
+ ID_OUI_FROM_DATABASE=C-COM Satellite Systems Inc.
+
+OUI:70B3D5D50*
+ ID_OUI_FROM_DATABASE=GRIDSMART Technologies
+
OUI:70B3D5D51*
ID_OUI_FROM_DATABASE=Azcom Technology S.r.l.
@@ -64352,6 +66500,9 @@ OUI:70B3D5D9E*
OUI:70B3D5DA1*
ID_OUI_FROM_DATABASE=Qprel srl
+OUI:70B3D5DA3*
+ ID_OUI_FROM_DATABASE=Voleatech GmbH
+
OUI:70B3D5DA4*
ID_OUI_FROM_DATABASE=CRDE
@@ -64367,6 +66518,9 @@ OUI:70B3D5DA8*
OUI:70B3D5DAA*
ID_OUI_FROM_DATABASE=AmTote Australasia
+OUI:70B3D5DAB*
+ ID_OUI_FROM_DATABASE=SET Power Systems GmbH
+
OUI:70B3D5DAD*
ID_OUI_FROM_DATABASE=GD Mission Systems
@@ -64388,15 +66542,24 @@ OUI:70B3D5DB5*
OUI:70B3D5DB6*
ID_OUI_FROM_DATABASE=csintech
+OUI:70B3D5DB7*
+ ID_OUI_FROM_DATABASE=Pengo Technology Co., Ltd
+
OUI:70B3D5DB8*
ID_OUI_FROM_DATABASE=SISTEM SA
+OUI:70B3D5DBE*
+ ID_OUI_FROM_DATABASE=Hiber
+
OUI:70B3D5DBF*
ID_OUI_FROM_DATABASE=Infodev Electronic Designers Intl.
OUI:70B3D5DC0*
ID_OUI_FROM_DATABASE=ATEME
+OUI:70B3D5DC2*
+ ID_OUI_FROM_DATABASE=SwineTech, Inc.
+
OUI:70B3D5DC5*
ID_OUI_FROM_DATABASE=Excel Medical Electronics LLC
@@ -64424,6 +66587,12 @@ OUI:70B3D5DCF*
OUI:70B3D5DD1*
ID_OUI_FROM_DATABASE=em-tec GmbH
+OUI:70B3D5DD2*
+ ID_OUI_FROM_DATABASE=Insitu, Inc
+
+OUI:70B3D5DD5*
+ ID_OUI_FROM_DATABASE=Cooltera Limited
+
OUI:70B3D5DD7*
ID_OUI_FROM_DATABASE=DETECT Australia
@@ -64451,6 +66620,9 @@ OUI:70B3D5DE2*
OUI:70B3D5DE4*
ID_OUI_FROM_DATABASE=MAVILI ELEKTRONIK TIC. VE SAN. A.S.
+OUI:70B3D5DE5*
+ ID_OUI_FROM_DATABASE=ASML
+
OUI:70B3D5DE6*
ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
@@ -64484,6 +66656,9 @@ OUI:70B3D5DF2*
OUI:70B3D5DF3*
ID_OUI_FROM_DATABASE=SPC Bioclinicum
+OUI:70B3D5DF5*
+ ID_OUI_FROM_DATABASE=Beijing Huanyu Zhilian Science &Technology Co., Ltd.
+
OUI:70B3D5DF6*
ID_OUI_FROM_DATABASE=Tiab Limited
@@ -64526,6 +66701,9 @@ OUI:70B3D5E09*
OUI:70B3D5E0B*
ID_OUI_FROM_DATABASE=ENTEC Electric & Electronic Co., LTD.
+OUI:70B3D5E0C*
+ ID_OUI_FROM_DATABASE=Communication Systems Solutions
+
OUI:70B3D5E0D*
ID_OUI_FROM_DATABASE=Sigma Connectivity AB
@@ -64536,7 +66714,7 @@ OUI:70B3D5E16*
ID_OUI_FROM_DATABASE=China Entropy Co., Ltd.
OUI:70B3D5E17*
- ID_OUI_FROM_DATABASE=Private
+ ID_OUI_FROM_DATABASE=SA Photonics
OUI:70B3D5E18*
ID_OUI_FROM_DATABASE=Plasmapp Co.,Ltd.
@@ -64550,6 +66728,9 @@ OUI:70B3D5E1B*
OUI:70B3D5E1C*
ID_OUI_FROM_DATABASE=Xcenter AS
+OUI:70B3D5E1F*
+ ID_OUI_FROM_DATABASE=THETA432
+
OUI:70B3D5E20*
ID_OUI_FROM_DATABASE=Signature Control Systems, LLC.
@@ -64583,6 +66764,9 @@ OUI:70B3D5E2E*
OUI:70B3D5E30*
ID_OUI_FROM_DATABASE=QUISS AG
+OUI:70B3D5E32*
+ ID_OUI_FROM_DATABASE=HERUTU ELECTRONICS CORPORATION
+
OUI:70B3D5E35*
ID_OUI_FROM_DATABASE=Nanospeed Technologies Limited
@@ -64598,6 +66782,9 @@ OUI:70B3D5E3A*
OUI:70B3D5E3B*
ID_OUI_FROM_DATABASE=ComNav Technology Ltd.
+OUI:70B3D5E3C*
+ ID_OUI_FROM_DATABASE=Independent Project Engineering Lmited
+
OUI:70B3D5E3D*
ID_OUI_FROM_DATABASE=Leo Bodnar Electronics Ltd
@@ -64607,12 +66794,18 @@ OUI:70B3D5E3E*
OUI:70B3D5E3F*
ID_OUI_FROM_DATABASE=BESTCODE LLC
+OUI:70B3D5E40*
+ ID_OUI_FROM_DATABASE=Siemens Mobility GmbH - MO TI SPA
+
OUI:70B3D5E43*
ID_OUI_FROM_DATABASE=SL Audio A/S
OUI:70B3D5E45*
ID_OUI_FROM_DATABASE=Momentum Data Systems
+OUI:70B3D5E47*
+ ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH
+
OUI:70B3D5E48*
ID_OUI_FROM_DATABASE=TDI. Co., LTD
@@ -64655,6 +66848,9 @@ OUI:70B3D5E58*
OUI:70B3D5E59*
ID_OUI_FROM_DATABASE=Fracarro srl
+OUI:70B3D5E5D*
+ ID_OUI_FROM_DATABASE=Boffins Technologies AB
+
OUI:70B3D5E5E*
ID_OUI_FROM_DATABASE=Critical Link LLC
@@ -64667,6 +66863,9 @@ OUI:70B3D5E67*
OUI:70B3D5E69*
ID_OUI_FROM_DATABASE=Fire4 Systems UK Ltd
+OUI:70B3D5E6A*
+ ID_OUI_FROM_DATABASE=MAC Solutions (UK) Ltd
+
OUI:70B3D5E6C*
ID_OUI_FROM_DATABASE=Fusar Technologies inc
@@ -64694,6 +66893,12 @@ OUI:70B3D5E76*
OUI:70B3D5E77*
ID_OUI_FROM_DATABASE=OPTIX JSC
+OUI:70B3D5E78*
+ ID_OUI_FROM_DATABASE=Camwell India LLP
+
+OUI:70B3D5E79*
+ ID_OUI_FROM_DATABASE=Acrodea, Inc.
+
OUI:70B3D5E7A*
ID_OUI_FROM_DATABASE=ART SPA
@@ -64712,6 +66917,9 @@ OUI:70B3D5E7E*
OUI:70B3D5E82*
ID_OUI_FROM_DATABASE=RF Track
+OUI:70B3D5E84*
+ ID_OUI_FROM_DATABASE=ENTEC Electric & Electronic Co., LTD.
+
OUI:70B3D5E85*
ID_OUI_FROM_DATABASE=Explorer Inc.
@@ -64721,6 +66929,9 @@ OUI:70B3D5E86*
OUI:70B3D5E88*
ID_OUI_FROM_DATABASE=Breas Medical AB
+OUI:70B3D5E8C*
+ ID_OUI_FROM_DATABASE=Fracarro srl
+
OUI:70B3D5E8E*
ID_OUI_FROM_DATABASE=Macnica Technology
@@ -64739,6 +66950,9 @@ OUI:70B3D5E92*
OUI:70B3D5E93*
ID_OUI_FROM_DATABASE=ECON Technology Co.Ltd
+OUI:70B3D5E94*
+ ID_OUI_FROM_DATABASE=Lumiplan Duhamel
+
OUI:70B3D5E95*
ID_OUI_FROM_DATABASE=BroadSoft Inc
@@ -64766,6 +66980,9 @@ OUI:70B3D5E9E*
OUI:70B3D5EA0*
ID_OUI_FROM_DATABASE=PARK24
+OUI:70B3D5EA1*
+ ID_OUI_FROM_DATABASE=Qntra Technology
+
OUI:70B3D5EA2*
ID_OUI_FROM_DATABASE=Transportal Solutions Ltd
@@ -64817,6 +67034,9 @@ OUI:70B3D5EB7*
OUI:70B3D5EB9*
ID_OUI_FROM_DATABASE=Thiel Audio Products Company, LLC
+OUI:70B3D5EBA*
+ ID_OUI_FROM_DATABASE=Last Mile Gear
+
OUI:70B3D5EBB*
ID_OUI_FROM_DATABASE=Beijing Wing ICT Technology Co., Ltd.
@@ -64832,6 +67052,9 @@ OUI:70B3D5EBE*
OUI:70B3D5EC1*
ID_OUI_FROM_DATABASE=Xafax Nederland bv
+OUI:70B3D5EC3*
+ ID_OUI_FROM_DATABASE=Virtual Control Systems Ltd
+
OUI:70B3D5EC6*
ID_OUI_FROM_DATABASE=ESII
@@ -64865,6 +67088,12 @@ OUI:70B3D5ED1*
OUI:70B3D5ED5*
ID_OUI_FROM_DATABASE=hangzhou battle link technology Co.,Ltd
+OUI:70B3D5ED7*
+ ID_OUI_FROM_DATABASE=WAVE
+
+OUI:70B3D5ED8*
+ ID_OUI_FROM_DATABASE=Transas Marine Limited
+
OUI:70B3D5EDB*
ID_OUI_FROM_DATABASE=Netfort Solutions
@@ -64874,6 +67103,9 @@ OUI:70B3D5EDC*
OUI:70B3D5EDD*
ID_OUI_FROM_DATABASE=Solar Network & Partners
+OUI:70B3D5EDE*
+ ID_OUI_FROM_DATABASE=Agrident GmbH
+
OUI:70B3D5EDF*
ID_OUI_FROM_DATABASE=GridNavigator
@@ -64889,6 +67121,12 @@ OUI:70B3D5EE4*
OUI:70B3D5EE5*
ID_OUI_FROM_DATABASE=Beijing Hzhytech Technology Co.Ltd
+OUI:70B3D5EE7*
+ ID_OUI_FROM_DATABASE=BLUE-SOLUTIONS CANADA INC.
+
+OUI:70B3D5EE8*
+ ID_OUI_FROM_DATABASE=robert juliat
+
OUI:70B3D5EEA*
ID_OUI_FROM_DATABASE=Dameca a/s
@@ -64946,6 +67184,9 @@ OUI:70B3D5F01*
OUI:70B3D5F03*
ID_OUI_FROM_DATABASE=GMI Ltd
+OUI:70B3D5F04*
+ ID_OUI_FROM_DATABASE=Scame Sistemi srl
+
OUI:70B3D5F05*
ID_OUI_FROM_DATABASE=Motomuto Aps
@@ -64982,9 +67223,15 @@ OUI:70B3D5F13*
OUI:70B3D5F14*
ID_OUI_FROM_DATABASE=SANYU SWITCH CO., LTD.
+OUI:70B3D5F16*
+ ID_OUI_FROM_DATABASE=BRS Sistemas Eletrônicos
+
OUI:70B3D5F17*
ID_OUI_FROM_DATABASE=VITEC
+OUI:70B3D5F18*
+ ID_OUI_FROM_DATABASE=HD Vision Systems GmbH
+
OUI:70B3D5F19*
ID_OUI_FROM_DATABASE=Vitro Technology Corporation
@@ -65000,12 +67247,21 @@ OUI:70B3D5F1E*
OUI:70B3D5F1F*
ID_OUI_FROM_DATABASE=HKC Limited
+OUI:70B3D5F21*
+ ID_OUI_FROM_DATABASE=dds
+
+OUI:70B3D5F22*
+ ID_OUI_FROM_DATABASE=Shengli Financial Software Development
+
OUI:70B3D5F24*
ID_OUI_FROM_DATABASE=Daavlin
OUI:70B3D5F25*
ID_OUI_FROM_DATABASE=JSC “Scientific Industrial Enterprise Rubin
+OUI:70B3D5F27*
+ ID_OUI_FROM_DATABASE=NIRIT- Xinwei Telecom Technology Co., Ltd.
+
OUI:70B3D5F2A*
ID_OUI_FROM_DATABASE=WIBOND Informationssysteme GmbH
@@ -65063,6 +67319,9 @@ OUI:70B3D5F4D*
OUI:70B3D5F4F*
ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L.
+OUI:70B3D5F51*
+ ID_OUI_FROM_DATABASE=IoT Routers Limited
+
OUI:70B3D5F52*
ID_OUI_FROM_DATABASE=Alere Technologies AS
@@ -65108,6 +67367,9 @@ OUI:70B3D5F63*
OUI:70B3D5F65*
ID_OUI_FROM_DATABASE=MARKUS LABS
+OUI:70B3D5F67*
+ ID_OUI_FROM_DATABASE=winsun AG
+
OUI:70B3D5F68*
ID_OUI_FROM_DATABASE=AL ZAJEL MODERN TELECOMM
@@ -65117,6 +67379,9 @@ OUI:70B3D5F6D*
OUI:70B3D5F6E*
ID_OUI_FROM_DATABASE=Streambox Inc
+OUI:70B3D5F6F*
+ ID_OUI_FROM_DATABASE=Smashtag Ltd
+
OUI:70B3D5F70*
ID_OUI_FROM_DATABASE=Honeywell
@@ -65147,6 +67412,9 @@ OUI:70B3D5F7A*
OUI:70B3D5F7B*
ID_OUI_FROM_DATABASE=KST technology
+OUI:70B3D5F7C*
+ ID_OUI_FROM_DATABASE=Private
+
OUI:70B3D5F7E*
ID_OUI_FROM_DATABASE=Alpha Elettronica s.r.l.
@@ -65162,9 +67430,15 @@ OUI:70B3D5F84*
OUI:70B3D5F85*
ID_OUI_FROM_DATABASE=Solystic
+OUI:70B3D5F86*
+ ID_OUI_FROM_DATABASE=Wireless Systems Solutions LLC
+
OUI:70B3D5F87*
ID_OUI_FROM_DATABASE=SHINWA INDUSTRIES, INC.
+OUI:70B3D5F88*
+ ID_OUI_FROM_DATABASE=ODAWARAKIKI AUTO-MACHINE MFG.CO.,LTD
+
OUI:70B3D5F8A*
ID_OUI_FROM_DATABASE=FRS GmbH & Co. KG
@@ -65204,6 +67478,9 @@ OUI:70B3D5F99*
OUI:70B3D5F9A*
ID_OUI_FROM_DATABASE=Krabbenhøft og Ingolfsson
+OUI:70B3D5F9B*
+ ID_OUI_FROM_DATABASE=EvoLogics GmbH
+
OUI:70B3D5F9C*
ID_OUI_FROM_DATABASE=SureFlap Ltd
@@ -65225,6 +67502,9 @@ OUI:70B3D5FA3*
OUI:70B3D5FA4*
ID_OUI_FROM_DATABASE=Energybox Limited
+OUI:70B3D5FA5*
+ ID_OUI_FROM_DATABASE=Shenzhen Hui Rui Tianyan Technology Co., Ltd.
+
OUI:70B3D5FA6*
ID_OUI_FROM_DATABASE=RFL Electronics, Inc.
@@ -65234,6 +67514,9 @@ OUI:70B3D5FA7*
OUI:70B3D5FAA*
ID_OUI_FROM_DATABASE=LogiM GmbH Software und Entwicklung
+OUI:70B3D5FAB*
+ ID_OUI_FROM_DATABASE=Open System Solutions Limited
+
OUI:70B3D5FAD*
ID_OUI_FROM_DATABASE=ARC Technology Solutions, LLC
@@ -65276,6 +67559,9 @@ OUI:70B3D5FBE*
OUI:70B3D5FBF*
ID_OUI_FROM_DATABASE=SenSys (Design Electronics Ltd)
+OUI:70B3D5FC0*
+ ID_OUI_FROM_DATABASE=CODESYSTEM Co.,Ltd
+
OUI:70B3D5FC1*
ID_OUI_FROM_DATABASE=InDiCor
@@ -65297,6 +67583,9 @@ OUI:70B3D5FC9*
OUI:70B3D5FCA*
ID_OUI_FROM_DATABASE=M2M Cybernetics Pvt Ltd
+OUI:70B3D5FCB*
+ ID_OUI_FROM_DATABASE=Tieline Research Pty Ltd
+
OUI:70B3D5FCC*
ID_OUI_FROM_DATABASE=DIgSILENT GmbH
@@ -65306,6 +67595,9 @@ OUI:70B3D5FCD*
OUI:70B3D5FCF*
ID_OUI_FROM_DATABASE=Acc+Ess Ltd
+OUI:70B3D5FD0*
+ ID_OUI_FROM_DATABASE=Alcohol Countermeasure Systems
+
OUI:70B3D5FD1*
ID_OUI_FROM_DATABASE=RedRat Ltd
@@ -65315,9 +67607,15 @@ OUI:70B3D5FD2*
OUI:70B3D5FD3*
ID_OUI_FROM_DATABASE=AKIS technologies
+OUI:70B3D5FD4*
+ ID_OUI_FROM_DATABASE=GETRALINE
+
OUI:70B3D5FD6*
ID_OUI_FROM_DATABASE=Visual Fan
+OUI:70B3D5FD7*
+ ID_OUI_FROM_DATABASE=Centum Adetel Group
+
OUI:70B3D5FD8*
ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
@@ -65327,6 +67625,9 @@ OUI:70B3D5FDA*
OUI:70B3D5FDB*
ID_OUI_FROM_DATABASE=Design SHIFT
+OUI:70B3D5FDD*
+ ID_OUI_FROM_DATABASE=Laser Imagineering Vertriebs GmbH
+
OUI:70B3D5FDE*
ID_OUI_FROM_DATABASE=AERONAUTICAL & GENERAL INSTRUMENTS LTD.
@@ -65363,6 +67664,12 @@ OUI:70B3D5FEB*
OUI:70B3D5FEC*
ID_OUI_FROM_DATABASE=Finder SpA
+OUI:70B3D5FED*
+ ID_OUI_FROM_DATABASE=Niron systems & Projects
+
+OUI:70B3D5FEE*
+ ID_OUI_FROM_DATABASE=Kawasaki Robot Service,Ltd.
+
OUI:70B3D5FEF*
ID_OUI_FROM_DATABASE=HANGZHOU HUALAN MICROELECTRONIQUE CO.,LTD
@@ -65414,9 +67721,18 @@ OUI:70B921*
OUI:70BAEF*
ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited
+OUI:70BBE9*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
+OUI:70BC10*
+ ID_OUI_FROM_DATABASE=Microsoft Corporation
+
OUI:70BF3E*
ID_OUI_FROM_DATABASE=Charles River Laboratories
+OUI:70BF92*
+ ID_OUI_FROM_DATABASE=GN Audio A/S
+
OUI:70C6AC*
ID_OUI_FROM_DATABASE=Bosch Automotive Aftermarket
@@ -65429,6 +67745,9 @@ OUI:70C833*
OUI:70C94E*
ID_OUI_FROM_DATABASE=Liteon Technology Corporation
+OUI:70C9C6*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:70CA4D*
ID_OUI_FROM_DATABASE=Shenzhen lnovance Technology Co.,Ltd.
@@ -65438,9 +67757,15 @@ OUI:70CA9B*
OUI:70CD60*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:70CD91*
+ ID_OUI_FROM_DATABASE=TERACOM TELEMATICA S.A
+
OUI:70D081*
ID_OUI_FROM_DATABASE=Beijing Netpower Technologies Inc.
+OUI:70D313*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:70D379*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -65510,6 +67835,9 @@ OUI:70E72C*
OUI:70E843*
ID_OUI_FROM_DATABASE=Beijing C&W Optical Communication Technology Co.,Ltd.
+OUI:70EA1A*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:70ECE4*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -65792,6 +68120,9 @@ OUI:743256*
OUI:743400*
ID_OUI_FROM_DATABASE=MTG Co., Ltd.
+OUI:74366D*
+ ID_OUI_FROM_DATABASE=Vodafone Italia S.p.A.
+
OUI:74372F*
ID_OUI_FROM_DATABASE=Tongfang Shenzhen Cloudcomputing Technology Co.,Ltd
@@ -65813,6 +68144,9 @@ OUI:743ECB*
OUI:7440BB*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:7440BE*
+ ID_OUI_FROM_DATABASE=LG Innotek
+
OUI:744401*
ID_OUI_FROM_DATABASE=NETGEAR
@@ -65828,6 +68162,9 @@ OUI:744AA4*
OUI:744BE9*
ID_OUI_FROM_DATABASE=EXPLORER HYPERTECH CO.,LTD
+OUI:744D28*
+ ID_OUI_FROM_DATABASE=Routerboard.com
+
OUI:744D79*
ID_OUI_FROM_DATABASE=Arrive Systems Inc.
@@ -65855,6 +68192,51 @@ OUI:745933*
OUI:745AAA*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:745BC50*
+ ID_OUI_FROM_DATABASE=IRS Systementwicklung GmbH
+
+OUI:745BC51*
+ ID_OUI_FROM_DATABASE=Beijing Inspiry Technology Co., Ltd.
+
+OUI:745BC52*
+ ID_OUI_FROM_DATABASE=SIGLENT TECHNOLOGIES CO., LTD.
+
+OUI:745BC53*
+ ID_OUI_FROM_DATABASE=OXON AG
+
+OUI:745BC54*
+ ID_OUI_FROM_DATABASE=uGrid Network Inc.
+
+OUI:745BC55*
+ ID_OUI_FROM_DATABASE=SpringCard
+
+OUI:745BC56*
+ ID_OUI_FROM_DATABASE=Yekani Manufacturing PTY Ltd
+
+OUI:745BC57*
+ ID_OUI_FROM_DATABASE=SHENZHEN ATX TECHNOLOGY CO.,LTD
+
+OUI:745BC58*
+ ID_OUI_FROM_DATABASE=EDOMO Systems GmbH
+
+OUI:745BC59*
+ ID_OUI_FROM_DATABASE=Haikou Frun Flash&Mcu Microcontrol Technology Development Co.,Ltd
+
+OUI:745BC5A*
+ ID_OUI_FROM_DATABASE=Fournie Grospaud Energie SASU
+
+OUI:745BC5B*
+ ID_OUI_FROM_DATABASE=Smartiply Inc.
+
+OUI:745BC5C*
+ ID_OUI_FROM_DATABASE=ComNot
+
+OUI:745BC5D*
+ ID_OUI_FROM_DATABASE=CELYSS SAS
+
+OUI:745BC5E*
+ ID_OUI_FROM_DATABASE=Qingdao Wintec System Co., Ltd
+
OUI:745C4B*
ID_OUI_FROM_DATABASE=GN Audio A/S
@@ -65867,9 +68249,15 @@ OUI:745E1C*
OUI:745F00*
ID_OUI_FROM_DATABASE=Samsung Semiconductor Inc.
+OUI:745F90*
+ ID_OUI_FROM_DATABASE=LAM Technologies
+
OUI:745FAE*
ID_OUI_FROM_DATABASE=TSL PPL
+OUI:7460FA*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:74614B*
ID_OUI_FROM_DATABASE=Chongqing Huijiatong Information Technology Co., Ltd.
@@ -65963,6 +68351,9 @@ OUI:7483EF*
OUI:74852A*
ID_OUI_FROM_DATABASE=PEGATRON CORPORATION
+OUI:7485C4*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
OUI:74860B*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -65981,6 +68372,12 @@ OUI:74882A*
OUI:74888B*
ID_OUI_FROM_DATABASE=ADB Broadband Italia
+OUI:7488BB*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
+OUI:748A0D*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:748A69*
ID_OUI_FROM_DATABASE=Korea Image Technology Co., Ltd
@@ -66015,7 +68412,7 @@ OUI:74943D*
ID_OUI_FROM_DATABASE=AgJunction
OUI:7495EC*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:749637*
ID_OUI_FROM_DATABASE=Todaair Electronic Co., Ltd
@@ -66032,6 +68429,9 @@ OUI:749C52*
OUI:749CE3*
ID_OUI_FROM_DATABASE=KodaCloud Canada, Inc
+OUI:749D79*
+ ID_OUI_FROM_DATABASE=Sercomm Corporation.
+
OUI:749D8F*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -66086,6 +68486,9 @@ OUI:74B472*
OUI:74B57E*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:74B587*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:74B91E*
ID_OUI_FROM_DATABASE=Nanjing Bestway Automation System Co., Ltd
@@ -66107,9 +68510,15 @@ OUI:74BFA1*
OUI:74BFB7*
ID_OUI_FROM_DATABASE=Nusoft Corporation
+OUI:74BFC0*
+ ID_OUI_FROM_DATABASE=CANON INC.
+
OUI:74C14F*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:74C17D*
+ ID_OUI_FROM_DATABASE=Infinix mobility limited
+
OUI:74C246*
ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
@@ -66320,6 +68729,9 @@ OUI:74F661*
OUI:74F726*
ID_OUI_FROM_DATABASE=Neuron Robotics
+OUI:74F737*
+ ID_OUI_FROM_DATABASE=KCE
+
OUI:74F85D*
ID_OUI_FROM_DATABASE=Berkeley Nucleonics Corp
@@ -66422,6 +68834,9 @@ OUI:780CB8*
OUI:780CF0*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:780ED1*
+ ID_OUI_FROM_DATABASE=TRUMPF Werkzeugmaschinen GmbH+Co.KG
+
OUI:780F77*
ID_OUI_FROM_DATABASE=HangZhou Gubei Electronics Technology Co.,Ltd
@@ -66464,6 +68879,9 @@ OUI:782079*
OUI:78223D*
ID_OUI_FROM_DATABASE=Affirmed Networks
+OUI:782327*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:7823AE*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
@@ -66515,6 +68933,12 @@ OUI:78321B*
OUI:78324F*
ID_OUI_FROM_DATABASE=Millennium Group, Inc.
+OUI:7835A0*
+ ID_OUI_FROM_DATABASE=Zurn Industries LLC
+
+OUI:783607*
+ ID_OUI_FROM_DATABASE=Cermate Technologies Inc.
+
OUI:783690*
ID_OUI_FROM_DATABASE=Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd
@@ -66563,6 +68987,9 @@ OUI:7846C4*
OUI:78471D*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:7847E3*
+ ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+
OUI:784859*
ID_OUI_FROM_DATABASE=Hewlett Packard
@@ -66659,6 +69086,9 @@ OUI:786C1C*
OUI:786D94*
ID_OUI_FROM_DATABASE=Palo Alto Networks
+OUI:787052*
+ ID_OUI_FROM_DATABASE=Welotec GmbH
+
OUI:78719C*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
@@ -66671,6 +69101,9 @@ OUI:787B8A*
OUI:787D48*
ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED
+OUI:787D53*
+ ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+
OUI:787E61*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -66803,6 +69236,9 @@ OUI:78A6E1*
OUI:78A714*
ID_OUI_FROM_DATABASE=Amphenol
+OUI:78A7EB*
+ ID_OUI_FROM_DATABASE=1MORE
+
OUI:78A873*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -66827,6 +69263,9 @@ OUI:78AF58*
OUI:78AFE4*
ID_OUI_FROM_DATABASE=Comau S.p.A
+OUI:78B213*
+ ID_OUI_FROM_DATABASE=DWnet Technologies(Suzhou) Corporation
+
OUI:78B28D*
ID_OUI_FROM_DATABASE=Beijing Tengling Technology CO.Ltd
@@ -67085,12 +69524,18 @@ OUI:78DA07*
OUI:78DA6E*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:78DAA2*
+ ID_OUI_FROM_DATABASE=Cynosure Technologies Co.,Ltd
+
OUI:78DAB3*
ID_OUI_FROM_DATABASE=GBO Technology
OUI:78DD08*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:78DD12*
+ ID_OUI_FROM_DATABASE=Arcadyan Corporation
+
OUI:78DDD6*
ID_OUI_FROM_DATABASE=c-scape
@@ -67196,6 +69641,9 @@ OUI:7C02BC*
OUI:7C034C*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+OUI:7C035E*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
OUI:7C03AB*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
@@ -67229,6 +69677,9 @@ OUI:7C0A50*
OUI:7C0BC6*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:7C0CF6*
+ ID_OUI_FROM_DATABASE=Guangdong Huiwei High-tech Co., Ltd.
+
OUI:7C0ECE*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -67331,6 +69782,9 @@ OUI:7C3866*
OUI:7C386C*
ID_OUI_FROM_DATABASE=Real Time Logic
+OUI:7C38AD*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:7C3920*
ID_OUI_FROM_DATABASE=SSOMA SECURITY
@@ -67395,7 +69849,7 @@ OUI:7C477CB*
ID_OUI_FROM_DATABASE=Hangzhou Yiyitaidi Information Technology Co., Ltd.
OUI:7C477CC*
- ID_OUI_FROM_DATABASE=annapurnalabs
+ ID_OUI_FROM_DATABASE=Annapurna labs
OUI:7C477CD*
ID_OUI_FROM_DATABASE=Speedifi Inc
@@ -67433,12 +69887,18 @@ OUI:7C4FB5*
OUI:7C5049*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:7C5259*
+ ID_OUI_FROM_DATABASE=Sichuan Jiuzhou Electronic Technology Co., Ltd.
+
OUI:7C534A*
ID_OUI_FROM_DATABASE=Metamako
OUI:7C55E7*
ID_OUI_FROM_DATABASE=YSI, Inc.
+OUI:7C573C*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
OUI:7C574E*
ID_OUI_FROM_DATABASE=COBI GmbH
@@ -67451,9 +69911,15 @@ OUI:7C5A67*
OUI:7C5CF8*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:7C604A*
+ ID_OUI_FROM_DATABASE=Avelon
+
OUI:7C6097*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:7C6166*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:7C6193*
ID_OUI_FROM_DATABASE=HTC Corporation
@@ -67619,6 +70085,12 @@ OUI:7C8274*
OUI:7C8306*
ID_OUI_FROM_DATABASE=Glen Dimplex Nordic as
+OUI:7C89C1*
+ ID_OUI_FROM_DATABASE=Palo Alto Networks
+
+OUI:7C8BB5*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:7C8BCA*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
@@ -67646,6 +70118,9 @@ OUI:7C96D2*
OUI:7C9763*
ID_OUI_FROM_DATABASE=Openmatics s.r.o.
+OUI:7C9A1D*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:7C9A54*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
@@ -67746,7 +70221,7 @@ OUI:7CBACC9*
ID_OUI_FROM_DATABASE=Yongguan Electronic Technology (D.G)LTD
OUI:7CBACCA*
- ID_OUI_FROM_DATABASE=annapurnalabs
+ ID_OUI_FROM_DATABASE=Annapurna labs
OUI:7CBACCB*
ID_OUI_FROM_DATABASE=Briowireless Inc.
@@ -67766,6 +70241,51 @@ OUI:7CBB6F*
OUI:7CBB8A*
ID_OUI_FROM_DATABASE=Nintendo Co., Ltd.
+OUI:7CBC840*
+ ID_OUI_FROM_DATABASE=AG Neovo
+
+OUI:7CBC841*
+ ID_OUI_FROM_DATABASE=Xiamen Mage Information Technology Co.,Ltd.
+
+OUI:7CBC842*
+ ID_OUI_FROM_DATABASE=3S Technology Co., Ltd.
+
+OUI:7CBC843*
+ ID_OUI_FROM_DATABASE=Shanghai Yitu Technology Co. Ltd
+
+OUI:7CBC844*
+ ID_OUI_FROM_DATABASE=CONTINENTAL
+
+OUI:7CBC845*
+ ID_OUI_FROM_DATABASE=Nanning auto digital technology co.,LTD
+
+OUI:7CBC846*
+ ID_OUI_FROM_DATABASE=Société de Transport de Montréal
+
+OUI:7CBC847*
+ ID_OUI_FROM_DATABASE=Xuji Changnan Communication Equipment Co., Ltd.
+
+OUI:7CBC848*
+ ID_OUI_FROM_DATABASE=Shenzhen Kuang-chi Space Technology Co., Ltd.
+
+OUI:7CBC849*
+ ID_OUI_FROM_DATABASE=HITIQ LIMITED
+
+OUI:7CBC84A*
+ ID_OUI_FROM_DATABASE=OPNT BV
+
+OUI:7CBC84B*
+ ID_OUI_FROM_DATABASE=Guangzhou Puppyrobot Technology Co.Ltd Beijing Branch
+
+OUI:7CBC84C*
+ ID_OUI_FROM_DATABASE=Tibit Communications
+
+OUI:7CBC84D*
+ ID_OUI_FROM_DATABASE=VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD
+
+OUI:7CBC84E*
+ ID_OUI_FROM_DATABASE=Beijing Topnew Group Co., Ltd
+
OUI:7CBD06*
ID_OUI_FROM_DATABASE=AE REFUsol
@@ -67775,6 +70295,9 @@ OUI:7CBF88*
OUI:7CBFB1*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:7CC385*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:7CC3A1*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -67877,12 +70400,18 @@ OUI:7CD762*
OUI:7CD844*
ID_OUI_FROM_DATABASE=Enmotus Inc
+OUI:7CD95C*
+ ID_OUI_FROM_DATABASE=Google, Inc.
+
OUI:7CD9FE*
ID_OUI_FROM_DATABASE=New Cosmos Electric Co., Ltd.
OUI:7CDA84*
ID_OUI_FROM_DATABASE=Dongnian Networks Inc.
+OUI:7CDB98*
+ ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP
+
OUI:7CDD11*
ID_OUI_FROM_DATABASE=Chongqing MAS SCI&TECH.Co.,Ltd
@@ -67967,6 +70496,9 @@ OUI:7CFADF*
OUI:7CFC3C*
ID_OUI_FROM_DATABASE=Visteon Corporation
+OUI:7CFD82*
+ ID_OUI_FROM_DATABASE=GUANGDONG GENIUS TECHNOLOGY CO., LTD.
+
OUI:7CFE28*
ID_OUI_FROM_DATABASE=Salutron Inc.
@@ -68186,6 +70718,9 @@ OUI:8048A5*
OUI:804971*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:804A14*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:804B20*
ID_OUI_FROM_DATABASE=Ventilation Control
@@ -68207,6 +70742,9 @@ OUI:805067*
OUI:8050F6*
ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED
+OUI:80546A*
+ ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
+
OUI:8056F2*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
@@ -68255,6 +70793,12 @@ OUI:8065E9*
OUI:806629*
ID_OUI_FROM_DATABASE=Prescope Technologies CO.,LTD.
+OUI:806933*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
+OUI:806940*
+ ID_OUI_FROM_DATABASE=LEXAR CO.,LIMITED
+
OUI:806AB0*
ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp.
@@ -68345,18 +70889,30 @@ OUI:807B85E*
OUI:807B85F*
ID_OUI_FROM_DATABASE=Private
+OUI:807D14*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:807D1B*
ID_OUI_FROM_DATABASE=Neosystem Co. Ltd.
+OUI:807D3A*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:807DE3*
ID_OUI_FROM_DATABASE=Chongqing Sichuan Instrument Microcircuit Co.LTD.
OUI:8081A5*
ID_OUI_FROM_DATABASE=TONGQING COMMUNICATION EQUIPMENT (SHENZHEN) Co.,Ltd
+OUI:808223*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:808287*
ID_OUI_FROM_DATABASE=ATCOM Technology Co.Ltd.
+OUI:8084A9*
+ ID_OUI_FROM_DATABASE=oshkosh Corporation
+
OUI:808698*
ID_OUI_FROM_DATABASE=Netronics Technologies Inc.
@@ -68375,6 +70931,9 @@ OUI:808C97*
OUI:808DB7*
ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
+OUI:808F1D*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
OUI:80912A*
ID_OUI_FROM_DATABASE=Lih Rong electronic Enterprise Co., Ltd.
@@ -68528,6 +71087,9 @@ OUI:80CF41*
OUI:80D019*
ID_OUI_FROM_DATABASE=Embed, Inc
+OUI:80D065*
+ ID_OUI_FROM_DATABASE=CKS Corporation
+
OUI:80D09B*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -68540,6 +71102,9 @@ OUI:80D18B*
OUI:80D21D*
ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
+OUI:80D336*
+ ID_OUI_FROM_DATABASE=CERN
+
OUI:80D433*
ID_OUI_FROM_DATABASE=LzLabs GmbH
@@ -68651,6 +71216,12 @@ OUI:80FA5B*
OUI:80FB06*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:80FBF0*
+ ID_OUI_FROM_DATABASE=Quectel Wireless Solutions Co., Ltd.
+
+OUI:80FD7A*
+ ID_OUI_FROM_DATABASE=BLU Products Inc
+
OUI:80FFA8*
ID_OUI_FROM_DATABASE=UNIDIS
@@ -68681,6 +71252,9 @@ OUI:84100D*
OUI:84119E*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:84139F*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:8416F9*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
@@ -68708,6 +71282,9 @@ OUI:841B38*
OUI:841B5E*
ID_OUI_FROM_DATABASE=NETGEAR
+OUI:841C70*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:841E26*
ID_OUI_FROM_DATABASE=KERNEL-I Co.,LTD
@@ -68777,6 +71354,9 @@ OUI:842F75*
OUI:8430E5*
ID_OUI_FROM_DATABASE=SkyHawke Technologies, LLC
+OUI:84326F*
+ ID_OUI_FROM_DATABASE=GUANGZHOU AVA ELECTRONICS TECHNOLOGY CO.,LTD
+
OUI:8432EA*
ID_OUI_FROM_DATABASE=ANHUI WANZTEN P&T CO., LTD
@@ -68912,6 +71492,9 @@ OUI:84683E*
OUI:846878*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:846991*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:846A66*
ID_OUI_FROM_DATABASE=Sumitomo Kizai Co.,Ltd.
@@ -68921,6 +71504,9 @@ OUI:846AED*
OUI:846EB1*
ID_OUI_FROM_DATABASE=Park Assist LLC
+OUI:846FCE*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:847207*
ID_OUI_FROM_DATABASE=I&C Technology
@@ -68957,12 +71543,18 @@ OUI:847A88*
OUI:847BEB*
ID_OUI_FROM_DATABASE=Dell Inc.
+OUI:847C9B*
+ ID_OUI_FROM_DATABASE=GD Midea Air-Conditioning Equipment Co.,Ltd.
+
OUI:847D50*
ID_OUI_FROM_DATABASE=Holley Metering Limited
OUI:847E40*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:847F3D*
+ ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+
OUI:84802D*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -68987,6 +71579,9 @@ OUI:848506*
OUI:84850A*
ID_OUI_FROM_DATABASE=Hella Sonnen- und Wetterschutztechnik GmbH
+OUI:8485E6*
+ ID_OUI_FROM_DATABASE=Guangdong Asano Technology CO.,Ltd.
+
OUI:8486F3*
ID_OUI_FROM_DATABASE=Greenvity Communications
@@ -69092,6 +71687,9 @@ OUI:849DC5*
OUI:849FB5*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:84A06E*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
OUI:84A134*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -69119,6 +71717,9 @@ OUI:84A788*
OUI:84A8E4*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:84A93E*
+ ID_OUI_FROM_DATABASE=Hewlett Packard
+
OUI:84A991*
ID_OUI_FROM_DATABASE=Cyber Trans Japan Co.,Ltd.
@@ -69210,7 +71811,7 @@ OUI:84D32A*
ID_OUI_FROM_DATABASE=IEEE 1905.1
OUI:84D47E*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:84D4C8*
ID_OUI_FROM_DATABASE=Widex A/S
@@ -69225,7 +71826,7 @@ OUI:84D9C8*
ID_OUI_FROM_DATABASE=Unipattern Co.,
OUI:84DB2F*
- ID_OUI_FROM_DATABASE=Sierra Wireless Inc
+ ID_OUI_FROM_DATABASE=Sierra Wireless
OUI:84DB9E*
ID_OUI_FROM_DATABASE=Aifloo AB
@@ -69308,6 +71909,9 @@ OUI:84E327*
OUI:84E4D9*
ID_OUI_FROM_DATABASE=Shenzhen NEED technology Ltd.
+OUI:84E5D8*
+ ID_OUI_FROM_DATABASE=Guangdong UNIPOE IoT Technology Co.,Ltd.
+
OUI:84E629*
ID_OUI_FROM_DATABASE=Bluwan SA
@@ -69320,6 +71924,9 @@ OUI:84EA99*
OUI:84EB18*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:84EB3E*
+ ID_OUI_FROM_DATABASE=Vivint Smart Home
+
OUI:84ED33*
ID_OUI_FROM_DATABASE=BBMC Co.,Ltd
@@ -69497,6 +72104,9 @@ OUI:883F99*
OUI:883FD3*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:88403B*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:884157*
ID_OUI_FROM_DATABASE=Shenzhen Atsmart Technology Co.,Ltd.
@@ -69518,6 +72128,9 @@ OUI:8844F6*
OUI:88462A*
ID_OUI_FROM_DATABASE=Telechips Inc.
+OUI:884A18*
+ ID_OUI_FROM_DATABASE=Opulinks
+
OUI:884AEA*
ID_OUI_FROM_DATABASE=Texas Instruments
@@ -69530,6 +72143,9 @@ OUI:884CCF*
OUI:8850DD*
ID_OUI_FROM_DATABASE=Infiniband Trade Association
+OUI:8850F6*
+ ID_OUI_FROM_DATABASE=Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd
+
OUI:8851FB*
ID_OUI_FROM_DATABASE=Hewlett Packard
@@ -69548,6 +72164,9 @@ OUI:88576D*
OUI:8857EE*
ID_OUI_FROM_DATABASE=BUFFALO.INC
+OUI:885A06*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:885A92*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -69686,6 +72305,9 @@ OUI:886B6E*
OUI:886B76*
ID_OUI_FROM_DATABASE=CHINA HOPEFUL GROUP HOPEFUL ELECTRIC CO.,LTD
+OUI:886FD4*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:887033*
ID_OUI_FROM_DATABASE=Hangzhou Silan Microelectronic Inc
@@ -69695,6 +72317,9 @@ OUI:88708C*
OUI:8870EF*
ID_OUI_FROM_DATABASE=SC Professional Trading Co., Ltd.
+OUI:8871B1*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:8871E5*
ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
@@ -69908,6 +72533,9 @@ OUI:88B168*
OUI:88B1E1*
ID_OUI_FROM_DATABASE=Mojo Networks, Inc.
+OUI:88B291*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:88B362*
ID_OUI_FROM_DATABASE=Nokia Shanghai Bell Co. Ltd.)
@@ -69983,6 +72611,9 @@ OUI:88D039*
OUI:88D171*
ID_OUI_FROM_DATABASE=BEGHELLI S.P.A
+OUI:88D211*
+ ID_OUI_FROM_DATABASE=Eko Devices, Inc.
+
OUI:88D274*
ID_OUI_FROM_DATABASE=zte corporation
@@ -70025,6 +72656,9 @@ OUI:88DEA9*
OUI:88DF9E*
ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+OUI:88E034*
+ ID_OUI_FROM_DATABASE=Shinwa industries(China) ltd.
+
OUI:88E0A0*
ID_OUI_FROM_DATABASE=Shenzhen VisionSTOR Technologies Co., Ltd
@@ -70079,6 +72713,9 @@ OUI:88F488*
OUI:88F490*
ID_OUI_FROM_DATABASE=Jetmobile Pte Ltd
+OUI:88F56E*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:88F7BF*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
@@ -70187,6 +72824,9 @@ OUI:8C15C7*
OUI:8C1645*
ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd
+OUI:8C1850*
+ ID_OUI_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co., Ltd.
+
OUI:8C18D9*
ID_OUI_FROM_DATABASE=Shenzhen RF Technology Co., Ltd
@@ -70346,6 +72986,9 @@ OUI:8C41F4*
OUI:8C4435*
ID_OUI_FROM_DATABASE=Shanghai BroadMobi Communication Technology Co., Ltd.
+OUI:8C444F*
+ ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
+
OUI:8C4500*
ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
@@ -70370,6 +73013,9 @@ OUI:8C4DEA*
OUI:8C5105*
ID_OUI_FROM_DATABASE=Shenzhen ireadygo Information Technology CO.,LTD.
+OUI:8C53D2*
+ ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
+
OUI:8C53F7*
ID_OUI_FROM_DATABASE=A&D ENGINEERING CO., LTD.
@@ -70403,6 +73049,9 @@ OUI:8C59C3*
OUI:8C5AF0*
ID_OUI_FROM_DATABASE=Exeltech Solar Products
+OUI:8C5AF8*
+ ID_OUI_FROM_DATABASE=Beijing Xiaomi Electronics Co., Ltd.
+
OUI:8C5BF0*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
@@ -70448,6 +73097,9 @@ OUI:8C6D50*
OUI:8C6D77*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:8C6DC4*
+ ID_OUI_FROM_DATABASE=Megapixel VR
+
OUI:8C705A*
ID_OUI_FROM_DATABASE=Intel Corporate
@@ -70475,6 +73127,9 @@ OUI:8C7967*
OUI:8C7B9D*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:8C7BF0*
+ ID_OUI_FROM_DATABASE=Xufeng Development Limited
+
OUI:8C7C92*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -70511,6 +73166,9 @@ OUI:8C8580*
OUI:8C8590*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:8C85E6*
+ ID_OUI_FROM_DATABASE=Cleondris GmbH
+
OUI:8C873B*
ID_OUI_FROM_DATABASE=Leica Camera AG
@@ -70559,6 +73217,9 @@ OUI:8C9351*
OUI:8C94CF*
ID_OUI_FROM_DATABASE=Encell Technology, Inc.
+OUI:8C965F*
+ ID_OUI_FROM_DATABASE=Shandong Zhongan Technology Co., Ltd.
+
OUI:8C99E6*
ID_OUI_FROM_DATABASE=TCT mobile ltd
@@ -70589,6 +73250,9 @@ OUI:8CAE4C*
OUI:8CAE89*
ID_OUI_FROM_DATABASE=Y-cam Solutions Ltd
+OUI:8CAEDB*
+ ID_OUI_FROM_DATABASE=NAG LLC
+
OUI:8CB094*
ID_OUI_FROM_DATABASE=Airtech I&C Co., Ltd
@@ -70784,9 +73448,15 @@ OUI:8CF9C9*
OUI:8CFABA*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:8CFCA0*
+ ID_OUI_FROM_DATABASE=Shenzhen Smart Device Technology Co., LTD.
+
OUI:8CFDF0*
ID_OUI_FROM_DATABASE=Qualcomm Inc.
+OUI:8CFE57*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:8CFE74*
ID_OUI_FROM_DATABASE=Ruckus Wireless
@@ -70802,6 +73472,9 @@ OUI:9000DB*
OUI:90013B*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+OUI:900218*
+ ID_OUI_FROM_DATABASE=BSkyB Ltd
+
OUI:90028A*
ID_OUI_FROM_DATABASE=Shenzhen Shidean Legrand Electronic Products Co.,Ltd
@@ -70847,6 +73520,9 @@ OUI:900DCB*
OUI:900E83*
ID_OUI_FROM_DATABASE=Monico Monitoring, Inc.
+OUI:900EB3*
+ ID_OUI_FROM_DATABASE=Shenzhen Amediatech Technology Co., Ltd.
+
OUI:901711*
ID_OUI_FROM_DATABASE=Hagenuk Marinekommunikation GmbH
@@ -70886,6 +73562,9 @@ OUI:90203A*
OUI:902083*
ID_OUI_FROM_DATABASE=General Engine Management Systems Ltd.
+OUI:9020C2*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
OUI:902106*
ID_OUI_FROM_DATABASE=BSkyB Ltd
@@ -71066,6 +73745,9 @@ OUI:905682*
OUI:905692*
ID_OUI_FROM_DATABASE=Autotalks Ltd.
+OUI:905851*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
OUI:9059AF*
ID_OUI_FROM_DATABASE=Texas Instruments
@@ -71087,6 +73769,9 @@ OUI:90610C*
OUI:9061AE*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:90633B*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:906717*
ID_OUI_FROM_DATABASE=Alphion India Private Limited
@@ -71105,6 +73790,9 @@ OUI:9068C3*
OUI:906CAC*
ID_OUI_FROM_DATABASE=Fortinet, Inc.
+OUI:906D05*
+ ID_OUI_FROM_DATABASE=BXB ELECTRONICS CO., LTD
+
OUI:906DC8*
ID_OUI_FROM_DATABASE=DLG Automação Industrial Ltda
@@ -71141,9 +73829,15 @@ OUI:907A0A*
OUI:907A28*
ID_OUI_FROM_DATABASE=Beijing Morncloud Information And Technology Co. Ltd.
+OUI:907A58*
+ ID_OUI_FROM_DATABASE=Zegna-Daidong Limited
+
OUI:907AF1*
ID_OUI_FROM_DATABASE=Wally
+OUI:907E30*
+ ID_OUI_FROM_DATABASE=LARS
+
OUI:907EBA*
ID_OUI_FROM_DATABASE=UTEK TECHNOLOGY (SHENZHEN) CO.,LTD
@@ -71171,9 +73865,15 @@ OUI:90848B*
OUI:908674*
ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO., LTD
+OUI:90869B*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:9088A2*
ID_OUI_FROM_DATABASE=IONICS TECHNOLOGY ME LTDA
+OUI:90895F*
+ ID_OUI_FROM_DATABASE=WEIFANG GOERTEK ELECTRONICS CO.,LTD
+
OUI:908C09*
ID_OUI_FROM_DATABASE=Total Phase
@@ -71204,6 +73904,9 @@ OUI:909060*
OUI:9092B4*
ID_OUI_FROM_DATABASE=Diehl BGT Defence GmbH & Co. KG
+OUI:90940A*
+ ID_OUI_FROM_DATABASE=Analog Devices, Inc
+
OUI:909497*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -71306,6 +74009,9 @@ OUI:90C1C6*
OUI:90C35F*
ID_OUI_FROM_DATABASE=Nanjing Jiahao Technology Co., Ltd.
+OUI:90C54A*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
OUI:90C6820*
ID_OUI_FROM_DATABASE=Shenzhen Lencotion Technology Co.,Ltd
@@ -71361,7 +74067,7 @@ OUI:90C7D8*
ID_OUI_FROM_DATABASE=zte corporation
OUI:90C99B*
- ID_OUI_FROM_DATABASE=Recore Systems
+ ID_OUI_FROM_DATABASE=Tesorion Nederland B.V.
OUI:90CC24*
ID_OUI_FROM_DATABASE=Synaptics, Inc
@@ -71420,12 +74126,21 @@ OUI:90DFFB*
OUI:90E0F0*
ID_OUI_FROM_DATABASE=IEEE 1722a Working Group
+OUI:90E17B*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
+OUI:90E202*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:90E2BA*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:90E6BA*
ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
+OUI:90E710*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
OUI:90E7C4*
ID_OUI_FROM_DATABASE=HTC Corporation
@@ -71468,6 +74183,9 @@ OUI:90F652*
OUI:90F72F*
ID_OUI_FROM_DATABASE=Phillips Machine & Welding Co., Inc.
+OUI:90F891*
+ ID_OUI_FROM_DATABASE=Kaonmedia CO., LTD.
+
OUI:90FB5B*
ID_OUI_FROM_DATABASE=Avaya Inc
@@ -71525,6 +74243,9 @@ OUI:9411DA*
OUI:94147A*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+OUI:941625*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:941673*
ID_OUI_FROM_DATABASE=Point Core SARL
@@ -71546,6 +74267,12 @@ OUI:942197*
OUI:94236E*
ID_OUI_FROM_DATABASE=Shenzhen Junlan Electronic Ltd
+OUI:9424E1*
+ ID_OUI_FROM_DATABASE=Alcatel-Lucent Enterprise
+
+OUI:942790*
+ ID_OUI_FROM_DATABASE=TCT mobile ltd
+
OUI:94282E*
ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
@@ -71597,6 +74324,9 @@ OUI:943FC2*
OUI:9440A2*
ID_OUI_FROM_DATABASE=Anywave Communication Technologies, Inc.
+OUI:9440C9*
+ ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
+
OUI:9441C1*
ID_OUI_FROM_DATABASE=Mini-Cam Limited
@@ -71618,6 +74348,9 @@ OUI:944A09*
OUI:944A0C*
ID_OUI_FROM_DATABASE=Sercomm Corporation.
+OUI:944F4C*
+ ID_OUI_FROM_DATABASE=Sound United LLC
+
OUI:945047*
ID_OUI_FROM_DATABASE=Rechnerbetriebsgruppe
@@ -71639,9 +74372,15 @@ OUI:945330*
OUI:945493*
ID_OUI_FROM_DATABASE=Rigado, LLC
+OUI:9454DF*
+ ID_OUI_FROM_DATABASE=YST CORP.
+
OUI:9457A5*
ID_OUI_FROM_DATABASE=Hewlett Packard
+OUI:9458CB*
+ ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd
+
OUI:945907*
ID_OUI_FROM_DATABASE=Shanghai HITE-BELDEN Network Technology Co., Ltd.
@@ -71675,6 +74414,12 @@ OUI:94659C*
OUI:9466E7*
ID_OUI_FROM_DATABASE=WOM Engineering
+OUI:94677E*
+ ID_OUI_FROM_DATABASE=Belden India Private Limited
+
+OUI:946A77*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
OUI:946AB0*
ID_OUI_FROM_DATABASE=Arcadyan Corporation
@@ -71747,6 +74492,9 @@ OUI:948DEF*
OUI:948E89*
ID_OUI_FROM_DATABASE=INDUSTRIAS UNIDAS SA DE CV
+OUI:948FCF*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:948FEE*
ID_OUI_FROM_DATABASE=Verizon Telematics
@@ -71804,6 +74552,9 @@ OUI:94A1A2*
OUI:94A3CA*
ID_OUI_FROM_DATABASE=KonnectONE, LLC
+OUI:94A40C*
+ ID_OUI_FROM_DATABASE=Diehl Metering GmbH
+
OUI:94A7B7*
ID_OUI_FROM_DATABASE=zte corporation
@@ -71825,6 +74576,9 @@ OUI:94AE61*
OUI:94AEE3*
ID_OUI_FROM_DATABASE=Belden Hirschmann Industries (Suzhou) Ltd.
+OUI:94B01F*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:94B10A*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -71832,7 +74586,7 @@ OUI:94B2CC*
ID_OUI_FROM_DATABASE=PIONEER CORPORATION
OUI:94B40F*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:94B819*
ID_OUI_FROM_DATABASE=Nokia
@@ -71864,6 +74618,9 @@ OUI:94BF2D*
OUI:94BF95*
ID_OUI_FROM_DATABASE=Shenzhen Coship Electronics Co., Ltd
+OUI:94BFC4*
+ ID_OUI_FROM_DATABASE=Ruckus Wireless
+
OUI:94C014*
ID_OUI_FROM_DATABASE=Sorter Sp. j. Konrad Grzeszczyk MichaA, Ziomek
@@ -71909,12 +74666,18 @@ OUI:94CE2C*
OUI:94CE31*
ID_OUI_FROM_DATABASE=CTS Limited
+OUI:94D00D*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:94D019*
ID_OUI_FROM_DATABASE=Cydle Corp.
OUI:94D029*
ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+OUI:94D075*
+ ID_OUI_FROM_DATABASE=CIS Crypto
+
OUI:94D417*
ID_OUI_FROM_DATABASE=GPI KOREA INC.
@@ -71966,6 +74729,9 @@ OUI:94DF58*
OUI:94E0D0*
ID_OUI_FROM_DATABASE=HealthStream Taiwan Inc.
+OUI:94E0D6*
+ ID_OUI_FROM_DATABASE=China Dragon Technology Limited
+
OUI:94E1AC*
ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd.
@@ -71996,6 +74762,9 @@ OUI:94E979*
OUI:94E98C*
ID_OUI_FROM_DATABASE=Nokia
+OUI:94EAEA*
+ ID_OUI_FROM_DATABASE=TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO
+
OUI:94EB2C*
ID_OUI_FROM_DATABASE=Google, Inc.
@@ -72023,9 +74792,15 @@ OUI:94F692*
OUI:94F6A3*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:94F6D6*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:94F720*
ID_OUI_FROM_DATABASE=Tianjin Deviser Electronics Instrument Co., Ltd
+OUI:94F7AD*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
OUI:94FAE8*
ID_OUI_FROM_DATABASE=Shenzhen Eycom Technology Co., Ltd
@@ -72125,6 +74900,9 @@ OUI:9803D8*
OUI:98072D*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:9809CF*
+ ID_OUI_FROM_DATABASE=OnePlus Technology (Shenzhen) Co., Ltd
+
OUI:980C82*
ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD.
@@ -72152,6 +74930,9 @@ OUI:9814D2*
OUI:9816EC*
ID_OUI_FROM_DATABASE=IC Intracom
+OUI:981888*
+ ID_OUI_FROM_DATABASE=Cisco Meraki
+
OUI:981DFA*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -72224,6 +75005,9 @@ OUI:98398E*
OUI:983B16*
ID_OUI_FROM_DATABASE=AMPAK Technology, Inc.
+OUI:983B8F*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:983F9F*
ID_OUI_FROM_DATABASE=China SSJ (Suzhou) Network Technology Inc.
@@ -72236,12 +75020,21 @@ OUI:984246*
OUI:9843DA*
ID_OUI_FROM_DATABASE=INTERTECH
+OUI:9844B6*
+ ID_OUI_FROM_DATABASE=INFRANOR SAS
+
OUI:984562*
ID_OUI_FROM_DATABASE=Shanghai Baud Data Communication Co.,Ltd.
+OUI:98460A*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:98473C*
ID_OUI_FROM_DATABASE=SHANGHAI SUNMON COMMUNICATION TECHNOGY CO.,LTD
+OUI:9849E1*
+ ID_OUI_FROM_DATABASE=Boeing Defence Australia
+
OUI:984A47*
ID_OUI_FROM_DATABASE=CHG Hospital Beds
@@ -72290,6 +75083,9 @@ OUI:985C93*
OUI:985D46*
ID_OUI_FROM_DATABASE=PeopleNet Communication
+OUI:985D82*
+ ID_OUI_FROM_DATABASE=Arista Networks
+
OUI:985DAD*
ID_OUI_FROM_DATABASE=Texas Instruments
@@ -72440,6 +75236,9 @@ OUI:989449*
OUI:9897D1*
ID_OUI_FROM_DATABASE=MitraStar Technology Corp.
+OUI:989BCB*
+ ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH
+
OUI:989C57*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -72548,6 +75347,9 @@ OUI:98CA33*
OUI:98CB27*
ID_OUI_FROM_DATABASE=Galore Networks Pvt. Ltd.
+OUI:98CC4D*
+ ID_OUI_FROM_DATABASE=Shenzhen mantunsci co., LTD
+
OUI:98CDB4*
ID_OUI_FROM_DATABASE=Virident Systems, Inc.
@@ -72563,6 +75365,9 @@ OUI:98D331*
OUI:98D3D2*
ID_OUI_FROM_DATABASE=MEKRA Lang GmbH & Co. KG
+OUI:98D3E7*
+ ID_OUI_FROM_DATABASE=Netafim L
+
OUI:98D686*
ID_OUI_FROM_DATABASE=Chyi Lee industry Co., ltd.
@@ -72581,6 +75386,9 @@ OUI:98D88C*
OUI:98DA92*
ID_OUI_FROM_DATABASE=Vuzix Corporation
+OUI:98DAC4*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
OUI:98DCD9*
ID_OUI_FROM_DATABASE=UNITEC Co., Ltd.
@@ -72614,6 +75422,9 @@ OUI:98E848*
OUI:98EC65*
ID_OUI_FROM_DATABASE=Cosesy ApS
+OUI:98ED5C*
+ ID_OUI_FROM_DATABASE=Tesla Motors, Inc
+
OUI:98EECB*
ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation
@@ -72653,6 +75464,54 @@ OUI:98F8C1*
OUI:98F8DB*
ID_OUI_FROM_DATABASE=Marini Impianti Industriali s.r.l.
+OUI:98F9C70*
+ ID_OUI_FROM_DATABASE=SHENZHEN HUNTKEY ELECTRIC CO., LTD.
+
+OUI:98F9C71*
+ ID_OUI_FROM_DATABASE=HighSecLabs
+
+OUI:98F9C72*
+ ID_OUI_FROM_DATABASE=Pozyx NV
+
+OUI:98F9C73*
+ ID_OUI_FROM_DATABASE=Beijing Horizon Information Technology Co., Ltd
+
+OUI:98F9C74*
+ ID_OUI_FROM_DATABASE=Promess GmbH
+
+OUI:98F9C75*
+ ID_OUI_FROM_DATABASE=Tonycore Technology Co.,Ltd.
+
+OUI:98F9C76*
+ ID_OUI_FROM_DATABASE=GoodBox
+
+OUI:98F9C77*
+ ID_OUI_FROM_DATABASE=ARIMA Communications Corp.
+
+OUI:98F9C78*
+ ID_OUI_FROM_DATABASE=Renalsense
+
+OUI:98F9C79*
+ ID_OUI_FROM_DATABASE=Koala Technology CO., LTD.
+
+OUI:98F9C7A*
+ ID_OUI_FROM_DATABASE=MSB Elektronik und Gerätebau GmbH
+
+OUI:98F9C7B*
+ ID_OUI_FROM_DATABASE=HIROIA Communications Pte. Ltd. Taiwan Branch
+
+OUI:98F9C7C*
+ ID_OUI_FROM_DATABASE=ShenZhen Chuangwei Electronic Appliance Co.,Ltd
+
+OUI:98F9C7D*
+ ID_OUI_FROM_DATABASE=hangzhou soar security technologies limited liability company
+
+OUI:98F9C7E*
+ ID_OUI_FROM_DATABASE=NC-LINK Technology Co., Ltd.
+
+OUI:98FA9B*
+ ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd
+
OUI:98FAE3*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
@@ -72713,6 +75572,9 @@ OUI:9C0E4A*
OUI:9C13AB*
ID_OUI_FROM_DATABASE=Chanson Water Co., Ltd.
+OUI:9C1463*
+ ID_OUI_FROM_DATABASE=Zhejiang Dahua Technology Co., Ltd.
+
OUI:9C1465*
ID_OUI_FROM_DATABASE=Edata Elektronik San. ve Tic. A.Åž.
@@ -72720,7 +75582,10 @@ OUI:9C1874*
ID_OUI_FROM_DATABASE=Nokia Danmark A/S
OUI:9C1C12*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
+OUI:9C1D36*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:9C1D58*
ID_OUI_FROM_DATABASE=Texas Instruments
@@ -72947,6 +75812,9 @@ OUI:9C63ED*
OUI:9C645E*
ID_OUI_FROM_DATABASE=Harman Consumer Group
+OUI:9C648B*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:9C65B0*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -72962,6 +75830,51 @@ OUI:9C6650*
OUI:9C685B*
ID_OUI_FROM_DATABASE=Octonion SA
+OUI:9C6937*
+ ID_OUI_FROM_DATABASE=Qorvo Utrecht B.V.
+
+OUI:9C69B40*
+ ID_OUI_FROM_DATABASE=Suzhou Fitcan Technology Co.,LTD
+
+OUI:9C69B42*
+ ID_OUI_FROM_DATABASE=MOZI (Shenzhen) Artificial Intelligence Technology Co., Ltd.
+
+OUI:9C69B43*
+ ID_OUI_FROM_DATABASE=Appareo Systems, LLC
+
+OUI:9C69B44*
+ ID_OUI_FROM_DATABASE=Globalcom Engineering SPA
+
+OUI:9C69B45*
+ ID_OUI_FROM_DATABASE=Elesta GmbH
+
+OUI:9C69B46*
+ ID_OUI_FROM_DATABASE=Shenzhen jiahua zhongli technology co.LTD
+
+OUI:9C69B47*
+ ID_OUI_FROM_DATABASE=PCI Limited
+
+OUI:9C69B48*
+ ID_OUI_FROM_DATABASE=Skydock do Brasil Ltda
+
+OUI:9C69B49*
+ ID_OUI_FROM_DATABASE=Teptron AB
+
+OUI:9C69B4A*
+ ID_OUI_FROM_DATABASE=BEIJING PICOHOOD TECHNOLOGY CO.,LTD
+
+OUI:9C69B4B*
+ ID_OUI_FROM_DATABASE=Toughdog Security Systems
+
+OUI:9C69B4C*
+ ID_OUI_FROM_DATABASE=Guangdong Hanwei intergration Co.,Ltd
+
+OUI:9C69B4D*
+ ID_OUI_FROM_DATABASE=Intellect module LLC
+
+OUI:9C69B4E*
+ ID_OUI_FROM_DATABASE=NINGBO SHEN LINK COMMUNICATION TECHNOLOGY CO., LTD
+
OUI:9C6ABE*
ID_OUI_FROM_DATABASE=QEES ApS.
@@ -73004,6 +75917,9 @@ OUI:9C807D*
OUI:9C80DF*
ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation
+OUI:9C8275*
+ ID_OUI_FROM_DATABASE=Yichip Microelectronics (Hangzhou) Co.,Ltd
+
OUI:9C83BF*
ID_OUI_FROM_DATABASE=PRO-VISION, Inc.
@@ -73028,11 +75944,14 @@ OUI:9C8BF1*
OUI:9C8C6E*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:9C8CD8*
+ ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
+
OUI:9C8D1A*
ID_OUI_FROM_DATABASE=INTEG process group inc
OUI:9C8D7C*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:9C8DD3*
ID_OUI_FROM_DATABASE=Leonton Technologies
@@ -73085,6 +76004,9 @@ OUI:9CA3A9*
OUI:9CA3BA*
ID_OUI_FROM_DATABASE=SAKURA Internet Inc.
+OUI:9CA525*
+ ID_OUI_FROM_DATABASE=Shandong USR IOT Technology Limited
+
OUI:9CA577*
ID_OUI_FROM_DATABASE=Osorno Enterprises Inc.
@@ -73169,6 +76091,9 @@ OUI:9CC7D1*
OUI:9CC8AE*
ID_OUI_FROM_DATABASE=Becton, Dickinson and Company
+OUI:9CC8FC*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:9CC950*
ID_OUI_FROM_DATABASE=Baumer Holding
@@ -73211,6 +76136,9 @@ OUI:9CD9CB*
OUI:9CDA3E*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:9CDB07*
+ ID_OUI_FROM_DATABASE=Thum+Mahr GmbH
+
OUI:9CDC71*
ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
@@ -73281,7 +76209,7 @@ OUI:9CF67D*
ID_OUI_FROM_DATABASE=Ricardo Prague, s.r.o.
OUI:9CF6DD0*
- ID_OUI_FROM_DATABASE=annapurnalabs
+ ID_OUI_FROM_DATABASE=Annapurna labs
OUI:9CF6DD1*
ID_OUI_FROM_DATABASE=Ithor IT Co.,Ltd.
@@ -73535,6 +76463,9 @@ OUI:A02833D*
OUI:A02833E*
ID_OUI_FROM_DATABASE=Precision Planting, LLC.
+OUI:A028ED*
+ ID_OUI_FROM_DATABASE=HMD Global Oy
+
OUI:A02BB8*
ID_OUI_FROM_DATABASE=Hewlett Packard
@@ -73548,7 +76479,7 @@ OUI:A03299*
ID_OUI_FROM_DATABASE=Lenovo (Beijing) Co., Ltd.
OUI:A0341B*
- ID_OUI_FROM_DATABASE=TrackR, Inc
+ ID_OUI_FROM_DATABASE=Adero Inc
OUI:A0369F*
ID_OUI_FROM_DATABASE=Intel Corporate
@@ -73646,9 +76577,15 @@ OUI:A041A7*
OUI:A0423F*
ID_OUI_FROM_DATABASE=Tyan Computer Corp
+OUI:A04246*
+ ID_OUI_FROM_DATABASE=IT Telecom Co., Ltd.
+
OUI:A043DB*
ID_OUI_FROM_DATABASE=Sitael S.p.A.
+OUI:A047D7*
+ ID_OUI_FROM_DATABASE=Best IT World (India) Pvt Ltd
+
OUI:A0481C*
ID_OUI_FROM_DATABASE=Hewlett Packard
@@ -73670,6 +76607,9 @@ OUI:A04EA7*
OUI:A04FD4*
ID_OUI_FROM_DATABASE=ADB Broadband Italia
+OUI:A0510B*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:A051C6*
ID_OUI_FROM_DATABASE=Avaya Inc
@@ -73880,9 +76820,15 @@ OUI:A0A23C*
OUI:A0A33B*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:A0A3B8*
+ ID_OUI_FROM_DATABASE=WISCLOUD
+
OUI:A0A3E2*
ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc
+OUI:A0A4C5*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:A0A65C*
ID_OUI_FROM_DATABASE=Supercomputing Systems AG
@@ -73919,6 +76865,9 @@ OUI:A0B437*
OUI:A0B4A5*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:A0B549*
+ ID_OUI_FROM_DATABASE=Arcadyan Corporation
+
OUI:A0B5DA*
ID_OUI_FROM_DATABASE=HongKong THTF Co., Ltd
@@ -73982,6 +76931,9 @@ OUI:A0BB3EE*
OUI:A0BB3EF*
ID_OUI_FROM_DATABASE=Private
+OUI:A0BD1D*
+ ID_OUI_FROM_DATABASE=Zhejiang Dahua Technology Co., Ltd.
+
OUI:A0BDCD*
ID_OUI_FROM_DATABASE=BSkyB Ltd
@@ -74022,7 +76974,7 @@ OUI:A0C5F24*
ID_OUI_FROM_DATABASE=AiCare Corp.
OUI:A0C5F25*
- ID_OUI_FROM_DATABASE=Tango Wave
+ ID_OUI_FROM_DATABASE=Spacepath Communications Ltd
OUI:A0C5F26*
ID_OUI_FROM_DATABASE=ShenZhen JuWangShi Tech
@@ -74183,6 +77135,9 @@ OUI:A0F849*
OUI:A0F895*
ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp.
+OUI:A0F9B7*
+ ID_OUI_FROM_DATABASE=Ademco Smart Homes Technology(Tianjin)Co.,Ltd.
+
OUI:A0F9E0*
ID_OUI_FROM_DATABASE=VIVATEL COMPANY LIMITED
@@ -74222,6 +77177,9 @@ OUI:A409CB*
OUI:A40BED*
ID_OUI_FROM_DATABASE=Carry Technology Co.,Ltd
+OUI:A40C66*
+ ID_OUI_FROM_DATABASE=Shenzhen Colorful Yugong Technology and Development Co., Ltd.
+
OUI:A40CC3*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -74234,6 +77192,9 @@ OUI:A40E2B*
OUI:A41115*
ID_OUI_FROM_DATABASE=Robert Bosch Engineering and Business Solutions pvt. Ltd.
+OUI:A41162*
+ ID_OUI_FROM_DATABASE=Arlo Technology
+
OUI:A411630*
ID_OUI_FROM_DATABASE=Adetel Equipment
@@ -74279,6 +77240,12 @@ OUI:A41163D*
OUI:A41163E*
ID_OUI_FROM_DATABASE=tinylogics
+OUI:A41194*
+ ID_OUI_FROM_DATABASE=Lenovo
+
+OUI:A41232*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:A41242*
ID_OUI_FROM_DATABASE=NEC Platforms, Ltd.
@@ -74297,9 +77264,15 @@ OUI:A41588*
OUI:A41731*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:A41791*
+ ID_OUI_FROM_DATABASE=Shenzhen Decnta Technology Co.,LTD.
+
OUI:A41875*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:A41908*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:A41BC0*
ID_OUI_FROM_DATABASE=Fastec Imaging Corporation
@@ -74324,6 +77297,9 @@ OUI:A4251B*
OUI:A42618*
ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+OUI:A42655*
+ ID_OUI_FROM_DATABASE=LTI Motion (Shanghai) Co., Ltd.
+
OUI:A42940*
ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd
@@ -74432,6 +77408,9 @@ OUI:A43D78*
OUI:A43E51*
ID_OUI_FROM_DATABASE=ANOV FRANCE
+OUI:A43EA0*
+ ID_OUI_FROM_DATABASE=iComm HK LIMITED
+
OUI:A44027*
ID_OUI_FROM_DATABASE=zte corporation
@@ -74510,6 +77489,9 @@ OUI:A44F29E*
OUI:A44F29F*
ID_OUI_FROM_DATABASE=Private
+OUI:A45046*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
OUI:A45055*
ID_OUI_FROM_DATABASE=BUSWARE.DE
@@ -74594,6 +77576,9 @@ OUI:A45DA1*
OUI:A45E60*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:A45F9B*
+ ID_OUI_FROM_DATABASE=Nexell
+
OUI:A46011*
ID_OUI_FROM_DATABASE=Verifone
@@ -74669,12 +77654,18 @@ OUI:A47C1F*
OUI:A47E39*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:A4817A*
+ ID_OUI_FROM_DATABASE=CIG SHANGHAI CO LTD
+
OUI:A481EE*
ID_OUI_FROM_DATABASE=Nokia Corporation
OUI:A48269*
ID_OUI_FROM_DATABASE=Datrium, Inc.
+OUI:A483E7*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:A48431*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -74687,6 +77678,9 @@ OUI:A486AE*
OUI:A4895B*
ID_OUI_FROM_DATABASE=ARK INFOSOLUTIONS PVT LTD
+OUI:A48CC0*
+ ID_OUI_FROM_DATABASE=JLG Industries, Inc.
+
OUI:A48CDB*
ID_OUI_FROM_DATABASE=Lenovo
@@ -74711,6 +77705,9 @@ OUI:A4933F*
OUI:A4934C*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:A49426*
+ ID_OUI_FROM_DATABASE=Elgama-Elektronika Ltd.
+
OUI:A497BB*
ID_OUI_FROM_DATABASE=Hitachi Industrial Equipment Systems Co.,Ltd
@@ -74834,6 +77831,9 @@ OUI:A4C2AB*
OUI:A4C361*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:A4C3F0*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:A4C494*
ID_OUI_FROM_DATABASE=Intel Corporate
@@ -74849,6 +77849,9 @@ OUI:A4CAA0*
OUI:A4CC32*
ID_OUI_FROM_DATABASE=Inficomm Co., Ltd
+OUI:A4CF12*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:A4D094*
ID_OUI_FROM_DATABASE=Erwin Peters Systemtechnik GmbH
@@ -74879,6 +77882,9 @@ OUI:A4D856*
OUI:A4D8CA*
ID_OUI_FROM_DATABASE=HONG KONG WATER WORLD TECHNOLOGY CO. LIMITED
+OUI:A4D931*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:A4D990*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -74999,6 +78005,12 @@ OUI:A4ED430*
OUI:A4ED431*
ID_OUI_FROM_DATABASE=INGELABS S.L.
+OUI:A4ED432*
+ ID_OUI_FROM_DATABASE=Shanghai Mission Information Technologies (Group) Co.,Ltd
+
+OUI:A4ED433*
+ ID_OUI_FROM_DATABASE=Dongguan Mingji Electronics technology Group Co., Ltd.
+
OUI:A4ED434*
ID_OUI_FROM_DATABASE=NETAS TELEKOMUNIKASYON A.S.
@@ -75008,6 +78020,9 @@ OUI:A4ED435*
OUI:A4ED436*
ID_OUI_FROM_DATABASE=Shanghai Facom Electronics Technology Co, ltd.
+OUI:A4ED437*
+ ID_OUI_FROM_DATABASE=Wuxi Junction Infomation Technology Incorporated Company
+
OUI:A4ED438*
ID_OUI_FROM_DATABASE=Linseis Messgeraete GmbH
@@ -75017,9 +78032,18 @@ OUI:A4ED439*
OUI:A4ED43A*
ID_OUI_FROM_DATABASE=Guangzhou Maxfaith Communication Technology Co.,LTD.
+OUI:A4ED43B*
+ ID_OUI_FROM_DATABASE=Paragon Business Solutions Ltd.
+
+OUI:A4ED43C*
+ ID_OUI_FROM_DATABASE=leakSMART
+
OUI:A4ED43D*
ID_OUI_FROM_DATABASE=Brand New Brand Nordic AB
+OUI:A4ED43E*
+ ID_OUI_FROM_DATABASE=TOEC TECHNOLOGY CO.,LTD.
+
OUI:A4ED4E*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
@@ -75050,6 +78074,9 @@ OUI:A4F7D0*
OUI:A4FB8D*
ID_OUI_FROM_DATABASE=Hangzhou Dunchong Technology Co.Ltd
+OUI:A4FC77*
+ ID_OUI_FROM_DATABASE=Mega Well Limited
+
OUI:A4FCCE*
ID_OUI_FROM_DATABASE=Security Expert Ltd.
@@ -75122,6 +78149,9 @@ OUI:A81FAF*
OUI:A82066*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:A823FE*
+ ID_OUI_FROM_DATABASE=LG Electronics
+
OUI:A824EB*
ID_OUI_FROM_DATABASE=ZAO NPO Introtest
@@ -75149,15 +78179,66 @@ OUI:A830AD*
OUI:A8329A*
ID_OUI_FROM_DATABASE=Digicom Futuristic Technologies Ltd.
+OUI:A8346A*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:A8367A*
ID_OUI_FROM_DATABASE=frogblue TECHNOLOGY GmbH
OUI:A83944*
ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc
+OUI:A83CCB*
+ ID_OUI_FROM_DATABASE=ROSSMA
+
OUI:A83E0E*
ID_OUI_FROM_DATABASE=HMD Global Oy
+OUI:A83FA10*
+ ID_OUI_FROM_DATABASE=Imecon Engineering SrL
+
+OUI:A83FA11*
+ ID_OUI_FROM_DATABASE=GTDevice LLC
+
+OUI:A83FA12*
+ ID_OUI_FROM_DATABASE=MEDCAPTAIN MEDICAL TECHNOLOGY CO., LTD.
+
+OUI:A83FA13*
+ ID_OUI_FROM_DATABASE=Guangzhou Tupu Internet Technology Co., Ltd.
+
+OUI:A83FA14*
+ ID_OUI_FROM_DATABASE=Zhejiang Wellsun Intelligent Technology Co.,Ltd.
+
+OUI:A83FA15*
+ ID_OUI_FROM_DATABASE=Sercomm Corporation.
+
+OUI:A83FA16*
+ ID_OUI_FROM_DATABASE=BEGLEC
+
+OUI:A83FA17*
+ ID_OUI_FROM_DATABASE=Plejd AB
+
+OUI:A83FA18*
+ ID_OUI_FROM_DATABASE=Neos Ventures Limited
+
+OUI:A83FA19*
+ ID_OUI_FROM_DATABASE=Shenzhen ITLONG Intelligent Technology Co.,Ltd
+
+OUI:A83FA1A*
+ ID_OUI_FROM_DATABASE=Shanghai East China Computer Co., Ltd
+
+OUI:A83FA1B*
+ ID_OUI_FROM_DATABASE=Exel s.r.l. unipersonale
+
+OUI:A83FA1C*
+ ID_OUI_FROM_DATABASE=Laonz Co.,Ltd
+
+OUI:A83FA1D*
+ ID_OUI_FROM_DATABASE=Shenzhen BIO I/E Co.,Ltd
+
+OUI:A83FA1E*
+ ID_OUI_FROM_DATABASE=Guangzhou Navigateworx Technologies Co., Limited
+
OUI:A84041*
ID_OUI_FROM_DATABASE=Dragino Technology Co., Limited
@@ -75254,6 +78335,12 @@ OUI:A86B7C*
OUI:A86BAD*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:A86D5F*
+ ID_OUI_FROM_DATABASE=Raisecom Technology CO., LTD
+
+OUI:A86DAA*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:A870A5*
ID_OUI_FROM_DATABASE=UniComm Inc.
@@ -75305,6 +78392,9 @@ OUI:A886DD*
OUI:A88792*
ID_OUI_FROM_DATABASE=Broadband Antenna Tracking Systems
+OUI:A887B3*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:A887ED*
ID_OUI_FROM_DATABASE=ARC Wireless LLC
@@ -75323,6 +78413,9 @@ OUI:A88E24*
OUI:A89008*
ID_OUI_FROM_DATABASE=Beijing Yuecheng Technology Co. Ltd.
+OUI:A89042*
+ ID_OUI_FROM_DATABASE=Beijing Wanwei Intelligent Technology Co., Ltd.
+
OUI:A8922C*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
@@ -75359,6 +78452,12 @@ OUI:A89A93*
OUI:A89B10*
ID_OUI_FROM_DATABASE=inMotion Ltd.
+OUI:A89CA4*
+ ID_OUI_FROM_DATABASE=Furrion Limited
+
+OUI:A89CED*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
OUI:A89D21*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -75401,6 +78500,9 @@ OUI:A8B1D4*
OUI:A8B2DA*
ID_OUI_FROM_DATABASE=FUJITSU LIMITED
+OUI:A8B456*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:A8B86E*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
@@ -75413,6 +78515,9 @@ OUI:A8BB50*
OUI:A8BBCF*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:A8BC9C*
+ ID_OUI_FROM_DATABASE=Cloud Light Technology Limited
+
OUI:A8BD1A*
ID_OUI_FROM_DATABASE=Honey Bee (Hong Kong) Limited
@@ -75425,6 +78530,9 @@ OUI:A8BD3A*
OUI:A8BE27*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:A8BF3C*
+ ID_OUI_FROM_DATABASE=HDV Phoelectron Technology Limited
+
OUI:A8C222*
ID_OUI_FROM_DATABASE=TM-Research Inc.
@@ -75485,6 +78593,9 @@ OUI:A8DA01*
OUI:A8E018*
ID_OUI_FROM_DATABASE=Nokia Corporation
+OUI:A8E2C3*
+ ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd
+
OUI:A8E3EE*
ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc.
@@ -75518,6 +78629,9 @@ OUI:A8F470*
OUI:A8F5AC*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:A8F5DD*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:A8F7E0*
ID_OUI_FROM_DATABASE=PLANET Technology Corporation
@@ -75593,6 +78707,9 @@ OUI:AC1461*
OUI:AC14D2*
ID_OUI_FROM_DATABASE=wi-daq, inc.
+OUI:AC1585*
+ ID_OUI_FROM_DATABASE=silergy corp
+
OUI:AC162D*
ID_OUI_FROM_DATABASE=Hewlett Packard
@@ -75692,6 +78809,9 @@ OUI:AC2B6E*
OUI:AC2DA3*
ID_OUI_FROM_DATABASE=TXTR GmbH
+OUI:AC2DA9*
+ ID_OUI_FROM_DATABASE=TECNO MOBILE LIMITED
+
OUI:AC2FA8*
ID_OUI_FROM_DATABASE=Humannix Co.,Ltd.
@@ -75740,6 +78860,9 @@ OUI:AC40EA*
OUI:AC4122*
ID_OUI_FROM_DATABASE=Eclipse Electronic Systems Inc.
+OUI:AC4330*
+ ID_OUI_FROM_DATABASE=Versa Networks
+
OUI:AC44F2*
ID_OUI_FROM_DATABASE=YAMAHA CORPORATION
@@ -75767,6 +78890,9 @@ OUI:AC4FFC*
OUI:AC5036*
ID_OUI_FROM_DATABASE=Pi-Coral Inc
+OUI:AC5093*
+ ID_OUI_FROM_DATABASE=Magna Electronics Europe GmbH & Co. OHG
+
OUI:AC512C*
ID_OUI_FROM_DATABASE=Infinix mobility limited
@@ -75785,6 +78911,9 @@ OUI:AC54EC*
OUI:AC562C*
ID_OUI_FROM_DATABASE=LAVA INTERNATIONAL(H.K) LIMITED
+OUI:AC5775*
+ ID_OUI_FROM_DATABASE=HMD Global Oy
+
OUI:AC583B*
ID_OUI_FROM_DATABASE=Human Assembler, Inc.
@@ -75794,6 +78923,9 @@ OUI:AC587B*
OUI:AC5A14*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:AC5AEE*
+ ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
+
OUI:AC5D10*
ID_OUI_FROM_DATABASE=Pace Americas
@@ -75912,7 +79044,7 @@ OUI:AC7A42*
ID_OUI_FROM_DATABASE=iConnectivity
OUI:AC7A4D*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:AC7BA1*
ID_OUI_FROM_DATABASE=Intel Corporate
@@ -75959,6 +79091,9 @@ OUI:AC867E*
OUI:AC87A3*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:AC88FD*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:AC8995*
ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
@@ -75968,6 +79103,9 @@ OUI:AC8ACD*
OUI:AC8D14*
ID_OUI_FROM_DATABASE=Smartrove Inc
+OUI:AC8FF8*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:AC9232*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -76005,7 +79143,7 @@ OUI:ACA22C*
ID_OUI_FROM_DATABASE=Baycity Technologies Ltd
OUI:ACA31E*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:ACA430*
ID_OUI_FROM_DATABASE=Peerless AV
@@ -76028,6 +79166,9 @@ OUI:ACAB8D*
OUI:ACABBF*
ID_OUI_FROM_DATABASE=AthenTek Inc.
+OUI:ACAE19*
+ ID_OUI_FROM_DATABASE=Roku, Inc
+
OUI:ACAFB9*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -76043,6 +79184,9 @@ OUI:ACB74F*
OUI:ACB859*
ID_OUI_FROM_DATABASE=Uniband Electronic Corp,
+OUI:ACBB61*
+ ID_OUI_FROM_DATABASE=YSTen Technology Co.,Ltd
+
OUI:ACBC32*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -76124,6 +79268,9 @@ OUI:ACD1B8*
OUI:ACD364*
ID_OUI_FROM_DATABASE=ABB SPA, ABB SACE DIV.
+OUI:ACD564*
+ ID_OUI_FROM_DATABASE=CHONGQING FUGUI ELECTRONICS CO.,LTD.
+
OUI:ACD657*
ID_OUI_FROM_DATABASE=Shaanxi GuoLian Digital TV Technology Co.,Ltd.
@@ -76193,6 +79340,9 @@ OUI:ACED5C*
OUI:ACEE3B*
ID_OUI_FROM_DATABASE=6harmonics Inc
+OUI:ACEE70*
+ ID_OUI_FROM_DATABASE=Fontem Ventures BV
+
OUI:ACEE9E*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -76205,6 +79355,9 @@ OUI:ACF1DF*
OUI:ACF2C5*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:ACF6F7*
+ ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+
OUI:ACF7F3*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
@@ -76226,9 +79379,15 @@ OUI:ACFDCE*
OUI:ACFDEC*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:B00073*
+ ID_OUI_FROM_DATABASE=Wistron Neweb Corporation
+
OUI:B000B4*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:B00247*
+ ID_OUI_FROM_DATABASE=AMPAK Technology, Inc.
+
OUI:B0027E*
ID_OUI_FROM_DATABASE=MULLER SERVICES
@@ -76244,6 +79403,9 @@ OUI:B009D3*
OUI:B009DA*
ID_OUI_FROM_DATABASE=Ring Solutions
+OUI:B00CD1*
+ ID_OUI_FROM_DATABASE=Hewlett Packard
+
OUI:B01041*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
@@ -76340,6 +79502,9 @@ OUI:B02680*
OUI:B02A43*
ID_OUI_FROM_DATABASE=Google, Inc.
+OUI:B033A6*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
OUI:B03495*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -76463,6 +79628,9 @@ OUI:B0672F*
OUI:B068B6*
ID_OUI_FROM_DATABASE=Hangzhou OYE Technology Co. Ltd
+OUI:B068E6*
+ ID_OUI_FROM_DATABASE=CHONGQING FUGUI ELECTRONICS CO.,LTD.
+
OUI:B06971*
ID_OUI_FROM_DATABASE=DEI Sales, Inc.
@@ -76472,6 +79640,9 @@ OUI:B06CBF*
OUI:B06EBF*
ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
+OUI:B06FE0*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:B0702D*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -76511,6 +79682,9 @@ OUI:B07D47*
OUI:B07D62*
ID_OUI_FROM_DATABASE=Dipl.-Ing. H. Horstmann GmbH
+OUI:B07E11*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:B07E70*
ID_OUI_FROM_DATABASE=Zadara Storage Ltd.
@@ -76631,6 +79805,9 @@ OUI:B0ACFA*
OUI:B0ADAA*
ID_OUI_FROM_DATABASE=Avaya Inc
+OUI:B0AE25*
+ ID_OUI_FROM_DATABASE=Varikorea
+
OUI:B0B28F*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
@@ -76655,6 +79832,9 @@ OUI:B0B8D5*
OUI:B0B98A*
ID_OUI_FROM_DATABASE=NETGEAR
+OUI:B0BB8B*
+ ID_OUI_FROM_DATABASE=WAVETEL TECHNOLOGY LIMITED
+
OUI:B0BD6D*
ID_OUI_FROM_DATABASE=Echostreams Innovative Solutions
@@ -76682,6 +79862,9 @@ OUI:B0C205*
OUI:B0C287*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+OUI:B0C387*
+ ID_OUI_FROM_DATABASE=GOEFER, Inc.
+
OUI:B0C46C*
ID_OUI_FROM_DATABASE=Senseit
@@ -76772,6 +79955,9 @@ OUI:B0D09C*
OUI:B0D2F5*
ID_OUI_FROM_DATABASE=Vello Systems, Inc.
+OUI:B0D568*
+ ID_OUI_FROM_DATABASE=Shenzhen Cultraview Digital Technology Co., Ltd
+
OUI:B0D59D*
ID_OUI_FROM_DATABASE=Shenzhen Zowee Technology Co., Ltd
@@ -76817,9 +80003,15 @@ OUI:B0E50E*
OUI:B0E5ED*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:B0E71D*
+ ID_OUI_FROM_DATABASE=Shanghai Maigantech Co.,Ltd
+
OUI:B0E754*
ID_OUI_FROM_DATABASE=2Wire Inc
+OUI:B0E7DE*
+ ID_OUI_FROM_DATABASE=Homa Technologies JSC
+
OUI:B0E892*
ID_OUI_FROM_DATABASE=Seiko Epson Corporation
@@ -76901,6 +80093,9 @@ OUI:B40AC6*
OUI:B40B44*
ID_OUI_FROM_DATABASE=Smartisan Technology Co., Ltd.
+OUI:B40B78*
+ ID_OUI_FROM_DATABASE=Brusa Elektronik AG
+
OUI:B40B7A*
ID_OUI_FROM_DATABASE=Brusa Elektronik AG
@@ -76916,6 +80111,9 @@ OUI:B40EDC*
OUI:B40F3B*
ID_OUI_FROM_DATABASE=Tenda Technology Co.,Ltd.Dongguan branch
+OUI:B40FB3*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
OUI:B41489*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -76931,6 +80129,9 @@ OUI:B418D1*
OUI:B41C30*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:B41D2B*
+ ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd
+
OUI:B41DEF*
ID_OUI_FROM_DATABASE=Internet Laboratories, Inc.
@@ -77072,6 +80273,9 @@ OUI:B44326*
OUI:B4475E*
ID_OUI_FROM_DATABASE=Avaya Inc
+OUI:B447F5*
+ ID_OUI_FROM_DATABASE=Earda Technologies co Ltd
+
OUI:B44BD2*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -77151,7 +80355,7 @@ OUI:B45CA4*
ID_OUI_FROM_DATABASE=Thing-talk Wireless Communication Technologies Corporation Limited
OUI:B45D50*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:B461FF*
ID_OUI_FROM_DATABASE=Lumigon A/S
@@ -77198,9 +80402,15 @@ OUI:B4749F*
OUI:B4750E*
ID_OUI_FROM_DATABASE=Belkin International Inc.
+OUI:B47748*
+ ID_OUI_FROM_DATABASE=Shenzhen Neoway Technology Co.,Ltd.
+
OUI:B479A7*
ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
+OUI:B479C8*
+ ID_OUI_FROM_DATABASE=Ruckus Wireless
+
OUI:B47C29*
ID_OUI_FROM_DATABASE=Shenzhen Guzidi Technology Co.,Ltd
@@ -77300,6 +80510,9 @@ OUI:B4A95A*
OUI:B4A984*
ID_OUI_FROM_DATABASE=Symantec Corporation
+OUI:B4A9FC*
+ ID_OUI_FROM_DATABASE=Quanta Computer Inc.
+
OUI:B4A9FE*
ID_OUI_FROM_DATABASE=GHIA Technology (Shenzhen) LTD
@@ -77363,6 +80576,9 @@ OUI:B4C170*
OUI:B4C44E*
ID_OUI_FROM_DATABASE=VXL eTech Pvt Ltd
+OUI:B4C62E*
+ ID_OUI_FROM_DATABASE=Molex CMS
+
OUI:B4C6F8*
ID_OUI_FROM_DATABASE=Axilspot Communication
@@ -77390,6 +80606,9 @@ OUI:B4CEFE*
OUI:B4CFDB*
ID_OUI_FROM_DATABASE=Shenzhen Jiuzhou Electric Co.,LTD
+OUI:B4D0A9*
+ ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
+
OUI:B4D135*
ID_OUI_FROM_DATABASE=Cloudistics
@@ -77454,7 +80673,7 @@ OUI:B4E9B0*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:B4EC02*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:B4ED19*
ID_OUI_FROM_DATABASE=Pie Digital, Inc.
@@ -77498,6 +80717,9 @@ OUI:B4F7A1*
OUI:B4F81E*
ID_OUI_FROM_DATABASE=Kinova
+OUI:B4F949*
+ ID_OUI_FROM_DATABASE=optilink networks pvt ltd
+
OUI:B4FBE4*
ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc.
@@ -77576,6 +80798,9 @@ OUI:B8241A*
OUI:B824F0*
ID_OUI_FROM_DATABASE=SOYO Technology Development Co., Ltd.
+OUI:B8259A*
+ ID_OUI_FROM_DATABASE=Thalmic Labs
+
OUI:B8266C*
ID_OUI_FROM_DATABASE=ANOV France
@@ -77598,11 +80823,14 @@ OUI:B82ADC*
ID_OUI_FROM_DATABASE=EFR Europäische Funk-Rundsteuerung GmbH
OUI:B82CA0*
- ID_OUI_FROM_DATABASE=Honeywell HomMed
+ ID_OUI_FROM_DATABASE=Resideo
OUI:B830A8*
ID_OUI_FROM_DATABASE=Road-Track Telematics Development
+OUI:B831B5*
+ ID_OUI_FROM_DATABASE=Microsoft Corporation
+
OUI:B83241*
ID_OUI_FROM_DATABASE=Wuhan Tianyu Information Industry Co., Ltd.
@@ -77678,6 +80906,9 @@ OUI:B85AF7*
OUI:B85AFE*
ID_OUI_FROM_DATABASE=Handaer Communication Technology (Beijing) Co., Ltd
+OUI:B85D0A*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:B85E7B*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -77735,6 +80966,9 @@ OUI:B8763F*
OUI:B877C3*
ID_OUI_FROM_DATABASE=METER Group
+OUI:B87826*
+ ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd
+
OUI:B8782E*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -77801,6 +81035,9 @@ OUI:B88EDF*
OUI:B88F14*
ID_OUI_FROM_DATABASE=Analytica GmbH
+OUI:B88FB4*
+ ID_OUI_FROM_DATABASE=JABIL CIRCUIT ITALIA S.R.L
+
OUI:B8921D*
ID_OUI_FROM_DATABASE=BG T&A
@@ -77828,6 +81065,9 @@ OUI:B89919*
OUI:B899B0*
ID_OUI_FROM_DATABASE=Cohere Technologies
+OUI:B89A9A*
+ ID_OUI_FROM_DATABASE=Xin Shi Jia Technology (Beijing) Co.,Ltd
+
OUI:B89ACD*
ID_OUI_FROM_DATABASE=ELITE OPTOELECTRONIC(ASIA)CO.,LTD
@@ -77876,6 +81116,9 @@ OUI:B8B1C7*
OUI:B8B2EB*
ID_OUI_FROM_DATABASE=Googol Technology (HK) Limited
+OUI:B8B2F8*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:B8B3DC*
ID_OUI_FROM_DATABASE=DEREK (SHAOGUAN) LIMITED
@@ -77912,6 +81155,9 @@ OUI:B8BBAF*
OUI:B8BC1B*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:B8BC5B*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:B8BD79*
ID_OUI_FROM_DATABASE=TrendPoint Systems
@@ -77930,6 +81176,15 @@ OUI:B8C111*
OUI:B8C1A2*
ID_OUI_FROM_DATABASE=Dragon Path Technologies Co., Limited
+OUI:B8C227*
+ ID_OUI_FROM_DATABASE=PSTec
+
+OUI:B8C253*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
+OUI:B8C385*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:B8C3BF*
ID_OUI_FROM_DATABASE=Henan Chengshi NetWork Technology Co.,Ltd
@@ -77942,6 +81197,9 @@ OUI:B8C68E*
OUI:B8C716*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+OUI:B8C74A*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:B8C75D*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -78077,6 +81335,9 @@ OUI:B8EE65*
OUI:B8EE79*
ID_OUI_FROM_DATABASE=YWire Technologies, Inc.
+OUI:B8EF8B*
+ ID_OUI_FROM_DATABASE=SHENZHEN CANNICE TECHNOLOGY CO.,LTD
+
OUI:B8F080*
ID_OUI_FROM_DATABASE=SPS, INC.
@@ -78200,6 +81461,9 @@ OUI:BC261D*
OUI:BC2643*
ID_OUI_FROM_DATABASE=Elprotronic Inc.
+OUI:BC26C7*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:BC282C*
ID_OUI_FROM_DATABASE=e-Smart Systems Pvt. Ltd
@@ -78293,6 +81557,9 @@ OUI:BC3400F*
OUI:BC35E5*
ID_OUI_FROM_DATABASE=Hydro Systems Company
+OUI:BC3865*
+ ID_OUI_FROM_DATABASE=JWCNETWORKS
+
OUI:BC38D2*
ID_OUI_FROM_DATABASE=Pandachip Limited
@@ -78311,9 +81578,15 @@ OUI:BC3BAF*
OUI:BC3D85*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:BC3E07*
+ ID_OUI_FROM_DATABASE=Hitron Technologies. Inc
+
OUI:BC3E13*
ID_OUI_FROM_DATABASE=Accordance Systems Inc.
+OUI:BC3F4E*
+ ID_OUI_FROM_DATABASE=Teleepoch Ltd
+
OUI:BC3F8F*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -78383,6 +81656,9 @@ OUI:BC54FC*
OUI:BC5C4C*
ID_OUI_FROM_DATABASE=ELECOM CO.,LTD.
+OUI:BC5EA1*
+ ID_OUI_FROM_DATABASE=PsiKick, Inc.
+
OUI:BC5FF4*
ID_OUI_FROM_DATABASE=ASRock Incorporation
@@ -78497,9 +81773,15 @@ OUI:BC72B1*
OUI:BC74D7*
ID_OUI_FROM_DATABASE=HangZhou JuRu Technology CO.,LTD
+OUI:BC7536*
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
+
OUI:BC7574*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:BC7596*
+ ID_OUI_FROM_DATABASE=Beijing Broadwit Technology Co., Ltd.
+
OUI:BC764E*
ID_OUI_FROM_DATABASE=Rackspace US, Inc.
@@ -78572,6 +81854,9 @@ OUI:BC91B5*
OUI:BC926B*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:BC9325*
+ ID_OUI_FROM_DATABASE=Ningbo Joyson Preh Car Connect Co.,Ltd.
+
OUI:BC9680*
ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
@@ -78584,6 +81869,9 @@ OUI:BC9911*
OUI:BC99BC*
ID_OUI_FROM_DATABASE=FonSee Technology Inc.
+OUI:BC9B68*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
OUI:BC9C31*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -78644,6 +81932,9 @@ OUI:BCB308*
OUI:BCB852*
ID_OUI_FROM_DATABASE=Cybera, Inc.
+OUI:BCB863*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:BCBAE1*
ID_OUI_FROM_DATABASE=AREC Inc.
@@ -78686,6 +81977,9 @@ OUI:BCCAB5*
OUI:BCCD45*
ID_OUI_FROM_DATABASE=VOISMART
+OUI:BCCF4F*
+ ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
+
OUI:BCCFCC*
ID_OUI_FROM_DATABASE=HTC Corporation
@@ -78731,6 +82025,9 @@ OUI:BCE63F*
OUI:BCE767*
ID_OUI_FROM_DATABASE=Quanzhou TDX Electronics Co., Ltd
+OUI:BCE796*
+ ID_OUI_FROM_DATABASE=Wireless CCTV Ltd
+
OUI:BCEA2B*
ID_OUI_FROM_DATABASE=CityCom GmbH
@@ -78758,6 +82055,9 @@ OUI:BCF292*
OUI:BCF2AF*
ID_OUI_FROM_DATABASE=devolo AG
+OUI:BCF310*
+ ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+
OUI:BCF5AC*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
@@ -78773,6 +82073,9 @@ OUI:BCF811*
OUI:BCFE8C*
ID_OUI_FROM_DATABASE=Altronic, LLC
+OUI:BCFED9*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:BCFFAC*
ID_OUI_FROM_DATABASE=TOPCON CORPORATION
@@ -78788,9 +82091,15 @@ OUI:C00380*
OUI:C005C2*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:C0074A*
+ ID_OUI_FROM_DATABASE=Brita GmbH
+
OUI:C00D7E*
ID_OUI_FROM_DATABASE=Additech, Inc.
+OUI:C010B1*
+ ID_OUI_FROM_DATABASE=HMD Global Oy
+
OUI:C01173*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -78800,6 +82109,9 @@ OUI:C011A6*
OUI:C01242*
ID_OUI_FROM_DATABASE=Alpha Security Products
+OUI:C0132B*
+ ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd.
+
OUI:C0143D*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
@@ -78812,6 +82124,9 @@ OUI:C01885*
OUI:C01ADA*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:C01B23*
+ ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+
OUI:C01E9B*
ID_OUI_FROM_DATABASE=Pixavi AS
@@ -78819,7 +82134,7 @@ OUI:C0210D*
ID_OUI_FROM_DATABASE=SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.
OUI:C02250*
- ID_OUI_FROM_DATABASE=Private
+ ID_OUI_FROM_DATABASE=Koss Corporation
OUI:C02506*
ID_OUI_FROM_DATABASE=AVM GmbH
@@ -78857,6 +82172,9 @@ OUI:C02C7A*
OUI:C02DEE*
ID_OUI_FROM_DATABASE=Cuff
+OUI:C02E25*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:C02FF1*
ID_OUI_FROM_DATABASE=Volta Networks
@@ -78887,6 +82205,9 @@ OUI:C03B8F*
OUI:C03D46*
ID_OUI_FROM_DATABASE=Shanghai Sango Network Technology Co.,Ltd
+OUI:C03DD9*
+ ID_OUI_FROM_DATABASE=MitraStar Technology Corp.
+
OUI:C03E0F*
ID_OUI_FROM_DATABASE=BSkyB Ltd
@@ -78902,6 +82223,9 @@ OUI:C03FD5*
OUI:C04004*
ID_OUI_FROM_DATABASE=Medicaroid Corporation
+OUI:C04121*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:C041F6*
ID_OUI_FROM_DATABASE=LG ELECTRONICS INC
@@ -78932,6 +82256,9 @@ OUI:C04A09*
OUI:C04DF7*
ID_OUI_FROM_DATABASE=SERELEC
+OUI:C05336*
+ ID_OUI_FROM_DATABASE=Beijing National Railway Research & Design Institute of Signal & Communication Group Co..Ltd.
+
OUI:C05627*
ID_OUI_FROM_DATABASE=Belkin International Inc.
@@ -78983,6 +82310,12 @@ OUI:C07009*
OUI:C0742B*
ID_OUI_FROM_DATABASE=SHENZHEN XUNLONG SOFTWARE CO.,LIMITED
+OUI:C074AD*
+ ID_OUI_FROM_DATABASE=Grandstream Networks, Inc.
+
+OUI:C07878*
+ ID_OUI_FROM_DATABASE=FLEXTRONICS MANUFACTURING(ZHUHAI)CO.,LTD.
+
OUI:C07BBC*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -79067,6 +82400,9 @@ OUI:C0885B*
OUI:C08997*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:C08ACD*
+ ID_OUI_FROM_DATABASE=Guangzhou Shiyuan Electronic Technology Company Limited
+
OUI:C08ADE*
ID_OUI_FROM_DATABASE=Ruckus Wireless
@@ -79076,6 +82412,9 @@ OUI:C08B6F*
OUI:C08C60*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:C08C71*
+ ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+
OUI:C09132*
ID_OUI_FROM_DATABASE=Patriot Memory
@@ -79097,6 +82436,9 @@ OUI:C098E5*
OUI:C09A71*
ID_OUI_FROM_DATABASE=XIAMEN MEITU MOBILE TECHNOLOGY CO.LTD
+OUI:C09AD0*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:C09C04*
ID_OUI_FROM_DATABASE=Shaanxi GuoLian Digital TV Technology Co.,Ltd.
@@ -79145,6 +82487,9 @@ OUI:C0A53E*
OUI:C0A5DD*
ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.
+OUI:C0A600*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:C0A8F0*
ID_OUI_FROM_DATABASE=Adamson Systems Engineering
@@ -79160,6 +82505,9 @@ OUI:C0B339*
OUI:C0B357*
ID_OUI_FROM_DATABASE=Yoshiki Electronics Industry Ltd.
+OUI:C0B5D7*
+ ID_OUI_FROM_DATABASE=CHONGQING FUGUI ELECTRONICS CO.,LTD.
+
OUI:C0B658*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -79178,9 +82526,15 @@ OUI:C0BAE6*
OUI:C0BD42*
ID_OUI_FROM_DATABASE=ZPA Smart Energy a.s.
+OUI:C0BDC8*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:C0BDD1*
ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
+OUI:C0BFA7*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
OUI:C0BFC0*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -79280,6 +82634,9 @@ OUI:C0D391E*
OUI:C0D3C0*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:C0D834*
+ ID_OUI_FROM_DATABASE=xvtec ltd
+
OUI:C0D962*
ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP
@@ -79304,6 +82661,9 @@ OUI:C0E42D*
OUI:C0E54E*
ID_OUI_FROM_DATABASE=ARIES Embedded GmbH
+OUI:C0E862*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:C0EAE4*
ID_OUI_FROM_DATABASE=Sonicwall
@@ -79373,6 +82733,9 @@ OUI:C4047B*
OUI:C40528*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:C40683*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:C4072F*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -79457,6 +82820,9 @@ OUI:C4282D*
OUI:C4291D*
ID_OUI_FROM_DATABASE=KLEMSAN ELEKTRIK ELEKTRONIK SAN.VE TIC.AS.
+OUI:C42AD0*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:C42C03*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -79604,6 +82970,9 @@ OUI:C46354*
OUI:C46413*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:C464B7*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:C464E3*
ID_OUI_FROM_DATABASE=Texas Instruments
@@ -79652,6 +83021,9 @@ OUI:C47295*
OUI:C4731E*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:C474F8*
+ ID_OUI_FROM_DATABASE=Hot Pepper, Inc.
+
OUI:C477AB*
ID_OUI_FROM_DATABASE=Beijing ASU Tech Co.,Ltd
@@ -79907,6 +83279,9 @@ OUI:C4E032*
OUI:C4E17C*
ID_OUI_FROM_DATABASE=U2S co.
+OUI:C4E506*
+ ID_OUI_FROM_DATABASE=Piper Networks, Inc.
+
OUI:C4E510*
ID_OUI_FROM_DATABASE=Mechatro, Inc.
@@ -79940,6 +83315,9 @@ OUI:C4EF70*
OUI:C4F081*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:C4F0EC*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:C4F1D1*
ID_OUI_FROM_DATABASE=BEIJING SOGOU TECHNOLOGY DEVELOPMENT CO., LTD.
@@ -79955,9 +83333,15 @@ OUI:C4F57C*
OUI:C4F5A5*
ID_OUI_FROM_DATABASE=Kumalift Co., Ltd.
+OUI:C4F839*
+ ID_OUI_FROM_DATABASE=Actia Automotive
+
OUI:C4FCE4*
ID_OUI_FROM_DATABASE=DishTV NZ Ltd
+OUI:C4FDE6*
+ ID_OUI_FROM_DATABASE=DRTECH
+
OUI:C4FEE2*
ID_OUI_FROM_DATABASE=AMICCOM Electronics Corporation
@@ -80027,6 +83411,9 @@ OUI:C802A6*
OUI:C80718*
ID_OUI_FROM_DATABASE=TDSi
+OUI:C80873*
+ ID_OUI_FROM_DATABASE=Ruckus Wireless
+
OUI:C808E9*
ID_OUI_FROM_DATABASE=LG Electronics
@@ -80096,12 +83483,18 @@ OUI:C82158*
OUI:C825E1*
ID_OUI_FROM_DATABASE=Lemobile Information Technology (Beijing) Co., Ltd
+OUI:C82832*
+ ID_OUI_FROM_DATABASE=Beijing Xiaomi Electronics Co., Ltd.
+
OUI:C8292A*
ID_OUI_FROM_DATABASE=Barun Electronics
OUI:C82A14*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:C82E47*
+ ID_OUI_FROM_DATABASE=Suzhou SmartChip Semiconductor Co., LTD
+
OUI:C82E94*
ID_OUI_FROM_DATABASE=Halfa Enterprise Co., Ltd.
@@ -80168,6 +83561,9 @@ OUI:C84544*
OUI:C8458F*
ID_OUI_FROM_DATABASE=Wyler AG
+OUI:C84782*
+ ID_OUI_FROM_DATABASE=Areson Technology Corp.
+
OUI:C8478C*
ID_OUI_FROM_DATABASE=Beken Corporation
@@ -80177,12 +83573,18 @@ OUI:C848F5*
OUI:C84C75*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:C84F86*
+ ID_OUI_FROM_DATABASE=Sophos Ltd
+
OUI:C850E9*
ID_OUI_FROM_DATABASE=Raisecom Technology CO., LTD
OUI:C85195*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:C85261*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:C8544B*
ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
@@ -80354,6 +83756,9 @@ OUI:C894D2*
OUI:C8979F*
ID_OUI_FROM_DATABASE=Nokia Corporation
+OUI:C89C13*
+ ID_OUI_FROM_DATABASE=Inspiremobile
+
OUI:C89C1D*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -80453,6 +83858,9 @@ OUI:C8C2C6*
OUI:C8C2F5*
ID_OUI_FROM_DATABASE=FLEXTRONICS MANUFACTURING(ZHUHAI)CO.,LTD.
+OUI:C8C2FA*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:C8C50E*
ID_OUI_FROM_DATABASE=Shenzhen Primestone Network Technologies.Co., Ltd.
@@ -80582,12 +83990,21 @@ OUI:C8F650*
OUI:C8F68D*
ID_OUI_FROM_DATABASE=S.E.TECHNOLOGIES LIMITED
+OUI:C8F6C8*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:C8F704*
ID_OUI_FROM_DATABASE=Building Block Video
OUI:C8F733*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:C8F742*
+ ID_OUI_FROM_DATABASE=HangZhou Gubei Electronics Technology Co.,Ltd
+
+OUI:C8F750*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:C8F86D*
ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd
@@ -80804,6 +84221,9 @@ OUI:CC29F5*
OUI:CC2A80*
ID_OUI_FROM_DATABASE=Micro-Biz intelligence solutions Co.,Ltd
+OUI:CC2C83*
+ ID_OUI_FROM_DATABASE=DarkMatter L.L.C
+
OUI:CC2D21*
ID_OUI_FROM_DATABASE=Tenda Technology Co.,Ltd.Dongguan branch
@@ -80837,6 +84257,9 @@ OUI:CC34D7*
OUI:CC3540*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+OUI:CC355A*
+ ID_OUI_FROM_DATABASE=SecuGen Corporation
+
OUI:CC37AB*
ID_OUI_FROM_DATABASE=Edgecore Networks Corportation
@@ -80867,6 +84290,9 @@ OUI:CC3E5F*
OUI:CC3F1D*
ID_OUI_FROM_DATABASE=Intesis Software SL
+OUI:CC3FEA*
+ ID_OUI_FROM_DATABASE=BAE Systems, Inc
+
OUI:CC40D0*
ID_OUI_FROM_DATABASE=NETGEAR
@@ -80978,15 +84404,24 @@ OUI:CC6DEF*
OUI:CC6EA4*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:CC70ED*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:CC720F*
ID_OUI_FROM_DATABASE=Viscount Systems Inc.
+OUI:CC7286*
+ ID_OUI_FROM_DATABASE=Xi'an Fengyu Information Technology Co., Ltd.
+
OUI:CC7314*
ID_OUI_FROM_DATABASE=HONG KONG WHEATEK TECHNOLOGY LIMITED
OUI:CC7498*
ID_OUI_FROM_DATABASE=Filmetrics Inc.
+OUI:CC75E2*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:CC7669*
ID_OUI_FROM_DATABASE=SEETECH
@@ -81065,6 +84500,9 @@ OUI:CC9635*
OUI:CC96A0*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:CC988B*
+ ID_OUI_FROM_DATABASE=SONY Visual Products Inc.
+
OUI:CC9891*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -81074,6 +84512,9 @@ OUI:CC9916*
OUI:CC9E00*
ID_OUI_FROM_DATABASE=Nintendo Co., Ltd.
+OUI:CC9EA2*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:CC9F35*
ID_OUI_FROM_DATABASE=Transbit Sp. z o.o.
@@ -81137,6 +84578,9 @@ OUI:CCB8A8*
OUI:CCB8F1*
ID_OUI_FROM_DATABASE=EAGLE KINGDOM TECHNOLOGIES LIMITED
+OUI:CCBBFE*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:CCBD35*
ID_OUI_FROM_DATABASE=Steinel GmbH
@@ -81197,6 +84641,9 @@ OUI:CCCE1E*
OUI:CCCE40*
ID_OUI_FROM_DATABASE=Janteq Corp
+OUI:CCD281*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:CCD29B*
ID_OUI_FROM_DATABASE=Shenzhen Bopengfa Elec&Technology CO.,Ltd
@@ -81245,23 +84692,77 @@ OUI:CCD31ED*
OUI:CCD31EE*
ID_OUI_FROM_DATABASE=ShenZhenBoryNet Co.,LTD.
+OUI:CCD39D1*
+ ID_OUI_FROM_DATABASE=Evoko Unlimited AB
+
+OUI:CCD39D2*
+ ID_OUI_FROM_DATABASE=Continental Control Systems
+
+OUI:CCD39D3*
+ ID_OUI_FROM_DATABASE=MagTarget LLC
+
+OUI:CCD39D4*
+ ID_OUI_FROM_DATABASE=Shenzhen Chenggu Technology Co., Ltd
+
+OUI:CCD39D5*
+ ID_OUI_FROM_DATABASE=SHENZHEN ROYOLE TECHNOLOGIES CO., LTD.
+
+OUI:CCD39D6*
+ ID_OUI_FROM_DATABASE=Krontech
+
+OUI:CCD39D7*
+ ID_OUI_FROM_DATABASE=Glenair
+
+OUI:CCD39D8*
+ ID_OUI_FROM_DATABASE=Obelisk Inc.
+
+OUI:CCD39D9*
+ ID_OUI_FROM_DATABASE=Bejing Nexsec Inc.
+
+OUI:CCD39DA*
+ ID_OUI_FROM_DATABASE=Lubelskie Fabryki Wag FAWAG S.A.
+
+OUI:CCD39DB*
+ ID_OUI_FROM_DATABASE=Q-Branch Labs, Inc.
+
+OUI:CCD39DC*
+ ID_OUI_FROM_DATABASE=Hangzhou Scooper Technology Co.,Ltd.
+
+OUI:CCD39DD*
+ ID_OUI_FROM_DATABASE=Ethernity Networks
+
+OUI:CCD39DE*
+ ID_OUI_FROM_DATABASE=Shanghai tongli information technology co. LTD
+
+OUI:CCD3C1*
+ ID_OUI_FROM_DATABASE=Vestel Elektronik San ve Tic. A.Åž.
+
OUI:CCD3E2*
ID_OUI_FROM_DATABASE=Jiangsu Yinhe Electronics Co.,Ltd.
+OUI:CCD4A1*
+ ID_OUI_FROM_DATABASE=MitraStar Technology Corp.
+
OUI:CCD539*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:CCD811*
ID_OUI_FROM_DATABASE=Aiconn Technology Corporation
+OUI:CCD81F*
+ ID_OUI_FROM_DATABASE=Maipu Communication Technology Co.,Ltd.
+
OUI:CCD8C1*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:CCD9E9*
ID_OUI_FROM_DATABASE=SCR Engineers Ltd.
+OUI:CCDC55*
+ ID_OUI_FROM_DATABASE=Dragonchip Limited
+
OUI:CCE0C3*
- ID_OUI_FROM_DATABASE=Mangstor, Inc.
+ ID_OUI_FROM_DATABASE=EXTEN Technologies, Inc.
OUI:CCE17F*
ID_OUI_FROM_DATABASE=Juniper Networks
@@ -81281,6 +84782,9 @@ OUI:CCE8AC*
OUI:CCEA1C*
ID_OUI_FROM_DATABASE=DCONWORKS Co., Ltd
+OUI:CCEDDC*
+ ID_OUI_FROM_DATABASE=MitraStar Technology Corp.
+
OUI:CCEED9*
ID_OUI_FROM_DATABASE=VAHLE Automation GmbH
@@ -81302,6 +84806,9 @@ OUI:CCF538*
OUI:CCF67A*
ID_OUI_FROM_DATABASE=Ayecka Communication Systems LTD
+OUI:CCF735*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:CCF841*
ID_OUI_FROM_DATABASE=Lumewave
@@ -81518,6 +85025,9 @@ OUI:D04F7E*
OUI:D05099*
ID_OUI_FROM_DATABASE=ASRock Incorporation
+OUI:D05157*
+ ID_OUI_FROM_DATABASE=LEAX Arkivator Telecom
+
OUI:D05162*
ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
@@ -81551,6 +85061,9 @@ OUI:D05875*
OUI:D058A8*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:D058C0*
+ ID_OUI_FROM_DATABASE=Qingdao Haier Multimedia Limited.
+
OUI:D058FC*
ID_OUI_FROM_DATABASE=BSkyB Ltd
@@ -81620,6 +85133,9 @@ OUI:D069D0*
OUI:D06A1F*
ID_OUI_FROM_DATABASE=BSE CO.,LTD.
+OUI:D06EDE*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
OUI:D06F4A*
ID_OUI_FROM_DATABASE=TOPWELL INTERNATIONAL HOLDINGS LIMITED
@@ -81752,6 +85268,9 @@ OUI:D08CFF*
OUI:D0929E*
ID_OUI_FROM_DATABASE=Microsoft Corporation
+OUI:D092FA*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:D09380*
ID_OUI_FROM_DATABASE=Ducere Technologies Pvt. Ltd.
@@ -81797,6 +85316,9 @@ OUI:D0A5A6*
OUI:D0A637*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:D0ABD5*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:D0AEEC*
ID_OUI_FROM_DATABASE=Alpha Networks Inc.
@@ -81830,6 +85352,9 @@ OUI:D0B53D*
OUI:D0B5C2*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:D0B60A*
+ ID_OUI_FROM_DATABASE=Xingluo Technology Company Limited
+
OUI:D0BAE4*
ID_OUI_FROM_DATABASE=Shanghai MXCHIP Information Technology Co., Ltd.
@@ -81992,6 +85517,9 @@ OUI:D0EB03*
OUI:D0EB9E*
ID_OUI_FROM_DATABASE=Seowoo Inc.
+OUI:D0EC35*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:D0EFC1*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -82061,9 +85589,15 @@ OUI:D41090*
OUI:D410CF*
ID_OUI_FROM_DATABASE=Huanshun Network Science and Technology Co., Ltd.
+OUI:D411A3*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:D411D6*
ID_OUI_FROM_DATABASE=ShotSpotter, Inc.
+OUI:D41243*
+ ID_OUI_FROM_DATABASE=AMPAK Technology, Inc.
+
OUI:D41296*
ID_OUI_FROM_DATABASE=Anobit Technologies Ltd.
@@ -82103,6 +85637,48 @@ OUI:D4224E*
OUI:D4258B*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:D425CC0*
+ ID_OUI_FROM_DATABASE=NORDI TELEKOMMUNIKATSIOONI OÜ
+
+OUI:D425CC1*
+ ID_OUI_FROM_DATABASE=Eware Information Technology com.,Ltd
+
+OUI:D425CC2*
+ ID_OUI_FROM_DATABASE=MusicLens Inc.
+
+OUI:D425CC3*
+ ID_OUI_FROM_DATABASE=EISST Ltd
+
+OUI:D425CC4*
+ ID_OUI_FROM_DATABASE=Barobo, Inc.
+
+OUI:D425CC5*
+ ID_OUI_FROM_DATABASE=bvk technology
+
+OUI:D425CC6*
+ ID_OUI_FROM_DATABASE=Nanjing LES Information Technology Co., Ltd
+
+OUI:D425CC8*
+ ID_OUI_FROM_DATABASE=DOLBY LABORATORIES, INC.
+
+OUI:D425CC9*
+ ID_OUI_FROM_DATABASE=TAKUMI JAPAN LTD
+
+OUI:D425CCA*
+ ID_OUI_FROM_DATABASE=E-MetroTel
+
+OUI:D425CCB*
+ ID_OUI_FROM_DATABASE=Veea
+
+OUI:D425CCC*
+ ID_OUI_FROM_DATABASE=POSNET Polska S.A.
+
+OUI:D425CCD*
+ ID_OUI_FROM_DATABASE=Combined Energy Technologies Pty Ltd
+
+OUI:D425CCE*
+ ID_OUI_FROM_DATABASE=Coperion
+
OUI:D42751*
ID_OUI_FROM_DATABASE=Infopia Co., Ltd
@@ -82130,6 +85706,9 @@ OUI:D42F23*
OUI:D4319D*
ID_OUI_FROM_DATABASE=Sinwatec
+OUI:D43260*
+ ID_OUI_FROM_DATABASE=GoPro
+
OUI:D43266*
ID_OUI_FROM_DATABASE=Fike Corporation
@@ -82145,18 +85724,30 @@ OUI:D437D7*
OUI:D4389C*
ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
+OUI:D43A2E*
+ ID_OUI_FROM_DATABASE=SHENZHEN MTC CO LTD
+
OUI:D43A65*
ID_OUI_FROM_DATABASE=IGRS Engineering Lab Ltd.
OUI:D43AE9*
ID_OUI_FROM_DATABASE=DONGGUAN ipt INDUSTRIAL CO., LTD
+OUI:D43B04*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
+OUI:D43D39*
+ ID_OUI_FROM_DATABASE=FCI. Inc
+
OUI:D43D67*
ID_OUI_FROM_DATABASE=Carma Industries Inc.
OUI:D43D7E*
ID_OUI_FROM_DATABASE=Micro-Star Int'l Co, Ltd
+OUI:D43FCB*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:D440F0*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -82199,6 +85790,9 @@ OUI:D45251*
OUI:D45297*
ID_OUI_FROM_DATABASE=nSTREAMS Technologies, Inc.
+OUI:D45383*
+ ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
+
OUI:D453AF*
ID_OUI_FROM_DATABASE=VIGO System S.A.
@@ -82208,6 +85802,9 @@ OUI:D45556*
OUI:D455BE*
ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD
+OUI:D45800*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:D45AB2*
ID_OUI_FROM_DATABASE=Galleon Systems
@@ -82256,6 +85853,9 @@ OUI:D466A8*
OUI:D46761*
ID_OUI_FROM_DATABASE=United Gulf Gate Co.
+OUI:D467D3*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:D467E7*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
@@ -82424,6 +86024,9 @@ OUI:D490E0*
OUI:D491AF*
ID_OUI_FROM_DATABASE=Electroacustica General Iberica, S.A.
+OUI:D49234*
+ ID_OUI_FROM_DATABASE=NEC Corporation
+
OUI:D49398*
ID_OUI_FROM_DATABASE=Nokia Corporation
@@ -82460,6 +86063,9 @@ OUI:D49C28*
OUI:D49C8E*
ID_OUI_FROM_DATABASE=University of FUKUI
+OUI:D49CDD*
+ ID_OUI_FROM_DATABASE=AMPAK Technology,Inc.
+
OUI:D49CF4*
ID_OUI_FROM_DATABASE=Palo Alto Networks
@@ -82499,6 +86105,9 @@ OUI:D4AC4E*
OUI:D4AD2D*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+OUI:D4AD71*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:D4AE05*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -82517,9 +86126,18 @@ OUI:D4B27A*
OUI:D4B43E*
ID_OUI_FROM_DATABASE=Messcomp Datentechnik GmbH
+OUI:D4B761*
+ ID_OUI_FROM_DATABASE=Sichuan AI-Link Technology Co., Ltd.
+
OUI:D4B8FF*
ID_OUI_FROM_DATABASE=Home Control Singapore Pte Ltd
+OUI:D4BBC8*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
+OUI:D4BD1E*
+ ID_OUI_FROM_DATABASE=5VT Technologies,Taiwan LTd.
+
OUI:D4BED9*
ID_OUI_FROM_DATABASE=Dell Inc.
@@ -82544,6 +86162,12 @@ OUI:D4C766*
OUI:D4C8B0*
ID_OUI_FROM_DATABASE=Prime Electronics & Satellitics Inc.
+OUI:D4C93C*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
+OUI:D4C94B*
+ ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+
OUI:D4C9B2*
ID_OUI_FROM_DATABASE=Quanergy Systems Inc
@@ -82610,6 +86234,9 @@ OUI:D4E33F*
OUI:D4E6B7*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:D4E880*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:D4E8B2*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -82649,6 +86276,9 @@ OUI:D4F4BE*
OUI:D4F513*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:D4F527*
+ ID_OUI_FROM_DATABASE=SIEMENS AG
+
OUI:D4F63F*
ID_OUI_FROM_DATABASE=IEA S.R.L.
@@ -82721,9 +86351,15 @@ OUI:D81BFE*
OUI:D81C14*
ID_OUI_FROM_DATABASE=Compacta International, Ltd.
+OUI:D81C79*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:D81D72*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:D81EDD*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:D81EDE*
ID_OUI_FROM_DATABASE=B&W Group Ltd
@@ -82802,6 +86438,9 @@ OUI:D8380D*
OUI:D838FC*
ID_OUI_FROM_DATABASE=Ruckus Wireless
+OUI:D83AF5*
+ ID_OUI_FROM_DATABASE=Wideband Labs LLC
+
OUI:D83C69*
ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp.
@@ -82853,6 +86492,9 @@ OUI:D8543A*
OUI:D854A2*
ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+OUI:D85575*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:D855A3*
ID_OUI_FROM_DATABASE=zte corporation
@@ -82967,6 +86609,48 @@ OUI:D881CE*
OUI:D88466*
ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
+OUI:D8860B0*
+ ID_OUI_FROM_DATABASE=Inspur Group Co., Ltd.
+
+OUI:D8860B1*
+ ID_OUI_FROM_DATABASE=Krspace
+
+OUI:D8860B2*
+ ID_OUI_FROM_DATABASE=Get SAT
+
+OUI:D8860B3*
+ ID_OUI_FROM_DATABASE=Auvidea GmbH
+
+OUI:D8860B4*
+ ID_OUI_FROM_DATABASE=Teplovodokhran Ltd.
+
+OUI:D8860B5*
+ ID_OUI_FROM_DATABASE=CAMTRACE
+
+OUI:D8860B6*
+ ID_OUI_FROM_DATABASE=SCANMATIK
+
+OUI:D8860B7*
+ ID_OUI_FROM_DATABASE=Grünbeck Wasseraufbereitung GmbH
+
+OUI:D8860B8*
+ ID_OUI_FROM_DATABASE=VRINDA NANO TECHNOLOGIES PVT LTD
+
+OUI:D8860B9*
+ ID_OUI_FROM_DATABASE=DIGITAL CONCEPTS
+
+OUI:D8860BA*
+ ID_OUI_FROM_DATABASE=GLO Science
+
+OUI:D8860BB*
+ ID_OUI_FROM_DATABASE=Library Ideas
+
+OUI:D8860BD*
+ ID_OUI_FROM_DATABASE=ComNav Technology Ltd.
+
+OUI:D8860BE*
+ ID_OUI_FROM_DATABASE=Shenzhen Yidong Technology Co.,Ltd
+
OUI:D887D5*
ID_OUI_FROM_DATABASE=Leadcore Technology CO.,LTD
@@ -82982,6 +86666,9 @@ OUI:D88B4C*
OUI:D88D5C*
ID_OUI_FROM_DATABASE=Elentec
+OUI:D88DC8*
+ ID_OUI_FROM_DATABASE=Atil Technology Co., LTD
+
OUI:D88F76*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -83024,6 +86711,9 @@ OUI:D897BA*
OUI:D89A34*
ID_OUI_FROM_DATABASE=Beijing SHENQI Technology Co., Ltd.
+OUI:D89B3B*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:D89C67*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
@@ -83051,6 +86741,15 @@ OUI:D8A25E*
OUI:D8A534*
ID_OUI_FROM_DATABASE=Spectronix Corporation
+OUI:D8A6FD*
+ ID_OUI_FROM_DATABASE=Ghost Locomotion
+
+OUI:D8A756*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
+OUI:D8A98B*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:D8ADDD*
ID_OUI_FROM_DATABASE=Sonavation, Inc.
@@ -83127,7 +86826,7 @@ OUI:D8C771*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:D8C7C8*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:D8C8E9*
ID_OUI_FROM_DATABASE=Phicomm (Shanghai) Co., Ltd.
@@ -83138,6 +86837,9 @@ OUI:D8C99D*
OUI:D8CB8A*
ID_OUI_FROM_DATABASE=Micro-Star INTL CO., LTD.
+OUI:D8CE3A*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
OUI:D8CF9C*
ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -83159,6 +86861,9 @@ OUI:D8D5B9*
OUI:D8D67E*
ID_OUI_FROM_DATABASE=GSK CNC EQUIPMENT CO.,LTD
+OUI:D8D6F3*
+ ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+
OUI:D8D723*
ID_OUI_FROM_DATABASE=IDS, Inc
@@ -83231,6 +86936,9 @@ OUI:D8F0F2*
OUI:D8F1F0*
ID_OUI_FROM_DATABASE=Pepxim International Limited
+OUI:D8F2CA*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:D8F3DB*
ID_OUI_FROM_DATABASE=Post CH AG
@@ -83279,6 +86987,9 @@ OUI:DC05ED*
OUI:DC07C1*
ID_OUI_FROM_DATABASE=HangZhou QiYang Technology Co.,Ltd.
+OUI:DC080F*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:DC0856*
ID_OUI_FROM_DATABASE=Alcatel-Lucent Enterprise
@@ -83339,6 +87050,9 @@ OUI:DC1EA3*
OUI:DC2008*
ID_OUI_FROM_DATABASE=ASD Electronics Ltd
+OUI:DC21B9*
+ ID_OUI_FROM_DATABASE=Sentec Co.Ltd
+
OUI:DC2834*
ID_OUI_FROM_DATABASE=HAKKO Corporation
@@ -83525,6 +87239,9 @@ OUI:DC56E7*
OUI:DC5726*
ID_OUI_FROM_DATABASE=Power-One
+OUI:DC58BC*
+ ID_OUI_FROM_DATABASE=Thomas-Krenn.AG
+
OUI:DC5E36*
ID_OUI_FROM_DATABASE=Paterson Technology
@@ -83543,6 +87260,9 @@ OUI:DC663A*
OUI:DC6672*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:DC6723*
+ ID_OUI_FROM_DATABASE=barox Kommunikation GmbH
+
OUI:DC68EB*
ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd
@@ -83597,6 +87317,9 @@ OUI:DC8B28*
OUI:DC9088*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:DC962C*
+ ID_OUI_FROM_DATABASE=NST Audio Ltd
+
OUI:DC9914*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -83756,6 +87479,9 @@ OUI:DCD916*
OUI:DCDA4F*
ID_OUI_FROM_DATABASE=GETCK TECHNOLOGY, INC
+OUI:DCDA80*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
OUI:DCDB70*
ID_OUI_FROM_DATABASE=Tonfunk Systementwicklung und Service GmbH
@@ -83843,12 +87569,18 @@ OUI:DCE838*
OUI:DCEB53*
ID_OUI_FROM_DATABASE=Wuhan QianXiao Elecronic Technology CO.,LTD
+OUI:DCEB69*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
OUI:DCEB94*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:DCEC06*
ID_OUI_FROM_DATABASE=Heimi Network Technology Co., Ltd.
+OUI:DCED84*
+ ID_OUI_FROM_DATABASE=Haverford Systems Inc
+
OUI:DCEE06*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -83867,6 +87599,9 @@ OUI:DCF090*
OUI:DCF110*
ID_OUI_FROM_DATABASE=Nokia Corporation
+OUI:DCF401*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:DCF505*
ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
@@ -83876,6 +87611,9 @@ OUI:DCF719*
OUI:DCF755*
ID_OUI_FROM_DATABASE=SITRONIK
+OUI:DCF756*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:DCF858*
ID_OUI_FROM_DATABASE=Lorent Networks, Inc.
@@ -83903,6 +87641,9 @@ OUI:E006E6*
OUI:E0071B*
ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
+OUI:E009BF*
+ ID_OUI_FROM_DATABASE=SHENZHEN TONG BO WEI TECHNOLOGY Co.,LTD
+
OUI:E00B28*
ID_OUI_FROM_DATABASE=Inovonics
@@ -83924,12 +87665,18 @@ OUI:E0107F*
OUI:E01283*
ID_OUI_FROM_DATABASE=Shenzhen Fanzhuo Communication Technology Co., Lt
+OUI:E013B5*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
OUI:E0143E*
ID_OUI_FROM_DATABASE=Modoosis Inc.
OUI:E01877*
ID_OUI_FROM_DATABASE=FUJITSU LIMITED
+OUI:E0189F*
+ ID_OUI_FROM_DATABASE=EM Microelectronic
+
OUI:E0191D*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -84017,6 +87764,9 @@ OUI:E03676*
OUI:E036E3*
ID_OUI_FROM_DATABASE=Stage One International Co., Ltd.
+OUI:E03717*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
OUI:E037BF*
ID_OUI_FROM_DATABASE=Wistron Neweb Corporation
@@ -84047,9 +87797,15 @@ OUI:E04136*
OUI:E043DB*
ID_OUI_FROM_DATABASE=Shenzhen ViewAt Technology Co.,Ltd.
+OUI:E0456D*
+ ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
+
OUI:E0469A*
ID_OUI_FROM_DATABASE=NETGEAR
+OUI:E046E5*
+ ID_OUI_FROM_DATABASE=Gosuncn Technology Group Co., Ltd.
+
OUI:E048AF*
ID_OUI_FROM_DATABASE=Premietech Limited
@@ -84089,9 +87845,57 @@ OUI:E056F4*
OUI:E0589E*
ID_OUI_FROM_DATABASE=Laerdal Medical
+OUI:E05A9F0*
+ ID_OUI_FROM_DATABASE=Annapurna labs
+
+OUI:E05A9F1*
+ ID_OUI_FROM_DATABASE=AITEC SYSTEM CO., LTD.
+
+OUI:E05A9F2*
+ ID_OUI_FROM_DATABASE=Chengdu Song Yuan Electronic Technology Co.,Ltd
+
+OUI:E05A9F3*
+ ID_OUI_FROM_DATABASE=Link of Things Co., Ltd.
+
+OUI:E05A9F4*
+ ID_OUI_FROM_DATABASE=Hale Products
+
+OUI:E05A9F5*
+ ID_OUI_FROM_DATABASE=TRYEN
+
+OUI:E05A9F6*
+ ID_OUI_FROM_DATABASE=Fibrain
+
+OUI:E05A9F7*
+ ID_OUI_FROM_DATABASE=OMB Guitars LLC
+
+OUI:E05A9F8*
+ ID_OUI_FROM_DATABASE=Fujian Newland Auto-ID Tech. Co.,Ltd.
+
+OUI:E05A9F9*
+ ID_OUI_FROM_DATABASE=Gemalto Document Readers
+
+OUI:E05A9FA*
+ ID_OUI_FROM_DATABASE=Contemporary Amperex Technology Co., Limited
+
+OUI:E05A9FB*
+ ID_OUI_FROM_DATABASE=Shenzhen Rongan Networks Technology Co.,Ltd
+
+OUI:E05A9FC*
+ ID_OUI_FROM_DATABASE=ShenZhen Mornsun Smartlinker Limited Co., LTD
+
+OUI:E05A9FD*
+ ID_OUI_FROM_DATABASE=Mountz, Inc.
+
+OUI:E05A9FE*
+ ID_OUI_FROM_DATABASE=ShenZhen Arts Changhua Intelligent Technology Co., Ltd
+
OUI:E05B70*
ID_OUI_FROM_DATABASE=Innovid, Co., Ltd.
+OUI:E05D5C*
+ ID_OUI_FROM_DATABASE=Oy Everon Ab
+
OUI:E05DA6*
ID_OUI_FROM_DATABASE=Detlef Fink Elektronik & Softwareentwicklung
@@ -84141,7 +87945,7 @@ OUI:E0735F*
ID_OUI_FROM_DATABASE=NUCOM
OUI:E0750A*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:E0757D*
ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
@@ -84152,6 +87956,9 @@ OUI:E076D0*
OUI:E078A3*
ID_OUI_FROM_DATABASE=Shanghai Winner Information Technology Co.,Inc
+OUI:E0795E*
+ ID_OUI_FROM_DATABASE=Wuxi Xiaohu Technology Co.,Ltd.
+
OUI:E07C13*
ID_OUI_FROM_DATABASE=zte corporation
@@ -84179,6 +87986,9 @@ OUI:E087B1*
OUI:E0885D*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+OUI:E0897E*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:E0899D*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -84224,6 +88034,9 @@ OUI:E09DB8*
OUI:E09DFA*
ID_OUI_FROM_DATABASE=Wanan Hongsheng Electronic Co.Ltd
+OUI:E09F2A*
+ ID_OUI_FROM_DATABASE=Iton Technology Corp.
+
OUI:E0A198*
ID_OUI_FROM_DATABASE=NOJA Power Switchgear Pty Ltd
@@ -84236,6 +88049,9 @@ OUI:E0A30F*
OUI:E0A3AC*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:E0A509*
+ ID_OUI_FROM_DATABASE=Bitmain Technologies Inc
+
OUI:E0A670*
ID_OUI_FROM_DATABASE=Nokia Corporation
@@ -84264,7 +88080,7 @@ OUI:E0ACF1*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:E0AE5E*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:E0AEB2*
ID_OUI_FROM_DATABASE=Bender GmbH &amp; Co.KG
@@ -84611,12 +88427,18 @@ OUI:E435FB*
OUI:E437D7*
ID_OUI_FROM_DATABASE=HENRI DEPAEPE S.A.S.
+OUI:E4388C*
+ ID_OUI_FROM_DATABASE=Digital Products Limited
+
OUI:E438F2*
ID_OUI_FROM_DATABASE=Advantage Controls
OUI:E43A6E*
ID_OUI_FROM_DATABASE=Shenzhen Zeroone Technology CO.,LTD
+OUI:E43C80*
+ ID_OUI_FROM_DATABASE=University of Oklahoma
+
OUI:E43ED7*
ID_OUI_FROM_DATABASE=Arcadyan Corporation
@@ -84650,6 +88472,48 @@ OUI:E448C7*
OUI:E44C6C*
ID_OUI_FROM_DATABASE=Shenzhen Guo Wei Electronic Co,. Ltd.
+OUI:E44CC70*
+ ID_OUI_FROM_DATABASE=Alert Alarm AB
+
+OUI:E44CC71*
+ ID_OUI_FROM_DATABASE=ACS-Solutions GmbH
+
+OUI:E44CC72*
+ ID_OUI_FROM_DATABASE=Doowon Electronics & Telecom Co.,Ltd
+
+OUI:E44CC73*
+ ID_OUI_FROM_DATABASE=JSC Svyaz Inginiring M
+
+OUI:E44CC74*
+ ID_OUI_FROM_DATABASE=Beijing Zhongchuangwei Nanjing Quantum Communication Technology Co., Ltd.
+
+OUI:E44CC75*
+ ID_OUI_FROM_DATABASE=CE LABS, LLC
+
+OUI:E44CC76*
+ ID_OUI_FROM_DATABASE=HANGZHOU OLE-SYSTEMS CO., LTD
+
+OUI:E44CC77*
+ ID_OUI_FROM_DATABASE=Channel Enterprises (HK) Ltd.
+
+OUI:E44CC78*
+ ID_OUI_FROM_DATABASE=IAG GROUP LTD
+
+OUI:E44CC79*
+ ID_OUI_FROM_DATABASE=Ottomate International Pvt. Ltd.
+
+OUI:E44CC7A*
+ ID_OUI_FROM_DATABASE=Muzik Inc
+
+OUI:E44CC7C*
+ ID_OUI_FROM_DATABASE=EPS Bio
+
+OUI:E44CC7D*
+ ID_OUI_FROM_DATABASE=Telo Systems Limitd
+
+OUI:E44CC7E*
+ ID_OUI_FROM_DATABASE=FLK information security technology Co,. Ltd
+
OUI:E44E18*
ID_OUI_FROM_DATABASE=Gardasoft VisionLimited
@@ -84686,6 +88550,9 @@ OUI:E458E7*
OUI:E45AA2*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+OUI:E45D37*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
OUI:E45D51*
ID_OUI_FROM_DATABASE=SFR
@@ -84875,6 +88742,9 @@ OUI:E49ADC*
OUI:E49E12*
ID_OUI_FROM_DATABASE=FREEBOX SAS
+OUI:E49F1E*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:E4A1E6*
ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd
@@ -84923,9 +88793,15 @@ OUI:E4B005*
OUI:E4B021*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:E4B2FB*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:E4B318*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:E4B97A*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:E4BAD9*
ID_OUI_FROM_DATABASE=360 Fly Inc.
@@ -84986,6 +88862,9 @@ OUI:E4D124*
OUI:E4D332*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+OUI:E4D3AA*
+ ID_OUI_FROM_DATABASE=FUJITSU CONNECTED TECHNOLOGIES LIMITED
+
OUI:E4D3F1*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -85016,6 +88895,9 @@ OUI:E4E409*
OUI:E4E4AB*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:E4E749*
+ ID_OUI_FROM_DATABASE=Hewlett Packard
+
OUI:E4EA83*
ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
@@ -85130,6 +89012,9 @@ OUI:E80C38*
OUI:E80C75*
ID_OUI_FROM_DATABASE=Syncbak, Inc.
+OUI:E80FC8*
+ ID_OUI_FROM_DATABASE=Universal Electronics, Inc.
+
OUI:E8102E*
ID_OUI_FROM_DATABASE=Really Simple Software, Inc
@@ -85202,6 +89087,9 @@ OUI:E81863E*
OUI:E81863F*
ID_OUI_FROM_DATABASE=Private
+OUI:E81A58*
+ ID_OUI_FROM_DATABASE=TECHNOLOGIC SYSTEMS
+
OUI:E81AAC*
ID_OUI_FROM_DATABASE=ORFEO SOUNDWORKS Inc.
@@ -85226,6 +89114,9 @@ OUI:E82A44*
OUI:E82AEA*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:E82C6D*
+ ID_OUI_FROM_DATABASE=SmartRG, Inc.
+
OUI:E82E24*
ID_OUI_FROM_DATABASE=Out of the Fog Research LLC
@@ -85238,6 +89129,9 @@ OUI:E83381*
OUI:E8343E*
ID_OUI_FROM_DATABASE=Beijing Infosec Technologies Co., LTD.
+OUI:E83617*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:E8361D*
ID_OUI_FROM_DATABASE=Sense Labs, Inc.
@@ -85280,6 +89174,9 @@ OUI:E8447E*
OUI:E8481F*
ID_OUI_FROM_DATABASE=Advanced Automotive Antennas
+OUI:E84C56*
+ ID_OUI_FROM_DATABASE=INTERCEPT SERVICES LIMITED
+
OUI:E84DD0*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -85316,9 +89213,15 @@ OUI:E856D6*
OUI:E85AA7*
ID_OUI_FROM_DATABASE=LLC Emzior
+OUI:E85AD1*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:E85B5B*
ID_OUI_FROM_DATABASE=LG ELECTRONICS INC
+OUI:E85BB7*
+ ID_OUI_FROM_DATABASE=Ample Systems Inc.
+
OUI:E85BF0*
ID_OUI_FROM_DATABASE=Imaging Diagnostics
@@ -85430,6 +89333,9 @@ OUI:E892A4*
OUI:E89309*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:E89363*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:E8944C*
ID_OUI_FROM_DATABASE=Cogent Healthcare Systems Ltd
@@ -85484,12 +89390,18 @@ OUI:E8ABF3*
OUI:E8ABFA*
ID_OUI_FROM_DATABASE=Shenzhen Reecam Tech.Ltd.
+OUI:E8ADA6*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
OUI:E8B1FC*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:E8B2AC*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:E8B2FE*
+ ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
+
OUI:E8B4AE*
ID_OUI_FROM_DATABASE=Shenzhen C&D Electronics Co.,Ltd
@@ -85775,6 +89687,9 @@ OUI:EC2AF0*
OUI:EC2C49*
ID_OUI_FROM_DATABASE=University of Tokyo
+OUI:EC2CE2*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:EC2E4E*
ID_OUI_FROM_DATABASE=HITACHI-LG DATA STORAGE INC
@@ -85814,6 +89729,9 @@ OUI:EC3EF7*
OUI:EC3F05*
ID_OUI_FROM_DATABASE=Institute 706, The Second Academy China Aerospace Science & Industry Corp
+OUI:EC4118*
+ ID_OUI_FROM_DATABASE=XIAOMI Electronics,CO.,LTD
+
OUI:EC42B4*
ID_OUI_FROM_DATABASE=ADC Corporation
@@ -85874,6 +89792,9 @@ OUI:EC59E7*
OUI:EC5A86*
ID_OUI_FROM_DATABASE=Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd
+OUI:EC5C68*
+ ID_OUI_FROM_DATABASE=CHONGQING FUGUI ELECTRONICS CO.,LTD.
+
OUI:EC5C69*
ID_OUI_FROM_DATABASE=MITSUBISHI HEAVY INDUSTRIES MECHATRONICS SYSTEMS,LTD.
@@ -85926,7 +89847,7 @@ OUI:EC7D11*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
OUI:EC7D9D*
- ID_OUI_FROM_DATABASE=MEI
+ ID_OUI_FROM_DATABASE=CPI
OUI:EC7FC6*
ID_OUI_FROM_DATABASE=ECCEL CORPORATION SAS
@@ -86073,7 +89994,7 @@ OUI:ECA86B*
ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd.
OUI:ECA9FA*
- ID_OUI_FROM_DATABASE=GUANGDONG GENIUS TECHNOLOGY CO.,LTD.
+ ID_OUI_FROM_DATABASE=GUANGDONG GENIUS TECHNOLOGY CO., LTD.
OUI:ECAAA0*
ID_OUI_FROM_DATABASE=PEGATRON CORPORATION
@@ -86129,6 +90050,9 @@ OUI:ECC38A*
OUI:ECC40D*
ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd
+OUI:ECC57F*
+ ID_OUI_FROM_DATABASE=Suzhou Pairlink Network Technology
+
OUI:ECC882*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -86210,6 +90134,9 @@ OUI:ECEED8*
OUI:ECF00E*
ID_OUI_FROM_DATABASE=AboCom
+OUI:ECF0FE*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:ECF236*
ID_OUI_FROM_DATABASE=NEOMONTANA ELECTRONICS
@@ -86225,6 +90152,9 @@ OUI:ECF451*
OUI:ECF4BB*
ID_OUI_FROM_DATABASE=Dell Inc.
+OUI:ECF6BD*
+ ID_OUI_FROM_DATABASE=SNCF MOBILITÉS
+
OUI:ECF72B*
ID_OUI_FROM_DATABASE=HD DIGITAL TECH CO., LTD.
@@ -86273,9 +90203,15 @@ OUI:F00D5C*
OUI:F00E1D*
ID_OUI_FROM_DATABASE=Megafone Limited
+OUI:F00EBF*
+ ID_OUI_FROM_DATABASE=ZettaHash Inc.
+
OUI:F00FEC*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:F010AB*
+ ID_OUI_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co., Ltd.
+
OUI:F013C3*
ID_OUI_FROM_DATABASE=SHENZHEN FENDA TECHNOLOGY CO., LTD
@@ -86349,7 +90285,7 @@ OUI:F023B99*
ID_OUI_FROM_DATABASE=Emu Technology
OUI:F023B9A*
- ID_OUI_FROM_DATABASE=annapurnalabs
+ ID_OUI_FROM_DATABASE=Annapurna labs
OUI:F023B9B*
ID_OUI_FROM_DATABASE=Q Core Medical Ltd
@@ -86519,6 +90455,12 @@ OUI:F04DA2*
OUI:F04F7C*
ID_OUI_FROM_DATABASE=Private
+OUI:F051EA*
+ ID_OUI_FROM_DATABASE=Fitbit, Inc.
+
+OUI:F05494*
+ ID_OUI_FROM_DATABASE=Honeywell Connected Building
+
OUI:F05849*
ID_OUI_FROM_DATABASE=CareView Communications
@@ -86529,7 +90471,7 @@ OUI:F05B7B*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:F05C19*
- ID_OUI_FROM_DATABASE=Aruba Networks
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:F05D89*
ID_OUI_FROM_DATABASE=Dycon Limited
@@ -86549,6 +90491,9 @@ OUI:F0620D*
OUI:F06281*
ID_OUI_FROM_DATABASE=ProCurve Networking by HP
+OUI:F063F9*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:F065C2*
ID_OUI_FROM_DATABASE=Yanfeng Visteon Electronics Technology (Shanghai) Co.,Ltd.
@@ -86660,6 +90605,9 @@ OUI:F0933A*
OUI:F093C5*
ID_OUI_FROM_DATABASE=Garland Technology
+OUI:F095F1*
+ ID_OUI_FROM_DATABASE=Carl Zeiss AG
+
OUI:F097E5*
ID_OUI_FROM_DATABASE=TAMIO, INC
@@ -86762,6 +90710,9 @@ OUI:F0AE51*
OUI:F0AF50*
ID_OUI_FROM_DATABASE=Phantom Intelligence
+OUI:F0AF85*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:F0B014*
ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH
@@ -86774,6 +90725,9 @@ OUI:F0B0E7*
OUI:F0B2E5*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:F0B31E*
+ ID_OUI_FROM_DATABASE=Universal Electronics, Inc.
+
OUI:F0B429*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
@@ -86846,6 +90800,9 @@ OUI:F0D3A7*
OUI:F0D3E7*
ID_OUI_FROM_DATABASE=Sensometrix SA
+OUI:F0D4E2*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:F0D4F6*
ID_OUI_FROM_DATABASE=Lars Thrane A/S
@@ -86987,6 +90944,9 @@ OUI:F40343*
OUI:F4044C*
ID_OUI_FROM_DATABASE=ValenceTech Limited
+OUI:F40616*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:F40669*
ID_OUI_FROM_DATABASE=Intel Corporate
@@ -87134,6 +91094,9 @@ OUI:F437B7*
OUI:F43814*
ID_OUI_FROM_DATABASE=Shanghai Howell Electronic Co.,Ltd
+OUI:F43909*
+ ID_OUI_FROM_DATABASE=Hewlett Packard
+
OUI:F43D80*
ID_OUI_FROM_DATABASE=FAG Industrial Services GmbH
@@ -87272,6 +91235,12 @@ OUI:F46DE2*
OUI:F46E24*
ID_OUI_FROM_DATABASE=NEC Personal Computers, Ltd.
+OUI:F46E95*
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
+
+OUI:F46F4E*
+ ID_OUI_FROM_DATABASE=Echowell
+
OUI:F470AB*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
@@ -87293,6 +91262,9 @@ OUI:F47ACC*
OUI:F47B5E*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:F47DEF*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:F47F35*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -87320,6 +91292,9 @@ OUI:F48B32*
OUI:F48C50*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:F48CEB*
+ ID_OUI_FROM_DATABASE=D-Link International
+
OUI:F48E09*
ID_OUI_FROM_DATABASE=Nokia Corporation
@@ -87347,6 +91322,9 @@ OUI:F49461*
OUI:F49466*
ID_OUI_FROM_DATABASE=CountMax, ltd
+OUI:F4951B*
+ ID_OUI_FROM_DATABASE=Hefei Radio Communication Technology Co., Ltd
+
OUI:F49634*
ID_OUI_FROM_DATABASE=Intel Corporate
@@ -87380,6 +91358,9 @@ OUI:F4A997*
OUI:F4ACC1*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:F4AFE7*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:F4B164*
ID_OUI_FROM_DATABASE=Lightning Telecommunications Technology Co. Ltd
@@ -87416,6 +91397,9 @@ OUI:F4B8A7*
OUI:F4BC97*
ID_OUI_FROM_DATABASE=Shenzhen Crave Communication Co., LTD
+OUI:F4BCDA*
+ ID_OUI_FROM_DATABASE=Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd
+
OUI:F4BD7C*
ID_OUI_FROM_DATABASE=Chengdu jinshi communication Co., LTD
@@ -87443,6 +91427,9 @@ OUI:F4C714*
OUI:F4C795*
ID_OUI_FROM_DATABASE=WEY Elektronik AG
+OUI:F4C7C8*
+ ID_OUI_FROM_DATABASE=Kelvin Inc.
+
OUI:F4CA24*
ID_OUI_FROM_DATABASE=FreeBit Co., Ltd.
@@ -87467,6 +91454,9 @@ OUI:F4CFE2*
OUI:F4D032*
ID_OUI_FROM_DATABASE=Yunnan Ideal Information&Technology.,Ltd
+OUI:F4D108*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:F4D261*
ID_OUI_FROM_DATABASE=SEMOCON Co., Ltd
@@ -87476,6 +91466,9 @@ OUI:F4D7B2*
OUI:F4D9FB*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:F4DBE6*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:F4DC41*
ID_OUI_FROM_DATABASE=YOUNGZONE CULTURE (SHANGHAI) CORP
@@ -87671,15 +91664,24 @@ OUI:F80D60*
OUI:F80DEA*
ID_OUI_FROM_DATABASE=ZyCast Technology Inc.
+OUI:F80DF1*
+ ID_OUI_FROM_DATABASE=Sontex SA
+
OUI:F80F41*
ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation
+OUI:F80F6F*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:F80F84*
ID_OUI_FROM_DATABASE=Natural Security SAS
OUI:F81037*
ID_OUI_FROM_DATABASE=Atopia Systems, LP
+OUI:F81308*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:F81547*
ID_OUI_FROM_DATABASE=Avaya Inc
@@ -87779,6 +91781,9 @@ OUI:F82BC8*
OUI:F82C18*
ID_OUI_FROM_DATABASE=2Wire Inc
+OUI:F82D7C*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:F82DC0*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
@@ -87786,11 +91791,14 @@ OUI:F82EDB*
ID_OUI_FROM_DATABASE=RTW GmbH & Co. KG
OUI:F82F08*
- ID_OUI_FROM_DATABASE=Molex
+ ID_OUI_FROM_DATABASE=Molex CMS
OUI:F82F5B*
ID_OUI_FROM_DATABASE=eGauge Systems LLC
+OUI:F82F6A*
+ ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED
+
OUI:F82FA8*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
@@ -87818,6 +91826,9 @@ OUI:F835DD*
OUI:F8369B*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:F83880*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:F83D4E*
ID_OUI_FROM_DATABASE=Softlink Automation System Co., Ltd
@@ -87857,9 +91868,15 @@ OUI:F84A7F*
OUI:F84ABF*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:F84DFC*
+ ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd.
+
OUI:F84F57*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:F8501C*
+ ID_OUI_FROM_DATABASE=Tianjin Geneuo Technology Co.,Ltd
+
OUI:F85063*
ID_OUI_FROM_DATABASE=Verathon
@@ -88049,6 +92066,9 @@ OUI:F89066*
OUI:F8912A*
ID_OUI_FROM_DATABASE=GLP German Light Products GmbH
+OUI:F89173*
+ ID_OUI_FROM_DATABASE=AEDLE SAS
+
OUI:F893F3*
ID_OUI_FROM_DATABASE=VOLANS
@@ -88103,6 +92123,9 @@ OUI:F8A188*
OUI:F8A2B4*
ID_OUI_FROM_DATABASE=RHEWA-WAAGENFABRIK August Freudewald GmbH &amp;Co. KG
+OUI:F8A2D6*
+ ID_OUI_FROM_DATABASE=Liteon Technology Corporation
+
OUI:F8A34F*
ID_OUI_FROM_DATABASE=zte corporation
@@ -88211,6 +92234,9 @@ OUI:F8C091*
OUI:F8C120*
ID_OUI_FROM_DATABASE=Xi'an Link-Science Technology Co.,Ltd
+OUI:F8C249*
+ ID_OUI_FROM_DATABASE=Private
+
OUI:F8C288*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -88229,6 +92255,9 @@ OUI:F8C678*
OUI:F8C96C*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+OUI:F8CA59*
+ ID_OUI_FROM_DATABASE=NetComm Wireless
+
OUI:F8CAB8*
ID_OUI_FROM_DATABASE=Dell Inc.
@@ -88256,6 +92285,9 @@ OUI:F8D3A9*
OUI:F8D462*
ID_OUI_FROM_DATABASE=Pumatronix Equipamentos Eletronicos Ltda.
+OUI:F8D478*
+ ID_OUI_FROM_DATABASE=Flextronics Tech.(Ind) Pvt Ltd
+
OUI:F8D756*
ID_OUI_FROM_DATABASE=Simm Tronic Limited
@@ -88272,7 +92304,7 @@ OUI:F8DADF*
ID_OUI_FROM_DATABASE=EcoTech, Inc.
OUI:F8DAE2*
- ID_OUI_FROM_DATABASE=Beta LaserMike
+ ID_OUI_FROM_DATABASE=NDC Technologies
OUI:F8DAF4*
ID_OUI_FROM_DATABASE=Taishan Online Technology Co., Ltd.
@@ -88319,6 +92351,9 @@ OUI:F8E811*
OUI:F8E903*
ID_OUI_FROM_DATABASE=D-Link International
+OUI:F8E94E*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:F8E968*
ID_OUI_FROM_DATABASE=Egker Kft.
@@ -88439,6 +92474,9 @@ OUI:FC1607*
OUI:FC1794*
ID_OUI_FROM_DATABASE=InterCreative Co., Ltd
+OUI:FC183C*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:FC1910*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -88484,6 +92522,9 @@ OUI:FC2A54*
OUI:FC2A9C*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:FC2BB2*
+ ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc
+
OUI:FC2D5E*
ID_OUI_FROM_DATABASE=zte corporation
@@ -88611,7 +92652,7 @@ OUI:FC626E*
ID_OUI_FROM_DATABASE=Beijing MDC Telecom
OUI:FC62B9*
- ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD.
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
OUI:FC643A*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -88646,6 +92687,9 @@ OUI:FC7516*
OUI:FC75E6*
ID_OUI_FROM_DATABASE=Handreamnet
+OUI:FC7774*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:FC790B*
ID_OUI_FROM_DATABASE=Hitachi High Technologies America, Inc.
@@ -88667,12 +92711,18 @@ OUI:FC8399*
OUI:FC83C6*
ID_OUI_FROM_DATABASE=N-Radio Technologies Co., Ltd.
+OUI:FC8743*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:FC8B97*
ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
OUI:FC8E7E*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:FC8F7D*
+ ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
+
OUI:FC8F90*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -88691,6 +92741,9 @@ OUI:FC923B*
OUI:FC946C*
ID_OUI_FROM_DATABASE=UBIVELOX
+OUI:FC94CE*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:FC94E3*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
@@ -88745,9 +92798,15 @@ OUI:FCA9B0*
OUI:FCAA14*
ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD.
+OUI:FCAAB6*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:FCAD0F*
ID_OUI_FROM_DATABASE=QTS NETWORKS
+OUI:FCAE34*
+ ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+
OUI:FCAF6A*
ID_OUI_FROM_DATABASE=Qulsar Inc
@@ -88766,9 +92825,15 @@ OUI:FCB4E6*
OUI:FCB58A*
ID_OUI_FROM_DATABASE=Wapice Ltd.
+OUI:FCB662*
+ ID_OUI_FROM_DATABASE=IC Holdings LLC
+
OUI:FCB698*
ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd.
+OUI:FCB6D8*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:FCB7F0*
ID_OUI_FROM_DATABASE=Idaho National Laboratory
@@ -88778,6 +92843,9 @@ OUI:FCBBA1*
OUI:FCBC9C*
ID_OUI_FROM_DATABASE=Vimar Spa
+OUI:FCBE7B*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
OUI:FCC233*
ID_OUI_FROM_DATABASE=Private
@@ -88805,6 +92873,27 @@ OUI:FCCF43*
OUI:FCCF62*
ID_OUI_FROM_DATABASE=IBM Corp
+OUI:FCD2B60*
+ ID_OUI_FROM_DATABASE=CG POWER AND INDUSTRIAL SOLUTIONS LTD
+
+OUI:FCD2B62*
+ ID_OUI_FROM_DATABASE=Soma GmbH
+
+OUI:FCD2B63*
+ ID_OUI_FROM_DATABASE=Coet Costruzioni Elettrotecniche
+
+OUI:FCD2B65*
+ ID_OUI_FROM_DATABASE=Grandway Technology (Shenzhen) Limited
+
+OUI:FCD2B6A*
+ ID_OUI_FROM_DATABASE=NREAL TECHNOLOGY LIMITED
+
+OUI:FCD2B6B*
+ ID_OUI_FROM_DATABASE=T CHIP DIGITAL TECHNOLOGY CO.LTD
+
+OUI:FCD2B6D*
+ ID_OUI_FROM_DATABASE=Bee Smart(Changzhou) Information Technology Co., Ltd
+
OUI:FCD4F2*
ID_OUI_FROM_DATABASE=The Coca Cola Company
diff --git a/hwdb/20-acpi-vendor.hwdb b/hwdb/20-acpi-vendor.hwdb
index 4207f07144..3a857eb586 100644
--- a/hwdb/20-acpi-vendor.hwdb
+++ b/hwdb/20-acpi-vendor.hwdb
@@ -54,6 +54,9 @@ acpi:BOSC*:
acpi:BRCM*:
ID_VENDOR_FROM_DATABASE=Broadcom Corporation
+acpi:CMHR*:
+ ID_VENDOR_FROM_DATABASE=COMHEAR, INC.
+
acpi:CORE*:
ID_VENDOR_FROM_DATABASE=CoreOS, Inc
@@ -69,6 +72,9 @@ acpi:DLGS*:
acpi:DLLK*:
ID_VENDOR_FROM_DATABASE=Dell, Inc.
+acpi:DMST*:
+ ID_VENDOR_FROM_DATABASE=DMIST RESEARCH LTD
+
acpi:DSUO*:
ID_VENDOR_FROM_DATABASE=Shenzhen DSO Microelectronics Co.,Ltd.
@@ -93,6 +99,9 @@ acpi:GHSW*:
acpi:GOOG*:
ID_VENDOR_FROM_DATABASE=Google, Inc.
+acpi:GTCH*:
+ ID_VENDOR_FROM_DATABASE=G2touch Co., LTD
+
acpi:HIMX*:
ID_VENDOR_FROM_DATABASE=Himax Technologies, Inc.
@@ -117,6 +126,9 @@ acpi:IBMX*:
acpi:IDEA*:
ID_VENDOR_FROM_DATABASE=Lenovo Beijing Co. Ltd.
+acpi:IDEM*:
+ ID_VENDOR_FROM_DATABASE=IDEMIA
+
acpi:IHSE*:
ID_VENDOR_FROM_DATABASE=IHSE GmbH
@@ -210,6 +222,9 @@ acpi:RZSN*:
acpi:SHRP*:
ID_VENDOR_FROM_DATABASE=Sharp Corporation
+acpi:SNSL*:
+ ID_VENDOR_FROM_DATABASE=Sensel, Inc.
+
acpi:SONY*:
ID_VENDOR_FROM_DATABASE=Sony Corporation
@@ -240,6 +255,9 @@ acpi:VAIO*:
acpi:VFSI*:
ID_VENDOR_FROM_DATABASE=Validity Sensors, Inc
+acpi:VSHY*:
+ ID_VENDOR_FROM_DATABASE=Vishay Intertechnology, Inc.
+
acpi:WCOM*:
ID_VENDOR_FROM_DATABASE=Wacom
@@ -450,6 +468,9 @@ acpi:AGT*:
acpi:AHC*:
ID_VENDOR_FROM_DATABASE=Advantech Co., Ltd.
+acpi:AHQ*:
+ ID_VENDOR_FROM_DATABASE=Astro HQ LLC
+
acpi:AHS*:
ID_VENDOR_FROM_DATABASE=Beijing AnHeng SecoTech Information Technology Co., Ltd.
@@ -1371,6 +1392,9 @@ acpi:CHY*:
acpi:CIC*:
ID_VENDOR_FROM_DATABASE=Comm. Intelligence Corporation
+acpi:CIE*:
+ ID_VENDOR_FROM_DATABASE=Convergent Engineering, Inc.
+
acpi:CII*:
ID_VENDOR_FROM_DATABASE=Cromack Industries Inc
@@ -2697,6 +2721,9 @@ acpi:GAL*:
acpi:GAU*:
ID_VENDOR_FROM_DATABASE=Gaudi Co., Ltd.
+acpi:GBT*:
+ ID_VENDOR_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO., LTD.
+
acpi:GCC*:
ID_VENDOR_FROM_DATABASE=GCC Technologies Inc
@@ -2842,7 +2869,7 @@ acpi:GSC*:
ID_VENDOR_FROM_DATABASE=General Standards Corporation
acpi:GSM*:
- ID_VENDOR_FROM_DATABASE=Goldstar Company Ltd
+ ID_VENDOR_FROM_DATABASE=LG Electronics
acpi:GSN*:
ID_VENDOR_FROM_DATABASE=Grandstream Networks, Inc.
@@ -3753,6 +3780,9 @@ acpi:KOE*:
acpi:KOL*:
ID_VENDOR_FROM_DATABASE=Kollmorgen Motion Technologies Group
+acpi:KOM*:
+ ID_VENDOR_FROM_DATABASE=Kontron GmbH
+
acpi:KOU*:
ID_VENDOR_FROM_DATABASE=KOUZIRO Co.,Ltd.
@@ -4152,6 +4182,9 @@ acpi:MCG*:
acpi:MCI*:
ID_VENDOR_FROM_DATABASE=Micronics Computers
+acpi:MCJ*:
+ ID_VENDOR_FROM_DATABASE=Medicaroid Corporation
+
acpi:MCL*:
ID_VENDOR_FROM_DATABASE=Motorola Communications Israel
@@ -5778,6 +5811,9 @@ acpi:RWC*:
acpi:RXT*:
ID_VENDOR_FROM_DATABASE=Tectona SoftSolutions (P) Ltd.,
+acpi:RZR*:
+ ID_VENDOR_FROM_DATABASE=Razer Taiwan Co. Ltd.
+
acpi:RZS*:
ID_VENDOR_FROM_DATABASE=Rozsnyó, s.r.o.
@@ -7089,6 +7125,9 @@ acpi:VAL*:
acpi:VAR*:
ID_VENDOR_FROM_DATABASE=Varian Australia Pty Ltd
+acpi:VAT*:
+ ID_VENDOR_FROM_DATABASE=VADATECH INC
+
acpi:VBR*:
ID_VENDOR_FROM_DATABASE=VBrick Systems Inc.
@@ -7366,7 +7405,7 @@ acpi:WNV*:
ID_VENDOR_FROM_DATABASE=Winnov L.P.
acpi:WNX*:
- ID_VENDOR_FROM_DATABASE=Wincor Nixdorf International GmbH
+ ID_VENDOR_FROM_DATABASE=Diebold Nixdorf Systems GmbH
acpi:WPA*:
ID_VENDOR_FROM_DATABASE=Matsushita Communication Industrial Co., Ltd.
@@ -7413,6 +7452,9 @@ acpi:WWV*:
acpi:WXT*:
ID_VENDOR_FROM_DATABASE=Woxter Technology Co. Ltd
+acpi:WYR*:
+ ID_VENDOR_FROM_DATABASE=WyreStorm Technologies LLC
+
acpi:WYS*:
ID_VENDOR_FROM_DATABASE=Wyse Technology
@@ -7516,7 +7558,7 @@ acpi:ZAX*:
ID_VENDOR_FROM_DATABASE=Zefiro Acoustics
acpi:ZAZ*:
- ID_VENDOR_FROM_DATABASE=Zazzle Technologies
+ ID_VENDOR_FROM_DATABASE=ZeeVee, Inc.
acpi:ZBR*:
ID_VENDOR_FROM_DATABASE=Zebra Technologies International, LLC
diff --git a/hwdb/20-acpi-vendor.hwdb.patch b/hwdb/20-acpi-vendor.hwdb.patch
index 308d1353d4..88af2cb401 100644
--- a/hwdb/20-acpi-vendor.hwdb.patch
+++ b/hwdb/20-acpi-vendor.hwdb.patch
@@ -1,5 +1,5 @@
---- 20-acpi-vendor.hwdb.base 2018-06-12 15:51:50.897235123 +0200
-+++ 20-acpi-vendor.hwdb 2018-06-12 15:51:50.907235139 +0200
+--- 20-acpi-vendor.hwdb.base 2018-12-20 16:29:34.999977602 +0100
++++ 20-acpi-vendor.hwdb 2018-12-20 16:29:35.022977859 +0100
@@ -3,6 +3,8 @@
# Data imported from:
# http://www.uefi.org/uefi-pnp-export
@@ -19,7 +19,7 @@
acpi:AMDI*:
ID_VENDOR_FROM_DATABASE=AMD
-@@ -253,6 +252,9 @@
+@@ -271,6 +270,9 @@
acpi:AAA*:
ID_VENDOR_FROM_DATABASE=Avolites Ltd
@@ -29,7 +29,7 @@
acpi:AAE*:
ID_VENDOR_FROM_DATABASE=Anatek Electronics Inc.
-@@ -280,6 +282,9 @@
+@@ -298,6 +300,9 @@
acpi:ABO*:
ID_VENDOR_FROM_DATABASE=D-Link Systems Inc
@@ -39,7 +39,7 @@
acpi:ABS*:
ID_VENDOR_FROM_DATABASE=Abaco Systems, Inc.
-@@ -325,7 +330,7 @@
+@@ -343,7 +348,7 @@
acpi:ACO*:
ID_VENDOR_FROM_DATABASE=Allion Computer Inc.
@@ -48,7 +48,7 @@
ID_VENDOR_FROM_DATABASE=Aspen Tech Inc
acpi:ACR*:
-@@ -595,6 +600,9 @@
+@@ -616,6 +621,9 @@
acpi:AMT*:
ID_VENDOR_FROM_DATABASE=AMT International Industry
@@ -58,7 +58,7 @@
acpi:AMX*:
ID_VENDOR_FROM_DATABASE=AMX LLC
-@@ -643,6 +651,9 @@
+@@ -664,6 +672,9 @@
acpi:AOA*:
ID_VENDOR_FROM_DATABASE=AOpen Inc.
@@ -68,7 +68,7 @@
acpi:AOE*:
ID_VENDOR_FROM_DATABASE=Advanced Optics Electronics, Inc.
-@@ -652,6 +663,9 @@
+@@ -673,6 +684,9 @@
acpi:AOT*:
ID_VENDOR_FROM_DATABASE=Alcatel
@@ -78,7 +78,7 @@
acpi:APC*:
ID_VENDOR_FROM_DATABASE=American Power Conversion
-@@ -827,7 +841,7 @@
+@@ -848,7 +862,7 @@
ID_VENDOR_FROM_DATABASE=Alps Electric Inc
acpi:AUO*:
@@ -87,7 +87,7 @@
acpi:AUR*:
ID_VENDOR_FROM_DATABASE=Aureal Semiconductor
-@@ -907,6 +921,9 @@
+@@ -928,6 +942,9 @@
acpi:AXE*:
ID_VENDOR_FROM_DATABASE=Axell Corporation
@@ -97,7 +97,7 @@
acpi:AXI*:
ID_VENDOR_FROM_DATABASE=American Magnetics
-@@ -1054,6 +1071,9 @@
+@@ -1075,6 +1092,9 @@
acpi:BML*:
ID_VENDOR_FROM_DATABASE=BIOMED Lab
@@ -107,7 +107,7 @@
acpi:BMS*:
ID_VENDOR_FROM_DATABASE=BIOMEDISYS
-@@ -1066,6 +1086,9 @@
+@@ -1087,6 +1107,9 @@
acpi:BNO*:
ID_VENDOR_FROM_DATABASE=Bang & Olufsen
@@ -117,7 +117,7 @@
acpi:BNS*:
ID_VENDOR_FROM_DATABASE=Boulder Nonlinear Systems
-@@ -1306,6 +1329,9 @@
+@@ -1327,6 +1350,9 @@
acpi:CHA*:
ID_VENDOR_FROM_DATABASE=Chase Research PLC
@@ -127,7 +127,7 @@
acpi:CHD*:
ID_VENDOR_FROM_DATABASE=ChangHong Electric Co.,Ltd
-@@ -1456,6 +1482,9 @@
+@@ -1480,6 +1506,9 @@
acpi:COD*:
ID_VENDOR_FROM_DATABASE=CODAN Pty. Ltd.
@@ -137,7 +137,7 @@
acpi:COI*:
ID_VENDOR_FROM_DATABASE=Codec Inc.
-@@ -1859,7 +1888,7 @@
+@@ -1883,7 +1912,7 @@
ID_VENDOR_FROM_DATABASE=Dragon Information Technology
acpi:DJE*:
@@ -146,7 +146,7 @@
acpi:DJP*:
ID_VENDOR_FROM_DATABASE=Maygay Machines, Ltd
-@@ -2182,6 +2211,9 @@
+@@ -2206,6 +2235,9 @@
acpi:EIN*:
ID_VENDOR_FROM_DATABASE=Elegant Invention
@@ -156,7 +156,7 @@
acpi:EKA*:
ID_VENDOR_FROM_DATABASE=MagTek Inc.
-@@ -2440,6 +2472,9 @@
+@@ -2464,6 +2496,9 @@
acpi:FCG*:
ID_VENDOR_FROM_DATABASE=First International Computer Ltd
@@ -166,7 +166,16 @@
acpi:FCS*:
ID_VENDOR_FROM_DATABASE=Focus Enhancements, Inc.
-@@ -2908,6 +2943,9 @@
+@@ -2834,7 +2869,7 @@
+ ID_VENDOR_FROM_DATABASE=General Standards Corporation
+
+ acpi:GSM*:
+- ID_VENDOR_FROM_DATABASE=Goldstar Company Ltd
++ ID_VENDOR_FROM_DATABASE=LG Electronics
+
+ acpi:GSN*:
+ ID_VENDOR_FROM_DATABASE=Grandstream Networks, Inc.
+@@ -2935,6 +2970,9 @@
acpi:HEC*:
ID_VENDOR_FROM_DATABASE=Hisense Electric Co., Ltd.
@@ -176,7 +185,7 @@
acpi:HEL*:
ID_VENDOR_FROM_DATABASE=Hitachi Micro Systems Europe Ltd
-@@ -3037,6 +3075,9 @@
+@@ -3064,6 +3102,9 @@
acpi:HSD*:
ID_VENDOR_FROM_DATABASE=HannStar Display Corp
@@ -186,7 +195,7 @@
acpi:HSM*:
ID_VENDOR_FROM_DATABASE=AT&T Microelectronics
-@@ -3160,6 +3201,9 @@
+@@ -3187,6 +3228,9 @@
acpi:ICI*:
ID_VENDOR_FROM_DATABASE=Infotek Communication Inc
@@ -196,7 +205,7 @@
acpi:ICM*:
ID_VENDOR_FROM_DATABASE=Intracom SA
-@@ -3253,6 +3297,9 @@
+@@ -3280,6 +3324,9 @@
acpi:IKE*:
ID_VENDOR_FROM_DATABASE=Ikegami Tsushinki Co. Ltd.
@@ -206,7 +215,7 @@
acpi:IKS*:
ID_VENDOR_FROM_DATABASE=Ikos Systems Inc
-@@ -3298,6 +3345,9 @@
+@@ -3325,6 +3372,9 @@
acpi:IMT*:
ID_VENDOR_FROM_DATABASE=Inmax Technology Corporation
@@ -216,7 +225,7 @@
acpi:INA*:
ID_VENDOR_FROM_DATABASE=Inventec Corporation
-@@ -3802,6 +3852,9 @@
+@@ -3832,6 +3882,9 @@
acpi:LAN*:
ID_VENDOR_FROM_DATABASE=Sodeman Lancom Inc
@@ -226,7 +235,7 @@
acpi:LAS*:
ID_VENDOR_FROM_DATABASE=LASAT Comm. A/S
-@@ -3847,6 +3900,9 @@
+@@ -3877,6 +3930,9 @@
acpi:LED*:
ID_VENDOR_FROM_DATABASE=Long Engineering Design Inc
@@ -236,7 +245,7 @@
acpi:LEG*:
ID_VENDOR_FROM_DATABASE=Legerity, Inc
-@@ -3862,6 +3918,9 @@
+@@ -3892,6 +3948,9 @@
acpi:LGC*:
ID_VENDOR_FROM_DATABASE=Logic Ltd
@@ -246,7 +255,7 @@
acpi:LGI*:
ID_VENDOR_FROM_DATABASE=Logitech Inc
-@@ -3913,6 +3972,9 @@
+@@ -3943,6 +4002,9 @@
acpi:LND*:
ID_VENDOR_FROM_DATABASE=Land Computer Company Ltd
@@ -256,7 +265,7 @@
acpi:LNK*:
ID_VENDOR_FROM_DATABASE=Link Tech Inc
-@@ -3947,7 +4009,7 @@
+@@ -3977,7 +4039,7 @@
ID_VENDOR_FROM_DATABASE=Design Technology
acpi:LPL*:
@@ -265,7 +274,7 @@
acpi:LSC*:
ID_VENDOR_FROM_DATABASE=LifeSize Communications
-@@ -4120,6 +4182,9 @@
+@@ -4153,6 +4215,9 @@
acpi:MCX*:
ID_VENDOR_FROM_DATABASE=Millson Custom Solutions Inc.
@@ -275,7 +284,7 @@
acpi:MDA*:
ID_VENDOR_FROM_DATABASE=Media4 Inc
-@@ -4351,6 +4416,9 @@
+@@ -4384,6 +4449,9 @@
acpi:MOM*:
ID_VENDOR_FROM_DATABASE=Momentum Data Systems
@@ -285,7 +294,7 @@
acpi:MOS*:
ID_VENDOR_FROM_DATABASE=Moses Corporation
-@@ -4576,6 +4644,9 @@
+@@ -4609,6 +4677,9 @@
acpi:NAL*:
ID_VENDOR_FROM_DATABASE=Network Alchemy
@@ -295,7 +304,7 @@
acpi:NAT*:
ID_VENDOR_FROM_DATABASE=NaturalPoint Inc.
-@@ -5080,6 +5151,9 @@
+@@ -5113,6 +5184,9 @@
acpi:PCX*:
ID_VENDOR_FROM_DATABASE=PC Xperten
@@ -305,7 +314,7 @@
acpi:PDM*:
ID_VENDOR_FROM_DATABASE=Psion Dacom Plc.
-@@ -5143,9 +5217,6 @@
+@@ -5176,9 +5250,6 @@
acpi:PHE*:
ID_VENDOR_FROM_DATABASE=Philips Medical Systems Boeblingen GmbH
@@ -315,7 +324,7 @@
acpi:PHL*:
ID_VENDOR_FROM_DATABASE=Philips Consumer Electronics Company
-@@ -5230,9 +5301,6 @@
+@@ -5263,9 +5334,6 @@
acpi:PNL*:
ID_VENDOR_FROM_DATABASE=Panelview, Inc.
@@ -325,7 +334,7 @@
acpi:PNR*:
ID_VENDOR_FROM_DATABASE=Planar Systems, Inc.
-@@ -5368,15 +5436,9 @@
+@@ -5401,15 +5469,9 @@
acpi:PTS*:
ID_VENDOR_FROM_DATABASE=Plain Tree Systems Inc
@@ -341,7 +350,7 @@
acpi:PVG*:
ID_VENDOR_FROM_DATABASE=Proview Global Co., Ltd
-@@ -5689,9 +5751,6 @@
+@@ -5722,9 +5784,6 @@
acpi:RTI*:
ID_VENDOR_FROM_DATABASE=Rancho Tech Inc
@@ -351,7 +360,7 @@
acpi:RTL*:
ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Company Ltd
-@@ -5854,9 +5913,6 @@
+@@ -5890,9 +5949,6 @@
acpi:SEE*:
ID_VENDOR_FROM_DATABASE=SeeColor Corporation
@@ -361,7 +370,7 @@
acpi:SEI*:
ID_VENDOR_FROM_DATABASE=Seitz & Associates Inc
-@@ -6310,6 +6366,9 @@
+@@ -6346,6 +6402,9 @@
acpi:SVD*:
ID_VENDOR_FROM_DATABASE=SVD Computer
@@ -371,7 +380,7 @@
acpi:SVI*:
ID_VENDOR_FROM_DATABASE=Sun Microsystems
-@@ -6394,6 +6453,9 @@
+@@ -6430,6 +6489,9 @@
acpi:SZM*:
ID_VENDOR_FROM_DATABASE=Shenzhen MTC Co., Ltd
@@ -381,7 +390,7 @@
acpi:TAA*:
ID_VENDOR_FROM_DATABASE=Tandberg
-@@ -6484,6 +6546,9 @@
+@@ -6520,6 +6582,9 @@
acpi:TDG*:
ID_VENDOR_FROM_DATABASE=Six15 Technologies
@@ -391,7 +400,7 @@
acpi:TDM*:
ID_VENDOR_FROM_DATABASE=Tandem Computer Europe Inc
-@@ -6526,6 +6591,9 @@
+@@ -6562,6 +6627,9 @@
acpi:TEV*:
ID_VENDOR_FROM_DATABASE=Televés, S.A.
@@ -401,7 +410,7 @@
acpi:TEZ*:
ID_VENDOR_FROM_DATABASE=Tech Source Inc.
-@@ -6640,9 +6708,6 @@
+@@ -6676,9 +6744,6 @@
acpi:TNC*:
ID_VENDOR_FROM_DATABASE=TNC Industrial Company Ltd
@@ -411,7 +420,7 @@
acpi:TNM*:
ID_VENDOR_FROM_DATABASE=TECNIMAGEN SA
-@@ -6949,14 +7014,14 @@
+@@ -6985,14 +7050,14 @@
acpi:UNC*:
ID_VENDOR_FROM_DATABASE=Unisys Corporation
@@ -432,7 +441,7 @@
acpi:UNI*:
ID_VENDOR_FROM_DATABASE=Uniform Industry Corp.
-@@ -6991,6 +7056,9 @@
+@@ -7027,6 +7092,9 @@
acpi:USA*:
ID_VENDOR_FROM_DATABASE=Utimaco Safeware AG
@@ -442,7 +451,7 @@
acpi:USD*:
ID_VENDOR_FROM_DATABASE=U.S. Digital Corporation
-@@ -7228,9 +7296,6 @@
+@@ -7267,9 +7335,6 @@
acpi:WAL*:
ID_VENDOR_FROM_DATABASE=Wave Access
@@ -452,8 +461,8 @@
acpi:WAV*:
ID_VENDOR_FROM_DATABASE=Wavephore
-@@ -7349,7 +7414,7 @@
- ID_VENDOR_FROM_DATABASE=Woxter Technology Co. Ltd
+@@ -7391,7 +7456,7 @@
+ ID_VENDOR_FROM_DATABASE=WyreStorm Technologies LLC
acpi:WYS*:
- ID_VENDOR_FROM_DATABASE=Myse Technology
@@ -461,7 +470,7 @@
acpi:WYT*:
ID_VENDOR_FROM_DATABASE=Wooyoung Image & Information Co.,Ltd.
-@@ -7363,9 +7428,6 @@
+@@ -7405,9 +7470,6 @@
acpi:XDM*:
ID_VENDOR_FROM_DATABASE=XDM Ltd.
@@ -471,7 +480,7 @@
acpi:XES*:
ID_VENDOR_FROM_DATABASE=Extreme Engineering Solutions, Inc.
-@@ -7396,9 +7458,6 @@
+@@ -7438,9 +7500,6 @@
acpi:XNT*:
ID_VENDOR_FROM_DATABASE=XN Technologies, Inc.
@@ -481,7 +490,7 @@
acpi:XQU*:
ID_VENDOR_FROM_DATABASE=SHANGHAI SVA-DAV ELECTRONICS CO., LTD
-@@ -7465,6 +7524,9 @@
+@@ -7507,6 +7566,9 @@
acpi:ZBX*:
ID_VENDOR_FROM_DATABASE=Zebax Technologies
diff --git a/hwdb/20-pci-vendor-model.hwdb b/hwdb/20-pci-vendor-model.hwdb
index 94e495c280..47508fa903 100644
--- a/hwdb/20-pci-vendor-model.hwdb
+++ b/hwdb/20-pci-vendor-model.hwdb
@@ -1148,6 +1148,9 @@ pci:v00001000d0000005D*
pci:v00001000d0000005Dsv00001000sd00009361*
ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (MegaRAID SAS 9361-8i)
+pci:v00001000d0000005Dsv00001000sd00009363*
+ ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (MegaRAID SAS 9361-4i)
+
pci:v00001000d0000005Dsv00001000sd00009364*
ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (MegaRAID SAS 9364-8i)
@@ -1196,6 +1199,9 @@ pci:v00001000d0000005Dsv000017AAsd00001053*
pci:v00001000d0000005Dsv00001D49sd00000600*
ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (ThinkSystem RAID 730-8i 1GB Cache PCIe 12Gb Adapter)
+pci:v00001000d0000005Dsv00001D49sd00000608*
+ ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (ThinkSystem RAID 730-8i 2GB Flash PCIe 12Gb Adapter)
+
pci:v00001000d0000005Dsv00001D49sd00000609*
ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (ThinkSystem RAID 730-8i 4GB Flash PCIe 12Gb Adapter)
@@ -1355,12 +1361,18 @@ pci:v00001000d0000006E*
pci:v00001000d00000070*
ID_MODEL_FROM_DATABASE=SAS2004 PCI-Express Fusion-MPT SAS-2 [Spitfire]
+pci:v00001000d00000070sv00001000sd00003010*
+ ID_MODEL_FROM_DATABASE=SAS2004 PCI-Express Fusion-MPT SAS-2 [Spitfire] (SAS9211-4i)
+
pci:v00001000d00000071*
ID_MODEL_FROM_DATABASE=MR SAS HBA 2004
pci:v00001000d00000072*
ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon]
+pci:v00001000d00000072sv00001000sd000030B0*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (9200-8e [LSI SAS 6Gb/s SAS/SATA PCIe x8 External HBA])
+
pci:v00001000d00000072sv00001028sd00001F1C*
ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (6Gbps SAS HBA Adapter)
@@ -1661,6 +1673,12 @@ pci:v00001000d00000085*
pci:v00001000d00000086*
ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2
+pci:v00001000d00000086sv000015D9sd00000690*
+ ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (Onboard MegaRAID SAS2208 [Thunderbolt])
+
+pci:v00001000d00000086sv000015D9sd00000691*
+ ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (Onboard SAS2308 PCI-Express Fusion-MPT SAS-2)
+
pci:v00001000d00000087*
ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2
@@ -1673,6 +1691,9 @@ pci:v00001000d00000087sv00001000sd00003040*
pci:v00001000d00000087sv00001000sd00003050*
ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (SAS9217-8i)
+pci:v00001000d00000087sv00001014sd00000472*
+ ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (N2125 External Host Bus Adapter)
+
pci:v00001000d00000087sv00001590sd00000044*
ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (H220i)
@@ -1784,6 +1805,9 @@ pci:v00001000d000000AE*
pci:v00001000d000000AF*
ID_MODEL_FROM_DATABASE=SAS3408 Fusion-MPT Tri-Mode I/O Controller Chip (IOC)
+pci:v00001000d000000AFsv00001000sd00003010*
+ ID_MODEL_FROM_DATABASE=SAS3408 Fusion-MPT Tri-Mode I/O Controller Chip (IOC) (HBA 9400-8i)
+
pci:v00001000d000000AFsv00001D49sd00000200*
ID_MODEL_FROM_DATABASE=SAS3408 Fusion-MPT Tri-Mode I/O Controller Chip (IOC) (ThinkSystem 430-8i SAS/SATA 12Gb HBA)
@@ -1853,6 +1877,30 @@ pci:v00001000d000000D1*
pci:v00001000d000000D3*
ID_MODEL_FROM_DATABASE=MegaRAID Tri-Mode SAS3716W
+pci:v00001000d000000E0*
+ ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Unsupported SAS39xx
+
+pci:v00001000d000000E1*
+ ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe SAS39xx
+
+pci:v00001000d000000E2*
+ ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS39xx
+
+pci:v00001000d000000E3*
+ ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Unsupported SAS39xx
+
+pci:v00001000d000000E4*
+ ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Unsupported SAS38xx
+
+pci:v00001000d000000E5*
+ ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe SAS38xx
+
+pci:v00001000d000000E6*
+ ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx
+
+pci:v00001000d000000E7*
+ ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Unsupported SAS38xx
+
pci:v00001000d000002B0*
ID_MODEL_FROM_DATABASE=Virtual Endpoint on PCIe Switch
@@ -1862,6 +1910,12 @@ pci:v00001000d000002B0sv00001D49sd00000001*
pci:v00001000d000002B0sv00001D49sd00000002*
ID_MODEL_FROM_DATABASE=Virtual Endpoint on PCIe Switch (ThinkSystem 810-4P NVMe Switch Adapter)
+pci:v00001000d000002B1*
+ ID_MODEL_FROM_DATABASE=Virtual Endpoint on PCIe Switch (9749)
+
+pci:v00001000d000002B1sv00001D49sd00000004*
+ ID_MODEL_FROM_DATABASE=Virtual Endpoint on PCIe Switch (9749) (ThinkSystem 1610-8P NVMe Switch Adapter)
+
pci:v00001000d00000407*
ID_MODEL_FROM_DATABASE=MegaRAID
@@ -2072,6 +2126,30 @@ pci:v00001000d00000901*
pci:v00001000d00001000*
ID_MODEL_FROM_DATABASE=63C815
+pci:v00001000d000010E0*
+ ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Unsupported SAS39xx
+
+pci:v00001000d000010E1*
+ ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe SAS39xx
+
+pci:v00001000d000010E2*
+ ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Secure SAS39xx
+
+pci:v00001000d000010E3*
+ ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Unsupported SAS39xx
+
+pci:v00001000d000010E4*
+ ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Unsupported SAS38xx
+
+pci:v00001000d000010E5*
+ ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe SAS38xx
+
+pci:v00001000d000010E6*
+ ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Secure SAS38xx
+
+pci:v00001000d000010E7*
+ ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Unsupported SAS38xx
+
pci:v00001000d00001960*
ID_MODEL_FROM_DATABASE=MegaRAID
@@ -2225,6 +2303,9 @@ pci:v00001002d0000131C*
pci:v00001002d0000131D*
ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6 Graphics]
+pci:v00001002d000015D8*
+ ID_MODEL_FROM_DATABASE=Picasso
+
pci:v00001002d000015DD*
ID_MODEL_FROM_DATABASE=Raven Ridge [Radeon Vega Series / Radeon Vega Mobile Series]
@@ -4890,7 +4971,10 @@ pci:v00001002d00006646*
ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon R9 M280X]
pci:v00001002d00006647*
- ID_MODEL_FROM_DATABASE=Bonaire PRO [Radeon R9 M270X]
+ ID_MODEL_FROM_DATABASE=Saturn PRO/XT [Radeon R9 M270X/M280X]
+
+pci:v00001002d00006647sv00001043sd0000223D*
+ ID_MODEL_FROM_DATABASE=Saturn PRO/XT [Radeon R9 M270X/M280X] (N551ZU laptop Radeon R9 M280X)
pci:v00001002d00006649*
ID_MODEL_FROM_DATABASE=Bonaire [FirePro W5100]
@@ -4916,6 +5000,9 @@ pci:v00001002d00006651*
pci:v00001002d00006658*
ID_MODEL_FROM_DATABASE=Bonaire XTX [Radeon R7 260X/360]
+pci:v00001002d00006658sv00001043sd000004D3*
+ ID_MODEL_FROM_DATABASE=Bonaire XTX [Radeon R7 260X/360] (AMD Radeon R7 260X)
+
pci:v00001002d00006658sv0000148Csd00000907*
ID_MODEL_FROM_DATABASE=Bonaire XTX [Radeon R7 260X/360] (Radeon R7 360)
@@ -5055,7 +5142,7 @@ pci:v00001002d000066A3*
ID_MODEL_FROM_DATABASE=Vega 20
pci:v00001002d000066A7*
- ID_MODEL_FROM_DATABASE=Vega 20
+ ID_MODEL_FROM_DATABASE=Vega 20 [Radeon Pro Vega 20]
pci:v00001002d000066AF*
ID_MODEL_FROM_DATABASE=Vega 20
@@ -6419,6 +6506,9 @@ pci:v00001002d00006779sv0000103Csd00002128*
pci:v00001002d00006779sv0000103Csd00002AEE*
ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450A)
+pci:v00001002d00006779sv00001092sd00006450*
+ ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450)
+
pci:v00001002d00006779sv00001462sd00002125*
ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450)
@@ -6849,91 +6939,94 @@ pci:v00001002d000067D0*
ID_MODEL_FROM_DATABASE=Ellesmere [Radeon Pro V7300X / V7350x2]
pci:v00001002d000067DF*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X]
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590]
pci:v00001002d000067DFsv00001002sd00000B37*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 480)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480)
pci:v00001002d000067DFsv00001028sd00001722*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 570X)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 570X)
pci:v00001002d000067DFsv00001028sd00001723*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 580X)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 580X)
pci:v00001002d000067DFsv00001043sd000004A8*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 480)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480)
pci:v00001002d000067DFsv00001043sd000004B0*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 470)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 470)
pci:v00001002d000067DFsv00001043sd000004FB*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 480)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480)
pci:v00001002d000067DFsv00001043sd000004FD*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 480 8GB)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480 8GB)
+
+pci:v00001002d000067DFsv00001043sd0000056A*
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 590)
pci:v00001002d000067DFsv0000106Bsd00000161*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon Pro 580)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon Pro 580)
pci:v00001002d000067DFsv0000106Bsd00000162*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon Pro 575)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon Pro 575)
pci:v00001002d000067DFsv0000106Bsd00000163*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon Pro 570)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon Pro 570)
pci:v00001002d000067DFsv00001458sd000022F0*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 570)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 570)
pci:v00001002d000067DFsv00001458sd000022F7*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 570 Gaming 4G)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 570 Gaming 4G)
pci:v00001002d000067DFsv00001462sd00003411*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 470)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 470)
pci:v00001002d000067DFsv00001462sd00003413*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 480)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480 Gaming X 8GB)
pci:v00001002d000067DFsv00001462sd00003416*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 570)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 570)
pci:v00001002d000067DFsv00001462sd00003418*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 580 Armor 4G OC)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 580 Armor 4G OC)
pci:v00001002d000067DFsv0000148Csd00002372*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 480)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480)
pci:v00001002d000067DFsv0000148Csd00002373*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 470)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 470)
pci:v00001002d000067DFsv00001682sd00009470*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 470)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 470)
pci:v00001002d000067DFsv00001682sd00009480*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 480)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480)
pci:v00001002d000067DFsv00001682sd00009588*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 580 XTR)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 580 XTR)
pci:v00001002d000067DFsv0000174Bsd0000E347*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 470/480)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 470/480)
pci:v00001002d000067DFsv0000174Bsd0000E349*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 470)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 470)
pci:v00001002d000067DFsv00001787sd0000A470*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 470)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 470)
pci:v00001002d000067DFsv00001787sd0000A480*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Radeon RX 480)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480)
pci:v00001002d000067DFsv00001849sd00005001*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Phantom Gaming X RX 580 OC)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Phantom Gaming X RX 580 OC)
pci:v00001002d000067DFsv00001DA2sd0000E353*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Sapphire Radeon RX 580 Pulse 8GB)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 570 Pulse 4GB)
pci:v00001002d000067DFsv00001DA2sd0000E366*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X] (Nitro+ Radeon RX 580 4GB)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Nitro+ Radeon RX 570/580)
pci:v00001002d000067E0*
ID_MODEL_FROM_DATABASE=Baffin [Radeon Pro WX 4170]
@@ -6984,28 +7077,34 @@ pci:v00001002d000067EB*
ID_MODEL_FROM_DATABASE=Baffin [Radeon Pro V5300X]
pci:v00001002d000067EF*
- ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/560]
+ ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X]
pci:v00001002d000067EFsv0000106Bsd00000160*
- ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/560] (Radeon Pro 460)
+ ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Radeon Pro 460)
pci:v00001002d000067EFsv0000106Bsd00000166*
- ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/560] (Radeon Pro 455)
+ ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Radeon Pro 455)
pci:v00001002d000067EFsv0000106Bsd00000167*
- ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/560] (Radeon Pro 450)
+ ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Radeon Pro 450)
pci:v00001002d000067EFsv0000106Bsd00000179*
- ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/560] (Radeon Pro 560)
+ ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Radeon Pro 560)
pci:v00001002d000067EFsv0000106Bsd0000017A*
- ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/560] (Radeon Pro 555)
+ ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Radeon Pro 555)
+
+pci:v00001002d000067EFsv0000106Bsd0000018F*
+ ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Radeon Pro 560X)
+
+pci:v00001002d000067EFsv0000106Bsd00000190*
+ ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Radeon Pro 555X)
pci:v00001002d000067EFsv00001642sd00001727*
- ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/560] (Polaris 21 XL [Radeon RX 560D])
+ ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Polaris 21 XL [Radeon RX 560D])
pci:v00001002d000067EFsv00001682sd0000956D*
- ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/560] (Polaris 21 XL [Radeon RX 560D])
+ ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Polaris 21 XL [Radeon RX 560D])
pci:v00001002d000067FF*
ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 550 640SP / RX 560/560X]
@@ -7257,10 +7356,13 @@ pci:v00001002d0000682A*
ID_MODEL_FROM_DATABASE=Venus PRO
pci:v00001002d0000682B*
- ID_MODEL_FROM_DATABASE=Venus LE / Tropo PRO-L [Radeon HD 8830M / R7 M465X]
+ ID_MODEL_FROM_DATABASE=Cape Verde PRO / Venus LE / Tropo PRO-L [Radeon HD 8830M / R7 250 / R7 M465X]
pci:v00001002d0000682Bsv00000128sd0000079C*
- ID_MODEL_FROM_DATABASE=Venus LE / Tropo PRO-L [Radeon HD 8830M / R7 M465X] (Radeon R7 465X)
+ ID_MODEL_FROM_DATABASE=Cape Verde PRO / Venus LE / Tropo PRO-L [Radeon HD 8830M / R7 250 / R7 M465X] (Radeon R7 465X)
+
+pci:v00001002d0000682Bsv00001462sd00003012*
+ ID_MODEL_FROM_DATABASE=Cape Verde PRO / Venus LE / Tropo PRO-L [Radeon HD 8830M / R7 250 / R7 M465X] (Radeon R7 250)
pci:v00001002d0000682C*
ID_MODEL_FROM_DATABASE=Cape Verde GL [FirePro W4100]
@@ -7658,6 +7760,12 @@ pci:v00001002d00006843*
pci:v00001002d00006860*
ID_MODEL_FROM_DATABASE=Vega 10 [Radeon Instinct MI25]
+pci:v00001002d00006860sv00001002sd00000C35*
+ ID_MODEL_FROM_DATABASE=Vega 10 [Radeon Instinct MI25] (Radeon PRO V320)
+
+pci:v00001002d00006860sv00001002sd00006C75*
+ ID_MODEL_FROM_DATABASE=Vega 10 [Radeon Instinct MI25] (Radeon PRO V320)
+
pci:v00001002d00006860sv0000106Bsd0000017C*
ID_MODEL_FROM_DATABASE=Vega 10 [Radeon Instinct MI25] (Radeon Pro Vega 64)
@@ -7677,13 +7785,19 @@ pci:v00001002d00006867*
ID_MODEL_FROM_DATABASE=Vega 10 XL [Radeon Pro Vega 56]
pci:v00001002d00006868*
- ID_MODEL_FROM_DATABASE=Vega
+ ID_MODEL_FROM_DATABASE=Vega 10 [Radeon PRO WX 8100]
pci:v00001002d0000686C*
ID_MODEL_FROM_DATABASE=Vega 10 [Radeon Instinct MI25 MxGPU]
pci:v00001002d0000687F*
- ID_MODEL_FROM_DATABASE=Vega 10 XT [Radeon RX Vega 64]
+ ID_MODEL_FROM_DATABASE=Vega 10 XL/XT [Radeon RX Vega 56/64]
+
+pci:v00001002d00006880*
+ ID_MODEL_FROM_DATABASE=Lexington [Radeon HD 6550M]
+
+pci:v00001002d00006880sv0000103Csd0000163C*
+ ID_MODEL_FROM_DATABASE=Lexington [Radeon HD 6550M] (Pavilion dv6 Radeon HD 6550M)
pci:v00001002d00006888*
ID_MODEL_FROM_DATABASE=Cypress XT [FirePro V8800]
@@ -9219,7 +9333,7 @@ pci:v00001002d0000694C*
ID_MODEL_FROM_DATABASE=Polaris 22 [Radeon RX Vega M GH]
pci:v00001002d0000694E*
- ID_MODEL_FROM_DATABASE=Polaris 22 [Radeon RX Vega M GL]
+ ID_MODEL_FROM_DATABASE=Polaris 22 XL [Radeon RX Vega M GL]
pci:v00001002d00006980*
ID_MODEL_FROM_DATABASE=Polaris12
@@ -9266,6 +9380,9 @@ pci:v00001002d000069A3*
pci:v00001002d000069AF*
ID_MODEL_FROM_DATABASE=Vega 12
+pci:v00001002d00006FDF*
+ ID_MODEL_FROM_DATABASE=Polaris 20 XL [Radeon RX 580 2048SP]
+
pci:v00001002d0000700F*
ID_MODEL_FROM_DATABASE=RS100 AGP Bridge
@@ -9692,6 +9809,12 @@ pci:v00001002d00007300sv00001043sd000004A0*
pci:v00001002d00007300sv0000174Bsd0000E329*
ID_MODEL_FROM_DATABASE=Fiji [Radeon R9 FURY / NANO Series] (Radeon R9 FURY)
+pci:v00001002d00007310*
+ ID_MODEL_FROM_DATABASE=Navi 10
+
+pci:v00001002d0000731F*
+ ID_MODEL_FROM_DATABASE=Navi 10
+
pci:v00001002d00007833*
ID_MODEL_FROM_DATABASE=RS350 Host Bridge
@@ -10638,19 +10761,19 @@ pci:v00001002d00009921*
ID_MODEL_FROM_DATABASE=Liverpool HDMI/DP Audio Controller
pci:v00001002d00009990*
- ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7520G]
+ ID_MODEL_FROM_DATABASE=Trinity 2 [Radeon HD 7520G]
pci:v00001002d00009991*
- ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7540D]
+ ID_MODEL_FROM_DATABASE=Trinity 2 [Radeon HD 7540D]
pci:v00001002d00009992*
- ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7420G]
+ ID_MODEL_FROM_DATABASE=Trinity 2 [Radeon HD 7420G]
pci:v00001002d00009993*
- ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7480D]
+ ID_MODEL_FROM_DATABASE=Trinity 2 [Radeon HD 7480D]
pci:v00001002d00009994*
- ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7400G]
+ ID_MODEL_FROM_DATABASE=Trinity 2 [Radeon HD 7400G]
pci:v00001002d00009995*
ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8450G]
@@ -10680,13 +10803,13 @@ pci:v00001002d0000999D*
ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8550D]
pci:v00001002d000099A0*
- ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7520G]
+ ID_MODEL_FROM_DATABASE=Trinity 2 [Radeon HD 7520G]
pci:v00001002d000099A2*
- ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7420G]
+ ID_MODEL_FROM_DATABASE=Trinity 2 [Radeon HD 7420G]
pci:v00001002d000099A4*
- ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7400G]
+ ID_MODEL_FROM_DATABASE=Trinity 2 [Radeon HD 7400G]
pci:v00001002d0000AA00*
ID_MODEL_FROM_DATABASE=R600 HDMI Audio [Radeon HD 2900 GT/PRO/XT]
@@ -10784,11 +10907,14 @@ pci:v00001002d0000AAD8*
pci:v00001002d0000AAD8sv0000174Bsd0000AAD8*
ID_MODEL_FROM_DATABASE=Tonga HDMI Audio [Radeon R9 285/380] (Radeon R9 285/380 HDMI Audio)
+pci:v00001002d0000AAE0*
+ ID_MODEL_FROM_DATABASE=Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X]
+
pci:v00001002d0000AAE8*
ID_MODEL_FROM_DATABASE=Fiji HDMI/DP Audio [Radeon R9 Nano / FURY/FURY X]
pci:v00001002d0000AAF0*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 580]
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 570/580]
pci:v00001002d0000AC00*
ID_MODEL_FROM_DATABASE=Theater 600 Pro
@@ -12050,6 +12176,9 @@ pci:v00001014d0000044B*
pci:v00001014d000004AA*
ID_MODEL_FROM_DATABASE=Flash Adapter 90 (PCIe2 0.9TB)
+pci:v00001014d000004C1*
+ ID_MODEL_FROM_DATABASE=POWER9 Host Bridge (PHB4)
+
pci:v00001014d000004DA*
ID_MODEL_FROM_DATABASE=PCI-E IPR SAS+ Adapter (ASIC)
@@ -12767,18 +12896,36 @@ pci:v00001022d000043A2*
pci:v00001022d000043A3*
ID_MODEL_FROM_DATABASE=Hudson PCI to PCI bridge (PCIE port 3)
+pci:v00001022d000043B0*
+ ID_MODEL_FROM_DATABASE=X370 Series Chipset PCIe Upstream Port
+
+pci:v00001022d000043B0sv00001849sd000043C6*
+ ID_MODEL_FROM_DATABASE=X370 Series Chipset PCIe Upstream Port (Fatal1ty X370 Professional Gaming)
+
pci:v00001022d000043B1*
ID_MODEL_FROM_DATABASE=X399 Series Chipset PCIe Bridge
pci:v00001022d000043B4*
ID_MODEL_FROM_DATABASE=300 Series Chipset PCIe Port
+pci:v00001022d000043B5*
+ ID_MODEL_FROM_DATABASE=X370 Series Chipset SATA Controller
+
+pci:v00001022d000043B5sv00001849sd000043C8*
+ ID_MODEL_FROM_DATABASE=X370 Series Chipset SATA Controller (Fatal1ty X370 Professional Gaming)
+
pci:v00001022d000043B6*
ID_MODEL_FROM_DATABASE=X399 Series Chipset SATA Controller
pci:v00001022d000043B7*
ID_MODEL_FROM_DATABASE=300 Series Chipset SATA Controller
+pci:v00001022d000043B9*
+ ID_MODEL_FROM_DATABASE=X370 Series Chipset USB 3.1 xHCI Controller
+
+pci:v00001022d000043B9sv00001849sd000043D0*
+ ID_MODEL_FROM_DATABASE=X370 Series Chipset USB 3.1 xHCI Controller (Fatal1ty X370 Professional Gaming)
+
pci:v00001022d000043BA*
ID_MODEL_FROM_DATABASE=X399 Series Chipset USB 3.1 xHCI Controller
@@ -14120,9 +14267,15 @@ pci:v0000102Bd00000532sv00001028sd0000029C*
pci:v0000102Bd00000532sv00001028sd000002A4*
ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (PowerEdge T310 MGA G200eW WPCM450)
+pci:v0000102Bd00000532sv000015D9sd00000605*
+ ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (X8SIL)
+
pci:v0000102Bd00000532sv000015D9sd00000624*
ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (X9SCM-F Motherboard)
+pci:v0000102Bd00000532sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (X9SRL-F)
+
pci:v0000102Bd00000532sv000015D9sd0000A811*
ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (H8DGU)
@@ -14135,6 +14288,9 @@ pci:v0000102Bd00000533sv0000103Csd00003381*
pci:v0000102Bd00000534*
ID_MODEL_FROM_DATABASE=G200eR2
+pci:v0000102Bd00000534sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=G200eR2 (PowerEdge R320 server)
+
pci:v0000102Bd00000536*
ID_MODEL_FROM_DATABASE=Integrated Matrox G200eW3 Graphics Controller
@@ -15911,6 +16067,9 @@ pci:v0000103Cd00003306sv0000103Csd0000330E*
pci:v0000103Cd00003306sv0000103Csd00003381*
ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Slave Instrumentation & System Support (iLO4)
+pci:v0000103Cd00003306sv00001590sd000000E4*
+ ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Slave Instrumentation & System Support (iLO5)
+
pci:v0000103Cd00003307*
ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Management Processor Support and Messaging
@@ -17216,18 +17375,45 @@ pci:v0000104Dd00008004*
pci:v0000104Dd00008009*
ID_MODEL_FROM_DATABASE=CXD1947Q i.LINK Controller
+pci:v0000104Dd0000800C*
+ ID_MODEL_FROM_DATABASE=DTL-H800 [PS1 sound development board]
+
pci:v0000104Dd00008039*
ID_MODEL_FROM_DATABASE=CXD3222 i.LINK Controller
+pci:v0000104Dd00008047*
+ ID_MODEL_FROM_DATABASE=PS2 TOOL MRP
+
pci:v0000104Dd00008056*
ID_MODEL_FROM_DATABASE=Rockwell HCF 56K modem
pci:v0000104Dd0000808A*
ID_MODEL_FROM_DATABASE=Memory Stick Controller
+pci:v0000104Dd000080FF*
+ ID_MODEL_FROM_DATABASE=PS2 Performance Analyzer
+
+pci:v0000104Dd0000814A*
+ ID_MODEL_FROM_DATABASE=PS2 Performance Analyzer
+
+pci:v0000104Dd00008183*
+ ID_MODEL_FROM_DATABASE=ATHENS [PS3 prototype developer interface card]
+
+pci:v0000104Dd000081B0*
+ ID_MODEL_FROM_DATABASE=BM-1 [PSP TOOL Board Management Device]
+
+pci:v0000104Dd000081C3*
+ ID_MODEL_FROM_DATABASE=VO-4 [PSP TOOL Video Output Device]
+
pci:v0000104Dd000081CE*
ID_MODEL_FROM_DATABASE=SxS Pro memory card
+pci:v0000104Dd000081FF*
+ ID_MODEL_FROM_DATABASE=PS3 TOOL MRP
+
+pci:v0000104Dd0000820E*
+ ID_MODEL_FROM_DATABASE=CXD9208GP [PS3 PS2 emulation subsystem adapter]
+
pci:v0000104Dd0000905C*
ID_MODEL_FROM_DATABASE=SxS Pro memory card
@@ -18521,6 +18707,12 @@ pci:v0000106Bd00000074*
pci:v0000106Bd00001645*
ID_MODEL_FROM_DATABASE=Broadcom NetXtreme BCM5701 Gigabit Ethernet
+pci:v0000106Bd00001801*
+ ID_MODEL_FROM_DATABASE=T2 Bridge Controller
+
+pci:v0000106Bd00001802*
+ ID_MODEL_FROM_DATABASE=T2 Secure Enclave Processor
+
pci:v0000106Bd00002001*
ID_MODEL_FROM_DATABASE=S1X NVMe Controller
@@ -18866,6 +19058,12 @@ pci:v00001077d00002261sv00001077sd000002AB*
pci:v00001077d00002261sv00001077sd000002AC*
ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2742 Dual Port 32Gb FC to PCIe Gen3 x8 Adapter)
+pci:v00001077d00002261sv00001077sd000002B8*
+ ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (2x16Gb QME2692 FC HBA)
+
+pci:v00001077d00002261sv00001077sd000002B9*
+ ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (2x32Gb QME2742 FC HBA)
+
pci:v00001077d00002261sv00001590sd000000F9*
ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (StoreFabric SN1100Q 16Gb Single Port Fibre Channel Host Bus Adapter)
@@ -19073,6 +19271,12 @@ pci:v00001077d00008070sv00001077sd00000009*
pci:v00001077d00008070sv00001077sd0000000B*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (25GE 2P QL41262HxCU-DE Adapter)
+pci:v00001077d00008070sv00001077sd0000000F*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (2x25GE QL41262HMKR CNA)
+
+pci:v00001077d00008070sv00001077sd00000010*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (2x25GE QL41232HMKR NIC)
+
pci:v00001077d00008070sv00001077sd00000011*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (FastLinQ QL41212HLCU 25GbE Adapter)
@@ -19085,6 +19289,12 @@ pci:v00001077d00008070sv00001077sd00000019*
pci:v00001077d00008070sv00001077sd00000039*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (QLogic QL41262 PCIe 25Gb 2-Port SFP28 Ethernet Adapter)
+pci:v00001077d00008070sv00001590sd0000021A*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2P QL41162HLRJ-HP Adapter)
+
+pci:v00001077d00008070sv00001590sd0000021B*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2P QL41162HLRJ-HP Adapter)
+
pci:v00001077d00008070sv00001590sd0000021D*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10/25GbE 2P QL41222HLCU-HP Adapter)
@@ -19133,6 +19343,15 @@ pci:v00001077d00008080sv00001077sd0000000D*
pci:v00001077d00008080sv00001077sd0000000E*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (FCoE) (FastLinQ QL41162H 10GbE FCoE Adapter)
+pci:v00001077d00008080sv00001077sd0000000F*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (FCoE) (2x25GE QL41262HMKR CNA)
+
+pci:v00001077d00008080sv00001590sd0000021A*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (FCoE) (10GbE 2P QL41162HLRJ-HP Adapter)
+
+pci:v00001077d00008080sv00001590sd0000021B*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (FCoE) (10GbE 2P QL41162HLRJ-HP Adapter)
+
pci:v00001077d00008084*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI)
@@ -19172,6 +19391,15 @@ pci:v00001077d00008084sv00001077sd0000000D*
pci:v00001077d00008084sv00001077sd0000000E*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (FastLinQ QL41162H 10GbE iSCSI Adapter)
+pci:v00001077d00008084sv00001077sd0000000F*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (2x25GE QL41262HMKR CNA)
+
+pci:v00001077d00008084sv00001590sd0000021A*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (10GbE 2P QL41162HLRJ-HP Adapter)
+
+pci:v00001077d00008084sv00001590sd0000021B*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (10GbE 2P QL41162HLRJ-HP Adapter)
+
pci:v00001077d00008090*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF)
@@ -19211,12 +19439,24 @@ pci:v00001077d00008090sv00001077sd0000000D*
pci:v00001077d00008090sv00001077sd0000000E*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL41162H 10GbE iSCSI Adapter (SR-IOV VF))
+pci:v00001077d00008090sv00001077sd0000000F*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (2x25GE QL41262HMKR CNA)
+
+pci:v00001077d00008090sv00001077sd00000010*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (2x25GE QL41232HMKR NIC)
+
pci:v00001077d00008090sv00001077sd00000011*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL41212H 25GbE Adapter (SR-IOV VF))
pci:v00001077d00008090sv00001077sd00000012*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL41112H 10GbE Adapter (SR-IOV VF))
+pci:v00001077d00008090sv00001590sd0000021A*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (10GbE 2P QL41162HLRJ-HP Adapter)
+
+pci:v00001077d00008090sv00001590sd0000021B*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (10GbE 2P QL41162HLRJ-HP Adapter)
+
pci:v00001077d00008090sv00001590sd0000021E*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (10/25GbE 2P QL41162HMRJ-HP Adapter)
@@ -22214,6 +22454,9 @@ pci:v0000109Ed0000036Csv000013E9sd00000070*
pci:v0000109Ed0000036E*
ID_MODEL_FROM_DATABASE=Bt878 Video Capture
+pci:v0000109Ed0000036Esv00000000sd00000001*
+ ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Euresys Picolo PCIe)
+
pci:v0000109Ed0000036Esv00000070sd000013EB*
ID_MODEL_FROM_DATABASE=Bt878 Video Capture (WinTV Series)
@@ -22448,6 +22691,9 @@ pci:v0000109Ed00000370sv00001852sd00001852*
pci:v0000109Ed00000878*
ID_MODEL_FROM_DATABASE=Bt878 Audio Capture
+pci:v0000109Ed00000878sv00000000sd00000001*
+ ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Euresys Picolo PCIe)
+
pci:v0000109Ed00000878sv00000070sd000013EB*
ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (WinTV Series)
@@ -23531,6 +23777,9 @@ pci:v000010B5d00009733sv00001D49sd00000002*
pci:v000010B5d00009749*
ID_MODEL_FROM_DATABASE=PEX 9749 49-lane, 13-port PCI Express Gen 3 (8.0 GT/s) Switch
+pci:v000010B5d00009749sv00001D49sd00000004*
+ ID_MODEL_FROM_DATABASE=PEX 9749 49-lane, 13-port PCI Express Gen 3 (8.0 GT/s) Switch (ThinkSystem 1610-8P NVMe Switch Adapter)
+
pci:v000010B5d0000A100*
ID_MODEL_FROM_DATABASE=Blackmagic Design DeckLink
@@ -28046,6 +28295,9 @@ pci:v000010DEd00000410*
pci:v000010DEd00000414*
ID_MODEL_FROM_DATABASE=G92 [GeForce 9800 GT]
+pci:v000010DEd00000418*
+ ID_MODEL_FROM_DATABASE=G92 [GeForce GT 330 OEM]
+
pci:v000010DEd00000420*
ID_MODEL_FROM_DATABASE=G86 [GeForce 8400 SE]
@@ -30614,6 +30866,9 @@ pci:v000010DEd00000F01*
pci:v000010DEd00000F02*
ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 730]
+pci:v000010DEd00000F03*
+ ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 610]
+
pci:v000010DEd00000F06*
ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 730]
@@ -30626,6 +30881,9 @@ pci:v000010DEd00000FB8*
pci:v000010DEd00000FB9*
ID_MODEL_FROM_DATABASE=GP107GL High Definition Audio Controller
+pci:v000010DEd00000FBA*
+ ID_MODEL_FROM_DATABASE=GM206 High Definition Audio Controller
+
pci:v000010DEd00000FBB*
ID_MODEL_FROM_DATABASE=GM204 High Definition Audio Controller
@@ -30858,7 +31116,7 @@ pci:v000010DEd00001007*
ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX 780 Rev. 2]
pci:v000010DEd00001008*
- ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX 780 Ti Rev. 2]
+ ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX 780 Ti 6GB]
pci:v000010DEd0000100A*
ID_MODEL_FROM_DATABASE=GK110B [GeForce GTX 780 Ti]
@@ -31142,6 +31400,15 @@ pci:v000010DEd000010F0*
pci:v000010DEd000010F1*
ID_MODEL_FROM_DATABASE=GP106 High Definition Audio Controller
+pci:v000010DEd000010F7*
+ ID_MODEL_FROM_DATABASE=TU102 High Definition Audio Controller
+
+pci:v000010DEd000010F9*
+ ID_MODEL_FROM_DATABASE=TU106 High Definition Audio Controller
+
+pci:v000010DEd000010F9sv00001043sd00008673*
+ ID_MODEL_FROM_DATABASE=TU106 High Definition Audio Controller (TURBO-RTX2070-8G)
+
pci:v000010DEd00001140*
ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M]
@@ -32750,6 +33017,9 @@ pci:v000010DEd00001393*
pci:v000010DEd00001398*
ID_MODEL_FROM_DATABASE=GM107M [GeForce 845M]
+pci:v000010DEd00001399*
+ ID_MODEL_FROM_DATABASE=GM107M [GeForce 945M]
+
pci:v000010DEd0000139A*
ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 950M]
@@ -32774,6 +33044,9 @@ pci:v000010DEd0000139Asv000017AAsd000036B9*
pci:v000010DEd0000139B*
ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 960M]
+pci:v000010DEd0000139Bsv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 960M] (XPS 15 9550)
+
pci:v000010DEd0000139Bsv0000103Csd00002B4C*
ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 960M] (GeForce GTX 960A)
@@ -32969,6 +33242,9 @@ pci:v000010DEd0000174E*
pci:v000010DEd00001789*
ID_MODEL_FROM_DATABASE=GM107GL [GRID M3-3020]
+pci:v000010DEd0000179C*
+ ID_MODEL_FROM_DATABASE=GM107 [GeForce 940MX]
+
pci:v000010DEd000017C2*
ID_MODEL_FROM_DATABASE=GM200 [GeForce GTX TITAN X]
@@ -32984,6 +33260,24 @@ pci:v000010DEd000017F1*
pci:v000010DEd000017FD*
ID_MODEL_FROM_DATABASE=GM200GL [Tesla M40]
+pci:v000010DEd00001AD6*
+ ID_MODEL_FROM_DATABASE=TU102 USB 3.1 Controller
+
+pci:v000010DEd00001AD7*
+ ID_MODEL_FROM_DATABASE=TU102 UCSI Controller
+
+pci:v000010DEd00001ADA*
+ ID_MODEL_FROM_DATABASE=TU106 USB 3.1 Host Controller
+
+pci:v000010DEd00001ADAsv00001043sd00008673*
+ ID_MODEL_FROM_DATABASE=TU106 USB 3.1 Host Controller (TURBO-RTX2070-8G)
+
+pci:v000010DEd00001ADB*
+ ID_MODEL_FROM_DATABASE=TU106 USB Type-C Port Policy Controller
+
+pci:v000010DEd00001ADBsv00001043sd00008673*
+ ID_MODEL_FROM_DATABASE=TU106 USB Type-C Port Policy Controller (TURBO-RTX2070-8G)
+
pci:v000010DEd00001B00*
ID_MODEL_FROM_DATABASE=GP102 [TITAN X]
@@ -32993,6 +33287,9 @@ pci:v000010DEd00001B01*
pci:v000010DEd00001B02*
ID_MODEL_FROM_DATABASE=GP102 [TITAN Xp]
+pci:v000010DEd00001B04*
+ ID_MODEL_FROM_DATABASE=GP102
+
pci:v000010DEd00001B06*
ID_MODEL_FROM_DATABASE=GP102 [GeForce GTX 1080 Ti]
@@ -33047,6 +33344,9 @@ pci:v000010DEd00001BA1sv00001462sd000011E9*
pci:v000010DEd00001BA1sv00001558sd00009501*
ID_MODEL_FROM_DATABASE=GP104M [GeForce GTX 1070 Mobile] (GeForce GTX 1070 Max-Q)
+pci:v000010DEd00001BA2*
+ ID_MODEL_FROM_DATABASE=GP104M [GeForce GTX 1070 Mobile]
+
pci:v000010DEd00001BAD*
ID_MODEL_FROM_DATABASE=GP104 [GeForce GTX 1070 Engineering Sample]
@@ -33065,6 +33365,9 @@ pci:v000010DEd00001BB4*
pci:v000010DEd00001BB5*
ID_MODEL_FROM_DATABASE=GP104GLM [Quadro P5200 Mobile]
+pci:v000010DEd00001BB5sv0000103Csd0000842F*
+ ID_MODEL_FROM_DATABASE=GP104GLM [Quadro P5200 Mobile] (P5200 [Zbook 17 G5 mobile workstation])
+
pci:v000010DEd00001BB6*
ID_MODEL_FROM_DATABASE=GP104GLM [Quadro P5000 Mobile]
@@ -33080,23 +33383,29 @@ pci:v000010DEd00001BB8*
pci:v000010DEd00001BB9*
ID_MODEL_FROM_DATABASE=GP104GLM [Quadro P4200 Mobile]
+pci:v000010DEd00001BB9sv0000103Csd0000842F*
+ ID_MODEL_FROM_DATABASE=GP104GLM [Quadro P4200 Mobile] (P4200 [Zbook 17 G5 mobile workstation])
+
pci:v000010DEd00001BBB*
ID_MODEL_FROM_DATABASE=GP104GLM [Quadro P3200 Mobile]
+pci:v000010DEd00001BBBsv0000103Csd0000842F*
+ ID_MODEL_FROM_DATABASE=GP104GLM [Quadro P3200 Mobile] (P3200 [Zbook 17 G5 moble workstation])
+
pci:v000010DEd00001BC7*
ID_MODEL_FROM_DATABASE=GP104 [P104-101]
pci:v000010DEd00001BE0*
- ID_MODEL_FROM_DATABASE=GP104M [GeForce GTX 1080 Mobile]
+ ID_MODEL_FROM_DATABASE=GP104BM [GeForce GTX 1080 Mobile]
pci:v000010DEd00001BE0sv00001028sd000007C0*
- ID_MODEL_FROM_DATABASE=GP104M [GeForce GTX 1080 Mobile] (GeForce GTX 1080 Max-Q)
+ ID_MODEL_FROM_DATABASE=GP104BM [GeForce GTX 1080 Mobile] (GeForce GTX 1080 Max-Q)
pci:v000010DEd00001BE0sv00001458sd0000355B*
- ID_MODEL_FROM_DATABASE=GP104M [GeForce GTX 1080 Mobile] (GeForce GTX 1080 Max-Q)
+ ID_MODEL_FROM_DATABASE=GP104BM [GeForce GTX 1080 Mobile] (GeForce GTX 1080 Max-Q)
pci:v000010DEd00001BE1*
- ID_MODEL_FROM_DATABASE=GP104M [GeForce GTX 1070 Mobile]
+ ID_MODEL_FROM_DATABASE=GP104BM [GeForce GTX 1070 Mobile]
pci:v000010DEd00001C00*
ID_MODEL_FROM_DATABASE=GP106
@@ -33134,6 +33443,12 @@ pci:v000010DEd00001C21*
pci:v000010DEd00001C22*
ID_MODEL_FROM_DATABASE=GP106M [GeForce GTX 1050 Mobile]
+pci:v000010DEd00001C23*
+ ID_MODEL_FROM_DATABASE=GP106M [GeForce GTX 1060 Mobile Rev. 2]
+
+pci:v000010DEd00001C23sv00001414sd00000020*
+ ID_MODEL_FROM_DATABASE=GP106M [GeForce GTX 1060 Mobile Rev. 2] (GTX 1060 Mobile)
+
pci:v000010DEd00001C30*
ID_MODEL_FROM_DATABASE=GP106GL [Quadro P2000]
@@ -33141,29 +33456,29 @@ pci:v000010DEd00001C35*
ID_MODEL_FROM_DATABASE=GP106
pci:v000010DEd00001C60*
- ID_MODEL_FROM_DATABASE=GP106M [GeForce GTX 1060 Mobile 6GB]
+ ID_MODEL_FROM_DATABASE=GP106BM [GeForce GTX 1060 Mobile 6GB]
pci:v000010DEd00001C60sv0000103Csd00008390*
- ID_MODEL_FROM_DATABASE=GP106M [GeForce GTX 1060 Mobile 6GB] (GeForce GTX 1060 Max-Q 6GB)
+ ID_MODEL_FROM_DATABASE=GP106BM [GeForce GTX 1060 Mobile 6GB] (GeForce GTX 1060 Max-Q 6GB)
pci:v000010DEd00001C61*
- ID_MODEL_FROM_DATABASE=GP106M [GeForce GTX 1050 Ti Mobile]
+ ID_MODEL_FROM_DATABASE=GP106BM [GeForce GTX 1050 Ti Mobile]
pci:v000010DEd00001C62*
- ID_MODEL_FROM_DATABASE=GP106M [GeForce GTX 1050 Mobile]
+ ID_MODEL_FROM_DATABASE=GP106BM [GeForce GTX 1050 Mobile]
pci:v000010DEd00001C70*
ID_MODEL_FROM_DATABASE=GP106GL
-pci:v000010DEd00001C80*
- ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 3GB]
-
pci:v000010DEd00001C81*
ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050]
pci:v000010DEd00001C82*
ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 Ti]
+pci:v000010DEd00001C83*
+ ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 3GB]
+
pci:v000010DEd00001C8C*
ID_MODEL_FROM_DATABASE=GP107M [GeForce GTX 1050 Ti Mobile]
@@ -33173,6 +33488,12 @@ pci:v000010DEd00001C8D*
pci:v000010DEd00001C8E*
ID_MODEL_FROM_DATABASE=GP107M
+pci:v000010DEd00001C8F*
+ ID_MODEL_FROM_DATABASE=GP107M [GeForce GTX 1050 Ti Max-Q]
+
+pci:v000010DEd00001C92*
+ ID_MODEL_FROM_DATABASE=GP107M [GeForce GTX 1050 Mobile]
+
pci:v000010DEd00001CA7*
ID_MODEL_FROM_DATABASE=GP107GL
@@ -33197,18 +33518,45 @@ pci:v000010DEd00001CB6*
pci:v000010DEd00001CBA*
ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P2000 Mobile]
+pci:v000010DEd00001CBAsv0000103Csd0000842C*
+ ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P2000 Mobile] (P2000 [Zbook 15 G5 mobile workstation])
+
+pci:v000010DEd00001CBAsv0000103Csd0000842F*
+ ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P2000 Mobile] (P2000 [Zbook 17 G5 mobile workstation])
+
pci:v000010DEd00001CBB*
ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P1000 Mobile]
+pci:v000010DEd00001CBBsv0000103Csd00008429*
+ ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P1000 Mobile] (P1000 [Zbook Studio G5 mobile workstation])
+
+pci:v000010DEd00001CBBsv0000103Csd0000842C*
+ ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P1000 Mobile] (P1000 [Zbook 15 G5 mobile workstation])
+
+pci:v000010DEd00001CBBsv0000103Csd0000842F*
+ ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P1000 Mobile] (P1000 [Zbook 17 G5 mobile workstation])
+
+pci:v000010DEd00001CBBsv0000103Csd00008451*
+ ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P1000 Mobile] (P1000 [Zbook Studio x360 G5 mobile workstation])
+
pci:v000010DEd00001CBC*
ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P600 Mobile]
+pci:v000010DEd00001CCC*
+ ID_MODEL_FROM_DATABASE=GP107BM [GeForce GTX 1050 Ti Mobile]
+
+pci:v000010DEd00001CCD*
+ ID_MODEL_FROM_DATABASE=GP107BM [GeForce GTX 1050 Mobile]
+
pci:v000010DEd00001D01*
ID_MODEL_FROM_DATABASE=GP108 [GeForce GT 1030]
pci:v000010DEd00001D10*
ID_MODEL_FROM_DATABASE=GP108M [GeForce MX150]
+pci:v000010DEd00001D10sv000017AAsd0000225E*
+ ID_MODEL_FROM_DATABASE=GP108M [GeForce MX150] (ThinkPad T480)
+
pci:v000010DEd00001D12*
ID_MODEL_FROM_DATABASE=GP108M [GeForce MX150]
@@ -33224,6 +33572,9 @@ pci:v000010DEd00001D81*
pci:v000010DEd00001DB1*
ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 SXM2 16GB]
+pci:v000010DEd00001DB2*
+ ID_MODEL_FROM_DATABASE=GV100 [Tesla V100-DGXS-16GB]
+
pci:v000010DEd00001DB3*
ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 FHHL 16GB]
@@ -33242,6 +33593,81 @@ pci:v000010DEd00001DB7*
pci:v000010DEd00001DBA*
ID_MODEL_FROM_DATABASE=GV100GL [Quadro GV100]
+pci:v000010DEd00001DBAsv000010DEsd000012EB*
+ ID_MODEL_FROM_DATABASE=GV100GL [Quadro GV100] (TITAN V CEO Edition)
+
+pci:v000010DEd00001E02*
+ ID_MODEL_FROM_DATABASE=TU102 [TITAN RTX]
+
+pci:v000010DEd00001E04*
+ ID_MODEL_FROM_DATABASE=TU102 [GeForce RTX 2080 Ti]
+
+pci:v000010DEd00001E07*
+ ID_MODEL_FROM_DATABASE=TU102 [GeForce RTX 2080 Ti Rev. A]
+
+pci:v000010DEd00001E07sv00001462sd00003715*
+ ID_MODEL_FROM_DATABASE=TU102 [GeForce RTX 2080 Ti Rev. A] (RTX 2080 Ti GAMING X TRIO)
+
+pci:v000010DEd00001E2D*
+ ID_MODEL_FROM_DATABASE=TU102B
+
+pci:v000010DEd00001E2E*
+ ID_MODEL_FROM_DATABASE=TU102B
+
+pci:v000010DEd00001E30*
+ ID_MODEL_FROM_DATABASE=TU102GL [Quadro RTX 6000]
+
+pci:v000010DEd00001E38*
+ ID_MODEL_FROM_DATABASE=TU102GL
+
+pci:v000010DEd00001E3C*
+ ID_MODEL_FROM_DATABASE=TU102GL
+
+pci:v000010DEd00001E3D*
+ ID_MODEL_FROM_DATABASE=TU102GL
+
+pci:v000010DEd00001E3E*
+ ID_MODEL_FROM_DATABASE=TU102GL
+
+pci:v000010DEd00001E82*
+ ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2080]
+
+pci:v000010DEd00001E87*
+ ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2080 Rev. A]
+
+pci:v000010DEd00001EAB*
+ ID_MODEL_FROM_DATABASE=TU104M [GeForce RTX 2080 Mobile]
+
+pci:v000010DEd00001EAE*
+ ID_MODEL_FROM_DATABASE=TU104M
+
+pci:v000010DEd00001EB0*
+ ID_MODEL_FROM_DATABASE=TU104GL [Quadro RTX 5000]
+
+pci:v000010DEd00001EB1*
+ ID_MODEL_FROM_DATABASE=TU104GL [Quadro RTX 4000]
+
+pci:v000010DEd00001EB8*
+ ID_MODEL_FROM_DATABASE=TU104GL [Tesla T4]
+
+pci:v000010DEd00001F02*
+ ID_MODEL_FROM_DATABASE=TU106 [GeForce RTX 2070]
+
+pci:v000010DEd00001F02sv00001043sd00008673*
+ ID_MODEL_FROM_DATABASE=TU106 [GeForce RTX 2070] (TURBO RTX 2070)
+
+pci:v000010DEd00001F04*
+ ID_MODEL_FROM_DATABASE=TU106
+
+pci:v000010DEd00001F07*
+ ID_MODEL_FROM_DATABASE=TU106 [GeForce RTX 2070 Rev. A]
+
+pci:v000010DEd00001F08*
+ ID_MODEL_FROM_DATABASE=TU106
+
+pci:v000010DEd00001F82*
+ ID_MODEL_FROM_DATABASE=TU107
+
pci:v000010DF*
ID_VENDOR_FROM_DATABASE=Emulex Corporation
@@ -33362,6 +33788,9 @@ pci:v000010DFd0000E300sv000010DFsd0000E323*
pci:v000010DFd0000E300sv000010DFsd0000E325*
ID_MODEL_FROM_DATABASE=Lancer Gen6: LPe32000 Fibre Channel Host Adapter (Lancer Gen6: LPe31000 Fibre Channel Host Adapter)
+pci:v000010DFd0000E333*
+ ID_MODEL_FROM_DATABASE=Lancer Gen6: LPe32000 Fibre Channel Host Adapter
+
pci:v000010DFd0000F011*
ID_MODEL_FROM_DATABASE=Saturn: LightPulse Fibre Channel Host Adapter
@@ -33428,6 +33857,9 @@ pci:v000010DFd0000F400sv000010DFsd0000F401*
pci:v000010DFd0000F400sv000010DFsd0000F402*
ID_MODEL_FROM_DATABASE=LPe36000 Fibre Channel Host Adapter [Prism] (LPe35000 Fibre Channel Host Adapter [Prism])
+pci:v000010DFd0000F400sv000010DFsd0000F410*
+ ID_MODEL_FROM_DATABASE=LPe36000 Fibre Channel Host Adapter [Prism] (LPe35002-M2-D 2-Port 32Gb Fibre Channel Adapter)
+
pci:v000010DFd0000F700*
ID_MODEL_FROM_DATABASE=LP7000 Fibre Channel Host Adapter
@@ -33566,6 +33998,9 @@ pci:v000010E3d00000860*
pci:v000010E3d00000862*
ID_MODEL_FROM_DATABASE=CA91C862A [QSpan-II]
+pci:v000010E3d00008111*
+ ID_MODEL_FROM_DATABASE=Tsi381 PCIe to PCI Bridge
+
pci:v000010E3d00008260*
ID_MODEL_FROM_DATABASE=CA91L8200B [Dual PCI PowerSpan II]
@@ -33788,6 +34223,12 @@ pci:v000010ECd00005250*
pci:v000010ECd0000525A*
ID_MODEL_FROM_DATABASE=RTS525A PCI Express Card Reader
+pci:v000010ECd0000525Asv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=RTS525A PCI Express Card Reader (Latitude E7470)
+
+pci:v000010ECd0000525Asv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=RTS525A PCI Express Card Reader (XPS 15 9550)
+
pci:v000010ECd0000525Asv000017AAsd0000224F*
ID_MODEL_FROM_DATABASE=RTS525A PCI Express Card Reader (ThinkPad X1 Carbon 5th Gen)
@@ -34265,6 +34706,9 @@ pci:v000010ECd0000B822*
pci:v000010ECd0000C821*
ID_MODEL_FROM_DATABASE=RTL8821CE 802.11ac PCIe Wireless Network Adapter
+pci:v000010ECd0000D723*
+ ID_MODEL_FROM_DATABASE=RTL8723DE 802.11b/g/n PCIe Adapter
+
pci:v000010ED*
ID_VENDOR_FROM_DATABASE=Ascii Corporation
@@ -34349,6 +34793,9 @@ pci:v000010EEd0000EBF1*
pci:v000010EEd0000EBF2*
ID_MODEL_FROM_DATABASE=SED Systems Common PCI Interface
+pci:v000010EEd0000EBF3*
+ ID_MODEL_FROM_DATABASE=SED Systems PCIe-AXI Bridge
+
pci:v000010EF*
ID_VENDOR_FROM_DATABASE=Racore Computer Products, Inc.
@@ -37457,6 +37904,9 @@ pci:v0000111Dd0000806E*
pci:v0000111Dd0000806F*
ID_MODEL_FROM_DATABASE=HIO524G2 PCI Express Gen2 Switch
+pci:v0000111Dd00008077*
+ ID_MODEL_FROM_DATABASE=89HPES64H16G2 64-Lane 16-Port PCIe Gen2 System Interconnect Switch
+
pci:v0000111Dd00008088*
ID_MODEL_FROM_DATABASE=PES32NT8BG2 PCI Express Switch
@@ -37581,7 +38031,7 @@ pci:v0000112A*
ID_VENDOR_FROM_DATABASE=Hermes Electronics Company, Ltd.
pci:v0000112B*
- ID_VENDOR_FROM_DATABASE=Linotype - Hell AG
+ ID_VENDOR_FROM_DATABASE=Heidelberger Druckmaschinen AGHeidelberger Druckmaschinen AG
pci:v0000112C*
ID_VENDOR_FROM_DATABASE=Zenith Data Systems
@@ -38738,6 +39188,33 @@ pci:v00001137d00000043sv00001137sd00000137*
pci:v00001137d00000043sv00001137sd0000014D*
ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1385 PCIe Ethernet NIC)
+pci:v00001137d00000043sv00001137sd0000015D*
+ ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1387 MLOM Ethernet NIC)
+
+pci:v00001137d00000043sv00001137sd00000215*
+ ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1440 Mezzanine Ethernet NIC)
+
+pci:v00001137d00000043sv00001137sd00000216*
+ ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1480 MLOM Ethernet NIC)
+
+pci:v00001137d00000043sv00001137sd00000217*
+ ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1455 PCIe Ethernet NIC)
+
+pci:v00001137d00000043sv00001137sd00000218*
+ ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1457 MLOM Ethernet NIC)
+
+pci:v00001137d00000043sv00001137sd00000219*
+ ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1485 PCIe Ethernet NIC)
+
+pci:v00001137d00000043sv00001137sd0000021A*
+ ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1487 MLOM Ethernet NIC)
+
+pci:v00001137d00000043sv00001137sd0000024A*
+ ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1495 PCIe Ethernet NIC)
+
+pci:v00001137d00000043sv00001137sd0000024B*
+ ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1497 MLOM Ethernet NIC)
+
pci:v00001137d00000044*
ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic
@@ -38873,6 +39350,9 @@ pci:v00001137d000000CFsv00001137sd0000012E*
pci:v00001137d000000CFsv00001137sd00000137*
ID_MODEL_FROM_DATABASE=VIC Userspace NIC (VIC 1380 Mezzanine Userspace NIC)
+pci:v00001137d0000023E*
+ ID_MODEL_FROM_DATABASE=1GigE I350 LOM
+
pci:v00001138*
ID_VENDOR_FROM_DATABASE=Ziatech Corporation
@@ -39986,6 +40466,27 @@ pci:v00001179d00000103*
pci:v00001179d0000010F*
ID_MODEL_FROM_DATABASE=NVMe Controller
+pci:v00001179d00000110*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller Cx5
+
+pci:v00001179d00000110sv00001028sd00001FFB*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller Cx5 (Express Flash NVMe 960G (RI) U.2 (CD5))
+
+pci:v00001179d00000110sv00001028sd00001FFC*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller Cx5 (Express Flash NVMe 1.92T (RI) U.2 (CD5))
+
+pci:v00001179d00000110sv00001028sd00001FFD*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller Cx5 (Express Flash NVMe 3.84T (RI) U.2 (CD5))
+
+pci:v00001179d00000110sv00001028sd00001FFE*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller Cx5 (Express Flash NVMe 7.68T (RI) U.2 (CD5))
+
+pci:v00001179d00000110sv00001D49sd00004039*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller Cx5 (Thinksystem U.2 CM5 NVMe SSD)
+
+pci:v00001179d00000110sv00001D49sd0000403A*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller Cx5 (Thinksystem AIC CM5 NVMe SSD)
+
pci:v00001179d00000115*
ID_MODEL_FROM_DATABASE=XG4 NVMe SSD Controller
@@ -40293,7 +40794,7 @@ pci:v00001180d00000476sv000017AAsd000020C4*
ID_MODEL_FROM_DATABASE=RL5c476 II (ThinkPad T61/R61)
pci:v00001180d00000476sv000017AAsd000020C6*
- ID_MODEL_FROM_DATABASE=RL5c476 II (ThinkPad R61)
+ ID_MODEL_FROM_DATABASE=RL5c476 II (ThinkPad R61/T400)
pci:v00001180d00000477*
ID_MODEL_FROM_DATABASE=RL5c477
@@ -40398,7 +40899,7 @@ pci:v00001180d00000592sv0000144Dsd0000C018*
ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (X20 IV)
pci:v00001180d00000592sv000017AAsd000020CA*
- ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (ThinkPad T61)
+ ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (ThinkPad T61/T400)
pci:v00001180d00000811*
ID_MODEL_FROM_DATABASE=R5C811
@@ -40464,7 +40965,7 @@ pci:v00001180d00000822sv000017AAsd000020C7*
ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (ThinkPad T61)
pci:v00001180d00000822sv000017AAsd000020C8*
- ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (ThinkPad W500)
+ ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (ThinkPad T400/W500)
pci:v00001180d00000832*
ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller
@@ -40574,6 +41075,9 @@ pci:v00001180d00000852sv00001180sd00000852*
pci:v00001180d00000852sv00001324sd000010CF*
ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (P7120)
+pci:v00001180d00000852sv000017AAsd000020CB*
+ ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (ThinkPad T400)
+
pci:v00001180d0000E230*
ID_MODEL_FROM_DATABASE=R5U2xx (R5U230 / R5U231 / R5U241) [Memory Stick Host Controller]
@@ -40595,6 +41099,9 @@ pci:v00001180d0000E822sv00001028sd0000040A*
pci:v00001180d0000E822sv00001028sd0000040B*
ID_MODEL_FROM_DATABASE=MMC/SD Host Controller (Latitude E6510)
+pci:v00001180d0000E822sv000017AAsd000021CF*
+ ID_MODEL_FROM_DATABASE=MMC/SD Host Controller (ThinkPad T520)
+
pci:v00001180d0000E823*
ID_MODEL_FROM_DATABASE=PCIe SDXC/MMC Host Controller
@@ -40955,6 +41462,12 @@ pci:v0000119F*
pci:v0000119Fd00001081*
ID_MODEL_FROM_DATABASE=BXI Host Channel Adapter
+pci:v0000119Fd00001101*
+ ID_MODEL_FROM_DATABASE=BXI Host Channel Adapter v1.2
+
+pci:v0000119Fd00001121*
+ ID_MODEL_FROM_DATABASE=BXI Host Channel Adapter v1.3
+
pci:v000011A0*
ID_VENDOR_FROM_DATABASE=Convex Computer Corporation
@@ -42551,6 +43064,9 @@ pci:v000011F8d00008000*
pci:v000011F8d00008009*
ID_MODEL_FROM_DATABASE=PM8009 SPCve 8x6G
+pci:v000011F8d00008018*
+ ID_MODEL_FROM_DATABASE=PM8018 Adaptec SAS Adaptor ASA-70165H PCIe Gen3 x8 6 Gbps 16-lane 4x SFF-8644
+
pci:v000011F8d00008032*
ID_MODEL_FROM_DATABASE=PM8032 Tachyon QE8
@@ -48053,14 +48569,17 @@ pci:v000013FEd00001711*
pci:v000013FEd00001733*
ID_MODEL_FROM_DATABASE=PCI-1733 32-channel isolated digital input card
+pci:v000013FEd00001734*
+ ID_MODEL_FROM_DATABASE=PCI-1734 32-channel isolated digital output card
+
pci:v000013FEd00001752*
- ID_MODEL_FROM_DATABASE=PCI-1752
+ ID_MODEL_FROM_DATABASE=PCI-1752 64-channel Isolated Digital Output Card
pci:v000013FEd00001754*
- ID_MODEL_FROM_DATABASE=PCI-1754
+ ID_MODEL_FROM_DATABASE=PCI-1754 64-channel Isolated Digital Input Card
pci:v000013FEd00001756*
- ID_MODEL_FROM_DATABASE=PCI-1756
+ ID_MODEL_FROM_DATABASE=PCI-1756 64-ch Isolated Digital I/O PCI Card
pci:v000013FEd0000C302*
ID_MODEL_FROM_DATABASE=MIOe-3680 2-Port CAN-Bus MIOe Module with Isolation Protection
@@ -49427,6 +49946,15 @@ pci:v00001425d000050AC*
pci:v00001425d000050AD*
ID_MODEL_FROM_DATABASE=T520-50AD Unified Wire Ethernet Controller
+pci:v00001425d000050AE*
+ ID_MODEL_FROM_DATABASE=T540-50AE Unified Wire Ethernet Controller
+
+pci:v00001425d000050AF*
+ ID_MODEL_FROM_DATABASE=T580-50AF Unified Wire Ethernet Controller
+
+pci:v00001425d000050B0*
+ ID_MODEL_FROM_DATABASE=T520-50B0 Unified Wire Ethernet Controller
+
pci:v00001425d00005401*
ID_MODEL_FROM_DATABASE=T520-CR Unified Wire Ethernet Controller
@@ -49628,6 +50156,15 @@ pci:v00001425d000054AC*
pci:v00001425d000054AD*
ID_MODEL_FROM_DATABASE=T520-50AD Unified Wire Ethernet Controller
+pci:v00001425d000054AE*
+ ID_MODEL_FROM_DATABASE=T540-50AE Unified Wire Ethernet Controller
+
+pci:v00001425d000054AF*
+ ID_MODEL_FROM_DATABASE=T580-50AF Unified Wire Ethernet Controller
+
+pci:v00001425d000054B0*
+ ID_MODEL_FROM_DATABASE=T520-50B0 Unified Wire Ethernet Controller
+
pci:v00001425d00005501*
ID_MODEL_FROM_DATABASE=T520-CR Unified Wire Storage Controller
@@ -49829,6 +50366,15 @@ pci:v00001425d000055AC*
pci:v00001425d000055AD*
ID_MODEL_FROM_DATABASE=T520-50AD Unified Wire Storage Controller
+pci:v00001425d000055AE*
+ ID_MODEL_FROM_DATABASE=T540-50AE Unified Wire Storage Controller
+
+pci:v00001425d000055AF*
+ ID_MODEL_FROM_DATABASE=T580-50AF Unified Wire Storage Controller
+
+pci:v00001425d000055B0*
+ ID_MODEL_FROM_DATABASE=T520-50B0 Unified Wire Storage Controller
+
pci:v00001425d00005601*
ID_MODEL_FROM_DATABASE=T520-CR Unified Wire Storage Controller
@@ -50030,6 +50576,15 @@ pci:v00001425d000056AC*
pci:v00001425d000056AD*
ID_MODEL_FROM_DATABASE=T520-50AD Unified Wire Storage Controller
+pci:v00001425d000056AE*
+ ID_MODEL_FROM_DATABASE=T540-50AE Unified Wire Storage Controller
+
+pci:v00001425d000056AF*
+ ID_MODEL_FROM_DATABASE=T580-50AF Unified Wire Storage Controller
+
+pci:v00001425d000056B0*
+ ID_MODEL_FROM_DATABASE=T520-50B0 Unified Wire Storage Controller
+
pci:v00001425d00005701*
ID_MODEL_FROM_DATABASE=T520-CR Unified Wire Ethernet Controller
@@ -50348,6 +50903,15 @@ pci:v00001425d000058AC*
pci:v00001425d000058AD*
ID_MODEL_FROM_DATABASE=T520-50AD Unified Wire Ethernet Controller [VF]
+pci:v00001425d000058AE*
+ ID_MODEL_FROM_DATABASE=T540-50AE Unified Wire Ethernet Controller [VF]
+
+pci:v00001425d000058AF*
+ ID_MODEL_FROM_DATABASE=T580-50AF Unified Wire Ethernet Controller [VF]
+
+pci:v00001425d000058B0*
+ ID_MODEL_FROM_DATABASE=T520-50B0 Unified Wire Ethernet Controller [VF]
+
pci:v00001425d00006001*
ID_MODEL_FROM_DATABASE=T6225-CR Unified Wire Ethernet Controller
@@ -50417,6 +50981,9 @@ pci:v00001425d00006088*
pci:v00001425d00006089*
ID_MODEL_FROM_DATABASE=T62100-6089 Unified Wire Ethernet Controller
+pci:v00001425d0000608A*
+ ID_MODEL_FROM_DATABASE=T62100-608a Unified Wire Ethernet Controller
+
pci:v00001425d00006401*
ID_MODEL_FROM_DATABASE=T6225-CR Unified Wire Ethernet Controller
@@ -50486,6 +51053,9 @@ pci:v00001425d00006488*
pci:v00001425d00006489*
ID_MODEL_FROM_DATABASE=T62100-6089 Unified Wire Ethernet Controller
+pci:v00001425d0000648A*
+ ID_MODEL_FROM_DATABASE=T62100-608a Unified Wire Ethernet Controller
+
pci:v00001425d00006501*
ID_MODEL_FROM_DATABASE=T6225-CR Unified Wire Storage Controller
@@ -50555,6 +51125,9 @@ pci:v00001425d00006588*
pci:v00001425d00006589*
ID_MODEL_FROM_DATABASE=T62100-6089 Unified Wire Storage Controller
+pci:v00001425d0000658A*
+ ID_MODEL_FROM_DATABASE=T62100-608a Unified Wire Storage Controller
+
pci:v00001425d00006601*
ID_MODEL_FROM_DATABASE=T6225-CR Unified Wire Storage Controller
@@ -50624,6 +51197,9 @@ pci:v00001425d00006688*
pci:v00001425d00006689*
ID_MODEL_FROM_DATABASE=T62100-6089 Unified Wire Storage Controller
+pci:v00001425d0000668A*
+ ID_MODEL_FROM_DATABASE=T62100-608a Unified Wire Storage Controller
+
pci:v00001425d00006801*
ID_MODEL_FROM_DATABASE=T6225-CR Unified Wire Ethernet Controller [VF]
@@ -50693,6 +51269,9 @@ pci:v00001425d00006888*
pci:v00001425d00006889*
ID_MODEL_FROM_DATABASE=T62100-6089 Unified Wire Ethernet Controller [VF]
+pci:v00001425d0000688A*
+ ID_MODEL_FROM_DATABASE=T62100-608a Unified Wire Ethernet Controller [VF]
+
pci:v00001425d0000A000*
ID_MODEL_FROM_DATABASE=PE10K Unified Wire Ethernet Controller
@@ -50906,6 +51485,9 @@ pci:v0000144Dd0000A804*
pci:v0000144Dd0000A808*
ID_MODEL_FROM_DATABASE=NVMe SSD Controller SM981/PM981
+pci:v0000144Dd0000A808sv00001D49sd0000403B*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller SM981/PM981 (Thinksystem U.2 PM983 NVMe SSD)
+
pci:v0000144Dd0000A820*
ID_MODEL_FROM_DATABASE=NVMe SSD Controller 171X
@@ -50966,6 +51548,15 @@ pci:v0000144Dd0000A822sv00001014sd00000622*
pci:v0000144Dd0000A822sv00001014sd00000629*
ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172Xa/172Xb (PCIe3 6.4TB NVMe Flash Adapter II x8)
+pci:v0000144Dd0000A822sv00001014sd0000064A*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172Xa/172Xb (PCIe3 1.6TB NVMe Flash Adapter III x8)
+
+pci:v0000144Dd0000A822sv00001014sd0000064B*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172Xa/172Xb (PCIe3 3.2TB NVMe Flash Adapter III x8)
+
+pci:v0000144Dd0000A822sv00001014sd0000064C*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172Xa/172Xb (PCIe3 6.4TB NVMe Flash Adapter III x8)
+
pci:v0000144Dd0000A822sv00001028sd00001FD9*
ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172Xa/172Xb (Express Flash PM1725a 800GB SFF)
@@ -51656,6 +52247,15 @@ pci:v000014D2d0000E020*
pci:v000014D3*
ID_VENDOR_FROM_DATABASE=CIRTECH (UK) Ltd
+pci:v000014D3d00000002*
+ ID_MODEL_FROM_DATABASE=DTL-T14000 Rev. 1 [PS2 TOOL CD/DVD Emulator]
+
+pci:v000014D3d00000003*
+ ID_MODEL_FROM_DATABASE=DTL-T14000 Rev. 2 [PS2 TOOL CD/DVD Emulator]
+
+pci:v000014D3d00000004*
+ ID_MODEL_FROM_DATABASE=DTL-T14000 Rev. 3 [PS2 TOOL CD/DVD Emulator]
+
pci:v000014D4*
ID_VENDOR_FROM_DATABASE=Panacom Technology Corp
@@ -51756,7 +52356,7 @@ pci:v000014E3*
ID_VENDOR_FROM_DATABASE=AMTELCO
pci:v000014E4*
- ID_VENDOR_FROM_DATABASE=Broadcom Limited
+ ID_VENDOR_FROM_DATABASE=Broadcom Inc. and subsidiaries
pci:v000014E4d00000576*
ID_MODEL_FROM_DATABASE=BCM43224 802.11a/b/g/n
@@ -52320,7 +52920,10 @@ pci:v000014E4d0000165Esv000010CFsd00001279*
ID_MODEL_FROM_DATABASE=NetXtreme BCM5705M_2 Gigabit Ethernet (LifeBook E8010D)
pci:v000014E4d0000165F*
- ID_MODEL_FROM_DATABASE=NetXtreme BCM5720 Gigabit Ethernet PCIe
+ ID_MODEL_FROM_DATABASE=NetXtreme BCM5720 2-port Gigabit Ethernet PCIe
+
+pci:v000014E4d0000165Fsv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=NetXtreme BCM5720 2-port Gigabit Ethernet PCIe (PowerEdge R320 server)
pci:v000014E4d00001662*
ID_MODEL_FROM_DATABASE=NetXtreme II BCM57712 10 Gigabit Ethernet
@@ -53885,9 +54488,18 @@ pci:v000014E4d00005841*
pci:v000014E4d00005850*
ID_MODEL_FROM_DATABASE=BCM5850 Crypto Accelerator
+pci:v000014E4d00005E87*
+ ID_MODEL_FROM_DATABASE=Valkyrie offload engine
+
pci:v000014E4d00008602*
ID_MODEL_FROM_DATABASE=BCM7400/BCM7405 Serial ATA Controller
+pci:v000014E4d00009026*
+ ID_MODEL_FROM_DATABASE=CN99xx [ThunderX2] Integrated USB 3.0 xHCI Host Controller
+
+pci:v000014E4d00009027*
+ ID_MODEL_FROM_DATABASE=CN99xx [ThunderX2] Integrated AHCI/SATA 3 Host Controller
+
pci:v000014E4d0000A8D8*
ID_MODEL_FROM_DATABASE=BCM43224/5 Wireless Network Adapter
@@ -53933,6 +54545,21 @@ pci:v000014E4d0000B850*
pci:v000014E4d0000B960*
ID_MODEL_FROM_DATABASE=Broadcom BCM56960 Switch ASIC
+pci:v000014E4d0000D802*
+ ID_MODEL_FROM_DATABASE=BCM58802 Stingray 50Gb Ethernet SoC
+
+pci:v000014E4d0000D802sv000014E4sd00008021*
+ ID_MODEL_FROM_DATABASE=BCM58802 Stingray 50Gb Ethernet SoC (Stingray Dual-Port 25Gb Ethernet PCIe SmartNIC w16GB DRAM (Part No BCM958802A8046C))
+
+pci:v000014E4d0000D802sv000014E4sd00008024*
+ ID_MODEL_FROM_DATABASE=BCM58802 Stingray 50Gb Ethernet SoC (Stingray Dual-Port 25Gb Ethernet PCIe SmartNIC w4GB DRAM (Part No BCM958802A8044C))
+
+pci:v000014E4d0000D802sv000014E4sd00008028*
+ ID_MODEL_FROM_DATABASE=BCM58802 Stingray 50Gb Ethernet SoC (Stingray Dual-Port 25Gb Ethernet PCIe SmartNIC w8GB DRAM (Part No BCM958802A8048C))
+
+pci:v000014E4d0000D804*
+ ID_MODEL_FROM_DATABASE=BCM58804 Stingray 100Gb Ethernet SoC
+
pci:v000014E5*
ID_VENDOR_FROM_DATABASE=Pixelfusion Ltd
@@ -55883,6 +56510,9 @@ pci:v000015B3d00000210*
pci:v000015B3d00000211*
ID_MODEL_FROM_DATABASE=MT416842 Family [BlueField SoC Flash Recovery]
+pci:v000015B3d00000212*
+ ID_MODEL_FROM_DATABASE=MT2892 Family [ConnectX-6 Dx Flash Recovery]
+
pci:v000015B3d0000024E*
ID_MODEL_FROM_DATABASE=MT53100 [Spectrum-2, Flash recovery mode]
@@ -56121,10 +56751,10 @@ pci:v000015B3d0000101C*
ID_MODEL_FROM_DATABASE=MT28908 Family [ConnectX-6 Virtual Function]
pci:v000015B3d0000101D*
- ID_MODEL_FROM_DATABASE=MT28841
+ ID_MODEL_FROM_DATABASE=MT2892 Family [ConnectX-6 Dx]
pci:v000015B3d0000101E*
- ID_MODEL_FROM_DATABASE=MT28850
+ ID_MODEL_FROM_DATABASE=MT2892 Family [ConnectX-6 Dx Virtual Function]
pci:v000015B3d0000101F*
ID_MODEL_FROM_DATABASE=MT28851
@@ -56141,6 +56771,15 @@ pci:v000015B3d00001974*
pci:v000015B3d00001975*
ID_MODEL_FROM_DATABASE=MT416842 Family [BlueField SoC PCIe Bridge]
+pci:v000015B3d00004117*
+ ID_MODEL_FROM_DATABASE=MT27712A0-FDCF-AE
+
+pci:v000015B3d00004117sv00001BD4sd00000039*
+ ID_MODEL_FROM_DATABASE=MT27712A0-FDCF-AE (SN10XMP2P25)
+
+pci:v000015B3d00004117sv00001BD4sd0000004D*
+ ID_MODEL_FROM_DATABASE=MT27712A0-FDCF-AE (SN10XMP2P25,YZPC-01191-101)
+
pci:v000015B3d00005274*
ID_MODEL_FROM_DATABASE=MT21108 InfiniBridge
@@ -56301,7 +56940,7 @@ pci:v000015B3d0000CF6C*
ID_MODEL_FROM_DATABASE=MT53100 [Spectrum-2]
pci:v000015B3d0000D2F0*
- ID_MODEL_FROM_DATABASE=Switch-IB 3 HDR (200Gbps) switch
+ ID_MODEL_FROM_DATABASE=Quantum HDR (200Gbps) switch
pci:v000015B4*
ID_VENDOR_FROM_DATABASE=CCI/TRIAD
@@ -56369,6 +57008,9 @@ pci:v000015B7d00002001*
pci:v000015B7d00005001*
ID_MODEL_FROM_DATABASE=WD Black NVMe SSD
+pci:v000015B7d00005002*
+ ID_MODEL_FROM_DATABASE=WD Black 2018/PC SN720 NVMe SSD
+
pci:v000015B8*
ID_VENDOR_FROM_DATABASE=ADDI-DATA GmbH
@@ -57152,6 +57794,12 @@ pci:v0000165Cd000071A1*
pci:v0000165Cd000071B1*
ID_MODEL_FROM_DATABASE=Proc10A
+pci:v0000165Cd000072B1*
+ ID_MODEL_FROM_DATABASE=HawkEye
+
+pci:v0000165Cd000073B1*
+ ID_MODEL_FROM_DATABASE=Proc10s
+
pci:v0000165D*
ID_VENDOR_FROM_DATABASE=Hsing Tech. Enterprise Co., Ltd.
@@ -57239,6 +57887,9 @@ pci:v00001681*
pci:v00001682*
ID_VENDOR_FROM_DATABASE=XFX Pine Group Inc.
+pci:v00001682d0000C580*
+ ID_MODEL_FROM_DATABASE=Radeon RX 580
+
pci:v00001688*
ID_VENDOR_FROM_DATABASE=CastleNet Technology Inc.
@@ -57920,6 +58571,9 @@ pci:v0000168Cd0000003C*
pci:v0000168Cd0000003E*
ID_MODEL_FROM_DATABASE=QCA6174 802.11ac Wireless Network Adapter
+pci:v0000168Cd0000003Esv00001A56sd0000143A*
+ ID_MODEL_FROM_DATABASE=QCA6174 802.11ac Wireless Network Adapter (Killer 1435 Wireless-AC)
+
pci:v0000168Cd0000003Esv00001A56sd00001525*
ID_MODEL_FROM_DATABASE=QCA6174 802.11ac Wireless Network Adapter (Killer N1525 Wireless-AC)
@@ -58160,6 +58814,12 @@ pci:v000016D5d00004357*
pci:v000016D5d00004457*
ID_MODEL_FROM_DATABASE=PMC730, APC730, AcPC730 Multifunction Module
+pci:v000016D5d00004471*
+ ID_MODEL_FROM_DATABASE=XMC730 Multi-function I/O module with front I/O
+
+pci:v000016D5d00004473*
+ ID_MODEL_FROM_DATABASE=XMC730CC Multi-function I/O module with rear I/O Conduction-cooled
+
pci:v000016D5d0000464D*
ID_MODEL_FROM_DATABASE=PMC408 32-Channel Digital Input/Output Module
@@ -58559,6 +59219,27 @@ pci:v00001760d00000101*
pci:v00001760d00000102*
ID_MODEL_FROM_DATABASE=PCD-7104 Digital Input & Output PCI Card
+pci:v00001760d00000121*
+ ID_MODEL_FROM_DATABASE=PCT-7303A PC card with IRC counters
+
+pci:v00001760d00000122*
+ ID_MODEL_FROM_DATABASE=PCT-7408A PC card with counters and timers
+
+pci:v00001760d00000123*
+ ID_MODEL_FROM_DATABASE=PCT-7424 PCI card with standard counters
+
+pci:v00001760d00000214*
+ ID_MODEL_FROM_DATABASE=PCT-7424C (F0) PC card with standard counters
+
+pci:v00001760d00000215*
+ ID_MODEL_FROM_DATABASE=PCT-7424C (F1) PC card with standard counters
+
+pci:v00001760d00000216*
+ ID_MODEL_FROM_DATABASE=PCT-7424E (F0) PC card with standard counters
+
+pci:v00001760d00000217*
+ ID_MODEL_FROM_DATABASE=PCT-7424E (F1) PC card with standard counters
+
pci:v00001760d00000303*
ID_MODEL_FROM_DATABASE=PCD-7006C Digital Input & Output PCI Card
@@ -58859,6 +59540,12 @@ pci:v0000177Dd0000A200*
pci:v0000177Dd0000A300*
ID_MODEL_FROM_DATABASE=OCTEON TX CN83XX
+pci:v0000177Dd0000AF00*
+ ID_MODEL_FROM_DATABASE=CN99xx [ThunderX2] Integrated PCI Host bridge
+
+pci:v0000177Dd0000AF84*
+ ID_MODEL_FROM_DATABASE=CN99xx [ThunderX2] Integrated PCI Express RP Bridge
+
pci:v00001787*
ID_VENDOR_FROM_DATABASE=Hightech Information System Ltd.
@@ -59009,6 +59696,9 @@ pci:v000017A0d00008083*
pci:v000017A0d00008084*
ID_MODEL_FROM_DATABASE=GL880 USB 2.0 EHCI controller
+pci:v000017A0d00009750*
+ ID_MODEL_FROM_DATABASE=GL9750 SD Host Controller
+
pci:v000017AA*
ID_VENDOR_FROM_DATABASE=Lenovo
@@ -60827,17 +61517,26 @@ pci:v00001924d00000A03sv00001924sd0000801A*
pci:v00001924d00000A03sv00001924sd0000801B*
ID_MODEL_FROM_DATABASE=SFC9220 10/40G Ethernet Controller (SFN8522-R3 8000 Series 10G Adapter)
+pci:v00001924d00000A03sv00001924sd0000801C*
+ ID_MODEL_FROM_DATABASE=SFC9220 10/40G Ethernet Controller (SFN8042-R3 8000 Series 10/40G Adapter)
+
+pci:v00001924d00000A03sv00001924sd00008021*
+ ID_MODEL_FROM_DATABASE=SFC9220 10/40G Ethernet Controller (SFN8041-R1 8000 Series 10/40G Adapter)
+
pci:v00001924d00000B03*
- ID_MODEL_FROM_DATABASE=SFC9250 10/25/40/50/100G Ethernet Controller
+ ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller
pci:v00001924d00000B03sv00001924sd0000801D*
- ID_MODEL_FROM_DATABASE=SFC9250 10/25/40/50/100G Ethernet Controller (x2522-R1 2000 Series 10/25G Adapter)
+ ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (x2522-R1 2000 Series 10/25G Adapter)
pci:v00001924d00000B03sv00001924sd0000801E*
- ID_MODEL_FROM_DATABASE=SFC9250 10/25/40/50/100G Ethernet Controller (x2542-R1 2000 Series 40/100G Adapter)
+ ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (x2542-R1 2000 Series 40/100G Adapter)
pci:v00001924d00000B03sv00001924sd00008022*
- ID_MODEL_FROM_DATABASE=SFC9250 10/25/40/50/100G Ethernet Controller (x2522-R2 2000 Series 10/25G Adapter)
+ ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (XtremeScale X2522 10G Network Adapter)
+
+pci:v00001924d00000B03sv00001924sd00008028*
+ ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (XtremeScale X2522-25G Network Adapter)
pci:v00001924d00001803*
ID_MODEL_FROM_DATABASE=SFC9020 10G Ethernet Controller (Virtual Function)
@@ -60855,7 +61554,7 @@ pci:v00001924d00001A03*
ID_MODEL_FROM_DATABASE=SFC9220 10/40G Ethernet Controller (Virtual Function)
pci:v00001924d00001B03*
- ID_MODEL_FROM_DATABASE=SFC9250 10/25/40/50/100G Ethernet Controller (Virtual Function)
+ ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (Virtual Function)
pci:v00001924d00006703*
ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm]
@@ -60902,6 +61601,9 @@ pci:v00001924d0000C101*
pci:v0000192A*
ID_VENDOR_FROM_DATABASE=BiTMICRO Networks Inc.
+pci:v0000192Ad00000008*
+ ID_MODEL_FROM_DATABASE=RAMPART
+
pci:v0000192E*
ID_VENDOR_FROM_DATABASE=TransDimension
@@ -61379,6 +62081,9 @@ pci:v00001969d00002060*
pci:v00001969d00002062*
ID_MODEL_FROM_DATABASE=AR8152 v2.0 Fast Ethernet
+pci:v00001969d00002062sv00001043sd00008468*
+ ID_MODEL_FROM_DATABASE=AR8152 v2.0 Fast Ethernet (Eee PC 1015PX)
+
pci:v00001969d0000E091*
ID_MODEL_FROM_DATABASE=Killer E220x Gigabit Ethernet Controller
@@ -61403,6 +62108,9 @@ pci:v0000196Ad00000105*
pci:v0000196D*
ID_VENDOR_FROM_DATABASE=Club-3D BV
+pci:v0000196E*
+ ID_VENDOR_FROM_DATABASE=PNY
+
pci:v00001971*
ID_VENDOR_FROM_DATABASE=AGEIA Technologies, Inc.
@@ -61413,7 +62121,13 @@ pci:v00001971d00001011sv00001043sd00000001*
ID_MODEL_FROM_DATABASE=Physics Processing Unit [PhysX] (PhysX P1)
pci:v00001974*
- ID_VENDOR_FROM_DATABASE=Eberspaecher Electronics
+ ID_VENDOR_FROM_DATABASE=Star Electronics GmbH & Co. KG
+
+pci:v00001974d00000009*
+ ID_MODEL_FROM_DATABASE=FlexCard PMC-II
+
+pci:v00001974d00000011*
+ ID_MODEL_FROM_DATABASE=FlexCard PMC-II Ethernet
pci:v00001976*
ID_VENDOR_FROM_DATABASE=TRENDnet
@@ -61523,6 +62237,9 @@ pci:v00001982d00001600*
pci:v00001982d000016FF*
ID_MODEL_FROM_DATABASE=OX16C954 HOST-B
+pci:v00001987*
+ ID_VENDOR_FROM_DATABASE=Phison Electronics Corporation
+
pci:v00001989*
ID_VENDOR_FROM_DATABASE=Montilio Inc.
@@ -61832,6 +62549,9 @@ pci:v000019E5d0000A239*
pci:v000019E5d0000A23A*
ID_MODEL_FROM_DATABASE=HiSilicon USB 2.0 Host Controller
+pci:v000019E5d0000A23B*
+ ID_MODEL_FROM_DATABASE=HiSilicon USB 1.1 Host Controller
+
pci:v000019E5d0000A250*
ID_MODEL_FROM_DATABASE=HiSilicon ZIP Engine
@@ -61892,6 +62612,9 @@ pci:v00001A03d00001150*
pci:v00001A03d00002000*
ID_MODEL_FROM_DATABASE=ASPEED Graphics Family
+pci:v00001A03d00002000sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=ASPEED Graphics Family (X10SRL-F)
+
pci:v00001A07*
ID_VENDOR_FROM_DATABASE=Kvaser AB
@@ -62193,7 +62916,7 @@ pci:v00001AAE*
ID_VENDOR_FROM_DATABASE=Global Velocity, Inc.
pci:v00001AB4*
- ID_VENDOR_FROM_DATABASE=FFEI Ltd
+ ID_VENDOR_FROM_DATABASE=Distributed Management Task Force, Inc. (DMTF)Distributed Management Task Force, Inc. (DMTF)
pci:v00001AB6*
ID_VENDOR_FROM_DATABASE=CalDigit, Inc.
@@ -62274,7 +62997,7 @@ pci:v00001AE9*
ID_VENDOR_FROM_DATABASE=Wilocity Ltd.
pci:v00001AE9d00000101*
- ID_MODEL_FROM_DATABASE=Wil6200 PCI Express Root Port
+ ID_MODEL_FROM_DATABASE=Wil6200 PCI Express Upstream Port
pci:v00001AE9d00000200*
ID_MODEL_FROM_DATABASE=Wil6200 PCI Express Port
@@ -62471,12 +63194,54 @@ pci:v00001B21d00001080sv00001849sd00001080*
pci:v00001B21d00001142*
ID_MODEL_FROM_DATABASE=ASM1042A USB 3.0 Host Controller
+pci:v00001B21d00001184*
+ ID_MODEL_FROM_DATABASE=ASM1184e PCIe Switch Port
+
+pci:v00001B21d00001184sv00001849sd00001184*
+ ID_MODEL_FROM_DATABASE=ASM1184e PCIe Switch Port (ASM1184e PCIe Switch)
+
pci:v00001B21d00001242*
ID_MODEL_FROM_DATABASE=ASM1142 USB 3.1 Host Controller
pci:v00001B21d00001343*
ID_MODEL_FROM_DATABASE=ASM1143 USB 3.1 Host Controller
+pci:v00001B21d00002142*
+ ID_MODEL_FROM_DATABASE=ASM2142 USB 3.1 Host Controller
+
+pci:v00001B26*
+ ID_VENDOR_FROM_DATABASE=Netcope Technologies, a.s.
+
+pci:v00001B26d0000C132*
+ ID_MODEL_FROM_DATABASE=COMBO-LXT155
+
+pci:v00001B26d0000C1C0*
+ ID_MODEL_FROM_DATABASE=NFB-100G1-e0
+
+pci:v00001B26d0000C1C1*
+ ID_MODEL_FROM_DATABASE=NFB-100G1-e1
+
+pci:v00001B26d0000C250*
+ ID_MODEL_FROM_DATABASE=NFB-200G2-master
+
+pci:v00001B26d0000C251*
+ ID_MODEL_FROM_DATABASE=NFB-200G2-slave
+
+pci:v00001B26d0000C2C0*
+ ID_MODEL_FROM_DATABASE=NFB-100G2-e0
+
+pci:v00001B26d0000C2C1*
+ ID_MODEL_FROM_DATABASE=NFB-100G2-e1
+
+pci:v00001B26d0000CB20*
+ ID_MODEL_FROM_DATABASE=COMBO-20G
+
+pci:v00001B26d0000CB40*
+ ID_MODEL_FROM_DATABASE=COMBO-40G
+
+pci:v00001B26d0000CB80*
+ ID_MODEL_FROM_DATABASE=NFB-40G2
+
pci:v00001B2C*
ID_VENDOR_FROM_DATABASE=Opal-RT Technologies Inc.
@@ -62582,6 +63347,9 @@ pci:v00001B37d00000020*
pci:v00001B37d00000023*
ID_MODEL_FROM_DATABASE=ADQ7
+pci:v00001B37d00000026*
+ ID_MODEL_FROM_DATABASE=ADQ8
+
pci:v00001B37d00002014*
ID_MODEL_FROM_DATABASE=TX320
@@ -62753,6 +63521,9 @@ pci:v00001B73d00001009*
pci:v00001B73d00001100*
ID_MODEL_FROM_DATABASE=FL1100 USB 3.0 Host Controller
+pci:v00001B73d00001100sv000016B8sd00006E31*
+ ID_MODEL_FROM_DATABASE=FL1100 USB 3.0 Host Controller (Allegro Pro USB 3.0 PCIe)
+
pci:v00001B74*
ID_VENDOR_FROM_DATABASE=OpenVox Communication Co. Ltd.
@@ -62801,6 +63572,9 @@ pci:v00001B96*
pci:v00001B9A*
ID_VENDOR_FROM_DATABASE=XAVi Technologies Corp.
+pci:v00001BAA*
+ ID_VENDOR_FROM_DATABASE=QNAP Systems, Inc.
+
pci:v00001BAD*
ID_VENDOR_FROM_DATABASE=ReFLEX CES
@@ -62852,12 +63626,24 @@ pci:v00001BB1d00000100sv00001BB1sd00000101*
pci:v00001BB1d00000100sv00001BB1sd00000103*
ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5000)
+pci:v00001BB1d00000100sv00001BB1sd00000105*
+ ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5020)
+
+pci:v00001BB1d00000100sv00001BB1sd00000106*
+ ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5020 TCG)
+
pci:v00001BB1d00000100sv00001BB1sd00000121*
ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro XM1440)
pci:v00001BB1d00000100sv00001BB1sd00000123*
ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5000)
+pci:v00001BB1d00000100sv00001BB1sd00000125*
+ ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5020)
+
+pci:v00001BB1d00000100sv00001BB1sd00000126*
+ ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5020)
+
pci:v00001BB1d00000100sv00001BB1sd000001A1*
ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro XP7102)
@@ -62990,6 +63776,12 @@ pci:v00001C09d00004264*
pci:v00001C09d00004265*
ID_MODEL_FROM_DATABASE=10G-PCIE3-8E-2S Network Adapter
+pci:v00001C09d00005000*
+ ID_MODEL_FROM_DATABASE=25G-PCIE3-8A-2S Security Intelligent Adapter
+
+pci:v00001C09d00005001*
+ ID_MODEL_FROM_DATABASE=25G-PCIE3-8B-2S Security Intelligent Adapter
+
pci:v00001C1C*
ID_VENDOR_FROM_DATABASE=Symphony
@@ -63089,12 +63881,18 @@ pci:v00001C58d00000003sv00001014sd000004F6*
pci:v00001C58d00000023*
ID_MODEL_FROM_DATABASE=Ultrastar SN200 Series NVMe SSD
+pci:v00001C58d00000023sv00001C58sd00008823*
+ ID_MODEL_FROM_DATABASE=Ultrastar SN200 Series NVMe SSD (Ultrastar Memory (ME200))
+
pci:v00001C5C*
ID_VENDOR_FROM_DATABASE=SK hynix
pci:v00001C5Cd00001283*
ID_MODEL_FROM_DATABASE=PC300 NVMe Solid State Drive
+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.
@@ -63179,6 +63977,12 @@ pci:v00001CD7*
pci:v00001CD7d00000010*
ID_MODEL_FROM_DATABASE=Pro Capture Endpoint
+pci:v00001CD7d00000014*
+ ID_MODEL_FROM_DATABASE=PRO CAPTURE AIO 4K PLUS
+
+pci:v00001CD7d00000017*
+ ID_MODEL_FROM_DATABASE=PRO CAPTURE AIO 4K
+
pci:v00001CDD*
ID_VENDOR_FROM_DATABASE=secunet Security Networks AG
@@ -63209,12 +64013,24 @@ pci:v00001CE4d00000007*
pci:v00001CE4d00000008*
ID_MODEL_FROM_DATABASE=ExaNIC V5P
+pci:v00001CE4d00000009*
+ ID_MODEL_FROM_DATABASE=ExaNIC X25
+
+pci:v00001CE4d00000100*
+ ID_MODEL_FROM_DATABASE=ExaDISK FX1
+
+pci:v00001CF0*
+ ID_VENDOR_FROM_DATABASE=Akitio
+
pci:v00001CF7*
ID_VENDOR_FROM_DATABASE=Subspace Dynamics
pci:v00001D00*
ID_VENDOR_FROM_DATABASE=Pure Storage
+pci:v00001D05*
+ ID_VENDOR_FROM_DATABASE=Tongfang Hongkong Limited
+
pci:v00001D0F*
ID_VENDOR_FROM_DATABASE=Amazon.com, Inc.
@@ -63422,6 +64238,9 @@ pci:v00001D26d00000080*
pci:v00001D26d000000C0*
ID_MODEL_FROM_DATABASE=Turbocard3 Accelerator
+pci:v00001D26d00000140*
+ ID_MODEL_FROM_DATABASE=Open Network Interface Card 40G
+
pci:v00001D26d0000E004*
ID_MODEL_FROM_DATABASE=AB01/EMB01 Development Board
@@ -63548,9 +64367,15 @@ pci:v00001D78*
pci:v00001D7C*
ID_VENDOR_FROM_DATABASE=Aerotech, Inc.
+pci:v00001D82*
+ ID_VENDOR_FROM_DATABASE=NETINT Technologies Inc.
+
pci:v00001D87*
ID_VENDOR_FROM_DATABASE=Fuzhou Rockchip Electronics Co., Ltd
+pci:v00001D87d00001808*
+ ID_MODEL_FROM_DATABASE=RK1808 Neural Network Processor Card
+
pci:v00001D8F*
ID_VENDOR_FROM_DATABASE=Enyx
@@ -63647,6 +64472,12 @@ pci:v00001D94d0000790E*
pci:v00001D95*
ID_VENDOR_FROM_DATABASE=Graphcore Ltd
+pci:v00001D95d00000001*
+ ID_MODEL_FROM_DATABASE=Colossus GC2 [C2]
+
+pci:v00001D95d00000002*
+ ID_MODEL_FROM_DATABASE=Colossus GC1 [S1]
+
pci:v00001DA1*
ID_VENDOR_FROM_DATABASE=Teko Telecom S.r.l.
@@ -63656,6 +64487,12 @@ pci:v00001DA2*
pci:v00001DBB*
ID_VENDOR_FROM_DATABASE=NGD Systems, Inc.
+pci:v00001DBF*
+ ID_VENDOR_FROM_DATABASE=Guizhou Huaxintong Semiconductor Technology Co., Ltd
+
+pci:v00001DBFd00000401*
+ ID_MODEL_FROM_DATABASE=StarDragon4800 PCI Express Root Port
+
pci:v00001DE1*
ID_VENDOR_FROM_DATABASE=Tekram Technology Co.,Ltd.
@@ -63684,28 +64521,28 @@ pci:v00001DEF*
ID_VENDOR_FROM_DATABASE=Ampere Computing, LLC
pci:v00001DEFd0000E005*
- ID_MODEL_FROM_DATABASE=Skylark PCI Express Root Port 0 [X-Gene 3]
+ ID_MODEL_FROM_DATABASE=eMAG PCI Express Root Port 0
pci:v00001DEFd0000E006*
- ID_MODEL_FROM_DATABASE=Skylark PCI Express Root Port 1 [X-Gene 3]
+ ID_MODEL_FROM_DATABASE=eMAG PCI Express Root Port 1
pci:v00001DEFd0000E007*
- ID_MODEL_FROM_DATABASE=Skylark PCI Express Root Port 2 [X-Gene 3]
+ ID_MODEL_FROM_DATABASE=eMAG PCI Express Root Port 2
pci:v00001DEFd0000E008*
- ID_MODEL_FROM_DATABASE=Skylark PCI Express Root Port 3 [X-Gene 3]
+ ID_MODEL_FROM_DATABASE=eMAG PCI Express Root Port 3
pci:v00001DEFd0000E009*
- ID_MODEL_FROM_DATABASE=Skylark PCI Express Root Port 4 [X-Gene 3]
+ ID_MODEL_FROM_DATABASE=eMAG PCI Express Root Port 4
pci:v00001DEFd0000E00A*
- ID_MODEL_FROM_DATABASE=Skylark PCI Express Root Port 5 [X-Gene 3]
+ ID_MODEL_FROM_DATABASE=eMAG PCI Express Root Port 5
pci:v00001DEFd0000E00B*
- ID_MODEL_FROM_DATABASE=Skylark PCI Express Root Port 6 [X-Gene 3]
+ ID_MODEL_FROM_DATABASE=eMAG PCI Express Root Port 6
pci:v00001DEFd0000E00C*
- ID_MODEL_FROM_DATABASE=Skylark PCI Express Root Port 7 [X-Gene 3]
+ ID_MODEL_FROM_DATABASE=eMAG PCI Express Root Port 7
pci:v00001DF7*
ID_VENDOR_FROM_DATABASE=opencpi.org
@@ -63725,6 +64562,21 @@ pci:v00001DFC*
pci:v00001DFCd00001181*
ID_MODEL_FROM_DATABASE=TDM 8 Port E1/T1/J1 Adapter
+pci:v00001E24*
+ ID_VENDOR_FROM_DATABASE=Squirrels Research Labs
+
+pci:v00001E24d00000101*
+ ID_MODEL_FROM_DATABASE=Acorn CLE-101
+
+pci:v00001E24d00000215*
+ ID_MODEL_FROM_DATABASE=Acorn CLE-215
+
+pci:v00001E24d0000021F*
+ ID_MODEL_FROM_DATABASE=Acorn CLE-215+
+
+pci:v00001E24d00001525*
+ ID_MODEL_FROM_DATABASE=Xilinx BCU-1525
+
pci:v00001FC0*
ID_VENDOR_FROM_DATABASE=Ascom (Finland) Oy
@@ -63860,6 +64712,9 @@ pci:v00001FC9d00004025sv00001FC9sd00003015*
pci:v00001FC9d00004026*
ID_MODEL_FROM_DATABASE=TN9610 10GbE SFP+ Ethernet Adapter
+pci:v00001FC9d00004026sv00004C52sd00001000*
+ ID_MODEL_FROM_DATABASE=TN9610 10GbE SFP+ Ethernet Adapter (LREC6860AF 10 Gigabit Ethernet Adapter)
+
pci:v00001FC9d00004027*
ID_MODEL_FROM_DATABASE=TN9710P 10GBase-T/NBASE-T Ethernet Adapter
@@ -63872,9 +64727,15 @@ pci:v00001FC9d00004027sv00001432sd00008104*
pci:v00001FC9d00004027sv00001546sd00004027*
ID_MODEL_FROM_DATABASE=TN9710P 10GBase-T/NBASE-T Ethernet Adapter (GE10-PCIE4XG202P 10Gbase-T/NBASE-T Ethernet Adapter)
+pci:v00001FC9d00004027sv00001BAAsd00003310*
+ ID_MODEL_FROM_DATABASE=TN9710P 10GBase-T/NBASE-T Ethernet Adapter (PCIe Expansion Card)
+
pci:v00001FC9d00004027sv00001FC9sd00003015*
ID_MODEL_FROM_DATABASE=TN9710P 10GBase-T/NBASE-T Ethernet Adapter (Ethernet Adapter)
+pci:v00001FC9d00004027sv00004C52sd00001001*
+ ID_MODEL_FROM_DATABASE=TN9710P 10GBase-T/NBASE-T Ethernet Adapter (LREC6860BT 10 Gigabit Ethernet Adapter)
+
pci:v00001FC9d00004527*
ID_MODEL_FROM_DATABASE=TN9710Q 5GBase-T/NBASE-T Ethernet Adapter
@@ -63920,6 +64781,9 @@ pci:v00002003d00008800*
pci:v00002004*
ID_VENDOR_FROM_DATABASE=Smart Link Ltd.
+pci:v00002048*
+ ID_VENDOR_FROM_DATABASE=Beijing SpaceControl Technology Co.Ltd
+
pci:v000020F4*
ID_VENDOR_FROM_DATABASE=TRENDnet
@@ -65048,6 +65912,9 @@ pci:v00004B10*
pci:v00004C48*
ID_VENDOR_FROM_DATABASE=LUNG HWA Electronics
+pci:v00004C52*
+ ID_VENDOR_FROM_DATABASE=LR-Link
+
pci:v00004C53*
ID_VENDOR_FROM_DATABASE=SBS Technologies
@@ -65646,19 +66513,19 @@ pci:v0000544D*
ID_VENDOR_FROM_DATABASE=TBS Technologies
pci:v0000544Dd00006178*
- ID_MODEL_FROM_DATABASE=DVB-S2 4 Tuner PCIe Card
+ ID_MODEL_FROM_DATABASE=DVB Tuner PCIe Card
pci:v0000544Dd00006178sv0000544Dsd00006904*
- ID_MODEL_FROM_DATABASE=DVB-S2 4 Tuner PCIe Card (TBS6904 DVB-S2 Quad Tuner PCIe Card)
+ ID_MODEL_FROM_DATABASE=DVB Tuner PCIe Card (TBS6904 DVB-S2 Quad Tuner PCIe Card)
pci:v0000544Dd00006178sv0000544Dsd00006905*
- ID_MODEL_FROM_DATABASE=DVB-S2 4 Tuner PCIe Card (TBS6905 DVB-S2 Quad Tuner PCIe Card)
+ ID_MODEL_FROM_DATABASE=DVB Tuner PCIe Card (TBS6905 DVB-S2 Quad Tuner PCIe Card)
pci:v0000544Dd00006178sv00006205sd00000001*
- ID_MODEL_FROM_DATABASE=DVB-S2 4 Tuner PCIe Card (TBS6205 DVB-T2/T/C Quad TV Tuner PCIe Card)
+ ID_MODEL_FROM_DATABASE=DVB Tuner PCIe Card (TBS6205 DVB-T2/T/C Quad TV Tuner PCIe Card)
pci:v0000544Dd00006178sv00006209sd00000001*
- ID_MODEL_FROM_DATABASE=DVB-S2 4 Tuner PCIe Card (TBS6209 DVB-T2/C2/T/C/ISDB-T OctaTV Tuner)
+ ID_MODEL_FROM_DATABASE=DVB Tuner PCIe Card (TBS6209 DVB-T2/C2/T/C/ISDB-T OctaTV Tuner)
pci:v00005452*
ID_VENDOR_FROM_DATABASE=SCANLAB AG
@@ -65667,7 +66534,7 @@ pci:v00005452d00003443*
ID_MODEL_FROM_DATABASE=RTC4
pci:v00005455*
- ID_VENDOR_FROM_DATABASE=Technische University Berlin
+ ID_VENDOR_FROM_DATABASE=Technische Universitaet Berlin
pci:v00005455d00004458*
ID_MODEL_FROM_DATABASE=S5933
@@ -65738,6 +66605,9 @@ pci:v00005853d0000C110*
pci:v00005853d0000C147*
ID_MODEL_FROM_DATABASE=Virtualized Graphics Device
+pci:v00005853d0000C200*
+ ID_MODEL_FROM_DATABASE=XCP-ng Project PCI Device for Windows Update
+
pci:v00005854*
ID_VENDOR_FROM_DATABASE=GoTView
@@ -66312,7 +67182,7 @@ pci:v00008086d00000158sv00001043sd0000844D*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/Ivy Bridge DRAM Controller (P8 series motherboard)
pci:v00008086d00000158sv00008086sd00002010*
- ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/Ivy Bridge DRAM Controller (Server Board S1200BTS)
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/Ivy Bridge DRAM Controller (Server Board S1200BT Family)
pci:v00008086d00000159*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port
@@ -66441,7 +67311,7 @@ pci:v00008086d00000402*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller
pci:v00008086d00000406*
- ID_MODEL_FROM_DATABASE=4th Gen Core Processor Integrated Graphics Controller
+ ID_MODEL_FROM_DATABASE=Haswell Integrated Graphics Controller
pci:v00008086d0000040A*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3 Processor Integrated Graphics Controller
@@ -66449,6 +67319,9 @@ pci:v00008086d0000040A*
pci:v00008086d00000412*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller
+pci:v00008086d00000412sv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller (ThinkCentre M83)
+
pci:v00008086d00000416*
ID_MODEL_FROM_DATABASE=4th Gen Core Processor Integrated Graphics Controller
@@ -67407,37 +68280,124 @@ pci:v00008086d00000A53*
ID_MODEL_FROM_DATABASE=DC P3520 SSD
pci:v00008086d00000A54*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4500/P4600
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller]
pci:v00008086d00000A54sv00001028sd00001FE1*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4500/P4600 (Express Flash NVMe 1TB 2.5" U.2 (P4500))
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 1TB 2.5" U.2 (P4500))
pci:v00008086d00000A54sv00001028sd00001FE2*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4500/P4600 (Express Flash NVMe 2TB 2.5" U.2 (P4500))
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 2TB 2.5" U.2 (P4500))
pci:v00008086d00000A54sv00001028sd00001FE3*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4500/P4600 (Express Flash NVMe 4TB 2.5" U.2 (P4500))
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 4TB 2.5" U.2 (P4500))
pci:v00008086d00000A54sv00001028sd00001FE4*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4500/P4600 (Express Flash NVMe 4TB HHHL AIC (P4500))
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 4TB HHHL AIC (P4500))
+
+pci:v00008086d00000A54sv00001028sd00001FEE*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 1.6TB 2.5" U.2 (P4610))
+
+pci:v00008086d00000A54sv00001028sd00001FEF*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 3.2TB 2.5" U.2 (P4610))
+
+pci:v00008086d00000A54sv00001028sd00001FF0*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 6.4TB 2.5" U.2 (P4610))
+
+pci:v00008086d00000A54sv00001028sd00001FFF*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 8.0TB 2.5" U.2 (P4510))
+
+pci:v00008086d00000A54sv00001028sd00002003*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 1.0 TB 2.5" U.2 (P4510))
+
+pci:v00008086d00000A54sv00001028sd00002004*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 2.0TB 2.5" U.2 (P4510))
+
+pci:v00008086d00000A54sv00001028sd00002005*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 4.0TB 2.5" U.2 (P4510))
+
+pci:v00008086d00000A54sv0000108Esd00004870*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe PCIe 3.0 SSD 6.4TB AIC (P4608))
+
+pci:v00008086d00000A54sv0000108Esd00004871*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe PCIe 3.0 SSD 6.4TB 2.5-inch (P4600))
+
+pci:v00008086d00000A54sv0000108Esd0000487A*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe PCIe 3.0 SSD v2 6.4TB 2.5-inch (P4610))
+
+pci:v00008086d00000A54sv00001590sd0000025D*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] 1.0TB 2.5" U.2 (P4500))
+
+pci:v00008086d00000A54sv00001590sd0000025E*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] 2.0TB 2.5" U.2 (P4500))
+
+pci:v00008086d00000A54sv00001590sd0000025F*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] 4.0TB 2.5" U.2 (P4500))
+
+pci:v00008086d00000A54sv00001590sd00000262*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] 1.6TB 2.5" U.2 (P4600))
+
+pci:v00008086d00000A54sv00001590sd00000264*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] 3.2TB 2.5" U.2 (P4600))
+
+pci:v00008086d00000A54sv00001590sd00000265*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] 6.4TB 2.5" U.2 (P4600))
+
+pci:v00008086d00000A54sv00001590sd0000026C*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] 4.0TB AIC (P4500))
+
+pci:v00008086d00000A54sv00001D49sd00004802*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Thinksystem U.2 P4510 NVMe SSD)
+
+pci:v00008086d00000A54sv00001D49sd00004812*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Thinksystem U.2 P4610 NVMe SSD)
+
+pci:v00008086d00000A54sv00008086sd00004308*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (Intel SSD D5-P4320 and D5-P4326)
+
+pci:v00008086d00000A54sv00008086sd00004702*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] SE 2.5" U.2 (P4500))
+
+pci:v00008086d00000A54sv00008086sd00004704*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] SE AIC (P4500))
+
+pci:v00008086d00000A54sv00008086sd00004712*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] ME 2.5" U.2 (P4600))
+
+pci:v00008086d00000A54sv00008086sd00004714*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] ME AIC (P4600))
+
+pci:v00008086d00000A54sv00008086sd00004802*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] SE 2.5" U.2 (P4510))
+
+pci:v00008086d00000A54sv00008086sd00004804*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] SE AIC (P4510))
+
+pci:v00008086d00000A54sv00008086sd00004805*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] SE M.2 (P4511))
+
+pci:v00008086d00000A54sv00008086sd00004812*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] ME 2.5" U.2 (P4610))
+
+pci:v00008086d00000A54sv00008086sd00004814*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe Datacenter SSD [3DNAND] ME AIC (P4610))
pci:v00008086d00000A55*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4600
+ ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Beta Rock Controller]
pci:v00008086d00000A55sv00001028sd00001FE5*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4600 (Express Flash NVMe 1.6TB 2.5" U.2 (P4600))
+ ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 1.6TB 2.5" U.2 (P4600))
pci:v00008086d00000A55sv00001028sd00001FE6*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4600 (Express Flash NVMe 2TB 2.5" U.2 (P4600))
+ ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 2TB 2.5" U.2 (P4600))
pci:v00008086d00000A55sv00001028sd00001FE7*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4600 (Express Flash NVMe 3.2TB 2.5" U.2 (P4600))
+ ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 3.2TB 2.5" U.2 (P4600))
pci:v00008086d00000A55sv00001028sd00001FE8*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4600 (Express Flash NVMe 2.0TB HHHL AIC (P4600))
+ ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 2.0TB HHHL AIC (P4600))
pci:v00008086d00000A55sv00001028sd00001FE9*
- ID_MODEL_FROM_DATABASE=Express Flash NVMe P4600 (Express Flash NVMe 4.0TB HHHL AIC (P4600))
+ ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Beta Rock Controller] (Express Flash NVMe 4.0TB HHHL AIC (P4600))
pci:v00008086d00000BE0*
ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller
@@ -67520,6 +68480,9 @@ pci:v00008086d00000BF7*
pci:v00008086d00000C00*
ID_MODEL_FROM_DATABASE=4th Gen Core Processor DRAM Controller
+pci:v00008086d00000C00sv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=4th Gen Core Processor DRAM Controller (ThinkCentre M83)
+
pci:v00008086d00000C01*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor PCI Express x16 Controller
@@ -67547,6 +68510,9 @@ pci:v00008086d00000C0C*
pci:v00008086d00000C0Csv000017AAsd0000220E*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller (ThinkPad T440p)
+pci:v00008086d00000C0Csv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller (ThinkCentre M83)
+
pci:v00008086d00000C46*
ID_MODEL_FROM_DATABASE=Atom Processor S1200 PCI Express Root Port 1
@@ -67685,12 +68651,21 @@ pci:v00008086d00000D36*
pci:v00008086d00000E00*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DMI2
+pci:v00008086d00000E00sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DMI2 (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E00sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DMI2 (X9SRL-F)
+
pci:v00008086d00000E01*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port in DMI2 Mode
pci:v00008086d00000E02*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 1a
+pci:v00008086d00000E02sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 1a (Xeon E5 v2 on PowerEdge R320 server)
+
pci:v00008086d00000E03*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 1b
@@ -67709,6 +68684,9 @@ pci:v00008086d00000E07*
pci:v00008086d00000E08*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3a
+pci:v00008086d00000E08sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3a (Xeon E5 v2 on PowerEdge R320 server)
+
pci:v00008086d00000E09*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3b
@@ -67745,39 +68723,102 @@ pci:v00008086d00000E1F*
pci:v00008086d00000E20*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 0
+pci:v00008086d00000E20sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 0 (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E20sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 0 (X9SRL-F)
+
pci:v00008086d00000E21*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 1
+pci:v00008086d00000E21sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 1 (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E21sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 1 (X9SRL-F)
+
pci:v00008086d00000E22*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 2
+pci:v00008086d00000E22sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 2 (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E22sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 2 (X9SRL-F)
+
pci:v00008086d00000E23*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 3
+pci:v00008086d00000E23sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 3 (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E23sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 3 (X9SRL-F)
+
pci:v00008086d00000E24*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 4
+pci:v00008086d00000E24sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 4 (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E24sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 4 (X9SRL-F)
+
pci:v00008086d00000E25*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 5
+pci:v00008086d00000E25sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 5 (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E25sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 5 (X9SRL-F)
+
pci:v00008086d00000E26*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 6
+pci:v00008086d00000E26sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 6 (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E26sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 6 (X9SRL-F)
+
pci:v00008086d00000E27*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 7
+pci:v00008086d00000E27sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 7 (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E27sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 7 (X9SRL-F)
+
pci:v00008086d00000E28*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 VTd/Memory Map/Misc
+pci:v00008086d00000E28sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 VTd/Memory Map/Misc (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E28sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 VTd/Memory Map/Misc (X9SRL-F)
+
pci:v00008086d00000E29*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Memory Hotplug
pci:v00008086d00000E2A*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IIO RAS
+pci:v00008086d00000E2Asv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IIO RAS (Xeon E5 v2 on PowerEdge R320 server)
+
+pci:v00008086d00000E2Asv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IIO RAS (X9SRL-F)
+
pci:v00008086d00000E2C*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IOAPIC
+pci:v00008086d00000E2Csv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IOAPIC (X9SRL-F)
+
pci:v00008086d00000E2E*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 CBDMA
@@ -67787,6 +68828,9 @@ pci:v00008086d00000E2F*
pci:v00008086d00000E30*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 0
+pci:v00008086d00000E30sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 0 (Xeon E5 v2 on PowerEdge R320 server)
+
pci:v00008086d00000E32*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 0
@@ -67796,9 +68840,15 @@ pci:v00008086d00000E33*
pci:v00008086d00000E34*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 R2PCIe
+pci:v00008086d00000E34sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 R2PCIe (Xeon E5 v2 on PowerEdge R320 server)
+
pci:v00008086d00000E36*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Performance Ring Monitoring
+pci:v00008086d00000E36sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Performance Ring Monitoring (Xeon E5 v2 on PowerEdge R320 server)
+
pci:v00008086d00000E37*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Performance Ring Monitoring
@@ -67877,6 +68927,9 @@ pci:v00008086d00000E80*
pci:v00008086d00000E81*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Registers
+pci:v00008086d00000E81sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Registers (Xeon E5 v2 on PowerEdge R320 server)
+
pci:v00008086d00000E83*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Reut 0
@@ -67904,6 +68957,9 @@ pci:v00008086d00000E95*
pci:v00008086d00000EA0*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 0
+pci:v00008086d00000EA0sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 0 (Xeon E5 v2 on PowerEdge R320 server)
+
pci:v00008086d00000EA8*
ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Target Address/Thermal Registers
@@ -68735,6 +69791,12 @@ pci:v00008086d0000104C*
pci:v00008086d0000104D*
ID_MODEL_FROM_DATABASE=82566MC Gigabit Network Connection
+pci:v00008086d0000104E*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10 Gigabit SFP+
+
+pci:v00008086d0000104F*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10 Gigabit backplane
+
pci:v00008086d00001050*
ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller
@@ -69377,6 +70439,9 @@ pci:v00008086d000010D3sv00001093sd000076E9*
pci:v00008086d000010D3sv000010A9sd00008029*
ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (Prism XL Single Port Gigabit Ethernet)
+pci:v00008086d000010D3sv000015D9sd00000605*
+ ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (X8SIL)
+
pci:v00008086d000010D3sv000015D9sd0000060A*
ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (X7SPA-H/X7SPA-HF Motherboard)
@@ -69386,6 +70451,9 @@ pci:v00008086d000010D3sv000015D9sd0000060D*
pci:v00008086d000010D3sv00008086sd00000001*
ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (Gigabit CT2 Desktop Adapter)
+pci:v00008086d000010D3sv00008086sd00003578*
+ ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (Server Board S1200BTLR)
+
pci:v00008086d000010D3sv00008086sd0000357A*
ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (Server Board S1200BTS)
@@ -69542,6 +70610,9 @@ pci:v00008086d000010F4sv00008086sd0000A06F*
pci:v00008086d000010F5*
ID_MODEL_FROM_DATABASE=82567LM Gigabit Network Connection
+pci:v00008086d000010F5sv000017AAsd000020EE*
+ ID_MODEL_FROM_DATABASE=82567LM Gigabit Network Connection (ThinkPad T400)
+
pci:v00008086d000010F6*
ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection
@@ -70388,6 +71459,9 @@ pci:v00008086d00001502sv00001028sd000004A3*
pci:v00008086d00001502sv000017AAsd000021CE*
ID_MODEL_FROM_DATABASE=82579LM Gigabit Network Connection (Lewisville) (ThinkPad T520)
+pci:v00008086d00001502sv00008086sd00003578*
+ ID_MODEL_FROM_DATABASE=82579LM Gigabit Network Connection (Lewisville) (Server Board S1200BTLR)
+
pci:v00008086d00001502sv00008086sd0000357A*
ID_MODEL_FROM_DATABASE=82579LM Gigabit Network Connection (Lewisville) (Server Board S1200BTS)
@@ -70571,6 +71645,9 @@ pci:v00008086d00001521sv00001093sd0000775B*
pci:v00008086d00001521sv000010A9sd0000802A*
ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (UV2-BaseIO dual-port GbE)
+pci:v00008086d00001521sv00001137sd0000023E*
+ ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (1GigE I350 LOM)
+
pci:v00008086d00001521sv000015D9sd00000652*
ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Dual Port i350 GbE MicroLP [AOC-CGP-i2])
@@ -70820,6 +71897,9 @@ pci:v00008086d00001537sv00001059sd00000140*
pci:v00008086d00001537sv00001059sd00000150*
ID_MODEL_FROM_DATABASE=I210 Gigabit Backplane Connection (RD-01068 1GbE interface)
+pci:v00008086d00001537sv00001059sd00000170*
+ ID_MODEL_FROM_DATABASE=I210 Gigabit Backplane Connection (RD-01213 10GbE interface)
+
pci:v00008086d00001538*
ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection
@@ -70835,6 +71915,9 @@ pci:v00008086d0000153Asv0000103Csd00001909*
pci:v00008086d0000153Asv000017AAsd0000220E*
ID_MODEL_FROM_DATABASE=Ethernet Connection I217-LM (ThinkPad T440p)
+pci:v00008086d0000153Asv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection I217-LM (ThinkCentre M83)
+
pci:v00008086d0000153B*
ID_MODEL_FROM_DATABASE=Ethernet Connection I217-V
@@ -71000,6 +72083,9 @@ pci:v00008086d0000156D*
pci:v00008086d0000156F*
ID_MODEL_FROM_DATABASE=Ethernet Connection I219-LM
+pci:v00008086d0000156Fsv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection I219-LM (Latitude E7470)
+
pci:v00008086d00001570*
ID_MODEL_FROM_DATABASE=Ethernet Connection I219-V
@@ -71153,6 +72239,9 @@ pci:v00008086d00001581sv00001028sd00001F9E*
pci:v00008086d00001581sv00001059sd00000150*
ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE backplane (RD-01068 10GbE-KR interface)
+pci:v00008086d00001581sv00001059sd00000170*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE backplane (RD-01213 10GbE interface)
+
pci:v00008086d00001581sv00001590sd00000000*
ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE backplane (Ethernet 2-port 563i Adapter)
@@ -71279,12 +72368,24 @@ pci:v00008086d00001589sv00008086sd00001003*
pci:v00008086d0000158A*
ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE backplane
+pci:v00008086d0000158Asv00001590sd00000000*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE backplane (10/25Gb Ethernet Adapter)
+
+pci:v00008086d0000158Asv00001590sd00000286*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE backplane (Synergy 4610C 10/25Gb Ethernet Adapter)
+
pci:v00008086d0000158Asv00008086sd0000000A*
ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE backplane (Ethernet 25G 2P XXV710 Mezz)
pci:v00008086d0000158B*
ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28
+pci:v00008086d0000158Bsv00001137sd00000000*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet Network Adapter XXV710)
+
+pci:v00008086d0000158Bsv00001137sd00000225*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet Network Adapter XXV710)
+
pci:v00008086d0000158Bsv00008086sd00000000*
ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet Network Adapter XXV710)
@@ -71318,6 +72419,15 @@ pci:v00008086d0000158Bsv00008086sd00000009*
pci:v00008086d0000158Bsv00008086sd00004001*
ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet Network Adapter XXV710-2)
+pci:v00008086d00001591*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller E810-C for backplane
+
+pci:v00008086d00001592*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller E810-C for QSFP
+
+pci:v00008086d00001593*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller E810-C for SFP
+
pci:v00008086d000015A0*
ID_MODEL_FROM_DATABASE=Ethernet Connection (2) I218-LM
@@ -71348,12 +72458,15 @@ pci:v00008086d000015AA*
pci:v00008086d000015AAsv00001059sd00000120*
ID_MODEL_FROM_DATABASE=Ethernet Connection X552 10 GbE Backplane (T4008 10GbE interface)
-pci:v00008086d000015AAsv00001059sd00000150*
- ID_MODEL_FROM_DATABASE=Ethernet Connection X552 10 GbE Backplane (RD-01068 10GbE interface)
-
pci:v00008086d000015AB*
ID_MODEL_FROM_DATABASE=Ethernet Connection X552 10 GbE Backplane
+pci:v00008086d000015ABsv00001059sd00000150*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection X552 10 GbE Backplane (RD-01068 10GbE interface)
+
+pci:v00008086d000015ABsv00001059sd00000170*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection X552 10 GbE Backplane (RD-01213 10GbE interface)
+
pci:v00008086d000015AC*
ID_MODEL_FROM_DATABASE=Ethernet Connection X552 10 GbE SFP+
@@ -71483,6 +72596,9 @@ pci:v00008086d000015D8sv000017AAsd00002247*
pci:v00008086d000015D8sv000017AAsd0000224F*
ID_MODEL_FROM_DATABASE=Ethernet Connection (4) I219-V (ThinkPad X1 Carbon 5th Gen)
+pci:v00008086d000015D8sv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (4) I219-V (ThinkPad T480)
+
pci:v00008086d000015D9*
ID_MODEL_FROM_DATABASE=JHL6340 Thunderbolt 3 NHI (C step) [Alpine Ridge 2C 2016]
@@ -71534,6 +72650,9 @@ pci:v00008086d000015EF*
pci:v00008086d000015F0*
ID_MODEL_FROM_DATABASE=JHL7540 Thunderbolt 3 USB Controller [Titan Ridge DD 2018]
+pci:v00008086d000015FF*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T
+
pci:v00008086d00001600*
ID_MODEL_FROM_DATABASE=Broadwell-U Host Bridge -OPI
@@ -71663,9 +72782,21 @@ pci:v00008086d00001902*
pci:v00008086d00001903*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem
+pci:v00008086d00001903sv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (Latitude E7470)
+
+pci:v00008086d00001903sv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (XPS 15 9550)
+
+pci:v00008086d00001903sv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (ThinkPad T480)
+
pci:v00008086d00001904*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
+pci:v00008086d00001904sv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers (Latitude E7470)
+
pci:v00008086d00001904sv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers (Latitude 3570)
@@ -71696,6 +72827,9 @@ pci:v00008086d0000190F*
pci:v00008086d00001910*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
+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:v00008086d00001911*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th Gen Core Processor Gaussian Mixture Model
@@ -71705,12 +72839,18 @@ pci:v00008086d00001911sv000017AAsd00002247*
pci:v00008086d00001911sv000017AAsd0000224F*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th Gen Core Processor Gaussian Mixture Model (ThinkPad X1 Carbon 5th Gen)
+pci:v00008086d00001911sv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th Gen Core Processor Gaussian Mixture Model (ThinkPad T480)
+
pci:v00008086d00001912*
ID_MODEL_FROM_DATABASE=HD Graphics 530
pci:v00008086d00001916*
ID_MODEL_FROM_DATABASE=Skylake GT2 [HD Graphics 520]
+pci:v00008086d00001916sv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Skylake GT2 [HD Graphics 520] (Latitude E7470)
+
pci:v00008086d00001916sv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Skylake GT2 [HD Graphics 520] (Latitude 3570)
@@ -71723,6 +72863,9 @@ pci:v00008086d00001919*
pci:v00008086d0000191B*
ID_MODEL_FROM_DATABASE=HD Graphics 530
+pci:v00008086d0000191Bsv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=HD Graphics 530 (XPS 15 9550)
+
pci:v00008086d0000191D*
ID_MODEL_FROM_DATABASE=HD Graphics P530
@@ -71840,6 +72983,9 @@ pci:v00008086d00001962*
pci:v00008086d00001962sv0000105Asd00000000*
ID_MODEL_FROM_DATABASE=80960RM (i960RM) Microprocessor (SuperTrak SX6000 I2O CPU)
+pci:v00008086d00001964*
+ ID_MODEL_FROM_DATABASE=80960RN (i960RN) Microprocessor
+
pci:v00008086d000019AC*
ID_MODEL_FROM_DATABASE=Atom Processor C3000 Series SMBus Contoller - Host
@@ -71985,7 +73131,7 @@ pci:v00008086d00001C02sv00001043sd0000844D*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 6 port Desktop SATA AHCI Controller (P8 series motherboard)
pci:v00008086d00001C02sv00008086sd00007270*
- ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 6 port Desktop SATA AHCI Controller (Server Board S1200BTS)
+ ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 6 port Desktop SATA AHCI Controller (Server Board S1200BT Family)
pci:v00008086d00001C03*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 6 port Mobile SATA AHCI Controller
@@ -72156,7 +73302,7 @@ pci:v00008086d00001C22sv000017AAsd000021CF*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SMBus Controller (ThinkPad T520)
pci:v00008086d00001C22sv00008086sd00007270*
- ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SMBus Controller (Server Board S1200BTS / Apple MacBook Pro 8,1/8,2)
+ ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SMBus Controller (Server Board S1200BT Family / Apple MacBook Pro 8,1/8,2)
pci:v00008086d00001C24*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family Thermal Management Controller
@@ -72186,7 +73332,7 @@ pci:v00008086d00001C26sv000017AAsd000021CF*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 (ThinkPad T520)
pci:v00008086d00001C26sv00008086sd00007270*
- ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 (Server Board S1200BTS / Apple MacBook Pro 8,1/8,2)
+ ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 (Server Board S1200BT Family / Apple MacBook Pro 8,1/8,2)
pci:v00008086d00001C27*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Universal Host Controller #1
@@ -72222,7 +73368,7 @@ pci:v00008086d00001C2Dsv000017AAsd000021CF*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 (ThinkPad T520)
pci:v00008086d00001C2Dsv00008086sd00007270*
- ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 (Server Board S1200BTS / Apple MacBook Pro 8,1/8,2)
+ ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 (Server Board S1200BT Family / Apple MacBook Pro 8,1/8,2)
pci:v00008086d00001C33*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LAN Controller
@@ -72276,109 +73422,109 @@ pci:v00008086d00001C43*
ID_MODEL_FROM_DATABASE=Mobile 6 Series Chipset Family LPC Controller
pci:v00008086d00001C44*
- ID_MODEL_FROM_DATABASE=Z68 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=Z68 Express Chipset LPC Controller
pci:v00008086d00001C45*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller
pci:v00008086d00001C46*
- ID_MODEL_FROM_DATABASE=P67 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=P67 Express Chipset LPC Controller
pci:v00008086d00001C46sv00001043sd0000844D*
- ID_MODEL_FROM_DATABASE=P67 Express Chipset Family LPC Controller (P8P67 Deluxe Motherboard)
+ ID_MODEL_FROM_DATABASE=P67 Express Chipset LPC Controller (P8P67 Deluxe Motherboard)
pci:v00008086d00001C47*
- ID_MODEL_FROM_DATABASE=UM67 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=UM67 Express Chipset LPC Controller
pci:v00008086d00001C48*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller
pci:v00008086d00001C49*
- ID_MODEL_FROM_DATABASE=HM65 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=HM65 Express Chipset LPC Controller
pci:v00008086d00001C49sv00008086sd00007270*
- ID_MODEL_FROM_DATABASE=HM65 Express Chipset Family LPC Controller (Apple MacBookPro8,2 [Core i7, 15", 2011])
+ ID_MODEL_FROM_DATABASE=HM65 Express Chipset LPC Controller (Apple MacBookPro8,2 [Core i7, 15", 2011])
pci:v00008086d00001C4A*
- ID_MODEL_FROM_DATABASE=H67 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=H67 Express Chipset LPC Controller
pci:v00008086d00001C4Asv00001028sd000004AA*
- ID_MODEL_FROM_DATABASE=H67 Express Chipset Family LPC Controller (XPS 8300)
+ ID_MODEL_FROM_DATABASE=H67 Express Chipset LPC Controller (XPS 8300)
pci:v00008086d00001C4Asv00001043sd0000844D*
- ID_MODEL_FROM_DATABASE=H67 Express Chipset Family LPC Controller (P8H67 Series Motherboard)
+ ID_MODEL_FROM_DATABASE=H67 Express Chipset LPC Controller (P8H67 Series Motherboard)
pci:v00008086d00001C4B*
- ID_MODEL_FROM_DATABASE=HM67 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=HM67 Express Chipset LPC Controller
pci:v00008086d00001C4Bsv00001028sd000004B2*
- ID_MODEL_FROM_DATABASE=HM67 Express Chipset Family LPC Controller (Vostro 3350)
+ ID_MODEL_FROM_DATABASE=HM67 Express Chipset LPC Controller (Vostro 3350)
pci:v00008086d00001C4Bsv00001028sd000004DA*
- ID_MODEL_FROM_DATABASE=HM67 Express Chipset Family LPC Controller (Vostro 3750)
+ ID_MODEL_FROM_DATABASE=HM67 Express Chipset LPC Controller (Vostro 3750)
pci:v00008086d00001C4C*
- ID_MODEL_FROM_DATABASE=Q65 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=Q65 Express Chipset LPC Controller
pci:v00008086d00001C4D*
- ID_MODEL_FROM_DATABASE=QS67 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=QS67 Express Chipset LPC Controller
pci:v00008086d00001C4E*
- ID_MODEL_FROM_DATABASE=Q67 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=Q67 Express Chipset LPC Controller
pci:v00008086d00001C4F*
- ID_MODEL_FROM_DATABASE=QM67 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=QM67 Express Chipset LPC Controller
pci:v00008086d00001C4Fsv00001028sd000004A3*
- ID_MODEL_FROM_DATABASE=QM67 Express Chipset Family LPC Controller (Precision M4600)
+ ID_MODEL_FROM_DATABASE=QM67 Express Chipset LPC Controller (Precision M4600)
pci:v00008086d00001C4Fsv000017AAsd000021CF*
- ID_MODEL_FROM_DATABASE=QM67 Express Chipset Family LPC Controller (ThinkPad T520)
+ ID_MODEL_FROM_DATABASE=QM67 Express Chipset LPC Controller (ThinkPad T520)
pci:v00008086d00001C50*
- ID_MODEL_FROM_DATABASE=B65 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=B65 Express Chipset LPC Controller
pci:v00008086d00001C51*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller
pci:v00008086d00001C52*
- ID_MODEL_FROM_DATABASE=C202 Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=C202 Chipset LPC Controller
pci:v00008086d00001C52sv00008086sd00007270*
- ID_MODEL_FROM_DATABASE=C202 Chipset Family LPC Controller (Server Board S1200BTS)
+ ID_MODEL_FROM_DATABASE=C202 Chipset LPC Controller (Server Board S1200BTS)
pci:v00008086d00001C53*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller
pci:v00008086d00001C54*
- ID_MODEL_FROM_DATABASE=C204 Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=C204 Chipset LPC Controller
pci:v00008086d00001C55*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller
pci:v00008086d00001C56*
- ID_MODEL_FROM_DATABASE=C206 Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=C206 Chipset LPC Controller
pci:v00008086d00001C56sv00001043sd0000844D*
- ID_MODEL_FROM_DATABASE=C206 Chipset Family LPC Controller (P8B WS Motherboard)
+ ID_MODEL_FROM_DATABASE=C206 Chipset LPC Controller (P8B WS Motherboard)
pci:v00008086d00001C57*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller
pci:v00008086d00001C58*
- ID_MODEL_FROM_DATABASE=Upgraded B65 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=Upgraded B65 Express Chipset LPC Controller
pci:v00008086d00001C59*
- ID_MODEL_FROM_DATABASE=Upgraded HM67 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=Upgraded HM67 Express Chipset LPC Controller
pci:v00008086d00001C5A*
- ID_MODEL_FROM_DATABASE=Upgraded Q67 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=Upgraded Q67 Express Chipset LPC Controller
pci:v00008086d00001C5B*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller
pci:v00008086d00001C5C*
- ID_MODEL_FROM_DATABASE=H61 Express Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=H61 Express Chipset LPC Controller
pci:v00008086d00001C5D*
ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller
@@ -72395,6 +73541,9 @@ pci:v00008086d00001D00*
pci:v00008086d00001D02*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset 6-Port SATA AHCI Controller
+pci:v00008086d00001D02sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset 6-Port SATA AHCI Controller (C602J on PowerEdge R320 server)
+
pci:v00008086d00001D04*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset SATA RAID Controller
@@ -72407,6 +73556,9 @@ pci:v00008086d00001D08*
pci:v00008086d00001D10*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 1
+pci:v00008086d00001D10sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 1 (C602J on PowerEdge R320 server)
+
pci:v00008086d00001D11*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 1
@@ -72431,6 +73583,9 @@ pci:v00008086d00001D17*
pci:v00008086d00001D18*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 5
+pci:v00008086d00001D18sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 5 (C602J on PowerEdge R320 server)
+
pci:v00008086d00001D19*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 5
@@ -72449,6 +73604,9 @@ pci:v00008086d00001D1D*
pci:v00008086d00001D1E*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 8
+pci:v00008086d00001D1Esv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 8 (C602J on PowerEdge R320 server)
+
pci:v00008086d00001D1F*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 8
@@ -72458,18 +73616,36 @@ pci:v00008086d00001D20*
pci:v00008086d00001D22*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset SMBus Host Controller
+pci:v00008086d00001D22sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset SMBus Host Controller (X9SRL-F)
+
pci:v00008086d00001D24*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset Thermal Management Controller
+pci:v00008086d00001D24sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset Thermal Management Controller (X9SRL-F)
+
pci:v00008086d00001D25*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset DMI to PCI Bridge
pci:v00008086d00001D26*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset USB2 Enhanced Host Controller #1
+pci:v00008086d00001D26sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset USB2 Enhanced Host Controller #1 (C602J on PowerEdge R320 server)
+
+pci:v00008086d00001D26sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset USB2 Enhanced Host Controller #1 (X9SRL-F)
+
pci:v00008086d00001D2D*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset USB2 Enhanced Host Controller #2
+pci:v00008086d00001D2Dsv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset USB2 Enhanced Host Controller #2 (C602J on PowerEdge R320 server)
+
+pci:v00008086d00001D2Dsv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset USB2 Enhanced Host Controller #2 (X9SRL-F)
+
pci:v00008086d00001D33*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset LAN Controller
@@ -72479,9 +73655,21 @@ pci:v00008086d00001D35*
pci:v00008086d00001D3A*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset MEI Controller #1
+pci:v00008086d00001D3Asv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset MEI Controller #1 (C602J on PowerEdge R320 server)
+
+pci:v00008086d00001D3Asv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset MEI Controller #1 (X9SRL-F)
+
pci:v00008086d00001D3B*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset MEI Controller #2
+pci:v00008086d00001D3Bsv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset MEI Controller #2 (C602J on PowerEdge R320 server)
+
+pci:v00008086d00001D3Bsv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset MEI Controller #2 (X9SRL-F)
+
pci:v00008086d00001D3C*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset IDE-r Controller
@@ -72491,6 +73679,9 @@ pci:v00008086d00001D3D*
pci:v00008086d00001D3E*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Virtual Root Port
+pci:v00008086d00001D3Esv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Virtual Root Port (C602J on PowerEdge R320 server)
+
pci:v00008086d00001D3F*
ID_MODEL_FROM_DATABASE=C608/C606/X79 series chipset PCI Express Virtual Switch Port
@@ -72500,6 +73691,12 @@ pci:v00008086d00001D40*
pci:v00008086d00001D41*
ID_MODEL_FROM_DATABASE=C600/X79 series chipset LPC Controller
+pci:v00008086d00001D41sv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset LPC Controller (C602J on PowerEdge R320 server)
+
+pci:v00008086d00001D41sv000015D9sd0000066B*
+ ID_MODEL_FROM_DATABASE=C600/X79 series chipset LPC Controller (X9SRL-F)
+
pci:v00008086d00001D50*
ID_MODEL_FROM_DATABASE=C608 chipset Dual 4-Port SATA/SAS Storage Control Unit
@@ -72836,6 +74033,9 @@ pci:v00008086d00001E31sv00001043sd00001517*
pci:v00008086d00001E31sv00001043sd000084CA*
ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (P8 series motherboard)
+pci:v00008086d00001E31sv000017AAsd000021F3*
+ ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (ThinkPad T430)
+
pci:v00008086d00001E31sv00001849sd00001E31*
ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (Motherboard)
@@ -72978,13 +74178,13 @@ pci:v00008086d00001E5Dsv0000144Dsd0000C652*
ID_MODEL_FROM_DATABASE=HM75 Express Chipset LPC Controller (NP300E5C series laptop)
pci:v00008086d00001E5E*
- ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=HM70 Express Chipset LPC Controller
pci:v00008086d00001E5Esv00001043sd0000108D*
- ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller (VivoBook X202EV)
+ ID_MODEL_FROM_DATABASE=HM70 Express Chipset LPC Controller (VivoBook X202EV)
pci:v00008086d00001E5F*
- ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller
+ ID_MODEL_FROM_DATABASE=NM70 Express Chipset LPC Controller
pci:v00008086d00001F00*
ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router
@@ -73133,6 +74333,9 @@ pci:v00008086d00001F3B*
pci:v00008086d00001F3C*
ID_MODEL_FROM_DATABASE=Atom processor C2000 PCU SMBus
+pci:v00008086d00001F3D*
+ ID_MODEL_FROM_DATABASE=Atom Processor C2000 PECI SMBus
+
pci:v00008086d00001F3E*
ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA3 Controller
@@ -73178,6 +74381,9 @@ pci:v00008086d0000201C*
pci:v00008086d00002020*
ID_MODEL_FROM_DATABASE=Sky Lake-E DMI3 Registers
+pci:v00008086d00002020sv000015D9sd0000095D*
+ ID_MODEL_FROM_DATABASE=Sky Lake-E DMI3 Registers (X11SPM-TF)
+
pci:v00008086d00002021*
ID_MODEL_FROM_DATABASE=Sky Lake-E CBDMA Registers
@@ -73967,6 +75173,9 @@ pci:v00008086d0000244Esv00001028sd00000211*
pci:v00008086d0000244Esv00001028sd000002DA*
ID_MODEL_FROM_DATABASE=82801 PCI Bridge (OptiPlex 980)
+pci:v00008086d0000244Esv00001028sd000004F7*
+ ID_MODEL_FROM_DATABASE=82801 PCI Bridge (PowerEdge R320 server)
+
pci:v00008086d0000244Esv0000103Csd00002A3B*
ID_MODEL_FROM_DATABASE=82801 PCI Bridge (Pavilion A1512X)
@@ -75455,6 +76664,12 @@ pci:v00008086d000024FD*
pci:v00008086d000024FDsv00008086sd00000010*
ID_MODEL_FROM_DATABASE=Wireless 8265 / 8275 (Dual Band Wireless-AC 8265)
+pci:v00008086d000024FDsv00008086sd00000150*
+ ID_MODEL_FROM_DATABASE=Wireless 8265 / 8275 (Dual Band Wireless-AC 8265)
+
+pci:v00008086d000024FDsv00008086sd00001010*
+ ID_MODEL_FROM_DATABASE=Wireless 8265 / 8275 (Dual Band Wireless-AC 8265)
+
pci:v00008086d000024FDsv00008086sd00001130*
ID_MODEL_FROM_DATABASE=Wireless 8265 / 8275 (Dual Band Wireless-AC 8265)
@@ -76971,13 +78186,22 @@ pci:v00008086d00002700sv00008086sd00003901*
ID_MODEL_FROM_DATABASE=Optane SSD 900P Series (900P Series [2.5" SFF])
pci:v00008086d00002701*
- ID_MODEL_FROM_DATABASE=Optane DC P4800X Series SSD
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [Optane]
+
+pci:v00008086d00002701sv00001028sd00002000*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [Optane] (Express Flash NVMe [Optane] 375GB 2.5" U.2 (P4800X))
+
+pci:v00008086d00002701sv00001028sd00002001*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [Optane] (Express Flash NVMe [Optane] 750GB 2.5" U.2 (P4800X))
+
+pci:v00008086d00002701sv00001028sd00002002*
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [Optane] (Express Flash NVMe [Optane] 750GB AIC (P4800X))
pci:v00008086d00002701sv00008086sd00003904*
- ID_MODEL_FROM_DATABASE=Optane DC P4800X Series SSD (DC P4800X Series [Add-in Card])
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [Optane] (x4 AIC (P4800X))
pci:v00008086d00002701sv00008086sd00003905*
- ID_MODEL_FROM_DATABASE=Optane DC P4800X Series SSD (DC P4800X Series [2.5" SFF])
+ ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [Optane] (15mm 2.5" U.2 (P4800X))
pci:v00008086d00002770*
ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub
@@ -76997,6 +78221,9 @@ pci:v00008086d00002770sv0000107Bsd00005048*
pci:v00008086d00002770sv00001462sd00007418*
ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub (Wind PC MS-7418)
+pci:v00008086d00002770sv00001849sd00002770*
+ ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub (ConRoe1333-D667)
+
pci:v00008086d00002770sv00008086sd0000544E*
ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub (DeskTop Board D945GTP)
@@ -77012,6 +78239,9 @@ pci:v00008086d00002772sv0000103Csd00002A3B*
pci:v00008086d00002772sv00001462sd00007418*
ID_MODEL_FROM_DATABASE=82945G/GZ Integrated Graphics Controller (Wind PC MS-7418)
+pci:v00008086d00002772sv00001849sd00002772*
+ ID_MODEL_FROM_DATABASE=82945G/GZ Integrated Graphics Controller (ConRoe1333-D667)
+
pci:v00008086d00002772sv00008086sd0000544E*
ID_MODEL_FROM_DATABASE=82945G/GZ Integrated Graphics Controller (DeskTop Board D945GTP)
@@ -77237,6 +78467,9 @@ pci:v00008086d000027B9sv000017AAsd00002009*
pci:v00008086d000027BC*
ID_MODEL_FROM_DATABASE=NM10 Family LPC Controller
+pci:v00008086d000027BCsv00001043sd000083AD*
+ ID_MODEL_FROM_DATABASE=NM10 Family LPC Controller (Eee PC 1015PX)
+
pci:v00008086d000027BCsv0000105Bsd00000D7C*
ID_MODEL_FROM_DATABASE=NM10 Family LPC Controller (D270S/D250S Motherboard)
@@ -77306,6 +78539,9 @@ pci:v00008086d000027C1sv00001028sd000001DF*
pci:v00008086d000027C1sv0000103Csd00002A3B*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (Pavilion A1512X)
+pci:v00008086d000027C1sv00001043sd000083AD*
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (Eee PC 1015PX)
+
pci:v00008086d000027C1sv0000105Bsd00000D7C*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (D270S/D250S Motherboard)
@@ -77408,6 +78644,9 @@ pci:v00008086d000027C8sv00001043sd00001237*
pci:v00008086d000027C8sv00001043sd00008179*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (P5KPL-VM,P5LD2-VM Mainboard)
+pci:v00008086d000027C8sv00001043sd000083AD*
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (Eee PC 1015PX)
+
pci:v00008086d000027C8sv0000105Bsd00000D7C*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (D270S/D250S Motherboard)
@@ -77480,6 +78719,9 @@ pci:v00008086d000027C9sv00001043sd00001237*
pci:v00008086d000027C9sv00001043sd00008179*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (P5KPL-VM,P5LD2-VM Mainboard)
+pci:v00008086d000027C9sv00001043sd000083AD*
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (Eee PC 1015PX)
+
pci:v00008086d000027C9sv0000105Bsd00000D7C*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (D270S/D250S Motherboard)
@@ -77552,6 +78794,9 @@ pci:v00008086d000027CAsv00001043sd00001237*
pci:v00008086d000027CAsv00001043sd00008179*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (P5KPL-VM,P5LD2-VM Mainboard)
+pci:v00008086d000027CAsv00001043sd000083AD*
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (Eee PC 1015PX)
+
pci:v00008086d000027CAsv0000105Bsd00000D7C*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (D270S/D250S Motherboard)
@@ -77618,6 +78863,9 @@ pci:v00008086d000027CBsv00001043sd00001237*
pci:v00008086d000027CBsv00001043sd00008179*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (P5KPL-VM,P5LD2-VM Mainboard)
+pci:v00008086d000027CBsv00001043sd000083AD*
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (Eee PC 1015PX)
+
pci:v00008086d000027CBsv0000105Bsd00000D7C*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (D270S/D250S Motherboard)
@@ -77690,6 +78938,9 @@ pci:v00008086d000027CCsv00001043sd00001237*
pci:v00008086d000027CCsv00001043sd00008179*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (P5KPL-VM,P5LD2-VM Mainboard)
+pci:v00008086d000027CCsv00001043sd000083AD*
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (Eee PC 1015PX)
+
pci:v00008086d000027CCsv0000105Bsd00000D7C*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (D270S/D250S Motherboard)
@@ -77864,6 +79115,9 @@ pci:v00008086d000027D8sv00001043sd00008290*
pci:v00008086d000027D8sv00001043sd000082EA*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (P5KPL-CM Motherboard)
+pci:v00008086d000027D8sv00001043sd00008437*
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Eee PC 1015PX)
+
pci:v00008086d000027D8sv0000105Bsd00000D7C*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (D270S/D250S Motherboard)
@@ -78842,6 +80096,9 @@ pci:v00008086d00002916sv00008086sd00005044*
pci:v00008086d00002917*
ID_MODEL_FROM_DATABASE=ICH9M-E LPC Interface Controller
+pci:v00008086d00002917sv000017AAsd000020F5*
+ ID_MODEL_FROM_DATABASE=ICH9M-E LPC Interface Controller (ThinkPad T400)
+
pci:v00008086d00002917sv0000E4BFsd0000CC4D*
ID_MODEL_FROM_DATABASE=ICH9M-E LPC Interface Controller (CCM-BOOGIE)
@@ -78959,6 +80216,9 @@ pci:v00008086d00002929*
pci:v00008086d00002929sv0000103Csd00003628*
ID_MODEL_FROM_DATABASE=82801IBM/IEM (ICH9M/ICH9M-E) 4 port SATA Controller [AHCI mode] (dv6-1190en)
+pci:v00008086d00002929sv000017AAsd000020F8*
+ ID_MODEL_FROM_DATABASE=82801IBM/IEM (ICH9M/ICH9M-E) 4 port SATA Controller [AHCI mode] (ThinkPad T400)
+
pci:v00008086d00002929sv0000E4BFsd0000CC4D*
ID_MODEL_FROM_DATABASE=82801IBM/IEM (ICH9M/ICH9M-E) 4 port SATA Controller [AHCI mode] (CCM-BOOGIE)
@@ -78995,6 +80255,9 @@ pci:v00008086d00002930sv00001462sd00007345*
pci:v00008086d00002930sv00001462sd00007360*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (G33/P35 Neo)
+pci:v00008086d00002930sv000017AAsd000020F9*
+ ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (ThinkPad T400)
+
pci:v00008086d00002930sv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (QEMU Virtual Machine)
@@ -79058,6 +80321,9 @@ pci:v00008086d00002934sv00001462sd00007345*
pci:v00008086d00002934sv00001462sd00007360*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (G33/P35 Neo)
+pci:v00008086d00002934sv000017AAsd000020F0*
+ ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (ThinkPad T400)
+
pci:v00008086d00002934sv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (QEMU Virtual Machine)
@@ -79112,6 +80378,9 @@ pci:v00008086d00002935sv00001462sd00007345*
pci:v00008086d00002935sv00001462sd00007360*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (G33/P35 Neo)
+pci:v00008086d00002935sv000017AAsd000020F0*
+ ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (ThinkPad T400)
+
pci:v00008086d00002935sv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (QEMU Virtual Machine)
@@ -79160,6 +80429,9 @@ pci:v00008086d00002936sv00001462sd00007345*
pci:v00008086d00002936sv00001462sd00007360*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (G33/P35 Neo)
+pci:v00008086d00002936sv000017AAsd000020F0*
+ ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (ThinkPad T400)
+
pci:v00008086d00002936sv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (QEMU Virtual Machine)
@@ -79208,6 +80480,9 @@ pci:v00008086d00002937sv00001462sd00007345*
pci:v00008086d00002937sv00001462sd00007360*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (G33/P35 Neo)
+pci:v00008086d00002937sv000017AAsd000020F0*
+ ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (ThinkPad T400)
+
pci:v00008086d00002937sv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (QEMU Virtual Machine)
@@ -79259,6 +80534,9 @@ pci:v00008086d00002938sv00001462sd00007345*
pci:v00008086d00002938sv00001462sd00007360*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (G33/P35 Neo)
+pci:v00008086d00002938sv000017AAsd000020F0*
+ ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (ThinkPad T400)
+
pci:v00008086d00002938sv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (QEMU Virtual Machine)
@@ -79295,6 +80573,9 @@ pci:v00008086d00002939sv00001462sd00007345*
pci:v00008086d00002939sv00001462sd00007360*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (G33/P35 Neo)
+pci:v00008086d00002939sv000017AAsd000020F0*
+ ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (ThinkPad T400)
+
pci:v00008086d00002939sv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (QEMU Virtual Machine)
@@ -79349,6 +80630,9 @@ pci:v00008086d0000293Asv00001462sd00007345*
pci:v00008086d0000293Asv00001462sd00007360*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (G33/P35 Neo)
+pci:v00008086d0000293Asv000017AAsd000020F1*
+ ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (ThinkPad T400)
+
pci:v00008086d0000293Asv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (QEMU Virtual Machine)
@@ -79394,6 +80678,9 @@ pci:v00008086d0000293Csv00001462sd00007345*
pci:v00008086d0000293Csv00001462sd00007360*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (G33/P35 Neo)
+pci:v00008086d0000293Csv000017AAsd000020F1*
+ ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (ThinkPad T400)
+
pci:v00008086d0000293Csv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (QEMU Virtual Machine)
@@ -79430,6 +80717,9 @@ pci:v00008086d0000293Esv00001462sd0000735A*
pci:v00008086d0000293Esv00001462sd00007360*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (G33/P35 Neo)
+pci:v00008086d0000293Esv000017AAsd000020F2*
+ ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (ThinkPad T400)
+
pci:v00008086d0000293Esv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (QEMU Virtual Machine)
@@ -79955,6 +81245,9 @@ pci:v00008086d00002A17*
pci:v00008086d00002A40*
ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Memory Controller Hub
+pci:v00008086d00002A40sv000017AAsd000020E0*
+ ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Memory Controller Hub (ThinkPad T400)
+
pci:v00008086d00002A40sv0000E4BFsd0000CC4D*
ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Memory Controller Hub (CCM-BOOGIE)
@@ -79967,18 +81260,27 @@ pci:v00008086d00002A41sv0000E4BFsd0000CC4D*
pci:v00008086d00002A42*
ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Integrated Graphics Controller
+pci:v00008086d00002A42sv000017AAsd00002112*
+ ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Integrated Graphics Controller (ThinkPad T400)
+
pci:v00008086d00002A42sv0000E4BFsd0000CC4D*
ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Integrated Graphics Controller (CCM-BOOGIE)
pci:v00008086d00002A43*
ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Integrated Graphics Controller
+pci:v00008086d00002A43sv000017AAsd00002112*
+ ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Integrated Graphics Controller (ThinkPad T400)
+
pci:v00008086d00002A43sv0000E4BFsd0000CC4D*
ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Integrated Graphics Controller (CCM-BOOGIE)
pci:v00008086d00002A44*
ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset MEI Controller
+pci:v00008086d00002A44sv000017AAsd000020E6*
+ ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset MEI Controller (ThinkPad T400)
+
pci:v00008086d00002A45*
ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset MEI Controller
@@ -81191,6 +82493,48 @@ pci:v00008086d00003165sv00008086sd00004210*
pci:v00008086d00003166*
ID_MODEL_FROM_DATABASE=Dual Band Wireless-AC 3165 Plus Bluetooth
+pci:v00008086d00003184*
+ ID_MODEL_FROM_DATABASE=UHD Graphics 605
+
+pci:v00008086d0000318C*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Dynamic Platform and Thermal Framework Processor Participant
+
+pci:v00008086d0000318E*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor NorthPeak
+
+pci:v00008086d0000319A*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Trusted Execution Engine Interface
+
+pci:v00008086d000031AC*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Serial IO I2C Host Controller
+
+pci:v00008086d000031AE*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Serial IO I2C Host Controller
+
+pci:v00008086d000031BC*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Serial IO UART Host Controller
+
+pci:v00008086d000031BE*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Serial IO UART Host Controller
+
+pci:v00008086d000031C0*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Serial IO UART Host Controller
+
+pci:v00008086d000031C2*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Serial IO SPI Host Controller
+
+pci:v00008086d000031C4*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Serial IO SPI Host Controller
+
+pci:v00008086d000031C6*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Serial IO SPI Host Controller
+
+pci:v00008086d000031D4*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Gaussian Mixture Model
+
+pci:v00008086d000031EE*
+ ID_MODEL_FROM_DATABASE=Celeron/Pentium Silver Processor Serial IO UART Host Controller
+
pci:v00008086d00003200*
ID_MODEL_FROM_DATABASE=GD31244 PCI-X SATA HBA
@@ -81416,6 +82760,60 @@ pci:v00008086d00003433*
pci:v00008086d00003438*
ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub Throttle Registers
+pci:v00008086d00003482*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP LPC Controller
+
+pci:v00008086d000034A3*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP SMBus Controller
+
+pci:v00008086d000034A4*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP SPI Controller
+
+pci:v00008086d000034A8*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP Serial IO UART Controller #0
+
+pci:v00008086d000034A9*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP Serial IO UART Controller #1
+
+pci:v00008086d000034AA*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP Serial IO SPI Controller #0
+
+pci:v00008086d000034AB*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP Serial IO SPI Controller #1
+
+pci:v00008086d000034B0*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP PCI Express Root Port #9
+
+pci:v00008086d000034BC*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP PCI Express Root Port #5
+
+pci:v00008086d000034C5*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP Serial IO I2c Controller #4
+
+pci:v00008086d000034C6*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP Serial IO I2c Controller #5
+
+pci:v00008086d000034D3*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP SATA Controller [AHCI mode]
+
+pci:v00008086d000034E8*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP Serial IO I2C Controller #0
+
+pci:v00008086d000034E9*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP Serial IO I2C Controller #1
+
+pci:v00008086d000034EA*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP Serial IO I2C Controller #2
+
+pci:v00008086d000034EB*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP Serial IO I2C Controller #3
+
+pci:v00008086d000034ED*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP USB 3.1 xHCI Host Controller
+
+pci:v00008086d000034F8*
+ ID_MODEL_FROM_DATABASE=Ice Lake-LP SD Controller
+
pci:v00008086d00003500*
ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express Upstream Port
@@ -82562,6 +83960,9 @@ pci:v00008086d00003B13*
pci:v00008086d00003B14*
ID_MODEL_FROM_DATABASE=3420 Chipset LPC Interface Controller
+pci:v00008086d00003B14sv000015D9sd00000605*
+ ID_MODEL_FROM_DATABASE=3420 Chipset LPC Interface Controller (X8SIL)
+
pci:v00008086d00003B15*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller
@@ -82607,6 +84008,9 @@ pci:v00008086d00003B22*
pci:v00008086d00003B22sv00001028sd000002DA*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 6 port SATA AHCI Controller (OptiPlex 980)
+pci:v00008086d00003B22sv000015D9sd00000605*
+ ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 6 port SATA AHCI Controller (X8SIL)
+
pci:v00008086d00003B22sv000015D9sd0000060D*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 6 port SATA AHCI Controller (C7SIM-Q Motherboard)
@@ -82700,6 +84104,9 @@ pci:v00008086d00003B30sv00001043sd00008383*
pci:v00008086d00003B30sv0000144Dsd0000C06A*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (R730 Laptop)
+pci:v00008086d00003B30sv000015D9sd00000605*
+ ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (X8SIL)
+
pci:v00008086d00003B30sv000015D9sd0000060D*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (C7SIM-Q Motherboard)
@@ -82742,6 +84149,9 @@ pci:v00008086d00003B34sv00001028sd0000040B*
pci:v00008086d00003B34sv0000144Dsd0000C06A*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (R730 Laptop)
+pci:v00008086d00003B34sv000015D9sd00000605*
+ ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (X8SIL)
+
pci:v00008086d00003B34sv000015D9sd0000060D*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (C7SIM-Q Motherboard)
@@ -82787,6 +84197,9 @@ pci:v00008086d00003B3Csv00001028sd0000040B*
pci:v00008086d00003B3Csv0000144Dsd0000C06A*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (R730 Laptop)
+pci:v00008086d00003B3Csv000015D9sd00000605*
+ ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (X8SIL)
+
pci:v00008086d00003B3Csv000015D9sd0000060D*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (C7SIM-Q Motherboard)
@@ -83195,6 +84608,18 @@ pci:v00008086d00003E89*
pci:v00008086d00003E91*
ID_MODEL_FROM_DATABASE=8th Gen Core Processor Gaussian Mixture Model
+pci:v00008086d00003E92*
+ ID_MODEL_FROM_DATABASE=UHD Graphics 630 (Desktop)
+
+pci:v00008086d00003E9B*
+ ID_MODEL_FROM_DATABASE=UHD Graphics 630 (Mobile)
+
+pci:v00008086d00003EA0*
+ ID_MODEL_FROM_DATABASE=UHD Graphics 620 (Whiskey Lake)
+
+pci:v00008086d00003EA5*
+ ID_MODEL_FROM_DATABASE=Iris Plus Graphics 655
+
pci:v00008086d00003EC2*
ID_MODEL_FROM_DATABASE=8th Gen Core Processor Host Bridge/DRAM Registers
@@ -83738,6 +85163,12 @@ pci:v00008086d00005845*
pci:v00008086d00005845sv00001AF4sd00001100*
ID_MODEL_FROM_DATABASE=QEMU NVM Express Controller (QEMU Virtual Machine)
+pci:v00008086d00005900*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+
+pci:v00008086d00005901*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor PCIe Controller (x16)
+
pci:v00008086d00005902*
ID_MODEL_FROM_DATABASE=HD Graphics 610
@@ -83750,18 +85181,33 @@ pci:v00008086d00005904sv000017AAsd00002247*
pci:v00008086d00005904sv000017AAsd0000224F*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers (ThinkPad X1 Carbon 5th Gen)
+pci:v00008086d00005905*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor PCIe Controller (x8)
+
+pci:v00008086d00005909*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor PCIe Controller (x4)
+
+pci:v00008086d0000590C*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+
pci:v00008086d0000590F*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
pci:v00008086d00005910*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+pci:v00008086d00005911*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Gaussian Mixture Model
+
pci:v00008086d00005912*
ID_MODEL_FROM_DATABASE=HD Graphics 630
pci:v00008086d00005914*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+pci:v00008086d00005914sv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers (ThinkPad T480)
+
pci:v00008086d00005916*
ID_MODEL_FROM_DATABASE=HD Graphics 620
@@ -83774,11 +85220,35 @@ pci:v00008086d00005916sv000017AAsd0000224F*
pci:v00008086d00005917*
ID_MODEL_FROM_DATABASE=UHD Graphics 620
+pci:v00008086d00005917sv000017AAsd0000225E*
+ ID_MODEL_FROM_DATABASE=UHD Graphics 620 (ThinkPad T480)
+
+pci:v00008086d00005918*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+
+pci:v00008086d0000591B*
+ ID_MODEL_FROM_DATABASE=HD Graphics 630
+
+pci:v00008086d0000591C*
+ ID_MODEL_FROM_DATABASE=UHD Graphics 615
+
pci:v00008086d0000591D*
ID_MODEL_FROM_DATABASE=HD Graphics P630
+pci:v00008086d0000591E*
+ ID_MODEL_FROM_DATABASE=HD Graphics 615
+
pci:v00008086d0000591F*
- ID_MODEL_FROM_DATABASE=Intel Kaby Lake Host Bridge
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+
+pci:v00008086d00005923*
+ ID_MODEL_FROM_DATABASE=HD Graphics 635
+
+pci:v00008086d00005926*
+ ID_MODEL_FROM_DATABASE=Iris Plus Graphics 640
+
+pci:v00008086d00005927*
+ ID_MODEL_FROM_DATABASE=Iris Plus Graphics 650
pci:v00008086d00005A84*
ID_MODEL_FROM_DATABASE=Celeron N3350/Pentium N4200/Atom E3900 Series Integrated Graphics Controller
@@ -83948,6 +85418,9 @@ pci:v00008086d000065FF*
pci:v00008086d00006F00*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DMI2
+pci:v00008086d00006F00sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DMI2 (X10SRL-F)
+
pci:v00008086d00006F01*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 0
@@ -84032,39 +85505,75 @@ pci:v00008086d00006F1F*
pci:v00008086d00006F20*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 0
+pci:v00008086d00006F20sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 0 (X10SRL-F)
+
pci:v00008086d00006F21*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 1
+pci:v00008086d00006F21sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 1 (X10SRL-F)
+
pci:v00008086d00006F22*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 2
+pci:v00008086d00006F22sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 2 (X10SRL-F)
+
pci:v00008086d00006F23*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 3
+pci:v00008086d00006F23sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 3 (X10SRL-F)
+
pci:v00008086d00006F24*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 4
+pci:v00008086d00006F24sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 4 (X10SRL-F)
+
pci:v00008086d00006F25*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 5
+pci:v00008086d00006F25sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 5 (X10SRL-F)
+
pci:v00008086d00006F26*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 6
+pci:v00008086d00006F26sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 6 (X10SRL-F)
+
pci:v00008086d00006F27*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 7
+pci:v00008086d00006F27sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 7 (X10SRL-F)
+
pci:v00008086d00006F28*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Map/VTd_Misc/System Management
+pci:v00008086d00006F28sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Map/VTd_Misc/System Management (X10SRL-F)
+
pci:v00008086d00006F29*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Hot Plug
+pci:v00008086d00006F29sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Hot Plug (X10SRL-F)
+
pci:v00008086d00006F2A*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO RAS/Control Status/Global Errors
+pci:v00008086d00006F2Asv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO RAS/Control Status/Global Errors (X10SRL-F)
+
pci:v00008086d00006F2C*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D I/O APIC
+pci:v00008086d00006F2Csv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D I/O APIC (X10SRL-F)
+
pci:v00008086d00006F30*
ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Home Agent 0
@@ -84705,49 +86214,55 @@ pci:v00008086d00008003*
ID_MODEL_FROM_DATABASE=Trusted Execution Technology Registers
pci:v00008086d00008100*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo)
+ ID_MODEL_FROM_DATABASE=US15W/US15X SCH [Poulsbo] Host Bridge
+
+pci:v00008086d00008101*
+ ID_MODEL_FROM_DATABASE=US15L/UL11L SCH [Poulsbo] Host Bridge
pci:v00008086d00008108*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) Graphics Controller
+ ID_MODEL_FROM_DATABASE=US15W/US15X SCH [Poulsbo] Graphics Controller
+
+pci:v00008086d00008109*
+ ID_MODEL_FROM_DATABASE=US15L/UL11L SCH [Poulsbo] Graphics Controller
pci:v00008086d00008110*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) PCI Express Port 1
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] PCI Express Port 1
pci:v00008086d00008112*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) PCI Express Port 2
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] PCI Express Port 2
pci:v00008086d00008114*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) USB UHCI #1
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] USB UHCI Controller #1
pci:v00008086d00008115*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) USB UHCI #2
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] USB UHCI Controller #2
pci:v00008086d00008116*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) USB UHCI #3
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] USB UHCI Controller #3
pci:v00008086d00008117*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) USB EHCI #1
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] USB EHCI Controller
pci:v00008086d00008118*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) USB Client Controller
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] USB Client Controller
pci:v00008086d00008119*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) LPC Bridge
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] LPC Bridge
pci:v00008086d0000811A*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) IDE Controller
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] IDE Controller
pci:v00008086d0000811B*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) HD Audio Controller
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] HD Audio Controller
pci:v00008086d0000811C*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) SDIO Controller #1
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] SDIO/MMC Controller #1
pci:v00008086d0000811D*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) SDIO Controller #2
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] SDIO/MMC Controller #2
pci:v00008086d0000811E*
- ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) SDIO Controller #3
+ ID_MODEL_FROM_DATABASE=US15W/US15X/US15L/UL11L SCH [Poulsbo] SDIO/MMC Controller #3
pci:v00008086d00008180*
ID_MODEL_FROM_DATABASE=Atom Processor E6xx PCI Express Port 3
@@ -84815,6 +86330,9 @@ pci:v00008086d00008500sv00001993sd00000DEE*
pci:v00008086d00008500sv00001993sd00000DEF*
ID_MODEL_FROM_DATABASE=IXP4XX Network Processor (IXP420/421/422/425/IXC1100) (mGuard-PCI AV#0)
+pci:v00008086d000087C0*
+ ID_MODEL_FROM_DATABASE=UHD Graphics 617
+
pci:v00008086d00008800*
ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T PCI Express Port
@@ -84902,6 +86420,9 @@ pci:v00008086d00008C01*
pci:v00008086d00008C02*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode]
+pci:v00008086d00008C02sv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode] (ThinkCentre M83)
+
pci:v00008086d00008C03*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode]
@@ -84998,6 +86519,9 @@ pci:v00008086d00008C20sv0000103Csd00001909*
pci:v00008086d00008C20sv000017AAsd0000220E*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset High Definition Audio Controller (ThinkPad T440p)
+pci:v00008086d00008C20sv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset High Definition Audio Controller (ThinkCentre M83)
+
pci:v00008086d00008C21*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset High Definition Audio Controller
@@ -85010,6 +86534,9 @@ pci:v00008086d00008C22sv0000103Csd00001909*
pci:v00008086d00008C22sv000017AAsd0000220E*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SMBus Controller (ThinkPad T440p)
+pci:v00008086d00008C22sv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SMBus Controller (ThinkCentre M83)
+
pci:v00008086d00008C23*
ID_MODEL_FROM_DATABASE=8 Series Chipset Family CHAP Counters
@@ -85028,6 +86555,9 @@ pci:v00008086d00008C26sv000017AAsd0000220E*
pci:v00008086d00008C26sv000017AAsd00002210*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #1 (ThinkPad T540p)
+pci:v00008086d00008C26sv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #1 (ThinkCentre M83)
+
pci:v00008086d00008C26sv00002210sd000017AA*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #1 (ThinkPad T540p)
@@ -85040,6 +86570,9 @@ pci:v00008086d00008C2Dsv0000103Csd00001909*
pci:v00008086d00008C2Dsv000017AAsd0000220E*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #2 (ThinkPad T440p)
+pci:v00008086d00008C2Dsv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #2 (ThinkCentre M83)
+
pci:v00008086d00008C31*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB xHCI
@@ -85049,6 +86582,9 @@ pci:v00008086d00008C31sv0000103Csd00001909*
pci:v00008086d00008C31sv000017AAsd0000220E*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB xHCI (ThinkPad T440p)
+pci:v00008086d00008C31sv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB xHCI (ThinkCentre M83)
+
pci:v00008086d00008C33*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LAN Controller
@@ -85064,6 +86600,9 @@ pci:v00008086d00008C3Asv0000103Csd00001909*
pci:v00008086d00008C3Asv000017AAsd0000220E*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family MEI Controller #1 (ThinkPad T440p)
+pci:v00008086d00008C3Asv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family MEI Controller #1 (ThinkCentre M83)
+
pci:v00008086d00008C3B*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family MEI Controller #2
@@ -85112,6 +86651,9 @@ pci:v00008086d00008C4B*
pci:v00008086d00008C4C*
ID_MODEL_FROM_DATABASE=Q85 Express LPC Controller
+pci:v00008086d00008C4Csv000017AAsd0000309F*
+ ID_MODEL_FROM_DATABASE=Q85 Express LPC Controller (ThinkCentre M83)
+
pci:v00008086d00008C4D*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller
@@ -85164,7 +86706,7 @@ pci:v00008086d00008C5B*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller
pci:v00008086d00008C5C*
- ID_MODEL_FROM_DATABASE=C220 Series Chipset Family H81 Express LPC Controller
+ ID_MODEL_FROM_DATABASE=H81 Express LPC Controller
pci:v00008086d00008C5D*
ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller
@@ -85275,13 +86817,16 @@ pci:v00008086d00008CC2*
ID_MODEL_FROM_DATABASE=9 Series Chipset Family LPC Controller
pci:v00008086d00008CC3*
- ID_MODEL_FROM_DATABASE=9 Series Chipset Family HM97 LPC Controller
+ ID_MODEL_FROM_DATABASE=HM97 Chipset LPC Controller
pci:v00008086d00008CC4*
- ID_MODEL_FROM_DATABASE=9 Series Chipset Family Z97 LPC Controller
+ ID_MODEL_FROM_DATABASE=Z97 Chipset LPC Controller
+
+pci:v00008086d00008CC5*
+ ID_MODEL_FROM_DATABASE=QM97 Chipset LPC Controller
pci:v00008086d00008CC6*
- ID_MODEL_FROM_DATABASE=9 Series Chipset Family H97 Controller
+ ID_MODEL_FROM_DATABASE=H97 Chipset LPC Controller
pci:v00008086d00008D00*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset 4-port SATA Controller [IDE mode]
@@ -85361,18 +86906,30 @@ pci:v00008086d00008D21*
pci:v00008086d00008D22*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset SMBus Controller
+pci:v00008086d00008D22sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=C610/X99 series chipset SMBus Controller (X10SRL-F)
+
pci:v00008086d00008D24*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset Thermal Subsystem
pci:v00008086d00008D26*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset USB Enhanced Host Controller #1
+pci:v00008086d00008D26sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=C610/X99 series chipset USB Enhanced Host Controller #1 (X10SRL-F)
+
pci:v00008086d00008D2D*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset USB Enhanced Host Controller #2
+pci:v00008086d00008D2Dsv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=C610/X99 series chipset USB Enhanced Host Controller #2 (X10SRL-F)
+
pci:v00008086d00008D31*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset USB xHCI Host Controller
+pci:v00008086d00008D31sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=C610/X99 series chipset USB xHCI Host Controller (X10SRL-F)
+
pci:v00008086d00008D33*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset LAN Controller
@@ -85382,9 +86939,15 @@ pci:v00008086d00008D34*
pci:v00008086d00008D3A*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset MEI Controller #1
+pci:v00008086d00008D3Asv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=C610/X99 series chipset MEI Controller #1 (X10SRL-F)
+
pci:v00008086d00008D3B*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset MEI Controller #2
+pci:v00008086d00008D3Bsv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=C610/X99 series chipset MEI Controller #2 (X10SRL-F)
+
pci:v00008086d00008D3C*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset IDE-r Controller
@@ -85406,6 +86969,9 @@ pci:v00008086d00008D43*
pci:v00008086d00008D44*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller
+pci:v00008086d00008D44sv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller (X10SRL-F)
+
pci:v00008086d00008D45*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller
@@ -85460,6 +87026,9 @@ pci:v00008086d00008D6E*
pci:v00008086d00008D7C*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset SPSR
+pci:v00008086d00008D7Csv000015D9sd00000832*
+ ID_MODEL_FROM_DATABASE=C610/X99 series chipset SPSR (X10SRL-F)
+
pci:v00008086d00008D7D*
ID_MODEL_FROM_DATABASE=C610/X99 series chipset MS SMBus 0
@@ -85811,9 +87380,15 @@ pci:v00008086d00009CE6*
pci:v00008086d00009D03*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode]
+pci:v00008086d00009D03sv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (Latitude E7470)
+
pci:v00008086d00009D03sv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (Latitude 3570)
+pci:v00008086d00009D03sv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (ThinkPad T480)
+
pci:v00008086d00009D03sv000017AAsd0000382A*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (B51-80 Laptop)
@@ -85853,18 +87428,27 @@ pci:v00008086d00009D19*
pci:v00008086d00009D21*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC
+pci:v00008086d00009D21sv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (Latitude E7470)
+
pci:v00008086d00009D21sv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (Latitude 3570)
pci:v00008086d00009D21sv000017AAsd0000224F*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (ThinkPad X1 Carbon 5th Gen)
+pci:v00008086d00009D21sv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (ThinkPad T480)
+
pci:v00008086d00009D21sv000017AAsd0000382A*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (B51-80 Laptop)
pci:v00008086d00009D23*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus
+pci:v00008086d00009D23sv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (Latitude E7470)
+
pci:v00008086d00009D23sv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (Latitude 3570)
@@ -85874,6 +87458,9 @@ pci:v00008086d00009D23sv000017AAsd00002247*
pci:v00008086d00009D23sv000017AAsd0000224F*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (ThinkPad X1 Carbon 5th Gen)
+pci:v00008086d00009D23sv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (ThinkPad T480)
+
pci:v00008086d00009D23sv000017AAsd0000382A*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (B51-80 Laptop)
@@ -85895,18 +87482,27 @@ pci:v00008086d00009D2D*
pci:v00008086d00009D2F*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller
+pci:v00008086d00009D2Fsv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (Latitude E7470)
+
pci:v00008086d00009D2Fsv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (Latitude 3570)
pci:v00008086d00009D2Fsv000017AAsd00002247*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (ThinkPad T570)
+pci:v00008086d00009D2Fsv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (ThinkPad T480)
+
pci:v00008086d00009D2Fsv000017AAsd0000382A*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (B51-80 Laptop)
pci:v00008086d00009D31*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem
+pci:v00008086d00009D31sv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (Latitude E7470)
+
pci:v00008086d00009D31sv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (Latitude 3570)
@@ -85916,6 +87512,9 @@ pci:v00008086d00009D31sv000017AAsd00002247*
pci:v00008086d00009D31sv000017AAsd0000224F*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (ThinkPad X1 Carbon 5th Gen)
+pci:v00008086d00009D31sv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (ThinkPad T480)
+
pci:v00008086d00009D31sv000017AAsd0000382A*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (B51-80 Laptop)
@@ -85925,6 +87524,9 @@ pci:v00008086d00009D35*
pci:v00008086d00009D3A*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1
+pci:v00008086d00009D3Asv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (Latitude E7470)
+
pci:v00008086d00009D3Asv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (Latitude 3570)
@@ -85934,6 +87536,9 @@ pci:v00008086d00009D3Asv000017AAsd00002247*
pci:v00008086d00009D3Asv000017AAsd0000224F*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (ThinkPad X1 Carbon 5th Gen)
+pci:v00008086d00009D3Asv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (ThinkPad T480)
+
pci:v00008086d00009D3Asv000017AAsd0000382A*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (B51-80 Laptop)
@@ -85946,12 +87551,18 @@ pci:v00008086d00009D43sv000017AAsd0000382A*
pci:v00008086d00009D48*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller
+pci:v00008086d00009D48sv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller (Latitude E7470)
+
pci:v00008086d00009D48sv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller (Latitude 3570)
pci:v00008086d00009D4E*
ID_MODEL_FROM_DATABASE=Intel(R) 100 Series Chipset Family LPC Controller/eSPI Controller - 9D4E
+pci:v00008086d00009D4Esv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Intel(R) 100 Series Chipset Family LPC Controller/eSPI Controller - 9D4E (ThinkPad T480)
+
pci:v00008086d00009D56*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller
@@ -85970,6 +87581,9 @@ pci:v00008086d00009D60*
pci:v00008086d00009D60sv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (Latitude 3570)
+pci:v00008086d00009D60sv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (ThinkPad T480)
+
pci:v00008086d00009D60sv00008086sd00009D60*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (100 Series PCH/Sunrise Point PCH I2C0 [Skylake/Kaby Lake LPSS I2C])
@@ -85994,6 +87608,9 @@ pci:v00008086d00009D66*
pci:v00008086d00009D70*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio
+pci:v00008086d00009D70sv00001028sd000006DC*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (Latitude E7470)
+
pci:v00008086d00009D70sv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (Latitude 3570)
@@ -86003,6 +87620,9 @@ pci:v00008086d00009D70sv000017AAsd0000382A*
pci:v00008086d00009D71*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio
+pci:v00008086d00009D71sv000017AAsd0000225D*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (ThinkPad T480)
+
pci:v00008086d0000A000*
ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge
@@ -86036,18 +87656,27 @@ pci:v00008086d0000A003*
pci:v00008086d0000A010*
ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge
+pci:v00008086d0000A010sv00001043sd000083AC*
+ ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge (Eee PC 1015PX)
+
pci:v00008086d0000A010sv0000144Dsd0000C072*
ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge (Notebook N150P)
pci:v00008086d0000A011*
ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller
+pci:v00008086d0000A011sv00001043sd000083AC*
+ ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller (Eee PC 1015PX)
+
pci:v00008086d0000A011sv0000144Dsd0000C072*
ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller (Notebook N150P)
pci:v00008086d0000A012*
ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller
+pci:v00008086d0000A012sv00001043sd000083AC*
+ ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller (Eee PC 1015PX)
+
pci:v00008086d0000A012sv0000144Dsd0000C072*
ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller (Notebook N150P)
@@ -86055,130 +87684,151 @@ pci:v00008086d0000A013*
ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx CHAPS counter
pci:v00008086d0000A102*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA controller [AHCI mode]
+ ID_MODEL_FROM_DATABASE=Q170/Q150/B150/H170/H110/Z170/CM236 Chipset SATA Controller [AHCI Mode]
pci:v00008086d0000A103*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA Controller [AHCI mode]
+ ID_MODEL_FROM_DATABASE=HM170/QM170 Chipset SATA Controller [AHCI Mode]
+
+pci:v00008086d0000A103sv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=HM170/QM170 Chipset SATA Controller [AHCI Mode] (XPS 15 9550)
pci:v00008086d0000A105*
ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA Controller [RAID mode]
+pci:v00008086d0000A106*
+ ID_MODEL_FROM_DATABASE=Q170/H170/Z170/CM236 Chipset SATA Controller [RAID Mode]
+
pci:v00008086d0000A107*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA Controller [RAID mode]
+ ID_MODEL_FROM_DATABASE=HM170/QM170 Chipset SATA Controller [RAID Mode]
pci:v00008086d0000A10F*
ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA Controller [RAID mode]
pci:v00008086d0000A110*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #1
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #1
pci:v00008086d0000A111*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #2
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #2
pci:v00008086d0000A112*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #3
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #3
pci:v00008086d0000A113*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #4
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #4
pci:v00008086d0000A114*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #5
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #5
pci:v00008086d0000A115*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #6
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #6
pci:v00008086d0000A116*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #7
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #7
pci:v00008086d0000A117*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #8
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #8
pci:v00008086d0000A118*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #9
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #9
pci:v00008086d0000A119*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #10
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #10
pci:v00008086d0000A11A*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #11
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #11
pci:v00008086d0000A11B*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #12
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #12
pci:v00008086d0000A11C*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #13
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #13
pci:v00008086d0000A11D*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #14
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #14
pci:v00008086d0000A11E*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #15
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #15
pci:v00008086d0000A11F*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #16
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #16
pci:v00008086d0000A120*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H P2SB
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family P2SB
pci:v00008086d0000A121*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PMC
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Power Management Controller
+
+pci:v00008086d0000A121sv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Power Management Controller (XPS 15 9550)
pci:v00008086d0000A122*
ID_MODEL_FROM_DATABASE=Sunrise Point-H cAVS
pci:v00008086d0000A123*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H SMBus
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family SMBus
+
+pci:v00008086d0000A123sv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family SMBus (XPS 15 9550)
pci:v00008086d0000A124*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H SPI Controller
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family SPI Controller
pci:v00008086d0000A125*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Gigabit Ethernet Controller
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Gigabit Ethernet Controller
pci:v00008086d0000A126*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Northpeak
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Trace Hub
pci:v00008086d0000A127*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO UART #0
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO UART #0
pci:v00008086d0000A128*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO UART #1
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO UART #1
pci:v00008086d0000A129*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO SPI #0
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO GSPI #0
pci:v00008086d0000A12A*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO SPI #1
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO GSPI #1
pci:v00008086d0000A12F*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H USB 3.0 xHCI Controller
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family USB 3.0 xHCI Controller
+
+pci:v00008086d0000A12Fsv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family USB 3.0 xHCI Controller (XPS 15 9550)
pci:v00008086d0000A130*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H USB Device Controller (OTG)
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family USB Device Controller (OTG)
pci:v00008086d0000A131*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Thermal subsystem
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Thermal Subsystem
+
+pci:v00008086d0000A131sv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Thermal Subsystem (XPS 15 9550)
pci:v00008086d0000A133*
ID_MODEL_FROM_DATABASE=Sunrise Point-H Northpeak ACPI Function
pci:v00008086d0000A135*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Integrated Sensor Hub
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Integrated Sensor Hub
pci:v00008086d0000A13A*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H CSME HECI #1
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family MEI Controller #1
+
+pci:v00008086d0000A13Asv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family MEI Controller #1 (XPS 15 9550)
pci:v00008086d0000A13B*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H CSME HECI #2
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family MEI Controller #2
pci:v00008086d0000A13C*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H CSME IDE Redirection
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family IDE Redirection
pci:v00008086d0000A13D*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H KT Redirection
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family KT Redirection
pci:v00008086d0000A13E*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H CSME HECI #3
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family MEI Controller #3
pci:v00008086d0000A140*
ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
@@ -86190,28 +87840,28 @@ pci:v00008086d0000A142*
ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
pci:v00008086d0000A143*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=H110 Chipset LPC/eSPI Controller
pci:v00008086d0000A144*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=H170 Chipset LPC/eSPI Controller
pci:v00008086d0000A145*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=Z170 Chipset LPC/eSPI Controller
pci:v00008086d0000A146*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=Q170 Chipset LPC/eSPI Controller
pci:v00008086d0000A147*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=Q150 Chipset LPC/eSPI Controller
pci:v00008086d0000A148*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=B150 Chipset LPC/eSPI Controller
pci:v00008086d0000A149*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=C236 Chipset LPC/eSPI Controller
pci:v00008086d0000A14A*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=C232 Chipset LPC/eSPI Controller
pci:v00008086d0000A14B*
ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
@@ -86220,28 +87870,31 @@ pci:v00008086d0000A14C*
ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
pci:v00008086d0000A14D*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=QM170 Chipset LPC/eSPI Controller
pci:v00008086d0000A14E*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=HM170 Chipset LPC/eSPI Controller
+
+pci:v00008086d0000A14Esv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=HM170 Chipset LPC/eSPI Controller (XPS 15 9550)
pci:v00008086d0000A14F*
ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
pci:v00008086d0000A150*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=CM236 Chipset LPC/eSPI Controller
pci:v00008086d0000A151*
ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
pci:v00008086d0000A152*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=HM175 Chipset LPC/eSPI Controller
pci:v00008086d0000A153*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=QM175 Chipset LPC/eSPI Controller
pci:v00008086d0000A154*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
+ ID_MODEL_FROM_DATABASE=CM238 Chipset LPC/eSPI Controller
pci:v00008086d0000A155*
ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
@@ -86277,166 +87930,208 @@ pci:v00008086d0000A15F*
ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
pci:v00008086d0000A160*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO I2C Controller #0
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #0
+
+pci:v00008086d0000A160sv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #0 (XPS 15 9550)
pci:v00008086d0000A161*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO I2C Controller #1
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #1
+
+pci:v00008086d0000A161sv00001028sd000006E4*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #1 (XPS 15 9550)
+
+pci:v00008086d0000A162*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #2
+
+pci:v00008086d0000A163*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #3
pci:v00008086d0000A166*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO UART Controller #2
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO UART Controller #2
pci:v00008086d0000A167*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Root Port #17
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #17
pci:v00008086d0000A168*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Root Port #18
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #18
pci:v00008086d0000A169*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Root Port #19
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #19
pci:v00008086d0000A16A*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Root Port #20
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family PCI Express Root Port #20
pci:v00008086d0000A170*
- ID_MODEL_FROM_DATABASE=Sunrise Point-H HD Audio
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family HD Audio Controller
pci:v00008086d0000A171*
ID_MODEL_FROM_DATABASE=CM238 HD Audio Controller
pci:v00008086d0000A182*
- ID_MODEL_FROM_DATABASE=Lewisburg SATA Controller [AHCI mode]
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family SATA Controller [AHCI mode]
pci:v00008086d0000A186*
- ID_MODEL_FROM_DATABASE=Lewisburg SATA Controller [RAID mode]
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family SATA Controller [RAID mode]
pci:v00008086d0000A190*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #1
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #1
pci:v00008086d0000A191*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #2
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #2
pci:v00008086d0000A192*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #3
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #3
pci:v00008086d0000A193*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #4
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #4
pci:v00008086d0000A194*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #5
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #5
pci:v00008086d0000A195*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #6
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #6
pci:v00008086d0000A196*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #7
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #7
pci:v00008086d0000A197*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #8
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #8
pci:v00008086d0000A198*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #9
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #9
pci:v00008086d0000A199*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #10
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #10
pci:v00008086d0000A19A*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #11
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #11
pci:v00008086d0000A19B*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #12
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #12
pci:v00008086d0000A19C*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #13
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #13
pci:v00008086d0000A19D*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #14
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #14
pci:v00008086d0000A19E*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #15
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #15
pci:v00008086d0000A19F*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #16
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #16
pci:v00008086d0000A1A0*
- ID_MODEL_FROM_DATABASE=Lewisburg P2SB
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family P2SB
pci:v00008086d0000A1A1*
- ID_MODEL_FROM_DATABASE=Lewisburg PMC
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family Power Management Controller
+
+pci:v00008086d0000A1A1sv000015D9sd0000095D*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family Power Management Controller (X11SPM-TF)
pci:v00008086d0000A1A2*
- ID_MODEL_FROM_DATABASE=Lewisburg cAVS
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family cAVS
pci:v00008086d0000A1A3*
- ID_MODEL_FROM_DATABASE=Lewisburg SMBus
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family SMBus
+
+pci:v00008086d0000A1A3sv000015D9sd0000095D*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family SMBus (X11SPM-TF)
pci:v00008086d0000A1A4*
- ID_MODEL_FROM_DATABASE=Lewisburg SPI Controller
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family SPI Controller
+
+pci:v00008086d0000A1A4sv000015D9sd0000095D*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family SPI Controller (X11SPM-TF)
+
+pci:v00008086d0000A1A6*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family Trace Hub
pci:v00008086d0000A1AF*
- ID_MODEL_FROM_DATABASE=Lewisburg USB 3.0 xHCI Controller
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family USB 3.0 xHCI Controller
+
+pci:v00008086d0000A1AFsv000015D9sd0000095D*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family USB 3.0 xHCI Controller (X11SPM-TF)
pci:v00008086d0000A1B1*
- ID_MODEL_FROM_DATABASE=Lewisburg Thermal Subsystem
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family Thermal Subsystem
+
+pci:v00008086d0000A1B1sv000015D9sd0000095D*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family Thermal Subsystem (X11SPM-TF)
pci:v00008086d0000A1BA*
- ID_MODEL_FROM_DATABASE=Lewisburg CSME: HECI #1
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family MEI Controller #1
+
+pci:v00008086d0000A1BAsv000015D9sd0000095D*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family MEI Controller #1 (X11SPM-TF)
pci:v00008086d0000A1BB*
- ID_MODEL_FROM_DATABASE=Lewisburg CSME: HECI #2
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family MEI Controller #2
+
+pci:v00008086d0000A1BBsv000015D9sd0000095D*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family MEI Controller #2 (X11SPM-TF)
pci:v00008086d0000A1BC*
- ID_MODEL_FROM_DATABASE=Lewisburg CSME: IDE-r
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family IDE Redirection
pci:v00008086d0000A1BD*
- ID_MODEL_FROM_DATABASE=Lewisburg CSME: KT Controller
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family KT Redirection
pci:v00008086d0000A1BE*
- ID_MODEL_FROM_DATABASE=Lewisburg CSME: HECI #3
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family MEI Controller #3
+
+pci:v00008086d0000A1BEsv000015D9sd0000095D*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family MEI Controller #3 (X11SPM-TF)
pci:v00008086d0000A1C1*
- ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller
+ ID_MODEL_FROM_DATABASE=C621 Series Chipset LPC/eSPI Controller
pci:v00008086d0000A1C2*
- ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller
+ ID_MODEL_FROM_DATABASE=C622 Series Chipset LPC/eSPI Controller
+
+pci:v00008086d0000A1C2sv000015D9sd0000095D*
+ ID_MODEL_FROM_DATABASE=C622 Series Chipset LPC/eSPI Controller (X11SPM-TF)
pci:v00008086d0000A1C3*
- ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller
+ ID_MODEL_FROM_DATABASE=C624 Series Chipset LPC/eSPI Controller
pci:v00008086d0000A1C4*
- ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller
+ ID_MODEL_FROM_DATABASE=C625 Series Chipset LPC/eSPI Controller
pci:v00008086d0000A1C5*
- ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller
+ ID_MODEL_FROM_DATABASE=C626 Series Chipset LPC/eSPI Controller
pci:v00008086d0000A1C6*
- ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller
+ ID_MODEL_FROM_DATABASE=C627 Series Chipset LPC/eSPI Controller
pci:v00008086d0000A1C7*
- ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller
+ ID_MODEL_FROM_DATABASE=C628 Series Chipset LPC/eSPI Controller
pci:v00008086d0000A1D2*
- ID_MODEL_FROM_DATABASE=Lewisburg SSATA Controller [AHCI mode]
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family SSATA Controller [AHCI mode]
pci:v00008086d0000A1D6*
- ID_MODEL_FROM_DATABASE=Lewisburg SSATA Controller [RAID mode]
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family SSATA Controller [RAID mode]
pci:v00008086d0000A1E7*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #17
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #17
pci:v00008086d0000A1E8*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #18
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #18
pci:v00008086d0000A1E9*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #19
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #19
pci:v00008086d0000A1EA*
- ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #20
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family PCI Express Root Port #20
-pci:v00008086d0000A1F0*
- ID_MODEL_FROM_DATABASE=Lewisburg MROM 0
+pci:v00008086d0000A1EC*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family MROM 0
-pci:v00008086d0000A1F1*
- ID_MODEL_FROM_DATABASE=Lewisburg MROM 1
+pci:v00008086d0000A1ED*
+ ID_MODEL_FROM_DATABASE=C620 Series Chipset Family MROM 1
pci:v00008086d0000A1F8*
ID_MODEL_FROM_DATABASE=Lewisburg IE: HECI #1
@@ -86531,26 +88226,38 @@ pci:v00008086d0000A29E*
pci:v00008086d0000A29F*
ID_MODEL_FROM_DATABASE=200 Series PCH PCI Express Root Port #16
+pci:v00008086d0000A2A0*
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family P2SB
+
pci:v00008086d0000A2A1*
- ID_MODEL_FROM_DATABASE=200 Series PCH PMC
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family Power Management Controller
pci:v00008086d0000A2A3*
- ID_MODEL_FROM_DATABASE=200 Series PCH SMBus Controller
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family SMBus Controller
+
+pci:v00008086d0000A2A4*
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family SPI Controller
+
+pci:v00008086d0000A2A5*
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family Gigabit Ethernet Controller
+
+pci:v00008086d0000A2A6*
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family Trace Hub
pci:v00008086d0000A2A7*
- ID_MODEL_FROM_DATABASE=200 Series PCH Serial IO UART Controller #0
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family Serial IO UART Controller #0
pci:v00008086d0000A2A8*
- ID_MODEL_FROM_DATABASE=200 Series PCH Serial IO UART Controller #1
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family Serial IO UART Controller #1
pci:v00008086d0000A2A9*
- ID_MODEL_FROM_DATABASE=200 Series PCH Serial IO SPI Controller #0
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family Serial IO SPI Controller #0
pci:v00008086d0000A2AA*
- ID_MODEL_FROM_DATABASE=200 Series PCH Serial IO SPI Controller #1
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family Serial IO SPI Controller #1
pci:v00008086d0000A2AF*
- ID_MODEL_FROM_DATABASE=200 Series PCH USB 3.0 xHCI Controller
+ ID_MODEL_FROM_DATABASE=200 Series/Z370 Chipset Family USB 3.0 xHCI Controller
pci:v00008086d0000A2B1*
ID_MODEL_FROM_DATABASE=200 Series PCH Thermal Subsystem
@@ -86576,6 +88283,15 @@ pci:v00008086d0000A2C7*
pci:v00008086d0000A2C8*
ID_MODEL_FROM_DATABASE=200 Series PCH LPC Controller (B250)
+pci:v00008086d0000A2C9*
+ ID_MODEL_FROM_DATABASE=Z370 Chipset LPC/eSPI Controller
+
+pci:v00008086d0000A2D2*
+ ID_MODEL_FROM_DATABASE=X299 Chipset LPC/eSPI Controller
+
+pci:v00008086d0000A2D3*
+ ID_MODEL_FROM_DATABASE=C422 Chipset LPC/eSPI Controller
+
pci:v00008086d0000A2E0*
ID_MODEL_FROM_DATABASE=200 Series PCH Serial IO I2C Controller #0
@@ -86628,16 +88344,76 @@ pci:v00008086d0000A324*
ID_MODEL_FROM_DATABASE=Cannon Lake PCH SPI Controller
pci:v00008086d0000A32C*
- ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port 21
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #21
+
+pci:v00008086d0000A32D*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #22
+
+pci:v00008086d0000A32E*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #23
+
+pci:v00008086d0000A32F*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #24
pci:v00008086d0000A330*
- ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port 9
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #9
+
+pci:v00008086d0000A331*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #10
+
+pci:v00008086d0000A332*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #11
+
+pci:v00008086d0000A333*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #12
+
+pci:v00008086d0000A334*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #13
+
+pci:v00008086d0000A335*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #14
+
+pci:v00008086d0000A336*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #15
+
+pci:v00008086d0000A337*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #16
+
+pci:v00008086d0000A338*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #1
+
+pci:v00008086d0000A339*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #2
+
+pci:v00008086d0000A33A*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #3
+
+pci:v00008086d0000A33B*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #4
+
+pci:v00008086d0000A33C*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #5
+
+pci:v00008086d0000A33D*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #6
+
+pci:v00008086d0000A33E*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #7
+
+pci:v00008086d0000A33F*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #8
+
+pci:v00008086d0000A340*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #17
+
+pci:v00008086d0000A341*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #18
pci:v00008086d0000A342*
- ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port 19
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #19
pci:v00008086d0000A343*
- ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port 20
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #20
pci:v00008086d0000A348*
ID_MODEL_FROM_DATABASE=Cannon Lake PCH cAVS
@@ -86648,6 +88424,9 @@ pci:v00008086d0000A352*
pci:v00008086d0000A360*
ID_MODEL_FROM_DATABASE=Cannon Lake PCH HECI Controller
+pci:v00008086d0000A363*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH Active Management Technology - SOL
+
pci:v00008086d0000A36D*
ID_MODEL_FROM_DATABASE=Cannon Lake PCH USB 3.1 xHCI Host Controller
@@ -86699,6 +88478,9 @@ pci:v00008086d0000B555sv0000E4BFsd00001000*
pci:v00008086d0000D130*
ID_MODEL_FROM_DATABASE=Core Processor DMI
+pci:v00008086d0000D130sv000015D9sd00000605*
+ ID_MODEL_FROM_DATABASE=Core Processor DMI (X8SIL)
+
pci:v00008086d0000D131*
ID_MODEL_FROM_DATABASE=Core Processor DMI
@@ -86768,6 +88550,9 @@ pci:v00008086d0000D157*
pci:v00008086d0000D158*
ID_MODEL_FROM_DATABASE=Core Processor Miscellaneous Registers
+pci:v00008086d0000F1A5*
+ ID_MODEL_FROM_DATABASE=SSD 600P Series
+
pci:v00008086d0000F1A6*
ID_MODEL_FROM_DATABASE=SSD Pro 7600p/760p/E 6100p Series
@@ -87800,6 +89585,9 @@ pci:v00009005d0000028Fsv0000152Dsd00008A36*
pci:v00009005d0000028Fsv0000152Dsd00008A37*
ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (QS-8242-24i)
+pci:v00009005d0000028Fsv00009005sd00000608*
+ ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SmartRAID 3162-8i /e)
+
pci:v00009005d0000028Fsv00009005sd00000800*
ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SmartRAID 3154-8i)
@@ -88442,9 +90230,15 @@ pci:v0000BDBDd0000A143*
pci:v0000BDBDd0000A144*
ID_MODEL_FROM_DATABASE=DeckLink Mini Monitor 4K
+pci:v0000BDBDd0000A148*
+ ID_MODEL_FROM_DATABASE=DeckLink SDI Micro
+
pci:v0000BDBDd0000A14B*
ID_MODEL_FROM_DATABASE=DeckLink 8K Pro
+pci:v0000BDBDd0000A1FF*
+ ID_MODEL_FROM_DATABASE=eGPU RX580
+
pci:v0000C001*
ID_VENDOR_FROM_DATABASE=TSI Telsys
@@ -88484,6 +90278,9 @@ pci:v0000CAFEd00000003*
pci:v0000CAFEd00000006*
ID_MODEL_FROM_DATABASE=Luna PCI-e 3000 Hardware Security Module
+pci:v0000CC53*
+ ID_VENDOR_FROM_DATABASE=ScaleFlux Inc.
+
pci:v0000CCCC*
ID_VENDOR_FROM_DATABASE=Catapult Communications
diff --git a/hwdb/20-usb-vendor-model.hwdb b/hwdb/20-usb-vendor-model.hwdb
index 94ac8f2ff1..44e8b7875b 100644
--- a/hwdb/20-usb-vendor-model.hwdb
+++ b/hwdb/20-usb-vendor-model.hwdb
@@ -2291,6 +2291,9 @@ usb:v0403p8371*
usb:v0403p8372*
ID_MODEL_FROM_DATABASE=FT8U100AX Serial Port
+usb:v0403p87D0*
+ ID_MODEL_FROM_DATABASE=Cressi Dive Computer Interface
+
usb:v0403p8A28*
ID_MODEL_FROM_DATABASE=Rainforest Automation ZigBee Controller
@@ -2387,6 +2390,9 @@ usb:v0403pBCD9*
usb:v0403pBCDA*
ID_MODEL_FROM_DATABASE=Stellaris ICDI Board
+usb:v0403pBD90*
+ ID_MODEL_FROM_DATABASE=PICAXE Download Cable [AXE027]
+
usb:v0403pBDC8*
ID_MODEL_FROM_DATABASE=Egnite GmbH - JTAG/RS-232 adapter
@@ -3515,6 +3521,9 @@ usb:v0411p00DB*
usb:v0411p00E8*
ID_MODEL_FROM_DATABASE=WLI-UC-G300N Wireless LAN Adapter [Ralink RT2870]
+usb:v0411p00F9*
+ ID_MODEL_FROM_DATABASE=Portable DVD Writer (DVSM-PL58U2)
+
usb:v0411p0105*
ID_MODEL_FROM_DATABASE=External Hard Drive HD-CEU2 [Drive Station]
@@ -3554,12 +3563,18 @@ usb:v0411p01A1*
usb:v0411p01A2*
ID_MODEL_FROM_DATABASE=WLI-UC-GNM Wireless LAN Adapter [Ralink RT8070]
+usb:v0411p01BA*
+ ID_MODEL_FROM_DATABASE=SATA Bridge
+
usb:v0411p01DC*
ID_MODEL_FROM_DATABASE=Ultra-Slim Portable DVD Writer (DVSM-PC58U2V)
usb:v0411p01DE*
ID_MODEL_FROM_DATABASE=External Hard Drive HD-PCTU3 [Buffalo MiniStation]
+usb:v0411p01EA*
+ ID_MODEL_FROM_DATABASE=SATA Bridge
+
usb:v0411p01EE*
ID_MODEL_FROM_DATABASE=WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070]
@@ -3569,6 +3584,9 @@ usb:v0411p01F1*
usb:v0411p01FD*
ID_MODEL_FROM_DATABASE=WLI-UC-G450 Wireless LAN Adapter
+usb:v0411p027E*
+ ID_MODEL_FROM_DATABASE=HD-LCU3
+
usb:v0412*
ID_VENDOR_FROM_DATABASE=Award Software International
@@ -3896,6 +3914,9 @@ usb:v041Ep3220*
usb:v041Ep3232*
ID_MODEL_FROM_DATABASE=Sound Blaster Premium HD [SBX]
+usb:v041Ep3237*
+ ID_MODEL_FROM_DATABASE=SB X-Fi Surround 5.1 Pro
+
usb:v041Ep3F00*
ID_MODEL_FROM_DATABASE=E-Mu Xboard 25 MIDI Controller
@@ -5753,6 +5774,9 @@ usb:v0451p8142*
usb:v0451p926B*
ID_MODEL_FROM_DATABASE=TUSB9260 Boot Loader
+usb:v0451pBEF3*
+ ID_MODEL_FROM_DATABASE=CC1352R1 Launchpad
+
usb:v0451pDBC0*
ID_MODEL_FROM_DATABASE=Device Bay Controller
@@ -7088,6 +7112,9 @@ usb:v045Ep0799*
usb:v045Ep07A5*
ID_MODEL_FROM_DATABASE=Wireless Receiver 1461C
+usb:v045Ep07B2*
+ ID_MODEL_FROM_DATABASE=2.4GHz Transceiver v8.0 used by mouse Wireless Desktop 900
+
usb:v045Ep07B9*
ID_MODEL_FROM_DATABASE=Wired Keyboard 200
@@ -7133,6 +7160,9 @@ usb:v045Ep090C*
usb:v045Ep091A*
ID_MODEL_FROM_DATABASE=Hub
+usb:v045Ep09C0*
+ ID_MODEL_FROM_DATABASE=Surface Type Cover
+
usb:v045Ep0A00*
ID_MODEL_FROM_DATABASE=Lumia 950 Dual SIM (RM-1118)
@@ -7334,6 +7364,12 @@ usb:v0461p4D75*
usb:v0461p4D81*
ID_MODEL_FROM_DATABASE=Dell N889 Optical Mouse
+usb:v0461p4D91*
+ ID_MODEL_FROM_DATABASE=Laser mouse M-D16DL
+
+usb:v0461p4D92*
+ ID_MODEL_FROM_DATABASE=Optical mouse M-D17DR
+
usb:v0461p4DE3*
ID_MODEL_FROM_DATABASE=HP 5-Button Optical Comfort Mouse
@@ -7517,6 +7553,9 @@ usb:v046Dp0810*
usb:v046Dp0819*
ID_MODEL_FROM_DATABASE=Webcam C210
+usb:v046Dp081A*
+ ID_MODEL_FROM_DATABASE=Webcam C260
+
usb:v046Dp081B*
ID_MODEL_FROM_DATABASE=Webcam C310
@@ -8126,6 +8165,9 @@ usb:v046DpC07E*
usb:v046DpC083*
ID_MODEL_FROM_DATABASE=G403 Prodigy Gaming Mouse
+usb:v046DpC084*
+ ID_MODEL_FROM_DATABASE=G203 Gaming Mouse
+
usb:v046DpC101*
ID_MODEL_FROM_DATABASE=UltraX Media Remote
@@ -8414,6 +8456,9 @@ usb:v046DpC31D*
usb:v046DpC31F*
ID_MODEL_FROM_DATABASE=Comfort Keyboard K290
+usb:v046DpC326*
+ ID_MODEL_FROM_DATABASE=Washable Keyboard K310
+
usb:v046DpC328*
ID_MODEL_FROM_DATABASE=Corded Keyboard K280e
@@ -8423,6 +8468,9 @@ usb:v046DpC332*
usb:v046DpC335*
ID_MODEL_FROM_DATABASE=G910 Orion Spectrum Mechanical Keyboard
+usb:v046DpC33A*
+ ID_MODEL_FROM_DATABASE=G413 Gaming Keyboard
+
usb:v046DpC401*
ID_MODEL_FROM_DATABASE=TrackMan Marble Wheel
@@ -9416,6 +9464,9 @@ usb:v047FpAC01*
usb:v047FpAD01*
ID_MODEL_FROM_DATABASE=GameCom 777 5.1 Headset
+usb:v047FpAF01*
+ ID_MODEL_FROM_DATABASE=DA80
+
usb:v047FpC008*
ID_MODEL_FROM_DATABASE=Audio 655 DSP
@@ -9458,6 +9509,9 @@ usb:v0480pA00D*
usb:v0480pA100*
ID_MODEL_FROM_DATABASE=Canvio Alu 2TB 2.5" Black External Disk Model HDTH320EK3CA
+usb:v0480pA102*
+ ID_MODEL_FROM_DATABASE=Canvio Alu 2TB 2.5" Black External Disk Model HDTH320EK3CA
+
usb:v0480pA202*
ID_MODEL_FROM_DATABASE=Canvio Basics HDD
@@ -9539,6 +9593,12 @@ usb:v0483p0137*
usb:v0483p0138*
ID_MODEL_FROM_DATABASE=Unicorn II (ST70138B + MTC-20174TQ chipset)
+usb:v0483p0ADB*
+ ID_MODEL_FROM_DATABASE=Android Debug Bridge (ADB) device
+
+usb:v0483p0AFB*
+ ID_MODEL_FROM_DATABASE=Android Fastboot device
+
usb:v0483p1307*
ID_MODEL_FROM_DATABASE=Cytronix 6in1 Card Reader
@@ -9572,6 +9632,21 @@ usb:v0483p3748*
usb:v0483p374B*
ID_MODEL_FROM_DATABASE=ST-LINK/V2.1
+usb:v0483p374D*
+ ID_MODEL_FROM_DATABASE=STLINK-V3 Loader
+
+usb:v0483p374E*
+ ID_MODEL_FROM_DATABASE=STLINK-V3
+
+usb:v0483p374F*
+ ID_MODEL_FROM_DATABASE=STLINK-V3
+
+usb:v0483p3752*
+ ID_MODEL_FROM_DATABASE=ST-LINK/V2.1
+
+usb:v0483p3753*
+ ID_MODEL_FROM_DATABASE=STLINK-V3
+
usb:v0483p4810*
ID_MODEL_FROM_DATABASE=ISDN adapter
@@ -9591,7 +9666,7 @@ usb:v0483p5720*
ID_MODEL_FROM_DATABASE=Mass Storage Device
usb:v0483p5721*
- ID_MODEL_FROM_DATABASE=Hantek DDS-3X25 Arbitrary Waveform Generator
+ ID_MODEL_FROM_DATABASE=Interrupt Demo
usb:v0483p5722*
ID_MODEL_FROM_DATABASE=Bulk Demo
@@ -11081,6 +11156,9 @@ usb:v04A9p178A*
usb:v04A9p178D*
ID_MODEL_FROM_DATABASE=PIXMA MG6853
+usb:v04A9p180B*
+ ID_MODEL_FROM_DATABASE=PIXMA MG3000 series
+
usb:v04A9p1900*
ID_MODEL_FROM_DATABASE=CanoScan LiDE 90
@@ -11474,6 +11552,9 @@ usb:v04A9p26B4*
usb:v04A9p26B5*
ID_MODEL_FROM_DATABASE=MF4200 series
+usb:v04A9p26B6*
+ ID_MODEL_FROM_DATABASE=FAX-L140/L130
+
usb:v04A9p26DA*
ID_MODEL_FROM_DATABASE=LBP3010B printer
@@ -12395,9 +12476,18 @@ usb:v04A9p329D*
usb:v04A9p329F*
ID_MODEL_FROM_DATABASE=PowerShot SX530 HS
+usb:v04A9p32A0*
+ ID_MODEL_FROM_DATABASE=EOS M10
+
usb:v04A9p32A6*
ID_MODEL_FROM_DATABASE=PowerShot SX710 HS
+usb:v04A9p32A7*
+ ID_MODEL_FROM_DATABASE=PowerShot SX610 HS
+
+usb:v04A9p32A8*
+ ID_MODEL_FROM_DATABASE=PowerShot G3 X
+
usb:v04A9p32AA*
ID_MODEL_FROM_DATABASE=Powershot ELPH 160 / IXUS 160
@@ -12431,6 +12521,21 @@ usb:v04A9p32C1*
usb:v04A9p32C2*
ID_MODEL_FROM_DATABASE=PowerShot SX720 HS
+usb:v04A9p32C5*
+ ID_MODEL_FROM_DATABASE=EOS M6
+
+usb:v04A9p32CC*
+ ID_MODEL_FROM_DATABASE=EOS 200D
+
+usb:v04A9p32D1*
+ ID_MODEL_FROM_DATABASE=EOS M100
+
+usb:v04A9p32D2*
+ ID_MODEL_FROM_DATABASE=EOS M50
+
+usb:v04A9p32D4*
+ ID_MODEL_FROM_DATABASE=Powershot ELPH 185 / IXUS 185 / IXY 200
+
usb:v04A9p32D5*
ID_MODEL_FROM_DATABASE=PowerShot SX430 IS
@@ -13811,6 +13916,9 @@ usb:v04C5p10E7*
usb:v04C5p10FE*
ID_MODEL_FROM_DATABASE=S500
+usb:v04C5p1104*
+ ID_MODEL_FROM_DATABASE=KD02906 Line Thermal Printer
+
usb:v04C5p1150*
ID_MODEL_FROM_DATABASE=fi-6230
@@ -14366,6 +14474,9 @@ usb:v04D8p900A*
usb:v04D8p9012*
ID_MODEL_FROM_DATABASE=PICkit4
+usb:v04D8p9015*
+ ID_MODEL_FROM_DATABASE=ICD 4 In-Circuit Debugger
+
usb:v04D8pC001*
ID_MODEL_FROM_DATABASE=PicoLCD 20x4
@@ -14387,6 +14498,9 @@ usb:v04D8pF437*
usb:v04D8pF4B5*
ID_MODEL_FROM_DATABASE=SmartScope
+usb:v04D8pF5FE*
+ ID_MODEL_FROM_DATABASE=TrueRNG
+
usb:v04D8pF8DA*
ID_MODEL_FROM_DATABASE=Hughski Ltd. ColorHug
@@ -14480,6 +14594,9 @@ usb:v04D9p2832*
usb:v04D9p2834*
ID_MODEL_FROM_DATABASE=HT82A834R Audio MCU
+usb:v04D9p4545*
+ ID_MODEL_FROM_DATABASE=Keyboard [Diatec Majestouch 2 Tenkeyless]
+
usb:v04D9pA01C*
ID_MODEL_FROM_DATABASE=wireless multimedia keyboard with trackball [Trust ADURA 17911]
@@ -14537,6 +14654,9 @@ usb:v04DAp0D0D*
usb:v04DAp0D0E*
ID_MODEL_FROM_DATABASE=DVD-ROM & CD-R/RW
+usb:v04DAp0D14*
+ ID_MODEL_FROM_DATABASE=DVD-RAM MLT08
+
usb:v04DAp0F07*
ID_MODEL_FROM_DATABASE=KX-MB2030 Multifunction Laser Printer
@@ -15648,10 +15768,10 @@ usb:v04E8p685E*
ID_MODEL_FROM_DATABASE=GT-I9100 / GT-C3350 Phones (USB Debugging mode)
usb:v04E8p6860*
- ID_MODEL_FROM_DATABASE=Galaxy (MTP)
+ ID_MODEL_FROM_DATABASE=Galaxy series, misc. (MTP mode)
usb:v04E8p6863*
- ID_MODEL_FROM_DATABASE=GT-I9500 [Galaxy S4] / GT-I9250 [Galaxy Nexus] (network tethering)
+ ID_MODEL_FROM_DATABASE=Galaxy series, misc. (tethering mode)
usb:v04E8p6864*
ID_MODEL_FROM_DATABASE=GT-I9070 (network tethering, USB debugging enabled)
@@ -16070,12 +16190,18 @@ usb:v04F2pB40E*
usb:v04F2pB444*
ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam
+usb:v04F2pB563*
+ ID_MODEL_FROM_DATABASE=Integrated Camera
+
usb:v04F2pB5CE*
ID_MODEL_FROM_DATABASE=Integrated Camera
usb:v04F2pB5CF*
ID_MODEL_FROM_DATABASE=Integrated IR Camera
+usb:v04F2pB5DB*
+ ID_MODEL_FROM_DATABASE=HP Webcam
+
usb:v04F3*
ID_VENDOR_FROM_DATABASE=Elan Microelectronics Corp.
@@ -17801,6 +17927,9 @@ usb:v04FDp0003*
usb:v04FE*
ID_VENDOR_FROM_DATABASE=PFU, Ltd
+usb:v04FEp0006*
+ ID_MODEL_FROM_DATABASE=Happy Hacking Keyboard Lite2
+
usb:v04FF*
ID_VENDOR_FROM_DATABASE=E-CMOS Corp.
@@ -18074,6 +18203,9 @@ usb:v050Dp0304*
usb:v050Dp0307*
ID_MODEL_FROM_DATABASE=USB 2.0 - 7 ports Hub [FSU307]
+usb:v050Dp038C*
+ ID_MODEL_FROM_DATABASE=F2CU038 HDMI Adapter
+
usb:v050Dp0409*
ID_MODEL_FROM_DATABASE=F5U409 Serial
@@ -19394,6 +19526,9 @@ usb:v054Cp082F*
usb:v054Cp0847*
ID_MODEL_FROM_DATABASE=WG-C10 Portable Wireless Server
+usb:v054Cp0884*
+ ID_MODEL_FROM_DATABASE=MDR-ZX770BN [Wireless Noise Canceling Stereo Headset]
+
usb:v054Cp088C*
ID_MODEL_FROM_DATABASE=Portable Headphone Amplifier
@@ -19883,9 +20018,15 @@ usb:v0566p3002*
usb:v0566p3004*
ID_MODEL_FROM_DATABASE=Genius KB-29E
+usb:v0566p3027*
+ ID_MODEL_FROM_DATABASE=Sun-Flex ProTouch
+
usb:v0566p3107*
ID_MODEL_FROM_DATABASE=Keyboard
+usb:v0566p3132*
+ ID_MODEL_FROM_DATABASE=Optical mouse M-DY4DR / M-DY6DR
+
usb:v0566p4006*
ID_MODEL_FROM_DATABASE=FID 638 Mouse (Sun Microsystems)
@@ -20439,37 +20580,46 @@ usb:v056Ep0002*
ID_MODEL_FROM_DATABASE=29UO Mouse
usb:v056Ep0057*
- ID_MODEL_FROM_DATABASE=M-PGDL Mouse
+ ID_MODEL_FROM_DATABASE=Micro Grast Pop M-PGDL
usb:v056Ep005C*
- ID_MODEL_FROM_DATABASE=M-PGDL Mouse
+ ID_MODEL_FROM_DATABASE=Micro Grast Pop M-PG2DL
usb:v056Ep005D*
- ID_MODEL_FROM_DATABASE=M-FGDL Mouse
+ ID_MODEL_FROM_DATABASE=Micro Grast Fit M-FGDL
usb:v056Ep005E*
- ID_MODEL_FROM_DATABASE=M-FG2DL Mouse
+ ID_MODEL_FROM_DATABASE=Micro Grast Fit M-FG2DL
usb:v056Ep0062*
- ID_MODEL_FROM_DATABASE=M-D18DR Mouse
+ ID_MODEL_FROM_DATABASE=Optical mouse M-D18DR
usb:v056Ep0063*
- ID_MODEL_FROM_DATABASE=M-SODL Mouse
+ ID_MODEL_FROM_DATABASE=Laser mouse M-SODL
usb:v056Ep0069*
- ID_MODEL_FROM_DATABASE=M-GE1UL Mouse
+ ID_MODEL_FROM_DATABASE=Laser mouse M-GE1UL
usb:v056Ep0071*
- ID_MODEL_FROM_DATABASE=M-GE3DL Mouse
+ ID_MODEL_FROM_DATABASE=Laser mouse M-GE3DL
usb:v056Ep0072*
- ID_MODEL_FROM_DATABASE=M-LS6UL Mouse
+ ID_MODEL_FROM_DATABASE=Laser mouse M-LS6UL
usb:v056Ep0073*
- ID_MODEL_FROM_DATABASE=M-LS7UL Mouse
+ ID_MODEL_FROM_DATABASE=Laser mouse M-LS7UL
usb:v056Ep0074*
- ID_MODEL_FROM_DATABASE=M-FW1UL Mouse
+ ID_MODEL_FROM_DATABASE=Optical mouse M-FW1UL
+
+usb:v056Ep0075*
+ ID_MODEL_FROM_DATABASE=M-FW2DL Mouse
+
+usb:v056Ep2003*
+ ID_MODEL_FROM_DATABASE=JC-U3613M
+
+usb:v056Ep2004*
+ ID_MODEL_FROM_DATABASE=JC-U3613M
usb:v056Ep200C*
ID_MODEL_FROM_DATABASE=LD-USB/TX
@@ -54707,6 +54857,12 @@ usb:v1D57pAF01*
usb:v1D5B*
ID_VENDOR_FROM_DATABASE=Smartronix, Inc.
+usb:v1D5C*
+ ID_VENDOR_FROM_DATABASE=Fresco Logic
+
+usb:v1D5Cp2000*
+ ID_MODEL_FROM_DATABASE=FL2000/FL2000DX VGA/DVI/HDMI Adapter
+
usb:v1D6B*
ID_VENDOR_FROM_DATABASE=Linux Foundation
@@ -55097,6 +55253,9 @@ usb:v1FC9p0003*
usb:v1FC9p010B*
ID_MODEL_FROM_DATABASE=PR533
+usb:v1FC9p012B*
+ ID_MODEL_FROM_DATABASE=i.MX 8M Dual/8M QuadLite/8M Quad Serial Downloader
+
usb:v1FDE*
ID_VENDOR_FROM_DATABASE=ILX Lightwave Corporation
@@ -55808,6 +55967,63 @@ usb:v21D6*
usb:v21D6p0002*
ID_MODEL_FROM_DATABASE=Seismic recorder [Tellus]
+usb:v2207*
+ ID_VENDOR_FROM_DATABASE=Fuzhou Rockchip Electronics Company
+
+usb:v2207p0010*
+ ID_MODEL_FROM_DATABASE=GoClever Tab R83
+
+usb:v2207p0011*
+ ID_MODEL_FROM_DATABASE=SmartTab
+
+usb:v2207p281A*
+ ID_MODEL_FROM_DATABASE=RK2818 in Mask ROM mode
+
+usb:v2207p290A*
+ ID_MODEL_FROM_DATABASE=RK2918 in Mask ROM mode
+
+usb:v2207p292A*
+ ID_MODEL_FROM_DATABASE=RK2928 in Mask ROM mode
+
+usb:v2207p292C*
+ ID_MODEL_FROM_DATABASE=RK3026 in Mask ROM mode
+
+usb:v2207p300A*
+ ID_MODEL_FROM_DATABASE=RK3066 in Mask ROM mode
+
+usb:v2207p300B*
+ ID_MODEL_FROM_DATABASE=RK3168 in Mask ROM mode
+
+usb:v2207p301A*
+ ID_MODEL_FROM_DATABASE=RK3036 in Mask ROM mode
+
+usb:v2207p310A*
+ ID_MODEL_FROM_DATABASE=RK3066B in Mask ROM mode
+
+usb:v2207p310B*
+ ID_MODEL_FROM_DATABASE=RK3188 in Mask ROM mode
+
+usb:v2207p310C*
+ ID_MODEL_FROM_DATABASE=RK3126/RK3128 in Mask ROM mode
+
+usb:v2207p310D*
+ ID_MODEL_FROM_DATABASE=RK3126 in Mask ROM mode
+
+usb:v2207p320A*
+ ID_MODEL_FROM_DATABASE=RK3288 in Mask ROM mode
+
+usb:v2207p320B*
+ ID_MODEL_FROM_DATABASE=RK3228/RK3229 in Mask ROM mode
+
+usb:v2207p320C*
+ ID_MODEL_FROM_DATABASE=RK3328 in Mask ROM mode
+
+usb:v2207p330A*
+ ID_MODEL_FROM_DATABASE=RK3368 in Mask ROM mode
+
+usb:v2207p330C*
+ ID_MODEL_FROM_DATABASE=RK3399 in Mask ROM mode
+
usb:v2222*
ID_VENDOR_FROM_DATABASE=MacAlly
@@ -56462,6 +56678,15 @@ usb:v2516p0047*
usb:v2516p9494*
ID_MODEL_FROM_DATABASE=Sirus Headset
+usb:v2548*
+ ID_VENDOR_FROM_DATABASE=Pulse-Eight
+
+usb:v2548p1001*
+ ID_MODEL_FROM_DATABASE=CEC Adapter
+
+usb:v2548p1002*
+ ID_MODEL_FROM_DATABASE=CEC Adapter
+
usb:v2632*
ID_VENDOR_FROM_DATABASE=TwinMOS
@@ -56801,6 +57026,21 @@ usb:v289Bp0500*
usb:v289Bp0502*
ID_MODEL_FROM_DATABASE=Precision barometer
+usb:v28DE*
+ ID_VENDOR_FROM_DATABASE=Valve Software
+
+usb:v28DEp1102*
+ ID_MODEL_FROM_DATABASE=Wired Controller
+
+usb:v28DEp1142*
+ ID_MODEL_FROM_DATABASE=Wireless Steam Controller
+
+usb:v28DEp2000*
+ ID_MODEL_FROM_DATABASE=Lighthouse FPGA RX
+
+usb:v28DEp2101*
+ ID_MODEL_FROM_DATABASE=Watchman Dongle
+
usb:v2931*
ID_VENDOR_FROM_DATABASE=Jolla Oy
@@ -56933,6 +57173,33 @@ usb:v2A45p200C*
usb:v2A45p2012*
ID_MODEL_FROM_DATABASE=MX Phone (MTP & ACM)
+usb:v2AC7*
+ ID_VENDOR_FROM_DATABASE=Ultrahaptics Ltd.
+
+usb:v2AC7p0101*
+ ID_MODEL_FROM_DATABASE=Evaluation Kit [Dragonfly]
+
+usb:v2AC7p0102*
+ ID_MODEL_FROM_DATABASE=UHDK5
+
+usb:v2AC7p0104*
+ ID_MODEL_FROM_DATABASE=Touchbase
+
+usb:v2AC7p0110*
+ ID_MODEL_FROM_DATABASE=STRATOS Explore
+
+usb:v2AC7p0111*
+ ID_MODEL_FROM_DATABASE=STRATOS Explore DFU
+
+usb:v2AC7p0112*
+ ID_MODEL_FROM_DATABASE=STRATOS Inspire
+
+usb:v2AC7p0113*
+ ID_MODEL_FROM_DATABASE=STRATOS Inspire DFU
+
+usb:v2AC7pFFFF*
+ ID_MODEL_FROM_DATABASE=DFU
+
usb:v2B24*
ID_VENDOR_FROM_DATABASE=KeepKey LLC
@@ -56984,6 +57251,12 @@ usb:v2DCFpC952*
usb:v2FB2*
ID_VENDOR_FROM_DATABASE=Fujitsu, Ltd
+usb:v3016*
+ ID_VENDOR_FROM_DATABASE=Boundary Devices, LLC
+
+usb:v3016p0001*
+ ID_MODEL_FROM_DATABASE=Nitrogen Bootloader
+
usb:v3125*
ID_VENDOR_FROM_DATABASE=Eagletron
diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb
index ab4b3068e6..dcabb18d3e 100644
--- a/hwdb/60-evdev.hwdb
+++ b/hwdb/60-evdev.hwdb
@@ -40,10 +40,19 @@
# Sort by brand, model
#########################################
+# AIPTEK
+#########################################
+
+# Hyperpen 12000U
+evdev:input:b0003v08CAp0010*
+ EVDEV_ABS_00=::20
+ EVDEV_ABS_01=::20
+
+#########################################
# Apple
#########################################
-# Macbook2,1 (late 2006), single-button touchpad
+# Macbook2,1 (late 2006), single-button touchpad
evdev:input:b0003v05ACp021B*
# Macbook4,1
evdev:input:b0003v05ACp0229*
@@ -243,6 +252,13 @@ evdev:name:Atmel maXTouch Touch*:dmi:bvn*:bvr*:bd*:svnGOOGLE:pnSamus*
# HP
#########################################
+# HP Chromebook 14 (Falco)
+evdev:name:Cypress APA Trackpad ?cyapa?:dmi:*:svnHewlett-Packard*:pnFalco*:
+ EVDEV_ABS_00=:::8
+ EVDEV_ABS_01=:::8
+ EVDEV_ABS_35=:::8
+ EVDEV_ABS_36=:::8
+
# HP Pavilion dm4
evdev:name:SynPS/2 Synaptics TouchPad*:dmi:*svnHewlett-Packard:pnHPPaviliondm4*
EVDEV_ABS_00=1360:5563:47
@@ -250,6 +266,13 @@ evdev:name:SynPS/2 Synaptics TouchPad*:dmi:*svnHewlett-Packard:pnHPPaviliondm4*
EVDEV_ABS_35=1360:5563:47
EVDEV_ABS_36=1269:4618:61
+# HP Pavilion g6
+evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnHewlett-Packard:pnHPPaviliong6*
+ EVDEV_ABS_00=1284:5696:88
+ EVDEV_ABS_01=1287:4838:39
+ EVDEV_ABS_35=1284:5696:88
+ EVDEV_ABS_36=1287:4838:39
+
# HP Pavilion dv7
evdev:name:SynPS/2 Synaptics TouchPad*:dmi:*svnHewlett-Packard:pnHPPaviliondv7*
EVDEV_ABS_00=1068:5805:44
@@ -265,7 +288,7 @@ evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnHP:pnHPLaptop15-bs0xx:*
EVDEV_ABS_36=1029:4916:78
# HP Spectre
-evdev:name:SynPS/2 Synaptics TouchPad:dmi:i*svnHP:pnHPSpectreNotebook*
+evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnHP:pnHPSpectreNotebook*
EVDEV_ABS_00=1205:5691:47
EVDEV_ABS_01=1083:4808:65
EVDEV_ABS_35=1205:5691:47
@@ -311,6 +334,13 @@ evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pvrThinkPad??40?:*
EVDEV_ABS_35=::41
EVDEV_ABS_36=::37
+# Lenovo ThinkPad X140e
+evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pvrThinkPadX140e*
+ EVDEV_ABS_00=1176:5767:62
+ EVDEV_ABS_01=416:5534:160
+ EVDEV_ABS_35=1176:5767:62
+ EVDEV_ABS_36=416:5534:160
+
# Lenovo ThinkPad T430
evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pvrThinkPadT430*
EVDEV_ABS_00=1250:5631:58
@@ -417,7 +447,7 @@ evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pn*02173BG*:*pvrThinkPadEd
#########################################
# Razer Blade Stealth
-evdev:name:1A586753:00 06CB:8323 Touchpad:dmi:*svnRazer:pnBladeStealth:*
+evdev:name:1A58675*:00 06CB:8323 Touchpad:dmi:*svnRazer:pnBladeStealth:*
EVDEV_ABS_00=::12:8
EVDEV_ABS_01=::11:8
EVDEV_ABS_35=::12:8
@@ -476,6 +506,15 @@ evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnTOSHIBA:pnSATELLITER830*
EVDEV_ABS_36=1045:4826:76
#########################################
+# UGTablet
+#########################################
+
+# Trust Flex Graphics Tablet
+evdev:input:b0003v2179p0004*
+ EVDEV_ABS_00=::234
+ EVDEV_ABS_01=::328
+
+#########################################
# Waltop
#########################################
@@ -483,3 +522,8 @@ evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnTOSHIBA:pnSATELLITER830*
evdev:input:b0003v172Fp0031*
EVDEV_ABS_00=0:10000:400
EVDEV_ABS_01=0:6250:400
+
+#WALTOP International Corp. Graphics Tablet
+evdev:input:b0003v172Fp0047*
+ EVDEV_ABS_00=0:20000:80
+ EVDEV_ABS_01=0:12500:80
diff --git a/hwdb/60-input-id.hwdb b/hwdb/60-input-id.hwdb
index b05b402d74..7e9dc99220 100644
--- a/hwdb/60-input-id.hwdb
+++ b/hwdb/60-input-id.hwdb
@@ -62,3 +62,7 @@
id-input:modalias:input:b0003v5543p0081*
ID_INPUT_TABLET=1
ID_INPUT_TABLET_PAD=1
+
+# XP-PEN STAR 06
+id-input:modalias:input:b0003v28bdp0078*
+ ID_INPUT_TABLET=1
diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb
index ae3ec3ca15..0d9c31678b 100644
--- a/hwdb/60-keyboard.hwdb
+++ b/hwdb/60-keyboard.hwdb
@@ -98,7 +98,8 @@
# common keys
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn*
-evdev:atkbd:dmi:bvn*:bvr*:bd*:svnGateway*:pnA0A1*:pvr*
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnGateway*:pn*
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnPackard*Bell*:pn*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svneMachines:pneMachines*E725:pvr*
KEYBOARD_KEY_86=wlan # Fn+F3 or Fn+Q for comunication key
KEYBOARD_KEY_a5=help # Fn+F1
@@ -162,8 +163,9 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMate*C3[01]0*:pvr*
KEYBOARD_KEY_6b=fn
KEYBOARD_KEY_6c=screenlock # FIXME: lock tablet device/buttons
-# Travelmate P648-G2-MG and P645-S
+# Travelmate P648-G2-MG, P648-G3-M and P645-S
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMate*P648-G2-MG*:pvr*
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMate*P648-G3-M*:pvr*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMate*P645-S*:pvr*
KEYBOARD_KEY_8a=f20 # Microphone mute button; should be micmute
@@ -181,10 +183,6 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*1640:*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAOA*:pvr*
KEYBOARD_KEY_a9=!switchvideomode # Fn+F5
-# Easynote models
-evdev:atkbd:dmi:bvn*:bvr*:bd*:svnPackard*Bell*:pnEasynote*:pvr*
- KEYBOARD_KEY_86=wlan # Fn+F3 or Fn+Q for comunication key
-
###########################################################
# Alienware
###########################################################
@@ -220,6 +218,16 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svn*BenQ*:pn*Joybook*R22*:pvr*
KEYBOARD_KEY_6e=wlan
###########################################################
+# Clevo
+###########################################################
+
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnNotebook:pnW65_67SZ:pvr*
+ KEYBOARD_KEY_a0=!mute
+ KEYBOARD_KEY_a2=!playpause
+ KEYBOARD_KEY_ae=!volumedown
+ KEYBOARD_KEY_b0=!volumeup
+
+###########################################################
# Compal
###########################################################
@@ -264,7 +272,7 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pn*
KEYBOARD_KEY_85=brightnessdown # Fn+Down Brightness Down
KEYBOARD_KEY_86=brightnessup # Fn+Up Brightness Up
KEYBOARD_KEY_87=battery # Fn+F3 battery icon
- KEYBOARD_KEY_88=!wlan # Fn+(F2|PrtScr|Home) Turn On/Off Wireless
+ KEYBOARD_KEY_88=unknown # Fn+F2 Turn On/Off Wireless - handled in hardware
KEYBOARD_KEY_89=ejectclosecd # Fn+F10 Eject CD
KEYBOARD_KEY_8a=suspend # Fn+F1 hibernate
KEYBOARD_KEY_8b=switchvideomode # Fn+F8 CRT/LCD (high keycode: "displaytoggle")
@@ -300,6 +308,10 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnLatitude*2110:pvr*
KEYBOARD_KEY_85=unknown # Brightness Down, also emitted by acpi-video, ignore
KEYBOARD_KEY_86=unknown # Brightness Up, also emitted by acpi-video, ignore
+# Dell Inspiron 537*
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnInspiron537*:pvr*
+ KEYBOARD_KEY_88=!wlan # Fn-PrtScr rfkill
+
# Latitude XT2
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnLatitude*XT2:pvr*
KEYBOARD_KEY_9b=up # tablet rocker up
@@ -566,6 +578,16 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook*:pvr*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPZBook*:pvr*
KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute
+# HP ZBook 15 G2
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook15G2:pvr*
+ KEYBOARD_KEY_f8=wlan # Wireless HW switch button
+
+# HP ProBook 11 G1
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPProBook11G1:pvr*
+ KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute
+ KEYBOARD_KEY_d8=f21 # touchpad toggle
+ KEYBOARD_KEY_d9=f21 # touchpad toggle
+
# HP ZBook Studio G4
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pnHPZBookStudioG4:pvr*
KEYBOARD_KEY_f8=wlan # Wireless HW switch button
@@ -576,6 +598,10 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBookFolio1040G2:pvr*
KEYBOARD_KEY_d8=!f23 # touchpad off
KEYBOARD_KEY_d9=!f22 # touchpad on
+# HP ProBook 650
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*ProBook*650*:pvr*
+ KEYBOARD_KEY_f8=wlan # Wireless HW switch button
+
# HP ProBook 6555b
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard:pnHPProBook6555b:*
KEYBOARD_KEY_b2=www # Earth
@@ -587,6 +613,11 @@ evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*ProBook*640*G2*
KEYBOARD_KEY_85=unknown # lid close; also reported via special evdev
KEYBOARD_KEY_f8=unknown # rf kill; also reported via special evdev
+# HP ProBook 645 G4
+evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*ProBook*645*G4*
+ KEYBOARD_KEY_73=slash # Slash key
+ KEYBOARD_KEY_f8=wlan # Wireless HW switch button
+
###########################################################
# IBM
###########################################################
@@ -652,6 +683,7 @@ evdev:name:ThinkPad Extra Buttons:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*
KEYBOARD_KEY_16=mute
KEYBOARD_KEY_17=prog1
KEYBOARD_KEY_1a=f20 # Microphone mute button; should be micmute
+ KEYBOARD_KEY_45=bookmarks
# ThinkPad Keyboard with TrackPoint
evdev:input:b0003v17EFp6009*
@@ -742,9 +774,9 @@ evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*IdeaPad*Z370*:pvr*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*Lenovo*V480*:pvr*
KEYBOARD_KEY_f1=f21
-# Lenovo Thinkcentre M800z AIO machine
+# Lenovo ThinkCentre M800z/M820z/M920z AIO machines
# key_scancode 00 is KEY_MICMUTE
-keyboard:name:Microphone Mute Button:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*
+evdev:name:Microphone Mute Button:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*
KEYBOARD_KEY_00=f20
# enhanced USB keyboard
@@ -903,6 +935,12 @@ evdev:input:b0003v045Ep00DB*
KEYBOARD_KEY_c022d=up # zoomin
KEYBOARD_KEY_c022e=down # zoomout
+# Microsoft (Razer produced) Reclusa keyboard
+evdev:input:b0003v1532p0200*
+ KEYBOARD_KEY_c01c9=shuffle
+ KEYBOARD_KEY_c01ca=up # zoomin
+ KEYBOARD_KEY_c01cb=down # zoomout
+
###########################################################
# Micro Star
###########################################################
@@ -944,6 +982,10 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMicro-Star*:pn*PR200*:pvr*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMICRO-STAR*:pnU90/U100:*
KEYBOARD_KEY_e4=reserved
+# MSI GS65 Stealth Thin has a physical backslash key next to the space bar
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMicro-Star*:pnGS65StealthThin*:pvr*
+ KEYBOARD_KEY_56=backslash
+
###########################################################
# MSI
###########################################################
diff --git a/hwdb/60-sensor.hwdb b/hwdb/60-sensor.hwdb
index 36d13e68ca..5bce467a24 100644
--- a/hwdb/60-sensor.hwdb
+++ b/hwdb/60-sensor.hwdb
@@ -11,6 +11,17 @@
# Match string formats:
# sensor:modalias:<parent device modalias>:dmi:<dmi string>
#
+# The device modalias can be seen in the `modalias` file
+# of the sensor parent, for example:
+# cat /sys/`udevadm info -q path -n /dev/iio:device0`/../modalias
+#
+# The full DMI string of the running machine can be read from
+# /sys/class/dmi/id/modalias
+# That requires a kernel built with CONFIG_DMIID set, which is common.
+# The full DMI string is not needed here and the meaning of individual parts
+# can be seen in the source of the DMIID kernel module
+# https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/firmware/dmi-id.c
+#
# To add local entries, create a new file
# /etc/udev/hwdb.d/61-sensor-local.hwdb
# and add your rules there. To load the new rules execute (as root):
@@ -51,12 +62,18 @@
#########################################
# Acer
#########################################
+sensor:modalias:acpi:INVN6500*:dmi:*svn*Acer*:*pn*AspireSW5-011*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+
sensor:modalias:acpi:INVN6500*:dmi:*svn*Acer*:*pn*AspireSW5-012*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
sensor:modalias:acpi:BMA250E*:dmi:*:svnAcer:pnIconiaW1-810:*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+sensor:modalias:acpi:KIOX0009*:dmi:*:svnAcer:pnOneS1003:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
#########################################
# Archos
#########################################
@@ -69,12 +86,21 @@ sensor:modalias:acpi:SMO8500*:dmi:*:svnARCHOS:pnARCHOS80Cesium:*
sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:*pnT100CHI*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+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
@@ -90,6 +116,9 @@ sensor:modalias:acpi:SMO8500*:dmi:*svn*ASUSTeK*:*pn*TP500LB*
sensor:modalias:acpi:SMO8500*:dmi:*svn*ASUSTeK*:*pn*TP300LD*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+sensor:modalias:acpi:BOSC0200*:dmi:*svn*ASUSTeK*:*pn*TP412UA*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, 1
+
#########################################
# Axxo
#########################################
@@ -108,7 +137,11 @@ sensor:modalias:acpi:BMA250E*:dmi:bvnINSYDECorp.:bvrCHUWI.D86JLBNR*:svnInsyde:pn
sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo:pnD2D3_Vi8A1:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
-# Chuwi Hi8 Pro
+# Chuwi Vi10 (CWI505)
+sensor:modalias:acpi:BMA250E*:dmi:bvnINSYDECorp.:bvrG1D_S165*:svnilife:pnS165:*
+ ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+
+# Chuwi Hi8 Pro (CWI513)
sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo:pnX1D3_C806N:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
@@ -132,6 +165,18 @@ sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/07/201
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/28/2016:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnHampoo:rnCherryTrailCR:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+# Chuwi CoreBook
+# Chuwi CoreBook does not have its product name filled, so we
+# match the entire dmi-alias
+sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrY13D_KB133.103:bd06/01/2018:svnHampoo:pnDefaultstring:pvrV100:rvnHampoo:rnY13D_KB133:rvrV100:cvnDefaultstring:ct9:cvrDefaultstring:
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+
+#########################################
+# Connect
+#########################################
+sensor:modalias:acpi:KIOX000A*:dmi:*:svnConnect:pnTablet9:*
+ ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+
#########################################
# Cube
#########################################
@@ -152,6 +197,10 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni16:*
sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni8-L:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+# Cube iWork 10 Flagship
+sensor:modalias:acpi:BOSC0200*:dmi:*:svnCube:pnI15-TC:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
#########################################
# Cytrix (Mytrix)
#########################################
@@ -159,6 +208,12 @@ sensor:modalias:acpi:*KIOX000A*:dmi:*svn*CytrixTechnology:*pn*Complex11t*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
#########################################
+# DEXP
+#########################################
+sensor:modalias:acpi:SMO8500*:dmi:*svn*DEXP*:*pn*DEXPOEM*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+
+#########################################
# Endless
#########################################
sensor:modalias:acpi:ACCE0001*:dmi:*svnEndless*:*pnELT-NL3*
@@ -179,14 +234,22 @@ sensor:modalias:acpi:KIOX000A*:dmi:bvnINSYDECorp.:bvrBYT70A.YNCHENG.WIN.007:*:sv
#########################################
# HP
#########################################
-sensor:modalias:platform:lis3lv02d:dmi:*svn*Hewlett-Packard*:*pn*HPEliteBook8540w*
-sensor:modalias:platform:lis3lv02d:dmi:*svn*Hewlett-Packard*:*pn*HPEliteBook8560w*
+
+# Laptops using the lis3lv02d device should have a first quirk applied
+# to them in the drivers/platform/x86/hp_accel.c in the kernel. The
+# quirk from "can play neverball" to "matches Windows 8 orientation"
+# is then applied below.
+sensor:modalias:platform:lis3lv02d:dmi:*svn*Hewlett-Packard*:*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, 0, -1; 0, 1, 0
sensor:modalias:acpi:SMO8500*:dmi:*:svnHewlett-Packard:pnHPStream7Tablet:*
sensor:modalias:acpi:SMO8500*:dmi:*:svnHewlett-Packard:pnHPStream8Tablet:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+# HP Pavillion X2 10-n000nd
+sensor:modalias:i2c:bmc150_accel:dmi:*:svnHewlett-Packard:pnHPPavilionx2Detachable:*:rn815D:*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+
#########################################
# I.T.Works
#########################################
@@ -247,9 +310,14 @@ sensor:modalias:acpi:BOSC0200:BOSC0200:dmi:*ThinkPadYoga11e3rdGen*
sensor:modalias:acpi:BMA250E*:dmi:bvnLENOVO:*:pvrLenovoMIIX3-1030:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
-# IdeaPad Miix 310 note this only is for BIOS version (bvr) 1HCN4?WW, which has
+# Miix3-830
+sensor:modalias:acpi:SMO8500*:dmi:bvnLENOVO:*:pvrLenovoMIIX3-830:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+
+# IdeaPad Miix 310 note this only is for BIOS version (bvr) 1HCN4?WW and 1HCN2?WW, which has
# a portrait LCD panel, versions with bvr 1HCN3?WW have a landscape panel
sensor:modalias:acpi:KIOX000A*:dmi:bvnLENOVO:bvr1HCN4?WW:*:svnLENOVO:pn80SG:*
+sensor:modalias:acpi:KIOX000A*:dmi:bvnLENOVO:bvr1HCN2?WW:*:svnLENOVO:pn80SG:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
# IdeaPad Miix 320, different batches use a different sensor
@@ -257,8 +325,10 @@ sensor:modalias:acpi:*BOSC0200*:dmi:*:svnLENOVO*:pn80XF:*
sensor:modalias:acpi:SMO8840*:dmi:*:svnLENOVO:pn80XF:pvrLenovoMIIX320*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
-# IdeaPad Miix 510
+# IdeaPad Miix 510, multiple expressions match different internal names
+# pn80U1 matches IdeaPad Miix510-12ISK
sensor:modalias:acpi:*BOSC0200*:dmi:*:svnLENOVO*:pn80XE:*
+sensor:modalias:acpi:*BOSC0200*:dmi:*:svnLENOVO*:pn80U1:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
#########################################
@@ -269,6 +339,10 @@ sensor:modalias:acpi:*BOSC0200*:dmi:*:svnLENOVO*:pn80XE:*
sensor:modalias:acpi:BOSC0200*:dmi:*:svnLINX*:pnLINX1010B:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, -1
+# Linx 12X64
+sensor:modalias:acpi:KIOX000A*:dmi:*:svnLINX*:pnLINX12X64:*
+ ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+
#########################################
# MSI
#########################################
@@ -284,10 +358,18 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnTMAX:pnTM800W560L:*
#########################################
# Onda
#########################################
+sensor:modalias:acpi:BOSC0200*:dmi:*:svnONDA:pnV80PLUS:*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+
sensor:modalias:acpi:BMA250E*:dmi:bvnINSYDECorp.:bvrONDA.D89*:svnInsyde:pnONDATablet:*
+sensor:modalias:acpi:BMA250E*:dmi:bvnINSYDECorp.:bvrONDA.D86*:svnONDA:pnV820wDualOS:*
sensor:modalias:acpi:BMA250E*:dmi:bvnINSYDECorp.:bvrONDA.W89*:svnInsyde:pnONDATablet:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+# Onda v975w, generic DMI strings, match entire dmi modalias inc. bios-date
+sensor:modalias:acpi:SMO8500*:dmi:bvnAmericanMegatrendsInc.:bvr5.6.5:bd07/25/2014:svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+
#########################################
# Peaq
#########################################
@@ -321,6 +403,20 @@ sensor:modalias:acpi:BMA250E*:dmi:bvnAmericanMegatrendsInc.:bvr3BAIR1013:bd08/22
sensor:modalias:acpi:BMA250E*:dmi:bvnAmericanMegatrendsInc.:bvr3BAIR1014:bd10/24/2014:svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+# Point of View TAB-P1005W-232 (v2.0)
+sensor:modalias:acpi:KIOX000A*:dmi:*:rvnPOV:rnI102A:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
+# Point of View TAB-P1006W-232-3G (v1.0)
+sensor:modalias:i2c:bmc150_accel:dmi:bvnINSYDECorp.:*:svnInsyde:pnBayTrail:*:rvn105B:rn0E57:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
+#########################################
+# Prowise
+#########################################
+sensor:modalias:acpi:SMO8500*:dmi:*:svnProwise:pnPT301:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+
#########################################
# Teclast
#########################################
@@ -330,6 +426,14 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnX80Pro:*
sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnX98PlusII:*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+# Teclast X98 Plus I (A5C6), generic DMI strings, match entire dmi modalias inc. bios-date
+sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.011:bd11/03/2015:svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnCherryTrailCR:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:
+ ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+
+# Teclast F5
+sensor:modalias:acpi:KIOX010A*:dmi:*:svnTECLAST:pnF5:*
+ ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+
#########################################
# Trekstor
#########################################
@@ -337,6 +441,15 @@ sensor:modalias:acpi:BMA250*:dmi:*:bvrTREK.G.WI71C.JGBMRBA*:*:svnInsyde:pnST7041
sensor:modalias:acpi:BMA250*:dmi:*:bvrTREK.G.WI71C.JGBMRBA*:*:svnTrekStor:pnSurfTabwintron7.0ST70416-6:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+sensor:modalias:acpi:KIOX000A*:dmi:*:svnTREKSTOR:pnPrimetabT13B:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
+#########################################
+# Umax
+#########################################
+sensor:modalias:acpi:SMO8500*:dmi:*:svnUMAX:pnVisionBook10WiPlus:*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+
#########################################
# Yours
#########################################
diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb
index 5adf793752..07364c2c64 100644
--- a/hwdb/70-mouse.hwdb
+++ b/hwdb/70-mouse.hwdb
@@ -96,7 +96,7 @@
# The list may contain a single item which must be marked with an
# asterisk.
#
-# Local changes to the a non-default resolution of the mouse (e.g. through
+# Local changes to the non-default resolution of the mouse (e.g. through
# third-party software) must not be entered into this file, use a local
# hwdb instead.
#
@@ -281,6 +281,14 @@ mouse:usb:v04b3p3107:name:*
MOUSE_DPI=800@125
##########################################
+# Kensington
+##########################################
+
+# Kensington Expert Mouse trackball
+mouse:usb:v047dp1020:*Kensington Expert Mouse*
+ ID_INPUT_TRACKBALL=1
+
+##########################################
# Lenovo
##########################################
@@ -292,9 +300,9 @@ mouse:usb:v17efp6019:name:Lenovo Optical USB Mouse:
mouse:usb:v17efp6019:name:Logitech Lenovo USB Optical Mouse:
MOUSE_DPI=1000@166
-# ThinkPad USB Laser Mouse
-mouse:usb:v17efp6044:name:ThinkPad USB Laser Mouse:
- MOUSE_DPI=1200@125
+# Lenovo USB mouse model MO28UOL
+mouse:usb:v04b3p310c:name:USB Optical Mouse:
+ MOUSE_DPI=400@142
# Lenovo Precision USB Mouse
mouse:usb:v17efp6050:name:Lenovo Precision USB Mouse:
@@ -306,6 +314,9 @@ mouse:usb:v17efp601d:name:Primax Lenovo Laser Mouse:
mouse:usb:v17efp6045:name:Lenovo USB Laser Mouse:
MOUSE_DPI=1600@125
+# ThinkPad USB Laser Mouse
+mouse:usb:v17efp6044:name:ThinkPad USB Laser Mouse:
+ MOUSE_DPI=1200@125
##########################################
# Logitech
@@ -316,11 +327,143 @@ mouse:usb:v17efp6045:name:Lenovo USB Laser Mouse:
# model name. The usb vid/pid is the same for all those devices.
# Until 3.19 is available, this list just has the Wireless PID entry.
+## G Series ##
+
+# Logitech G5 Laser Mouse
+mouse:usb:v046dpc049:name:Logitech USB Gaming Mouse:
+# Logitech G500s Laser Gaming Mouse
+mouse:usb:v046dpc24e:name:Logitech G500s Laser Gaming Mouse:
+ MOUSE_DPI=400@500 *800@500 2000@500
+
+# Logitech G9
+mouse:usb:v046dpc048:name:Logitech G9 Laser Mouse:
+ MOUSE_DPI=400@1000 800@1000 *1600@1000
+
+# Logitech G9x [Call of Duty MW3 Edition]
+mouse:usb:v046dpc249:name:Logitech G9x Laser Mouse:
+ MOUSE_DPI=400@1000 800@1000 *1600@1000 3200@1000
+
+# Logitech G100s Optical Gaming Mouse
+mouse:usb:v046dpc247:name:Logitech G100s Optical Gaming Mouse:
+ MOUSE_DPI=*1000@500 1750@500 2500@500
+
+# Logitech G400 (Wired)
+mouse:usb:v046dpc245:name:Logitech Gaming Mouse G400:
+ MOUSE_DPI=400@1000 *800@1000 1800@1000 3600@1000
+
+# Logitech G400s (Wired)
+mouse:usb:v046dpc24c:name:Logitech G400s Optical Gaming Mouse:
+ MOUSE_DPI=400@1000 *800@1000 2000@1000 4000@1000
+
+# Logitech G402 Hyperion Fury
+mouse:usb:v046dpc07e:name:Logitech Gaming Mouse G402:
+ MOUSE_DPI=400@1000 *800@1000 1600@1000 3200@1000
+
+# Logitech G500 Mouse
+mouse:usb:v046dpc068:name:Logitech G500:
+ MOUSE_DPI=*1600@500 2600@500 3600@500
+
+# Logitech G502 Proteus Spectrum
+mouse:usb:v046dpc332:name:Logitech Gaming Mouse G502:
+ MOUSE_DPI=1200@1000 *2400@1000 3200@1000 6400@1000
+
+# Logitech G700 Laser Mouse (Wired)
+mouse:usb:v046dpc06b:name:Logitech G700 Laser Mouse:
+# Logitech G700 Laser Mouse (Wireless)
+mouse:usb:v046dpc531:name:Logitech USB Receiver:
+ MOUSE_DPI=*1000@500 3800@500 500@1000 1500@1000 2000@1000
+
+# Logitech G703 (Wired)
+mouse:usb:v046dpc087:name:Logitech G703 Wired/Wireless Gaming Mouse:
+# Logitech G703 (Wireless)
+mouse:usb:v046dpc539:name:Logitech USB Receiver Mouse:
+ MOUSE_DPI=400@1000 800@1000 *1600@1000 3200@1000
+
+## M Series ##
+
+# Logitech Wireless Mouse M185
+mouse:usb:v046dp4008:name:Logitech M185:
+mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4008:
+# Logitech Wireless Mouse M510
+mouse:usb:v046dp1025:name:Logitech M510:
+# Logitech M705 (marathon mouse)
+mouse:usb:v046dp101b:name:Logitech M705:
+mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:101b:
+ MOUSE_DPI=1000@125
+
+# Logitech M305 Wireless Optical Mouse
+mouse:usb:v046dpc52f:name:Logitech USB Receiver:
+ MOUSE_DPI=1000@170
+
+# Logitech Wireless Mouse M310
+mouse:usb:v046dp1024:name:Logitech M310:
+ MOUSE_DPI=1100@168
+
+# Logitech Wireless Mouse M325
+mouse:usb:v046dp400a:name:Logitech M325:
+mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:400a:
+ MOUSE_DPI=600@166
+ MOUSE_WHEEL_CLICK_ANGLE=20
+
+# Logitech M570 trackball
+mouse:usb:v046dp1028:name:Logitech M570:
+ MOUSE_DPI=540@167
+ ID_INPUT_TRACKBALL=1
+
+## MX Series ##
+
+# Logitech Performance MX
+mouse:usb:v046dp101a:name:Logitech Performance MX:
+ MOUSE_DPI=1000@166
+
+# Logitech MX Revolution
+mouse:usb:v046dpc51a:name:Logitech USB Receiver:
+ MOUSE_DPI=800@200
+
+# Logitech MX 518
+mouse:usb:v046dpc01e:name:Logitech USB-PS/2 Optical Mouse:
+ MOUSE_DPI=400@125 *800@125 1600@125
+
+# Logitech MX1000 Laser Cordless Mouse
+mouse:bluetooth:v046dpb003:name:Logitech MX1000 mouse:
+ MOUSE_DPI=800@80
+
# Logitech Anywhere MX
mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:1017:
mouse:usb:v046dp1017:name:Logitech Anywhere MX:
+# Logitech Anywhere MX 2S
+mouse:usb:v046dp406a:name:Logitech MX Anywhere 2S:
MOUSE_WHEEL_CLICK_ANGLE=20
+# Logitech MX Master
+# Horiz wheel has 14 stops, angle is rounded up
+mouse:usb:v046dp4041:name:Logitech MX Master:
+ MOUSE_DPI=1000@166
+ MOUSE_WHEEL_CLICK_ANGLE=15
+ MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26
+ MOUSE_WHEEL_CLICK_COUNT=24
+ MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL=14
+
+# Logitech MX Master 2S (via Logitech Unifying Receiver)
+# Horiz wheel has 14 stops, angle is rounded up
+mouse:usb:v046dp4069:name:Logitech MX Master 2s:
+ MOUSE_DPI=1000@125
+ MOUSE_WHEEL_CLICK_ANGLE=15
+ MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26
+ MOUSE_WHEEL_CLICK_COUNT=24
+ MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL=14
+
+# Logitech MX Master 2S (via Bluetooth)
+# Horiz wheel has 14 stops, angle is rounded up
+mouse:bluetooth:v046dpb019:name:MX Master 2S Mouse:
+ MOUSE_DPI=2000@2000
+ MOUSE_WHEEL_CLICK_ANGLE=15
+ MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26
+ MOUSE_WHEEL_CLICK_COUNT=24
+ MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL=14
+
+## Other ##
+
# Logitech M-BJ58 Optical Mouse
mouse:usb:v046dpc00e:name:Logitech USB-PS/2 Optical Mouse:
# Logitech Mini Optical Mouse
@@ -337,25 +480,10 @@ mouse:usb:v046dpc401:name:Logitech USB-PS/2 Trackball:
mouse:usb:v046dpc501:name:Logitech USB Receiver:
MOUSE_DPI=800@63
-# Lenovo USB mouse model MO28UOL
-mouse:usb:v04b3p310c:name:USB Optical Mouse:
- MOUSE_DPI=400@142
-
-# Logitech M570 trackball
-mouse:usb:v046dp1028:name:Logitech M570:
- MOUSE_DPI=540@167
- ID_INPUT_TRACKBALL=1
-
# Logitech USB-PS/2 M-BZ96C
mouse:usb:v046dpc045:name:Logitech USB-PS/2 Optical Mouse:
MOUSE_DPI=600@125
-# Logitech Wireless Mouse M325
-mouse:usb:v046dp400a:name:Logitech M325:
-mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:400a:
- MOUSE_DPI=600@166
- MOUSE_WHEEL_CLICK_ANGLE=20
-
# Logitech MX400 Performance Laser Mouse
mouse:usb:v046dpc043:name:Logitech USB-PS/2 Optical Mouse:
# Logitech MX1000 Laser Cordless Mouse
@@ -366,58 +494,10 @@ mouse:usb:v046dpc50e:name:Logitech USB Receiver:
mouse:usb:v046dpc040:name:Logitech USB-PS/2 Optical Mouse:
MOUSE_DPI=800@125
-# Logitech MX 518
-mouse:usb:v046dpc01e:name:Logitech USB-PS/2 Optical Mouse:
- MOUSE_DPI=400@125 *800@125 1600@125
-
# Logitech, Inc. RX 250 Optical Mouse
mouse:usb:v046dpc050:name:Logitech USB-PS/2 Optical Mouse:
MOUSE_DPI=1000@142
-# Logitech Wireless Mouse M185
-mouse:usb:v046dp4008:name:Logitech M185:
-mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4008:
-# Logitech Wireless Mouse M510
-mouse:usb:v046dp1025:name:Logitech M510:
-# Logitech M705 (marathon mouse)
-mouse:usb:v046dp101b:name:Logitech M705:
-mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:101b:
- MOUSE_DPI=1000@125
-
-# Logitech MX Revolution
-mouse:usb:v046dpc51a:name:Logitech USB Receiver:
- MOUSE_DPI=800@200
-
-# Logitech G5 Laser Mouse
-mouse:usb:v046dpc049:name:Logitech USB Gaming Mouse:
-# Logitech G500s Laser Gaming Mouse
-mouse:usb:v046dpc24e:name:Logitech G500s Laser Gaming Mouse:
- MOUSE_DPI=400@500 *800@500 2000@500
-
-# Logitech G9
-mouse:usb:v046dpc048:name:Logitech G9 Laser Mouse:
- MOUSE_DPI=400@1000 800@1000 *1600@1000
-
-# Logitech G9x [Call of Duty MW3 Edition]
-mouse:usb:v046dpc249:name:Logitech G9x Laser Mouse:
- MOUSE_DPI=400@1000 800@1000 *1600@1000 3200@1000
-
-# Logitech G400 (Wired)
-mouse:usb:v046dpc245:name:Logitech Gaming Mouse G400:
- MOUSE_DPI=400@1000 *800@1000 1800@1000 3600@1000
-
-# Logitech G400s (Wired)
-mouse:usb:v046dpc24c:name:Logitech G400s Optical Gaming Mouse:
- MOUSE_DPI=400@1000 *800@1000 2000@1000 4000@1000
-
-# Logitech G402 Hyperion Fury
-mouse:usb:v046dpc07e:name:Logitech Gaming Mouse G402:
- MOUSE_DPI=400@1000 *800@1000 1600@1000 3200@1000
-
-# Logitech G502 Proteus Spectrum
-mouse:usb:v046dpc332:name:Logitech Gaming Mouse G502:
- MOUSE_DPI=1200@1000 *2400@1000 3200@1000 6400@1000
-
# Logitech B605 Wireless Mouse (also M505)
mouse:usb:v046dp101d:name:Logitech B605:
mouse:usb:v046dp101d:name:Logitech M505:
@@ -443,50 +523,10 @@ mouse:usb:v046dp402d:name:Logitech M560:
mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:402d:
MOUSE_DPI=1000@125
-# Logitech M305 Wireless Optical Mouse
-mouse:usb:v046dpc52f:name:Logitech USB Receiver:
- MOUSE_DPI=1000@170
-
-# Logitech Performance MX
-mouse:usb:v046dp101a:name:Logitech Performance MX:
- MOUSE_DPI=1000@166
-
-# Logitech MX Master
-# Horiz wheel has 14 stops, angle is rounded up
-mouse:usb:v046dp4041:name:Logitech MX Master:
- MOUSE_DPI=1000@166
- MOUSE_WHEEL_CLICK_ANGLE=15
- MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26
- MOUSE_WHEEL_CLICK_COUNT=24
- MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL=14
-
-# Logitech MX Master 2s
-# Horiz wheel has 14 stops, angle is rounded up
-mouse:usb:v046dp4069:name:Logitech MX Master 2s:
- MOUSE_DPI=1000@125
- MOUSE_WHEEL_CLICK_ANGLE=15
- MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26
- MOUSE_WHEEL_CLICK_COUNT=24
- MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL=14
-
# Logitech MK260 Wireless Combo Receiver aka M-R0011
mouse:usb:v046dpc52e:name:Logitech USB Receiver:
MOUSE_DPI=1000@200
-# Logitech G100s Optical Gaming Mouse
-mouse:usb:v046dpc247:name:Logitech G100s Optical Gaming Mouse:
- MOUSE_DPI=*1000@500 1750@500 2500@500
-
-# Logitech G700 Laser Mouse (Wired)
-mouse:usb:v046dpc06b:name:Logitech G700 Laser Mouse:
-# Logitech G700 Laser Mouse (Wireless)
-mouse:usb:v046dpc531:name:Logitech USB Receiver:
- MOUSE_DPI=*1000@500 3800@500 500@1000 1500@1000 2000@1000
-
-# Logitech Wireless Mouse M310
-mouse:usb:v046dp1024:name:Logitech M310:
- MOUSE_DPI=1100@168
-
# Logitech USB Laser Mouse M-UAS144 [LS1 Laser Mouse]
mouse:usb:v046dpc062:name:Logitech USB Laser Mouse:
MOUSE_DPI=1200@125
@@ -501,18 +541,10 @@ mouse:usb:v046dp4026:name:Logitech T400:
mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4026:
MOUSE_DPI=1300@166
-# Logitech G500 Mouse
-mouse:usb:v046dpc068:name:Logitech G500:
- MOUSE_DPI=*1600@500 2600@500 3600@500
-
# Logitech TrackMan Wheel (USB)
mouse:usb:v046dpc404:name:Logitech Trackball:
MOUSE_DPI=300@125
-# Logitech MX1000 Laser Cordless Mouse
-mouse:bluetooth:v046dpb003:name:Logitech MX1000 mouse:
- MOUSE_DPI=800@80
-
# Logitech Ultrathin Touch Mouse
mouse:bluetooth:v046dpb00d:name:Ultrathin Touch Mouse:
MOUSE_DPI=1000@1000
@@ -569,6 +601,10 @@ mouse:bluetooth:v045ep07f3:name:Arc Touch Mouse SE:
mouse:bluetooth:v0000p0000:name:Surface Mouse:
MOUSE_DPI=2000@2000
+# Microsoft Classic IntelliMouse
+mouse:usb:v045ep0823:name:Microsoft Microsoft?? Classic IntelliMouse??:
+ MOUSE_DPI=3200@1000
+
##########################################
# Mionix
##########################################
diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb
index 3f070e09de..553465a87e 100644
--- a/hwdb/70-pointingstick.hwdb
+++ b/hwdb/70-pointingstick.hwdb
@@ -43,7 +43,7 @@
# udevadm info /dev/input/eventXX.
#
# Allowed properties are:
-# POINTINGSTICK_CONST_ACCEL
+# POINTINGSTICK_CONST_ACCEL (deprecated)
# POINTINGSTICK_SENSITIVITY
#
# Entries should be sorted with growing _SENSITIVITY and _CONST_ACCEL.
@@ -52,6 +52,11 @@
# POINTINGSTICK_CONST_ACCEL #
#########################################
#
+# DO NOT USE THIS PROPERTY. This property is kept for backwards
+# compatibility. The only known consumer, libinput, stopped reading this
+# property in version 1.9.0. No new entries for this property should be
+# added.
+#
# Trackpoint const accel settings are specified as
# POINTINGSTICK_CONST_ACCEL=<accel>
#
@@ -104,6 +109,8 @@ evdev:name:*DualPoint Stick:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE7470*:pvr*
# Lenovo Thinkpad X220
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX220:*
+# Lenovo Thinkpad X220 tablet
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX220Tablet:*
# Lenovo Thinkpad X230
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230:*
# Lenovo Thinkpad X230 tablet
@@ -129,12 +136,13 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Ta
POINTINGSTICK_SENSITIVITY=200
POINTINGSTICK_CONST_ACCEL=1.0
-# Lenovo Thinkpad X200/X201/X200s/X201s
+# Lenovo Thinkpad X200/X201/X200s/X201s/X200 Tablet/X201 Tablet
# Note these come with 2 revisions of keyboard, with the trackpoints having a
# different sensitivity in the different revisions. 1.25 is a bit slow for the
# least sensitive revision, but it is better to be a bit slow than too fast.
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX20?:*
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX20??:*
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX20?Tablet:*
POINTINGSTICK_SENSITIVITY=200
POINTINGSTICK_CONST_ACCEL=1.25
diff --git a/hwdb/acpi_id_registry.html b/hwdb/acpi_id_registry.html
index e1153b0133..46256ff138 100644
--- a/hwdb/acpi_id_registry.html
+++ b/hwdb/acpi_id_registry.html
@@ -86,6 +86,12 @@
<tr class="even"><td>Insyde Software</td><td>INSY</td><td>11/10/2017</td> </tr>
<tr class="odd"><td>Nexstgo Company Limited</td><td>NXGO</td><td>11/13/2017</td> </tr>
<tr class="even"><td>Ampere Computing</td><td>AMPC</td><td>03/29/2018</td> </tr>
+ <tr class="odd"><td>IDEMIA</td><td>IDEM</td><td>06/26/2018</td> </tr>
+ <tr class="even"><td>Vishay Intertechnology, Inc.</td><td>VSHY</td><td>07/09/2018</td> </tr>
+ <tr class="odd"><td>DMIST RESEARCH LTD</td><td>DMST</td><td>07/09/2018</td> </tr>
+ <tr class="even"><td>COMHEAR, INC.</td><td>CMHR</td><td>08/02/2018</td> </tr>
+ <tr class="odd"><td>Sensel, Inc.</td><td>SNSL</td><td>08/20/2018</td> </tr>
+ <tr class="even"><td>G2touch Co., LTD</td><td>GTCH</td><td>12/04/2018</td> </tr>
</tbody>
</table>
</body>
diff --git a/hwdb/ids_parser.py b/hwdb/ids_parser.py
index c80d22258a..61ca0ca6e0 100755
--- a/hwdb/ids_parser.py
+++ b/hwdb/ids_parser.py
@@ -17,7 +17,6 @@ TAB = White('\t', exact=1).suppress()
COMMENTLINE = pythonStyleComment + EOL
EMPTYLINE = LineEnd()
text_eol = lambda name: Regex(r'[^\n]+')(name) + EOL
-# text_eol = lambda name: Word(printables + ' ' + '®üäßçõãİó ×²â¶Â´â€â€œ\u200E\u200B')(name) + EOL
def klass_grammar():
klass_line = Literal('C ').suppress() + NUM2('klass') + text_eol('text')
diff --git a/hwdb/ma-large.txt b/hwdb/ma-large.txt
index 0ad82a2c1e..630a8f5a1d 100644
--- a/hwdb/ma-large.txt
+++ b/hwdb/ma-large.txt
@@ -56,36 +56,6 @@ B499BA (base 16) Hewlett Packard
Taipei 114
TW
-1C-BD-B9 (hex) D-Link International
-1CBDB9 (base 16) D-Link International
- 1 INTERNATIONAL BUSINESS PARK,
- SINGAPORE 609917
- SG
-
-90-94-E4 (hex) D-Link International
-9094E4 (base 16) D-Link International
- 1 International Business Park, #03-12, The Synergy
- SINGAPORE 609917
- SG
-
-28-10-7B (hex) D-Link International
-28107B (base 16) D-Link International
- 1 International Business Park, #03-12, The Synergy
- SINGAPORE 609917
- SG
-
-1C-7E-E5 (hex) D-Link International
-1C7EE5 (base 16) D-Link International
- 1 International Business Park, #03-12, The Synergy
- SINGAPORE 609917
- SG
-
-C4-A8-1D (hex) D-Link International
-C4A81D (base 16) D-Link International
- 1 Internal Business Park, #03-12,
- SINGAPORE Singapore 609917
- SG
-
18-62-2C (hex) Sagemcom Broadband SAS
18622C (base 16) Sagemcom Broadband SAS
250 route de l'Empereur
@@ -746,18 +716,6 @@ E448C7 (base 16) Cisco SPVTG
Lawrenceville GA 30044
US
-00-02-C7 (hex) ALPS ELECTRIC CO.,LTD.
-0002C7 (base 16) ALPS ELECTRIC CO.,LTD.
- 1-2-1, Okinouchi, Sama-City,
- Sama 00000
- JP
-
-04-76-6E (hex) ALPS ELECTRIC CO.,LTD.
-04766E (base 16) ALPS ELECTRIC CO.,LTD.
- 6-3-36 Furukawanakazato,
- Osaki Miyagi-pref 989-6181
- JP
-
00-6B-8E (hex) Shanghai Feixun Communication Co.,Ltd.
006B8E (base 16) Shanghai Feixun Communication Co.,Ltd.
Building 90,No,4855,Guangfulin Road
@@ -2027,12 +1985,6 @@ DC2F03 (base 16) Step forward Group Co., Ltd.
Tokyo Tokyo 108-8001
JP
-F8-E9-03 (hex) D-Link International
-F8E903 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
68-28-F6 (hex) Vubiq Networks, Inc.
6828F6 (base 16) Vubiq Networks, Inc.
9231 Irvine Blvd.
@@ -2135,12 +2087,6 @@ D48F33 (base 16) Microsoft Corporation
Dongguan City Guangdong 86
CN
-34-02-9B (hex) CloudBerry Technologies Private Limited
-34029B (base 16) CloudBerry Technologies Private Limited
- 209, S.C.O. - 11, SECTOR 7
- CHANDIGARH Punjab 160019
- IN
-
70-AF-25 (hex) Nishiyama Industry Co.,LTD.
70AF25 (base 16) Nishiyama Industry Co.,LTD.
177-2
@@ -2159,12 +2105,6 @@ B47C29 (base 16) Shenzhen Guzidi Technology Co.,Ltd
Kowloon Bay HongKong 852
CN
-6C-19-8F (hex) D-Link International
-6C198F (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
60-C1-CB (hex) Fujian Great Power PLC Equipment Co.,Ltd
60C1CB (base 16) Fujian Great Power PLC Equipment Co.,Ltd
6/F.Bld.34.Zone C.Software Park
@@ -2561,12 +2501,6 @@ F03FF8 (base 16) R L Drake
Old Bridge NJ 08857
US
-B0-C5-54 (hex) D-Link International
-B0C554 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
54-D1-63 (hex) MAX-TECH,INC
54D163 (base 16) MAX-TECH,INC
2F, 26, 2gil, Dujeonggongdan
@@ -2876,12 +2810,6 @@ BC2D98 (base 16) ThinGlobal LLC
Winter Park FL 32789
US
-18-1B-EB (hex) Actiontec Electronics, Inc
-181BEB (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
CC-74-98 (hex) Filmetrics Inc.
CC7498 (base 16) Filmetrics Inc.
3560 Dunhill Street, #100
@@ -2966,12 +2894,6 @@ B424E7 (base 16) Codetek Technology Co.,Ltd
New Taipei City 23148
TW
-24-93-CA (hex) Voxtronic Technology Computer-Systeme GmbH
-2493CA (base 16) Voxtronic Technology Computer-Systeme GmbH
- Jochen-Rindt-Straße 15
- Vienna 1230
- AT
-
68-8A-B5 (hex) EDP Servicos
688AB5 (base 16) EDP Servicos
Rua Castilho, número 165, 5º andar
@@ -3059,12 +2981,6 @@ D09D0A (base 16) LINKCOM
Zona Industrial de Vagos Portugal 3840-385
PT
-C0-A0-BB (hex) D-Link International
-C0A0BB (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
28-A1-EB (hex) ETEK TECHNOLOGY (SHENZHEN) CO.,LTD
28A1EB (base 16) ETEK TECHNOLOGY (SHENZHEN) CO.,LTD
A505-506ã€A509 Room,A5 Floor, Academy Of International Technology Innovation, Keji Nan 10th Road, Hi-Tech Industrial Park, Shenzhen, P.R.C.
@@ -3503,12 +3419,6 @@ B8CD93 (base 16) Penetek, Inc
New Taipei City 248
TW
-D8-FE-E3 (hex) D-Link International
-D8FEE3 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
F8-51-6D (hex) Denwa Technology Corp.
F8516D (base 16) Denwa Technology Corp.
1931 NW 150 TH AVE
@@ -3794,12 +3704,6 @@ DC1DD4 (base 16) Microstep-MIS spol. s r.o.
Bratislava 84104
SK
-E0-18-77 (hex) FUJITSU LIMITED
-E01877 (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
14-94-48 (hex) BLU CASTLE S.A.
149448 (base 16) BLU CASTLE S.A.
5 RUE BONNEVOIE
@@ -4382,12 +4286,6 @@ F49466 (base 16) CountMax, ltd
New Taipei City Taiwan 23586
TW
-F8-E4-FB (hex) Actiontec Electronics, Inc
-F8E4FB (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
58-87-E2 (hex) Shenzhen Coship Electronics Co., Ltd.
5887E2 (base 16) Shenzhen Coship Electronics Co., Ltd.
Rainbow Bldg., North, Hi-Tech Industrial Park
@@ -5279,12 +5177,6 @@ CC6BF1 (base 16) Sound Masking Inc.
Calgary Alberta T3E 6S3
CA
-B8-2C-A0 (hex) Honeywell HomMed
-B82CA0 (base 16) Honeywell HomMed
- 3400 Intertech Drive
- Brookfield WI 53025
- US
-
94-AE-61 (hex) Alcatel Lucent
94AE61 (base 16) Alcatel Lucent
Via Energy Park, 14
@@ -5453,12 +5345,6 @@ E4AFA1 (base 16) HES-SO
Hertfordshire WD6 3AW
GB
-20-76-00 (hex) Actiontec Electronics, Inc
-207600 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
84-D3-2A (hex) IEEE 1905.1
84D32A (base 16) IEEE 1905.1
445 Hoes Lane
@@ -5591,18 +5477,6 @@ E0ED1A (base 16) vastriver Technology Co., Ltd
Rockville MD 20850
US
-48-61-A3 (hex) Concern Axion JSC
-4861A3 (base 16) Concern Axion JSC
- 90, M. Gorkogo St.
- Izhevsk Udmurt Republic 426000
- RU
-
-D8-96-85 (hex) GoPro
-D89685 (base 16) GoPro
- 3000 Clearview Way
- San Mateo CA 94402
- US
-
08-A1-2B (hex) ShenZhen EZL Technology Co., Ltd
08A12B (base 16) ShenZhen EZL Technology Co., Ltd
RM21B, Building 2, China Phoenix Building,
@@ -6680,12 +6554,6 @@ A0DDE5 (base 16) SHARP Corporation
Oulu 90570
FI
-84-DB-2F (hex) Sierra Wireless Inc
-84DB2F (base 16) Sierra Wireless Inc
- 1381 Wireless Way
- Richmond BC V6V 3A4
- CA
-
C8-93-83 (hex) Embedded Automation, Inc.
C89383 (base 16) Embedded Automation, Inc.
17345 Abbey Drive
@@ -9128,12 +8996,6 @@ A8CE90 (base 16) CVC
SEOUL KSXX 0022
KR
-00-1F-90 (hex) Actiontec Electronics, Inc
-001F90 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
00-1F-8F (hex) Shanghai Bellmann Digital Source Co.,Ltd.
001F8F (base 16) Shanghai Bellmann Digital Source Co.,Ltd.
404-408 Main Bd, FDUSP, No.11 Guotai Rd.
@@ -9638,12 +9500,6 @@ A8CE90 (base 16) CVC
Flemington NJ 08822
US
-00-1E-70 (hex) Cobham Defence Communications Ltd
-001E70 (base 16) Cobham Defence Communications Ltd
- Haslingden Road
- Blackburn Lancashire BB1 2EE
- GB
-
00-1E-69 (hex) Thomson Inc.
001E69 (base 16) Thomson Inc.
101 West 103rd Street
@@ -10454,12 +10310,6 @@ A8CE90 (base 16) CVC
Yongin-si Gyeonggi-do 449-821
KR
-00-1A-3F (hex) intelbras
-001A3F (base 16) intelbras
- rodovia br 101 km 210
- sao jose sc 88104800
- BR
-
00-1A-41 (hex) INOCOVA Co.,Ltd
001A41 (base 16) INOCOVA Co.,Ltd
5F SEOUL BD.736-17
@@ -20816,12 +20666,6 @@ A8CE90 (base 16) CVC
RICHARDSON TX 75081
-00-00-BD (hex) MITSUBISHI CABLE COMPANY
-0000BD (base 16) MITSUBISHI CABLE COMPANY
- 520 MADISON AVENUE
- NEW YORK NY 10022
- US
-
00-00-37 (hex) OXFORD METRICS LIMITED
000037 (base 16) OXFORD METRICS LIMITED
UNIT 8, 7 WEST WAY,
@@ -21716,18 +21560,6 @@ E8B1FC (base 16) Intel Corporate
Kulim Kedah 09000
MY
-18-64-72 (hex) Aruba Networks
-186472 (base 16) Aruba Networks
- 1322 Crossman Ave.
- Sunnyvale CA 94089
- US
-
-00-24-6C (hex) Aruba Networks
-00246C (base 16) Aruba Networks
- 1344 Crossman Avenue
- Sunnyvale CA 94089
- US
-
64-D9-54 (hex) Taicang T&W Electronics
64D954 (base 16) Taicang T&W Electronics
89# Jiang Nan RD, Lu Du,
@@ -22196,18 +22028,6 @@ D059E4 (base 16) Samsung Electronics Co.,Ltd
PARIS IdF 75008
FR
-A0-89-E4 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
-A089E4 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
- 7F,Block A,Skyworth Building,
- Shenzhen Guangdong 518057
- CN
-
-00-1A-9A (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
-001A9A (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
- 13F,Block A,Skyworth Building,Gaoxin AVE.1.S,
- ShenZhen GuangDong 518057
- CN
-
AC-3A-7A (hex) Roku, Inc.
AC3A7A (base 16) Roku, Inc.
12980 Saratoga Ave
@@ -22478,12 +22298,6 @@ B47443 (base 16) Samsung Electronics Co.,Ltd
Heerlen 6411NK
NL
-54-5A-A6 (hex) Espressif Inc.
-545AA6 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
DC-E8-38 (hex) CK Telecom (Shenzhen) Limited
DCE838 (base 16) CK Telecom (Shenzhen) Limited
Floor 9th, Building 4C,Software Industry Base, Xuefu Road, Hi-Tech Park, Nanshan Dist.
@@ -22514,12 +22328,6 @@ DC2DCB (base 16) Beijing Unis HengYue Technology Co., Ltd.
Berlin Berlin 10559
DE
-1C-5F-2B (hex) D-Link International
-1C5F2B (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
D8-80-3C (hex) Anhui Huami Information Technology Company Limited
D8803C (base 16) Anhui Huami Information Technology Company Limited
Building A4, 12th Floor, No. 800, Wangjiang Road
@@ -22622,12 +22430,6 @@ AC44F2 (base 16) YAMAHA CORPORATION
Shenzhen Guangdong 518057
CN
-34-99-71 (hex) Quanta Storage Inc.
-349971 (base 16) Quanta Storage Inc.
- 3F. No.188, Wenhua 2nd Rd., Guishan Dist.,
- Taoyuan City 33383
- TW
-
24-61-5A (hex) China Mobile Group Device Co.,Ltd.
24615A (base 16) China Mobile Group Device Co.,Ltd.
32 Xuanwumen West Street,Xicheng District
@@ -22994,12 +22796,6 @@ D8E56D (base 16) TCT mobile ltd
Hsinchu Taiwan ROC. 30352
TW
-10-BE-F5 (hex) D-Link International
-10BEF5 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
7C-6A-F3 (hex) Integrated Device Technology (Malaysia) Sdn. Bhd.
7C6AF3 (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd.
Phase 3, Bayan Lepas FIZ
@@ -23930,12 +23726,6 @@ C4F5A5 (base 16) Kumalift Co., Ltd.
Yueqing Zhejiang 310025
CN
-24-0A-C4 (hex) Espressif Inc.
-240AC4 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
E4-C1-F1 (hex) SHENZHEN SPOTMAU INFORMATION TECHNOLIGY CO., Ltd
E4C1F1 (base 16) SHENZHEN SPOTMAU INFORMATION TECHNOLIGY CO., Ltd
10F, Block D, 5th Building, Shenzhen Software Industrial Base, Haitian 2nd Rd,Nanshan District
@@ -24386,66 +24176,6 @@ E0686D (base 16) Raybased AB
Västra Frölunda 42130
SE
-98-B0-39 (hex) Nokia
-98B039 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-84-26-2B (hex) Nokia
-84262B (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-94-E9-8C (hex) Nokia
-94E98C (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-E4-81-84 (hex) Nokia
-E48184 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-BC-8D-0E (hex) Nokia
-BC8D0E (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-B0-75-4D (hex) Nokia
-B0754D (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-BC-6B-4D (hex) Nokia
-BC6B4D (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-A4-7B-2C (hex) Nokia
-A47B2C (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-00-D0-F6 (hex) Nokia
-00D0F6 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-48-F8-E1 (hex) Nokia
-48F8E1 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
00-23-41 (hex) Vanderbilt International (SWE) AB
002341 (base 16) Vanderbilt International (SWE) AB
Englundavaegen 7
@@ -24680,12 +24410,6 @@ C025E9 (base 16) TP-LINK TECHNOLOGIES CO.,LTD.
São Paulo São Paulo 04571010
BR
-94-B8-19 (hex) Nokia
-94B819 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
A4-D9-A4 (hex) neXus ID Solutions AB
A4D9A4 (base 16) neXus ID Solutions AB
Telefonvägen 26
@@ -24824,42 +24548,6 @@ E0508B (base 16) Zhejiang Dahua Technology Co., Ltd.
Chongqing Chongqing 401332
CN
-9C-99-A0 (hex) Xiaomi Communications Co Ltd
-9C99A0 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-18-59-36 (hex) Xiaomi Communications Co Ltd
-185936 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-98-FA-E3 (hex) Xiaomi Communications Co Ltd
-98FAE3 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-64-09-80 (hex) Xiaomi Communications Co Ltd
-640980 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-8C-BE-BE (hex) Xiaomi Communications Co Ltd
-8CBEBE (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-F8-A4-5F (hex) Xiaomi Communications Co Ltd
-F8A45F (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
50-8A-0F (hex) SHENZHEN FISE TECHNOLOGY HOLDING CO.,LTD.
508A0F (base 16) SHENZHEN FISE TECHNOLOGY HOLDING CO.,LTD.
No.6 Building, Longfu Industrial Area, Huarong Road, Tongsheng Community, Dalang Street, Longhua New District
@@ -25334,9 +25022,6 @@ F0D2F1 (base 16) Amazon Technologies Inc.
Reno NV 89507
US
-F0-A2-25 (hex) Private
-F0A225 (base 16) Private
-
E0-48-AF (hex) Premietech Limited
E048AF (base 16) Premietech Limited
Unit 805, Tower 3, Enterprise Square I
@@ -25505,12 +25190,6 @@ C4FF1F (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Chanhassen MN 55317
US
-C4-0B-CB (hex) Xiaomi Communications Co Ltd
-C40BCB (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
D8-C0-6A (hex) Hunantv.com Interactive Entertainment Media Co.,Ltd.
D8C06A (base 16) Hunantv.com Interactive Entertainment Media Co.,Ltd.
Floor 2U, Hunan International Exhibition Center, Kaifu District, Changsha City, Hunan Province, P.R.C.
@@ -25829,12 +25508,6 @@ FC06ED (base 16) M2Motive Technology Inc.
SHENZHEN GUANGDONG 518100
CN
-30-C3-D9 (hex) ALPS ELECTRIC CO.,LTD.
-30C3D9 (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- Kakuda Miyagi-Pref 981-1595
- JP
-
FC-4D-8C (hex) SHENZHEN PANTE ELECTRONICS TECHNOLOGY CO., LTD
FC4D8C (base 16) SHENZHEN PANTE ELECTRONICS TECHNOLOGY CO., LTD
Building 5,Hui Mingsheng industrial park,Tongfu Rd,Fu Yong street
@@ -26063,12 +25736,6 @@ E86D65 (base 16) AUDIO MOBIL Elektronik GmbH
Braunau am Inn - Ranshofen 5282
AT
-E8-6F-F2 (hex) Actiontec Electronics, Inc
-E86FF2 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
00-01-6D (hex) CarrierComm Inc.
00016D (base 16) CarrierComm Inc.
2231 Rutherford, Suite 110
@@ -26171,18 +25838,6 @@ BC9680 (base 16) SHENZHEN GONGJIN ELECTRONICS CO.,LT
San Jose CA 94568
US
-BC-02-4A (hex) HMD Global Oy
-BC024A (base 16) HMD Global Oy
- Karaportti 2
- Espoo 02610
- FI
-
-90-A3-65 (hex) HMD Global Oy
-90A365 (base 16) HMD Global Oy
- Karaportti 2
- Espoo 02610
- FI
-
C4-44-A0 (hex) Cisco Systems, Inc
C444A0 (base 16) Cisco Systems, Inc
80 West Tasman Drive
@@ -26267,12 +25922,6 @@ B40F3B (base 16) Tenda Technology Co.,Ltd.Dongguan branch
Shenzhen Guangdong 518057
CN
-40-9B-CD (hex) D-Link International
-409BCD (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
00-5C-86 (hex) SHENZHEN FAST TECHNOLOGIES CO.,LTD
005C86 (base 16) SHENZHEN FAST TECHNOLOGIES CO.,LTD
Room 202,Building No.5,Section 30,No.2 of Kefa Road,Nanshan District,Shenzhen,P.R.China
@@ -26477,12 +26126,6 @@ A43412 (base 16) Thales Alenia Space
Toulouse 31037
FR
-EC-D0-9F (hex) Xiaomi Communications Co Ltd
-ECD09F (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
9C-65-EE (hex) DASAN Network Solutions
9C65EE (base 16) DASAN Network Solutions
DASAN Tower 8F, 49 Daewangpangyo-ro644beon-gil Bundang-gu
@@ -26549,15 +26192,6 @@ DC6AEA (base 16) Infinix mobility limited
HongKong HongKong 999077
HK
-00-25-DF (hex) Private
-0025DF (base 16) Private
-
-D8-A0-1D (hex) Espressif Inc.
-D8A01D (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
8C-E3-8E (hex) Toshiba Memory Corporation
8CE38E (base 16) Toshiba Memory Corporation
1-1, Shibaura 1-chome
@@ -26594,9 +26228,6 @@ C4F312 (base 16) Texas Instruments
San Jose CA 95134
US
-A0-D8-6F (hex) Private
-A0D86F (base 16) Private
-
38-90-A5 (hex) Cisco Systems, Inc
3890A5 (base 16) Cisco Systems, Inc
80 West Tasman Drive
@@ -26645,27 +26276,12 @@ E4A7C5 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
GUMI KYUNGBUK 730-030
KR
-48-D3-5D (hex) Private
-48D35D (base 16) Private
-
-E4-46-DA (hex) Xiaomi Communications Co Ltd
-E446DA (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
50-0F-80 (hex) Cisco Systems, Inc
500F80 (base 16) Cisco Systems, Inc
80 West Tasman Drive
San Jose CA 94568
US
-F4-F5-DB (hex) Xiaomi Communications Co Ltd
-F4F5DB (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
38-A6-CE (hex) BSkyB Ltd
38A6CE (base 16) BSkyB Ltd
130 Kings Road
@@ -26840,12 +26456,6 @@ CC2DE0 (base 16) Routerboard.com
Riga Riga LV1009
LV
-EC-FA-BC (hex) Espressif Inc.
-ECFABC (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
B0-FC-36 (hex) CyberTAN Technology Inc.
B0FC36 (base 16) CyberTAN Technology Inc.
99 Park Ave III, Hsinchu Science Park
@@ -27002,12 +26612,6 @@ E892A4 (base 16) LG Electronics (Mobile Communications)
Dallas TX 75243
US
-18-F0-E4 (hex) Xiaomi Communications Co Ltd
-18F0E4 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources , No.68 Qinghe Middle Street , Haidian District
- Beijing Beijing 100089
- CN
-
80-3B-F6 (hex) LOOK EASY INTERNATIONAL LIMITED
803BF6 (base 16) LOOK EASY INTERNATIONAL LIMITED
4th Floor, No. 551, Guang-Fu South Road
@@ -27308,12 +26912,6 @@ E43022 (base 16) Hanwha Techwin Security Vietnam
Nam Son Commune, Bac Ninh City Bac Ninh Province 000
VN
-B4-E6-2D (hex) Espressif Inc.
-B4E62D (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
F0-B5-B7 (hex) Disruptive Technologies Research AS
F0B5B7 (base 16) Disruptive Technologies Research AS
Ytrebygdsvegen 215
@@ -27338,12 +26936,6 @@ F0B5B7 (base 16) Disruptive Technologies Research AS
Wuhan Hubei Province 430223
CN
-04-C2-41 (hex) Nokia
-04C241 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
30-7B-AC (hex) New H3C Technologies Co., Ltd
307BAC (base 16) New H3C Technologies Co., Ltd
466 Changhe Road, Binjiang District
@@ -27464,12 +27056,6 @@ D4E6B7 (base 16) Samsung Electronics Co.,Ltd
Chiefland FL 32626
US
-8C-F7-73 (hex) Nokia
-8CF773 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
C4-64-E3 (hex) Texas Instruments
C464E3 (base 16) Texas Instruments
12500 TI Blvd
@@ -27578,12 +27164,6 @@ A438CC (base 16) Nintendo Co.,Ltd
San Jose CA 94568
US
-A0-66-10 (hex) FUJITSU LIMITED
-A06610 (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
68-D4-82 (hex) SHENZHEN GONGJIN ELECTRONICS CO.,LT
68D482 (base 16) SHENZHEN GONGJIN ELECTRONICS CO.,LT
SONGGANG
@@ -27602,12 +27182,6 @@ A06610 (base 16) FUJITSU LIMITED
Bayan Lepas Penang 11900
MY
-00-A0-D5 (hex) Sierra Wireless Inc
-00A0D5 (base 16) Sierra Wireless Inc
- 13811 Wireless Way
- RICHMOND B.C. V6V 3A4
- CA
-
6C-38-38 (hex) Marking System Technology Co., Ltd.
6C3838 (base 16) Marking System Technology Co., Ltd.
76-1, Hirakawa Yokomichi
@@ -27650,12 +27224,6 @@ A06610 (base 16) FUJITSU LIMITED
Santa Clara CA 95054
US
-9C-2E-A1 (hex) Xiaomi Communications Co Ltd
-9C2EA1 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
AC-35-EE (hex) FN-LINK TECHNOLOGY LIMITED
AC35EE (base 16) FN-LINK TECHNOLOGY LIMITED
A Building,HuiXin industial park,No 31, YongHe road, Fuyong town, Bao'an District
@@ -27680,12 +27248,6 @@ E44E76 (base 16) CHAMPIONTECH ENTERPRISE (SHENZHEN) INC
Englewood CO 80112
US
-6C-C4-D5 (hex) HMD Global Oy
-6CC4D5 (base 16) HMD Global Oy
- Karaportti 2
- Espoo 02610
- FI
-
E8-C1-B8 (hex) Nanjing Bangzhong Electronic Commerce Limited
E8C1B8 (base 16) Nanjing Bangzhong Electronic Commerce Limited
No.22, Liuzhou East Road, High - tech Zone
@@ -28964,12 +28526,6 @@ CCA462 (base 16) ARRIS Group, Inc.
Hangzhou Zhejiang 310011
CN
-50-A0-09 (hex) Xiaomi Communications Co Ltd
-50A009 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
2C-28-B7 (hex) Hangzhou Ruiying technology co., LTD
2C28B7 (base 16) Hangzhou Ruiying technology co., LTD
No. 1, building 305, Yunqi Town Cloud Computing Industrial Park, Hangzhou City, Xihu District
@@ -29000,8 +28556,737 @@ CCA462 (base 16) ARRIS Group, Inc.
Berlin 13509
DE
-8C-E7-48 (hex) Private
-8CE748 (base 16) Private
+00-D8-61 (hex) Micro-Star INTL CO., LTD.
+00D861 (base 16) Micro-Star INTL CO., LTD.
+ No.69, Lide St.,
+ New Taipei City Taiwan 235
+ TW
+
+04-33-85 (hex) Nanchang BlackShark Co.,Ltd.
+043385 (base 16) Nanchang BlackShark Co.,Ltd.
+ Room 319, Jiaoqiao Town Office Building, Economic and Technical development zone, Nanchang City, Jiangxi Province.
+ Nanchang 330013
+ CN
+
+A0-D8-6F (hex) Private
+A0D86F (base 16) Private
+
+48-D3-5D (hex) Private
+48D35D (base 16) Private
+
+D8-F2-CA (hex) Intel Corporate
+D8F2CA (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+B4-C6-2E (hex) Molex CMS
+B4C62E (base 16) Molex CMS
+ 2222 Wellington Court
+ Lisle IL 60532
+ US
+
+B8-25-9A (hex) Thalmic Labs
+B8259A (base 16) Thalmic Labs
+ 24 Charles Street West
+ Kitchener Ontario N2G 1H2
+ CA
+
+40-B3-0E (hex) Integrated Device Technology (Malaysia) Sdn. Bhd.
+40B30E (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd.
+ Phase 3, Bayan Lepas FIZ
+ Bayan Lepas Penang 11900
+ MY
+
+04-CE-7E (hex) NXP France Semiconductors France
+04CE7E (base 16) NXP France Semiconductors France
+ Parc les Algorithmes,Saint Aubin
+ Gif sur Yvette 91193
+ FR
+
+A4-0C-66 (hex) Shenzhen Colorful Yugong Technology and Development Co., Ltd.
+A40C66 (base 16) Shenzhen Colorful Yugong Technology and Development Co., Ltd.
+ 13F, Central Business Tower, No.88 Fuhua First Rd., Futian District, Shenzhen, Guangdong, China
+ Shenzhen Guangdong 518000
+ CN
+
+D8-A0-1D (hex) Espressif Inc.
+D8A01D (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+EC-FA-BC (hex) Espressif Inc.
+ECFABC (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+B4-E6-2D (hex) Espressif Inc.
+B4E62D (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+80-7D-3A (hex) Espressif Inc.
+807D3A (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+3C-71-BF (hex) Espressif Inc.
+3C71BF (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+54-5A-A6 (hex) Espressif Inc.
+545AA6 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+24-0A-C4 (hex) Espressif Inc.
+240AC4 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+98-CC-4D (hex) Shenzhen mantunsci co., LTD
+98CC4D (base 16) Shenzhen mantunsci co., LTD
+ 3 floor, 3 environmental protection industrial park, Nanshan District
+ Shenzhen Guangdong 518000
+ CN
+
+F8-E9-4E (hex) Apple, Inc.
+F8E94E (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+F4-06-16 (hex) Apple, Inc.
+F40616 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+BC-B8-63 (hex) Apple, Inc.
+BCB863 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+C4-E5-06 (hex) Piper Networks, Inc.
+C4E506 (base 16) Piper Networks, Inc.
+ 3636 Nobel Drive
+ San Diego CA 92122
+ US
+
+64-58-AD (hex) China Mobile IOT Company Limited
+6458AD (base 16) China Mobile IOT Company Limited
+ NO.8 Yu Ma Road, NanAn Area
+ Chongqing Chongqing 401336
+ CN
+
+A4-8C-C0 (hex) JLG Industries, Inc.
+A48CC0 (base 16) JLG Industries, Inc.
+ 13224 Fountainhead Plaza
+ Hagerstown MD 21742
+ US
+
+CC-75-E2 (hex) ARRIS Group, Inc.
+CC75E2 (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+EC-5C-68 (hex) CHONGQING FUGUI ELECTRONICS CO.,LTD.
+EC5C68 (base 16) CHONGQING FUGUI ELECTRONICS CO.,LTD.
+ Building D21,No.1, East Zone 1st Road,Xiyong Town,Shapingba District
+ Chongqing Chongqing 401332
+ CN
+
+44-EC-CE (hex) Juniper Networks
+44ECCE (base 16) Juniper Networks
+ 1133 Innovation Way
+ Sunnyvale CA 94089
+ US
+
+F8-A2-D6 (hex) Liteon Technology Corporation
+F8A2D6 (base 16) Liteon Technology Corporation
+ 4F, 90, Chien 1 Road
+ New Taipei City Taiwan 23585
+ TW
+
+74-36-6D (hex) Vodafone Italia S.p.A.
+74366D (base 16) Vodafone Italia S.p.A.
+ Via Lorenteggio nr. 240
+ Milan Italy 20147
+ IT
+
+A8-B4-56 (hex) Cisco Systems, Inc
+A8B456 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+F4-C7-C8 (hex) Kelvin Inc.
+F4C7C8 (base 16) Kelvin Inc.
+ 400 Centennial Parkway, Suite 190
+ Louisville CO 80027
+ US
+
+68-45-F1 (hex) TOSHIBA CLIENT SOLUTIONS CO., LTD.
+6845F1 (base 16) TOSHIBA CLIENT SOLUTIONS CO., LTD.
+ Tachihi Building No.2, 6-1-3, Sakae-Cho
+ Tachikawa-shi Tokyo 190-0003
+ JP
+
+84-DB-2F (hex) Sierra Wireless
+84DB2F (base 16) Sierra Wireless
+ 1381 Wireless Way
+ Richmond BC V6V 3A4
+ CA
+
+00-A0-D5 (hex) Sierra Wireless
+00A0D5 (base 16) Sierra Wireless
+ 13811 Wireless Way
+ Richmond RICHMOND B.C. V6V 3A4
+ CA
+
+28-EC-9A (hex) Texas Instruments
+28EC9A (base 16) Texas Instruments
+ 12500 TI Blvd
+ Dallas TX 75243
+ US
+
+70-B3-17 (hex) Cisco Systems, Inc
+70B317 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+94-E0-D6 (hex) China Dragon Technology Limited
+94E0D6 (base 16) China Dragon Technology Limited
+ B4 Bldg.Haoshan 1st Industry Park,
+ Shenzhen Guangdong 518104
+ CN
+
+CC-35-5A (hex) SecuGen Corporation
+CC355A (base 16) SecuGen Corporation
+ 2065 Martin Ave, Suite 108
+ Santa Clara CA 95050
+ US
+
+B0-D5-68 (hex) Shenzhen Cultraview Digital Technology Co., Ltd
+B0D568 (base 16) Shenzhen Cultraview Digital Technology Co., Ltd
+ F6,M6,Maqueling, High-tech park, Nanshan district
+ Shenzhen Guangdong 518057
+ CN
+
+DC-EB-69 (hex) Technicolor CH USA Inc.
+DCEB69 (base 16) Technicolor CH USA Inc.
+ 5030 Sugarloaf Parkway Bldg 6
+ Lawrenceville GA 30044
+ US
+
+94-A4-0C (hex) Diehl Metering GmbH
+94A40C (base 16) Diehl Metering GmbH
+ Industriestrasse 13
+ Ansbach 91522
+ DE
+
+8C-AE-DB (hex) NAG LLC
+8CAEDB (base 16) NAG LLC
+ Predelnaya st 57/2
+ Ekaterinburg Sverdlovskaya oblast 620161
+ RU
+
+D0-51-57 (hex) LEAX Arkivator Telecom
+D05157 (base 16) LEAX Arkivator Telecom
+ NanShan District YueHaiMen Street
+ ShenZhen GuangDong 518061
+ CN
+
+24-93-CA (hex) Voxtronic Austria
+2493CA (base 16) Voxtronic Austria
+ Modecenterstrasse 17 Objekt 1
+ Vienna 1110
+ AT
+
+90-7E-30 (hex) LARS
+907E30 (base 16) LARS
+ Swierkowa 14
+ Niepruszewo 64-320
+ PL
+
+44-1A-FA (hex) New H3C Technologies Co., Ltd
+441AFA (base 16) New H3C Technologies Co., Ltd
+ 466 Changhe Road, Binjiang District
+ Hangzhou Zhejiang 310052
+ CN
+
+00-4E-35 (hex) Hewlett Packard Enterprise
+004E35 (base 16) Hewlett Packard Enterprise
+ 8000 Foothills Blvd.
+ Roseville CA 95747
+ US
+
+00-1F-90 (hex) Actiontec Electronics, Inc
+001F90 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+20-76-00 (hex) Actiontec Electronics, Inc
+207600 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+F8-E4-FB (hex) Actiontec Electronics, Inc
+F8E4FB (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+18-1B-EB (hex) Actiontec Electronics, Inc
+181BEB (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+E8-6F-F2 (hex) Actiontec Electronics, Inc
+E86FF2 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+FC-2B-B2 (hex) Actiontec Electronics, Inc
+FC2BB2 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+50-00-84 (hex) Siemens Canada
+500084 (base 16) Siemens Canada
+ 300 Applewood Crescent
+ Concord Ontario L4K 5C7
+ CA
+
+34-02-9B (hex) Plexonics Technologies LImited
+34029B (base 16) Plexonics Technologies LImited
+ 1st Floor, 181/23 Industrial Area Phase 1
+ Chandigarh Punjab 160002
+ IN
+
+48-61-A3 (hex) Concern Axion JSC
+4861A3 (base 16) Concern Axion JSC
+ 90, M. Gorkogo St.
+ Izhevsk Udmurt Republic 426000
+ RU
+
+64-60-38 (hex) Hirschmann Automation and Control GmbH
+646038 (base 16) Hirschmann Automation and Control GmbH
+ Stuttgarter Straße 45-51
+ Neckartenzlingen D-72654
+ DE
+
+00-02-C7 (hex) ALPS ELECTRIC CO., LTD.
+0002C7 (base 16) ALPS ELECTRIC CO., LTD.
+ 1-2-1, Okinouchi, Sama-City,
+ Sama 00000
+ JP
+
+30-C3-D9 (hex) ALPS ELECTRIC CO., LTD.
+30C3D9 (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ Kakuda Miyagi-Pref 981-1595
+ JP
+
+04-76-6E (hex) ALPS ELECTRIC CO., LTD.
+04766E (base 16) ALPS ELECTRIC CO., LTD.
+ 6-3-36 Furukawanakazato,
+ Osaki Miyagi-pref 989-6181
+ JP
+
+E0-18-77 (hex) FUJITSU LIMITED
+E01877 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+A0-66-10 (hex) FUJITSU LIMITED
+A06610 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+00-1E-70 (hex) Cobham Antenna Systems
+001E70 (base 16) Cobham Antenna Systems
+ Cobham Centre, Fourth Avenue
+ Marlow Buckinghamshire SL7 1TF
+ GB
+
+EC-C5-7F (hex) Suzhou Pairlink Network Technology
+ECC57F (base 16) Suzhou Pairlink Network Technology
+ Room304, Building 4, No.209, Zhu Yuan Road, Suzhou new district
+ Suzhou Jiangsu 215011
+ CN
+
+B0-E7-DE (hex) Homa Technologies JSC
+B0E7DE (base 16) Homa Technologies JSC
+ Building 5, Quang Trung Software City, District 12
+ Ho Chi Minh 729226
+ VN
+
+F8-4D-FC (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.
+F84DFC (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.
+ No.555,qianmo road
+ Hangzhou Zhejiang 310052
+ CN
+
+00-6F-F2 (hex) MITSUMI ELECTRIC CO.,LTD.
+006FF2 (base 16) MITSUMI ELECTRIC CO.,LTD.
+ 2-11-2, Tsurumaki
+ Tama-shi Tokyo 206-8567
+ JP
+
+90-02-18 (hex) BSkyB Ltd
+900218 (base 16) BSkyB Ltd
+ 130 Kings Road
+ Brentwood Essex 08854
+ GB
+
+88-F5-6E (hex) HUAWEI TECHNOLOGIES CO.,LTD
+88F56E (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+D8-9B-3B (hex) HUAWEI TECHNOLOGIES CO.,LTD
+D89B3B (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+CC-3F-EA (hex) BAE Systems, Inc
+CC3FEA (base 16) BAE Systems, Inc
+ 1098 Clark St
+ Endicott NY 13760
+ US
+
+84-1C-70 (hex) zte corporation
+841C70 (base 16) zte corporation
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China
+ shenzhen guangdong 518057
+ CN
+
+1C-3B-8F (hex) Selve GmbH & Co. KG
+1C3B8F (base 16) Selve GmbH & Co. KG
+ Werdohler Landstraße 286
+ Lüdenscheid 58513
+ DE
+
+E4-E7-49 (hex) Hewlett Packard
+E4E749 (base 16) Hewlett Packard
+ 11445 Compaq Center Drive
+ Houston TX 77070
+ US
+
+CC-F7-35 (hex) Amazon Technologies Inc.
+CCF735 (base 16) Amazon Technologies Inc.
+ P.O Box 8102
+ Reno NV 89507
+ US
+
+38-83-9A (hex) SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.
+38839A (base 16) SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.
+ Bldg56A,6/F,Baotian Rd3,Xixiang Town,Baoan District,
+ Shenzhen Guangdong 518000
+ CN
+
+D4-92-34 (hex) NEC Corporation
+D49234 (base 16) NEC Corporation
+ 7-1 Shiba 5-chome
+ Minato-Ku Tokyo 108-8001
+ JP
+
+70-C9-C6 (hex) Cisco Systems, Inc
+70C9C6 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+70-EA-1A (hex) Cisco Systems, Inc
+70EA1A (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+78-DA-A2 (hex) Cynosure Technologies Co.,Ltd
+78DAA2 (base 16) Cynosure Technologies Co.,Ltd
+ Room 2708/2710, Building No.9A, Shenzhen Bay Science and Technology Ecological Park,Nanshan
+ Shenzhen city Guangdong Province 518057
+ CN
+
+2C-2B-F9 (hex) LG Innotek
+2C2BF9 (base 16) LG Innotek
+ 26, Hanamsandan 5beon-ro
+ Gwangju Gwangsan-gu 506-731
+ KR
+
+38-E2-6E (hex) ShenZhen Sweet Rain Electronics Co.,Ltd.
+38E26E (base 16) ShenZhen Sweet Rain Electronics Co.,Ltd.
+ Xinghua Road
+ Shenzhen Bao'an 518101
+ CN
+
+38-B1-9E (hex) IEEE Registration Authority
+38B19E (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+18-DF-B4 (hex) BOSUNG POWERTEC CO.,LTD.
+18DFB4 (base 16) BOSUNG POWERTEC CO.,LTD.
+ 70, Daechang-gil, Judeok-eup
+ Chungju-si Chungcheongbuk-do 27463
+ KR
+
+10-3D-0A (hex) Hui Zhou Gaoshengda Technology Co.,LTD
+103D0A (base 16) Hui Zhou Gaoshengda Technology Co.,LTD
+ No.75,Zhongkai High-Tech Development District,Huizhou
+ Hui Zhou Guangdong 516006
+ CN
+
+94-27-90 (hex) TCT mobile ltd
+942790 (base 16) TCT mobile ltd
+ No.86 hechang 7th road, zhongkai, Hi-Tech District
+ Hui Zhou Guang Dong 516006
+ CN
+
+64-29-ED (hex) AO PKK Milandr
+6429ED (base 16) AO PKK Milandr
+ Gergievsky prospekt, 5
+ Zelenograd Moscow 124498
+ RU
+
+F0-10-AB (hex) China Mobile (Hangzhou) Information Technology Co., Ltd.
+F010AB (base 16) China Mobile (Hangzhou) Information Technology Co., Ltd.
+ No. 1600 Yuhang Tong Road, Wuchang Street, Yuhang District
+ Hangzhou Zhejiang 310000
+ CN
+
+80-8F-1D (hex) TP-LINK TECHNOLOGIES CO.,LTD.
+808F1D (base 16) TP-LINK TECHNOLOGIES CO.,LTD.
+ Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan
+ Shenzhen Guangdong 518057
+ CN
+
+18-64-72 (hex) Aruba, a Hewlett Packard Enterprise Company
+186472 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+F8-2F-6A (hex) ITEL MOBILE LIMITED
+F82F6A (base 16) ITEL MOBILE LIMITED
+ RM B3 & B4 BLOCK B, KO FAI INDUSTRIAL BUILDING NO.7 KO FAI ROAD, YAU TONG, KLN, H.K
+ Hong Kong KOWLOON 999077
+ HK
+
+1C-2E-1B (hex) Suzhou Tremenet Communication Technology Co., Ltd.
+1C2E1B (base 16) Suzhou Tremenet Communication Technology Co., Ltd.
+ Room 413, Nangong Building, 399 Linquan Street, SIP
+ Suzhou Jiangsu 215123
+ CN
+
+00-24-6C (hex) Aruba, a Hewlett Packard Enterprise Company
+00246C (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+0C-F4-75 (hex) Zliide Technologies ApS
+0CF475 (base 16) Zliide Technologies ApS
+ Sverigesgade 6
+ Aarhus 8000
+ DK
+
+00-1A-3F (hex) Intelbras
+001A3F (base 16) Intelbras
+ BR 101, km 210, S/N°
+ São José Santa Catarina 88104800
+ BR
+
+B0-00-73 (hex) Wistron Neweb Corporation
+B00073 (base 16) Wistron Neweb Corporation
+ No.20,Park Avenue II,Hsinchu Science Park
+ Hsin-Chu R.O.C. 308
+ TW
+
+48-BD-0E (hex) Quanta Storage Inc.
+48BD0E (base 16) Quanta Storage Inc.
+ 3F. No.188, Wenhua 2nd Rd
+ Taoyuan City Guishan District 33383
+ TW
+
+34-99-71 (hex) Quanta Storage Inc.
+349971 (base 16) Quanta Storage Inc.
+ 3F. No.188, Wenhua 2nd Rd
+ Taoyuan City Guishan District 33383
+ TW
+
+98-B0-39 (hex) Nokia
+98B039 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+84-26-2B (hex) Nokia
+84262B (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+94-E9-8C (hex) Nokia
+94E98C (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+E4-81-84 (hex) Nokia
+E48184 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+BC-8D-0E (hex) Nokia
+BC8D0E (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+B0-75-4D (hex) Nokia
+B0754D (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+BC-6B-4D (hex) Nokia
+BC6B4D (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+A4-7B-2C (hex) Nokia
+A47B2C (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+8C-F7-73 (hex) Nokia
+8CF773 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+04-C2-41 (hex) Nokia
+04C241 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+94-B8-19 (hex) Nokia
+94B819 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+00-D0-F6 (hex) Nokia
+00D0F6 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+48-F8-E1 (hex) Nokia
+48F8E1 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+50-E0-EF (hex) Nokia
+50E0EF (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+3C-F0-11 (hex) Intel Corporate
+3CF011 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+08-EC-F5 (hex) Cisco Systems, Inc
+08ECF5 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+8C-5A-F8 (hex) Beijing Xiaomi Electronics Co., Ltd.
+8C5AF8 (base 16) Beijing Xiaomi Electronics Co., Ltd.
+ Building C, QingHe ShunShiJiaYe Technology Park, #66 ZhuFang Rd, HaiDian District
+ Beijing Beijing 10085
+ CN
+
+88-5A-06 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+885A06 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+50-5F-B5 (hex) Askey Computer Corp.
+505FB5 (base 16) Askey Computer Corp.
+ 10F, NO. 119, JIANKANG RD., ZHONGHE DIST.
+ NEW TAIPEI 235
+ TW
+
+44-3E-07 (hex) Electrolux
+443E07 (base 16) Electrolux
+ Corso Lino Zanussi 24
+ Porcia PORDENONE 33080
+ IT
+
+C0-8C-71 (hex) Motorola Mobility LLC, a Lenovo Company
+C08C71 (base 16) Motorola Mobility LLC, a Lenovo Company
+ 222 West Merchandise Mart Plaza
+ Chicago IL 60654
+ US
+
+5C-88-16 (hex) Rockwell Automation
+5C8816 (base 16) Rockwell Automation
+ 1 Allen-Bradley Dr.
+ Mayfield Heights OH 44124-6118
+ US
+
+2C-3F-0B (hex) Cisco Meraki
+2C3F0B (base 16) Cisco Meraki
+ 500 Terry A. Francois Blvd
+ San Francisco 94158
+ US
+
+1C-12-B0 (hex) Amazon Technologies Inc.
+1C12B0 (base 16) Amazon Technologies Inc.
+ P.O Box 8102
+ Reno NV 89507
+ US
+
+AC-D5-64 (hex) CHONGQING FUGUI ELECTRONICS CO.,LTD.
+ACD564 (base 16) CHONGQING FUGUI ELECTRONICS CO.,LTD.
+ Building D21,No.1, East Zone 1st Road,Xiyong Town,Shapingba District
+ Chongqing Chongqing 401332
+ CN
C0-D2-F3 (hex) Hui Zhou Gaoshengda Technology Co.,LTD
C0D2F3 (base 16) Hui Zhou Gaoshengda Technology Co.,LTD
@@ -29183,12 +29468,6 @@ BC6E64 (base 16) Sony Mobile Communications Inc
Shinagawa-ku Tokyo 140-0002
JP
-0C-B6-D2 (hex) D-Link International
-0CB6D2 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
B8-B7-F1 (hex) Wistron Neweb Corporation
B8B7F1 (base 16) Wistron Neweb Corporation
No.20,Park Avenue II,Hsinchu Science Park
@@ -29297,12 +29576,6 @@ F4EE14 (base 16) MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.
Shenzhen Guangdong 518057
CN
-20-A6-0C (hex) Xiaomi Communications Co Ltd
-20A60C (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
50-61-BF (hex) Cisco Systems, Inc
5061BF (base 16) Cisco Systems, Inc
80 West Tasman Drive
@@ -29405,9 +29678,6 @@ FC528D (base 16) Technicolor CH USA Inc.
Santa Clara CA 95054
US
-E8-9E-0C (hex) Private
-E89E0C (base 16) Private
-
54-4E-45 (hex) Private
544E45 (base 16) Private
@@ -29471,21 +29741,9 @@ F89910 (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd.
Bayan Lepas Penang 11900
MY
-50-E0-EF (hex) Nokia
-50E0EF (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
11-11-11 (hex) Private
111111 (base 16) Private
-2C-18-75 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
-2C1875 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
- 7F,Block A,Skyworth Building,
- Shenzhen Guangdong 518057
- CN
-
28-9E-FC (hex) Sagemcom Broadband SAS
289EFC (base 16) Sagemcom Broadband SAS
250, route de l'Empereur
@@ -29666,12 +29924,6 @@ EC6F0B (base 16) FADU, Inc.
Melbourne FL 32919
US
-3C-71-BF (hex) Espressif Inc.
-3C71BF (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
14-D1-69 (hex) HUAWEI TECHNOLOGIES CO.,LTD
14D169 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
@@ -30071,6 +30323,693 @@ CC08FB (base 16) TP-LINK TECHNOLOGIES CO.,LTD.
SURESNES HAUT DE SEINE 92150
FR
+C4-74-F8 (hex) Hot Pepper, Inc.
+C474F8 (base 16) Hot Pepper, Inc.
+ 5151 California Ave., Suite 100,
+ Irvine 92617
+ US
+
+1C-42-7D (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+1C427D (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+50-29-F5 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+5029F5 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+BC-38-65 (hex) JWCNETWORKS
+BC3865 (base 16) JWCNETWORKS
+ 114, Gasan digital 2-ro, Geumcheon-gu,
+ Seoul 08506
+ KR
+
+8C-FC-A0 (hex) Shenzhen Smart Device Technology Co., LTD.
+8CFCA0 (base 16) Shenzhen Smart Device Technology Co., LTD.
+ SSMEC Building,Gao Xin Nan First Avenue Hi-Tech Park South,Nanshan
+ Shenzhen GuangDong 518057
+ CN
+
+E8-1A-58 (hex) TECHNOLOGIC SYSTEMS
+E81A58 (base 16) TECHNOLOGIC SYSTEMS
+ 16525 E Laser Dr
+ Fountain Hills AZ 85268-6534
+ US
+
+2C-CA-0C (hex) WITHUS PLANET
+2CCA0C (base 16) WITHUS PLANET
+ 1604, O'BIZTOWER, Pyeong Chon, 126, Beolmal-ro, Dongan-gu
+ Anyang-si Gyeonggi-do 14057
+ KR
+
+4C-56-9D (hex) Apple, Inc.
+4C569D (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+14-C2-13 (hex) Apple, Inc.
+14C213 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+38-53-9C (hex) Apple, Inc.
+38539C (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+58-E6-BA (hex) Apple, Inc.
+58E6BA (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+B4-1D-2B (hex) Shenzhen YOUHUA Technology Co., Ltd
+B41D2B (base 16) Shenzhen YOUHUA Technology Co., Ltd
+ Room 407 Shenzhen University-town Business Park,Lishan Road,Taoyuan Street,Nanshan District
+ Shenzhen Guangdong 518055
+ CN
+
+3C-6A-2C (hex) IEEE Registration Authority
+3C6A2C (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+0C-BF-74 (hex) Morse Micro
+0CBF74 (base 16) Morse Micro
+ 113 / 2-4 Cornwallis Street
+ Eveleigh NSW 2015
+ AU
+
+FC-8F-7D (hex) SHENZHEN GONGJIN ELECTRONICS CO.,LT
+FC8F7D (base 16) SHENZHEN GONGJIN ELECTRONICS CO.,LT
+ SONGGANG
+ SHENZHEN GUANGDONG 518105
+ CN
+
+84-7F-3D (hex) Integrated Device Technology (Malaysia) Sdn. Bhd.
+847F3D (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd.
+ Phase 3, Bayan Lepas FIZ
+ Bayan Lepas Penang 11900
+ MY
+
+90-0E-B3 (hex) Shenzhen Amediatech Technology Co., Ltd.
+900EB3 (base 16) Shenzhen Amediatech Technology Co., Ltd.
+ 2th floor, block A, building B, Minsheng Industrial Zone, Longhua District
+ Shenzhen Guangdong 518109
+ CN
+
+BC-26-C7 (hex) Cisco Systems, Inc
+BC26C7 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+CC-BB-FE (hex) HUAWEI TECHNOLOGIES CO.,LTD
+CCBBFE (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+20-28-3E (hex) HUAWEI TECHNOLOGIES CO.,LTD
+20283E (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+0C-41-01 (hex) Ruichi Auto Technology (Guangzhou) Co., Ltd.
+0C4101 (base 16) Ruichi Auto Technology (Guangzhou) Co., Ltd.
+ No. 171 Haibin Road, Nansha District, Guangzhou, China
+ Guangzhou Guangdong Province 511400
+ CN
+
+F0-A2-25 (hex) Private
+F0A225 (base 16) Private
+
+00-B4-F5 (hex) DongGuan Siyoto Electronics Co., Ltd
+00B4F5 (base 16) DongGuan Siyoto Electronics Co., Ltd
+ Hecheng Industrial District, QiaoTou Town
+ DongGuan City Guangdong 523520
+ CN
+
+F8-C2-49 (hex) Private
+F8C249 (base 16) Private
+
+34-DA-C1 (hex) SAE Technologies Development(Dongguan) Co., Ltd.
+34DAC1 (base 16) SAE Technologies Development(Dongguan) Co., Ltd.
+ Winnerway Industrial Area,Nancheng
+ Dongguan City Guangdong Province 523087
+ CN
+
+60-CE-92 (hex) The Refined Industry Company Limited
+60CE92 (base 16) The Refined Industry Company Limited
+ 7/F Sun King Factory Bldg,1 - 7 Shing Chuen Road, Shatin, NT
+ Hong Kong NT 000000
+ HK
+
+48-A6-B8 (hex) Sonos, Inc.
+48A6B8 (base 16) Sonos, Inc.
+ 614 Chapala St
+ Santa Barbara CA 93101
+ US
+
+B4-77-48 (hex) Shenzhen Neoway Technology Co.,Ltd.
+B47748 (base 16) Shenzhen Neoway Technology Co.,Ltd.
+ 4F-2#,Lian Jian Science & Industry Park,Huarong Road,Dalang Street,Longhua District
+ Shenzhen Guangdong 518000
+ CN
+
+A8-3F-A1 (hex) IEEE Registration Authority
+A83FA1 (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+B8-31-B5 (hex) Microsoft Corporation
+B831B5 (base 16) Microsoft Corporation
+ One Microsoft Way
+ REDMOND WA 98052
+ US
+
+04-6B-25 (hex) SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+046B25 (base 16) SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+ NO.198 FIRST SECTION,SNOW MOUNTAIN AVENUE, JINYUAN TOWN, DAYI COUNTY,
+ CHENGDU SICHUAN 611330
+ CN
+
+E8-AD-A6 (hex) Sagemcom Broadband SAS
+E8ADA6 (base 16) Sagemcom Broadband SAS
+ 250, route de l'Empereur
+ Rueil Malmaison Cedex hauts de seine 92848
+ FR
+
+00-00-BD (hex) Mitsubishi Cable Industries, Ltd. / Ryosei Systems
+0000BD (base 16) Mitsubishi Cable Industries, Ltd. / Ryosei Systems
+ 8, NISHINO-CHO, HIGASHI-MUKOJIMA
+ AMAGASAKI HYOGO 660-0856
+ JP
+
+70-6D-15 (hex) Cisco Systems, Inc
+706D15 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+84-E5-D8 (hex) Guangdong UNIPOE IoT Technology Co.,Ltd.
+84E5D8 (base 16) Guangdong UNIPOE IoT Technology Co.,Ltd.
+ 11th Fl., BLDG. B1, Guangda WE Valley, Songshan Lake District
+ Dongguan Guangdong 523808
+ CN
+
+10-98-C3 (hex) Murata Manufacturing Co., Ltd.
+1098C3 (base 16) Murata Manufacturing Co., Ltd.
+ 1-10-1, Higashikotari
+ Nagaokakyo-shi Kyoto 617-8555
+ JP
+
+3C-01-EF (hex) Sony Mobile Communications Inc
+3C01EF (base 16) Sony Mobile Communications Inc
+ 4-12-3 Higashi – Shinagawa
+ Shinagawa-ku Tokyo 140-0002
+ JP
+
+18-BE-92 (hex) Delta Networks, Inc.
+18BE92 (base 16) Delta Networks, Inc.
+ 256 Yang Guang Street, Neihu
+ Taipei Taiwan 11491
+ TW
+
+D8-96-85 (hex) GoPro
+D89685 (base 16) GoPro
+ 3000 Clearview Way
+ San Mateo CA 94402
+ US
+
+80-FB-F0 (hex) Quectel Wireless Solutions Co., Ltd.
+80FBF0 (base 16) Quectel Wireless Solutions Co., Ltd.
+ 7th Floor, Hongye Building, No.1801 Hongmei Road, Xuhui District
+ Shanghai 200233
+ CN
+
+90-63-3B (hex) Samsung Electronics Co.,Ltd
+90633B (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+78-23-27 (hex) Samsung Electronics Co.,Ltd
+782327 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+1C-24-CD (hex) Askey Computer Corp.
+1C24CD (base 16) Askey Computer Corp.
+ 10F, No.119, JIANKANG RD.,ZHINGHE DIST,
+ NEW TAIPEI CITY 23585
+ TW
+
+B8-2C-A0 (hex) Resideo
+B82CA0 (base 16) Resideo
+ 2 Corporate Center Dr.
+ Melville NY 11747
+ US
+
+F8-A4-5F (hex) Xiaomi Communications Co Ltd
+F8A45F (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+8C-BE-BE (hex) Xiaomi Communications Co Ltd
+8CBEBE (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+64-09-80 (hex) Xiaomi Communications Co Ltd
+640980 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+98-FA-E3 (hex) Xiaomi Communications Co Ltd
+98FAE3 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+18-59-36 (hex) Xiaomi Communications Co Ltd
+185936 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+9C-99-A0 (hex) Xiaomi Communications Co Ltd
+9C99A0 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+60-A7-30 (hex) Shenzhen Yipinfang Internet Technology Co.,Ltd
+60A730 (base 16) Shenzhen Yipinfang Internet Technology Co.,Ltd
+ Shenzhen Konka R & D Building, 28th floor 21
+ GuangDong Nanshan District 518000
+ CN
+
+A0-B5-49 (hex) Arcadyan Corporation
+A0B549 (base 16) Arcadyan Corporation
+ No.8, Sec.2, Guangfu Rd.
+ Hsinchu City Hsinchu 30071
+ TW
+
+AC-AE-19 (hex) Roku, Inc
+ACAE19 (base 16) Roku, Inc
+ 150 Winchester Circle
+ Los Gatos CA 95032
+ US
+
+C4-0B-CB (hex) Xiaomi Communications Co Ltd
+C40BCB (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+EC-D0-9F (hex) Xiaomi Communications Co Ltd
+ECD09F (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+F4-F5-DB (hex) Xiaomi Communications Co Ltd
+F4F5DB (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+E4-46-DA (hex) Xiaomi Communications Co Ltd
+E446DA (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+18-F0-E4 (hex) Xiaomi Communications Co Ltd
+18F0E4 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources , No.68 Qinghe Middle Street , Haidian District
+ Beijing Beijing 100089
+ CN
+
+48-35-2E (hex) Shenzhen Wolck Network Product Co.,LTD
+48352E (base 16) Shenzhen Wolck Network Product Co.,LTD
+ 9# BuJi BanTian LongBi Industry Area
+ Shenzhen Guangdong 518129
+ CN
+
+9C-2E-A1 (hex) Xiaomi Communications Co Ltd
+9C2EA1 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+50-A0-09 (hex) Xiaomi Communications Co Ltd
+50A009 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+20-A6-0C (hex) Xiaomi Communications Co Ltd
+20A60C (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+7C-57-3C (hex) Aruba, a Hewlett Packard Enterprise Company
+7C573C (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+04-1E-FA (hex) BISSELL Homecare, Inc.
+041EFA (base 16) BISSELL Homecare, Inc.
+ 2345 Walker Ave NW
+ Grand Rapids MI 49544
+ US
+
+A4-11-62 (hex) Arlo Technology
+A41162 (base 16) Arlo Technology
+ 350 East Plumeria Drive
+ san jose CA 95134
+ US
+
+00-25-DF (hex) Private
+0025DF (base 16) Private
+
+E8-9E-0C (hex) Private
+E89E0C (base 16) Private
+
+3C-91-80 (hex) Liteon Technology Corporation
+3C9180 (base 16) Liteon Technology Corporation
+ 4F, 90, Chien 1 Road
+ New Taipei City Taiwan 23585
+ TW
+
+20-32-6C (hex) Samsung Electronics Co.,Ltd
+20326C (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+A8-34-6A (hex) Samsung Electronics Co.,Ltd
+A8346A (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+E4-4C-C7 (hex) IEEE Registration Authority
+E44CC7 (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+B0-6F-E0 (hex) Samsung Electronics Co.,Ltd
+B06FE0 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+70-BF-92 (hex) GN Audio A/S
+70BF92 (base 16) GN Audio A/S
+ Lautrupbjerg 7
+ Ballerup DK-2750
+ DK
+
+94-40-C9 (hex) Hewlett Packard Enterprise
+9440C9 (base 16) Hewlett Packard Enterprise
+ 8000 Foothills Blvd.
+ Roseville CA 95747
+ US
+
+90-6D-05 (hex) BXB ELECTRONICS CO., LTD
+906D05 (base 16) BXB ELECTRONICS CO., LTD
+ 6F.-1, NO.288-5, Xinya Rd., Qianzhen Dist.
+ Kaohsiung 80673
+ TW
+
+74-5B-C5 (hex) IEEE Registration Authority
+745BC5 (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+A4-C3-F0 (hex) Intel Corporate
+A4C3F0 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+80-FD-7A (hex) BLU Products Inc
+80FD7A (base 16) BLU Products Inc
+ 10814 NW 33rd Street
+ Miami FL 33172
+ US
+
+CC-DC-55 (hex) Dragonchip Limited
+CCDC55 (base 16) Dragonchip Limited
+ Room 601-2, 6/F, IC Development Centre, No. 6 Science Park West Avenue, Hong Kong Science Park, Shatin, N.T.
+ Hong Kong NA
+ HK
+
+A0-51-0B (hex) Intel Corporate
+A0510B (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+00-1A-9A (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+001A9A (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+A0-89-E4 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+A089E4 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+2C-18-75 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+2C1875 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+90-F8-91 (hex) Kaonmedia CO., LTD.
+90F891 (base 16) Kaonmedia CO., LTD.
+ 884-3, Seongnam-daero, Bundang-gu
+ Seongnam-si Gyeonggi-do 13517
+ KR
+
+44-5D-5E (hex) SHENZHEN Coolkit Technology CO.,LTD
+445D5E (base 16) SHENZHEN Coolkit Technology CO.,LTD
+ B09 2nd Floor, T6 ArtZone XiLi, Nanshan Dist
+ shenzhen guangdong 518110
+ CN
+
+98-9B-CB (hex) AVM Audiovisuelles Marketing und Computersysteme GmbH
+989BCB (base 16) AVM Audiovisuelles Marketing und Computersysteme GmbH
+ Alt-Moabit 95
+ Berlin Berlin 10559
+ DE
+
+60-03-A6 (hex) Inteno Broadband Technology AB
+6003A6 (base 16) Inteno Broadband Technology AB
+ Stensätravägen 13
+ Skärholmen SE 127 39
+ SE
+
+B0-BB-8B (hex) WAVETEL TECHNOLOGY LIMITED
+B0BB8B (base 16) WAVETEL TECHNOLOGY LIMITED
+ ROOM 1611B, 16/F, HO KING COMMERCIAL CENTRE, 2-16 FAYUEN STREET, MONGKOK, KOWLOON
+ HONGKONG 999077
+ CN
+
+34-41-A8 (hex) ER-Telecom
+3441A8 (base 16) ER-Telecom
+ Ovchinnikovskaya embankment, 20, Building 1
+ Moscow 115324
+ RU
+
+6C-C4-D5 (hex) HMD Global Oy
+6CC4D5 (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+6C-A9-28 (hex) HMD Global Oy
+6CA928 (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+70-AC-D7 (hex) Shenzhen YOUHUA Technology Co., Ltd
+70ACD7 (base 16) Shenzhen YOUHUA Technology Co., Ltd
+ Room 407 Shenzhen University-town Business Park,Lishan Road,Taoyuan Street,Nanshan District
+ Shenzhen Guangdong 518055
+ CN
+
+98-46-0A (hex) Apple, Inc.
+98460A (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+AC-88-FD (hex) Apple, Inc.
+AC88FD (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+90-A3-65 (hex) HMD Global Oy
+90A365 (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+BC-02-4A (hex) HMD Global Oy
+BC024A (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+14-9D-99 (hex) Apple, Inc.
+149D99 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+D8-FE-E3 (hex) D-Link International
+D8FEE3 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+C4-A8-1D (hex) D-Link International
+C4A81D (base 16) D-Link International
+ 1 Internal Business Park, #03-12,
+ SINGAPORE Singapore 609917
+ SG
+
+1C-7E-E5 (hex) D-Link International
+1C7EE5 (base 16) D-Link International
+ 1 International Business Park, #03-12, The Synergy
+ SINGAPORE 609917
+ SG
+
+28-10-7B (hex) D-Link International
+28107B (base 16) D-Link International
+ 1 International Business Park, #03-12, The Synergy
+ SINGAPORE 609917
+ SG
+
+90-94-E4 (hex) D-Link International
+9094E4 (base 16) D-Link International
+ 1 International Business Park, #03-12, The Synergy
+ SINGAPORE 609917
+ SG
+
+1C-BD-B9 (hex) D-Link International
+1CBDB9 (base 16) D-Link International
+ 1 INTERNATIONAL BUSINESS PARK,
+ SINGAPORE 609917
+ SG
+
+1C-5F-2B (hex) D-Link International
+1C5F2B (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+10-BE-F5 (hex) D-Link International
+10BEF5 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+C0-A0-BB (hex) D-Link International
+C0A0BB (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+B0-C5-54 (hex) D-Link International
+B0C554 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+6C-19-8F (hex) D-Link International
+6C198F (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+F8-E9-03 (hex) D-Link International
+F8E903 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+40-9B-CD (hex) D-Link International
+409BCD (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+0C-B6-D2 (hex) D-Link International
+0CB6D2 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+00-87-64 (hex) Cisco Systems, Inc
+008764 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+8C-E7-48 (hex) Private
+8CE748 (base 16) Private
+
+A8-BF-3C (hex) HDV Phoelectron Technology Limited
+A8BF3C (base 16) HDV Phoelectron Technology Limited
+ Room 1103, Hang Seng Mongkok Building, 677 Nathan Road,Mongkok
+ Kowloon Hong Kong 518103
+ CN
+
+D4-F5-27 (hex) SIEMENS AG
+D4F527 (base 16) SIEMENS AG
+ Oestliche Rheinbrückenstraße 50
+ Karlsruhe Baden-Württemberg 76181
+ DE
+
+84-6F-CE (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+846FCE (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+70-1E-68 (hex) Hanna Instruments, Inc.
+701E68 (base 16) Hanna Instruments, Inc.
+ 584 Park East Dr.
+ Woonsocket RI 02895
+ US
+
0C-6F-9C (hex) Shaw Communications Inc.
0C6F9C (base 16) Shaw Communications Inc.
Suite 900, 630 3rd Avenue S.W.
@@ -30233,18 +31172,6 @@ ACCF85 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Neihu District 114
TW
-AC-F1-DF (hex) D-Link International
-ACF1DF (base 16) D-Link International
- 1 International Business Park, #03-12, The Synergy
- SINGAPORE 609917
- SG
-
-FC-75-16 (hex) D-Link International
-FC7516 (base 16) D-Link International
- 1 International Business Park, #03-12, The Synergy
- SINGAPORE 609917
- SG
-
7C-18-CD (hex) E-TRON Co.,Ltd.
7C18CD (base 16) E-TRON Co.,Ltd.
66-11, Nonhyeon 2-dong, Gangnam-gu
@@ -30617,12 +31544,6 @@ F49FF3 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Nagaokakyo-shi Kyoto 617-8555
JP
-28-A1-83 (hex) ALPS ELECTRIC CO.,LTD.
-28A183 (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- Kakuda Miyagi-Pref 981-1595
- JP
-
5C-F8-A1 (hex) Murata Manufacturing Co., Ltd.
5CF8A1 (base 16) Murata Manufacturing Co., Ltd.
1-10-1 Higashikotari
@@ -31073,18 +31994,6 @@ F84F57 (base 16) Cisco Systems, Inc
San Jose CA 95134
US
-AC-7A-4D (hex) ALPS ELECTRIC CO.,LTD.
-AC7A4D (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- KAKUDA-CITY MIYAGI-PREF 981-1595
- JP
-
-FC-62-B9 (hex) ALPS ELECTRIC CO.,LTD.
-FC62B9 (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- kakuda-city Miyagi-Pref 981-1595
- JP
-
00-10-A6 (hex) Cisco Systems, Inc
0010A6 (base 16) Cisco Systems, Inc
170 WEST TASMAN DRIVE
@@ -31145,24 +32054,6 @@ F45FD4 (base 16) Cisco SPVTG
Lawrenceville GA 30044
US
-00-23-06 (hex) ALPS ELECTRIC CO.,LTD.
-002306 (base 16) ALPS ELECTRIC CO.,LTD.
- 1-2-1, Okinouchi,
- Soma-city, Fukushima-pref., 976-8501
- JP
-
-00-1E-3D (hex) ALPS ELECTRIC CO.,LTD.
-001E3D (base 16) ALPS ELECTRIC CO.,LTD.
- 1-2-1, Okinouchi,
- Soma-city, Fukushima-pref., 976-8501
- JP
-
-00-19-C1 (hex) ALPS ELECTRIC CO.,LTD.
-0019C1 (base 16) ALPS ELECTRIC CO.,LTD.
- 1-2-1, Okinouchi,
- Soma-city, Fukushima-pref., 976-8501
- JP
-
84-7D-50 (hex) Holley Metering Limited
847D50 (base 16) Holley Metering Limited
181 Wuchang Avenue
@@ -31379,12 +32270,6 @@ DCA3AC (base 16) RBcloudtech
Dalian Liaoning 116600
CN
-EC-A9-FA (hex) GUANGDONG GENIUS TECHNOLOGY CO.,LTD.
-ECA9FA (base 16) GUANGDONG GENIUS TECHNOLOGY CO.,LTD.
- #126,BBK Road,Wusha,Chang'An
- Dong Guan Guang Dong 523860
- CN
-
30-0C-23 (hex) zte corporation
300C23 (base 16) zte corporation
12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China
@@ -31895,12 +32780,6 @@ B88EC6 (base 16) Stateless Networks
Nanshan District, Shenzhen Guangdong 518000
CN
-40-A5-EF (hex) Shenzhen Four Seas Global Link Network Technology Co., Ltd.
-40A5EF (base 16) Shenzhen Four Seas Global Link Network Technology Co., Ltd.
- Room 607-610, Block B, TAOJINDI Electronic Business Incubation Base, Tenglong Road, Longhua District, Shenzhen, China.
- shenzhen guangdong 518000
- CN
-
7C-7A-53 (hex) Phytrex Technology Corp.
7C7A53 (base 16) Phytrex Technology Corp.
8F-16, No.81, Shuili Rd.,
@@ -32768,12 +33647,6 @@ DC5E36 (base 16) Paterson Technology
Genthin Sachsen-Anhalt 39307
DE
-9C-D6-43 (hex) D-Link International
-9CD643 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
28-FC-51 (hex) The Electric Controller and Manufacturing Co., LLC
28FC51 (base 16) The Electric Controller and Manufacturing Co., LLC
PO Box 468
@@ -33221,12 +34094,6 @@ E8481F (base 16) Advanced Automotive Antennas
Viladecavalls Barcelona 08232
ES
-18-D6-CF (hex) Kurth Electronic GmbH
-18D6CF (base 16) Kurth Electronic GmbH
- Im Scherbental 5
- Eningen BW 72800
- US
-
E0-7F-88 (hex) EVIDENCE Network SIA
E07F88 (base 16) EVIDENCE Network SIA
Zilupes 4A
@@ -33317,12 +34184,6 @@ D8B04C (base 16) Jinan USR IOT Technology Co., Ltd.
JINAN SHANDONG 250101
CN
-64-6E-EA (hex) Iskratel d.o.o.
-646EEA (base 16) Iskratel d.o.o.
- Ljubljanska cesta 24a
- Kranj 4000
- SI
-
04-3D-98 (hex) ChongQing QingJia Electronics CO.,LTD
043D98 (base 16) ChongQing QingJia Electronics CO.,LTD
No.1,DianCe Village JiangBei District
@@ -36605,12 +37466,6 @@ D8E3AE (base 16) CIRTEC MEDICAL SYSTEMS
Los Gatos CA 95032
US
-A8-39-44 (hex) Actiontec Electronics, Inc
-A83944 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
FC-1F-C0 (hex) EURECAM
FC1FC0 (base 16) EURECAM
BAT 9 - Miniparc
@@ -38090,12 +38945,6 @@ D0D286 (base 16) Beckman Coulter K.K.
SEOUL 153-768
KR
-00-26-15 (hex) Teracom Limited
-002615 (base 16) Teracom Limited
- B-84
- Noida Uttar Pradesh 201301
- IN
-
00-26-16 (hex) Rosemount Inc.
002616 (base 16) Rosemount Inc.
8200 Market Boulevard
@@ -40766,12 +41615,6 @@ D0D286 (base 16) Beckman Coulter K.K.
AURANGABAD MAHARASTRA 431136
IN
-00-19-3B (hex) Wilibox Deliberant Group LLC
-00193B (base 16) Wilibox Deliberant Group LLC
- 1440 Dutch Valley Place
- Atlanta GA 30324
- US
-
00-19-2F (hex) Cisco Systems, Inc
00192F (base 16) Cisco Systems, Inc
80 West Tasman Drive
@@ -42725,12 +43568,6 @@ D0D286 (base 16) Beckman Coulter K.K.
San Jose CA 94568
US
-00-13-1E (hex) Peiker acustic GmbH & Co. KG
-00131E (base 16) Peiker acustic GmbH & Co. KG
- Max-Planck-Straße 32
- Friedrichsdorf Hessen 61381
- DE
-
00-13-23 (hex) Cap Co., Ltd.
001323 (base 16) Cap Co., Ltd.
11F,684-2,deungchon 3-dong,gangseo-gu
@@ -44135,12 +44972,6 @@ D0D286 (base 16) Beckman Coulter K.K.
Langebrück Saxony D-01465
DE
-00-0C-17 (hex) AJA Video Systems Inc
-000C17 (base 16) AJA Video Systems Inc
- 443 Crown Point Circle
- Grass Valley California 95945
- US
-
00-0C-04 (hex) Tecnova
000C04 (base 16) Tecnova
1486 St. Paul Ave.
@@ -47765,12 +48596,6 @@ D0D286 (base 16) Beckman Coulter K.K.
Sijhih City Taipei County 22102
TW
-00-D0-2D (hex) ADEMCO
-00D02D (base 16) ADEMCO
- 165 EILEEN WAY
- SYOSSET NY 11791
- US
-
00-D0-7C (hex) KOYO ELECTRONICS INC. CO.,LTD.
00D07C (base 16) KOYO ELECTRONICS INC. CO.,LTD.
1-171 TENJIN-CHO KODAIRA
@@ -48017,12 +48842,6 @@ D0D286 (base 16) Beckman Coulter K.K.
PASADENA CA 91101
US
-00-D0-50 (hex) ISKRATEL
-00D050 (base 16) ISKRATEL
- LJUBLJANSKA C. 24A
-
- SI
-
00-D0-CB (hex) DASAN CO., LTD.
00D0CB (base 16) DASAN CO., LTD.
DASAN Tower 7F, 49 Daewangpangyo-ro644beon-gil
@@ -51728,12 +52547,6 @@ D07E35 (base 16) Intel Corporate
HILLSBORO OR 97124
US
-6C-F3-7F (hex) Aruba Networks
-6CF37F (base 16) Aruba Networks
- 1344 Crossman Ave.
- Sunnyvale CA 94089
- US
-
60-5B-B4 (hex) AzureWave Technology Inc.
605BB4 (base 16) AzureWave Technology Inc.
8F., No. 94, Baozhong Rd.,
@@ -51806,12 +52619,6 @@ E874E6 (base 16) ADB Broadband Italia
MILANO 20126
IT
-00-24-7B (hex) Actiontec Electronics, Inc
-00247B (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
68-9C-5E (hex) AcSiP Technology Corp.
689C5E (base 16) AcSiP Technology Corp.
3F., No.22, Dalin Rd.
@@ -51956,12 +52763,6 @@ FCB4E6 (base 16) ASKEY COMPUTER CORP
TAIWAN NEW TAIPEI 23585
TW
-F0-5C-19 (hex) Aruba Networks
-F05C19 (base 16) Aruba Networks
- 1344 Crossman Ave
- Sunnyvale CA 94089
- US
-
70-AA-B2 (hex) BlackBerry RTS
70AAB2 (base 16) BlackBerry RTS
451 Phillip Street
@@ -52244,12 +53045,6 @@ E0B9E5 (base 16) Technicolor
St. Laurent Quebec H4S 2A4
CA
-00-0B-5D (hex) FUJITSU LIMITED
-000B5D (base 16) FUJITSU LIMITED
- Musashi-kosugi Tower Place Bldg.,
- KAWASAKI 211 Kanagawa
- JP
-
F4-CA-E5 (hex) FREEBOX SAS
F4CAE5 (base 16) FREEBOX SAS
8 rue de la Ville l'Eveque
@@ -52532,12 +53327,6 @@ E45D75 (base 16) Samsung Electronics Co.,Ltd
Hod Hasharon Hod Hasharon 4501309
IL
-80-0D-D7 (hex) Latticework, Inc
-800DD7 (base 16) Latticework, Inc
- 333 Cobalt Way, Suite 108
- Sunnyvale CA 94085
- US
-
40-2E-28 (hex) MiXTelematics
402E28 (base 16) MiXTelematics
Blaauwklip Office Park 2
@@ -52736,12 +53525,6 @@ D8D723 (base 16) IDS, Inc
Sterling Heights MI 48312
US
-70-3A-0E (hex) Aruba Networks
-703A0E (base 16) Aruba Networks
- 1344 Crossman Ave
- Sunnyvale CA 94089
- US
-
70-54-D2 (hex) PEGATRON CORPORATION
7054D2 (base 16) PEGATRON CORPORATION
5F No. 76, Ligong St., Beitou District
@@ -53066,9 +53849,6 @@ B4D5BD (base 16) Intel Corporate
Hangzhou Zhejiang, 310053
CN
-24-BF-74 (hex) Private
-24BF74 (base 16) Private
-
B8-E7-79 (hex) 9Solutions Oy
B8E779 (base 16) 9Solutions Oy
Teknologiantie 2
@@ -53273,12 +54053,6 @@ D8B02E (base 16) Guangzhou Zonerich Business Machine Co., LTD.
Tsukuba Mirai-shi Ibaraki-ken 300-2493
JP
-A0-20-A6 (hex) Espressif Inc.
-A020A6 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
C4-BB-4C (hex) Zebra Information Tech Co. Ltd
C4BB4C (base 16) Zebra Information Tech Co. Ltd
Room 415, No.569 Anchi Road, JiaDing District
@@ -53465,12 +54239,6 @@ C81FBE (base 16) HUAWEI TECHNOLOGIES CO.,LTD
MianYang SiChuan PRC 621000
CN
-E4-1D-2D (hex) Mellanox Technologies, Inc.
-E41D2D (base 16) Mellanox Technologies, Inc.
- 350 Oakmead Parkway, Suite 100
- Sunnyvale CA 94085
- US
-
B8-00-18 (hex) Htel
B80018 (base 16) Htel
Dunchon-dearo, Jungwon-gu
@@ -53699,12 +54467,6 @@ F0038C (base 16) AzureWave Technology Inc.
New Taipei City Taiwan 231
TW
-B4-5D-50 (hex) Aruba Networks
-B45D50 (base 16) Aruba Networks
- 1344 Crossman Ave
- Sunnyvale CA 94089
- US
-
00-1E-7D (hex) Samsung Electronics Co.,Ltd
001E7D (base 16) Samsung Electronics Co.,Ltd
#94-1, Imsoo-Dong
@@ -53849,12 +54611,6 @@ D80F99 (base 16) Hon Hai Precision Ind. Co.,Ltd.
Chongqing Chongqing 401332
CN
-A0-AB-1B (hex) D-Link International
-A0AB1B (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
5C-49-79 (hex) AVM Audiovisuelles Marketing und Computersysteme GmbH
5C4979 (base 16) AVM Audiovisuelles Marketing und Computersysteme GmbH
Alt-Moabit 95
@@ -54821,30 +55577,6 @@ ACAB2E (base 16) Beijing LasNubes Technology Co., Ltd.
Beijing 100025
CN
-10-E8-78 (hex) Nokia
-10E878 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-48-F7-F1 (hex) Nokia
-48F7F1 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-4C-C9-4F (hex) Nokia
-4CC94F (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-1C-EA-1B (hex) Nokia
-1CEA1B (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
B4-F8-1E (hex) Kinova
B4F81E (base 16) Kinova
6110, rue Doris-Lussier,
@@ -55058,36 +55790,6 @@ A0CC2B (base 16) Murata Manufacturing Co., Ltd.
Camas WA 98607
US
-28-E3-1F (hex) Xiaomi Communications Co Ltd
-28E31F (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-0C-1D-AF (hex) Xiaomi Communications Co Ltd
-0C1DAF (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-14-F6-5A (hex) Xiaomi Communications Co Ltd
-14F65A (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-74-23-44 (hex) Xiaomi Communications Co Ltd
-742344 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-F0-B4-29 (hex) Xiaomi Communications Co Ltd
-F0B429 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
94-E9-79 (hex) Liteon Technology Corporation
94E979 (base 16) Liteon Technology Corporation
4F, 90, Chien 1 Road
@@ -55178,12 +55880,6 @@ A0E0AF (base 16) Cisco Systems, Inc
Subiaco WA 6008
AU
-10-62-EB (hex) D-Link International
-1062EB (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
E0-A7-00 (hex) Verkada Inc
E0A700 (base 16) Verkada Inc
325 Sharon Park Drive, Suite 519
@@ -55220,12 +55916,6 @@ F46E24 (base 16) NEC Personal Computers, Ltd.
Shenzhen City 518125
CN
-78-32-1B (hex) D-Link International
-78321B (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
EC-51-BC (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
EC51BC (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
NO.18 HAIBIN ROAD,
@@ -55478,9 +56168,6 @@ ECC06A (base 16) PowerChord Group Limited
Annandale NSW 2000
AU
-C0-22-50 (hex) Private
-C02250 (base 16) Private
-
00-94-A1 (hex) F5 Networks, Inc.
0094A1 (base 16) F5 Networks, Inc.
401 Elliott Ave. W.
@@ -55571,9 +56258,6 @@ F86CE1 (base 16) Taicang T&W Electronics
London W1T 1HY
GB
-40-A9-3F (hex) Private
-40A93F (base 16) Private
-
5C-77-76 (hex) TCT mobile ltd
5C7776 (base 16) TCT mobile ltd
No.86 hechang 7th road, zhongkai, Hi-Tech District
@@ -55664,12 +56348,6 @@ FCF528 (base 16) Zyxel Communications Corporation
Jaipur Rajasthan 302019
IN
-04-F1-28 (hex) HMD Global Oy
-04F128 (base 16) HMD Global Oy
- Karaportti 2
- Espoo 02610
- FI
-
F0-65-C2 (hex) Yanfeng Visteon Electronics Technology (Shanghai) Co.,Ltd.
F065C2 (base 16) Yanfeng Visteon Electronics Technology (Shanghai) Co.,Ltd.
1001 North Qin Zhou Road
@@ -55808,12 +56486,6 @@ F8B568 (base 16) IEEE Registration Authority
Shenzhen Guangdong 518055
CN
-0C-98-38 (hex) Xiaomi Communications Co Ltd
-0C9838 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
00-04-96 (hex) Extreme Networks, Inc.
000496 (base 16) Extreme Networks, Inc.
3585 Monroe Street
@@ -55964,12 +56636,6 @@ A013CB (base 16) Fiberhome Telecommunication Technologies Co.,LTD
Roseville CA 95747
US
-70-F2-20 (hex) Actiontec Electronics, Inc
-70F220 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
4C-C2-06 (hex) Somfy
4CC206 (base 16) Somfy
50 avenue du nouveau monde
@@ -56204,9 +56870,6 @@ E0E62E (base 16) TCT mobile ltd
Hui Zhou Guang Dong 516006
CN
-00-A0-85 (hex) Private
-00A085 (base 16) Private
-
94-B8-6D (hex) Intel Corporate
94B86D (base 16) Intel Corporate
Lot 8, Jalan Hi-Tech 2/3
@@ -56285,12 +56948,6 @@ F4E11E (base 16) Texas Instruments
Hui Zhou Guangdong 516006
CN
-7C-49-EB (hex) XIAOMI Electronics,CO.,LTD
-7C49EB (base 16) XIAOMI Electronics,CO.,LTD
- Xiaomi Building, No.68 Qinghe Middle Street,Haidian District
- Beijing Beijing 100085
- CN
-
C4-33-06 (hex) China Mobile Group Device Co.,Ltd.
C43306 (base 16) China Mobile Group Device Co.,Ltd.
32 Xuanwumen West Street,Xicheng District
@@ -57749,12 +58406,6 @@ D89EF3 (base 16) Dell Inc.
Round Rock TX 78682
US
-CC-66-B2 (hex) Nokia
-CC66B2 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
C0-74-2B (hex) SHENZHEN XUNLONG SOFTWARE CO.,LIMITED
C0742B (base 16) SHENZHEN XUNLONG SOFTWARE CO.,LIMITED
Room 532, Block A, Huameiju Business Center, Xinhu Road, Baoan 82 Area
@@ -57863,12 +58514,6 @@ D4C19E (base 16) Ruckus Wireless
Qingdao Shandong 266101
CN
-84-F3-EB (hex) Espressif Inc.
-84F3EB (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
10-B3-6F (hex) Bowei Technology Company Limited
10B36F (base 16) Bowei Technology Company Limited
2F,Building No.6C,1658,Gumei Rd
@@ -59153,18 +59798,6 @@ A0E617 (base 16) MATIS
New Taipei City Taiwan 23585
TW
-D8-32-E3 (hex) Xiaomi Communications Co Ltd
-D832E3 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-84-0D-8E (hex) Espressif Inc.
-840D8E (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
FC-90-FA (hex) Independent Technologies
FC90FA (base 16) Independent Technologies
1960 Ridgeview Rd
@@ -59624,12 +60257,6 @@ E4D124 (base 16) Mojo Networks, Inc.
Chongqing Chongqing 401332
CN
-40-31-3C (hex) XIAOMI Electronics,CO.,LTD
-40313C (base 16) XIAOMI Electronics,CO.,LTD
- Xiaomi Building, No.68 Qinghe Middle Street,Haidian District
- Beijing Beijing 100085
- CN
-
CC-3A-DF (hex) Private
CC3ADF (base 16) Private
@@ -59639,12 +60266,6 @@ B4CEFE (base 16) James Czekaj
Northville MI 48168
US
-E0-62-67 (hex) Xiaomi Communications Co Ltd
-E06267 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
20-1A-06 (hex) COMPAL INFORMATION (KUNSHAN) CO., LTD.
201A06 (base 16) COMPAL INFORMATION (KUNSHAN) CO., LTD.
NO. 15, THE 3RD Street KUNSHAN EXPORT PROCESSING ZONE
@@ -59717,12 +60338,6 @@ F8CC6E (base 16) DEPO Electronics Ltd
Seoul KS013
KR
-48-2C-A0 (hex) Xiaomi Communications Co Ltd
-482CA0 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
B8-69-F4 (hex) Routerboard.com
B869F4 (base 16) Routerboard.com
Mikrotikls SIA
@@ -60161,6 +60776,1332 @@ F0D7DC (base 16) Wesine (Wuhan) Technology Co., Ltd.
Hsin-Chu R.O.C. 308
TW
+30-0A-60 (hex) IEEE Registration Authority
+300A60 (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+2C-79-D7 (hex) Sagemcom Broadband SAS
+2C79D7 (base 16) Sagemcom Broadband SAS
+ 250, route de l'Empereur
+ Rueil Malmaison Cedex hauts de seine 92848
+ FR
+
+10-C2-2F (hex) China Entropy Co., Ltd.
+10C22F (base 16) China Entropy Co., Ltd.
+ Haidian District
+ Beijing 100085
+ CN
+
+C4-FD-E6 (hex) DRTECH
+C4FDE6 (base 16) DRTECH
+ 29, Dunchon-daero 541beon-gil, Jungwon-gu
+ Seongnam Gyeonggi-do 13216
+ KR
+
+44-47-CC (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.
+4447CC (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.
+ No.555 Qianmo Road
+ Hangzhou Zhejiang 310052
+ CN
+
+4C-D9-8F (hex) Dell Inc.
+4CD98F (base 16) Dell Inc.
+ One Dell Way
+ Round Rock TX 78682
+ US
+
+E4-1D-2D (hex) Mellanox Technologies, Inc.
+E41D2D (base 16) Mellanox Technologies, Inc.
+ 350 Oakmead Parkway, Suite 100
+ Sunnyvale CA 94085
+ US
+
+B0-AE-25 (hex) Varikorea
+B0AE25 (base 16) Varikorea
+ #505 kolon digital tower aston, gasan, geumcheon
+ seoul 08502
+ KR
+
+44-00-49 (hex) Amazon Technologies Inc.
+440049 (base 16) Amazon Technologies Inc.
+ P.O Box 8102
+ Reno NV 89507
+ US
+
+C0-74-AD (hex) Grandstream Networks, Inc.
+C074AD (base 16) Grandstream Networks, Inc.
+ 1297 Beacon Street
+ Brookline MA 02446
+ US
+
+04-91-62 (hex) Microchip Technology Inc.
+049162 (base 16) Microchip Technology Inc.
+ 2355 W. Chandler Blvd.
+ Chandler AZ 85224
+ US
+
+98-18-88 (hex) Cisco Meraki
+981888 (base 16) Cisco Meraki
+ 500 Terry A. Francois Blvd
+ San Francisco 94158
+ US
+
+74-B5-87 (hex) Apple, Inc.
+74B587 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+D8-1C-79 (hex) Apple, Inc.
+D81C79 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+8C-FE-57 (hex) Apple, Inc.
+8CFE57 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+C0-A6-00 (hex) Apple, Inc.
+C0A600 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+CC-D4-A1 (hex) MitraStar Technology Corp.
+CCD4A1 (base 16) MitraStar Technology Corp.
+ No. 6, Innovation Road II,
+ Hsinchu 300
+ TW
+
+08-BA-5F (hex) Qingdao Hisense Electronics Co.,Ltd.
+08BA5F (base 16) Qingdao Hisense Electronics Co.,Ltd.
+ Qianwangang Roard 218
+ Qingdao Shandong 266510
+ CN
+
+54-06-8B (hex) Ningbo Deli Kebei Technology Co.LTD
+54068B (base 16) Ningbo Deli Kebei Technology Co.LTD
+ zone 2nd , 301#, Road Xuxiake, Ninghai yuelong district
+ ningbo Zhejiang 315600
+ CN
+
+54-9F-AE (hex) iBASE Gaming Inc
+549FAE (base 16) iBASE Gaming Inc
+ 2F., No.542-17, Zhongzheng Rd
+ Xinzhuang Dist., New Taipei City 24255
+ TW
+
+80-0D-D7 (hex) Latticework, Inc
+800DD7 (base 16) Latticework, Inc
+ 2210 O'Toole Ave, Suite 250
+ San Jose CA 95131
+ US
+
+68-8F-2E (hex) Hitron Technologies. Inc
+688F2E (base 16) Hitron Technologies. Inc
+ No. 1-8, Lising 1st Rd. Hsinchu Science Park, Hsinchu, 300, Taiwan, R.O.C
+ Hsin-chu Taiwan 300
+ TW
+
+80-69-33 (hex) HUAWEI TECHNOLOGIES CO.,LTD
+806933 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+C8-9C-13 (hex) Inspiremobile
+C89C13 (base 16) Inspiremobile
+ Rm1412, Daeryung Techno-Town, 15th, 401 , Simin-daero, Dongan-gu
+ Anyang-si Gyeonggi-do 14057
+ KR
+
+E0-5D-5C (hex) Oy Everon Ab
+E05D5C (base 16) Oy Everon Ab
+ Teräskatu 8
+ Turku 20520
+ FI
+
+78-47-E3 (hex) SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+7847E3 (base 16) SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+ NO.198 FIRST SECTION,SNOW MOUNTAIN AVENUE, JINYUAN TOWN, DAYI COUNTY,
+ CHENGDU SICHUAN 611330
+ CN
+
+6C-9B-C0 (hex) Chemoptics Inc.
+6C9BC0 (base 16) Chemoptics Inc.
+ 261, Techno 2-ro, Yuseong-gu
+ Daejeon 34026
+ KR
+
+A8-23-FE (hex) LG Electronics
+A823FE (base 16) LG Electronics
+ 222 LG-ro, JINWI-MYEON
+ Pyeongtaek-si Gyeonggi-do 451-713
+ KR
+
+C0-78-78 (hex) FLEXTRONICS MANUFACTURING(ZHUHAI)CO.,LTD.
+C07878 (base 16) FLEXTRONICS MANUFACTURING(ZHUHAI)CO.,LTD.
+ Xin Qing Science & Technology Industrial Park,Jin An Town,Doumen ,Zhuhai,Guangdong,PRC
+ Zhuhai Guangdong 519180
+ CN
+
+E0-46-E5 (hex) Gosuncn Technology Group Co., Ltd.
+E046E5 (base 16) Gosuncn Technology Group Co., Ltd.
+ 6F, 2819 KaiChuang Blvd., Science Town, Huangpu District
+ Guangzhou City Guangdong 510530
+ CN
+
+30-13-89 (hex) Siemens AG, Automations & Drives,
+301389 (base 16) Siemens AG, Automations & Drives,
+ Systems Engineering
+ Fürth Deutschlang 90766
+ DE
+
+F4-DB-E6 (hex) Cisco Systems, Inc
+F4DBE6 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+DC-F4-01 (hex) Dell Inc.
+DCF401 (base 16) Dell Inc.
+ One Dell Way
+ Round Rock TX 78682
+ US
+
+F4-95-1B (hex) Hefei Radio Communication Technology Co., Ltd
+F4951B (base 16) Hefei Radio Communication Technology Co., Ltd
+ No.108, YinXing Road, High-tech Development Zone
+ Hefei Anhui 230088
+ CN
+
+D0-92-FA (hex) Fiberhome Telecommunication Technologies Co.,LTD
+D092FA (base 16) Fiberhome Telecommunication Technologies Co.,LTD
+ No.5 DongXin Road
+ Wuhan Hubei 430074
+ CN
+
+E8-5A-D1 (hex) Fiberhome Telecommunication Technologies Co.,LTD
+E85AD1 (base 16) Fiberhome Telecommunication Technologies Co.,LTD
+ No.5 DongXin Road
+ Wuhan Hubei 430074
+ CN
+
+BC-75-96 (hex) Beijing Broadwit Technology Co., Ltd.
+BC7596 (base 16) Beijing Broadwit Technology Co., Ltd.
+ Beijing Changping District Beijing International Information Industry Base Jizhida Building 3rd Floor Southeast
+ Beijing Beijing 10000
+ CN
+
+CC-72-86 (hex) Xi'an Fengyu Information Technology Co., Ltd.
+CC7286 (base 16) Xi'an Fengyu Information Technology Co., Ltd.
+ 5F, Block A, STRC, No.10, Zhangba 5th Road, Yanta
+ Xi'an Shaanxi 710077
+ CN
+
+04-92-26 (hex) ASUSTek COMPUTER INC.
+049226 (base 16) ASUSTek COMPUTER INC.
+ 15,Li-Te Rd., Peitou, Taipei 112, Taiwan
+ Taipei Taiwan 112
+ TW
+
+84-32-6F (hex) GUANGZHOU AVA ELECTRONICS TECHNOLOGY CO.,LTD
+84326F (base 16) GUANGZHOU AVA ELECTRONICS TECHNOLOGY CO.,LTD
+ Science town luogang district guangzhou city branch bead road 232 profit people park 301, building 2
+ guangzhou guangdong 510000
+ CN
+
+00-B8-B3 (hex) Cisco Systems, Inc
+00B8B3 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+A8-BC-9C (hex) Cloud Light Technology Limited
+A8BC9C (base 16) Cloud Light Technology Limited
+ 3/F, 6 Science Park East Avenue Hong Kong Science Park Shatin, N.T., Hong Kong
+ Hong Kong 00000
+ HK
+
+0C-B4-A4 (hex) Xintai Automobile Intelligent Network Technology
+0CB4A4 (base 16) Xintai Automobile Intelligent Network Technology
+ Room3703E Changfu Jinmao Building,Shihua Road
+ Futian Duty Free Zone,Fubao Street,Futian District Shenzhen City 518000
+ CN
+
+2C-CC-44 (hex) Sony Interactive Entertainment Inc.
+2CCC44 (base 16) Sony Interactive Entertainment Inc.
+ 1-7-1 Konan
+ Minato-ku Tokyo 108-0075
+ JP
+
+FC-AA-B6 (hex) Samsung Electronics Co.,Ltd
+FCAAB6 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+C0-BD-C8 (hex) Samsung Electronics Co.,Ltd
+C0BDC8 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+A8-87-B3 (hex) Samsung Electronics Co.,Ltd
+A887B3 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+00-D0-2D (hex) Resideo
+00D02D (base 16) Resideo
+ 2 Corporate Center Dr.
+ Melville NY 11747
+ US
+
+10-12-B4 (hex) SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+1012B4 (base 16) SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+ NO.198 FIRST SECTION,SNOW MOUNTAIN AVENUE, JINYUAN TOWN, DAYI COUNTY,
+ CHENGDU SICHUAN 611330
+ CN
+
+3C-9B-D6 (hex) Vizio, Inc
+3C9BD6 (base 16) Vizio, Inc
+ 39 Tesla
+ Irvine CA 92618
+ US
+
+74-23-44 (hex) Xiaomi Communications Co Ltd
+742344 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+D8-32-E3 (hex) Xiaomi Communications Co Ltd
+D832E3 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+E0-62-67 (hex) Xiaomi Communications Co Ltd
+E06267 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+48-2C-A0 (hex) Xiaomi Communications Co Ltd
+482CA0 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+18-01-F1 (hex) Xiaomi Communications Co Ltd
+1801F1 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+70-BB-E9 (hex) Xiaomi Communications Co Ltd
+70BBE9 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+F0-B4-29 (hex) Xiaomi Communications Co Ltd
+F0B429 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+0C-98-38 (hex) Xiaomi Communications Co Ltd
+0C9838 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+0C-1D-AF (hex) Xiaomi Communications Co Ltd
+0C1DAF (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+28-E3-1F (hex) Xiaomi Communications Co Ltd
+28E31F (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+14-F6-5A (hex) Xiaomi Communications Co Ltd
+14F65A (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+B4-F9-49 (hex) optilink networks pvt ltd
+B4F949 (base 16) optilink networks pvt ltd
+ 501/502, sanjona complex, hemu kalani marg, chembur
+ mumbai maharashtra 400071
+ IN
+
+3C-5C-C4 (hex) Amazon Technologies Inc.
+3C5CC4 (base 16) Amazon Technologies Inc.
+ P.O Box 8102
+ Reno NV 89507
+ US
+
+88-71-B1 (hex) ARRIS Group, Inc.
+8871B1 (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+F0-AF-85 (hex) ARRIS Group, Inc.
+F0AF85 (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+B8-9A-9A (hex) Xin Shi Jia Technology (Beijing) Co.,Ltd
+B89A9A (base 16) Xin Shi Jia Technology (Beijing) Co.,Ltd
+ Room 1002, A Tower, Zhongguancun E World Wealth Center, No.11, Zhongguancun Street, Haidian District, Beijing City
+ Beijing Beijing 100190
+ CN
+
+D4-C9-4B (hex) Motorola Mobility LLC, a Lenovo Company
+D4C94B (base 16) Motorola Mobility LLC, a Lenovo Company
+ 222 West Merchandise Mart Plaza
+ Chicago IL 60654
+ US
+
+C0-22-50 (hex) Koss Corporation
+C02250 (base 16) Koss Corporation
+ 4129 N. Port Washington Ave.
+ Milwaukee WI 53212
+ US
+
+10-9E-3A (hex) Zhejiang Tmall Technology Co., Ltd.
+109E3A (base 16) Zhejiang Tmall Technology Co., Ltd.
+ Ali Center,No.3331 Keyuan South RD (Shenzhen bay), Nanshan District,
+ Shenzhen Guangdong 518000
+ CN
+
+2C-1C-F6 (hex) Alien Green LLC
+2C1CF6 (base 16) Alien Green LLC
+ A. Kazbegi Ave., No24g, apt 227
+ Tbilisi Tbilisi 0160
+ GE
+
+E4-38-8C (hex) Digital Products Limited
+E4388C (base 16) Digital Products Limited
+ 53 Clark Road
+ Rothesay New Brunswick E2E 2K9
+ CA
+
+18-1E-95 (hex) AuVerte
+181E95 (base 16) AuVerte
+ 14 Riverview Road
+ Niantic CT 06357
+ US
+
+18-4B-DF (hex) Caavo Inc
+184BDF (base 16) Caavo Inc
+ 1525 McCarthy Blvd., #1182
+ Milpitas 95035
+ US
+
+1C-54-9E (hex) Universal Electronics, Inc.
+1C549E (base 16) Universal Electronics, Inc.
+ 201 E. Sandpointe Ave
+ Santa Ana CA 92707
+ US
+
+70-3A-51 (hex) Xiaomi Communications Co Ltd
+703A51 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+A0-20-A6 (hex) Espressif Inc.
+A020A6 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+84-F3-EB (hex) Espressif Inc.
+84F3EB (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+84-0D-8E (hex) Espressif Inc.
+840D8E (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+54-9B-72 (hex) Ericsson AB
+549B72 (base 16) Ericsson AB
+ Torshamnsgatan 36
+ Stockholm SE-164 80
+ SE
+
+DC-08-0F (hex) Apple, Inc.
+DC080F (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+F8-2D-7C (hex) Apple, Inc.
+F82D7C (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+9C-64-8B (hex) Apple, Inc.
+9C648B (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+C0-3D-D9 (hex) MitraStar Technology Corp.
+C03DD9 (base 16) MitraStar Technology Corp.
+ No. 6, Innovation Road II,
+ Hsinchu 300
+ TW
+
+A0-A3-B8 (hex) WISCLOUD
+A0A3B8 (base 16) WISCLOUD
+ Tech Park Xia Sha
+ Hangzhou Zhejiang 310000
+ CN
+
+14-D0-0D (hex) Apple, Inc.
+14D00D (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+74-85-C4 (hex) New H3C Technologies Co., Ltd
+7485C4 (base 16) New H3C Technologies Co., Ltd
+ 466 Changhe Road, Binjiang District
+ Hangzhou Zhejiang 310052
+ CN
+
+34-93-42 (hex) TTE Corporation
+349342 (base 16) TTE Corporation
+ 7/F, Building 22E 22 Science Park East Avenue Hong Kong Science Park Shatin, N.T.
+ Hong Kong 999077
+ HK
+
+48-E6-95 (hex) Insigma Inc
+48E695 (base 16) Insigma Inc
+ 43490, Yukon Drive, Suite 102
+ Ashburn VA 20147
+ US
+
+B4-79-C8 (hex) Ruckus Wireless
+B479C8 (base 16) Ruckus Wireless
+ 350 West Java Drive
+ Sunnyvale CA 94089
+ US
+
+F8-0D-F1 (hex) Sontex SA
+F80DF1 (base 16) Sontex SA
+ rue de la gare
+ sonceboz Bern 2605
+ CH
+
+9C-8C-D8 (hex) Hewlett Packard Enterprise
+9C8CD8 (base 16) Hewlett Packard Enterprise
+ 8000 Foothills Blvd.
+ Roseville CA 95747
+ US
+
+88-D2-11 (hex) Eko Devices, Inc.
+88D211 (base 16) Eko Devices, Inc.
+ 2600 10th St Ste 260
+ Berkeley CA 94710-2597
+ US
+
+1C-F2-9A (hex) Google, Inc.
+1CF29A (base 16) Google, Inc.
+ 1600 Amphitheatre Parkway
+ Mountain View CA 94043
+ US
+
+94-54-DF (hex) YST CORP.
+9454DF (base 16) YST CORP.
+ A-1407, 767, Sinsu-ro, Suji-gu,
+ Yongin-si Gyeonggi-do 16827
+ KR
+
+74-F7-37 (hex) KCE
+74F737 (base 16) KCE
+ 5F KCE B/D,34,Annam-ro 369beon-gil,Bupyoung-gu
+ Incheon 21312
+ KR
+
+8C-18-50 (hex) China Mobile (Hangzhou) Information Technology Co., Ltd.
+8C1850 (base 16) China Mobile (Hangzhou) Information Technology Co., Ltd.
+ No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District
+ Hangzhou Hangzhou 310000
+ CN
+
+78-0E-D1 (hex) TRUMPF Werkzeugmaschinen GmbH+Co.KG
+780ED1 (base 16) TRUMPF Werkzeugmaschinen GmbH+Co.KG
+ Johann-Maus-Straße 2
+ Ditzingen 71254
+ DE
+
+A8-9C-A4 (hex) Furrion Limited
+A89CA4 (base 16) Furrion Limited
+ Units 503C & 505-508, Level 5, Core D, Cyberport 3, 100 Cyberport Road
+ Hong Kong 00000
+ HK
+
+7C-DB-98 (hex) ASKEY COMPUTER CORP
+7CDB98 (base 16) ASKEY COMPUTER CORP
+ 10F,No.119,JIANKANG RD,ZHONGHE DIST
+ NEW TAIPEI TAIWAN 23585
+ TW
+
+6C-DF-FB (hex) IEEE Registration Authority
+6CDFFB (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+DC-21-B9 (hex) Sentec Co.Ltd
+DC21B9 (base 16) Sentec Co.Ltd
+ 10, Baekseokgongdan 1-ro, Seobuk-gu
+ Cheonan-si Chungcheongnam-do 31094
+ KR
+
+E4-D3-AA (hex) FUJITSU CONNECTED TECHNOLOGIES LIMITED
+E4D3AA (base 16) FUJITSU CONNECTED TECHNOLOGIES LIMITED
+ 4-1-1, Kamikodanaka, Nakahara-ku
+ Kawasaki Kanagawa 2118588
+ JP
+
+B0-02-47 (hex) AMPAK Technology, Inc.
+B00247 (base 16) AMPAK Technology, Inc.
+ 3F.,No.15-1 Zhonghua Road,Hsinchu Industrial Park, Hukou,Hsinchu
+ Hsinchu Taiwan ROC. 30352
+ TW
+
+BC-E7-96 (hex) Wireless CCTV Ltd
+BCE796 (base 16) Wireless CCTV Ltd
+ charles Babbage house
+ Rochdale Greater Manchester ol164nw
+ GB
+
+70-5E-55 (hex) Realme Chongqing MobileTelecommunications Corp Ltd
+705E55 (base 16) Realme Chongqing MobileTelecommunications Corp Ltd
+ No.24 Nichang Boulevard, Huixing Block, Yubei District, Chongqing.
+ Chongqing China 401120
+ CN
+
+D4-67-D3 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+D467D3 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+48-E3-C3 (hex) JENOPTIK Advanced Systems GmbH
+48E3C3 (base 16) JENOPTIK Advanced Systems GmbH
+ Feldstrasse 155
+ Wedel Schleswig-Holstein 22880
+ DE
+
+84-EB-3E (hex) Vivint Smart Home
+84EB3E (base 16) Vivint Smart Home
+ 4931 N. 300 W.
+ Provo UT 84604
+ US
+
+CC-70-ED (hex) Cisco Systems, Inc
+CC70ED (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+A8-39-44 (hex) Actiontec Electronics, Inc
+A83944 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+00-24-7B (hex) Actiontec Electronics, Inc
+00247B (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+70-F2-20 (hex) Actiontec Electronics, Inc
+70F220 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+D4-3D-39 (hex) FCI. Inc
+D43D39 (base 16) FCI. Inc
+ B-7F, SiliconPark, 35, Pangyo-ro 255beon-gil, Bundang-gu
+ Seongnam-si Gyeonggi-do 13486
+ KR
+
+4C-96-2D (hex) Fresh AB
+4C962D (base 16) Fresh AB
+ Gransholmsvägen 136
+ Gemla 35599
+ SE
+
+AC-7A-4D (hex) ALPS ELECTRIC CO., LTD.
+AC7A4D (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ KAKUDA-CITY MIYAGI-PREF 981-1595
+ JP
+
+58-C6-F0 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+58C6F0 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+64-9D-99 (hex) FS COM INC
+649D99 (base 16) FS COM INC
+ 380 Centerpoint Blvd New Castle
+ New Castle DE 19720
+ US
+
+00-19-3B (hex) LigoWave
+00193B (base 16) LigoWave
+ 138 Mountain Brook Drive
+ Canton GA 30115
+ US
+
+FC-62-B9 (hex) ALPS ELECTRIC CO., LTD.
+FC62B9 (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ kakuda-city Miyagi-Pref 981-1595
+ JP
+
+00-19-C1 (hex) ALPS ELECTRIC CO., LTD.
+0019C1 (base 16) ALPS ELECTRIC CO., LTD.
+ 1-2-1, Okinouchi,
+ Soma-city, Fukushima-pref., 976-8501
+ JP
+
+00-1E-3D (hex) ALPS ELECTRIC CO., LTD.
+001E3D (base 16) ALPS ELECTRIC CO., LTD.
+ 1-2-1, Okinouchi,
+ Soma-city, Fukushima-pref., 976-8501
+ JP
+
+00-23-06 (hex) ALPS ELECTRIC CO., LTD.
+002306 (base 16) ALPS ELECTRIC CO., LTD.
+ 1-2-1, Okinouchi,
+ Soma-city, Fukushima-pref., 976-8501
+ JP
+
+28-A1-83 (hex) ALPS ELECTRIC CO., LTD.
+28A183 (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ Kakuda Miyagi-Pref 981-1595
+ JP
+
+88-4A-18 (hex) Opulinks
+884A18 (base 16) Opulinks
+ F 28, No.328, Huashan Rd
+ Shanghai 200040
+ CN
+
+00-0B-5D (hex) FUJITSU LIMITED
+000B5D (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+14-4E-2A (hex) Ciena Corporation
+144E2A (base 16) Ciena Corporation
+ 7035 Ridge Road
+ Hanover MD 21076
+ US
+
+D4-C9-3C (hex) Cisco Systems, Inc
+D4C93C (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+88-6F-D4 (hex) Dell Inc.
+886FD4 (base 16) Dell Inc.
+ One Dell Way
+ Round Rock TX 78682
+ US
+
+C4-06-83 (hex) HUAWEI TECHNOLOGIES CO.,LTD
+C40683 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+FC-87-43 (hex) HUAWEI TECHNOLOGIES CO.,LTD
+FC8743 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+50-2B-98 (hex) Es-tech International
+502B98 (base 16) Es-tech International
+ 228-70, Saneop-ro 155beon-gil, Gwonseon-gu, Suwon-si, Gyeonggi-do, Korea
+ Suwon 16648
+ KR
+
+A0-F9-B7 (hex) Ademco Smart Homes Technology(Tianjin)Co.,Ltd.
+A0F9B7 (base 16) Ademco Smart Homes Technology(Tianjin)Co.,Ltd.
+ No.156 Nanhai Road,TEDA, Jinbin Development Park , 21st Factory Building
+ Tianjin Tianjin 300457
+ CN
+
+48-F1-7F (hex) Intel Corporate
+48F17F (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+10-9C-70 (hex) Prusa Research s.r.o.
+109C70 (base 16) Prusa Research s.r.o.
+ Partyzanska 188/7a
+ Prague 17000
+ CZ
+
+8C-44-4F (hex) HUMAX Co., Ltd.
+8C444F (base 16) HUMAX Co., Ltd.
+ HUMAX Village, 216, Hwangsaeul-ro, Bu
+ Seongnam-si Gyeonggi-do 463-875
+ KR
+
+A4-19-08 (hex) Fiberhome Telecommunication Technologies Co.,LTD
+A41908 (base 16) Fiberhome Telecommunication Technologies Co.,LTD
+ No.5 DongXin Road
+ Wuhan Hubei 430074
+ CN
+
+EC-A9-FA (hex) GUANGDONG GENIUS TECHNOLOGY CO., LTD.
+ECA9FA (base 16) GUANGDONG GENIUS TECHNOLOGY CO., LTD.
+ #126,BBK Road,Wusha,Chang'An
+ Dong Guan Guang Dong 523860
+ CN
+
+44-B4-62 (hex) Flextronics Tech.(Ind) Pvt Ltd
+44B462 (base 16) Flextronics Tech.(Ind) Pvt Ltd
+ SURVEYNO.381, PADUR ROAD, KUTHAMBAKKAM VILLAGE, 602107 POONAMALLEE TALUK, THIRUVALLUR DISTRIC
+ Chennai 602107
+ IN
+
+DC-67-23 (hex) barox Kommunikation GmbH
+DC6723 (base 16) barox Kommunikation GmbH
+ Marie-Curie-Strasse 8
+ Lörrach DE-79539
+ DE
+
+1C-24-EB (hex) Burlywood
+1C24EB (base 16) Burlywood
+ 1501 S Sunset Street
+ Longmont CO 80501
+ US
+
+64-6E-EA (hex) Iskratel d.o.o.
+646EEA (base 16) Iskratel d.o.o.
+ Ljubljanska cesta 24a
+ Kranj 4000
+ SI
+
+00-D0-50 (hex) Iskratel d.o.o.
+00D050 (base 16) Iskratel d.o.o.
+ Ljubljanska cesta 24a
+ Kranj 4000
+ SI
+
+7C-60-4A (hex) Avelon
+7C604A (base 16) Avelon
+ Bändliweg 20
+ Zurich 8048
+ CH
+
+7C-D9-5C (hex) Google, Inc.
+7CD95C (base 16) Google, Inc.
+ 1600 Amphitheatre Parkway
+ Mountain View CA 94043
+ US
+
+F0-5C-19 (hex) Aruba, a Hewlett Packard Enterprise Company
+F05C19 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+70-3A-0E (hex) Aruba, a Hewlett Packard Enterprise Company
+703A0E (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+B4-5D-50 (hex) Aruba, a Hewlett Packard Enterprise Company
+B45D50 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+6C-F3-7F (hex) Aruba, a Hewlett Packard Enterprise Company
+6CF37F (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+68-FF-7B (hex) TP-LINK TECHNOLOGIES CO.,LTD.
+68FF7B (base 16) TP-LINK TECHNOLOGIES CO.,LTD.
+ Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan
+ Shenzhen Guangdong 518057
+ CN
+
+38-21-C7 (hex) Aruba, a Hewlett Packard Enterprise Company
+3821C7 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+B8-EF-8B (hex) SHENZHEN CANNICE TECHNOLOGY CO.,LTD
+B8EF8B (base 16) SHENZHEN CANNICE TECHNOLOGY CO.,LTD
+ F-20,7A,Baoneng Technology Park
+ Shenzhen Guangdong 518109
+ CN
+
+00-13-1E (hex) peiker acustic GmbH
+00131E (base 16) peiker acustic GmbH
+ Max-Planck-Strasse 28-32
+ Friedrichsdorf 61381
+ DE
+
+D4-9C-DD (hex) AMPAK Technology,Inc.
+D49CDD (base 16) AMPAK Technology,Inc.
+ 3F, No.15-1 Zhonghua Road, Hsinchu Industrail Park, Hukou,
+ Hsinchu Hsinchu,Taiwan R.O.C. 30352
+ TW
+
+84-69-91 (hex) Nokia
+846991 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+E8-93-63 (hex) Nokia
+E89363 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+CC-66-B2 (hex) Nokia
+CC66B2 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+10-E8-78 (hex) Nokia
+10E878 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+48-F7-F1 (hex) Nokia
+48F7F1 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+4C-C9-4F (hex) Nokia
+4CC94F (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+04-CF-8C (hex) XIAOMI Electronics,CO.,LTD
+04CF8C (base 16) XIAOMI Electronics,CO.,LTD
+ Xiaomi Building, No.68 Qinghe Middle Street
+ Haidian District Beijing 100085
+ CN
+
+40-31-3C (hex) XIAOMI Electronics,CO.,LTD
+40313C (base 16) XIAOMI Electronics,CO.,LTD
+ Xiaomi Building, No.68 Qinghe Middle Street
+ Haidian District Beijing 100085
+ CN
+
+7C-49-EB (hex) XIAOMI Electronics,CO.,LTD
+7C49EB (base 16) XIAOMI Electronics,CO.,LTD
+ Xiaomi Building, No.68 Qinghe Middle Street
+ Haidian District Beijing 100085
+ CN
+
+1C-EA-1B (hex) Nokia
+1CEA1B (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+E0-09-BF (hex) SHENZHEN TONG BO WEI TECHNOLOGY Co.,LTD
+E009BF (base 16) SHENZHEN TONG BO WEI TECHNOLOGY Co.,LTD
+ 5th floor building 4 pengtengda industrial,langkou community,dalang street longhua newly developed area
+ Shenzhen GuangDong 518000
+ CN
+
+00-0C-17 (hex) AJA Video Systems Inc
+000C17 (base 16) AJA Video Systems Inc
+ 180 Litton Drive
+ Grass Valley CA 95945
+ US
+
+CC-ED-DC (hex) MitraStar Technology Corp.
+CCEDDC (base 16) MitraStar Technology Corp.
+ No. 6, Innovation Road II,
+ Hsinchu 300
+ TW
+
+D4-58-00 (hex) Fiberhome Telecommunication Technologies Co.,LTD
+D45800 (base 16) Fiberhome Telecommunication Technologies Co.,LTD
+ No.5 DongXin Road
+ Wuhan Hubei 430074
+ CN
+
+C4-64-B7 (hex) Fiberhome Telecommunication Technologies Co.,LTD
+C464B7 (base 16) Fiberhome Telecommunication Technologies Co.,LTD
+ No.5 DongXin Road
+ Wuhan Hubei 430074
+ CN
+
+4C-4D-66 (hex) Nanjing Jiahao Technology Co., Ltd.
+4C4D66 (base 16) Nanjing Jiahao Technology Co., Ltd.
+ Moling Industrial Park, Development Zone, Jiangning, Nanjing
+ Nanjing Jiangsu 211111
+ CN
+
+90-58-51 (hex) Technicolor CH USA Inc.
+905851 (base 16) Technicolor CH USA Inc.
+ 5030 Sugarloaf Parkway Bldg 6
+ Lawrenceville GA 30044
+ US
+
+38-F8-5E (hex) HUMAX Co., Ltd.
+38F85E (base 16) HUMAX Co., Ltd.
+ HUMAX Village, 216, Hwangsaeul-ro, Bu
+ Seongnam-si Gyeonggi-do 463-875
+ KR
+
+C0-2E-25 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+C02E25 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+40-A5-EF (hex) Shenzhen Four Seas Global Link Network Technology Co., Ltd.
+40A5EF (base 16) Shenzhen Four Seas Global Link Network Technology Co., Ltd.
+ Room 607-610, Block B, TAOJINDI Electronic Business Incubation Base
+ Tenglong Road, Longhua District, Shenzhen Guangdong 518000
+ CN
+
+48-E6-C0 (hex) SIMCom Wireless Solutions Co.,Ltd.
+48E6C0 (base 16) SIMCom Wireless Solutions Co.,Ltd.
+ Building B,SIM Technology Building,No.633,Jinzhong Road
+ Shanghai 200335
+ CN
+
+CC-D8-1F (hex) Maipu Communication Technology Co.,Ltd.
+CCD81F (base 16) Maipu Communication Technology Co.,Ltd.
+ Maipu Mansion, No.288 Tianfu 3rd Street, High-tech Zone
+ Chengdu Sichuan 610094
+ CN
+
+10-0C-6B (hex) NETGEAR
+100C6B (base 16) NETGEAR
+ 350 East Plumeria Drive
+ San Jose CA 95134
+ US
+
+2C-F4-32 (hex) Espressif Inc.
+2CF432 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+AC-BB-61 (hex) YSTen Technology Co.,Ltd
+ACBB61 (base 16) YSTen Technology Co.,Ltd
+ Room 1715,17/F North Star Times Tower,Chaoyang District,Beijing.
+ Beijing 100101
+ CN
+
+60-6E-D0 (hex) SEAL AG
+606ED0 (base 16) SEAL AG
+ Landstrasse 176
+ Wettingen 5430
+ CH
+
+24-79-F8 (hex) KUPSON spol. s r.o.
+2479F8 (base 16) KUPSON spol. s r.o.
+ Hradecka 787/14
+ Opava Czech Republic 74601
+ CZ
+
+00-A0-85 (hex) Private
+00A085 (base 16) Private
+
+40-A9-3F (hex) Pivotal Commware, Inc.
+40A93F (base 16) Pivotal Commware, Inc.
+ 1555 132nd Ave. NE
+ Bellevue WA 98005
+ US
+
+18-D6-CF (hex) Kurth Electronic GmbH
+18D6CF (base 16) Kurth Electronic GmbH
+ Mühleweg 11
+ Eningen 72800
+ DE
+
+24-3F-30 (hex) Oxygen Broadband s.a.
+243F30 (base 16) Oxygen Broadband s.a.
+ 2 Messogeion ave., Athens Tower
+ Athens Attiki 11527
+ GR
+
+48-04-9F (hex) ELECOM CO., LTD
+48049F (base 16) ELECOM CO., LTD
+ 9FLand Axis Tower.1-1 fushimi machi,4-chome chuoku
+ osaka 5418765
+ JP
+
+08-7F-98 (hex) vivo Mobile Communication Co., Ltd.
+087F98 (base 16) vivo Mobile Communication Co., Ltd.
+ #283,BBK Road
+ Wusha,Chang'An DongGuan City,Guangdong, 523860
+ CN
+
+B4-D0-A9 (hex) China Mobile Group Device Co.,Ltd.
+B4D0A9 (base 16) China Mobile Group Device Co.,Ltd.
+ 32 Xuanwumen West Street,Xicheng District
+ Beijing 100053
+ CN
+
+48-89-E7 (hex) Intel Corporate
+4889E7 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+04-D4-C4 (hex) ASUSTek COMPUTER INC.
+04D4C4 (base 16) ASUSTek COMPUTER INC.
+ 15,Li-Te Rd., Peitou, Taipei 112, Taiwan
+ Taipei Taiwan 112
+ TW
+
+48-46-C1 (hex) FN-LINK TECHNOLOGY LIMITED
+4846C1 (base 16) FN-LINK TECHNOLOGY LIMITED
+ A Building,HuiXin industial park,No 31, YongHe road, Fuyong town, Bao'an District
+ SHENZHEN GUANGDONG 518100
+ CN
+
+24-BF-74 (hex) Private
+24BF74 (base 16) Private
+
+00-26-15 (hex) Teracom Limited
+002615 (base 16) Teracom Limited
+ B-84
+ Noida Uttar Pradesh 201301
+ IN
+
+58-CB-52 (hex) Google, Inc.
+58CB52 (base 16) Google, Inc.
+ 1600 Amphitheatre Parkway
+ Mountain View CA 94043
+ US
+
+F8-CA-59 (hex) NetComm Wireless
+F8CA59 (base 16) NetComm Wireless
+ LEVEL 5, 18-20 ORION RD. LANE COVE
+ LANE COVE WEST NSW 2066
+ AU
+
+6C-2C-DC (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+6C2CDC (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+80-4A-14 (hex) Apple, Inc.
+804A14 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+B8-5D-0A (hex) Apple, Inc.
+B85D0A (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+94-16-25 (hex) Apple, Inc.
+941625 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+74-40-BE (hex) LG Innotek
+7440BE (base 16) LG Innotek
+ 26, Hanamsandan 5beon-ro
+ Gwangju Gwangsan-gu 506-731
+ KR
+
+34-A8-EB (hex) Apple, Inc.
+34A8EB (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+04-F1-28 (hex) HMD Global Oy
+04F128 (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+AC-57-75 (hex) HMD Global Oy
+AC5775 (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+4C-6A-F6 (hex) HMD Global Oy
+4C6AF6 (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+AC-8F-F8 (hex) Nokia
+AC8FF8 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+10-82-86 (hex) Luxshare Precision Industry Co.,Ltd
+108286 (base 16) Luxshare Precision Industry Co.,Ltd
+ 2nd floor, A building, Sanyo New Industrial Area, West of Maoyi, Shajing Baoan District
+ Shenzhen Shenzhen 518104
+ CN
+
+FC-75-16 (hex) D-Link International
+FC7516 (base 16) D-Link International
+ 1 International Business Park, #03-12, The Synergy
+ SINGAPORE 609917
+ SG
+
+AC-F1-DF (hex) D-Link International
+ACF1DF (base 16) D-Link International
+ 1 International Business Park, #03-12, The Synergy
+ SINGAPORE 609917
+ SG
+
+A0-AB-1B (hex) D-Link International
+A0AB1B (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+10-62-EB (hex) D-Link International
+1062EB (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+78-32-1B (hex) D-Link International
+78321B (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+9C-D6-43 (hex) D-Link International
+9CD643 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+AC-EE-70 (hex) Fontem Ventures BV
+ACEE70 (base 16) Fontem Ventures BV
+ Motion Building 8F, Radarweg 60
+ Amsterdam Noord-Holland 1043NT
+ NL
+
+00-AD-24 (hex) D-Link International
+00AD24 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+F4-8C-EB (hex) D-Link International
+F48CEB (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+FC-D2-B6 (hex) IEEE Registration Authority
+FCD2B6 (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+00-B6-00 (hex) VOIM Co., Ltd.
+00B600 (base 16) VOIM Co., Ltd.
+ 70, Seotan-ro, Jinwi-myeon
+ Pyeongtaek-si Gyeonggi-do 17706
+ KR
+
+60-61-DF (hex) Z-meta Research LLC
+6061DF (base 16) Z-meta Research LLC
+ 8365 Quay Drive
+ Arvada CO 80003
+ US
+
+DC-96-2C (hex) NST Audio Ltd
+DC962C (base 16) NST Audio Ltd
+ 32 Whitewall
+ Norton North Yorkshire YO17 9EH
+ GB
+
+50-AD-71 (hex) Tessolve Semiconductor Private Limited
+50AD71 (base 16) Tessolve Semiconductor Private Limited
+ Plot No: 31, P2, Electronic City Phase II, Electronic City
+ Bengaluru Karnataka 560100
+ IN
+
+48-83-B4 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+4883B4 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+28-23-F5 (hex) China Mobile (Hangzhou) Information Technology Co., Ltd.
+2823F5 (base 16) China Mobile (Hangzhou) Information Technology Co., Ltd.
+ No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District
+ Hangzhou Zhejiang 310000
+ CN
+
+20-2A-C5 (hex) Petite-En
+202AC5 (base 16) Petite-En
+ 1, Gwanak-ro, Gwanak-gu
+ Seoul 08826
+ KR
+
58-46-E1 (hex) Baxter International Inc
5846E1 (base 16) Baxter International Inc
One Baxter Parkway
@@ -60191,12 +62132,6 @@ D084B0 (base 16) Sagemcom Broadband SAS
San Jose CA 94568
US
-EC-22-80 (hex) D-Link International
-EC2280 (base 16) D-Link International
- 1 Internal Business Park, #03-12,
- SINGAPORE Singapore 609917
- SG
-
04-78-63 (hex) Shanghai MXCHIP Information Technology Co., Ltd.
047863 (base 16) Shanghai MXCHIP Information Technology Co., Ltd.
9th Floor, No. 5 Building, 2145 Jinshajiang Rd., Putuo District
@@ -60335,36 +62270,6 @@ A4BA76 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Houston 77070
US
-14-D6-4D (hex) D-Link International
-14D64D (base 16) D-Link International
- 1 INTERNATIONAL BUSINESS PARK
- SINGAPORE 609917
- SG
-
-C8-BE-19 (hex) D-Link International
-C8BE19 (base 16) D-Link International
- 1 International Business Park, #03-12, The Synergy
- SINGAPORE 609917
- SG
-
-BC-F6-85 (hex) D-Link International
-BCF685 (base 16) D-Link International
- 1 International Business Park, #03-12, The Synergy
- SINGAPORE 609917
- SG
-
-CC-B2-55 (hex) D-Link International
-CCB255 (base 16) D-Link International
- 1 International Business Park, #03-12, The Synergy
- SINGAPORE 609917
- SG
-
-84-C9-B2 (hex) D-Link International
-84C9B2 (base 16) D-Link International
- 1 International Business Park, #03-12, The Synergy
- SINGAPORE 609917
- SG
-
DC-D3-21 (hex) HUMAX Co., Ltd.
DCD321 (base 16) HUMAX Co., Ltd.
HUMAX Village, 11-4, Sunae-dong, Bundang-gu
@@ -60977,30 +62882,6 @@ F83DFF (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Shenzhen Guangdong 518129
CN
-00-16-FE (hex) ALPS ELECTRIC CO.,LTD.
-0016FE (base 16) ALPS ELECTRIC CO.,LTD.
- 1-2-1, Okinouchi,
- Soma-city, Fukushima-pref., 976-8501
- JP
-
-04-98-F3 (hex) ALPS ELECTRIC CO.,LTD.
-0498F3 (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1 NISHIDA
- KAKUDA MIYAGI PREF 9876-8501
- JP
-
-38-C0-96 (hex) ALPS ELECTRIC CO.,LTD.
-38C096 (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- KAKUDA-CITY MIYAGI-PREF 981-1595
- JP
-
-E0-75-0A (hex) ALPS ELECTRIC CO.,LTD.
-E0750A (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- kakuda-City 981-1595
- US
-
B0-59-47 (hex) Shenzhen Qihu Intelligent Technology Company Limited
B05947 (base 16) Shenzhen Qihu Intelligent Technology Company Limited
Room 201, Block A, No.1, Qianwan Road 1,Qianhai Shenzhen HongKong Modern Service Industry Cooperation Zone
@@ -61085,12 +62966,6 @@ A4A24A (base 16) Cisco SPVTG
Lawrenceville GA 30044
US
-00-1B-FB (hex) ALPS ELECTRIC CO.,LTD.
-001BFB (base 16) ALPS ELECTRIC CO.,LTD.
- 1-2-1, Okinouchi,
- Soma-city, Fukushima-pref., 976-8501
- JP
-
00-E0-8F (hex) Cisco Systems, Inc
00E08F (base 16) Cisco Systems, Inc
170 WEST TASMAN DRIVE
@@ -61631,12 +63506,6 @@ DCDB70 (base 16) Tonfunk Systementwicklung und Service GmbH
Falkenstein / Harz OT Ermsleben Sachsen-Anhalt 06463
DE
-C4-7D-46 (hex) FUJITSU LIMITED
-C47D46 (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
68-ED-A4 (hex) Shenzhen Seavo Technology Co.,Ltd
68EDA4 (base 16) Shenzhen Seavo Technology Co.,Ltd
5H,West Building,NO.210,Terra Hi-Tech Industrial Park,
@@ -61679,12 +63548,6 @@ D85DEF (base 16) Busch-Jaeger Elektro GmbH
Taoyuan County Taiwan 330
TW
-38-FA-CA (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
-38FACA (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
- 7F,Block A,Skyworth Building,
- Shenzhen Guangdong 518057
- CN
-
44-C6-9B (hex) Wuhan Feng Tian Information Network CO.,LTD
44C69B (base 16) Wuhan Feng Tian Information Network CO.,LTD
Room 1002,10th Floor,Oversea talent Building A,
@@ -62069,12 +63932,6 @@ F02A23 (base 16) Creative Next Design
Lugano Ticino 6900
CH
-E8-CC-18 (hex) D-Link International
-E8CC18 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
B0-91-37 (hex) ISis ImageStream Internet Solutions, Inc
B09137 (base 16) ISis ImageStream Internet Solutions, Inc
4374 FM 1518
@@ -62297,12 +64154,6 @@ F82441 (base 16) Yeelink
Namdong-gu Incheon 405-846
KR
-70-F1-96 (hex) Actiontec Electronics, Inc
-70F196 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
6C-6E-FE (hex) Core Logic Inc.
6C6EFE (base 16) Core Logic Inc.
11th Fl., 1-B U-SPACE Bldg.
@@ -62321,12 +64172,6 @@ E4C62B (base 16) Airware
Haidian District Beijing 100085
CN
-94-B4-0F (hex) Aruba Networks
-94B40F (base 16) Aruba Networks
- 1344 Crossman Ave
- Sunnyvale CA 94089
- US
-
4C-2C-83 (hex) Zhejiang KaNong Network Technology Co.,Ltd.
4C2C83 (base 16) Zhejiang KaNong Network Technology Co.,Ltd.
Room 633, Building B, ShunFan Technology Park,JingChang Road No.768, YuHang District
@@ -62405,12 +64250,6 @@ A8F7E0 (base 16) PLANET Technology Corporation
New Taipei City 22341
TW
-2C-5B-E1 (hex) Centripetal Networks, Inc
-2C5BE1 (base 16) Centripetal Networks, Inc
- 11720 Sunrise Valley Drive, Suite 100
- Reston Virginia 20191
- US
-
D8-7E-B1 (hex) x.o.ware, inc.
D87EB1 (base 16) x.o.ware, inc.
114 E. Haley St., Ste N
@@ -63830,12 +65669,6 @@ C47DCC (base 16) Zebra Technologies Inc
St. Marienkirchen Upper Austria 4774
US
-10-5F-06 (hex) Actiontec Electronics, Inc
-105F06 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
84-17-15 (hex) GP Electronics (HK) Ltd.
841715 (base 16) GP Electronics (HK) Ltd.
Gold Peak Industrial Building, 6F
@@ -64616,12 +66449,6 @@ D41E35 (base 16) TOHO Electronics INC.
Shangahi 200120
CN
-2C-D4-44 (hex) FUJITSU LIMITED
-2CD444 (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
EC-1A-59 (hex) Belkin International Inc.
EC1A59 (base 16) Belkin International Inc.
12045 East Waterfront Drive
@@ -65324,12 +67151,6 @@ A898C6 (base 16) Shinbo Co., Ltd.
SHENZHEN GUANGDONG 518104
CN
-50-26-90 (hex) FUJITSU LIMITED
-502690 (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
B4-21-1D (hex) Beijing GuangXin Technology Co., Ltd
B4211D (base 16) Beijing GuangXin Technology Co., Ltd
Room 313,B Bld,Horizon International Tower,Zhichun Road,6,Haidian District,Beijing City,P.R.China
@@ -66047,12 +67868,6 @@ C88439 (base 16) Sunrise Technologies
Deer Park IL 60010
US
-EC-7D-9D (hex) MEI
-EC7D9D (base 16) MEI
- 3222 Phoenixville Pike
- Malvern PA 19355
- US
-
9C-95-F8 (hex) SmartDoor Systems, LLC
9C95F8 (base 16) SmartDoor Systems, LLC
5711-A Center Lane
@@ -67136,12 +68951,6 @@ B09074 (base 16) Fulan Electronics Limited
Bucheon-city Kyunggi-do 420-857
KR
-8C-73-6E (hex) FUJITSU LIMITED
-8C736E (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
30-EF-D1 (hex) Alstom Strongwish (Shenzhen) Co., Ltd.
30EFD1 (base 16) Alstom Strongwish (Shenzhen) Co., Ltd.
5F,Building No.6, Keji Middle 2 Road High-Tech Industrial Park,
@@ -69392,12 +71201,6 @@ A893E6 (base 16) JIANGXI JINGGANGSHAN CKING COMMUNICATION TECHNOLOGY CO.,LT
Miami Florida 33166
US
-00-1E-A7 (hex) Actiontec Electronics, Inc
-001EA7 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
00-1E-A1 (hex) Brunata a/s
001EA1 (base 16) Brunata a/s
Vesterlundvej 14
@@ -72941,12 +74744,6 @@ A893E6 (base 16) JIANGXI JINGGANGSHAN CKING COMMUNICATION TECHNOLOGY CO.,LT
Maia Minho 4470-177
PT
-00-0E-8C (hex) Siemens AG A&D ET
-000E8C (base 16) Siemens AG A&D ET
- Siemensstraße 10
- Regensburg 93055
- DE
-
00-0E-86 (hex) Alcatel North America
000E86 (base 16) Alcatel North America
2301 Sugar Bush Road
@@ -75134,12 +76931,6 @@ A893E6 (base 16) JIANGXI JINGGANGSHAN CKING COMMUNICATION TECHNOLOGY CO.,LT
SE
-00-07-02 (hex) Varian Medical Systems
-000702 (base 16) Varian Medical Systems
- 2599 Garcia Avenue
- Mountain View CA 94043
- US
-
00-06-F3 (hex) AcceLight Networks
0006F3 (base 16) AcceLight Networks
70 Abele Road, Building 1200
@@ -75752,12 +77543,6 @@ A06A00 (base 16) Verilink Corporation
Korea (ROK)
KR
-00-04-DF (hex) Teracom Telematica Ltda.
-0004DF (base 16) Teracom Telematica Ltda.
- R. Felipe Neri, 246 CJ. 301
- Bazil
- BR
-
00-05-53 (hex) DVC Company, Inc.
000553 (base 16) DVC Company, Inc.
10200 Hwy 290 W.
@@ -77204,12 +78989,6 @@ A06A00 (base 16) Verilink Corporation
Milpitas CA 95035
US
-00-01-47 (hex) Zhone Technologies
-000147 (base 16) Zhone Technologies
- 7001 Oakport Street
- Oakland CA 94621
- US
-
00-01-2B (hex) TELENET Co., Ltd.
00012B (base 16) TELENET Co., Ltd.
@@ -80870,12 +82649,6 @@ A01B29 (base 16) Sagemcom Broadband SAS
Rueil Malmaison Cedex Hauts de Seine 92848
FR
-E4-6F-13 (hex) D-Link International
-E46F13 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
94-C1-50 (hex) 2Wire Inc
94C150 (base 16) 2Wire Inc
1764 Automation Parkway
@@ -81116,36 +82889,12 @@ F40669 (base 16) Intel Corporate
Kulim Kedah 09000
MY
-AC-A3-1E (hex) Aruba Networks
-ACA31E (base 16) Aruba Networks
- 1322 Crossman Ave.
- Sunnyvale CA 94089
- US
-
-9C-1C-12 (hex) Aruba Networks
-9C1C12 (base 16) Aruba Networks
- 1344 Crossman Ave.
- Sunnyvale CA 94089
- US
-
-00-1A-1E (hex) Aruba Networks
-001A1E (base 16) Aruba Networks
- 1322 Crossman Av
- Sunnyvale CA 94089
- US
-
28-C2-DD (hex) AzureWave Technology Inc.
28C2DD (base 16) AzureWave Technology Inc.
8F., No. 94, Baozhong Rd.,
New Taipei City Taiwan 231
TW
-84-D4-7E (hex) Aruba Networks
-84D47E (base 16) Aruba Networks
- 1322 Crossman Ave.
- Sunnyvale CA 94089
- US
-
A8-58-40 (hex) Cambridge Industries(Group) Co.,Ltd.
A85840 (base 16) Cambridge Industries(Group) Co.,Ltd.
5/F,Building 8, 2388 ChenHang Road, MinHang District
@@ -81236,12 +82985,6 @@ A4526F (base 16) ADB Broadband Italia
Taoyuan Taoyuan County 33067
TW
-00-26-B8 (hex) Actiontec Electronics, Inc
-0026B8 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
00-30-F1 (hex) Accton Technology Corp
0030F1 (base 16) Accton Technology Corp
No. 1, Creation Rd. IV
@@ -81272,18 +83015,6 @@ ECF00E (base 16) AboCom
Milano 20126
IT
-90-97-D5 (hex) Espressif Inc.
-9097D5 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
-18-FE-34 (hex) Espressif Inc.
-18FE34 (base 16) Espressif Inc.
- 2966 Jin Ke Road
- Shanghai 201203
- CN
-
54-F6-C5 (hex) FUJIAN STAR-NET COMMUNICATION CO.,LTD
54F6C5 (base 16) FUJIAN STAR-NET COMMUNICATION CO.,LTD
19-22# Building, Star-net Science Plaza, Juyuanzhou,
@@ -81794,24 +83525,6 @@ F40E22 (base 16) Samsung Electronics Co.,Ltd
Olathe KS 66062
US
-00-E0-00 (hex) FUJITSU LIMITED
-00E000 (base 16) FUJITSU LIMITED
- Musashi-kosugi Tower Place Bldg.,
- KAWASAKI 211 Kanagawa
- JP
-
-00-00-0E (hex) FUJITSU LIMITED
-00000E (base 16) FUJITSU LIMITED
- Musashi-kosugi Tower Place Bldg.,
- KAWASAKI 211 Kanagawa
- JP
-
-00-23-26 (hex) FUJITSU LIMITED
-002326 (base 16) FUJITSU LIMITED
- Musashi-kosugi Tower Place Bldg.,
- KAWASAKI 211 Kanagawa
- JP
-
00-07-CB (hex) FREEBOX SAS
0007CB (base 16) FREEBOX SAS
8 rue de la Ville l'Eveque
@@ -81872,12 +83585,6 @@ A4C0E1 (base 16) Nintendo Co., Ltd.
KYOTO KYOTO 601-8501
JP
-60-01-94 (hex) Espressif Inc.
-600194 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
F4-4D-17 (hex) GOLDCARD HIGH-TECH CO.,LTD.
F44D17 (base 16) GOLDCARD HIGH-TECH CO.,LTD.
No.158, Jinqiao Stree,Economic&Technological Development Area,
@@ -83078,12 +84785,6 @@ E4509A (base 16) HW Communications Ltd
Shenzhen 518000
CN
-20-4C-03 (hex) Aruba Networks
-204C03 (base 16) Aruba Networks
- 1344 Crossman Ave
- Sunnyvale CA 94089
- US
-
90-F0-52 (hex) MEIZU Technology Co., Ltd.
90F052 (base 16) MEIZU Technology Co., Ltd.
MEIZU Tech Bldg., Technology & Innovation Coast
@@ -83546,18 +85247,6 @@ B4A984 (base 16) Symantec Corporation
Piscataway NJ 08554
US
-24-8A-07 (hex) Mellanox Technologies, Inc.
-248A07 (base 16) Mellanox Technologies, Inc.
- 350 Oakmead Parkway, Suite 100
- Sunnyvale CA 94085
- US
-
-00-25-8B (hex) Mellanox Technologies, Inc.
-00258B (base 16) Mellanox Technologies, Inc.
- 350 Oakmead Parkway, Suite 100
- Sunnyvale CA 94085
- US
-
3C-2D-B7 (hex) Texas Instruments
3C2DB7 (base 16) Texas Instruments
12500 TI Boulevard, MS 8723
@@ -84206,30 +85895,6 @@ A41437 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.
Sunnyvale CA 94089
US
-38-52-1A (hex) Nokia
-38521A (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-84-DB-FC (hex) Nokia
-84DBFC (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-14-3E-60 (hex) Nokia
-143E60 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-D4-E3-3F (hex) Nokia
-D4E33F (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
54-54-CF (hex) PROBEDIGITAL CO.,LTD
5454CF (base 16) PROBEDIGITAL CO.,LTD
#107 Hyundai I-Valley, 31, Galmachi-ro 244beon-gil, Jungwon-gu
@@ -84620,12 +86285,6 @@ F0219D (base 16) Cal-Comp Electronics & Communications Company Ltd.
HangZhou ZheJiang 310053
CN
-EC-0D-9A (hex) Mellanox Technologies, Inc.
-EC0D9A (base 16) Mellanox Technologies, Inc.
- 350 Oakmead Parkway, Suite 100
- Sunnyvale CA 94085
- US
-
00-00-64 (hex) Yokogawa Digital Computer Corporation
000064 (base 16) Yokogawa Digital Computer Corporation
Shinjuku MIDWEST Bldg.4-30-3
@@ -84677,12 +86336,6 @@ D071C4 (base 16) zte corporation
shenzhen guangdong 518057
CN
-48-F0-7B (hex) ALPS ELECTRIC CO.,LTD.
-48F07B (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- Kakuda Miyagi-Pref 981-1595
- JP
-
3C-80-AA (hex) Ransnet Singapore Pte Ltd
3C80AA (base 16) Ransnet Singapore Pte Ltd
114, Lavender Street, #08-83, CT Hub 2
@@ -84701,36 +86354,6 @@ E89EB4 (base 16) Hon Hai Precision Ind. Co.,Ltd.
Chongqing Chongqing 401332
CN
-D4-97-0B (hex) Xiaomi Communications Co Ltd
-D4970B (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-64-CC-2E (hex) Xiaomi Communications Co Ltd
-64CC2E (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-B0-E2-35 (hex) Xiaomi Communications Co Ltd
-B0E235 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-38-A4-ED (hex) Xiaomi Communications Co Ltd
-38A4ED (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-F4-8B-32 (hex) Xiaomi Communications Co Ltd
-F48B32 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
AC-83-F3 (hex) AMPAK Technology, Inc.
AC83F3 (base 16) AMPAK Technology, Inc.
No.1,Jen Ai Road Hsinchu Industrial Park, Hukou
@@ -85097,12 +86720,6 @@ BC3F8F (base 16) HUAWEI TECHNOLOGIES CO.,LTD
SAN JOSE CA 95134
US
-50-40-61 (hex) Nokia
-504061 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
54-E3-F6 (hex) Alcatel-Lucent
54E3F6 (base 16) Alcatel-Lucent
777 East Middlefield Road
@@ -85247,12 +86864,6 @@ F8FF0B (base 16) Electronic Technology Inc.
Seongnam-si Gyeonggi-do 463-875
KR
-00-09-3A (hex) Molex
-00093A (base 16) Molex
- 5224 Katrine Avenue
- Downers Grove IL 60515
- US
-
98-AA-FC (hex) IEEE Registration Authority
98AAFC (base 16) IEEE Registration Authority
445 Hoes Lane
@@ -85511,12 +87122,6 @@ F07485 (base 16) NGD Systems, Inc.
Irvine CA 92618
US
-2C-3A-E8 (hex) Espressif Inc.
-2C3AE8 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
DC-74-A8 (hex) Samsung Electronics Co.,Ltd
DC74A8 (base 16) Samsung Electronics Co.,Ltd
#94-1, Imsoo-Dong
@@ -85613,12 +87218,6 @@ B40016 (base 16) INGENICO TERMINALS SAS
San Jose CA 94568
US
-A0-34-1B (hex) TrackR, Inc
-A0341B (base 16) TrackR, Inc
- 19 W. Carrillo St.
- Santa Barbara CA 93101
- US
-
FC-A6-67 (hex) Amazon Technologies Inc.
FCA667 (base 16) Amazon Technologies Inc.
P.O Box 8102
@@ -85793,12 +87392,6 @@ E4A749 (base 16) Palo Alto Networks
East Dundee 60118
US
-18-B4-30 (hex) Nest Labs Inc.
-18B430 (base 16) Nest Labs Inc.
- 235 Alma
- Palo Alto CA 94301
- US
-
3C-F5-91 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
3CF591 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
NO.18 HAIBIN ROAD,
@@ -85907,12 +87500,6 @@ A89675 (base 16) Motorola Mobility LLC, a Lenovo Company
Roseville 95747
US
-A4-7B-9D (hex) Espressif Inc.
-A47B9D (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
60-8E-08 (hex) Samsung Electronics Co.,Ltd
608E08 (base 16) Samsung Electronics Co.,Ltd
#94-1, Imsoo-Dong
@@ -85997,12 +87584,6 @@ FC7F56 (base 16) CoSyst Control Systems GmbH
Dongguan 523808
CN
-4C-49-E3 (hex) Xiaomi Communications Co Ltd
-4C49E3 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
28-D4-36 (hex) Jiangsu dewosi electric co., LTD
28D436 (base 16) Jiangsu dewosi electric co., LTD
Dantu district fengjingchengbang xibanya 503, building 6
@@ -86513,9 +88094,6 @@ BC5451 (base 16) Samsung Electronics Co.,Ltd
Piscataway NJ 08554
US
-AC-F8-5C (hex) Private
-ACF85C (base 16) Private
-
A0-39-EE (hex) Sagemcom Broadband SAS
A039EE (base 16) Sagemcom Broadband SAS
250, route de l'Empereur
@@ -86558,12 +88136,6 @@ D49CF4 (base 16) Palo Alto Networks
Beijing 100053
CN
-50-6B-4B (hex) Mellanox Technologies, Inc.
-506B4B (base 16) Mellanox Technologies, Inc.
- 350 Oakmead Parkway, Suite 100
- Sunnyvale CA 94085
- US
-
F8-C1-20 (hex) Xi'an Link-Science Technology Co.,Ltd
F8C120 (base 16) Xi'an Link-Science Technology Co.,Ltd
1/F,Block F,Building zhichao Weilai,No.999,10#Caotan Road,Xi'an
@@ -86672,12 +88244,6 @@ C8FAE1 (base 16) ARQ Digital LLC
Escondido CA 92029
US
-80-AD-16 (hex) Xiaomi Communications Co Ltd
-80AD16 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
04-4E-AF (hex) LG Innotek
044EAF (base 16) LG Innotek
26, Hanamsandan 5beon-ro
@@ -86690,12 +88256,6 @@ DCA333 (base 16) Shenzhen YOUHUA Technology Co., Ltd
Shenzhen Guangdong 518055
CN
-BC-DD-C2 (hex) Espressif Inc.
-BCDDC2 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
FC-7C-02 (hex) Phicomm (Shanghai) Co., Ltd.
FC7C02 (base 16) Phicomm (Shanghai) Co., Ltd.
3666 SiXian Rd.,Songjiang District
@@ -87824,12 +89384,6 @@ E8FAF7 (base 16) Guangdong Uniteddata Holding Group Co., Ltd.
GUANGZHOU GUANGDONG 510623
CN
-94-87-E0 (hex) Xiaomi Communications Co Ltd
-9487E0 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
C0-81-35 (hex) Ningbo Forfan technology Co., LTD
C08135 (base 16) Ningbo Forfan technology Co., LTD
Room B308,Tianjing Building,Tianan Cyber Park,Futian
@@ -87851,9 +89405,6 @@ B46BFC (base 16) Intel Corporate
Granby Quebec J2J 1P2
CA
-28-EF-01 (hex) Private
-28EF01 (base 16) Private
-
F8-5C-4D (hex) Nokia
F85C4D (base 16) Nokia
1 Robbins Road
@@ -88226,12 +89777,6 @@ A8B86E (base 16) LG Electronics (Mobile Communications)
Seoul 153-801
KR
-DC-4F-22 (hex) Espressif Inc.
-DC4F22 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
34-2A-F1 (hex) Texas Instruments
342AF1 (base 16) Texas Instruments
12500 TI Blvd
@@ -88268,12 +89813,6 @@ FC9DD8 (base 16) Beijing TongTongYiLian Science and Technology Ltd.
BEIJING BEIJING 100193
CN
-04-B1-67 (hex) Xiaomi Communications Co Ltd
-04B167 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
38-AD-BE (hex) New H3C Technologies Co., Ltd
38ADBE (base 16) New H3C Technologies Co., Ltd
466 Changhe Road, Binjiang District
@@ -88496,12 +90035,6 @@ DC48B2 (base 16) Baraja Pty. Ltd.
4-4-9 Kitahama Chuo-ku, Osaka 541-0041
JP
-D8-63-75 (hex) Xiaomi Communications Co Ltd
-D86375 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
DC-BF-E9 (hex) Motorola Mobility LLC, a Lenovo Company
DCBFE9 (base 16) Motorola Mobility LLC, a Lenovo Company
222 West Merchandise Mart Plaza
@@ -88514,12 +90047,6 @@ DCBFE9 (base 16) Motorola Mobility LLC, a Lenovo Company
Qingdao Shandong 266101
CN
-74-95-EC (hex) ALPS ELECTRIC CO.,LTD.
-7495EC (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- Kakuda Miyagi-Pref 981-1595
- JP
-
18-52-82 (hex) Fiberhome Telecommunication Technologies Co.,LTD
185282 (base 16) Fiberhome Telecommunication Technologies Co.,LTD
No.5 DongXin Road
@@ -88544,9 +90071,6 @@ DCBFE9 (base 16) Motorola Mobility LLC, a Lenovo Company
Gumi Gyeongbuk 730-350
KR
-E4-F1-4C (hex) Private
-E4F14C (base 16) Private
-
34-1A-35 (hex) Fiberhome Telecommunication Technologies Co.,LTD
341A35 (base 16) Fiberhome Telecommunication Technologies Co.,LTD
No.5 DongXin Road
@@ -88661,24 +90185,12 @@ E40EEE (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Dongguan 523808
CN
-20-47-DA (hex) Xiaomi Communications Co Ltd
-2047DA (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
10-1D-51 (hex) 8Mesh Networks Limited
101D51 (base 16) 8Mesh Networks Limited
Unit 607, 6/F, Yen Sheng Centre,
64 Hoi Yuen Road Kwun Tong 000
HK
-80-35-C1 (hex) Xiaomi Communications Co Ltd
-8035C1 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
78-B6-EC (hex) Scuf Gaming International LLC
78B6EC (base 16) Scuf Gaming International LLC
3970 Johns Creek Court Suite 325 Suwanee
@@ -88931,12 +90443,6 @@ D0B214 (base 16) PoeWit Inc
Beijing Beijing 100083
CN
-A4-92-CB (hex) Nokia
-A492CB (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
DC-B4-AC (hex) FLEXTRONICS MANUFACTURING(ZHUHAI)CO.,LTD.
DCB4AC (base 16) FLEXTRONICS MANUFACTURING(ZHUHAI)CO.,LTD.
Xin Qing Science & Technology Industrial Park,Jin An Town,Doumen ,Zhuhai,Guangdong,PRC
@@ -89417,12 +90923,6 @@ F85E3C (base 16) SHENZHEN ZHIBOTONG ELECTRONICS CO.,LTD
shengzhen 518110
CN
-7C-03-AB (hex) Xiaomi Communications Co Ltd
-7C03AB (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
C4-98-5C (hex) Hui Zhou Gaoshengda Technology Co.,LTD
C4985C (base 16) Hui Zhou Gaoshengda Technology Co.,LTD
No.75,Zhongkai High-Tech Development District,Huizhou
@@ -89597,6 +91097,1410 @@ D003DF (base 16) Samsung Electronics Co.,Ltd
Kulim Kedah 09000
MY
+0C-75-12 (hex) Shenzhen Kunlun TongTai Technology Co.,Ltd.
+0C7512 (base 16) Shenzhen Kunlun TongTai Technology Co.,Ltd.
+ Room 2401,B,Building 3,Tianan Yungu Industry Park,Gangtou community,bantian streets,Longgang District
+ Shenzhen Guangdong 518129
+ CN
+
+20-AD-56 (hex) Continental Automotive Systems Inc.
+20AD56 (base 16) Continental Automotive Systems Inc.
+ 21440 W. Lake Cook Rd.
+ Deer Park IL 60010
+ US
+
+18-38-AE (hex) CONSPIN SOLUTION
+1838AE (base 16) CONSPIN SOLUTION
+ 2F, Geonwoong Bldg. 109, Gwanak-ro, Gwanak-gu
+ Seoul 08833
+ KR
+
+00-25-8B (hex) Mellanox Technologies, Inc.
+00258B (base 16) Mellanox Technologies, Inc.
+ 350 Oakmead Parkway, Suite 100
+ Sunnyvale CA 94085
+ US
+
+24-8A-07 (hex) Mellanox Technologies, Inc.
+248A07 (base 16) Mellanox Technologies, Inc.
+ 350 Oakmead Parkway, Suite 100
+ Sunnyvale CA 94085
+ US
+
+EC-0D-9A (hex) Mellanox Technologies, Inc.
+EC0D9A (base 16) Mellanox Technologies, Inc.
+ 350 Oakmead Parkway, Suite 100
+ Sunnyvale CA 94085
+ US
+
+50-6B-4B (hex) Mellanox Technologies, Inc.
+506B4B (base 16) Mellanox Technologies, Inc.
+ 350 Oakmead Parkway, Suite 100
+ Sunnyvale CA 94085
+ US
+
+78-7D-53 (hex) Aerohive Networks Inc.
+787D53 (base 16) Aerohive Networks Inc.
+ 1011 McCarthy Blvd
+ Milpitas CA 95035
+ US
+
+E0-45-6D (hex) China Mobile Group Device Co.,Ltd.
+E0456D (base 16) China Mobile Group Device Co.,Ltd.
+ 32 Xuanwumen West Street,Xicheng District
+ Beijing 100053
+ CN
+
+48-87-2D (hex) SHEN ZHEN DA XIA LONG QUE TECHNOLOGY CO.,LTD
+48872D (base 16) SHEN ZHEN DA XIA LONG QUE TECHNOLOGY CO.,LTD
+ Room 511, Building C, Yuxing Technology Park, Gushu Second Road, Bao’an District
+ SHEN ZHEN GUANG DONG 518000
+ CN
+
+E4-B2-FB (hex) Apple, Inc.
+E4B2FB (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+38-B4-D3 (hex) BSH Hausgeraete GmbH
+38B4D3 (base 16) BSH Hausgeraete GmbH
+ Im Gewerbepark B10
+ Regensburg 93059
+ DE
+
+F8-38-80 (hex) Apple, Inc.
+F83880 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+24-1B-7A (hex) Apple, Inc.
+241B7A (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+40-26-19 (hex) Apple, Inc.
+402619 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+BC-FE-D9 (hex) Apple, Inc.
+BCFED9 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+80-82-23 (hex) Apple, Inc.
+808223 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+D4-BD-1E (hex) 5VT Technologies,Taiwan LTd.
+D4BD1E (base 16) 5VT Technologies,Taiwan LTd.
+ 6F,No.19-9,SanChong Rd.,Nangang Dist,
+ Taipei 11501
+ TW
+
+10-DF-FC (hex) Siemens AG
+10DFFC (base 16) Siemens AG
+ Siemensstrasse 10
+ Regensburg 93055
+ DE
+
+1C-59-9B (hex) HUAWEI TECHNOLOGIES CO.,LTD
+1C599B (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+80-7D-14 (hex) HUAWEI TECHNOLOGIES CO.,LTD
+807D14 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+7C-C3-85 (hex) HUAWEI TECHNOLOGIES CO.,LTD
+7CC385 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+38-30-F9 (hex) LG Electronics (Mobile Communications)
+3830F9 (base 16) LG Electronics (Mobile Communications)
+ 60-39, Gasan-dong, Geumcheon-gu
+ Seoul 153-801
+ KR
+
+8C-85-E6 (hex) Cleondris GmbH
+8C85E6 (base 16) Cleondris GmbH
+ Zuercherstrasse 42
+ Uitikon ZH 8142
+ CH
+
+A0-A4-C5 (hex) Intel Corporate
+A0A4C5 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+68-A4-7D (hex) Sun Cupid Technology (HK) LTD
+68A47D (base 16) Sun Cupid Technology (HK) LTD
+ 16/F, CEO Tower, 77 Wing Hong Street, Kowloon
+ Hong Kong 00000
+ HK
+
+A4-11-94 (hex) Lenovo
+A41194 (base 16) Lenovo
+ 1009 Think Place
+ Morrisvilee NC 27560
+ US
+
+94-EA-EA (hex) TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO
+94EAEA (base 16) TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO
+ Av. Buriti, 1900 – Setor B – Distrito Industrial
+ Manaus Amazonas 69075-000
+ BR
+
+50-76-AF (hex) Intel Corporate
+5076AF (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+EC-F6-BD (hex) SNCF MOBILITÉS
+ECF6BD (base 16) SNCF MOBILITÉS
+ 9 rue Jean-Philippe Rameau
+ SAINT-DENIS 93200
+ FR
+
+E0-79-5E (hex) Wuxi Xiaohu Technology Co.,Ltd.
+E0795E (base 16) Wuxi Xiaohu Technology Co.,Ltd.
+ FL.2, Building A10, 777 West Jianzhu Road, Binhu District
+ Wuxi Jiangsu Province 214000
+ CN
+
+D0-B6-0A (hex) Xingluo Technology Company Limited
+D0B60A (base 16) Xingluo Technology Company Limited
+ 28F, Building A, Aerospace Science And Technology Square, Nanshan District
+ ShenZhen GuangDong 518067
+ CN
+
+F4-39-09 (hex) Hewlett Packard
+F43909 (base 16) Hewlett Packard
+ 11445 Compaq Center Drive
+ Houston TX 77070
+ US
+
+00-42-79 (hex) Sunitec Enterprise Co.,Ltd
+004279 (base 16) Sunitec Enterprise Co.,Ltd
+ 3F.,No.98-1,Mincyuan Rd.Sindian City
+ Taipei County 231 231141
+ CN
+
+C0-13-2B (hex) Sichuan Changhong Electric Ltd.
+C0132B (base 16) Sichuan Changhong Electric Ltd.
+ No.35,East MianXin Road,MianYang,Sichaun,China.
+ MianYang SiChuan PRC 621000
+ CN
+
+6C-00-6B (hex) Samsung Electronics Co.,Ltd
+6C006B (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+80-69-40 (hex) LEXAR CO.,LIMITED
+806940 (base 16) LEXAR CO.,LIMITED
+ 10TH FLOOR,CHINA AEROSPACE CENTRE,143 HOI BUN ROAD,KWUN TONG,KOWLOON, HONG KONG
+ HONG KONG 999077
+ HK
+
+A4-26-55 (hex) LTI Motion (Shanghai) Co., Ltd.
+A42655 (base 16) LTI Motion (Shanghai) Co., Ltd.
+ NO.80, Lane 2927, LaiYang Road Pudong New District
+ Shanghai Shanghai 200137
+ CN
+
+38-A4-ED (hex) Xiaomi Communications Co Ltd
+38A4ED (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+B0-E2-35 (hex) Xiaomi Communications Co Ltd
+B0E235 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+64-CC-2E (hex) Xiaomi Communications Co Ltd
+64CC2E (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+D8-63-75 (hex) Xiaomi Communications Co Ltd
+D86375 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+80-AD-16 (hex) Xiaomi Communications Co Ltd
+80AD16 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+20-47-DA (hex) Xiaomi Communications Co Ltd
+2047DA (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+80-35-C1 (hex) Xiaomi Communications Co Ltd
+8035C1 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+94-87-E0 (hex) Xiaomi Communications Co Ltd
+9487E0 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+7C-03-AB (hex) Xiaomi Communications Co Ltd
+7C03AB (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+D4-97-0B (hex) Xiaomi Communications Co Ltd
+D4970B (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+F4-8B-32 (hex) Xiaomi Communications Co Ltd
+F48B32 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+4C-49-E3 (hex) Xiaomi Communications Co Ltd
+4C49E3 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+04-B1-67 (hex) Xiaomi Communications Co Ltd
+04B167 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+74-C1-7D (hex) Infinix mobility limited
+74C17D (base 16) Infinix mobility limited
+ RMS 05-15, 13A/F SOUTH TOWER WORLD FINANCE CTR HARBOUR CITY 17 CANTON RD TST KLN HONG KONG
+ HongKong HongKong 999077
+ HK
+
+9C-C8-FC (hex) ARRIS Group, Inc.
+9CC8FC (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+FC-AE-34 (hex) ARRIS Group, Inc.
+FCAE34 (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+3C-2C-30 (hex) Dell Inc.
+3C2C30 (base 16) Dell Inc.
+ One Dell Way
+ Round Rock TX 78682
+ US
+
+AC-F8-5C (hex) Private
+ACF85C (base 16) Private
+
+6C-2B-59 (hex) Dell Inc.
+6C2B59 (base 16) Dell Inc.
+ One Dell Way
+ Round Rock TX 78682
+ US
+
+0C-D0-F8 (hex) Cisco Systems, Inc
+0CD0F8 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+70-0B-4F (hex) Cisco Systems, Inc
+700B4F (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+10-2C-6B (hex) AMPAK Technology, Inc.
+102C6B (base 16) AMPAK Technology, Inc.
+ 3F.,No.15-1 Zhonghua Road,Hsinchu Industrial Park, Hukou,Hsinchu, Taiwan (R.O.C.)
+ Hsinchu Taiwan ROC. 30352
+ TW
+
+20-79-18 (hex) Intel Corporate
+207918 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+18-FE-34 (hex) Espressif Inc.
+18FE34 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+90-97-D5 (hex) Espressif Inc.
+9097D5 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+60-01-94 (hex) Espressif Inc.
+600194 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+D8-CE-3A (hex) Xiaomi Communications Co Ltd
+D8CE3A (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+B8-C7-4A (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+B8C74A (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+2C-3A-E8 (hex) Espressif Inc.
+2C3AE8 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+A4-7B-9D (hex) Espressif Inc.
+A47B9D (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+DC-4F-22 (hex) Espressif Inc.
+DC4F22 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+BC-DD-C2 (hex) Espressif Inc.
+BCDDC2 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+FC-18-3C (hex) Apple, Inc.
+FC183C (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+C0-E8-62 (hex) Apple, Inc.
+C0E862 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+EC-2C-E2 (hex) Apple, Inc.
+EC2CE2 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+CC-D3-C1 (hex) Vestel Elektronik San ve Tic. A.Åž.
+CCD3C1 (base 16) Vestel Elektronik San ve Tic. A.Åž.
+ Organize san
+ Manisa Turket 45030
+ TR
+
+64-C7-53 (hex) Apple, Inc.
+64C753 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+84-A9-3E (hex) Hewlett Packard
+84A93E (base 16) Hewlett Packard
+ 11445 Compaq Center Drive
+ Houston TX 77070
+ US
+
+B8-C2-27 (hex) PSTec
+B8C227 (base 16) PSTec
+ #80, Hwanggeum 3-ro 7beon-gil, Yangchon-eup
+ Gimpo-si Gyeonggi-do 10048
+ KR
+
+A8-F5-DD (hex) ARRIS Group, Inc.
+A8F5DD (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+44-D3-AD (hex) Shenzhen TINNO Mobile Technology Corp.
+44D3AD (base 16) Shenzhen TINNO Mobile Technology Corp.
+ 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen
+ Shenzhen guangdong 518053
+ CN
+
+FC-BE-7B (hex) vivo Mobile Communication Co., Ltd.
+FCBE7B (base 16) vivo Mobile Communication Co., Ltd.
+ #283,BBK Road
+ Wusha,Chang'An DongGuan City,Guangdong, 523860
+ CN
+
+48-A4-93 (hex) TAIYO YUDEN CO.,LTD
+48A493 (base 16) TAIYO YUDEN CO.,LTD
+ 8-1, Sakae-cho
+ Takasaki-shi Gunma 370-8522
+ JP
+
+7C-BC-84 (hex) IEEE Registration Authority
+7CBC84 (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+44-1C-12 (hex) Technicolor CH USA Inc.
+441C12 (base 16) Technicolor CH USA Inc.
+ 5030 Sugarloaf Parkway Bldg 6
+ Lawrenceville GA 30044
+ US
+
+00-09-3A (hex) Molex CMS
+00093A (base 16) Molex CMS
+ 5224 Katrine Avenue
+ Downers Grove IL 60515
+ US
+
+E4-F1-4C (hex) Private
+E4F14C (base 16) Private
+
+18-BB-26 (hex) FN-LINK TECHNOLOGY LIMITED
+18BB26 (base 16) FN-LINK TECHNOLOGY LIMITED
+ A Building,HuiXin industial park,No 31, YongHe road, Fuyong town, Bao'an District
+ SHENZHEN GUANGDONG 518100
+ CN
+
+08-F1-EA (hex) Hewlett Packard Enterprise
+08F1EA (base 16) Hewlett Packard Enterprise
+ 8000 Foothills Blvd.
+ Roseville CA 95747
+ US
+
+FC-94-CE (hex) zte corporation
+FC94CE (base 16) zte corporation
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China
+ shenzhen guangdong 518057
+ CN
+
+EC-F0-FE (hex) zte corporation
+ECF0FE (base 16) zte corporation
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China
+ shenzhen guangdong 518057
+ CN
+
+88-50-F6 (hex) Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd
+8850F6 (base 16) Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd
+ 3/F,A5 Building Zhiyuan Community No.1001,Xueyuan Road Nanshan Distric
+ Shenzhen Guangdong 518055
+ CN
+
+D8-A6-FD (hex) Ghost Locomotion
+D8A6FD (base 16) Ghost Locomotion
+ 800 California St. Suite 200
+ Mountain View CA 94041
+ US
+
+94-58-CB (hex) Nintendo Co.,Ltd
+9458CB (base 16) Nintendo Co.,Ltd
+ 11-1 HOKOTATE-CHO KAMITOBA,MINAMI-KU
+ KYOTO KYOTO 601-8501
+ JP
+
+90-E2-02 (hex) Texas Instruments
+90E202 (base 16) Texas Instruments
+ 12500 TI Blvd
+ Dallas TX 75243
+ US
+
+24-7D-4D (hex) Texas Instruments
+247D4D (base 16) Texas Instruments
+ 12500 TI Blvd
+ Dallas TX 75243
+ US
+
+4C-E5-AE (hex) Tianjin Beebox Intelligent Technology Co.,Ltd.
+4CE5AE (base 16) Tianjin Beebox Intelligent Technology Co.,Ltd.
+ Room 103,NO.1 of the 2nd Street
+ Tianjin Pilot Free Trade Zone Airport EconomicArea Airport International Logistics Zone, Tianjin 300300
+ CN
+
+28-75-D8 (hex) FUJIAN STAR-NET COMMUNICATION CO.,LTD
+2875D8 (base 16) FUJIAN STAR-NET COMMUNICATION CO.,LTD
+ 19-22# Building, Star-net Science Plaza, Juyuanzhou,
+ FUZHOU FUJIAN 350002
+ CN
+
+70-35-09 (hex) Cisco Systems, Inc
+703509 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+0C-EC-84 (hex) Shenzhen TINNO Mobile Technology Corp.
+0CEC84 (base 16) Shenzhen TINNO Mobile Technology Corp.
+ 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen
+ Shenzhen guangdong 518053
+ CN
+
+9C-DB-07 (hex) Thum+Mahr GmbH
+9CDB07 (base 16) Thum+Mahr GmbH
+ Heinrich-Hertz-Strasse 1-3
+ Monheim am Rhein NRW 40789
+ DE
+
+38-D9-A5 (hex) Mikotek Information Inc.
+38D9A5 (base 16) Mikotek Information Inc.
+ 3F, No. 20, Aly. 18, Ln. 478, Ruiguang Rd.
+ Taipei 114
+ TW
+
+04-07-2E (hex) VTech Electronics Ltd.
+04072E (base 16) VTech Electronics Ltd.
+ 23rd Floor, Block 1, Tai Ping Industrial Centre, 57 Ting Kok Road
+ Tai Po, N.T. NA
+ HK
+
+48-FD-A3 (hex) Xiaomi Communications Co Ltd
+48FDA3 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+7C-03-5E (hex) Xiaomi Communications Co Ltd
+7C035E (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+00-32-17 (hex) Cisco Systems, Inc
+003217 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+E0-9F-2A (hex) Iton Technology Corp.
+E09F2A (base 16) Iton Technology Corp.
+ Room 1302, Block A, Building 4,Huangge Road, Longgang District
+ Shenzhen Guangdong 518116
+ CN
+
+48-4A-30 (hex) George Robotics Limited
+484A30 (base 16) George Robotics Limited
+ Salisbury House, Station Road
+ Cambridge CB1 2LA
+ GB
+
+68-4F-64 (hex) Dell Inc.
+684F64 (base 16) Dell Inc.
+ One Dell Way
+ Round Rock TX 78682
+ US
+
+50-AD-92 (hex) NX Technologies
+50AD92 (base 16) NX Technologies
+ 5F(OS Building), 57, Daeril-2gil, Beomseo-eup
+ Ulsan 44922
+ KR
+
+00-1E-A7 (hex) Actiontec Electronics, Inc
+001EA7 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+10-5F-06 (hex) Actiontec Electronics, Inc
+105F06 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+70-F1-96 (hex) Actiontec Electronics, Inc
+70F196 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+00-26-B8 (hex) Actiontec Electronics, Inc
+0026B8 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+64-25-5E (hex) Observint Technologies, Inc.
+64255E (base 16) Observint Technologies, Inc.
+ 11000 N Mopac Expressway Suite 300
+ Austin TX 78759
+ US
+
+94-67-7E (hex) Belden India Private Limited
+94677E (base 16) Belden India Private Limited
+ Plot No. D-228, Chakan MIDC Phase 2,
+ Village Bhamboli, Taluka:Khed Pune, Maharashtra 410 507
+ IN
+
+70-BC-10 (hex) Microsoft Corporation
+70BC10 (base 16) Microsoft Corporation
+ One Microsoft Way
+ REDMOND WA 98052
+ US
+
+80-D3-36 (hex) CERN
+80D336 (base 16) CERN
+ CH-1211
+ GENEVE SUISSE/SWITZ 023
+ CH
+
+F0-51-EA (hex) Fitbit, Inc.
+F051EA (base 16) Fitbit, Inc.
+ 199 Fremont Street, 14th Fl
+ San Francisco CA 94105
+ US
+
+00-16-FE (hex) ALPS ELECTRIC CO., LTD.
+0016FE (base 16) ALPS ELECTRIC CO., LTD.
+ 1-2-1, Okinouchi,
+ Soma-city, Fukushima-pref., 976-8501
+ JP
+
+00-1B-FB (hex) ALPS ELECTRIC CO., LTD.
+001BFB (base 16) ALPS ELECTRIC CO., LTD.
+ 1-2-1, Okinouchi,
+ Soma-city, Fukushima-pref., 976-8501
+ JP
+
+48-F0-7B (hex) ALPS ELECTRIC CO., LTD.
+48F07B (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ Kakuda Miyagi-Pref 981-1595
+ JP
+
+74-95-EC (hex) ALPS ELECTRIC CO., LTD.
+7495EC (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ Kakuda Miyagi-Pref 981-1595
+ JP
+
+AC-5A-EE (hex) China Mobile Group Device Co.,Ltd.
+AC5AEE (base 16) China Mobile Group Device Co.,Ltd.
+ 32 Xuanwumen West Street,Xicheng District
+ Beijing 100053
+ CN
+
+04-98-F3 (hex) ALPS ELECTRIC CO., LTD.
+0498F3 (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1 NISHIDA
+ KAKUDA MIYAGI PREF 9876-8501
+ JP
+
+38-C0-96 (hex) ALPS ELECTRIC CO., LTD.
+38C096 (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ KAKUDA-CITY MIYAGI-PREF 981-1595
+ JP
+
+E0-75-0A (hex) ALPS ELECTRIC CO., LTD.
+E0750A (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ kakuda-City 981-1595
+ US
+
+BC-75-36 (hex) ALPS ELECTRIC CO., LTD.
+BC7536 (base 16) ALPS ELECTRIC CO., LTD.
+ nishida 6-1
+ Kakuda-City Miyagi-Pref 981-1595
+ JP
+
+10-8E-BA (hex) Molekule
+108EBA (base 16) Molekule
+ 1184 Harrison Street
+ San Francisco 94103
+ US
+
+C0-B5-D7 (hex) CHONGQING FUGUI ELECTRONICS CO.,LTD.
+C0B5D7 (base 16) CHONGQING FUGUI ELECTRONICS CO.,LTD.
+ Building D21,No.1, East Zone 1st Road,Xiyong Town,Shapingba District
+ Chongqing Chongqing 401332
+ CN
+
+2C-5D-34 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+2C5D34 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+9C-14-63 (hex) Zhejiang Dahua Technology Co., Ltd.
+9C1463 (base 16) Zhejiang Dahua Technology Co., Ltd.
+ No.1199,Waterfront Road
+ Hangzhou Zhejiang 310053
+ CN
+
+C4-7D-46 (hex) FUJITSU LIMITED
+C47D46 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+00-23-26 (hex) FUJITSU LIMITED
+002326 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+00-00-0E (hex) FUJITSU LIMITED
+00000E (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+00-E0-00 (hex) FUJITSU LIMITED
+00E000 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+8C-73-6E (hex) FUJITSU LIMITED
+8C736E (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+50-26-90 (hex) FUJITSU LIMITED
+502690 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+2C-D4-44 (hex) FUJITSU LIMITED
+2CD444 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+A8-E2-C3 (hex) Shenzhen YOUHUA Technology Co., Ltd
+A8E2C3 (base 16) Shenzhen YOUHUA Technology Co., Ltd
+ Room 407 Shenzhen University-town Business Park,Lishan Road,Taoyuan Street,Nanshan District
+ Shenzhen Guangdong 518055
+ CN
+
+C8-F7-42 (hex) HangZhou Gubei Electronics Technology Co.,Ltd
+C8F742 (base 16) HangZhou Gubei Electronics Technology Co.,Ltd
+ Room 106, No.611 Jianghong Road, Binjiang District, Hangzhou, Zhejiang, China
+ Hangzhou ZheJiang 310052
+ CN
+
+94-6A-77 (hex) Technicolor CH USA Inc.
+946A77 (base 16) Technicolor CH USA Inc.
+ 5030 Sugarloaf Parkway Bldg 6
+ Lawrenceville GA 30044
+ US
+
+74-60-FA (hex) HUAWEI TECHNOLOGIES CO.,LTD
+7460FA (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+88-40-3B (hex) HUAWEI TECHNOLOGIES CO.,LTD
+88403B (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+68-A0-3E (hex) HUAWEI TECHNOLOGIES CO.,LTD
+68A03E (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+B8-C3-85 (hex) HUAWEI TECHNOLOGIES CO.,LTD
+B8C385 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+30-DF-8D (hex) SHENZHEN GONGJIN ELECTRONICS CO.,LT
+30DF8D (base 16) SHENZHEN GONGJIN ELECTRONICS CO.,LT
+ SONGGANG
+ SHENZHEN GUANGDONG 518105
+ CN
+
+C8-28-32 (hex) Beijing Xiaomi Electronics Co., Ltd.
+C82832 (base 16) Beijing Xiaomi Electronics Co., Ltd.
+ Building C, QingHe ShunShiJiaYe Technology Park, #66 ZhuFang Rd, HaiDian District
+ Beijing Beijing 10085
+ CN
+
+18-B4-30 (hex) Nest Labs Inc.
+18B430 (base 16) Nest Labs Inc.
+ 3400 Hillview Ave.
+ Palo Alto CA 94304
+ US
+
+44-3C-88 (hex) FICOSA MAROC INTERNATIONAL
+443C88 (base 16) FICOSA MAROC INTERNATIONAL
+ Zone Franche Technopolis Rocade Rabat-Salé 11103 Sala Al Jadida - Salé
+ Salé 11103
+ MA
+
+2C-5B-E1 (hex) Centripetal Networks, Inc
+2C5BE1 (base 16) Centripetal Networks, Inc
+ 2251 Corporate Park Drive, Suite 150
+ Herndon VA 201715806
+ US
+
+70-18-A7 (hex) Cisco Systems, Inc
+7018A7 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+EC-7D-9D (hex) CPI
+EC7D9D (base 16) CPI
+ 3222 Phoenixville Pike Suite 200
+ Malvern PA 19355
+ US
+
+A4-3E-A0 (hex) iComm HK LIMITED
+A43EA0 (base 16) iComm HK LIMITED
+ Room 702 Kowloon Building 555 Nathan Road Kowloon HongKong
+ Kowloon NA
+ HK
+
+64-C2-DE (hex) LG Electronics (Mobile Communications)
+64C2DE (base 16) LG Electronics (Mobile Communications)
+ 60-39, Gasan-dong, Geumcheon-gu
+ Seoul 153-801
+ KR
+
+C8-F6-C8 (hex) Fiberhome Telecommunication Technologies Co.,LTD
+C8F6C8 (base 16) Fiberhome Telecommunication Technologies Co.,LTD
+ No.5 DongXin Road
+ Wuhan Hubei 430074
+ CN
+
+00-07-02 (hex) Varex Imaging
+000702 (base 16) Varex Imaging
+ 1678 South Pioneer Road
+ Salt Lake City UT 84104
+ US
+
+38-AF-D0 (hex) Private
+38AFD0 (base 16) Private
+
+30-4F-75 (hex) DASAN Network Solutions
+304F75 (base 16) DASAN Network Solutions
+ DASAN Tower 8F, 49 Daewangpangyo-ro644beon-gil Bundang-gu
+ Seongnam-si Gyeonggi-do 13493
+ KR
+
+C8-F7-50 (hex) Dell Inc.
+C8F750 (base 16) Dell Inc.
+ One Dell Way
+ Round Rock TX 78682
+ US
+
+DC-58-BC (hex) Thomas-Krenn.AG
+DC58BC (base 16) Thomas-Krenn.AG
+ Speltenbach-Steinaecker 1
+ Freyung 94078
+ DE
+
+C8-4F-86 (hex) Sophos Ltd
+C84F86 (base 16) Sophos Ltd
+ The Pentagon
+ Abingdon Oxfordshire OX14 3YP
+ GB
+
+A4-5F-9B (hex) Nexell
+A45F9B (base 16) Nexell
+ 12F, 31 Hwangsaeul-ro 258 beon-gil, Bundang-gu
+ Seongnam-si, Gyeonggi-do 13595
+ KR
+
+00-01-47 (hex) Zhone Technologies
+000147 (base 16) Zhone Technologies
+ 7001 Oakport Street
+ Oakland CA 94621
+ US
+
+68-9A-87 (hex) Amazon Technologies Inc.
+689A87 (base 16) Amazon Technologies Inc.
+ P.O Box 8102
+ Reno NV 89507
+ US
+
+D0-6E-DE (hex) Sagemcom Broadband SAS
+D06EDE (base 16) Sagemcom Broadband SAS
+ 250, route de l'Empereur
+ Rueil Malmaison Cedex hauts de seine 92848
+ FR
+
+84-D4-7E (hex) Aruba, a Hewlett Packard Enterprise Company
+84D47E (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+20-4C-03 (hex) Aruba, a Hewlett Packard Enterprise Company
+204C03 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+00-1A-1E (hex) Aruba, a Hewlett Packard Enterprise Company
+001A1E (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+94-B4-0F (hex) Aruba, a Hewlett Packard Enterprise Company
+94B40F (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+00-29-C2 (hex) Cisco Systems, Inc
+0029C2 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+18-7C-0B (hex) Ruckus Wireless
+187C0B (base 16) Ruckus Wireless
+ 350 West Java Drive
+ Sunnyvale CA 94089
+ US
+
+9C-1C-12 (hex) Aruba, a Hewlett Packard Enterprise Company
+9C1C12 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+AC-A3-1E (hex) Aruba, a Hewlett Packard Enterprise Company
+ACA31E (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+C0-D8-34 (hex) xvtec ltd
+C0D834 (base 16) xvtec ltd
+ Ha'nagar 24
+ Hod Hasharon Israel 4527713
+ IL
+
+4C-AE-A3 (hex) Hewlett Packard Enterprise
+4CAEA3 (base 16) Hewlett Packard Enterprise
+ 8000 Foothills Blvd.
+ Roseville CA 95747
+ US
+
+00-EE-AB (hex) Cisco Systems, Inc
+00EEAB (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+D4-53-83 (hex) Murata Manufacturing Co., Ltd.
+D45383 (base 16) Murata Manufacturing Co., Ltd.
+ 1-10-1, Higashikotari
+ Nagaokakyo-shi Kyoto 617-8555
+ JP
+
+D8-86-0B (hex) IEEE Registration Authority
+D8860B (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+78-A7-EB (hex) 1MORE
+78A7EB (base 16) 1MORE
+ TianliaoBuilding F14
+ New Materials Industrial Park,Xueyuan Blvd Shenzhen, Nanshan District 518005
+ CN
+
+20-B7-80 (hex) Toshiba Visual Solutions Corporation Co.,Ltd
+20B780 (base 16) Toshiba Visual Solutions Corporation Co.,Ltd
+ Shinkawasaki Mitsui Builing West Tower 19F, 1-1-2 Kashimada
+ Saiwai-ku, Kawasaki-shi Kanagawa 212-0058
+ JP
+
+EC-41-18 (hex) XIAOMI Electronics,CO.,LTD
+EC4118 (base 16) XIAOMI Electronics,CO.,LTD
+ Xiaomi Building, No.68 Qinghe Middle Street
+ Haidian District Beijing 100085
+ CN
+
+90-7A-58 (hex) Zegna-Daidong Limited
+907A58 (base 16) Zegna-Daidong Limited
+ Rooms 14-17, 18/F, Nan Fung Commercial, Centre 19 Lam Lok Street, Kowloon Bay
+ Hong Kong 999077
+ HK
+
+F4-BC-DA (hex) Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd
+F4BCDA (base 16) Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd
+ 3/F,A5 Building Zhiyuan Community No.1001,Xueyuan Road Nanshan District
+ Shenzhen Guangdong 518055
+ CN
+
+50-40-61 (hex) Nokia
+504061 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+38-52-1A (hex) Nokia
+38521A (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+84-DB-FC (hex) Nokia
+84DBFC (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+14-3E-60 (hex) Nokia
+143E60 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+F8-13-08 (hex) Nokia
+F81308 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+A4-92-CB (hex) Nokia
+A492CB (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+D4-E3-3F (hex) Nokia
+D4E33F (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+38-00-25 (hex) Intel Corporate
+380025 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+A4-81-7A (hex) CIG SHANGHAI CO LTD
+A4817A (base 16) CIG SHANGHAI CO LTD
+ 5th Floor, Building 8 No 2388 Chenhang Road
+ SHANGHAI 201114
+ CN
+
+88-E0-34 (hex) Shinwa industries(China) ltd.
+88E034 (base 16) Shinwa industries(China) ltd.
+ No.26,Huifeng West 2 Road,Zhongkai High-tech Zone
+ Huizhou Guangdong 516006
+ CN
+
+48-D8-45 (hex) Shenzhen Mainuoke Electronics Co., Ltd
+48D845 (base 16) Shenzhen Mainuoke Electronics Co., Ltd
+ 9th Floor, Hengtemei Building, Buji
+ Shenzhen Guangdong 518000
+ CN
+
+68-8B-0F (hex) China Mobile IOT Company Limited
+688B0F (base 16) China Mobile IOT Company Limited
+ NO.8 Yu Ma Road, NanAn Area
+ Chongqing Chongqing 401336
+ CN
+
+58-D9-C3 (hex) Motorola Mobility LLC, a Lenovo Company
+58D9C3 (base 16) Motorola Mobility LLC, a Lenovo Company
+ 222 West Merchandise Mart Plaza
+ Chicago IL 60654
+ US
+
+4C-BC-98 (hex) IEEE Registration Authority
+4CBC98 (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+C0-53-36 (hex) Beijing National Railway Research & Design Institute of Signal & Communication Group Co..Ltd.
+C05336 (base 16) Beijing National Railway Research & Design Institute of Signal & Communication Group Co..Ltd.
+ 7 floor, No.1 Automobile Museum South Road, Fengtai Science and Technology Park,
+ Beijing Beijing 100070
+ CN
+
+E8-0F-C8 (hex) Universal Electronics, Inc.
+E80FC8 (base 16) Universal Electronics, Inc.
+ 201 E. Sandpointe Ave
+ Santa Ana CA 92707
+ US
+
+A0-34-1B (hex) Adero Inc
+A0341B (base 16) Adero Inc
+ 7410 HOllister Ave
+ Goleta CA 93117
+ US
+
+10-77-17 (hex) SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD
+107717 (base 16) SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD
+ Unit East Block22-24/F,Skyworth semiconductor design Bldg., Gaoxin Ave.4.S.,Nanshan District,Shenzhen,China
+ SHENZHEN GUANGDONG 518057
+ CN
+
+74-4D-28 (hex) Routerboard.com
+744D28 (base 16) Routerboard.com
+ Mikrotikls SIA
+ Riga Riga LV1009
+ LV
+
+28-EF-01 (hex) Private
+28EF01 (base 16) Private
+
+40-F9-D5 (hex) Tecore Networks
+40F9D5 (base 16) Tecore Networks
+ 7030 Hi Tech Drive
+ Hanover MD 21076
+ US
+
+48-9D-D1 (hex) Samsung Electronics Co.,Ltd
+489DD1 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+D4-BB-C8 (hex) vivo Mobile Communication Co., Ltd.
+D4BBC8 (base 16) vivo Mobile Communication Co., Ltd.
+ #283,BBK Road
+ Wusha,Chang'An DongGuan City,Guangdong, 523860
+ CN
+
+D8-1E-DD (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+D81EDD (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+20-34-FB (hex) Xiaomi Communications Co Ltd
+2034FB (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+D4-3F-CB (hex) ARRIS Group, Inc.
+D43FCB (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+C8-52-61 (hex) ARRIS Group, Inc.
+C85261 (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+C0-8A-CD (hex) Guangzhou Shiyuan Electronic Technology Company Limited
+C08ACD (base 16) Guangzhou Shiyuan Electronic Technology Company Limited
+ No.6, 4th Yunpu Road, Yunpu industry District
+ Guangzhou Guangdong 510530
+ CN
+
+A4-CF-12 (hex) Espressif Inc.
+A4CF12 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+08-9C-86 (hex) Nokia Shanghai Bell Co. Ltd.)
+089C86 (base 16) Nokia Shanghai Bell Co. Ltd.)
+ No.388 Ning Qiao Road,Jin Qiao Pudong Shanghai 201206,P.R.China
+ Shanghai Pudong 201206
+ CN
+
+94-BF-C4 (hex) Ruckus Wireless
+94BFC4 (base 16) Ruckus Wireless
+ 350 West Java Drive
+ Sunnyvale CA 94089
+ US
+
+00-04-DF (hex) TERACOM TELEMATICA S.A
+0004DF (base 16) TERACOM TELEMATICA S.A
+ RUA AMERICA N.1000
+ Eldorado do Sul - RS Brazil
+ BR
+
+08-36-C9 (hex) NETGEAR
+0836C9 (base 16) NETGEAR
+ 350 East Plumeria Drive
+ San Jose CA 95134
+ US
+
+38-FA-CA (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+38FACA (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+C0-07-4A (hex) Brita GmbH
+C0074A (base 16) Brita GmbH
+ Heinrich-Hertz-Str. 4
+ Taunusstein 65232
+ DE
+
+7C-52-59 (hex) Sichuan Jiuzhou Electronic Technology Co., Ltd.
+7C5259 (base 16) Sichuan Jiuzhou Electronic Technology Co., Ltd.
+ No. 259, Jiuzhou Road
+ Mianyang City Sichuan Province 621000
+ CN
+
+90-20-C2 (hex) Aruba, a Hewlett Packard Enterprise Company
+9020C2 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+40-F2-1C (hex) DASAN Zhone Solutions
+40F21C (base 16) DASAN Zhone Solutions
+ 7195 Oakport Street
+ Oakland CA 94621
+ US
+
+0C-A0-6C (hex) Industrial Cyber Sensing Inc.
+0CA06C (base 16) Industrial Cyber Sensing Inc.
+ Unit 1A - 343 Montrose Street North
+ Cambridge Ontario N3H 2H6
+ CA
+
+F4-AF-E7 (hex) Apple, Inc.
+F4AFE7 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+7C-9A-1D (hex) Apple, Inc.
+7C9A1D (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+A4-83-E7 (hex) Apple, Inc.
+A483E7 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+CC-B2-55 (hex) D-Link International
+CCB255 (base 16) D-Link International
+ 1 International Business Park, #03-12, The Synergy
+ SINGAPORE 609917
+ SG
+
+BC-F6-85 (hex) D-Link International
+BCF685 (base 16) D-Link International
+ 1 International Business Park, #03-12, The Synergy
+ SINGAPORE 609917
+ SG
+
+C8-BE-19 (hex) D-Link International
+C8BE19 (base 16) D-Link International
+ 1 International Business Park, #03-12, The Synergy
+ SINGAPORE 609917
+ SG
+
+14-D6-4D (hex) D-Link International
+14D64D (base 16) D-Link International
+ 1 INTERNATIONAL BUSINESS PARK
+ SINGAPORE 609917
+ SG
+
+E4-6F-13 (hex) D-Link International
+E46F13 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+84-7C-9B (hex) GD Midea Air-Conditioning Equipment Co.,Ltd.
+847C9B (base 16) GD Midea Air-Conditioning Equipment Co.,Ltd.
+ Midea Global Innovation Center,Beijiao Town,Shunde
+ Foshan Guangdong 528311
+ CN
+
+EC-22-80 (hex) D-Link International
+EC2280 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,
+ SINGAPORE Singapore 609917
+ SG
+
+84-C9-B2 (hex) D-Link International
+84C9B2 (base 16) D-Link International
+ 1 International Business Park, #03-12, The Synergy
+ SINGAPORE 609917
+ SG
+
+E8-CC-18 (hex) D-Link International
+E8CC18 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+08-97-98 (hex) COMPAL INFORMATION (KUNSHAN) CO., LTD.
+089798 (base 16) COMPAL INFORMATION (KUNSHAN) CO., LTD.
+ NO. 25, THE 3RD Street KUNSHAN EXPORT PROCESSING ZONE
+ KUNSHAN SUZHOU 215300
+ CN
+
+00-0E-8C (hex) Siemens AG
+000E8C (base 16) Siemens AG
+ Siemensstraße 10
+ Regensburg 93055
+ DE
+
+50-EC-50 (hex) Beijing Xiaomi Mobile Software Co., Ltd
+50EC50 (base 16) Beijing Xiaomi Mobile Software Co., Ltd
+ The Rainbow City Office Building, 68 Qinghe Middle Street Haidian District
+ Beijing Beijing 100085
+ CN
+
D8-6C-E9 (hex) Sagemcom Broadband SAS
D86CE9 (base 16) Sagemcom Broadband SAS
250 route de l'Empereur
@@ -89723,24 +92627,6 @@ F40304 (base 16) Google, Inc.
Neihu District, Taipei City 114
TW
-1C-AF-F7 (hex) D-Link International
-1CAFF7 (base 16) D-Link International
- 1 INTERNATIONAL BUSINESS PARK,
- SINGAPORE 609917
- SG
-
-B8-A3-86 (hex) D-Link International
-B8A386 (base 16) D-Link International
- 1 International Business Park, #03-12, The Synergy
- SINGAPORE 609917
- SG
-
-C8-D3-A3 (hex) D-Link International
-C8D3A3 (base 16) D-Link International
- 1 Internal Business Park, #03-12. The Synergy Singapore
- Singapore 609917
- US
-
44-19-B6 (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.
4419B6 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.
No.469,Jianghui Road
@@ -90359,42 +93245,12 @@ BCC810 (base 16) Cisco SPVTG
Lawrenceville GA 30044
US
-00-21-4F (hex) ALPS ELECTRIC CO.,LTD.
-00214F (base 16) ALPS ELECTRIC CO.,LTD.
- 1-2-1, Okinouchi,
- Soma-city, Fukushima-pref., 976-8501
- JP
-
00-E0-36 (hex) PIONEER CORPORATION
00E036 (base 16) PIONEER CORPORATION
2610 Hanazono 4-Chome
Saitama Prefecture, 359-8 34567
JP
-E0-AE-5E (hex) ALPS ELECTRIC CO.,LTD.
-E0AE5E (base 16) ALPS ELECTRIC CO.,LTD.
- 6-3-36 Furukawanakazato,
- Osaki Miyagi-pref 989-6181
- JP
-
-34-C7-31 (hex) ALPS ELECTRIC CO.,LTD.
-34C731 (base 16) ALPS ELECTRIC CO.,LTD.
- 6-3-36 Furukawanakazato,
- Osaki Miyagi-pref 989-6181
- JP
-
-60-38-0E (hex) ALPS ELECTRIC CO.,LTD.
-60380E (base 16) ALPS ELECTRIC CO.,LTD.
- 1-2-1, Okinouchi
- Soma-city Fukushima 976-8501
- JP
-
-64-D4-BD (hex) ALPS ELECTRIC CO.,LTD.
-64D4BD (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- KAKUDA-CITY MIYAGI-PREF 981-1595
- JP
-
00-00-0C (hex) Cisco Systems, Inc
00000C (base 16) Cisco Systems, Inc
170 WEST TASMAN DRIVE
@@ -90713,12 +93569,6 @@ F0F249 (base 16) Hitron Technologies. Inc
Hsin-chu Taiwan 300
TW
-CC-E0-C3 (hex) Mangstor, Inc.
-CCE0C3 (base 16) Mangstor, Inc.
- 108 Wild Basin Rd
- Austin Texas 78746
- US
-
84-A4-23 (hex) Sagemcom Broadband SAS
84A423 (base 16) Sagemcom Broadband SAS
250, route de l'Empereur
@@ -90803,24 +93653,12 @@ BC9C31 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Dongguan 523808
CN
-90-C9-9B (hex) Recore Systems
-90C99B (base 16) Recore Systems
- Capitool 5
- Enschede Overijssel 7521 PL
- NL
-
5C-B5-59 (hex) CNEX Labs
5CB559 (base 16) CNEX Labs
2880 Stevens Creek Blvd
San Jose CA 95128
US
-5C-CF-7F (hex) Espressif Inc.
-5CCF7F (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
38-05-46 (hex) Foctek Photonics, Inc.
380546 (base 16) Foctek Photonics, Inc.
No. 8, the 7th Road Phase II of Minhou Tieling Industrial District
@@ -91061,12 +93899,6 @@ A0A65C (base 16) Supercomputing Systems AG
Dongguan 523808
CN
-C4-12-F5 (hex) D-Link International
-C412F5 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
44-F4-36 (hex) zte corporation
44F436 (base 16) zte corporation
12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China
@@ -91085,12 +93917,6 @@ E861BE (base 16) Melec Inc.
Hachioji Tokyo 193-0834
JP
-54-B8-0A (hex) D-Link International
-54B80A (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
D8-AD-DD (hex) Sonavation, Inc.
D8ADDD (base 16) Sonavation, Inc.
3970 RCA Blvd.
@@ -91169,12 +93995,6 @@ CC5FBF (base 16) Topwise 3G Communication Co., Ltd.
Taipei Taiwan 112
TW
-48-5D-36 (hex) Verizon
-485D36 (base 16) Verizon
- One Verizon Way
- Basking Ridge NJ 07030
- US
-
EC-60-E0 (hex) AVI-ON LABS
EC60E0 (base 16) AVI-ON LABS
2585 LARKSPUR DR
@@ -91469,12 +94289,6 @@ BCD165 (base 16) Cisco SPVTG
Osaka-City Osaka-Prefecture 545-8522
JP
-3C-1E-04 (hex) D-Link International
-3C1E04 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
E0-FF-F7 (hex) Softiron Inc.
E0FFF7 (base 16) Softiron Inc.
6540 Central Avenue
@@ -91793,12 +94607,6 @@ AC3870 (base 16) Lenovo Mobile Communication Technology Ltd.
Shenzhen Guangdong 518055
CN
-F4-DD-9E (hex) GoPro
-F4DD9E (base 16) GoPro
- 3000 Clearview Way
- San Mateo CA 94402
- US
-
40-B3-CD (hex) Chiyoda Electronics Co.,Ltd.
40B3CD (base 16) Chiyoda Electronics Co.,Ltd.
1-3-11 Nishiki-cho
@@ -92009,24 +94817,12 @@ FCC2DE (base 16) Murata Manufacturing Co., Ltd.
Dalian LiaoNing 116023
CN
-70-62-B8 (hex) D-Link International
-7062B8 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
B8-75-C0 (hex) PayPal, Inc.
B875C0 (base 16) PayPal, Inc.
2211 North 1st Street
San Jose California 95131
US
-E4-7F-B2 (hex) FUJITSU LIMITED
-E47FB2 (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
38-26-2B (hex) UTran Technology
38262B (base 16) UTran Technology
No.130, Guoji Rd., Tucheng Dist.,
@@ -92561,12 +95357,6 @@ FC19D0 (base 16) Cloud Vision Networks Technology Co.,Ltd.
Norwood MA 02062
US
-D4-D9-19 (hex) GoPro
-D4D919 (base 16) GoPro
- 3000 Clearview Way
- San Mateo CA 94402
- US
-
A4-9F-89 (hex) Shanghai Rui Rui Communication Technology Co.Ltd.
A49F89 (base 16) Shanghai Rui Rui Communication Technology Co.Ltd.
Room 1130,Building No.1
@@ -92765,12 +95555,6 @@ B8DF6B (base 16) SpotCam Co., Ltd.
New Taipei City 239
TW
-74-2B-62 (hex) FUJITSU LIMITED
-742B62 (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
58-BD-F9 (hex) Sigrand
58BDF9 (base 16) Sigrand
UNIT 602 6/F, Causeway Bay Comm Bldg,
@@ -93974,12 +96758,6 @@ C8C791 (base 16) Zero1.tv GmbH
Fremont CA 94538
US
-10-9F-A9 (hex) Actiontec Electronics, Inc
-109FA9 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
C0-A3-64 (hex) 3D Systems Massachusetts
C0A364 (base 16) 3D Systems Massachusetts
19 Connector Road
@@ -94514,12 +97292,6 @@ D0D212 (base 16) K2NET Co.,Ltd.
Seoul 158-092
KR
-9C-8E-DC (hex) Teracom Limited
-9C8EDC (base 16) Teracom Limited
-
- Noida Uttar Pradesh 201301
- IN
-
14-6A-0B (hex) Cypress Electronics Limited
146A0B (base 16) Cypress Electronics Limited
11/F., Block G, East Sun Industrial Centre, 16 Shing Yip Street,
@@ -95765,12 +98537,6 @@ A4134E (base 16) Luxul
Draper UT 84020
US
-B0-99-28 (hex) FUJITSU LIMITED
-B09928 (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
8C-11-CB (hex) ABUS Security-Center GmbH & Co. KG
8C11CB (base 16) ABUS Security-Center GmbH & Co. KG
Linker Kreuthweg 5
@@ -98315,12 +101081,6 @@ EC3091 (base 16) Cisco Systems, Inc
New Delhi 110058
IN
-00-23-50 (hex) LynTec
-002350 (base 16) LynTec
- 8401 Melrose Drive
- Lenexa KS 66214
- US
-
00-23-49 (hex) Helmholtz Centre Berlin for Material and Energy
002349 (base 16) Helmholtz Centre Berlin for Material and Energy
Department FMD
@@ -101984,12 +104744,6 @@ EC3091 (base 16) Cisco Systems, Inc
Orem UT 84097
US
-00-14-FB (hex) Technical Solutions Inc.
-0014FB (base 16) Technical Solutions Inc.
- 101-7311 Vantage Way
- Delta BC V4G 1C9
- CA
-
00-14-FA (hex) AsGa S.A.
0014FA (base 16) AsGa S.A.
Rod Roberto Moreira Km 04
@@ -104633,12 +107387,6 @@ EC3091 (base 16) Cisco Systems, Inc
Lafayette Colorado 80026
US
-00-0B-05 (hex) Pacific Broadband Networks
-000B05 (base 16) Pacific Broadband Networks
- 8-10 Keith Campbell Court
- SCORESBY VIC 3179
- AU
-
00-0A-FE (hex) NovaPal Ltd
000AFE (base 16) NovaPal Ltd
Floor 6
@@ -109394,12 +112142,6 @@ EC3091 (base 16) Cisco Systems, Inc
CHAMPAIGN IL 61821
US
-00-A0-68 (hex) BHP LIMITED
-00A068 (base 16) BHP LIMITED
- GPO BOX 86A
-
- AU
-
00-A0-B3 (hex) ZYKRONIX
00A0B3 (base 16) ZYKRONIX
7248 SOUTH TUCSON WAY
@@ -110864,12 +113606,6 @@ F82C18 (base 16) 2Wire Inc
Compton CA 90220-5221
US
-38-86-02 (hex) Flexoptix GmbH
-388602 (base 16) Flexoptix GmbH
- Waldstr.92
- Dietzenbach 63128
- DE
-
F4-EB-38 (hex) Sagemcom Broadband SAS
F4EB38 (base 16) Sagemcom Broadband SAS
15 Avenue Ambroise Croizat
@@ -111242,18 +113978,6 @@ C40938 (base 16) FUJIAN STAR-NET COMMUNICATION CO.,LTD
Kulim Kedah 09000
MY
-04-BD-88 (hex) Aruba Networks
-04BD88 (base 16) Aruba Networks
- 1322 Crossman Ave.
- Sunnyvale CA 94089
- US
-
-00-0B-86 (hex) Aruba Networks
-000B86 (base 16) Aruba Networks
- 180 Great Oaks Blvd. Suite B
- San Jose CA 95119
- US
-
88-96-F2 (hex) Valeo Schalter und Sensoren GmbH
8896F2 (base 16) Valeo Schalter und Sensoren GmbH
Laiernstrasse 12
@@ -111362,12 +114086,6 @@ F0842F (base 16) ADB Broadband Italia
Taoyuan City 330
TW
-00-20-E0 (hex) Actiontec Electronics, Inc
-0020E0 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave.
- Sunnyvale. CA 94085
- US
-
00-04-E3 (hex) Accton Technology Corp
0004E3 (base 16) Accton Technology Corp
No. 1 Creation Rd. III,
@@ -112070,12 +114788,6 @@ E0CDFD (base 16) Beijing E3Control Technology Co, LTD
Beijing Beijing 100085
CN
-20-8B-37 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
-208B37 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
- 7F,Block A,Skyworth Building,
- Shenzhen Guangdong 518057
- CN
-
08-BE-77 (hex) Green Electronics
08BE77 (base 16) Green Electronics
47801 Fremont Blvd
@@ -112178,12 +114890,6 @@ E85659 (base 16) Advanced-Connectek Inc.
Shinagawa-ku Tokyo 140-0001
JP
-FC-08-4A (hex) FUJITSU LIMITED
-FC084A (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
84-7B-EB (hex) Dell Inc.
847BEB (base 16) Dell Inc.
One Dell Way
@@ -113000,18 +115706,6 @@ DC1A01 (base 16) Ecoliv Technology ( Shenzhen ) Ltd.
Shenzhen Guangdong 518109
CN
-7C-FE-90 (hex) Mellanox Technologies, Inc.
-7CFE90 (base 16) Mellanox Technologies, Inc.
- 350 Oakmead Parkway, Suite 100
- Sunnyvale CA 94085
- US
-
-00-02-C9 (hex) Mellanox Technologies, Inc.
-0002C9 (base 16) Mellanox Technologies, Inc.
- 350 Oakmead Parkway, Suite 100
- Sunnyvale CA 94085
- US
-
D0-5F-B8 (hex) Texas Instruments
D05FB8 (base 16) Texas Instruments
12500 TI BLVD
@@ -113426,36 +116120,6 @@ F82FA8 (base 16) Hon Hai Precision Ind. Co.,Ltd.
San Jose CA 94568
US
-40-7C-7D (hex) Nokia
-407C7D (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-BC-52-B4 (hex) Nokia
-BC52B4 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-FC-2F-AA (hex) Nokia
-FC2FAA (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-90-3A-A0 (hex) Nokia
-903AA0 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-70-25-26 (hex) Nokia
-702526 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
38-F7-B2 (hex) SEOJUN ELECTRIC
38F7B2 (base 16) SEOJUN ELECTRIC
468 Gangseo-ro, 07573, SEOJUN ELECTRIC Ace Techno Tower 805
@@ -113642,42 +116306,6 @@ D0608C (base 16) zte corporation
shenzhen guangdong 518057
CN
-00-9E-C8 (hex) Xiaomi Communications Co Ltd
-009EC8 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-AC-F7-F3 (hex) Xiaomi Communications Co Ltd
-ACF7F3 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-10-2A-B3 (hex) Xiaomi Communications Co Ltd
-102AB3 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-58-44-98 (hex) Xiaomi Communications Co Ltd
-584498 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-A0-86-C6 (hex) Xiaomi Communications Co Ltd
-A086C6 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-7C-1D-D9 (hex) Xiaomi Communications Co Ltd
-7C1DD9 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
C8-66-2C (hex) Beijing Haitai Fangyuan High Technology Co,.Ltd.
C8662C (base 16) Beijing Haitai Fangyuan High Technology Co,.Ltd.
B1st&2nd floor,Unit E,NO.9 Building,Zhongguancun Software Park,Dongbeiwang West Road 8.Haidian District,Beijing
@@ -113744,12 +116372,6 @@ D436DB (base 16) Jiangsu Toppower Automotive Electronics Co., Ltd
Saitama-ken 369-1106
JP
-AC-58-7B (hex) JCT Healthcare
-AC587B (base 16) JCT Healthcare
- 2 Shearer Drive
- Adelaide SA 5169
- AU
-
30-E1-71 (hex) Hewlett Packard
30E171 (base 16) Hewlett Packard
11445 Compaq Center Drive
@@ -114098,12 +116720,6 @@ E417D8 (base 16) 8BITDO TECHNOLOGY HK LIMITED
Hong Kong Hong Kong 000000
CN
-28-6C-07 (hex) XIAOMI Electronics,CO.,LTD
-286C07 (base 16) XIAOMI Electronics,CO.,LTD
- Xiaomi Building, No.68 Qinghe Middle Street,Haidian District
- Beijing Beijing 100085
- CN
-
84-D9-31 (hex) Hangzhou H3C Technologies Co., Limited
84D931 (base 16) Hangzhou H3C Technologies Co., Limited
466 Changhe Road, Binjiang District
@@ -114566,12 +117182,6 @@ A8A5E2 (base 16) MSF-Vathauer Antriebstechnik GmbH & Co KG
San Jose CA 94568
US
-74-FF-4C (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
-74FF4C (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
- 7F,Block A,Skyworth Building,
- Shenzhen Guangdong 518057
- CN
-
A0-2C-36 (hex) FN-LINK TECHNOLOGY LIMITED
A02C36 (base 16) FN-LINK TECHNOLOGY LIMITED
A Building,HuiXin industial park,No 31, YongHe road, Fuyong town, Bao'an District
@@ -114608,12 +117218,6 @@ B47447 (base 16) CoreOS
San Francisco CA 94120-7775
US
-AC-C1-EE (hex) Xiaomi Communications Co Ltd
-ACC1EE (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
CC-A2-19 (hex) SHENZHEN ALONG INVESTMENT CO.,LTD
CCA219 (base 16) SHENZHEN ALONG INVESTMENT CO.,LTD
Room 1301,13F,Zhenye international Business Center,No.3101-90,Qianhai Road,Nanshan District
@@ -114842,12 +117446,6 @@ A084CB (base 16) SonicSensory,Inc.
Los Angeles CA 90026
US
-78-02-F8 (hex) Xiaomi Communications Co Ltd
-7802F8 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
00-23-8A (hex) Ciena Corporation
00238A (base 16) Ciena Corporation
920 Elkridge Landing
@@ -115016,12 +117614,6 @@ D8325A (base 16) Shenzhen YOUHUA Technology Co., Ltd
San Jose CA 94568
US
-80-26-89 (hex) D-Link International
-802689 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
40-9F-38 (hex) AzureWave Technology Inc.
409F38 (base 16) AzureWave Technology Inc.
8F., No. 94, Baozhong Rd.
@@ -115598,12 +118190,6 @@ EC42B4 (base 16) ADC Corporation
Piscataway NJ 08554
US
-64-16-66 (hex) Nest Labs Inc.
-641666 (base 16) Nest Labs Inc.
- 3400 Hillview Ave.
- Palo Alto CA 94304
- US
-
94-14-7A (hex) vivo Mobile Communication Co., Ltd.
94147A (base 16) vivo Mobile Communication Co., Ltd.
#283,BBK Road
@@ -115838,12 +118424,6 @@ A82BB5 (base 16) Edgecore Networks Corporation
San Diego CA 92123
US
-A8-B2-DA (hex) FUJITSU LIMITED
-A8B2DA (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
0C-B9-37 (hex) Ubee Interactive Co., Limited
0CB937 (base 16) Ubee Interactive Co., Limited
Room 1607 Dominion Centre, 43 Queen’s Road East
@@ -115910,12 +118490,6 @@ E4FB5D (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Dongguan 523808
CN
-50-8F-4C (hex) Xiaomi Communications Co Ltd
-508F4C (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
00-27-F8 (hex) Brocade Communications Systems, Inc.
0027F8 (base 16) Brocade Communications Systems, Inc.
130 Holger Way
@@ -116324,12 +118898,6 @@ E81DA8 (base 16) Ruckus Wireless
New Taipei Taiwan 24892
TW
-F4-4C-70 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
-F44C70 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
- 7F,Block A,Skyworth Building,
- Shenzhen Guangdong 518057
- CN
-
D4-90-E0 (hex) Topcon Electronics GmbH & Co. KG
D490E0 (base 16) Topcon Electronics GmbH & Co. KG
Industriestraße 7
@@ -116432,9 +119000,6 @@ F06E0B (base 16) Microsoft Corporation
Oslo 0612
NO
-F0-4F-7C (hex) Private
-F04F7C (base 16) Private
-
0C-B2-B7 (hex) Texas Instruments
0CB2B7 (base 16) Texas Instruments
12500 TI Blvd
@@ -116597,12 +119162,6 @@ D4AD2D (base 16) Fiberhome Telecommunication Technologies Co.,LTD
SHENZHEN GUANGDONG 518105
CN
-04-D1-3A (hex) Xiaomi Communications Co Ltd
-04D13A (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
24-7E-12 (hex) Cisco Systems, Inc
247E12 (base 16) Cisco Systems, Inc
80 West Tasman Drive
@@ -116687,12 +119246,6 @@ B0ACD2 (base 16) zte corporation
Kulim Kedah 09000
MY
-0C-F3-46 (hex) Xiaomi Communications Co Ltd
-0CF346 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
20-2D-23 (hex) Collinear Networks Inc.
202D23 (base 16) Collinear Networks Inc.
2901 Tasman Drive
@@ -117254,12 +119807,6 @@ C8E7D8 (base 16) MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.
San Francisco 94158
US
-A8-3E-0E (hex) HMD Global Oy
-A83E0E (base 16) HMD Global Oy
- Karaportti 2
- Espoo 02610
- FI
-
10-C1-72 (hex) HUAWEI TECHNOLOGIES CO.,LTD
10C172 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
@@ -117272,24 +119819,12 @@ A83E0E (base 16) HMD Global Oy
Dongguan 523808
CN
-00-0D-82 (hex) PHSNET SRLS
-000D82 (base 16) PHSNET SRLS
- Piazza Mariano Rumor, 18
- Arcugnano Vicenza 36057
- IT
-
08-D4-6A (hex) LG Electronics (Mobile Communications)
08D46A (base 16) LG Electronics (Mobile Communications)
60-39, Gasan-dong, Geumcheon-gu
Seoul 153-801
KR
-B4-EC-02 (hex) ALPS ELECTRIC CO.,LTD.
-B4EC02 (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- Kakuda Miyagi-Pref 981-1595
- JP
-
00-01-49 (hex) TDT AG
000149 (base 16) TDT AG
Siemensstraße 18
@@ -117512,9 +120047,6 @@ C863F1 (base 16) Sony Interactive Entertainment Inc.
Yongin-si Gyeounggi-do 16950
KR
-10-AE-60 (hex) Private
-10AE60 (base 16) Private
-
DC-28-34 (hex) HAKKO Corporation
DC2834 (base 16) HAKKO Corporation
4-5, Shiokusa 2-chome, Naniwaku
@@ -117605,12 +120137,6 @@ FC0A81 (base 16) Extreme Networks, Inc.
Lincolnshire IL 60069
US
-20-E0-9C (hex) Nokia
-20E09C (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
34-BA-38 (hex) PAL MOHAN ELECTRONICS PVT LTD
34BA38 (base 16) PAL MOHAN ELECTRONICS PVT LTD
40 DLF, INDUSTRIAL AREA, KIRTI NAGAR
@@ -117701,12 +120227,6 @@ C0A8F0 (base 16) Adamson Systems Engineering
Gilbert AZ 85233
US
-28-3B-82 (hex) D-Link International
-283B82 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
C4-00-AD (hex) Advantech Technology (CHINA) Co., Ltd.
C400AD (base 16) Advantech Technology (CHINA) Co., Ltd.
No.666, Han-Pu Rd. Yu-Shan
@@ -118733,12 +121253,6 @@ ECAF97 (base 16) GIT
Hsinchu Taiwan ROC. 30352
TW
-20-39-56 (hex) HMD Global Oy
-203956 (base 16) HMD Global Oy
- Karaportti 2
- Espoo 02610
- FI
-
78-72-5D (hex) Cisco Systems, Inc
78725D (base 16) Cisco Systems, Inc
80 West Tasman Drive
@@ -118757,21 +121271,12 @@ ECAF97 (base 16) GIT
San Diego CA 92121
US
-A4-68-BC (hex) Private
-A468BC (base 16) Private
-
CC-51-B4 (hex) Integrated Device Technology (Malaysia) Sdn. Bhd.
CC51B4 (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd.
Phase 3, Bayan Lepas FIZ
Bayan Lepas Penang 11900
MY
-7C-41-A2 (hex) Nokia
-7C41A2 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
64-A2-F9 (hex) OnePlus Technology (Shenzhen) Co., Ltd
64A2F9 (base 16) OnePlus Technology (Shenzhen) Co., Ltd
18C02, 18C03, 18C04 ,18C05,TAIRAN BUILDING,
@@ -119042,18 +121547,6 @@ D47C44 (base 16) IEEE Registration Authority
Irvine CA 92618
US
-08-25-25 (hex) Xiaomi Communications Co Ltd
-082525 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-F4-60-E2 (hex) Xiaomi Communications Co Ltd
-F460E2 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
7C-9A-54 (hex) Technicolor CH USA Inc.
7C9A54 (base 16) Technicolor CH USA Inc.
5030 Sugarloaf Parkway Bldg 6
@@ -119351,12 +121844,6 @@ B05365 (base 16) China Mobile IOT Company Limited
Chengdu Sichuan 610000
CN
-58-D5-6E (hex) D-Link International
-58D56E (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
1C-1B-B5 (hex) Intel Corporate
1C1BB5 (base 16) Intel Corporate
Lot 8, Jalan Hi-Tech 2/3
@@ -119513,12 +122000,6 @@ B0907E (base 16) Cisco Systems, Inc
Seattle WA 98104-3892
US
-54-6A-D8 (hex) Elster Water Metering Limited
-546AD8 (base 16) Elster Water Metering Limited
- Mas des Cavaliers II, 471 Rue Charles Nungesser
- Mauguio 34130
- FR
-
18-62-E4 (hex) Texas Instruments
1862E4 (base 16) Texas Instruments
12500 TI Blvd
@@ -119579,6 +122060,1407 @@ E81CBA (base 16) Fortinet, Inc.
LIMONEST Rhone 69760
FR
+80-D0-65 (hex) CKS Corporation
+80D065 (base 16) CKS Corporation
+ 1-24-11 Akebono
+ Tachikawa Tokyo 190-0012
+ JP
+
+04-EB-40 (hex) Cisco Systems, Inc
+04EB40 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+7C-0C-F6 (hex) Guangdong Huiwei High-tech Co., Ltd.
+7C0CF6 (base 16) Guangdong Huiwei High-tech Co., Ltd.
+ E Block No. 1 in Ecological Area in Puzhai NewArea
+ Fengshun County, Meizhou Guangdong province 514000
+ CN
+
+00-02-C9 (hex) Mellanox Technologies, Inc.
+0002C9 (base 16) Mellanox Technologies, Inc.
+ 350 Oakmead Parkway, Suite 100
+ Sunnyvale CA 94085
+ US
+
+7C-FE-90 (hex) Mellanox Technologies, Inc.
+7CFE90 (base 16) Mellanox Technologies, Inc.
+ 350 Oakmead Parkway, Suite 100
+ Sunnyvale CA 94085
+ US
+
+28-39-26 (hex) CyberTAN Technology Inc.
+283926 (base 16) CyberTAN Technology Inc.
+ 99 Park Ave III, Hsinchu Science Park
+ Hsinchu 308
+ TW
+
+F0-54-94 (hex) Honeywell Connected Building
+F05494 (base 16) Honeywell Connected Building
+ 1985 DOUGLAS DRIVE
+ Golden Valley MN 55422
+ US
+
+30-E3-D6 (hex) Spotify USA Inc.
+30E3D6 (base 16) Spotify USA Inc.
+ 45 West 18th Street
+ New York NY 10011
+ US
+
+F0-95-F1 (hex) Carl Zeiss AG
+F095F1 (base 16) Carl Zeiss AG
+ Carl-Zeiss-Straße 22
+ Oberkochen 73447
+ DE
+
+90-E1-7B (hex) Apple, Inc.
+90E17B (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+18-81-0E (hex) Apple, Inc.
+18810E (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+60-8C-4A (hex) Apple, Inc.
+608C4A (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+A4-D9-31 (hex) Apple, Inc.
+A4D931 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+18-A7-F1 (hex) Qingdao Haier Technology Co.,Ltd
+18A7F1 (base 16) Qingdao Haier Technology Co.,Ltd
+ Building A01,Haier Information Park, No.1 Haier Road,
+ Qingdao Shandong 266101
+ CN
+
+20-B0-01 (hex) Technicolor
+20B001 (base 16) Technicolor
+ Prins Boudewijnlaan 47
+ Edegem - Belgium B-2650
+ BE
+
+94-4F-4C (hex) Sound United LLC
+944F4C (base 16) Sound United LLC
+ One Viper Way
+ Vista CA 92081
+ US
+
+00-D6-FE (hex) Cisco Systems, Inc
+00D6FE (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+BC-9B-68 (hex) Technicolor CH USA Inc.
+BC9B68 (base 16) Technicolor CH USA Inc.
+ 5030 Sugarloaf Parkway Bldg 6
+ Lawrenceville GA 30044
+ US
+
+24-BE-18 (hex) DADOUTEK COMPANY LIMITED
+24BE18 (base 16) DADOUTEK COMPANY LIMITED
+ 14/F, Wilson Logistics Centre,No.24-28 Kung Yip St
+ Kwai Chung New Territories 000
+ CN
+
+74-9D-79 (hex) Sercomm Corporation.
+749D79 (base 16) Sercomm Corporation.
+ 3F,No.81,Yu-Yih Rd.,Chu-Nan Chen
+ Miao-Lih Hsuan 115
+ TW
+
+BC-5E-A1 (hex) PsiKick, Inc.
+BC5EA1 (base 16) PsiKick, Inc.
+ 2348 Walsh Ave
+ Santa Clara CA 95051
+ US
+
+74-BF-C0 (hex) CANON INC.
+74BFC0 (base 16) CANON INC.
+ 30-2 Shimomaruko 3-chome,
+ Ohta-ku Tokyo 146-8501
+ JP
+
+00-F4-8D (hex) Liteon Technology Corporation
+00F48D (base 16) Liteon Technology Corporation
+ 4F, 90, Chien 1 Road
+ New Taipei City Taiwan 23585
+ TW
+
+70-19-2F (hex) HUAWEI TECHNOLOGIES CO.,LTD
+70192F (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+70-D3-13 (hex) HUAWEI TECHNOLOGIES CO.,LTD
+70D313 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+F0-63-F9 (hex) HUAWEI TECHNOLOGIES CO.,LTD
+F063F9 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+9C-1D-36 (hex) HUAWEI TECHNOLOGIES CO.,LTD
+9C1D36 (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+00-B7-71 (hex) Cisco Systems, Inc
+00B771 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+14-EF-CF (hex) SCHREDER
+14EFCF (base 16) SCHREDER
+ Rue Lusambo 67
+ Brussels 1190
+ BE
+
+54-80-28 (hex) Hewlett Packard Enterprise
+548028 (base 16) Hewlett Packard Enterprise
+ 8000 Foothills Blvd.
+ Roseville CA 95747
+ US
+
+10-B9-F7 (hex) Niko-Servodan
+10B9F7 (base 16) Niko-Servodan
+ Stenager 5
+ Sønderborg 6400
+ DK
+
+10-5B-AD (hex) Mega Well Limited
+105BAD (base 16) Mega Well Limited
+ Building D21,No.1, East Zone 1st Road,Xiyong Town,Shapingba District
+ Chongqing Chongqing 401332
+ CN
+
+70-5D-CC (hex) EFM Networks
+705DCC (base 16) EFM Networks
+ 6F, Benposra II 1197-1 Bojeong Giheung Gu
+ Yong In Kyunggi do 446913
+ KR
+
+44-07-0B (hex) Google, Inc.
+44070B (base 16) Google, Inc.
+ 1600 Amphitheatre Parkway
+ Mountain View CA 94043
+ US
+
+30-29-52 (hex) Hillstone Networks Inc
+302952 (base 16) Hillstone Networks Inc
+ North Olympic Science & Technology Park Building #20, Floor #5, SouthBaosheng
+ Beijing 100192
+ CN
+
+18-4B-0D (hex) Ruckus Wireless
+184B0D (base 16) Ruckus Wireless
+ 350 West Java Drive
+ Sunnyvale CA 94089
+ US
+
+D4-12-43 (hex) AMPAK Technology, Inc.
+D41243 (base 16) AMPAK Technology, Inc.
+ No.1,Jen Ai Road Hsinchu Industrial Park, Hukou
+ Hsinchu Taiwan ROC. 30352
+ TW
+
+B8-78-26 (hex) Nintendo Co.,Ltd
+B87826 (base 16) Nintendo Co.,Ltd
+ 11-1 HOKOTATE-CHO KAMITOBA,MINAMI-KU
+ KYOTO KYOTO 601-8501
+ JP
+
+F8-50-1C (hex) Tianjin Geneuo Technology Co.,Ltd
+F8501C (base 16) Tianjin Geneuo Technology Co.,Ltd
+ Technology Avenue South JingHai Economic Development Area,Tianjin China
+ Tianjin 301609
+ CN
+
+90-E7-10 (hex) New H3C Technologies Co., Ltd
+90E710 (base 16) New H3C Technologies Co., Ltd
+ 466 Changhe Road, Binjiang District
+ Hangzhou Zhejiang 310052
+ CN
+
+10-C7-53 (hex) Qingdao Intelligent&Precise Electronics Co.,Ltd.
+10C753 (base 16) Qingdao Intelligent&Precise Electronics Co.,Ltd.
+ No.218 Qianwangang Road
+ Qingdao Shangdong 266510
+ CN
+
+6C-38-45 (hex) Fiberhome Telecommunication Technologies Co.,LTD
+6C3845 (base 16) Fiberhome Telecommunication Technologies Co.,LTD
+ No.5 DongXin Road
+ Wuhan Hubei 430074
+ CN
+
+BC-93-25 (hex) Ningbo Joyson Preh Car Connect Co.,Ltd.
+BC9325 (base 16) Ningbo Joyson Preh Car Connect Co.,Ltd.
+ No. 99, Qingyi Road
+ Ningbo Zhejiang 315040
+ CN
+
+2C-61-04 (hex) SHENZHEN FENGLIAN TECHNOLOGY CO., LTD.
+2C6104 (base 16) SHENZHEN FENGLIAN TECHNOLOGY CO., LTD.
+ ORIENTAL CYBERPORT, HIGHTECH 6 ROAD
+ SHENZHEN GUANGDONG 518057
+ CN
+
+90-C5-4A (hex) vivo Mobile Communication Co., Ltd.
+90C54A (base 16) vivo Mobile Communication Co., Ltd.
+ #283,BBK Road
+ Wusha,Chang'An DongGuan City,Guangdong, 523860
+ CN
+
+E0-13-B5 (hex) vivo Mobile Communication Co., Ltd.
+E013B5 (base 16) vivo Mobile Communication Co., Ltd.
+ #283,BBK Road
+ Wusha,Chang'An DongGuan City,Guangdong, 523860
+ CN
+
+54-83-3A (hex) Zyxel Communications Corporation
+54833A (base 16) Zyxel Communications Corporation
+ No. 6 Innovation Road II, Science Park
+ Hsichu Taiwan 300
+ TW
+
+98-D3-E7 (hex) Netafim L
+98D3E7 (base 16) Netafim L
+ Kibutz Magal
+ Kibutz Magal 38845
+ IL
+
+90-C9-9B (hex) Tesorion Nederland B.V.
+90C99B (base 16) Tesorion Nederland B.V.
+ Auke Vleerstraat 6-D
+ Enschede 7521 PG
+ NL
+
+6C-5C-3D (hex) IEEE Registration Authority
+6C5C3D (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+F4-DD-9E (hex) GoPro
+F4DD9E (base 16) GoPro
+ 3000 Clearview Way
+ San Mateo CA 94402
+ US
+
+D4-D9-19 (hex) GoPro
+D4D919 (base 16) GoPro
+ 3000 Clearview Way
+ San Mateo CA 94402
+ US
+
+D8-A9-8B (hex) Texas Instruments
+D8A98B (base 16) Texas Instruments
+ 12500 TI Blvd
+ Dallas TX 75243
+ US
+
+B0-7E-11 (hex) Texas Instruments
+B07E11 (base 16) Texas Instruments
+ 12500 TI Blvd
+ Dallas TX 75243
+ US
+
+6C-C7-EC (hex) SAMSUNG ELECTRO-MECHANICS(THAILAND)
+6CC7EC (base 16) SAMSUNG ELECTRO-MECHANICS(THAILAND)
+ 93Moo5T. Bangsamak SEMTHAI, WELLGROW INDUSTRIAL ESTATE
+ Bangpakong Chachoengsao 24180
+ TH
+
+54-6A-D8 (hex) Elster Water Metering
+546AD8 (base 16) Elster Water Metering
+ Mas des Cavaliers II, 471 Rue Charles Nungesser
+ Mauguio 34130
+ FR
+
+64-7B-CE (hex) Samsung Electronics Co.,Ltd
+647BCE (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+D4-32-60 (hex) GoPro
+D43260 (base 16) GoPro
+ 3000 Clearview Way
+ San Mateo CA 94402
+ US
+
+50-DB-3F (hex) SHENZHEN GONGJIN ELECTRONICS CO.,LT
+50DB3F (base 16) SHENZHEN GONGJIN ELECTRONICS CO.,LT
+ SONGGANG
+ SHENZHEN GUANGDONG 518105
+ CN
+
+78-70-52 (hex) Welotec GmbH
+787052 (base 16) Welotec GmbH
+ zum Hagenbach 7
+ Laer NRW 48366
+ DE
+
+58-44-98 (hex) Xiaomi Communications Co Ltd
+584498 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+AC-C1-EE (hex) Xiaomi Communications Co Ltd
+ACC1EE (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+78-02-F8 (hex) Xiaomi Communications Co Ltd
+7802F8 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+50-8F-4C (hex) Xiaomi Communications Co Ltd
+508F4C (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+04-D1-3A (hex) Xiaomi Communications Co Ltd
+04D13A (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+0C-F3-46 (hex) Xiaomi Communications Co Ltd
+0CF346 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+08-25-25 (hex) Xiaomi Communications Co Ltd
+082525 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+F4-60-E2 (hex) Xiaomi Communications Co Ltd
+F460E2 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+A4-50-46 (hex) Xiaomi Communications Co Ltd
+A45046 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+AC-58-7B (hex) JCT Healthcare
+AC587B (base 16) JCT Healthcare
+ 1/25 London Road
+ Mile End South South Australia 5031
+ AU
+
+00-23-50 (hex) RDC, Inc. dba LynTec
+002350 (base 16) RDC, Inc. dba LynTec
+ 8385 Melrose Drive
+ Lenexa KS 66214
+ US
+
+98-5D-82 (hex) Arista Networks
+985D82 (base 16) Arista Networks
+ 5453 Great America Parkway
+ Santa Clara CA 95054
+ US
+
+D8-D6-F3 (hex) Integrated Device Technology (Malaysia) Sdn. Bhd.
+D8D6F3 (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd.
+ Phase 3, Bayan Lepas FIZ
+ Bayan Lepas Penang 11900
+ MY
+
+00-9E-C8 (hex) Xiaomi Communications Co Ltd
+009EC8 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+7C-1D-D9 (hex) Xiaomi Communications Co Ltd
+7C1DD9 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+A0-86-C6 (hex) Xiaomi Communications Co Ltd
+A086C6 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+10-2A-B3 (hex) Xiaomi Communications Co Ltd
+102AB3 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+AC-F7-F3 (hex) Xiaomi Communications Co Ltd
+ACF7F3 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+08-35-1B (hex) Shenzhen Jialihua Electronic Technology Co., Ltd
+08351B (base 16) Shenzhen Jialihua Electronic Technology Co., Ltd
+ Building 26,Xin'e Industrial Area,E'gongling Village,Pinghu Town,Longgang District
+ Shenzhen Guangdong 518111
+ CN
+
+8C-7B-F0 (hex) Xufeng Development Limited
+8C7BF0 (base 16) Xufeng Development Limited
+ FLAT/RM1616 16F INTERATIONAL TRADE CENTRE 11-19 SHA TSUI RD TSUEN WAN
+ HongKong HongKong 999077
+ HK
+
+AC-50-93 (hex) Magna Electronics Europe GmbH & Co. OHG
+AC5093 (base 16) Magna Electronics Europe GmbH & Co. OHG
+ Kurfürst-Eppstein-Ring 9
+ Sailauf Bavaria 63877
+ DE
+
+E0-A5-09 (hex) Bitmain Technologies Inc
+E0A509 (base 16) Bitmain Technologies Inc
+ Building 25, North Olympic Science & Technology Park, Baosheng South Road, Haidian District, Beijing, China
+ Beijing BEIJING CHINA
+ CN
+
+3C-37-86 (hex) NETGEAR
+3C3786 (base 16) NETGEAR
+ 350 East Plumeria Drive
+ San Jose CA 95134
+ US
+
+9C-69-37 (hex) Qorvo Utrecht B.V.
+9C6937 (base 16) Qorvo Utrecht B.V.
+ Leidseveer 10
+ Utrecht Utrecht 3511SB
+ NL
+
+DC-DA-80 (hex) New H3C Technologies Co., Ltd
+DCDA80 (base 16) New H3C Technologies Co., Ltd
+ 466 Changhe Road, Binjiang District
+ Hangzhou Zhejiang 310052
+ CN
+
+60-1D-91 (hex) Motorola Mobility LLC, a Lenovo Company
+601D91 (base 16) Motorola Mobility LLC, a Lenovo Company
+ 222 West Merchandise Mart Plaza
+ Chicago IL 60654
+ US
+
+F0-4F-7C (hex) Private
+F04F7C (base 16) Private
+
+0C-FE-5D (hex) IEEE Registration Authority
+0CFE5D (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+00-A0-68 (hex) BHP LIMITED
+00A068 (base 16) BHP LIMITED
+ 1500 Post Oak Boulevard #11.08 B
+ Houston TX 77056-3030
+ US
+
+50-8C-F5 (hex) China Mobile Group Device Co.,Ltd.
+508CF5 (base 16) China Mobile Group Device Co.,Ltd.
+ 32 Xuanwumen West Street,Xicheng District
+ Beijing 100053
+ CN
+
+A0-47-D7 (hex) Best IT World (India) Pvt Ltd
+A047D7 (base 16) Best IT World (India) Pvt Ltd
+ 87, Mistry Complex,, Midc Cross Road A, Andheri-East
+ Mumbai Maharashtra 400093
+ IN
+
+5C-CF-7F (hex) Espressif Inc.
+5CCF7F (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+0C-95-41 (hex) CHIPSEA TECHNOLOGIES (SHENZHEN) CORP.
+0C9541 (base 16) CHIPSEA TECHNOLOGIES (SHENZHEN) CORP.
+ 9F,BLOCK A,GARDEN CITY DIGITAL BUILDING,NO.1079 NANHAI ROAD,NANSHAN DISTRICT
+ SHEN ZHEN GUANG DONG 518000
+ CN
+
+98-F9-C7 (hex) IEEE Registration Authority
+98F9C7 (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+B0-C3-87 (hex) GOEFER, Inc.
+B0C387 (base 16) GOEFER, Inc.
+ 118 N. Market St.
+ Frederick MD 21701
+ US
+
+38-F9-D3 (hex) Apple, Inc.
+38F9D3 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+44-E6-6E (hex) Apple, Inc.
+44E66E (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+E8-36-17 (hex) Apple, Inc.
+E83617 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+08-6B-D7 (hex) Silicon Laboratories
+086BD7 (base 16) Silicon Laboratories
+ 7000 W. William Cannon Dr.
+ Austin TX 78735
+ US
+
+34-42-62 (hex) Apple, Inc.
+344262 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+C0-9A-D0 (hex) Apple, Inc.
+C09AD0 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+04-F9-D9 (hex) Speaker Electronic(Jiashan) Co.,Ltd
+04F9D9 (base 16) Speaker Electronic(Jiashan) Co.,Ltd
+ No. 8 Development Zone Road, Huimin Sub-district
+ Jiashan Zhejiang 314112
+ CN
+
+10-C5-95 (hex) Lenovo
+10C595 (base 16) Lenovo
+ 1009 Think Place
+ Morrisvilee NC 27560
+ US
+
+44-28-A3 (hex) Jiangsu fulian Communication Technology Co., Ltd.
+4428A3 (base 16) Jiangsu fulian Communication Technology Co., Ltd.
+ The south of lanling road, yongan new district
+ Danyang Jiangsu 212300
+ CN
+
+F8-0F-6F (hex) Cisco Systems, Inc
+F80F6F (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+34-F8-E7 (hex) Cisco Systems, Inc
+34F8E7 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+B4-0F-B3 (hex) vivo Mobile Communication Co., Ltd.
+B40FB3 (base 16) vivo Mobile Communication Co., Ltd.
+ #283,BBK Road
+ Wusha,Chang'An DongGuan City,Guangdong, 523860
+ CN
+
+18-2A-44 (hex) HIROSE ELECTRONIC SYSTEM
+182A44 (base 16) HIROSE ELECTRONIC SYSTEM
+ 1-9-6 Ebisuminami
+ Shibuya Tokyo 150-0022
+ JP
+
+9C-82-75 (hex) Yichip Microelectronics (Hangzhou) Co.,Ltd
+9C8275 (base 16) Yichip Microelectronics (Hangzhou) Co.,Ltd
+ Room 401, Building 15, No.498 Guoshoujing Road, Pudong Software Park
+ Shanghai 200120
+ CN
+
+E0-18-9F (hex) EM Microelectronic
+E0189F (base 16) EM Microelectronic
+ Rue des Sors 3
+ Marin-Epagnier Neuchatel 2074
+ CH
+
+18-B9-05 (hex) Hong Kong Bouffalo Lab Limited
+18B905 (base 16) Hong Kong Bouffalo Lab Limited
+ RM 1903, 19/F Lee Garden One 33 Hysan Avenue, Causeway Bay
+ HongKong 999077
+ HK
+
+90-86-9B (hex) zte corporation
+90869B (base 16) zte corporation
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China
+ shenzhen guangdong 518057
+ CN
+
+80-54-6A (hex) SHENZHEN GONGJIN ELECTRONICS CO.,LT
+80546A (base 16) SHENZHEN GONGJIN ELECTRONICS CO.,LT
+ SONGGANG
+ SHENZHEN GUANGDONG 518105
+ CN
+
+B4-A9-FC (hex) Quanta Computer Inc.
+B4A9FC (base 16) Quanta Computer Inc.
+ No. 211, Wen-Hwa 2nd Rd.,Kuei-Shan Dist.
+ Taoyuan City Taiwan 33377
+ TW
+
+F0-0E-BF (hex) ZettaHash Inc.
+F00EBF (base 16) ZettaHash Inc.
+ 6F Chiyoda Ogawamachi Crosta, 1-11, Kanda Ogawamachi
+ Chiyoda-ku Tokyo 101-0052
+ JP
+
+30-4A-26 (hex) Shenzhen Trolink Technology CO, LTD
+304A26 (base 16) Shenzhen Trolink Technology CO, LTD
+ 201 B building 4 shijie, Chashu industry 505 block, Baoan airport Sanwei community, Hangcheng street Baoan area.
+ Shenzhen GuangDong 518000
+ CN
+
+A4-12-32 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+A41232 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+78-B2-13 (hex) DWnet Technologies(Suzhou) Corporation
+78B213 (base 16) DWnet Technologies(Suzhou) Corporation
+ No.8,Tangzhuang Road, Suzhou Industrial Park, Jiangsu, China
+ Suzhou 21500
+ CN
+
+4C-E1-9E (hex) TECNO MOBILE LIMITED
+4CE19E (base 16) TECNO MOBILE LIMITED
+ ROOMS 05-15, 13A/F., SOUTH TOWER, WORLD FINANCE CENTRE, HARBOUR CITY, 17 CANTON ROAD, TSIM SHA TSUI, KOWLOON, HONG KONG
+ Hong Kong Hong Kong 999077
+ HK
+
+D4-B7-61 (hex) Sichuan AI-Link Technology Co., Ltd.
+D4B761 (base 16) Sichuan AI-Link Technology Co., Ltd.
+ Anzhou,Industrial Park
+ Anzhou,Industrial Park Sichuan 621000
+ CN
+
+CC-9E-A2 (hex) Amazon Technologies Inc.
+CC9EA2 (base 16) Amazon Technologies Inc.
+ P.O Box 8102
+ Reno 89507
+ US
+
+4C-21-8C (hex) Panasonic India Private limited
+4C218C (base 16) Panasonic India Private limited
+ 12th floor, Ambience tower, Ambience Island
+ Gurgaon Haryana 122002
+ IN
+
+44-B4-33 (hex) tide.co.,ltd
+44B433 (base 16) tide.co.,ltd
+ 9F Kapeul GreatValley A-dong, Digitalro 9-ghil 32, Geumcheon-gu
+ seoul seoul ASIKRKS006
+ KR
+
+D4-3A-2E (hex) SHENZHEN MTC CO LTD
+D43A2E (base 16) SHENZHEN MTC CO LTD
+ 5th Floor, 3rd Building, SHENZHEN MTC Industrial Park, XiaLilang Rd, Nanwan Street, Long’gang District
+ Shenzhen Guangdong 518100
+ CN
+
+10-9F-A9 (hex) Actiontec Electronics, Inc
+109FA9 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+00-20-E0 (hex) Actiontec Electronics, Inc
+0020E0 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+AC-43-30 (hex) Versa Networks
+AC4330 (base 16) Versa Networks
+ 6001 America center Drive, Suite 400
+ San Jose CA 95070
+ US
+
+40-B0-76 (hex) ASUSTek COMPUTER INC.
+40B076 (base 16) ASUSTek COMPUTER INC.
+ 15,Li-Te Rd., Peitou, Taipei 112, Taiwan
+ Taipei Taiwan 112
+ TW
+
+4C-C7-D6 (hex) FLEXTRONICS MANUFACTURING(ZHUHAI)CO.,LTD.
+4CC7D6 (base 16) FLEXTRONICS MANUFACTURING(ZHUHAI)CO.,LTD.
+ Xin Qing Science & Technology Industrial Park,Jin An Town,Doumen ,Zhuhai,Guangdong,PRC
+ Zhuhai Guangdong 519180
+ CN
+
+B8-BC-5B (hex) Samsung Electronics Co.,Ltd
+B8BC5B (base 16) Samsung Electronics Co.,Ltd
+ 129, Samsung-ro, Youngtongl-Gu
+ Suwon Gyeonggi-Do 16677
+ KR
+
+90-94-0A (hex) Analog Devices, Inc
+90940A (base 16) Analog Devices, Inc
+ Unit 2200, Airport Business Park, Kinsale Road
+ Cork T12 X36X
+ IE
+
+00-21-4F (hex) ALPS ELECTRIC CO., LTD.
+00214F (base 16) ALPS ELECTRIC CO., LTD.
+ 1-2-1, Okinouchi,
+ Soma-city, Fukushima-pref., 976-8501
+ JP
+
+B4-EC-02 (hex) ALPS ELECTRIC CO., LTD.
+B4EC02 (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ Kakuda Miyagi-Pref 981-1595
+ JP
+
+E0-AE-5E (hex) ALPS ELECTRIC CO., LTD.
+E0AE5E (base 16) ALPS ELECTRIC CO., LTD.
+ 6-3-36 Furukawanakazato,
+ Osaki Miyagi-pref 989-6181
+ JP
+
+34-C7-31 (hex) ALPS ELECTRIC CO., LTD.
+34C731 (base 16) ALPS ELECTRIC CO., LTD.
+ 6-3-36 Furukawanakazato,
+ Osaki Miyagi-pref 989-6181
+ JP
+
+60-38-0E (hex) ALPS ELECTRIC CO., LTD.
+60380E (base 16) ALPS ELECTRIC CO., LTD.
+ 1-2-1, Okinouchi
+ Soma-city Fukushima 976-8501
+ JP
+
+64-D4-BD (hex) ALPS ELECTRIC CO., LTD.
+64D4BD (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ KAKUDA-CITY MIYAGI-PREF 981-1595
+ JP
+
+74-2B-62 (hex) FUJITSU LIMITED
+742B62 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+E4-7F-B2 (hex) FUJITSU LIMITED
+E47FB2 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+FC-08-4A (hex) FUJITSU LIMITED
+FC084A (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+A8-B2-DA (hex) FUJITSU LIMITED
+A8B2DA (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+D4-25-CC (hex) IEEE Registration Authority
+D425CC (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+B0-99-28 (hex) FUJITSU LIMITED
+B09928 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+8C-6D-C4 (hex) Megapixel VR
+8C6DC4 (base 16) Megapixel VR
+ 340 S. Lemon Ave
+ Walnut CA 91789
+ US
+
+78-DD-12 (hex) Arcadyan Corporation
+78DD12 (base 16) Arcadyan Corporation
+ No.8, Sec.2, Guangfu Rd.
+ Hsinchu City Hsinchu 30071
+ TW
+
+D4-AD-71 (hex) Cisco Systems, Inc
+D4AD71 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+64-16-66 (hex) Nest Labs Inc.
+641666 (base 16) Nest Labs Inc.
+ 3400 Hillview Ave.
+ Palo Alto CA 94304
+ US
+
+94-D0-0D (hex) HUAWEI TECHNOLOGIES CO.,LTD
+94D00D (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+E8-4C-56 (hex) INTERCEPT SERVICES LIMITED
+E84C56 (base 16) INTERCEPT SERVICES LIMITED
+ Bates Mill, Colne Road
+ Huddersfield North Yorkshire HD1 3AG
+ GB
+
+38-86-02 (hex) Flexoptix GmbH
+388602 (base 16) Flexoptix GmbH
+ Muehltalstr. 153
+ Darmstadt 64297
+ DE
+
+98-09-CF (hex) OnePlus Technology (Shenzhen) Co., Ltd
+9809CF (base 16) OnePlus Technology (Shenzhen) Co., Ltd
+ 18C02, 18C03, 18C04 ,18C05,TAIRAN BUILDING,
+ Shenzhen Guangdong 518000
+ CN
+
+84-13-9F (hex) zte corporation
+84139F (base 16) zte corporation
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China
+ shenzhen guangdong 518057
+ CN
+
+40-DF-02 (hex) LINE BIZ Plus
+40DF02 (base 16) LINE BIZ Plus
+ Alphadom Tower IV (6F), Bundangnaegok-ro 117, Bundang-gu
+ Seongnam Gyeonggi 13529
+ KR
+
+2C-C4-07 (hex) machineQ
+2CC407 (base 16) machineQ
+ 1900 market st
+ philadelphia PA 19103
+ US
+
+54-47-41 (hex) XCHENG HOLDING
+544741 (base 16) XCHENG HOLDING
+ ROOM 401F, Building 5, No.3000 LONG DONG Avenue, Pudong New District
+ Shanghai 201203
+ CN
+
+98-44-B6 (hex) INFRANOR SAS
+9844B6 (base 16) INFRANOR SAS
+ Avenue Jean Moulin
+ LOURDES 65100
+ FR
+
+00-67-62 (hex) Fiberhome Telecommunication Technologies Co.,LTD
+006762 (base 16) Fiberhome Telecommunication Technologies Co.,LTD
+ No.5 DongXin Road
+ Wuhan Hubei 430074
+ CN
+
+00-0B-05 (hex) Pacific Broadband Networks
+000B05 (base 16) Pacific Broadband Networks
+ Suite 15, Building 3, 195 Wellington Road
+ Clayton VIC 3168
+ AU
+
+A4-17-91 (hex) Shenzhen Decnta Technology Co.,LTD.
+A41791 (base 16) Shenzhen Decnta Technology Co.,LTD.
+ F13,No.02,Building Shangqi,Nanhaidadao 4050 Nanshan District,Shenzhen,P.R.China
+ shenzhen Guangdong 518057
+ CN
+
+B0-68-E6 (hex) CHONGQING FUGUI ELECTRONICS CO.,LTD.
+B068E6 (base 16) CHONGQING FUGUI ELECTRONICS CO.,LTD.
+ Building D21,No.1, East Zone 1st Road,Xiyong Town,Shapingba District
+ Chongqing Chongqing 401332
+ CN
+
+F8-D4-78 (hex) Flextronics Tech.(Ind) Pvt Ltd
+F8D478 (base 16) Flextronics Tech.(Ind) Pvt Ltd
+ SURVEYNO.381, PADUR ROAD, KUTHAMBAKKAM VILLAGE, 602107 POONAMALLEE TALUK, THIRUVALLUR DISTRIC
+ Chennai 602107
+ IN
+
+B8-C2-53 (hex) Juniper Networks
+B8C253 (base 16) Juniper Networks
+ 1133 Innovation Way
+ Sunnyvale CA 94089
+ US
+
+98-DA-C4 (hex) TP-LINK TECHNOLOGIES CO.,LTD.
+98DAC4 (base 16) TP-LINK TECHNOLOGIES CO.,LTD.
+ Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan
+ Shenzhen Guangdong 518057
+ CN
+
+04-BD-88 (hex) Aruba, a Hewlett Packard Enterprise Company
+04BD88 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+00-0B-86 (hex) Aruba, a Hewlett Packard Enterprise Company
+000B86 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+00-0D-82 (hex) PHSNET
+000D82 (base 16) PHSNET
+ Piazza Mariano Rumor, 18
+ Arcugnano Vicenza 36057
+ IT
+
+CC-E0-C3 (hex) EXTEN Technologies, Inc.
+CCE0C3 (base 16) EXTEN Technologies, Inc.
+ 4201 W Parmer Lane Bldg A, Ste 200
+ Austin TX 78727
+ US
+
+8C-53-D2 (hex) China Mobile Group Device Co.,Ltd.
+8C53D2 (base 16) China Mobile Group Device Co.,Ltd.
+ 32 Xuanwumen West Street,Xicheng District
+ Beijing 100053
+ CN
+
+78-36-07 (hex) Cermate Technologies Inc.
+783607 (base 16) Cermate Technologies Inc.
+ 7F-1, No 168 Lien-Chen Rd.
+ Chung-Ho Dist New Taipei City
+ TW
+
+48-5D-36 (hex) Verizon
+485D36 (base 16) Verizon
+ One Verizon Way
+ Basking Ridge NJ 07030
+ US
+
+BC-52-B4 (hex) Nokia
+BC52B4 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+FC-2F-AA (hex) Nokia
+FC2FAA (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+20-E0-9C (hex) Nokia
+20E09C (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+40-7C-7D (hex) Nokia
+407C7D (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+50-A0-A4 (hex) Nokia
+50A0A4 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+7C-41-A2 (hex) Nokia
+7C41A2 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+90-3A-A0 (hex) Nokia
+903AA0 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+70-25-26 (hex) Nokia
+702526 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+28-6C-07 (hex) XIAOMI Electronics,CO.,LTD
+286C07 (base 16) XIAOMI Electronics,CO.,LTD
+ Xiaomi Building, No.68 Qinghe Middle Street
+ Haidian District Beijing 100085
+ CN
+
+2C-55-7C (hex) Shenzhen YOUHUA Technology Co., Ltd
+2C557C (base 16) Shenzhen YOUHUA Technology Co., Ltd
+ Room 407 Shenzhen University-town Business Park,Lishan Road,Taoyuan Street,Nanshan District
+ Shenzhen Guangdong 518055
+ CN
+
+D8-8D-C8 (hex) Atil Technology Co., LTD
+D88DC8 (base 16) Atil Technology Co., LTD
+ Rm. 3, 21F., No.93, Sec. 2, Roosevelt Rd.,
+ Da’an Dist., Taipei City 106
+ TW
+
+38-3C-9C (hex) Fujian Newland Payment Technology Co.,Ltd.
+383C9C (base 16) Fujian Newland Payment Technology Co.,Ltd.
+ No. B602,Building #1,Haixia Jingmao Plaza,Fuzhou Bonded Area
+ Fuzhou Fujian 350015
+ CN
+
+00-14-FB (hex) Technical Solutions Inc.
+0014FB (base 16) Technical Solutions Inc.
+ 101-7188 Progress Way
+ Delta BC V4G 1M6
+ CA
+
+4C-C8-A1 (hex) Cisco Meraki
+4CC8A1 (base 16) Cisco Meraki
+ 500 Terry A. Francois Blvd
+ San Francisco 94158
+ US
+
+00-52-C2 (hex) peiker acustic GmbH
+0052C2 (base 16) peiker acustic GmbH
+ Max-Planck-Strasse 28-32
+ Friedrichsdorf 61381
+ DE
+
+58-EC-ED (hex) Integrated Device Technology (Malaysia) Sdn. Bhd.
+58ECED (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd.
+ Phase 3, Bayan Lepas FIZ
+ Bayan Lepas Penang 11900
+ MY
+
+94-D0-75 (hex) CIS Crypto
+94D075 (base 16) CIS Crypto
+ Nauchnyy proezd 6
+ Moscow 117246
+ RU
+
+B8-8F-B4 (hex) JABIL CIRCUIT ITALIA S.R.L
+B88FB4 (base 16) JABIL CIRCUIT ITALIA S.R.L
+ via andrea appiani 12
+ milano 20121
+ IT
+
+DC-ED-84 (hex) Haverford Systems Inc
+DCED84 (base 16) Haverford Systems Inc
+ 152 Robbins Road
+ Downingtown PA 19335
+ US
+
+64-4C-36 (hex) Intel Corporate
+644C36 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+64-73-66 (hex) Shenzhen Siera Technology Ltd
+647366 (base 16) Shenzhen Siera Technology Ltd
+ Room 2039, Shenhai Building, Wanzhong Village, Bulong Road, Minzhi, Longhua district, City: Shenzhen
+ Shenzhen Guangdong 518131
+ CN
+
+A4-68-BC (hex) Private
+A468BC (base 16) Private
+
+D4-E8-80 (hex) Cisco Systems, Inc
+D4E880 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+10-AE-60 (hex) Private
+10AE60 (base 16) Private
+
+AC-2D-A9 (hex) TECNO MOBILE LIMITED
+AC2DA9 (base 16) TECNO MOBILE LIMITED
+ ROOMS 05-15, 13A/F., SOUTH TOWER, WORLD FINANCE CENTRE, HARBOUR CITY, 17 CANTON ROAD, TSIM SHA TSUI, KOWLOON, HONG KONG
+ Hong Kong Hong Kong 999077
+ HK
+
+AC-F6-F7 (hex) LG Electronics (Mobile Communications)
+ACF6F7 (base 16) LG Electronics (Mobile Communications)
+ 60-39, Gasan-dong, Geumcheon-gu
+ Seoul 153-801
+ KR
+
+7C-38-AD (hex) Samsung Electronics Co.,Ltd
+7C38AD (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+3C-20-F6 (hex) Samsung Electronics Co.,Ltd
+3C20F6 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+E4-9F-1E (hex) ARRIS Group, Inc.
+E49F1E (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+FC-B6-62 (hex) IC Holdings LLC
+FCB662 (base 16) IC Holdings LLC
+ 9066 S 300 W
+ Sandy UT 84070
+ US
+
+74-88-BB (hex) Cisco Systems, Inc
+7488BB (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+B0-0C-D1 (hex) Hewlett Packard
+B00CD1 (base 16) Hewlett Packard
+ 11445 Compaq Center Drive
+ Houston 77070
+ US
+
+F0-D4-E2 (hex) Dell Inc.
+F0D4E2 (base 16) Dell Inc.
+ One Dell Way
+ Round Rock TX 78682
+ US
+
+9C-8E-DC (hex) Teracom Limited
+9C8EDC (base 16) Teracom Limited
+
+ Noida Uttar Pradesh 201301
+ IN
+
+20-8B-37 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+208B37 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+74-FF-4C (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+74FF4C (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+F4-4C-70 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+F44C70 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+2C-CC-E6 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+2CCCE6 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+E8-B2-FE (hex) HUMAX Co., Ltd.
+E8B2FE (base 16) HUMAX Co., Ltd.
+ HUMAX Village, 216, Hwangsaeul-ro, Bu
+ Seongnam-si Gyeonggi-do 463-875
+ KR
+
+8C-96-5F (hex) Shandong Zhongan Technology Co., Ltd.
+8C965F (base 16) Shandong Zhongan Technology Co., Ltd.
+ North of Kejia Road, East District, Jinan High-tech Zone
+ Jinan Shandong 250100
+ CN
+
+20-39-56 (hex) HMD Global Oy
+203956 (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+A8-3E-0E (hex) HMD Global Oy
+A83E0E (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+70-3C-69 (hex) Apple, Inc.
+703C69 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+B8-B2-F8 (hex) Apple, Inc.
+B8B2F8 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+00-5B-94 (hex) Apple, Inc.
+005B94 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+88-B2-91 (hex) Apple, Inc.
+88B291 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+CC-D2-81 (hex) Apple, Inc.
+CCD281 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+34-DB-9C (hex) Sagemcom Broadband SAS
+34DB9C (base 16) Sagemcom Broadband SAS
+ 250, route de l'Empereur
+ Rueil Malmaison Cedex hauts de seine 92848
+ FR
+
+54-62-E2 (hex) Apple, Inc.
+5462E2 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+44-18-FD (hex) Apple, Inc.
+4418FD (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+14-7B-AC (hex) Nokia
+147BAC (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+70-CD-91 (hex) TERACOM TELEMATICA S.A
+70CD91 (base 16) TERACOM TELEMATICA S.A
+ RUA AMERICA,1000
+ Eldorado do Sul Rio Grande do Sul 92990-000
+ BR
+
+70-62-B8 (hex) D-Link International
+7062B8 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+3C-1E-04 (hex) D-Link International
+3C1E04 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+54-B8-0A (hex) D-Link International
+54B80A (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+C4-12-F5 (hex) D-Link International
+C412F5 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+C8-D3-A3 (hex) D-Link International
+C8D3A3 (base 16) D-Link International
+ 1 Internal Business Park, #03-12. The Synergy Singapore
+ Singapore 609917
+ US
+
+B8-A3-86 (hex) D-Link International
+B8A386 (base 16) D-Link International
+ 1 International Business Park, #03-12, The Synergy
+ SINGAPORE 609917
+ SG
+
+1C-AF-F7 (hex) D-Link International
+1CAFF7 (base 16) D-Link International
+ 1 INTERNATIONAL BUSINESS PARK,
+ SINGAPORE 609917
+ SG
+
+28-3B-82 (hex) D-Link International
+283B82 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+58-D5-6E (hex) D-Link International
+58D56E (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+80-26-89 (hex) D-Link International
+802689 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+10-A3-B8 (hex) Iskratel d.o.o.
+10A3B8 (base 16) Iskratel d.o.o.
+ Ljubljanska cesta 24a
+ Kranj 4000
+ SI
+
+00-77-8D (hex) Cisco Systems, Inc
+00778D (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+70-57-BF (hex) New H3C Technologies Co., Ltd
+7057BF (base 16) New H3C Technologies Co., Ltd
+ 466 Changhe Road, Binjiang District
+ Hangzhou Zhejiang 310052
+ CN
+
2C-39-96 (hex) Sagemcom Broadband SAS
2C3996 (base 16) Sagemcom Broadband SAS
250 route de l'Empereur
@@ -119627,12 +123509,6 @@ A0F895 (base 16) Shenzhen TINNO Mobile Technology Corp.
Richardson TX 75082
US
-0C-61-27 (hex) Actiontec Electronics, Inc
-0C6127 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
00-1B-11 (hex) D-Link Corporation
001B11 (base 16) D-Link Corporation
NO.289, Sinhu 3rd Rd.,
@@ -119663,12 +123539,6 @@ F07D68 (base 16) D-Link Corporation
Taipei City 114
TW
-78-54-2E (hex) D-Link International
-78542E (base 16) D-Link International
- 1 Internal Business Park, #03-12.
- SINGAPORE Singapore 609917
- TW
-
3C-DD-89 (hex) SOMO HOLDINGS & TECH. CO.,LTD.
3CDD89 (base 16) SOMO HOLDINGS & TECH. CO.,LTD.
6, Mujeonggonddan-Gil
@@ -120587,42 +124457,12 @@ A46C2A (base 16) Cisco Systems, Inc
Lawrenceville 30044
US
-00-26-43 (hex) ALPS ELECTRIC CO.,LTD.
-002643 (base 16) ALPS ELECTRIC CO.,LTD.
- 1-2-1, Okinouchi,
- Soma-city, Fukushima-pref., 976-8501
- JP
-
-00-24-33 (hex) ALPS ELECTRIC CO.,LTD.
-002433 (base 16) ALPS ELECTRIC CO.,LTD.
- 1-2-1, Okinouchi,
- Soma-city, Fukushima-pref., 976-8501
- JP
-
74-5E-1C (hex) PIONEER CORPORATION
745E1C (base 16) PIONEER CORPORATION
1-1 Shin-ogura
Kawasaki-shi Kanagawa Prefecture 2120031
JP
-00-06-F5 (hex) ALPS ELECTRIC CO.,LTD.
-0006F5 (base 16) ALPS ELECTRIC CO.,LTD.
- 6-3-36 Furukawanakazato,
- Osaki Miyagi-pref 989-6181
- JP
-
-00-06-F7 (hex) ALPS ELECTRIC CO.,LTD.
-0006F7 (base 16) ALPS ELECTRIC CO.,LTD.
- 6-3-36 Furukawanakazato,
- Osaki Miyagi-pref 989-6181
- JP
-
-00-07-04 (hex) ALPS ELECTRIC CO.,LTD.
-000704 (base 16) ALPS ELECTRIC CO.,LTD.
- 6-3-36 Furukawanakazato,
- Osaki Miyagi-pref 989-6181
- JP
-
1C-1D-86 (hex) Cisco Systems, Inc
1C1D86 (base 16) Cisco Systems, Inc
170 West Tasman Drive
@@ -120779,12 +124619,6 @@ F8C372 (base 16) TSUZUKI DENKI
mainato-ku tokyo-to 105-8665
JP
-90-8D-78 (hex) D-Link International
-908D78 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
A4-CC-32 (hex) Inficomm Co., Ltd
A4CC32 (base 16) Inficomm Co., Ltd
3F, B-31 Building, Tanglang Industry Park, Taoyuan Str, Nanshan District
@@ -121103,12 +124937,6 @@ E01AEA (base 16) Allied Telesis, Inc.
Hefei Anhui 230000
CN
-6C-72-20 (hex) D-Link International
-6C7220 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
F0-26-24 (hex) WAFA TECHNOLOGIES CO., LTD.
F02624 (base 16) WAFA TECHNOLOGIES CO., LTD.
Room302, Longtaili Building
@@ -121634,12 +125462,6 @@ F42C56 (base 16) SENOR TECH CO LTD
Gorinchem Zuid Holland 4201 HL
NL
-48-EE-0C (hex) D-Link International
-48EE0C (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
48-C0-93 (hex) Xirrus, Inc.
48C093 (base 16) Xirrus, Inc.
2101 Corporate Center Dr.
@@ -121796,12 +125618,6 @@ D4EC86 (base 16) LinkedHope Intelligent Technologies Co., Ltd
Redmond Washington 98052
FI
-A0-A3-E2 (hex) Actiontec Electronics, Inc
-A0A3E2 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
54-09-8D (hex) deister electronic GmbH
54098D (base 16) deister electronic GmbH
Hermann-Bahlsen-Str. 11
@@ -121850,12 +125666,6 @@ CCBDD3 (base 16) Ultimaker B.V.
Seongnam-si Gyeonggi-do 463-400
KR
-58-10-8C (hex) Intelbras
-58108C (base 16) Intelbras
- BR 101, km 210, S/N°
- São José Santa Catarina 88104800
- BR
-
18-71-17 (hex) eta plus electronic gmbh
187117 (base 16) eta plus electronic gmbh
Lauterstr. 29
@@ -122276,12 +126086,6 @@ D42F23 (base 16) Akenori PTE Ltd
Fremont CA 94538
US
-4C-8B-30 (hex) Actiontec Electronics, Inc
-4C8B30 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
08-05-CD (hex) DongGuang EnMai Electronic Product Co.Ltd.
0805CD (base 16) DongGuang EnMai Electronic Product Co.Ltd.
701 7th Floor,YiFeng Building,
@@ -123218,12 +127022,6 @@ B8C46F (base 16) PRIMMCON INDUSTRIES INC
Hsinchu City 30070
TW
-0C-11-05 (hex) Ringslink (Xiamen) Network Communication Technologies Co., Ltd
-0C1105 (base 16) Ringslink (Xiamen) Network Communication Technologies Co., Ltd
- Suite 201-15, 31 WangHai Rd
- Xiamen Fujian 361008
- CN
-
94-50-47 (hex) Rechnerbetriebsgruppe
945047 (base 16) Rechnerbetriebsgruppe
Werner von Siemens Str. 64
@@ -124178,12 +127976,6 @@ EC4993 (base 16) Qihan Technology Co., Ltd
Shenzhen Guangdong 518040
CN
-B0-AC-FA (hex) FUJITSU LIMITED
-B0ACFA (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
1C-95-9F (hex) Veethree Electronics And Marine LLC
1C959F (base 16) Veethree Electronics And Marine LLC
2050 47th Terrace East
@@ -124709,12 +128501,6 @@ D8337F (base 16) Office FA.com Co.,Ltd.
Mitcham VIC 3132
AU
-40-8B-07 (hex) Actiontec Electronics, Inc
-408B07 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
98-02-84 (hex) Theobroma Systems GmbH
980284 (base 16) Theobroma Systems GmbH
Gutheil-Schoder Gasse 17
@@ -124847,12 +128633,6 @@ B01C91 (base 16) Elim Co
Suwon city Gyeonggi-Do 440-300
KR
-0C-A2-F4 (hex) Chameleon Technology (UK) Limited
-0CA2F4 (base 16) Chameleon Technology (UK) Limited
- Winters Barn, Haggs Farm Business Park
- Harrogate North Yorkshire HG3 1EQ
- GB
-
84-6A-ED (hex) Wireless Tsukamoto.,co.LTD
846AED (base 16) Wireless Tsukamoto.,co.LTD
16-21 1chome,Isoyama
@@ -125405,12 +129185,6 @@ F0022B (base 16) Chrontel
San Jose CA 95131
US
-00-7F-28 (hex) Actiontec Electronics, Inc
-007F28 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
0C-92-4E (hex) Rice Lake Weighing Systems
0C924E (base 16) Rice Lake Weighing Systems
230 West Coleman St
@@ -126065,12 +129839,6 @@ B8A8AF (base 16) Logic S.p.A.
Irvine CA 92618
US
-5C-9A-D8 (hex) FUJITSU LIMITED
-5C9AD8 (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
14-4C-1A (hex) Max Communication GmbH
144C1A (base 16) Max Communication GmbH
Siemensstrasse 47
@@ -126605,12 +130373,6 @@ E08A7E (base 16) Exponent
Frangarto Bolzano 39010
IT
-F8-DA-E2 (hex) Beta LaserMike
-F8DAE2 (base 16) Beta LaserMike
- 8001 Technology Blvd
- Dayton OH 45424
- US
-
E8-04-62 (hex) Cisco Systems, Inc
E80462 (base 16) Cisco Systems, Inc
80 West Tasman Drive
@@ -129272,12 +133034,6 @@ DC3350 (base 16) TechSAT GmbH
San Jose CA 94568
US
-00-1E-B8 (hex) Fortis, Inc.
-001EB8 (base 16) Fortis, Inc.
- #502 Techno plaza, 149-10 Yatap-dong,
- Seongnam-si, Gyeonggi-do, 463-816
- KR
-
00-1E-B1 (hex) Cryptsoft Pty Ltd
001EB1 (base 16) Cryptsoft Pty Ltd
P.O. Box 6389
@@ -131180,12 +134936,6 @@ DC3350 (base 16) TechSAT GmbH
Chicago IL 60661
US
-00-18-01 (hex) Actiontec Electronics, Inc
-001801 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
00-17-F5 (hex) LIG NEOPTEK
0017F5 (base 16) LIG NEOPTEK
926 Gosaek-dong
@@ -131885,12 +135635,6 @@ DC3350 (base 16) TechSAT GmbH
Brno EU/Europe 618 00
CZ
-00-15-05 (hex) Actiontec Electronics, Inc
-001505 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
00-15-04 (hex) GAME PLUS CO., LTD.
001504 (base 16) GAME PLUS CO., LTD.
164-57, Yeonghwa-dong, Jangan-gu
@@ -132491,24 +136235,12 @@ DC3350 (base 16) TechSAT GmbH
Yangchun-Gu Seoul 158-050
KR
-00-14-1D (hex) LTi DRIVES GmbH
-00141D (base 16) LTi DRIVES GmbH
- Gewerbestrasse 5-9
- Lahnau Hessen 35633
- DE
-
00-14-11 (hex) Deutschmann Automation GmbH & Co. KG
001411 (base 16) Deutschmann Automation GmbH & Co. KG
Carl-Zeiss-Str. 8
Bad Camberg D - 65520
DE
-00-45-01 (hex) Versus Technology, Inc.
-004501 (base 16) Versus Technology, Inc.
- 2600 Miller Creek Road
- Traverse City MI 49684
- US
-
00-14-03 (hex) Renasis, LLC
001403 (base 16) Renasis, LLC
1530 N. State St.
@@ -133025,12 +136757,6 @@ DC3350 (base 16) TechSAT GmbH
609602
SG
-00-0F-B3 (hex) Actiontec Electronics, Inc
-000FB3 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
00-0F-A6 (hex) S2 Security Corporation
000FA6 (base 16) S2 Security Corporation
6 Abbott Road
@@ -137963,12 +141689,6 @@ DC3350 (base 16) TechSAT GmbH
FR
-00-D0-D9 (hex) DEDICATED MICROCOMPUTERS
-00D0D9 (base 16) DEDICATED MICROCOMPUTERS
- GROUP, LTD.
- M274FL UNITED KINGDOM
- GB
-
00-D0-CD (hex) ATAN TECHNOLOGY INC.
00D0CD (base 16) ATAN TECHNOLOGY INC.
#5, ALLEY 18, LANE 81,
@@ -141209,12 +144929,6 @@ AA0000 (base 16) DIGITAL EQUIPMENT CORPORATION
WEBSTER NY 14580
US
-08-00-30 (hex) CERN
-080030 (base 16) CERN
- CH-1211 GENEVE 23
- SUISSE/SWITZ
- CH
-
00-DD-01 (hex) UNGERMANN-BASS INC.
00DD01 (base 16) UNGERMANN-BASS INC.
3900 FREEDOM CIRCLE
@@ -141653,24 +145367,6 @@ D00ED9 (base 16) Taicang T&W Electronics
Kulim Kedah 09000
MY
-40-E3-D6 (hex) Aruba Networks
-40E3D6 (base 16) Aruba Networks
- 1322 Crossman Ave.
- Sunnyvale CA 94089
- US
-
-24-DE-C6 (hex) Aruba Networks
-24DEC6 (base 16) Aruba Networks
- 1344 Crossman Ave.
- Sunnyvale CA 94089
- US
-
-D8-C7-C8 (hex) Aruba Networks
-D8C7C8 (base 16) Aruba Networks
- 1344 Crossman Ave.
- Sunnyvale CA 94089
- US
-
90-0B-C1 (hex) Sprocomm Technologies CO.,Ltd
900BC1 (base 16) Sprocomm Technologies CO.,Ltd
5D-506 F1.6 Block,Tianfa Building,Tianan Chegongmiao Industrial Park,Futian Dist,Shenzhen China
@@ -141731,12 +145427,6 @@ A04FD4 (base 16) ADB Broadband Italia
Taoyuan Taoyuan County 33067
TW
-00-26-62 (hex) Actiontec Electronics, Inc
-002662 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
00-19-3E (hex) ADB Broadband Italia
00193E (base 16) ADB Broadband Italia
VIALE SARCA 222
@@ -141761,12 +145451,6 @@ DC0B1A (base 16) ADB Broadband Italia
Milano 20126
IT
-AC-D0-74 (hex) Espressif Inc.
-ACD074 (base 16) Espressif Inc.
- A207-1, 456 Bibo Road, Zhangjiang High-tech Park,Shanghai 201203, China
- Shanghai Shanghai 201203
- CN
-
D0-53-49 (hex) Liteon Technology Corporation
D05349 (base 16) Liteon Technology Corporation
4F, 90, Chien 1 Road ChungHo
@@ -142169,12 +145853,6 @@ A8A089 (base 16) Tactical Communications
Shenzhen Guangdong 518057
CN
-5C-C6-D0 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
-5CC6D0 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
- 13F,Block A,Skyworth Building,Gaoxin AVE.1.S,
- ShenZhen GuangDong 518057
- US
-
08-05-81 (hex) Roku, Inc.
080581 (base 16) Roku, Inc.
12980 Saratoga Ave
@@ -142205,12 +145883,6 @@ DC3A5E (base 16) Roku, Inc.
Hukou, Hsinchu 303
TW
-00-17-42 (hex) FUJITSU LIMITED
-001742 (base 16) FUJITSU LIMITED
- Musashi-kosugi Tower Place Bldg.,403
- Kawasaki Kanagawa 211-8588
- JP
-
2C-10-C1 (hex) Nintendo Co., Ltd.
2C10C1 (base 16) Nintendo Co., Ltd.
11-1 HOKOTATE-CHO KAMITOBA, MINAMI-KU
@@ -142427,12 +146099,6 @@ C4E510 (base 16) Mechatro, Inc.
Wuppertal NRW 42279
DE
-10-78-5B (hex) Actiontec Electronics, Inc
-10785B (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
88-A0-84 (hex) Formation Data Systems
88A084 (base 16) Formation Data Systems
39141 Civic Center Drive, Suite 410
@@ -142505,12 +146171,6 @@ A09D91 (base 16) SoundBridge
Newcastle upon Tyne NE3 3PF
GB
-2C-21-D7 (hex) IMAX Corporation
-2C21D7 (base 16) IMAX Corporation
- 4320 La Jolla Village Drive Suite #250
- San Diego CA 92122
- US
-
00-26-F7 (hex) Nivetti Systems Pvt. Ltd.
0026F7 (base 16) Nivetti Systems Pvt. Ltd.
#727,8th Main, JP Nagar 3rd Phase
@@ -142955,12 +146615,6 @@ D89403 (base 16) Hewlett Packard Enterprise
Roseville CA 95747
US
-9C-8D-7C (hex) ALPS ELECTRIC CO.,LTD.
-9C8D7C (base 16) ALPS ELECTRIC CO.,LTD.
- 6-1
- Kakuda Miyagi-Pref 981-1595
- JP
-
E0-4F-43 (hex) Universal Global Scientific Industrial Co., Ltd.
E04F43 (base 16) Universal Global Scientific Industrial Co., Ltd.
141, Lane 351, Taiping Road, Sec.1,Tsao Tuen
@@ -143141,12 +146795,6 @@ B0D7CC (base 16) Tridonic GmbH & Co KG
Mayfield Heights OH 44124-6118
US
-20-C0-47 (hex) Verizon
-20C047 (base 16) Verizon
- One Verizon Way
- Basking Ridge 07030
- US
-
AC-04-81 (hex) Jiangsu Huaxing Electronics Co., Ltd.
AC0481 (base 16) Jiangsu Huaxing Electronics Co., Ltd.
the Industrial concentration zone, Zhiqian town,Jintan distric
@@ -143357,12 +147005,6 @@ A48269 (base 16) Datrium, Inc.
Mountlake Terrace WA 98043
US
-00-18-DA (hex) AMBER wireless GmbH
-0018DA (base 16) AMBER wireless GmbH
- Rudi-Schillings-Str. 31
- Trier 54296
- DE
-
EC-24-B8 (hex) Texas Instruments
EC24B8 (base 16) Texas Instruments
12500 TI BLVD
@@ -143417,12 +147059,6 @@ D0FF50 (base 16) Texas Instruments
Shenzhen Guangdong 518125
CN
-F4-52-14 (hex) Mellanox Technologies, Inc.
-F45214 (base 16) Mellanox Technologies, Inc.
- 350 Oakmead Parkway, Suite 100
- Sunnyvale CA 94085
- US
-
68-9E-19 (hex) Texas Instruments
689E19 (base 16) Texas Instruments
12500 TI BLVD
@@ -144113,12 +147749,6 @@ B4D135 (base 16) Cloudistics
Berlin 10559
DE
-30-AE-A4 (hex) Espressif Inc.
-30AEA4 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
0C-49-33 (hex) Sichuan Jiuzhou Electronic Technology Co., Ltd.
0C4933 (base 16) Sichuan Jiuzhou Electronic Technology Co., Ltd.
No. 259, Jiuzhou Road
@@ -144137,48 +147767,6 @@ B05216 (base 16) Hon Hai Precision Ind. Co.,Ltd.
New Taipei City Linkou District 24452
TW
-68-DF-DD (hex) Xiaomi Communications Co Ltd
-68DFDD (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-C4-6A-B7 (hex) Xiaomi Communications Co Ltd
-C46AB7 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-FC-64-BA (hex) Xiaomi Communications Co Ltd
-FC64BA (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-20-82-C0 (hex) Xiaomi Communications Co Ltd
-2082C0 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-34-80-B3 (hex) Xiaomi Communications Co Ltd
-3480B3 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-74-51-BA (hex) Xiaomi Communications Co Ltd
-7451BA (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
-64-B4-73 (hex) Xiaomi Communications Co Ltd
-64B473 (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
8C-2F-A6 (hex) Solid Optics B.V.
8C2FA6 (base 16) Solid Optics B.V.
Huchtstraat 35
@@ -144203,12 +147791,6 @@ BCA8A6 (base 16) Intel Corporate
Edegem - Belgium B-2650
BE
-38-AF-D7 (hex) FUJITSU LIMITED
-38AFD7 (base 16) FUJITSU LIMITED
- Mushashi-kosuge Tower Place 13F
- Kawasaki Kanagawa 211-0063
- JP
-
28-99-3A (hex) Arista Networks
28993A (base 16) Arista Networks
5453 Great America Parkway
@@ -144713,30 +148295,6 @@ C8E776 (base 16) PTCOM Technology
REDMOND WA 98052
US
-C4-08-4A (hex) Nokia
-C4084A (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-0C-54-B9 (hex) Nokia
-0C54B9 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-8C-90-D3 (hex) Nokia
-8C90D3 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
-34-AA-99 (hex) Nokia
-34AA99 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
F8-63-3F (hex) Intel Corporate
F8633F (base 16) Intel Corporate
Lot 8, Jalan Hi-Tech 2/3
@@ -144959,12 +148517,6 @@ C0288D (base 16) Logitech, Inc
Camas WA 98607
US
-9C-1E-95 (hex) Actiontec Electronics, Inc
-9C1E95 (base 16) Actiontec Electronics, Inc
- 760 North Mary Ave
- Sunnyvale CA 94085
- US
-
E0-78-A3 (hex) Shanghai Winner Information Technology Co.,Inc
E078A3 (base 16) Shanghai Winner Information Technology Co.,Inc
Lujiazui Software Park,No.190,Lane 91,Eshan Road,Pudong New Area
@@ -145646,9 +149198,6 @@ EC8AC7 (base 16) Fiberhome Telecommunication Technologies Co.,LTD
Wuhan Hubei 430074
CN
-00-50-79 (hex) Private
-005079 (base 16) Private
-
5C-00-38 (hex) Viasat Group S.p.A.
5C0038 (base 16) Viasat Group S.p.A.
Via Aosta 23
@@ -145697,12 +149246,6 @@ B4A8B9 (base 16) Cisco Systems, Inc
Englewood CO 80112
US
-38-E6-0A (hex) Xiaomi Communications Co Ltd
-38E60A (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
C0-42-D0 (hex) Juniper Networks
C042D0 (base 16) Juniper Networks
1133 Innovation Way
@@ -145811,12 +149354,6 @@ EC9B8B (base 16) Hewlett Packard Enterprise
New Taipei City Taiwan 23585
TW
-18-0F-76 (hex) D-Link International
-180F76 (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
70-3A-73 (hex) Shenzhen Sundray Technologies Company Limited
703A73 (base 16) Shenzhen Sundray Technologies Company Limited
6th Floor,Block A1, Nanshan iPark, No.1001 XueYuan Road, Nanshan District
@@ -145895,12 +149432,6 @@ B02680 (base 16) Cisco Systems, Inc
Chiyodaku Tokyo 101-8532
JP
-C8-8F-26 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
-C88F26 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
- 7F,Block A,Skyworth Building,
- Shenzhen Guangdong 518057
- CN
-
48-BD-3D (hex) New H3C Technologies Co., Ltd
48BD3D (base 16) New H3C Technologies Co., Ltd
466 Changhe Road, Binjiang District
@@ -147467,18 +150998,6 @@ E81367 (base 16) AIRSOUND Inc.
Foshan Guangdong 528311
CN
-34-CE-00 (hex) XIAOMI Electronics,CO.,LTD
-34CE00 (base 16) XIAOMI Electronics,CO.,LTD
- Xiaomi Building, No.68 Qinghe Middle Street,Haidian District
- Beijing Beijing 100085
- CN
-
-F8-2F-08 (hex) Molex
-F82F08 (base 16) Molex
- 2222 Wellington Court
- Lisle IL 60532
- US
-
68-26-2A (hex) SICHUAN TIANYI COMHEART TELECOMCO., LTD
68262A (base 16) SICHUAN TIANYI COMHEART TELECOMCO., LTD
FL12, TowerB,Tianyi international Hotel,No.2 West Section One, Second Ring Road,
@@ -147623,12 +151142,6 @@ CC90E8 (base 16) Shenzhen YOUHUA Technology Co., Ltd
Shenzhen Guangdong 518055
CN
-88-CC-45 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
-88CC45 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
- 7F,Block A,Skyworth Building,
- Shenzhen Guangdong 518057
- CN
-
60-53-17 (hex) Sandstone Technologies
605317 (base 16) Sandstone Technologies
1920 lyell ave
@@ -147803,12 +151316,6 @@ C8DE51 (base 16) IntegraOptics
Seoul Seoul 03920
KR
-74-DA-DA (hex) D-Link International
-74DADA (base 16) D-Link International
- 1 Internal Business Park, #03-12,The Synergy, Singapore
- Singapore Singapore 609917
- SG
-
BC-D7-13 (hex) Owl Labs
BCD713 (base 16) Owl Labs
33-1/2 Union Square
@@ -147893,12 +151400,6 @@ C4BED4 (base 16) Avaya Inc
Morristown NJ 07960
US
-30-FE-31 (hex) Nokia
-30FE31 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
70-30-18 (hex) Avaya Inc
703018 (base 16) Avaya Inc
360 Mt Kemble Ave
@@ -147917,12 +151418,6 @@ C4BED4 (base 16) Avaya Inc
Wals Salzburg A-5071
AT
-00-EC-0A (hex) Xiaomi Communications Co Ltd
-00EC0A (base 16) Xiaomi Communications Co Ltd
- The Rainbow City of China Resources
- NO.68, Qinghe Middle Street Haidian District, Beijing 100085
- CN
-
A8-6B-7C (hex) SHENZHEN FENGLIAN TECHNOLOGY CO., LTD.
A86B7C (base 16) SHENZHEN FENGLIAN TECHNOLOGY CO., LTD.
ORIENTAL CYBERPORT, HIGHTECH 6 ROAD
@@ -148115,12 +151610,6 @@ E0107F (base 16) Ruckus Wireless
Sunnyvale CA 94089
US
-78-11-DC (hex) XIAOMI Electronics,CO.,LTD
-7811DC (base 16) XIAOMI Electronics,CO.,LTD
- Xiaomi Building, No.68 Qinghe Middle Street,Haidian District
- Beijing Beijing 100085
- CN
-
D8-37-BE (hex) SHENZHEN GONGJIN ELECTRONICS CO.,LT
D837BE (base 16) SHENZHEN GONGJIN ELECTRONICS CO.,LT
#1905 Mei Hong Road
@@ -148187,12 +151676,6 @@ F0F8F2 (base 16) Texas Instruments
Dallas TX 75243
US
-24-B2-DE (hex) Espressif Inc.
-24B2DE (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
78-D8-00 (hex) IEEE Registration Authority
78D800 (base 16) IEEE Registration Authority
445 Hoes Lane
@@ -148205,12 +151688,6 @@ F0F8F2 (base 16) Texas Instruments
Boston MA 02110
US
-50-64-2B (hex) XIAOMI Electronics,CO.,LTD
-50642B (base 16) XIAOMI Electronics,CO.,LTD
- Xiaomi Building, No.68 Qinghe Middle Street,Haidian District
- Beijing Beijing 100085
- CN
-
84-A1-D1 (hex) Sagemcom Broadband SAS
84A1D1 (base 16) Sagemcom Broadband SAS
250, route de l'Empereur
@@ -148571,12 +152048,6 @@ B0E17E (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Santa Ana CA 92707
US
-68-C6-3A (hex) Espressif Inc.
-68C63A (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
F4-E2-04 (hex) Traqueur
F4E204 (base 16) Traqueur
1, rue Royale
@@ -148673,12 +152144,6 @@ A407B6 (base 16) Samsung Electronics Co.,Ltd
Gumi Gyeongbuk 730-350
KR
-14-9F-B6 (hex) GUANGDONG GENIUS TECHNOLOGY CO.,LTD.
-149FB6 (base 16) GUANGDONG GENIUS TECHNOLOGY CO.,LTD.
- #126,BBK Road,Wusha,Chang'An
- Dong Guan Guang Dong 523860
- CN
-
7C-1C-4E (hex) LG Innotek
7C1C4E (base 16) LG Innotek
26, Hanamsandan 5beon-ro
@@ -148985,12 +152450,6 @@ CC7B61 (base 16) NIKKISO CO., LTD.
Dallas TX 75243
US
-CC-50-E3 (hex) Espressif Inc.
-CC50E3 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
DC-AF-68 (hex) WEIFANG GOERTEK ELECTRONICS CO.,LTD
DCAF68 (base 16) WEIFANG GOERTEK ELECTRONICS CO.,LTD
Gaoxin 2 Road, Free Trade Zone,Weifang,Shandong,261205,P.R.China
@@ -149255,12 +152714,6 @@ D46761 (base 16) United Gulf Gate Co.
Kuwait 0000
KW
-98-03-9B (hex) Mellanox Technologies, Inc.
-98039B (base 16) Mellanox Technologies, Inc.
- 350 Oakmead Parkway, Suite 100
- Sunnyvale CA 94085
- US
-
F8-27-2E (hex) Mercku
F8272E (base 16) Mercku
509 Beaver Creek Rd.
@@ -149495,12 +152948,6 @@ A49B4F (base 16) HUAWEI TECHNOLOGIES CO.,LTD
TAIPEI 100
TW
-24-21-24 (hex) Nokia
-242124 (base 16) Nokia
- 600 March Road
- Kanata Ontario K2K 2E6
- CA
-
64-62-8A (hex) evon GmbH
64628A (base 16) evon GmbH
Frank-Stronach-Straße 8
@@ -149537,12 +152984,6 @@ A49B4F (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Zhongshan Guangdong 528462
CN
-88-34-FE (hex) Bosch Automotive Products (Suzhou) Co. Ltd
-8834FE (base 16) Bosch Automotive Products (Suzhou) Co. Ltd
- No. 455 Xing Long Street,Suzhou Industrial Park,Suzhou P.R., 215021 China
- Suzhou Jiangsu 215021
- CN
-
54-C3-3E (hex) Ciena Corporation
54C33E (base 16) Ciena Corporation
7035 Ridge Road
@@ -149561,30 +153002,30 @@ A49B4F (base 16) HUAWEI TECHNOLOGIES CO.,LTD
Shenzhen Guangdong Province 518101
CN
+88-34-FE (hex) Bosch Automotive Products (Suzhou) Co. Ltd
+8834FE (base 16) Bosch Automotive Products (Suzhou) Co. Ltd
+ No. 455 Xing Long Street,Suzhou Industrial Park,Suzhou P.R., 215021 China
+ Suzhou Jiangsu 215021
+ CN
+
18-C2-BF (hex) BUFFALO.INC
18C2BF (base 16) BUFFALO.INC
AKAMONDORI Bld.,30-20,Ohsu 3-chome,Naka-ku
Nagoya Aichi Pref. 460-8315
JP
-D4-AB-82 (hex) ARRIS Group, Inc.
-D4AB82 (base 16) ARRIS Group, Inc.
- 6450 Sequence Drive
- San Diego CA 92121
- US
-
-C4-4F-33 (hex) Espressif Inc.
-C44F33 (base 16) Espressif Inc.
- Room 204, Building 2, 690 Bibo Road, Pudong New Area
- Shanghai Shanghai 201203
- CN
-
70-03-7E (hex) Technicolor CH USA Inc.
70037E (base 16) Technicolor CH USA Inc.
5030 Sugarloaf Parkway Bldg 6
Lawrenceville GA 30044
US
+D4-AB-82 (hex) ARRIS Group, Inc.
+D4AB82 (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
14-59-C0 (hex) NETGEAR
1459C0 (base 16) NETGEAR
350 East Plumeria Drive
@@ -149597,32 +153038,1337 @@ C44F33 (base 16) Espressif Inc.
Morrisvilee NC 27560
US
+EC-C4-0D (hex) Nintendo Co.,Ltd
+ECC40D (base 16) Nintendo Co.,Ltd
+ 11-1 HOKOTATE-CHO KAMITOBA,MINAMI-KU
+ KYOTO KYOTO 601-8501
+ JP
+
+B0-2A-43 (hex) Google, Inc.
+B02A43 (base 16) Google, Inc.
+ 1600 Amphitheatre Parkway
+ Mountain View CA 94043
+ US
+
+BC-AF-91 (hex) TE Connectivity Sensor Solutions
+BCAF91 (base 16) TE Connectivity Sensor Solutions
+ 4 rue Gaye-Marie, CS 83163
+ Toulouse 31027
+ FR
+
5C-C9-99 (hex) New H3C Technologies Co., Ltd
5CC999 (base 16) New H3C Technologies Co., Ltd
466 Changhe Road, Binjiang District
Hangzhou Zhejiang 310052
CN
+98-3B-8F (hex) Intel Corporate
+983B8F (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
74-05-A5 (hex) TP-LINK TECHNOLOGIES CO.,LTD.
7405A5 (base 16) TP-LINK TECHNOLOGIES CO.,LTD.
Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan
Shenzhen Guangdong 518057
CN
-B0-2A-43 (hex) Google, Inc.
-B02A43 (base 16) Google, Inc.
+BC-3F-4E (hex) Teleepoch Ltd
+BC3F4E (base 16) Teleepoch Ltd
+ No.13 Langshan Rd,HiTech Park,Nanshan District
+ Shenzhen Guangdong 518000
+ CN
+
+00-EA-BD (hex) Cisco Systems, Inc
+00EABD (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+C8-47-82 (hex) Areson Technology Corp.
+C84782 (base 16) Areson Technology Corp.
+ 11F., No. 646, Sec. 5, Chongxin Rd., Sanchong District
+ New Taipei City 24158
+ TW
+
+F4-52-14 (hex) Mellanox Technologies, Inc.
+F45214 (base 16) Mellanox Technologies, Inc.
+ 350 Oakmead Parkway, Suite 100
+ Sunnyvale CA 94085
+ US
+
+98-03-9B (hex) Mellanox Technologies, Inc.
+98039B (base 16) Mellanox Technologies, Inc.
+ 350 Oakmead Parkway, Suite 100
+ Sunnyvale CA 94085
+ US
+
+CC-98-8B (hex) SONY Visual Products Inc.
+CC988B (base 16) SONY Visual Products Inc.
+ 2-10-1 Osaki
+ Shinagawa-ku Tokyo 141-8610
+ JP
+
+BC-F3-10 (hex) Aerohive Networks Inc.
+BCF310 (base 16) Aerohive Networks Inc.
+ 1011 McCarthy Blvd
+ Milpitas CA 95035
+ US
+
+9C-A5-25 (hex) Shandong USR IOT Technology Limited
+9CA525 (base 16) Shandong USR IOT Technology Limited
+ Floor 11,Building 1,No.1166 Xinluo Street,Gaoxin District,Jinan,Shandong,250101,China
+ Jinan Shandong 250101
+ CN
+
+C0-BF-A7 (hex) Juniper Networks
+C0BFA7 (base 16) Juniper Networks
+ 1133 Innovation Way
+ Sunnyvale CA 94089
+ US
+
+14-69-A2 (hex) SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+1469A2 (base 16) SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+ NO.198 FIRST SECTION,SNOW MOUNTAIN AVENUE, JINYUAN TOWN, DAYI COUNTY,
+ CHENGDU SICHUAN 611330
+ CN
+
+FC-B6-D8 (hex) Apple, Inc.
+FCB6D8 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+6C-E8-5C (hex) Apple, Inc.
+6CE85C (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+70-2E-D9 (hex) Guangzhou Shiyuan Electronics Co., Ltd.
+702ED9 (base 16) Guangzhou Shiyuan Electronics Co., Ltd.
+ No.6, 4th Yunpu Road, Yunpu industry District
+ Gunagzhou Guangdong 510530
+ CN
+
+2C-21-D7 (hex) IMAX Corporation
+2C21D7 (base 16) IMAX Corporation
+ 2525 Speakman Drive
+ Mississauga Ontario L5K 1B1
+ CA
+
+00-18-DA (hex) Würth Elektronik eiSos GmbH & Co. KG
+0018DA (base 16) Würth Elektronik eiSos GmbH & Co. KG
+ Max-Eyth-Straße 1
+ Waldenburg 74638
+ DE
+
+A4-FC-77 (hex) Mega Well Limited
+A4FC77 (base 16) Mega Well Limited
+ Building D21,No.1, East Zone 1st Road,Xiyong Town,Shapingba District
+ Chongqing Chongqing 401332
+ CN
+
+E4-B9-7A (hex) Dell Inc.
+E4B97A (base 16) Dell Inc.
+ One Dell Way
+ Round Rock TX 78682
+ US
+
+0C-A2-F4 (hex) Chameleon Technology (UK) Limited
+0CA2F4 (base 16) Chameleon Technology (UK) Limited
+ Gardner House, Hornbeam Park Avenue
+ Hornbeam Park Harrogate HG2 8NA
+ GB
+
+00-14-1D (hex) LTI-Motion GmbH
+00141D (base 16) LTI-Motion GmbH
+ Gewerbestrasse 5-9
+ Lahnau Hessen 35633
+ DE
+
+F4-D1-08 (hex) Intel Corporate
+F4D108 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+00-B1-E3 (hex) Cisco Systems, Inc
+00B1E3 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+24-84-98 (hex) Beijing Jiaoda Microunion Tech.Co.,Ltd.
+248498 (base 16) Beijing Jiaoda Microunion Tech.Co.,Ltd.
+ 4-5/F,89 Building, First Section No.44 Gaoliangqiao Xie Street, Haidian District, Beijing, China
+ Beijing Beijing 100044
+ CN
+
+0C-1C-19 (hex) LONGCONN ELECTRONICS(SHENZHEN) CO.,LTD
+0C1C19 (base 16) LONGCONN ELECTRONICS(SHENZHEN) CO.,LTD
+ Building B, No. 42 Xingye Road, Phoenix First Industrial Zone, Fuyong, Baoan District
+ Shenzhen Guangdong 518103
+ CN
+
+20-1F-31 (hex) Inteno Broadband Technology AB
+201F31 (base 16) Inteno Broadband Technology AB
+ Stensätravägen 13
+ Skärholmen SE 127 39
+ SE
+
+00-7C-2D (hex) Samsung Electronics Co.,Ltd
+007C2D (base 16) Samsung Electronics Co.,Ltd
+ 129, Samsung-ro, Youngtongl-Gu
+ Suwon Gyeonggi-Do 16677
+ KR
+
+1C-FD-08 (hex) IEEE Registration Authority
+1CFD08 (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+78-35-A0 (hex) Zurn Industries LLC
+7835A0 (base 16) Zurn Industries LLC
+ 1747 Commerce Way
+ Paso Robles CA 93446
+ US
+
+64-EE-B7 (hex) Netcore Technology Inc
+64EEB7 (base 16) Netcore Technology Inc
+ Building 6, Baolong Plant, Able Technology Park, Longgang District,
+ Shenzhen 518116
+ CN
+
+14-11-14 (hex) TECNO MOBILE LIMITED
+141114 (base 16) TECNO MOBILE LIMITED
+ ROOMS 05-15, 13A/F., SOUTH TOWER, WORLD FINANCE CENTRE, HARBOUR CITY, 17 CANTON ROAD, TSIM SHA TSUI, KOWLOON, HONG KONG
+ Hong Kong Hong Kong 999077
+ HK
+
+A8-90-42 (hex) Beijing Wanwei Intelligent Technology Co., Ltd.
+A89042 (base 16) Beijing Wanwei Intelligent Technology Co., Ltd.
+ Room 616, Section 1, Cuicing, No. 1 Shanyuan Street, Haidian District
+ Beijing Beijing 100000
+ CN
+
+2C-A0-2F (hex) Veroguard Systems Pty Ltd
+2CA02F (base 16) Veroguard Systems Pty Ltd
+ PO Box 5003
+ Clayton VIC 3168
+ AU
+
+E4-3C-80 (hex) University of Oklahoma
+E43C80 (base 16) University of Oklahoma
+ Advanced Radar Research Center
+ Norman OK 73019
+ US
+
+B0-33-A6 (hex) Juniper Networks
+B033A6 (base 16) Juniper Networks
+ 1133 Innovation Way
+ Sunnyvale CA 94089
+ US
+
+98-ED-5C (hex) Tesla Motors, Inc
+98ED5C (base 16) Tesla Motors, Inc
+ 3500 Deer Creek Road
+ Palo Alto CA 94304
+ US
+
+38-81-D7 (hex) Texas Instruments
+3881D7 (base 16) Texas Instruments
+ 12500 TI Blvd
+ Dallas TX 75243
+ US
+
+18-04-ED (hex) Texas Instruments
+1804ED (base 16) Texas Instruments
+ 12500 TI Blvd
+ Dallas TX 75243
+ US
+
+10-81-B4 (hex) Hunan Greatwall Galaxy Science and Technology Co.,Ltd.
+1081B4 (base 16) Hunan Greatwall Galaxy Science and Technology Co.,Ltd.
+ No. 39, Jian Shan Road
+ Changsha Hunan 410205
+ CN
+
+F4-7D-EF (hex) Samsung Electronics Co.,Ltd
+F47DEF (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+7C-8B-B5 (hex) Samsung Electronics Co.,Ltd
+7C8BB5 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+DC-F7-56 (hex) Samsung Electronics Co.,Ltd
+DCF756 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+74-5F-90 (hex) LAM Technologies
+745F90 (base 16) LAM Technologies
+ Viale Ludovico Ariosto, 492/D
+ Sesto Fiorentino FIRENZE 50019
+ IT
+
+64-F8-1C (hex) Huawei Technologies Co., Ltd.
+64F81C (base 16) Huawei Technologies Co., Ltd.
+ Bantian, Longgang District, Shenzhen, 518129, P.R.C
+ Shenzhen GUANGDONG Province 518000
+ CN
+
+68-DF-DD (hex) Xiaomi Communications Co Ltd
+68DFDD (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+64-B4-73 (hex) Xiaomi Communications Co Ltd
+64B473 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+74-51-BA (hex) Xiaomi Communications Co Ltd
+7451BA (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+34-80-B3 (hex) Xiaomi Communications Co Ltd
+3480B3 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+20-82-C0 (hex) Xiaomi Communications Co Ltd
+2082C0 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+FC-64-BA (hex) Xiaomi Communications Co Ltd
+FC64BA (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+C4-6A-B7 (hex) Xiaomi Communications Co Ltd
+C46AB7 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+00-EC-0A (hex) Xiaomi Communications Co Ltd
+00EC0A (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+38-E6-0A (hex) Xiaomi Communications Co Ltd
+38E60A (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+04-E5-98 (hex) Xiaomi Communications Co Ltd
+04E598 (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+D8-A7-56 (hex) Sagemcom Broadband SAS
+D8A756 (base 16) Sagemcom Broadband SAS
+ 250, route de l'Empereur
+ Rueil Malmaison Cedex hauts de seine 92848
+ FR
+
+24-53-BF (hex) Enernet
+2453BF (base 16) Enernet
+ 1007 B-dong, Hyundai Knowledge Industry Center, 70 Dusan-ro, Geumcheon-gu, Seoul, KOREA
+ SEOUL 08584
+ KR
+
+AC-15-85 (hex) silergy corp
+AC1585 (base 16) silergy corp
+ Oleander Way,802 West Bay Road,P.O. BOX 32052
+ Grand Cayman KYI-1208,Cayman Islands 32052
+ US
+
+3C-8D-20 (hex) Google, Inc.
+3C8D20 (base 16) Google, Inc.
1600 Amphitheatre Parkway
Mountain View CA 94043
US
-BC-AF-91 (hex) TE Connectivity Sensor Solutions
-BCAF91 (base 16) TE Connectivity Sensor Solutions
- 4 rue Gaye-Marie, CS 83163
- Toulouse 31027
+C8-2E-47 (hex) Suzhou SmartChip Semiconductor Co., LTD
+C82E47 (base 16) Suzhou SmartChip Semiconductor Co., LTD
+ 9A,Science Plaza,1355 JinJiHu Avenue, Suzhou Industrial Park,
+ Suzhou Jiangsu 215021
+ CN
+
+FC-77-74 (hex) Intel Corporate
+FC7774 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+B4-0B-78 (hex) Brusa Elektronik AG
+B40B78 (base 16) Brusa Elektronik AG
+ Neudorf 14
+ Sennwald St. Gallen 9466
+ CH
+
+2C-AA-8E (hex) Wyze Labs Inc
+2CAA8E (base 16) Wyze Labs Inc
+ 4030 Lake Washington Boulevard NE
+ Kirkland WA 98033
+ US
+
+24-B2-DE (hex) Espressif Inc.
+24B2DE (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+68-C6-3A (hex) Espressif Inc.
+68C63A (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+CC-50-E3 (hex) Espressif Inc.
+CC50E3 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+C4-4F-33 (hex) Espressif Inc.
+C44F33 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+AC-D0-74 (hex) Espressif Inc.
+ACD074 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+30-AE-A4 (hex) Espressif Inc.
+30AEA4 (base 16) Espressif Inc.
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area
+ Shanghai Shanghai 201203
+ CN
+
+C0-1B-23 (hex) SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+C01B23 (base 16) SICHUAN TIANYI COMHEART TELECOM CO.,LTD
+ NO.198 FIRST SECTION,SNOW MOUNTAIN AVENUE, JINYUAN TOWN, DAYI COUNTY,
+ CHENGDU SICHUAN 611330
+ CN
+
+2C-A9-F0 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+2CA9F0 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+58-6B-14 (hex) Apple, Inc.
+586B14 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+94-B0-1F (hex) Apple, Inc.
+94B01F (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+94-F6-D6 (hex) Apple, Inc.
+94F6D6 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+40-BC-60 (hex) Apple, Inc.
+40BC60 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+E8-2C-6D (hex) SmartRG, Inc.
+E82C6D (base 16) SmartRG, Inc.
+ 501 SE Columbia Shores Blvd
+ Vancouver WA 98661
+ US
+
+58-9E-C6 (hex) Gigaset Communications GmbH
+589EC6 (base 16) Gigaset Communications GmbH
+ Frankenstrasse 2
+ Bocholt NRW 46395
+ DE
+
+68-29-DC (hex) Ficosa Electronics S.L.U.
+6829DC (base 16) Ficosa Electronics S.L.U.
+ Pol.Ind Can Mitjans s/n Viladecavalls Barcelona
+ Viladecavalls Barcelona ES08232
+ ES
+
+30-EB-5A (hex) LANDIS + GYR
+30EB5A (base 16) LANDIS + GYR
+ 78th km Old National Road Athens-Corinth
+ Corinth 20100
+ GR
+
+7C-89-C1 (hex) Palo Alto Networks
+7C89C1 (base 16) Palo Alto Networks
+ 3000 Tannery Way
+ Santa Clara CA 95054
+ US
+
+94-8F-CF (hex) ARRIS Group, Inc.
+948FCF (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+74-8A-0D (hex) ARRIS Group, Inc.
+748A0D (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+5C-CB-CA (hex) FUJIAN STAR-NET COMMUNICATION CO.,LTD
+5CCBCA (base 16) FUJIAN STAR-NET COMMUNICATION CO.,LTD
+ 19-22# Building, Star-net Science Plaza, Juyuanzhou,
+ FUZHOU FUJIAN 350002
+ CN
+
+28-E9-8E (hex) Mitsubishi Electric Corporation
+28E98E (base 16) Mitsubishi Electric Corporation
+ 2-7-3 Marunouchi Chiyoda-ku
+ Tokyo 100-8310
+ JP
+
+3C-28-6D (hex) Google, Inc.
+3C286D (base 16) Google, Inc.
+ 1600 Amphitheatre Parkway
+ Mountain View CA 94043
+ US
+
+F8-2F-08 (hex) Molex CMS
+F82F08 (base 16) Molex CMS
+ 2222 Wellington Court
+ Lisle IL 60532
+ US
+
+A4-94-26 (hex) Elgama-Elektronika Ltd.
+A49426 (base 16) Elgama-Elektronika Ltd.
+ Visoriu str. 2
+ Vilnius LT-08300
+ LT
+
+30-24-78 (hex) Sagemcom Broadband SAS
+302478 (base 16) Sagemcom Broadband SAS
+ 250, route de l'Empereur
+ Rueil Malmaison Cedex hauts de seine 92848
FR
-EC-C4-0D (hex) Nintendo Co.,Ltd
-ECC40D (base 16) Nintendo Co.,Ltd
- 11-1 HOKOTATE-CHO KAMITOBA,MINAMI-KU
- KYOTO KYOTO 601-8501
+20-32-33 (hex) SHENZHEN BILIAN ELECTRONIC CO.,LTD
+203233 (base 16) SHENZHEN BILIAN ELECTRONIC CO.,LTD
+ NO.268, Fuqian Rd, Jutang community, Guanlan Town, Longhua New district
+ shenzhen guangdong 518000
+ CN
+
+B4-47-F5 (hex) Earda Technologies co Ltd
+B447F5 (base 16) Earda Technologies co Ltd
+ Block A,Lianfeng Creative Park, #2 Jisheng Rd., Nansha District
+ Guangzhou Guangdong 511455
+ CN
+
+38-0B-3C (hex) Texas Instruments
+380B3C (base 16) Texas Instruments
+ 12500 TI Blvd
+ Dallas TX 75243
+ US
+
+5C-41-5A (hex) Amazon.com, LLC
+5C415A (base 16) Amazon.com, LLC
+ 1200 12th Ave. South, Suite 1200
+ Seattle 98144
+ US
+
+BC-3E-07 (hex) Hitron Technologies. Inc
+BC3E07 (base 16) Hitron Technologies. Inc
+ No. 1-8, Lising 1st Rd. Hsinchu Science Park, Hsinchu, 300, Taiwan, R.O.C
+ Hsin-chu Taiwan 300
+ TW
+
+44-FE-3B (hex) Arcadyan Corporation
+44FE3B (base 16) Arcadyan Corporation
+ No.8, Sec.2, Guangfu Rd.
+ Hsinchu City Hsinchu 30071
+ TW
+
+D8-3A-F5 (hex) Wideband Labs LLC
+D83AF5 (base 16) Wideband Labs LLC
+ 1027 S Main Street, STE 330
+ Joplin MO 64801
+ US
+
+F4-6E-95 (hex) Extreme Networks, Inc.
+F46E95 (base 16) Extreme Networks, Inc.
+ 6480 Via Del Oro
+ San Jose 95119
+ US
+
+00-0F-B3 (hex) Actiontec Electronics, Inc
+000FB3 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+00-15-05 (hex) Actiontec Electronics, Inc
+001505 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+00-18-01 (hex) Actiontec Electronics, Inc
+001801 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+28-80-88 (hex) NETGEAR
+288088 (base 16) NETGEAR
+ 350 East Plumeria Drive
+ San Jose CA 95134
+ US
+
+00-7F-28 (hex) Actiontec Electronics, Inc
+007F28 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+40-8B-07 (hex) Actiontec Electronics, Inc
+408B07 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+4C-8B-30 (hex) Actiontec Electronics, Inc
+4C8B30 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+A0-A3-E2 (hex) Actiontec Electronics, Inc
+A0A3E2 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+0C-61-27 (hex) Actiontec Electronics, Inc
+0C6127 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+00-26-62 (hex) Actiontec Electronics, Inc
+002662 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+10-78-5B (hex) Actiontec Electronics, Inc
+10785B (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+9C-1E-95 (hex) Actiontec Electronics, Inc
+9C1E95 (base 16) Actiontec Electronics, Inc
+ 301 Olcott St
+ Santa Clara CA 95054
+ US
+
+9C-69-B4 (hex) IEEE Registration Authority
+9C69B4 (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+E8-5B-B7 (hex) Ample Systems Inc.
+E85BB7 (base 16) Ample Systems Inc.
+ 11F-2, No. 95 Minquan Rd.
+ New Taipei City 231
+ TW
+
+C8-08-73 (hex) Ruckus Wireless
+C80873 (base 16) Ruckus Wireless
+ 350 West Java Drive
+ Sunnyvale CA 94089
+ US
+
+08-00-30 (hex) CERN
+080030 (base 16) CERN
+ CH-1211
+ GENEVE SUISSE/SWITZ 023
+ CH
+
+4C-DD-7D (hex) LHP Telematics LLC
+4CDD7D (base 16) LHP Telematics LLC
+ 17406 Tiller Ct. STE 100
+ westfield IN 46074
+ US
+
+00-24-33 (hex) ALPS ELECTRIC CO., LTD.
+002433 (base 16) ALPS ELECTRIC CO., LTD.
+ 1-2-1, Okinouchi,
+ Soma-city, Fukushima-pref., 976-8501
+ JP
+
+00-26-43 (hex) ALPS ELECTRIC CO., LTD.
+002643 (base 16) ALPS ELECTRIC CO., LTD.
+ 1-2-1, Okinouchi,
+ Soma-city, Fukushima-pref., 976-8501
+ JP
+
+9C-8D-7C (hex) ALPS ELECTRIC CO., LTD.
+9C8D7C (base 16) ALPS ELECTRIC CO., LTD.
+ 6-1
+ Kakuda Miyagi-Pref 981-1595
+ JP
+
+70-1B-FB (hex) Integrated Device Technology (Malaysia) Sdn. Bhd.
+701BFB (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd.
+ Phase 3, Bayan Lepas FIZ
+ Bayan Lepas Penang 11900
+ MY
+
+00-06-F5 (hex) ALPS ELECTRIC CO., LTD.
+0006F5 (base 16) ALPS ELECTRIC CO., LTD.
+ 6-3-36 Furukawanakazato,
+ Osaki Miyagi-pref 989-6181
+ JP
+
+00-06-F7 (hex) ALPS ELECTRIC CO., LTD.
+0006F7 (base 16) ALPS ELECTRIC CO., LTD.
+ 6-3-36 Furukawanakazato,
+ Osaki Miyagi-pref 989-6181
JP
+
+00-07-04 (hex) ALPS ELECTRIC CO., LTD.
+000704 (base 16) ALPS ELECTRIC CO., LTD.
+ 6-3-36 Furukawanakazato,
+ Osaki Miyagi-pref 989-6181
+ JP
+
+5C-9A-D8 (hex) FUJITSU LIMITED
+5C9AD8 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+B0-AC-FA (hex) FUJITSU LIMITED
+B0ACFA (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+00-17-42 (hex) FUJITSU LIMITED
+001742 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+38-AF-D7 (hex) FUJITSU LIMITED
+38AFD7 (base 16) FUJITSU LIMITED
+ 403, Kosugi-cho 1-chome, Nakahara-ku
+ Kawasaki Kanagawa 211-0063
+ JP
+
+84-A0-6E (hex) Sagemcom Broadband SAS
+84A06E (base 16) Sagemcom Broadband SAS
+ 250, route de l'Empereur
+ Rueil Malmaison Cedex hauts de seine 92848
+ FR
+
+A8-3C-CB (hex) ROSSMA
+A83CCB (base 16) ROSSMA
+ Malkova,12-108
+ PERM 614087
+ RU
+
+C8-C2-FA (hex) HUAWEI TECHNOLOGIES CO.,LTD
+C8C2FA (base 16) HUAWEI TECHNOLOGIES CO.,LTD
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park
+ Dongguan 523808
+ CN
+
+38-C2-BA (hex) CCTV NEOTECH
+38C2BA (base 16) CCTV NEOTECH
+ 68, Digital-ro 9-gil, Geumcheon-gu
+ Seoul ks013
+ KR
+
+CC-D3-9D (hex) IEEE Registration Authority
+CCD39D (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+D4-3B-04 (hex) Intel Corporate
+D43B04 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+34-DA-B7 (hex) zte corporation
+34DAB7 (base 16) zte corporation
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China
+ shenzhen guangdong 518057
+ CN
+
+A8-6D-AA (hex) Intel Corporate
+A86DAA (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+80-84-A9 (hex) oshkosh Corporation
+8084A9 (base 16) oshkosh Corporation
+ 2307 Oregon Street
+ Oshkosh WI 54902
+ US
+
+14-9F-B6 (hex) GUANGDONG GENIUS TECHNOLOGY CO., LTD.
+149FB6 (base 16) GUANGDONG GENIUS TECHNOLOGY CO., LTD.
+ #126,BBK Road,Wusha,Chang'An
+ Dong Guan Guang Dong 523860
+ CN
+
+C4-F8-39 (hex) Actia Automotive
+C4F839 (base 16) Actia Automotive
+ 5 rue Jorge Semprun
+ TOULOUSE 31400
+ FR
+
+00-45-01 (hex) Midmark RTLS
+004501 (base 16) Midmark RTLS
+ 2600 Miller Creek Road
+ Traverse City MI 49684
+ US
+
+F0-B3-1E (hex) Universal Electronics, Inc.
+F0B31E (base 16) Universal Electronics, Inc.
+ 201 E. Sandpointe Ave
+ Santa Ana CA 92707
+ US
+
+00-D0-D9 (hex) DEDICATED MICROCOMPUTERS
+00D0D9 (base 16) DEDICATED MICROCOMPUTERS
+ 1 Thellow Heath Park
+ Northwich CW9 6JB
+ GB
+
+48-C3-B0 (hex) Pharos Co.Ltd
+48C3B0 (base 16) Pharos Co.Ltd
+ 503 Ogong-ro 144 Deokjin-gu
+ Jeonju-si 54873
+ KR
+
+F8-91-73 (hex) AEDLE SAS
+F89173 (base 16) AEDLE SAS
+ 11 Rue Campagne Première, Cour Intérieure, Entresol, CODE 13B80
+ Paris IDF 75014
+ FR
+
+D0-58-C0 (hex) Qingdao Haier Multimedia Limited.
+D058C0 (base 16) Qingdao Haier Multimedia Limited.
+ Pingban Building, Haier Industry Park, Laoshan District,
+ Qingdao Shandong 266103
+ CN
+
+E0-37-17 (hex) Technicolor CH USA Inc.
+E03717 (base 16) Technicolor CH USA Inc.
+ 5030 Sugarloaf Parkway Bldg 6
+ Lawrenceville GA 30044
+ US
+
+54-A7-03 (hex) TP-LINK TECHNOLOGIES CO.,LTD.
+54A703 (base 16) TP-LINK TECHNOLOGIES CO.,LTD.
+ Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan
+ Shenzhen Guangdong 518057
+ CN
+
+24-DE-C6 (hex) Aruba, a Hewlett Packard Enterprise Company
+24DEC6 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+40-E3-D6 (hex) Aruba, a Hewlett Packard Enterprise Company
+40E3D6 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+D8-C7-C8 (hex) Aruba, a Hewlett Packard Enterprise Company
+D8C7C8 (base 16) Aruba, a Hewlett Packard Enterprise Company
+ 3333 Scott Blvd
+ Santa Clara CA 95054
+ US
+
+58-10-8C (hex) Intelbras
+58108C (base 16) Intelbras
+ BR 101, km 210, S/N°
+ São José Santa Catarina 88104800
+ BR
+
+18-0D-2C (hex) Intelbras
+180D2C (base 16) Intelbras
+ BR 101, km 210, S/N°
+ São José Santa Catarina 88104800
+ BR
+
+A0-42-46 (hex) IT Telecom Co., Ltd.
+A04246 (base 16) IT Telecom Co., Ltd.
+ 517 TheOvalley 555-9 Hogye-dong, Dong An-gu
+ Anyang-si Gyeonggi-do 14117
+ KR
+
+7C-FD-82 (hex) GUANGDONG GENIUS TECHNOLOGY CO., LTD.
+7CFD82 (base 16) GUANGDONG GENIUS TECHNOLOGY CO., LTD.
+ No.168, Middle Road Of East Gate
+ Xiaobian Community Chang'an Town 523851
+ CN
+
+84-85-E6 (hex) Guangdong Asano Technology CO.,Ltd.
+8485E6 (base 16) Guangdong Asano Technology CO.,Ltd.
+ Changsheng Road, Songxia Industrial Park, Songgang, Shishan Town, Nanhai
+ Foshan Guangdong, China. 528200
+ CN
+
+F8-DA-E2 (hex) NDC Technologies
+F8DAE2 (base 16) NDC Technologies
+ 8001 Technology Blvd
+ Dayton OH 45424
+ US
+
+00-1E-B8 (hex) Aloys, Inc
+001EB8 (base 16) Aloys, Inc
+ #3F HANWHA TECHWIN R&D BLDG. 4-5, YANGHYEON-RO 405 BEON-GIL,
+ JUNGWON-GU, SEONGNAM-SI GYEONGGI-DO 13438
+ KR
+
+34-20-03 (hex) Shenzhen Feitengyun Technology Co.,LTD
+342003 (base 16) Shenzhen Feitengyun Technology Co.,LTD
+ 7F 4building,Yalianhaoshida industrial Park
+ Shenzhen Guangdong 518100
+ CN
+
+20-C0-47 (hex) Verizon
+20C047 (base 16) Verizon
+ One Verizon Way
+ Basking Ridge 07030
+ US
+
+18-78-D4 (hex) Verizon
+1878D4 (base 16) Verizon
+ One Verizon Way
+ Basking Ridge NJ 07920
+ US
+
+50-64-2B (hex) XIAOMI Electronics,CO.,LTD
+50642B (base 16) XIAOMI Electronics,CO.,LTD
+ Xiaomi Building, No.68 Qinghe Middle Street
+ Haidian District Beijing 100085
+ CN
+
+78-11-DC (hex) XIAOMI Electronics,CO.,LTD
+7811DC (base 16) XIAOMI Electronics,CO.,LTD
+ Xiaomi Building, No.68 Qinghe Middle Street
+ Haidian District Beijing 100085
+ CN
+
+34-CE-00 (hex) XIAOMI Electronics,CO.,LTD
+34CE00 (base 16) XIAOMI Electronics,CO.,LTD
+ Xiaomi Building, No.68 Qinghe Middle Street
+ Haidian District Beijing 100085
+ CN
+
+34-AA-99 (hex) Nokia
+34AA99 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+C4-08-4A (hex) Nokia
+C4084A (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+0C-54-B9 (hex) Nokia
+0C54B9 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+30-FE-31 (hex) Nokia
+30FE31 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+24-21-24 (hex) Nokia
+242124 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+8C-90-D3 (hex) Nokia
+8C90D3 (base 16) Nokia
+ 600 March Road
+ Kanata Ontario K2K 2E6
+ CA
+
+D0-AB-D5 (hex) Intel Corporate
+D0ABD5 (base 16) Intel Corporate
+ Lot 8, Jalan Hi-Tech 2/3
+ Kulim Kedah 09000
+ MY
+
+C4-F0-EC (hex) Fiberhome Telecommunication Technologies Co.,LTD
+C4F0EC (base 16) Fiberhome Telecommunication Technologies Co.,LTD
+ No.5 DongXin Road
+ Wuhan Hubei 430074
+ CN
+
+00-CB-51 (hex) Sagemcom Broadband SAS
+00CB51 (base 16) Sagemcom Broadband SAS
+ 250, route de l'Empereur
+ Rueil Malmaison Cedex hauts de seine 92848
+ FR
+
+04-D7-A5 (hex) New H3C Technologies Co., Ltd
+04D7A5 (base 16) New H3C Technologies Co., Ltd
+ 466 Changhe Road, Binjiang District
+ Hangzhou Zhejiang 310052
+ CN
+
+90-89-5F (hex) WEIFANG GOERTEK ELECTRONICS CO.,LTD
+90895F (base 16) WEIFANG GOERTEK ELECTRONICS CO.,LTD
+ Gaoxin 2 Road, Free Trade Zone,Weifang,Shandong,261205,P.R.China
+ Weifang Shandong 261205
+ CN
+
+54-47-D3 (hex) TSAT AS
+5447D3 (base 16) TSAT AS
+ Martin Linges v 25
+ Fornebu 1364
+ NO
+
+E0-5A-9F (hex) IEEE Registration Authority
+E05A9F (base 16) IEEE Registration Authority
+ 445 Hoes Lane
+ Piscataway NJ 08554
+ US
+
+0C-E0-41 (hex) iDruide
+0CE041 (base 16) iDruide
+ 19 Rue de la Turbie
+ MONACO 98000
+ MC
+
+98-49-E1 (hex) Boeing Defence Australia
+9849E1 (base 16) Boeing Defence Australia
+ GPO Box 767
+ Brisbane Queensland 4001
+ AU
+
+BC-CF-4F (hex) Zyxel Communications Corporation
+BCCF4F (base 16) Zyxel Communications Corporation
+ No. 6 Innovation Road II, Science Park
+ Hsichu Taiwan 300
+ TW
+
+00-2F-5C (hex) Cisco Systems, Inc
+002F5C (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+F4-6F-4E (hex) Echowell
+F46F4E (base 16) Echowell
+ 7F-8, No. 8, Sec 1, JunShing Rd.
+ New Taipei City 24872
+ TW
+
+2C-73-A0 (hex) Cisco Systems, Inc
+2C73A0 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+2C-01-B5 (hex) Cisco Systems, Inc
+2C01B5 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+28-B4-FB (hex) Sprocomm Technologies CO.,LTD.
+28B4FB (base 16) Sprocomm Technologies CO.,LTD.
+ 5D F1.6 Block,Tianfa Building,Tianan Chegongmiao Industrial park,Futian Dist
+ shenzhen guangdong 518000
+ CN
+
+E4-5D-37 (hex) Juniper Networks
+E45D37 (base 16) Juniper Networks
+ 1133 Innovation Way
+ Sunnyvale CA 94089
+ US
+
+A8-6D-5F (hex) Raisecom Technology CO., LTD
+A86D5F (base 16) Raisecom Technology CO., LTD
+ No. 11, East Area, No. 10 Block, East Xibeiwang Road
+ Beijing 100094
+ CN
+
+CC-2C-83 (hex) DarkMatter L.L.C
+CC2C83 (base 16) DarkMatter L.L.C
+ Level 15, Aldar HQ
+ Abu Dhabi 27655
+ AE
+
+00-50-79 (hex) Private
+005079 (base 16) Private
+
+D0-EC-35 (hex) Cisco Systems, Inc
+D0EC35 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+50-3E-7C (hex) LeiShen Intelligent System Co.Ltd
+503E7C (base 16) LeiShen Intelligent System Co.Ltd
+ 4th Floor,No.1 Commercial Building,Cultural Center,Tanggang Road,Tanggang Community,Shajing Street,Baoan District
+ Shenzhen Guangdong 518104
+ CN
+
+20-0D-B0 (hex) Shenzhen Four Seas Global Link Network Technology Co., Ltd.
+200DB0 (base 16) Shenzhen Four Seas Global Link Network Technology Co., Ltd.
+ Room 607-610, Block B, TAOJINDI Electronic Business Incubation Base
+ Tenglong Road, Longhua District, Shenzhen Guangdong 518000
+ CN
+
+44-B9-94 (hex) Douglas Lighting Controls
+44B994 (base 16) Douglas Lighting Controls
+ 280 - 3605 Gilmore Way
+ Burnaby BC V5G4X5
+ CA
+
+D8-55-75 (hex) Samsung Electronics Co.,Ltd
+D85575 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+D4-11-A3 (hex) Samsung Electronics Co.,Ltd
+D411A3 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+64-89-F1 (hex) Samsung Electronics Co.,Ltd
+6489F1 (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+04-BA-8D (hex) Samsung Electronics Co.,Ltd
+04BA8D (base 16) Samsung Electronics Co.,Ltd
+ #94-1, Imsoo-Dong
+ Gumi Gyeongbuk 730-350
+ KR
+
+48-95-07 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+489507 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+ NO.18 HAIBIN ROAD,
+ DONG GUAN GUANG DONG 523860
+ CN
+
+A8-9C-ED (hex) Xiaomi Communications Co Ltd
+A89CED (base 16) Xiaomi Communications Co Ltd
+ The Rainbow City of China Resources
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085
+ CN
+
+6C-A6-04 (hex) ARRIS Group, Inc.
+6CA604 (base 16) ARRIS Group, Inc.
+ 6450 Sequence Drive
+ San Diego CA 92121
+ US
+
+C0-41-21 (hex) Nokia
+C04121 (base 16) Nokia
+ Karaportti 3
+ Espoo Finland 02610
+ FI
+
+A0-BD-1D (hex) Zhejiang Dahua Technology Co., Ltd.
+A0BD1D (base 16) Zhejiang Dahua Technology Co., Ltd.
+ No.1199,Waterfront Road
+ Hangzhou Zhejiang 310053
+ CN
+
+94-F7-AD (hex) Juniper Networks
+94F7AD (base 16) Juniper Networks
+ 1133 Innovation Way
+ Sunnyvale CA 94089
+ US
+
+88-CC-45 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+88CC45 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+C8-8F-26 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+C88F26 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+70-85-40 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+708540 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+0C-11-05 (hex) AKUVOX (XIAMEN) NETWORKS CO., LTD
+0C1105 (base 16) AKUVOX (XIAMEN) NETWORKS CO., LTD
+ Suite 201-15, 31 WangHai Rd
+ Xiamen Fujian 361008
+ CN
+
+5C-C6-D0 (hex) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+5CC6D0 (base 16) Skyworth Digital Technology(Shenzhen) Co.,Ltd
+ 7F,Block A,Skyworth Building,
+ Shenzhen Guangdong 518057
+ CN
+
+7C-61-66 (hex) Amazon Technologies Inc.
+7C6166 (base 16) Amazon Technologies Inc.
+ P.O Box 8102
+ Reno NV 89507
+ US
+
+98-FA-9B (hex) LCFC(HeFei) Electronics Technology co., ltd
+98FA9B (base 16) LCFC(HeFei) Electronics Technology co., ltd
+ YunGu Road 3188-1
+ Hefei Anhui 230000
+ CN
+
+00-FD-22 (hex) Cisco Systems, Inc
+00FD22 (base 16) Cisco Systems, Inc
+ 80 West Tasman Drive
+ San Jose CA 94568
+ US
+
+44-B2-95 (hex) Sichuan AI-Link Technology Co., Ltd.
+44B295 (base 16) Sichuan AI-Link Technology Co., Ltd.
+ Anzhou,Industrial Park
+ Anzhou,Industrial Park Sichuan 621000
+ CN
+
+94-24-E1 (hex) Alcatel-Lucent Enterprise
+9424E1 (base 16) Alcatel-Lucent Enterprise
+ 26801 West Agoura Rd
+ Calabasas CA 91301
+ US
+
+C4-2A-D0 (hex) Apple, Inc.
+C42AD0 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+E0-89-7E (hex) Apple, Inc.
+E0897E (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+10-30-25 (hex) Apple, Inc.
+103025 (base 16) Apple, Inc.
+ 1 Infinite Loop
+ Cupertino CA 95014
+ US
+
+C0-10-B1 (hex) HMD Global Oy
+C010B1 (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+A0-28-ED (hex) HMD Global Oy
+A028ED (base 16) HMD Global Oy
+ Bertel Jungin aukio 9
+ Espoo 02600
+ FI
+
+6C-72-20 (hex) D-Link International
+6C7220 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+90-8D-78 (hex) D-Link International
+908D78 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+78-54-2E (hex) D-Link International
+78542E (base 16) D-Link International
+ 1 Internal Business Park, #03-12.
+ SINGAPORE Singapore 609917
+ TW
+
+18-0F-76 (hex) D-Link International
+180F76 (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+74-DA-DA (hex) D-Link International
+74DADA (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+48-EE-0C (hex) D-Link International
+48EE0C (base 16) D-Link International
+ 1 Internal Business Park, #03-12,The Synergy, Singapore
+ Singapore Singapore 609917
+ SG
+
+B0-E7-1D (hex) Shanghai Maigantech Co.,Ltd
+B0E71D (base 16) Shanghai Maigantech Co.,Ltd
+ Room 2211,No.88 Caoxi North Rd,Xuhui District
+ Shanghai Shanghai 200030
+ CN
diff --git a/hwdb/ma-medium.txt b/hwdb/ma-medium.txt
index 858409fe13..4e78bb65b7 100644
--- a/hwdb/ma-medium.txt
+++ b/hwdb/ma-medium.txt
@@ -1679,12 +1679,6 @@ E00000-EFFFFF (base 16) CL International
Seongnam Kyeonggi-do 462-806
KR
-28-F5-37 (hex) 1MORE, INC.
-800000-8FFFFF (base 16) 1MORE, INC.
- Tianliao Building F14 East Block (New Materials Industrial Park), Xueyuan Road, Nanshan District
- Shenzhen Guangdong 518000
- CN
-
28-F5-37 (hex) Herbert Waldmann GmbH & Co. KG
900000-9FFFFF (base 16) Herbert Waldmann GmbH & Co. KG
Peter-Henlein-Straße 5
@@ -1961,12 +1955,6 @@ C00000-CFFFFF (base 16) Epoch International Enterprises, Inc.
Los Alamitos CA 90720
US
-3C-39-E7 (hex) Private
-F00000-FFFFFF (base 16) Private
-
-F8-02-78 (hex) Private
-F00000-FFFFFF (base 16) Private
-
28-2C-02 (hex) SAKATA DENKI Co., Ltd.
000000-0FFFFF (base 16) SAKATA DENKI Co., Ltd.
Yagisawa2-17-20
@@ -1997,9 +1985,6 @@ B00000-BFFFFF (base 16) Klashwerks Inc.
Lüdenscheid 58511
DE
-98-02-D8 (hex) Private
-F00000-FFFFFF (base 16) Private
-
C4-FF-BC (hex) KyongBo Electric Co., Ltd.
C00000-CFFFFF (base 16) KyongBo Electric Co., Ltd.
5, Seongsuil-ro 12-gagil Seongdong-gu
@@ -2036,12 +2021,6 @@ F0-41-C8 (hex) LINPA ACOUSTIC TECHNOLOGY CO.,LTD
Dongguan Guandong 523648
CN
-F0-41-C8 (hex) DongGuan Siyoto Electronics Co., Ltd
-100000-1FFFFF (base 16) DongGuan Siyoto Electronics Co., Ltd
- Hecheng Industrial District, QiaoTou Town, DongGuan City,Guangdong,China
- DongGuan 523520
- CN
-
74-19-F8 (hex) Heptagon Systems PTY. LTD.
700000-7FFFFF (base 16) Heptagon Systems PTY. LTD.
625-627 Ringwood Warrandyte Road
@@ -2186,15 +2165,219 @@ C00000-CFFFFF (base 16) Shenzhen Samchung Video Technology Co., Ltd.
Shin-Yokohama, Kohoku-ku, Yokohama, Kanagawa 222-0033
JP
-90-C6-82 (hex) Private
-F00000-FFFFFF (base 16) Private
-
D0-76-50 (hex) Private
F00000-FFFFFF (base 16) Private
1C-CA-E3 (hex) Private
F00000-FFFFFF (base 16) Private
+3C-39-E7 (hex) Private
+F00000-FFFFFF (base 16) Private
+
+0C-FE-5D (hex) Bepal Technology Co.,Ltd.
+C00000-CFFFFF (base 16) Bepal Technology Co.,Ltd.
+ Xihu Qu, Tianmushan Road, Qianjiang Xixi Xinzuo
+ HangZhou ZheJiang 310000
+ CN
+
+98-F9-C7 (hex) SHENZHEN HUNTKEY ELECTRIC CO., LTD.
+000000-0FFFFF (base 16) SHENZHEN HUNTKEY ELECTRIC CO., LTD.
+ Huntkey Industrial Park, Xue-Xiang Village, Bantian, Longgang District,
+ Shenzhen Guangdong 518129
+ CN
+
+98-F9-C7 (hex) ShenZhen Chuangwei Electronic Appliance Co.,Ltd
+C00000-CFFFFF (base 16) ShenZhen Chuangwei Electronic Appliance Co.,Ltd
+ 4F & 6F, Overseas plant south, Skyworth Industrial Park, Shiyan Street, Bao'an District,
+ Shenzhen Guangdong 518101
+ CN
+
+98-F9-C7 (hex) NC-LINK Technology Co., Ltd.
+E00000-EFFFFF (base 16) NC-LINK Technology Co., Ltd.
+ Block A2 Jinhai Business Mansion, Jinhai Road, Xixiang town
+ Shenzhen Guangdong 518101
+ CN
+
+7C-BC-84 (hex) Shanghai Yitu Technology Co. Ltd
+300000-3FFFFF (base 16) Shanghai Yitu Technology Co. Ltd
+ 23/F, Tower 1, No.523 Loushanguan Road, Changning District
+ Shanghai 200051
+ CN
+
+7C-BC-84 (hex) HITIQ LIMITED
+900000-9FFFFF (base 16) HITIQ LIMITED
+ 628 Newcastle St
+ Leederville WA 6007
+ AU
+
+6C-DF-FB (hex) Shenzhen HDCVT Technology
+000000-0FFFFF (base 16) Shenzhen HDCVT Technology
+ Floor 7, Building 5, Lihe Industrial Park, Songbai Road, Xili Street, Nanshan District
+ Shenzhen Guangdong 518055
+ CN
+
+6C-DF-FB (hex) Hashtrend AG
+700000-7FFFFF (base 16) Hashtrend AG
+ Bahnhofstrasse
+ Zug 6300
+ CH
+
+6C-DF-FB (hex) Lineable Inc
+400000-4FFFFF (base 16) Lineable Inc
+ 5, Ttukseom-ro 1na-gil, Seongdong-gu, Seoul, Republic of Korea, Heyground, G505
+ Seoul Seoul 04779
+ KR
+
+6C-DF-FB (hex) Nanjing Buruike Electronics Technology Co., Ltd.
+D00000-DFFFFF (base 16) Nanjing Buruike Electronics Technology Co., Ltd.
+ Jiangning District Moling Street, SuYuan Avenue No.117 Building 2, Floor 5 Room 541
+ Nanjing Jiangsu 210000
+ CN
+
+6C-DF-FB (hex) Beijing Ainemo Co Ltd
+300000-3FFFFF (base 16) Beijing Ainemo Co Ltd
+ Building K1, NO. 86 Beiyuan North road, Chaoyang district, Beijing China
+ Beijing Beijing 100012
+ CN
+
+4C-91-7A (hex) Annapurna labs
+E00000-EFFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+D4-25-CC (hex) Combined Energy Technologies Pty Ltd
+D00000-DFFFFF (base 16) Combined Energy Technologies Pty Ltd
+ 19 The Avenue
+ Newport NSW 2106
+ AU
+
+D4-25-CC (hex) Barobo, Inc.
+400000-4FFFFF (base 16) Barobo, Inc.
+ 221 G Street, #204
+ Davis CA 95616
+ US
+
+D4-25-CC (hex) Veea
+B00000-BFFFFF (base 16) Veea
+ 164 E183rd street
+ New York NY 10028
+ US
+
+CC-D3-9D (hex) MagTarget LLC
+300000-3FFFFF (base 16) MagTarget LLC
+ 1300 Reamwood Ave Sunnyvale CA
+ SUNNYVALE CA 94089
+ US
+
+CC-D3-9D (hex) Evoko Unlimited AB
+100000-1FFFFF (base 16) Evoko Unlimited AB
+ Hästholmsvägen 32
+ Nacka 13130
+ SE
+
+D4-25-CC (hex) Coperion
+E00000-EFFFFF (base 16) Coperion
+ 590 WOODBURY GLASSBORO RD.
+ SEWELL NJ 08080
+ US
+
+CC-D3-9D (hex) Obelisk Inc.
+800000-8FFFFF (base 16) Obelisk Inc.
+ 67 Batterymarch Street, Floor 4
+ Boston 02110
+ US
+
+CC-D3-9D (hex) Krontech
+600000-6FFFFF (base 16) Krontech
+ I.T.U ARI 3 Teknokent Kron Telekomunikasyon, Maslak
+ Istanbul 34467
+ TR
+
+CC-D3-9D (hex) Lubelskie Fabryki Wag FAWAG S.A.
+A00000-AFFFFF (base 16) Lubelskie Fabryki Wag FAWAG S.A.
+ ÅÄ™czyÅ„ska 58
+ Lublin 20-954
+ PL
+
+CC-D3-9D (hex) Continental Control Systems
+200000-2FFFFF (base 16) Continental Control Systems
+ 2150 Miller Drive Suite A
+ Longmont CO 80501
+ US
+
+CC-D3-9D (hex) Q-Branch Labs, Inc.
+B00000-BFFFFF (base 16) Q-Branch Labs, Inc.
+ 427 N. Tatnall St., Suite #82712
+ Wilmington DE 19801
+ US
+
+CC-D3-9D (hex) Shanghai tongli information technology co. LTD
+E00000-EFFFFF (base 16) Shanghai tongli information technology co. LTD
+ Floor 2, building 1, liqin building, No.1885, metropolis road, minhang district
+ Shanghai Shanghai 201108
+ CN
+
+38-B1-9E (hex) Beijing Memblaze Technology Co Ltd
+700000-7FFFFF (base 16) Beijing Memblaze Technology Co Ltd
+ Building B2,Dongsheng Park, 66 Xixiaokou Road, Haidian
+ Beijing Beijing 100192
+ CN
+
+38-B1-9E (hex) AVO DEVELOPMENT LTD
+300000-3FFFFF (base 16) AVO DEVELOPMENT LTD
+ 179 Shepherds Hill
+ Romford Essex RM3 0NR
+ GB
+
+28-F5-37 (hex) 1MORE
+800000-8FFFFF (base 16) 1MORE
+ TianliaoBuilding F14
+ New Materials Industrial Park,Xueyuan Blvd Shenzhen, Nanshan District 518005
+ CN
+
+D8-86-0B (hex) Grünbeck Wasseraufbereitung GmbH
+700000-7FFFFF (base 16) Grünbeck Wasseraufbereitung GmbH
+ Josef-Grünbeck-Str. 1
+ Hoechstaedt a.d. Donau 89420
+ DE
+
+E0-5A-9F (hex) Mountz, Inc.
+D00000-DFFFFF (base 16) Mountz, Inc.
+ 1080 N. 11th Street
+ San Jose CA 95112
+ US
+
+4C-BC-98 (hex) Shenzhen Shanling Digital Technology Development Co.,Ltd.
+800000-8FFFFF (base 16) Shenzhen Shanling Digital Technology Development Co.,Ltd.
+ No.10, Chiwan 1 Road, Shekou, Nanshan
+ Shenzhen GuangDong 518068
+ CN
+
+4C-BC-98 (hex) Airtex Manufacturing Partnership
+900000-9FFFFF (base 16) Airtex Manufacturing Partnership
+ 1441 Hastings Cres. SE
+ Calgary Alberta T2G 4C8
+ CA
+
+4C-BC-98 (hex) Voegtlin Instruments GmbH
+700000-7FFFFF (base 16) Voegtlin Instruments GmbH
+ Langenhagstrasse 1
+ Aesch CH-4147
+ CH
+
+4C-BC-98 (hex) Gronic Systems GmbH
+500000-5FFFFF (base 16) Gronic Systems GmbH
+ Hilpertswiese 7
+ Birstein 63633
+ DE
+
+38-B1-9E (hex) Doepke Schaltgeräte GmbH
+900000-9FFFFF (base 16) Doepke Schaltgeräte GmbH
+ Stellmacherstr. 11
+ Norden Niedersachsen 26506
+ DE
+
8C-1C-DA (hex) Anntec (Beijing) Technology Co.,Ltd.
400000-4FFFFF (base 16) Anntec (Beijing) Technology Co.,Ltd.
F803, Shangdi Third Street, No.9,HaiDian District
@@ -2384,6 +2567,231 @@ D00000-DFFFFF (base 16) Brand New Brand Nordic AB
Söderbärke 77794
SE
+A4-ED-43 (hex) Wuxi Junction Infomation Technology Incorporated Company
+700000-7FFFFF (base 16) Wuxi Junction Infomation Technology Incorporated Company
+ ROOM919,NO.401 XINGYUANBEILU ROAD, LIANGXI DISTRIC,WUXI CITY,JIANGSU PROVINCE
+ WuXi Jiangsu 214000
+ CN
+
+A4-ED-43 (hex) Paragon Business Solutions Ltd.
+B00000-BFFFFF (base 16) Paragon Business Solutions Ltd.
+ 303-5th Avenue, Suite # 1007
+ New York 10016
+ US
+
+A4-ED-43 (hex) leakSMART
+C00000-CFFFFF (base 16) leakSMART
+ 5920 GREENE POINTE DRIVE S GROVEPORT
+ Groveport OH 43125
+ US
+
+30-0A-60 (hex) Ampetronic Ltd
+A00000-AFFFFF (base 16) Ampetronic Ltd
+ Unit 2 Trentside Business Village, Farndon Road
+ Newark Nottinghamshire NG24 4XB
+ GB
+
+30-0A-60 (hex) Giax GmbH
+B00000-BFFFFF (base 16) Giax GmbH
+ Am Weichselgarten 7
+ Erlangen 91058
+ DE
+
+30-0A-60 (hex) AVIC JONHON OPTRONIC TECHNOLOGY CO., LTD.
+400000-4FFFFF (base 16) AVIC JONHON OPTRONIC TECHNOLOGY CO., LTD.
+ Zhoushan Road10,Jianxi District,Luoyang City
+ Luoyang City Henan Province 471003
+ CN
+
+30-0A-60 (hex) Imageo s.r.o.
+E00000-EFFFFF (base 16) Imageo s.r.o.
+ Golcova 485
+ Praha 14800
+ CZ
+
+30-0A-60 (hex) Realtime biometrics India pvt ltd
+600000-6FFFFF (base 16) Realtime biometrics India pvt ltd
+ C-68,1st Floor ganesh nagar pandav nagar complex
+ delhi Delhi 110092
+ IN
+
+3C-6A-2C (hex) La Barrière Automatique
+600000-6FFFFF (base 16) La Barrière Automatique
+ 451 chemin de Champivost
+ Limonest 69760
+ FR
+
+3C-6A-2C (hex) figur8, Inc.
+300000-3FFFFF (base 16) figur8, Inc.
+ 2 Park Plaza, Suite 605
+ BOSTON MA 02116
+ US
+
+F0-41-C8 (hex) DongGuan Siyoto Electronics Co., Ltd
+100000-1FFFFF (base 16) DongGuan Siyoto Electronics Co., Ltd
+ Hecheng Industrial District, QiaoTou Town
+ DongGuan City Guangdong 523520
+ CN
+
+3C-6A-2C (hex) Phytium Technology Co., Ltd.
+B00000-BFFFFF (base 16) Phytium Technology Co., Ltd.
+ Building 5, XinAn Business Square, Haiyuan Middle Road
+ Binhai New District, Tianjin 300450
+ CN
+
+A8-3F-A1 (hex) Shenzhen BIO I/E Co.,Ltd
+D00000-DFFFFF (base 16) Shenzhen BIO I/E Co.,Ltd
+ Huafan Industrial Park,Building #12, Henggang JianLong Village Industrial Zone, Longgang District
+ Shenzhen GuangDong province 518115
+ CN
+
+A8-3F-A1 (hex) Neos Ventures Limited
+800000-8FFFFF (base 16) Neos Ventures Limited
+ 47 Bermondsey Street
+ London SE1 3XT
+ GB
+
+A8-3F-A1 (hex) GTDevice LLC
+100000-1FFFFF (base 16) GTDevice LLC
+ PO BOX 86339
+ Portland OR 97286
+ US
+
+A8-3F-A1 (hex) Exel s.r.l. unipersonale
+B00000-BFFFFF (base 16) Exel s.r.l. unipersonale
+ via di corticella 201
+ bologna BO 40128
+ IT
+
+A8-3F-A1 (hex) Sercomm Corporation.
+500000-5FFFFF (base 16) Sercomm Corporation.
+ 3F,No.81,Yu-Yih Rd.,Chu-Nan Chen
+ Miao-Lih Hsuan 115
+ TW
+
+1C-FD-08 (hex) Beijing Hengxin Rainbow Information Technology Co.,Ltd
+500000-5FFFFF (base 16) Beijing Hengxin Rainbow Information Technology Co.,Ltd
+ 11Floor,north district,newton business building,No.25 Landian factory south road,Haidian District
+ Beijing Beijing 100097
+ CN
+
+1C-FD-08 (hex) guangzhou huiqun intelligent technology co. LTD
+B00000-BFFFFF (base 16) guangzhou huiqun intelligent technology co. LTD
+ room 502, building 2, no. 6, haijing road, xinzao town, panyu district
+ guangzhou guangdong 511436
+ CN
+
+1C-FD-08 (hex) sunweit industrial limited
+700000-7FFFFF (base 16) sunweit industrial limited
+ Block A ,503B Room,Zhihui Innovation Centre
+ Shenzhen GUANGDONG 518000
+ CN
+
+1C-FD-08 (hex) Umeox Innovations Co.,Ltd
+300000-3FFFFF (base 16) Umeox Innovations Co.,Ltd
+ Room 1208-09, Research Building, Tsinghua Information Port, No. 1, Xindong Road, Nanshan District, Shenzhen
+ Shenzhen Guangdong 518000
+ CN
+
+1C-FD-08 (hex) ShenZhen DeLippo Technology Co., LTD
+800000-8FFFFF (base 16) ShenZhen DeLippo Technology Co., LTD
+ District A of Fifth Floor,Building E,Guancheng Low Carbon Industrial Park, Shangcun Community,Gongming Street,Guangming District
+ Shenzhen 518106
+ CN
+
+6C-5C-3D (hex) SOUNDKING ELECTRONICS&SOUND CO., LTD.
+700000-7FFFFF (base 16) SOUNDKING ELECTRONICS&SOUND CO., LTD.
+ No.818 Chengxin RoadYinzhou Investment Business Park
+ Ningbo Zhejiang 315105
+ CN
+
+1C-FD-08 (hex) Tianjin Keyvia Electric Co.,Ltd
+D00000-DFFFFF (base 16) Tianjin Keyvia Electric Co.,Ltd
+ No.15 HaitaiFazhanErlu Road, Binhai Hi-tech Industrial Huayuan Development Area(Outer Ring)
+ Tianjin Tianjin 300392
+ CN
+
+6C-5C-3D (hex) choyang powertech
+C00000-CFFFFF (base 16) choyang powertech
+ 11. Deoksan-ro 189beon-gil.
+ ilsan seo-gu. Goyang-si Gyeonggi-do 10205
+ KR
+
+6C-5C-3D (hex) IskraUralTEL
+900000-9FFFFF (base 16) IskraUralTEL
+ Komvuzovkaya
+ Ekaterinburg 620137
+ RU
+
+6C-5C-3D (hex) Syowatsusinkougyo Co.,Ltd.
+D00000-DFFFFF (base 16) Syowatsusinkougyo Co.,Ltd.
+ 35 Ozekiazakitada,Azai-cho
+ Ichinomiya-City Aichi 491-0101
+ JP
+
+98-02-D8 (hex) Private
+F00000-FFFFFF (base 16) Private
+
+4C-BC-98 (hex) Shenzhen Cogitation Technology Co.,Ltd.
+A00000-AFFFFF (base 16) Shenzhen Cogitation Technology Co.,Ltd.
+ NO.509 LangShan Building,Nanshan Yungu Innovation Industrial Park,No.1183 Taoyuan Street,Nanshen District
+ Shenzhen Guangdong 518000
+ CN
+
+F8-02-78 (hex) Private
+F00000-FFFFFF (base 16) Private
+
+4C-BC-98 (hex) Wonder Workshop
+E00000-EFFFFF (base 16) Wonder Workshop
+ 1500 Fashion Island, Suite #200
+ San Mateo CA 94404
+ US
+
+E4-4C-C7 (hex) Telo Systems Limitd
+D00000-DFFFFF (base 16) Telo Systems Limitd
+ 4/F, Chuangye Building, Seven-Star Park, Yu'an 2nd Road
+ ShenZhen GuangDong 518000
+ CN
+
+E4-4C-C7 (hex) Channel Enterprises (HK) Ltd.
+700000-7FFFFF (base 16) Channel Enterprises (HK) Ltd.
+ Room 2006, 20/F., 43-47 Wang Lung Street,
+ Tsuen Wan NA
+ HK
+
+74-5B-C5 (hex) SpringCard
+500000-5FFFFF (base 16) SpringCard
+ 2 voie La Cardon, Parc Gutenberg
+ PALAISEAU IDF 91120
+ FR
+
+74-5B-C5 (hex) uGrid Network Inc.
+400000-4FFFFF (base 16) uGrid Network Inc.
+ 602 Gabriola Way
+ Ottawa Ontario K2T 0M2
+ CA
+
+74-5B-C5 (hex) EDOMO Systems GmbH
+800000-8FFFFF (base 16) EDOMO Systems GmbH
+ Obertorplatz 2
+ Landau Deutschland (DEU) 76829
+ DE
+
+90-C6-82 (hex) Private
+F00000-FFFFFF (base 16) Private
+
+FC-D2-B6 (hex) Grandway Technology (Shenzhen) Limited
+500000-5FFFFF (base 16) Grandway Technology (Shenzhen) Limited
+ Block 7, Zhu Keng Industrial Zone
+ Ping Shan District Shenzhen 518118
+ CN
+
+FC-D2-B6 (hex) Soma GmbH
+200000-2FFFFF (base 16) Soma GmbH
+ Gewerbering 9
+ Schalksmühle NRW 58579
+ DE
+
1C-87-76 (hex) Strone Technology
C00000-CFFFFF (base 16) Strone Technology
13 Ellis Street
@@ -2693,12 +3101,6 @@ E00000-EFFFFF (base 16) Mersen
Wellington 6023
NZ
-1C-CA-E3 (hex) Insigma Inc
-200000-2FFFFF (base 16) Insigma Inc
- 1920 Association Dr
- Reston Virginia 20191
- US
-
80-E4-DA (hex) Neutronics
A00000-AFFFFF (base 16) Neutronics
456 Creamery Way
@@ -3839,9 +4241,6 @@ CC-22-37 (hex) Apeiron Data Systems
San Francisco CA 94103
US
-74-1A-E0 (hex) Private
-900000-9FFFFF (base 16) Private
-
74-1A-E0 (hex) SHEN ZHEN YINGJIACHUANG ELECTRONICS TECHNOLOGY CO.,LTD.
B00000-BFFFFF (base 16) SHEN ZHEN YINGJIACHUANG ELECTRONICS TECHNOLOGY CO.,LTD.
Building A,Baishunjia Industrial Park,Guangming New District
@@ -3956,9 +4355,6 @@ F8-B5-68 (hex) CloudMinds (Shenzhen) Holdings Co., Ltd
Kirkland WA 98034
US
-10-07-23 (hex) Private
-F00000-FFFFFF (base 16) Private
-
28-2C-02 (hex) LLC MICROTEH
500000-5FFFFF (base 16) LLC MICROTEH
pl.5 bldg.2/3 Akademika Anokhina str.
@@ -4259,12 +4655,6 @@ F0-23-B9 (hex) EZVIS LIMITED
HONGKONG 999077
HK
-F0-23-B9 (hex) annapurnalabs
-A00000-AFFFFF (base 16) annapurnalabs
- HACRMEL 2 st Brosh BLDG FLOOR3
- YOKNEAM. POB 218 Israel 26814
- IL
-
8C-14-7D (hex) Unwired Networks
500000-5FFFFF (base 16) Unwired Networks
Gonzagagasse 11/25
@@ -4433,9 +4823,6 @@ B00000-BFFFFF (base 16) Eutron SPA
shenzhen guangdong 518102
CN
-C0-D3-91 (hex) Private
-B00000-BFFFFF (base 16) Private
-
90-4E-91 (hex) CommandScape, Inc.
800000-8FFFFF (base 16) CommandScape, Inc.
505 South Flagler Dr, Suite 900
@@ -4574,9 +4961,6 @@ C00000-CFFFFF (base 16) LDA Technologies
Mississauga Ontario L4W2C6
CA
-1C-A0-D3 (hex) Private
-700000-7FFFFF (base 16) Private
-
D4-7C-44 (hex) Innoviz Technologies LTD
100000-1FFFFF (base 16) Innoviz Technologies LTD
Atir Yeda 15
@@ -4781,6 +5165,333 @@ A4-ED-43 (hex) Linseis Messgeraete GmbH
Selb 95100
DE
+30-0A-60 (hex) Private
+300000-3FFFFF (base 16) Private
+
+30-0A-60 (hex) Beijing Ruiteng Zhongtian TECH Ltd.,Co
+100000-1FFFFF (base 16) Beijing Ruiteng Zhongtian TECH Ltd.,Co
+ Blk 6,Rm 602,Noble CenterⅡ,No.1 Automotive Museum East Lane,South Fourth Ring Road, Fengtai District
+ Beijing Beijing 100070
+ CN
+
+30-0A-60 (hex) KAZUtechnica Co.,Ltd.
+000000-0FFFFF (base 16) KAZUtechnica Co.,Ltd.
+ 1-9-18,Chuo,Chuo-ku
+ Sagamihara-shi Kanagawa 2520239
+ JP
+
+30-0A-60 (hex) Bronkhorst High-Tech BV
+800000-8FFFFF (base 16) Bronkhorst High-Tech BV
+ Nijverheidsstraat 1a
+ Ruurlo Gelderland NL-7261AK
+ NL
+
+3C-6A-2C (hex) Olibra LLC
+100000-1FFFFF (base 16) Olibra LLC
+ 45 legin dr
+ creskill NJ 07626
+ US
+
+3C-6A-2C (hex) XI'AN YEP TELECOM TECHNOLOGY CO.,LTD
+400000-4FFFFF (base 16) XI'AN YEP TELECOM TECHNOLOGY CO.,LTD
+ 5F,Building C,CLP Park,No.211, Tiangu 8 Road, High-tech Zone, Xi' an, Shanxi Province, China
+ Xi’an Shanxi 710001
+ CN
+
+3C-6A-2C (hex) Qingdao iGuan Technology Co., Ltd.
+500000-5FFFFF (base 16) Qingdao iGuan Technology Co., Ltd.
+ Room416, Science and Technology Park, Ocean University of China, No.23 HongKongEast Road
+ Qingdao Shandong 266100
+ CN
+
+3C-6A-2C (hex) Homegear GmbH
+700000-7FFFFF (base 16) Homegear GmbH
+ Am Schützenplatz 3
+ Preetz Schleswig-Holstein 24211
+ DE
+
+A8-3F-A1 (hex) Guangzhou Navigateworx Technologies Co., Limited
+E00000-EFFFFF (base 16) Guangzhou Navigateworx Technologies Co., Limited
+ Room 2320, Qianjin Commercial Building, Dongpu Town
+ Guangzhou Guangdong 510660
+ CN
+
+A8-3F-A1 (hex) Shanghai East China Computer Co., Ltd
+A00000-AFFFFF (base 16) Shanghai East China Computer Co., Ltd
+ 27/F Tower B, No.391 Guiping Rd
+ Shanghai Shanghai 200233
+ CN
+
+A8-3F-A1 (hex) BEGLEC
+600000-6FFFFF (base 16) BEGLEC
+ hofveld 2c
+ Groot-Bijgaarden 1702
+ BE
+
+1C-FD-08 (hex) SABIK Offshore GmbH
+400000-4FFFFF (base 16) SABIK Offshore GmbH
+ Wilhelm-Maybach-Straße 3
+ Schwerin Mecklenburg-Vorpommern D-19061
+ DE
+
+1C-FD-08 (hex) Shanghai YottaTech Co Ltd (上海尧它科技有é™å…¬å¸ï¼‰
+C00000-CFFFFF (base 16) Shanghai YottaTech Co Ltd (上海尧它科技有é™å…¬å¸ï¼‰
+ 399 keyuan Rd, Pudong New District
+ Shanghai 201203
+ CN
+
+10-07-23 (hex) Private
+F00000-FFFFFF (base 16) Private
+
+0C-FE-5D (hex) Chengdu Ledong Information & Technology Co., Ltd.
+000000-0FFFFF (base 16) Chengdu Ledong Information & Technology Co., Ltd.
+ D7-13F, Chengdu Tianfu Software Park, No. 599 Shijicheng South street, Gaoxin District, Chengdu, China
+ Chengdu sichaun 610041
+ CN
+
+C0-D3-91 (hex) Private
+B00000-BFFFFF (base 16) Private
+
+1C-CA-E3 (hex) Insigma Inc
+200000-2FFFFF (base 16) Insigma Inc
+ 43490, Yukon Drive, Suite 102
+ Ashburn VA 20147
+ US
+
+0C-FE-5D (hex) Celerway Communication AS
+900000-9FFFFF (base 16) Celerway Communication AS
+ Martin Lingesvei 25
+ Fornebu 1364
+ NO
+
+0C-FE-5D (hex) Beijing WayClouds Technology Co., Ltd.
+300000-3FFFFF (base 16) Beijing WayClouds Technology Co., Ltd.
+ RM501, 5F, DASCOM BD,NO.9 SHANGDI EAST RD, HAIDIAN DISTRICT,BEIJING,CHINA
+ Beijing 100085
+ CN
+
+98-F9-C7 (hex) Beijing Horizon Information Technology Co., Ltd
+300000-3FFFFF (base 16) Beijing Horizon Information Technology Co., Ltd
+ 3F,Unit H, West Gate, Hailong Mansion, No.1 Zhongguancun Street, Haidian District
+ Beijing 100080
+ CN
+
+98-F9-C7 (hex) GoodBox
+600000-6FFFFF (base 16) GoodBox
+ Ground Floor, Optimum House
+ Clippers Quay Salford Quays M50 3XP
+ GB
+
+98-F9-C7 (hex) Promess GmbH
+400000-4FFFFF (base 16) Promess GmbH
+ Schaffhausener Str. 44
+ Berlin 12099
+ DE
+
+7C-BC-84 (hex) CONTINENTAL
+400000-4FFFFF (base 16) CONTINENTAL
+ 1 AVENUE PAUL OURLIAC
+ TOULOUSE 31100
+ FR
+
+7C-BC-84 (hex) OPNT BV
+A00000-AFFFFF (base 16) OPNT BV
+ De Boelelaan 1081
+ Amsterdam 1081 HV
+ NL
+
+7C-BC-84 (hex) Tibit Communications
+C00000-CFFFFF (base 16) Tibit Communications
+ 1 Willowbrook Court, Suite 150
+ Petaluma CA 94954
+ US
+
+6C-DF-FB (hex) Toucan Systems Ltd
+C00000-CFFFFF (base 16) Toucan Systems Ltd
+ 3 Roseheyworth Business Park
+ Abertillery Bleanau Gwent NP13 1SP
+ GB
+
+6C-DF-FB (hex) YongTechs Electric Co. Ltd
+900000-9FFFFF (base 16) YongTechs Electric Co. Ltd
+ 18F-8, No.118, Ci-Yun Rd., Hsin Chu 30072, Taiwan(R.O.C.)
+ Hsin Chu 30072
+ TW
+
+6C-DF-FB (hex) Guilin Zhishen Information TechonlogyCO.,Ltd
+A00000-AFFFFF (base 16) Guilin Zhishen Information TechonlogyCO.,Ltd
+ Creative Industrial Park,GuiMo Rd.,Qi Xing
+ Guilin Guangxi 541004
+ CN
+
+74-1A-E0 (hex) Private
+900000-9FFFFF (base 16) Private
+
+4C-91-7A (hex) Openeye
+600000-6FFFFF (base 16) Openeye
+ 23221 East Knox Avenue
+ Liberty Lake WA 99019
+ US
+
+4C-91-7A (hex) AvertX
+B00000-BFFFFF (base 16) AvertX
+ 23221 E. Knox Ave
+ Liberty Lake WA 99019
+ US
+
+4C-91-7A (hex) LumiGrow Inc.
+400000-4FFFFF (base 16) LumiGrow Inc.
+ 1480 64th Street, Suite #150
+ Emeryville CA 94608
+ US
+
+9C-69-B4 (hex) Toughdog Security Systems
+B00000-BFFFFF (base 16) Toughdog Security Systems
+ 1317 E Hackberry Ave
+ McAllen TX 78501
+ US
+
+9C-69-B4 (hex) Guangdong Hanwei intergration Co.,Ltd
+C00000-CFFFFF (base 16) Guangdong Hanwei intergration Co.,Ltd
+ Room 404,7# Hongtai Zhihui Gu, No.23 Sicheng Road
+ Guangzhou Guangdong 510663
+ CN
+
+9C-69-B4 (hex) Skydock do Brasil Ltda
+800000-8FFFFF (base 16) Skydock do Brasil Ltda
+ Rua Gralha Azul, 147
+ Quatro Barras PR 83420-000
+ BR
+
+9C-69-B4 (hex) Globalcom Engineering SPA
+400000-4FFFFF (base 16) Globalcom Engineering SPA
+ Via Volta 9
+ MORNAGO VA 21020
+ IT
+
+F0-23-B9 (hex) Annapurna labs
+A00000-AFFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+D4-25-CC (hex) NORDI TELEKOMMUNIKATSIOONI OÜ
+000000-0FFFFF (base 16) NORDI TELEKOMMUNIKATSIOONI OÜ
+ Valukoja 8
+ Tallinn city Estonian Republic 11415
+ EE
+
+38-B1-9E (hex) Gesellschaft industrieller Technologien
+C00000-CFFFFF (base 16) Gesellschaft industrieller Technologien
+ Hauptstraße 10
+ Großbeeren 14979
+ DE
+
+38-B1-9E (hex) HDANYWHERE
+200000-2FFFFF (base 16) HDANYWHERE
+ Unit 23 Link Business Centre
+ Malvern Worcs WR14 1UQ
+ GB
+
+38-B1-9E (hex) Triple Jump Medical
+000000-0FFFFF (base 16) Triple Jump Medical
+ 5 HaCarmel St.
+ Yokneam 2069203
+ IL
+
+38-B1-9E (hex) Thrust Networks
+600000-6FFFFF (base 16) Thrust Networks
+ Pangeran Jayakarta 129 No B 7
+ Jakarta Jakarta 10730
+ ID
+
+D8-86-0B (hex) Teplovodokhran Ltd.
+400000-4FFFFF (base 16) Teplovodokhran Ltd.
+ Novaya , 51v
+ Ryazan 390027
+ RU
+
+D8-86-0B (hex) ComNav Technology Ltd.
+D00000-DFFFFF (base 16) ComNav Technology Ltd.
+ Buliding 2,No. 618 Chengliu Middle Road
+ JiaDing District Shanghai 201801
+ CN
+
+D8-86-0B (hex) VRINDA NANO TECHNOLOGIES PVT LTD
+800000-8FFFFF (base 16) VRINDA NANO TECHNOLOGIES PVT LTD
+ PLOT NO.283, SECTOR 7, IMT MANESAR, GURGAON
+ INDIA HARYANA 122050
+ IN
+
+D8-86-0B (hex) Shenzhen Yidong Technology Co.,Ltd
+E00000-EFFFFF (base 16) Shenzhen Yidong Technology Co.,Ltd
+ 13th Floor,Jia'anda Building, No.110 Huafan Road,Tongsheng Community, Dalang Street,Longhua District
+ Shenzhen Guangdong 518000
+ CN
+
+E0-5A-9F (hex) Hale Products
+400000-4FFFFF (base 16) Hale Products
+ 607 NW 27th Ave
+ Ocala FL 34475
+ US
+
+E0-5A-9F (hex) Chengdu Song Yuan Electronic Technology Co.,Ltd
+200000-2FFFFF (base 16) Chengdu Song Yuan Electronic Technology Co.,Ltd
+ Building 63 Cui Feng International, No.366 Bai Cao Road, High-tech West Zone
+ Chengdu Sichuan 610000
+ CN
+
+4C-BC-98 (hex) Heliotis AG
+C00000-CFFFFF (base 16) Heliotis AG
+ Längenbold 5
+ Root 6037
+ CH
+
+1C-A0-D3 (hex) Private
+700000-7FFFFF (base 16) Private
+
+E4-4C-C7 (hex) HANGZHOU OLE-SYSTEMS CO., LTD
+600000-6FFFFF (base 16) HANGZHOU OLE-SYSTEMS CO., LTD
+ No.35 Jiuhuan Road, Jianggan District , Hangzhou , Zhejiang , China
+ Hangzhou Zhejiang 310019
+ CN
+
+E4-4C-C7 (hex) Muzik Inc
+A00000-AFFFFF (base 16) Muzik Inc
+ 9220 Sunset Blvd #112
+ West Hollywood CA 90069
+ US
+
+74-5B-C5 (hex) Beijing Inspiry Technology Co., Ltd.
+100000-1FFFFF (base 16) Beijing Inspiry Technology Co., Ltd.
+ Building No. 5, East Zone, No. 10, Xibeiwang East Road, Haidian District
+ Beijing Beijing 100092
+ CN
+
+74-5B-C5 (hex) SHENZHEN ATX TECHNOLOGY CO.,LTD
+700000-7FFFFF (base 16) SHENZHEN ATX TECHNOLOGY CO.,LTD
+ 7/F,Zhengjiyuan Buiding,2 Road,Qianjing, Xixiang, Baoan District
+ Shenzhen GUANGDONG 518000
+ CN
+
+74-5B-C5 (hex) SIGLENT TECHNOLOGIES CO., LTD.
+200000-2FFFFF (base 16) SIGLENT TECHNOLOGIES CO., LTD.
+ Blog No.4 & No.5, Antongda Industrial Zone, 3rd Liuxian Road, Bao’an District, Shenzhen, 518101, China.
+ Shenzhen Guangdong 518101
+ CN
+
+74-5B-C5 (hex) Smartiply Inc.
+B00000-BFFFFF (base 16) Smartiply Inc.
+ 233 Mt. Airy Road
+ Basking Ridge NJ 07920
+ US
+
+FC-D2-B6 (hex) T CHIP DIGITAL TECHNOLOGY CO.LTD
+B00000-BFFFFF (base 16) T CHIP DIGITAL TECHNOLOGY CO.LTD
+ Room 320, C Tower, Jingji Building, HuaFeng Headquarter, Xixiang, Baoan
+ SHENZHEN Guangdong 518000
+ CN
+
1C-87-76 (hex) Zhuhai MYZR Technology Co.,Ltd
500000-5FFFFF (base 16) Zhuhai MYZR Technology Co.,Ltd
Room 302,Area D2,National Hi-tech Zone,NO.1,Software Park Road
@@ -6197,12 +6908,6 @@ B00000-BFFFFF (base 16) EnBW Energie Baden-Württemberg AG
Shannon Co. Clare
IE
-50-0B-91 (hex) annapurnalabs
-200000-2FFFFF (base 16) annapurnalabs
- HACRMEL 2 st Brosh BLDG FLOOR3
- YOKNEAM. POB 218 Israel 26814
- IL
-
50-0B-91 (hex) New Audio LLC
A00000-AFFFFF (base 16) New Audio LLC
132 W 31st Street Suite 701
@@ -6359,12 +7064,6 @@ A4-11-63 (hex) Carbon, Inc.
Redwood City CA 94063
US
-14-4F-D7 (hex) annapurnalabs
-000000-0FFFFF (base 16) annapurnalabs
- HACRMEL 2 st Brosh BLDG FLOOR3
- YOKNEAM. POB 218 Israel 26814
- IL
-
14-4F-D7 (hex) FLS FINLAND OY
500000-5FFFFF (base 16) FLS FINLAND OY
Voudinkatu 35 A
@@ -6485,12 +7184,6 @@ A0-C5-F2 (hex) Viettronimex JSC
Ho Chi Minh 70000
VN
-A0-C5-F2 (hex) Tango Wave
-500000-5FFFFF (base 16) Tango Wave
- 320 Soquel Way
- Sunnyvale CA 94085-4101
- US
-
A0-C5-F2 (hex) Glooko inc
C00000-CFFFFF (base 16) Glooko inc
899 W. Evelyn Avenue
@@ -6539,12 +7232,6 @@ A00000-AFFFFF (base 16) Protos GmbH
HongKong 529060
HK
-7C-BA-CC (hex) annapurnalabs
-A00000-AFFFFF (base 16) annapurnalabs
- HACRMEL 2 st Brosh BLDG FLOOR3
- YOKNEAM. POB 218 Israel 26814
- IL
-
7C-BA-CC (hex) Sun Asia Trade Co.
400000-4FFFFF (base 16) Sun Asia Trade Co.
RM 1005 10/F, HOKING COMMERCIAL CENTRE,2-16 FA YUEN STREET MONGKOk
@@ -6629,9 +7316,6 @@ B00000-BFFFFF (base 16) Shanghai JaWay Information Technology Co., Ltd.
Minneapolis MN 55401
US
-2C-27-9E (hex) Private
-300000-3FFFFF (base 16) Private
-
2C-27-9E (hex) Exegy Inc
A00000-AFFFFF (base 16) Exegy Inc
349 Marshall Avenue, Suite 100
@@ -6842,12 +7526,6 @@ F0-41-C8 (hex) POSTIUM KOREA CO., LTD.
Shenzhen 518000
CN
-48-0B-B2 (hex) annapurnalabs
-600000-6FFFFF (base 16) annapurnalabs
- HACRMEL 2 st Brosh BLDG FLOOR3
- YOKNEAM. POB 218 Israel 26814
- IL
-
48-0B-B2 (hex) BAJA ELECTRONICS TECHNOLOGY LIMITED
100000-1FFFFF (base 16) BAJA ELECTRONICS TECHNOLOGY LIMITED
403, Unit 3, Building 3, Zhongdian Hi-Tech Park, No.1 Kejiqi Rd.
@@ -7196,9 +7874,6 @@ C00000-CFFFFF (base 16) Impakt S.A.
Shenzhen Guangdong 518000
CN
-34-04-9E (hex) Private
-900000-9FFFFF (base 16) Private
-
3C-42-7E (hex) Teknoware Oy
400000-4FFFFF (base 16) Teknoware Oy
Ilmarisentie 8
@@ -7376,6 +8051,366 @@ F00000-FFFFFF (base 16) Private
C8-8E-D1 (hex) Private
F00000-FFFFFF (base 16) Private
+A4-ED-43 (hex) Shanghai Mission Information Technologies (Group) Co.,Ltd
+200000-2FFFFF (base 16) Shanghai Mission Information Technologies (Group) Co.,Ltd
+ Room 803, modern traffic building (East District), 218 Hengfeng Road, Jingan District
+ Shanghai 200041
+ CN
+
+30-0A-60 (hex) WINTEK System Co., Ltd
+900000-9FFFFF (base 16) WINTEK System Co., Ltd
+ Wintek Venture Bldg., 511, Poil-Dong
+ Uiwang-City Gyunggi-Do 16013
+ KR
+
+30-0A-60 (hex) Sixth Energy Technologies Private Limited
+D00000-DFFFFF (base 16) Sixth Energy Technologies Private Limited
+ #62, Sri Varda, 10th main, HMT Layout, R.T.Nagar
+ Bangalore Karnataka 560032
+ IN
+
+3C-6A-2C (hex) Xiamen Smarttek CO., Ltd.
+D00000-DFFFFF (base 16) Xiamen Smarttek CO., Ltd.
+ 5F, B Area, Chuangxin Plaza, SoftPark I, Siming District
+ Xiamen Fujian 361005
+ CN
+
+3C-6A-2C (hex) Rio Lago Technologies LLC
+000000-0FFFFF (base 16) Rio Lago Technologies LLC
+ 17741 Ludlow St.
+ Granada Hills CA 91344-4502
+ US
+
+3C-6A-2C (hex) WICKS Co., Ltd.
+900000-9FFFFF (base 16) WICKS Co., Ltd.
+ 202,1-29 shingi takasu
+ kochi City Kochi 781-8103
+ JP
+
+3C-6A-2C (hex) TP Radio
+800000-8FFFFF (base 16) TP Radio
+ Agenavej 37
+ Greve . 2670
+ DK
+
+A8-3F-A1 (hex) Guangzhou Tupu Internet Technology Co., Ltd.
+300000-3FFFFF (base 16) Guangzhou Tupu Internet Technology Co., Ltd.
+ 602, No.11, Jiangong Road, Tianhe Software Park, Tianhe District
+ Guangzhou Guangdong 510665
+ CN
+
+A8-3F-A1 (hex) Zhejiang Wellsun Intelligent Technology Co.,Ltd.
+400000-4FFFFF (base 16) Zhejiang Wellsun Intelligent Technology Co.,Ltd.
+ No.15,Xingye East 3rd St.,Fuxi District,Tiantai County
+ Taizhou Zhejiang 317200
+ CN
+
+1C-FD-08 (hex) InSeat Solutions, LLC
+000000-0FFFFF (base 16) InSeat Solutions, LLC
+ 1871 Wright Avenue
+ La Verne CA 91750
+ US
+
+1C-FD-08 (hex) Banmak Technogies Co.,Ltd
+A00000-AFFFFF (base 16) Banmak Technogies Co.,Ltd
+ 302, Building R3-B, High-Tech Park
+ Shenzhen Guangdong 518057
+ CN
+
+1C-FD-08 (hex) Shenzhen SEWO Technology Co.,Ltd.
+100000-1FFFFF (base 16) Shenzhen SEWO Technology Co.,Ltd.
+ Busha Road No 231. Buji Street. Longgang District.
+ Shenzhen Guangdong 518112
+ CN
+
+1C-FD-08 (hex) A&B Technology
+600000-6FFFFF (base 16) A&B Technology
+ 81, Geumnam-ro, Buk-gu
+ Gwangju 61247
+ KR
+
+6C-5C-3D (hex) KWONG MING ELECTRICAL MANUFACTORY LIMITED
+300000-3FFFFF (base 16) KWONG MING ELECTRICAL MANUFACTORY LIMITED
+ A3 bldg, Kwong Ming, Technology Industrial Zone, Shiwan Town, Boluo County
+ Huizhou Guangdong 516100
+ CN
+
+6C-5C-3D (hex) Shenzhen Justek Technology Co., Ltd
+100000-1FFFFF (base 16) Shenzhen Justek Technology Co., Ltd
+ 5/F, Building No.818, Qingtiexi Mabu Community, Bao 'an DistrictXixiang Street,
+ Shenzhen Guangdong 518102
+ CN
+
+1C-FD-08 (hex) MESHBOX FOUNDATION PTE. LTD.
+E00000-EFFFFF (base 16) MESHBOX FOUNDATION PTE. LTD.
+ 152 Beach Road #14-02 GATEWAY EAST TOWER
+ SINGAPORE 189721
+ SG
+
+6C-5C-3D (hex) Reconova Technologies
+B00000-BFFFFF (base 16) Reconova Technologies
+ B103,Shenzhen Institute of Nanjing University,Keyuan Road,Hi-Tech Park,Nanshan District
+ Shenzhen Guangdong 518000
+ CN
+
+2C-27-9E (hex) Private
+300000-3FFFFF (base 16) Private
+
+0C-FE-5D (hex) Dspread International Co.,Limited
+200000-2FFFFF (base 16) Dspread International Co.,Limited
+ UNIT 402. 4/F FAIRMONT HSE NO.8 COTTON TREE DRIVE ADMIRALTY
+ HONG KONG HONG KONG 999077
+ HK
+
+0C-FE-5D (hex) YINUO-LINK LIMITED
+B00000-BFFFFF (base 16) YINUO-LINK LIMITED
+ 5 Floor, Chuangyecheng,Xinghua Road,Xingwei,Fuyong
+ Shenzhen Guangdong 518103
+ CN
+
+0C-FE-5D (hex) SELECTRIC Nachrichten-Systeme GmbH
+500000-5FFFFF (base 16) SELECTRIC Nachrichten-Systeme GmbH
+ Haferlandweg 18
+ Münster 48155
+ DE
+
+0C-FE-5D (hex) Yantai Dongfang Wisdom Electic Co.,Ltd.
+400000-4FFFFF (base 16) Yantai Dongfang Wisdom Electic Co.,Ltd.
+ 6 Jindu Road
+ Yantai Shandong 264003
+ CN
+
+0C-FE-5D (hex) Antailiye Technology Co.,Ltd
+600000-6FFFFF (base 16) Antailiye Technology Co.,Ltd
+ 7/F,Zhengjiyuan Buiding,2 Road,Qianjing, Xixiang, Baoan District,Shenzhen
+ SHEN ZHEN GUANGDONG 518000
+ CN
+
+0C-FE-5D (hex) Maksat Technologies P Ltd
+D00000-DFFFFF (base 16) Maksat Technologies P Ltd
+ D-10/6, Okhla, Phase-I, Okhla
+ New Delhi Delhi 110020
+ IN
+
+98-F9-C7 (hex) Tonycore Technology Co.,Ltd.
+500000-5FFFFF (base 16) Tonycore Technology Co.,Ltd.
+ 2/F,Building A,Dingxin Technology Park,Honglang North 2nd Road,Xin’an Street,Bao’an District,
+ Shenzhen Guangdong 518000
+ CN
+
+98-F9-C7 (hex) Koala Technology CO., LTD.
+900000-9FFFFF (base 16) Koala Technology CO., LTD.
+ Room 2004, Building A3, Dayuan International Center, High Tech Zone
+ Chengdu Sichuan 610000
+ CN
+
+98-F9-C7 (hex) ARIMA Communications Corp.
+700000-7FFFFF (base 16) ARIMA Communications Corp.
+ 6F.,No.866,Zhongzheng Rd.,Zhonghe Dist.,
+ New Taipei City Taiwan 23586
+ TW
+
+7C-BC-84 (hex) AG Neovo
+000000-0FFFFF (base 16) AG Neovo
+ 5F-1, No. 3-1, Park Street, Nangang District, Taipei, 11503, Taiwan
+ Taipei 11503
+ TW
+
+7C-BC-84 (hex) VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD
+D00000-DFFFFF (base 16) VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD
+ B-11 SECTOR-VII
+ NOIDA UTTAR PRADESH 201301
+ IN
+
+6C-DF-FB (hex) Hardmeier
+800000-8FFFFF (base 16) Hardmeier
+ Pärnu mnt 102
+ Tallinn 11312
+ EE
+
+6C-DF-FB (hex) Sercomm Corporation.
+200000-2FFFFF (base 16) Sercomm Corporation.
+ 3F,No.81,Yu-Yih Rd.,Chu-Nan Chen
+ Miao-Lih Hsuan 115
+ TW
+
+4C-91-7A (hex) Shenzhen Dangs Science & Technology CO.,LTD
+000000-0FFFFF (base 16) Shenzhen Dangs Science & Technology CO.,LTD
+ 7D, 7th Floor, HSAE Building, Nanshan District,
+ Shenzhen GuangDong 518000
+ CN
+
+4C-91-7A (hex) Chongqing Unisinsight Technology Co.,Ltd.
+200000-2FFFFF (base 16) Chongqing Unisinsight Technology Co.,Ltd.
+ No.117-386, Yunhan Road
+ Beibei District Chongqing 400714
+ CN
+
+4C-91-7A (hex) Smart Access
+300000-3FFFFF (base 16) Smart Access
+ Nauchnuy Proezd 10
+ Moscow 117246
+ RU
+
+4C-91-7A (hex) Erlab DFS SAS
+A00000-AFFFFF (base 16) Erlab DFS SAS
+ Parc d'Affaires des Portes - BP403
+ Val de Reuil 27104
+ FR
+
+9C-69-B4 (hex) Suzhou Fitcan Technology Co.,LTD
+000000-0FFFFF (base 16) Suzhou Fitcan Technology Co.,LTD
+ Kechuang Road
+ Suzhou City Jiangsu Province 215163
+ CN
+
+9C-69-B4 (hex) Teptron AB
+900000-9FFFFF (base 16) Teptron AB
+ Box 1009
+ Varberg Halland 43213
+ SE
+
+50-0B-91 (hex) Annapurna labs
+200000-2FFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+14-4F-D7 (hex) Annapurna labs
+000000-0FFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+7C-BA-CC (hex) Annapurna labs
+A00000-AFFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+48-0B-B2 (hex) Annapurna labs
+600000-6FFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+D4-25-CC (hex) Nanjing LES Information Technology Co., Ltd
+600000-6FFFFF (base 16) Nanjing LES Information Technology Co., Ltd
+ No.8,YongZhi Road
+ Nanjing Jiangsu 210014
+ CN
+
+D4-25-CC (hex) EISST Ltd
+300000-3FFFFF (base 16) EISST Ltd
+ 10 Queen Street Place
+ London EC4R 1AG
+ GB
+
+D4-25-CC (hex) bvk technology
+500000-5FFFFF (base 16) bvk technology
+ Mithatpasa Mah. Serin Cikmazi No:4 C Blok Kemerburgaz, Eyupsultan
+ Istanbul Istanbul 34075
+ TR
+
+CC-D3-9D (hex) Glenair
+700000-7FFFFF (base 16) Glenair
+ 1211 Air Way
+ Glendale CA 91201
+ US
+
+CC-D3-9D (hex) Ethernity Networks
+D00000-DFFFFF (base 16) Ethernity Networks
+ Ha-Melacha 13
+ Lod Israel 7152025
+ IL
+
+38-B1-9E (hex) ShenZhen ShuaiXian Electronic Equipment Co.Ltd
+E00000-EFFFFF (base 16) ShenZhen ShuaiXian Electronic Equipment Co.Ltd
+ No. 10, Lane 3, Longxing Rd, Dakang Village,HengGang Town
+ ShenZhen Guangdong 518115
+ CN
+
+38-B1-9E (hex) Aeroespacial Guosheng Technology Co., Ltd
+A00000-AFFFFF (base 16) Aeroespacial Guosheng Technology Co., Ltd
+ 501 Edificio Gaode, No. 10 Huayuan East Road,
+ Haiden DIstrict Beijing 100191
+ CN
+
+D8-86-0B (hex) Inspur Group Co., Ltd.
+000000-0FFFFF (base 16) Inspur Group Co., Ltd.
+ No.1036 Langchao Rd.
+ Jinan Shandong 250101
+ CN
+
+D8-86-0B (hex) Get SAT
+200000-2FFFFF (base 16) Get SAT
+ Hamada 12
+ Rehovot Select From Dropdown 7670316
+ IL
+
+E0-5A-9F (hex) OMB Guitars LLC
+700000-7FFFFF (base 16) OMB Guitars LLC
+ 24 Hazorim St
+ Efrat Jerusalem 90435
+ IL
+
+E0-5A-9F (hex) Gemalto Document Readers
+900000-9FFFFF (base 16) Gemalto Document Readers
+ 3300 Acorn Street
+ Williamsburg VA 23188
+ US
+
+4C-BC-98 (hex) Elink Technology (Shenzhen) Co., Limited
+D00000-DFFFFF (base 16) Elink Technology (Shenzhen) Co., Limited
+ C-323,Baoan,Xinyidai Info.Tech.Industrial Park, No.139 Chuangye Er Rd.,Baoan District
+ Shenzhen Guangdong 518101
+ CN
+
+4C-BC-98 (hex) Charge-Amps AB
+000000-0FFFFF (base 16) Charge-Amps AB
+ GUSTAV III:S BOULEVARD 42, 8TR
+ SOLNA 16973
+ SE
+
+34-04-9E (hex) Private
+900000-9FFFFF (base 16) Private
+
+E4-4C-C7 (hex) JSC Svyaz Inginiring M
+300000-3FFFFF (base 16) JSC Svyaz Inginiring M
+ Varshavskoe Shosse, 42
+ Moscow 115230
+ RU
+
+E4-4C-C7 (hex) EPS Bio
+C00000-CFFFFF (base 16) EPS Bio
+ No. 8, R&D 3rd Rd, Science-Based Industrial Park
+ Hsinchu Taiwan 30077
+ TW
+
+74-5B-C5 (hex) OXON AG
+300000-3FFFFF (base 16) OXON AG
+ Waldeggstrasse 47
+ Liebefeld 3097
+ CH
+
+74-5B-C5 (hex) ComNot
+C00000-CFFFFF (base 16) ComNot
+ 15 chemin des Hirondelles
+ DARDILLY 69570
+ FR
+
+A0-C5-F2 (hex) Spacepath Communications Ltd
+500000-5FFFFF (base 16) Spacepath Communications Ltd
+ Unit 4, Bartley Point, Osborn Way
+ Hook, Hampshire RG27 9GX
+ GB
+
+FC-D2-B6 (hex) CG POWER AND INDUSTRIAL SOLUTIONS LTD
+000000-0FFFFF (base 16) CG POWER AND INDUSTRIAL SOLUTIONS LTD
+ A-3, MIDC , AMBAD
+ NASHIK MAHARASHTRA 422010
+ IN
+
1C-87-76 (hex) Hekatron Vertriebs GmbH
B00000-BFFFFF (base 16) Hekatron Vertriebs GmbH
Brühlmatten 9
@@ -8378,12 +9413,6 @@ A00000-AFFFFF (base 16) SECAD SA
Mountain View CA 94042
US
-7C-47-7C (hex) annapurnalabs
-C00000-CFFFFF (base 16) annapurnalabs
- HACRMEL 2 st Brosh BLDG FLOOR3
- YOKNEAM. POB 218 Israel 26814
- IL
-
98-6D-35 (hex) Vitronic Dr.-Ing. Stein Bildverarbeitungssysteme GmbH
600000-6FFFFF (base 16) Vitronic Dr.-Ing. Stein Bildverarbeitungssysteme GmbH
Hasengartenstraße 14
@@ -8933,12 +9962,6 @@ B00000-BFFFFF (base 16) PTYPE Co., LTD.
Anyang-si Gyeonggi-do 14056
KR
-8C-C8-F4 (hex) Beijing KXWELL Technology CO., LTD
-500000-5FFFFF (base 16) Beijing KXWELL Technology CO., LTD
- Room 608&609, Floor 6, Block C, Jiahao International Centre,#116 Zizhuyuan Road , Haidian District, Beijing, China
- Beijing Beijing 100097
- CN
-
8C-C8-F4 (hex) TOHO DENKI IND.CO.,LTD
300000-3FFFFF (base 16) TOHO DENKI IND.CO.,LTD
1-6-30 Meguro
@@ -9416,9 +10439,6 @@ A4-DA-22 (hex) Hydro Electronic Devices, Inc.
Hartford WI 53027
US
-A0-BB-3E (hex) Private
-F00000-FFFFFF (base 16) Private
-
88-A9-A7 (hex) Shenzhenshi kechuangzhixian technology Co.LTD
000000-0FFFFF (base 16) Shenzhenshi kechuangzhixian technology Co.LTD
Room 14G,14th Floor, Langshi Building , keji South Road 12 , High-tech Industrial Park , Nanshan District
@@ -9437,12 +10457,6 @@ B00000-BFFFFF (base 16) TWK-ELEKTRONIK
Pirmasens 66955
DE
-F0-41-C8 (hex) Nanchang BlackShark Co.,Ltd.
-700000-7FFFFF (base 16) Nanchang BlackShark Co.,Ltd.
- Room 319, Jiaoqiao Town Office Building, Economic and Technical development zone, Nanchang City, Jiangxi Province.
- Nanchang 330013
- CN
-
88-5F-E8 (hex) Hauch & Bach ApS
500000-5FFFFF (base 16) Hauch & Bach ApS
Femstykket 6
@@ -9473,12 +10487,6 @@ D00000-DFFFFF (base 16) D-Link (Shanghai)Limited Corp.
GYEONGGI-DO GOYANG-SI,ILSANDONG-GU 410315
KR
-24-4E-7B (hex) Private
-D00000-DFFFFF (base 16) Private
-
-2C-6A-6F (hex) Private
-F00000-FFFFFF (base 16) Private
-
2C-D1-41 (hex) Private
F00000-FFFFFF (base 16) Private
@@ -9551,12 +10559,6 @@ A00000-AFFFFF (base 16) flexlog GmbH
Karlsruhe 76227
DE
-38-73-EA (hex) annapurnalabs
-D00000-DFFFFF (base 16) annapurnalabs
- HACRMEL 2 st Brosh BLDG FLOOR3
- YOKNEAM. POB 218 Israel 26814
- IL
-
40-48-FD (hex) MITHRAS Technology Co., LTD
200000-2FFFFF (base 16) MITHRAS Technology Co., LTD
4F.-3, No. 880, Zhongzheng Rd., Zhonghe Dist.,
@@ -9617,12 +10619,6 @@ A4-DA-22 (hex) Original Products Pvt. Ltd.
New Delhi New Delhi 110062
IN
-2C-26-5F (hex) Private
-F00000-FFFFFF (base 16) Private
-
-28-FD-80 (hex) Private
-F00000-FFFFFF (base 16) Private
-
F0-41-C8 (hex) ATN Media Group FZ LLC
D00000-DFFFFF (base 16) ATN Media Group FZ LLC
Business Bay-alabrj st Business Towar By Damac.office-807
@@ -9683,9 +10679,6 @@ B00000-BFFFFF (base 16) Popit Oy
Shenzhen Guangdong 518000
CN
-38-B8-EB (hex) Private
-700000-7FFFFF (base 16) Private
-
8C-14-7D (hex) Private
100000-1FFFFF (base 16) Private
@@ -9887,18 +10880,330 @@ C00000-CFFFFF (base 16) Kalray S.A.
Tokyo 162-0825
JP
-A0-28-33 (hex) Kryptus Information Security S/A
-700000-7FFFFF (base 16) Kryptus Information Security S/A
- Av Romeu Tórtima, 554
- Campinas São Paulo 13084-791
- BR
-
E4-95-6E (hex) Private
F00000-FFFFFF (base 16) Private
74-E1-4A (hex) Private
F00000-FFFFFF (base 16) Private
+A4-ED-43 (hex) Dongguan Mingji Electronics technology Group Co., Ltd.
+300000-3FFFFF (base 16) Dongguan Mingji Electronics technology Group Co., Ltd.
+ NO.93,188 Industrial Road, Pingshan Village,Tangxia Town, Dongguan city, Guangdong Province , China.
+ DONGGUAN GUANGDONG 523710
+ CN
+
+30-0A-60 (hex) A9
+500000-5FFFFF (base 16) A9
+ 27,BUCHEON-RO 198BEON-GIL
+ BUCHEON-SI GYEONGGI-DO 14555
+ KR
+
+A8-3F-A1 (hex) Laonz Co.,Ltd
+C00000-CFFFFF (base 16) Laonz Co.,Ltd
+ 15-3,Eonnam-Gil,Seocho-gu
+ Seoul 06779
+ KR
+
+3C-6A-2C (hex) Beijing Donghua Hongtai Polytron Technologies Inc
+E00000-EFFFFF (base 16) Beijing Donghua Hongtai Polytron Technologies Inc
+ Room 126,Layer 1, Mudan building, , Huayuan Road No. 2, Haidian District
+ Beijing Beijing 100089
+ CN
+
+A8-3F-A1 (hex) Shenzhen ITLONG Intelligent Technology Co.,Ltd
+900000-9FFFFF (base 16) Shenzhen ITLONG Intelligent Technology Co.,Ltd
+ 12th floor, Building C1, Nanshan Zhiyuan, 1001 Xueyuan Avenue , Nanshan District
+ Shenzhen Guangdong 518055
+ CN
+
+A8-3F-A1 (hex) Plejd AB
+700000-7FFFFF (base 16) Plejd AB
+ Göteborgsvägen 52
+ Mölndal 431 37
+ SE
+
+A8-3F-A1 (hex) MEDCAPTAIN MEDICAL TECHNOLOGY CO., LTD.
+200000-2FFFFF (base 16) MEDCAPTAIN MEDICAL TECHNOLOGY CO., LTD.
+ 12th Floor, Baiwang Research Building, No.5158 Shahe West Road, Xili, Nanshan
+ Shenzhen 518055
+ CN
+
+1C-FD-08 (hex) HiHi Ltd
+200000-2FFFFF (base 16) HiHi Ltd
+ Loewy House 11 Enterprise Way
+ Christchurch BH236EW
+ GB
+
+F0-41-C8 (hex) Nanchang BlackShark Co.,Ltd.
+700000-7FFFFF (base 16) Nanchang BlackShark Co.,Ltd.
+ Room 319, Jiaoqiao Town Office Building, Economic and Technical development zone, Nanchang City, Jiangxi Province.
+ Nanchang 330013
+ CN
+
+6C-5C-3D (hex) Unitel Engineering
+500000-5FFFFF (base 16) Unitel Engineering
+ 2-Ñ ÐšÐ°Ð±ÐµÐ»ÑŒÐ½Ð°Ñ Ð´.2 ÑÑ‚Ñ€.1
+ МоÑква РоÑÑÐ¸Ñ 111024
+ RU
+
+6C-5C-3D (hex) krtkl inc.
+A00000-AFFFFF (base 16) krtkl inc.
+ 350 Townsend Street, Suite 301A
+ San Francisco CA 94107
+ US
+
+24-4E-7B (hex) Private
+D00000-DFFFFF (base 16) Private
+
+0C-FE-5D (hex) Fujian Jieyu Computer Technology Co., Ltd.
+A00000-AFFFFF (base 16) Fujian Jieyu Computer Technology Co., Ltd.
+ 3rd Floor, Building 21, Cangshan Park, Jinshan Orange Garden Industrial Park, Cangshan District
+ Fuzhou Fujian 350000
+ CN
+
+98-F9-C7 (hex) hangzhou soar security technologies limited liability company
+D00000-DFFFFF (base 16) hangzhou soar security technologies limited liability company
+ Zhebao Ideal Xiangyuan Creative industrial Park
+ Hangzhou Zhejiang Zhejiang
+ CN
+
+98-F9-C7 (hex) MSB Elektronik und Gerätebau GmbH
+A00000-AFFFFF (base 16) MSB Elektronik und Gerätebau GmbH
+ Hofwiesenstr. 23
+ Crailsheim 74564
+ DE
+
+98-F9-C7 (hex) HighSecLabs
+100000-1FFFFF (base 16) HighSecLabs
+ 29 Haeshel St
+ Caesarea 3088900
+ IL
+
+7C-BC-84 (hex) Xuji Changnan Communication Equipment Co., Ltd.
+700000-7FFFFF (base 16) Xuji Changnan Communication Equipment Co., Ltd.
+ No. 2725, Xu You Xi Road
+ Xuchang Henan 461001
+ CN
+
+7C-BC-84 (hex) Shenzhen Kuang-chi Space Technology Co., Ltd.
+800000-8FFFFF (base 16) Shenzhen Kuang-chi Space Technology Co., Ltd.
+ No. 1 building, No. 2 Reservoir Road, Nan Keng community , Bantian street, Longgang District, Shenzhen
+ shenzhen Guangdong 518000
+ CN
+
+7C-BC-84 (hex) Beijing Topnew Group Co., Ltd
+E00000-EFFFFF (base 16) Beijing Topnew Group Co., Ltd
+ No.9, A, Jin Tai Li, ChaoYang District
+ Beijing 100026
+ CN
+
+6C-DF-FB (hex) Chongqing Baoli Yota Technologies Limited
+100000-1FFFFF (base 16) Chongqing Baoli Yota Technologies Limited
+ No.2,1 Floor,Kelly Building,No.115 Xingtong Avenue,Guilin Street office, Tongnan District
+ Chongqing Chongqing 402660
+ CN
+
+8C-C8-F4 (hex) Beijing KXWELL Technology CO., LTD
+500000-5FFFFF (base 16) Beijing KXWELL Technology CO., LTD
+ Room 12A05, North Real Estate Building, Block 3
+ 81 Zizhuyuan Road, Haidian District Beijing 100089
+ CN
+
+9C-69-B4 (hex) PCI Limited
+700000-7FFFFF (base 16) PCI Limited
+ 35, Pioneer Road North
+ Singapore 628475
+ SG
+
+7C-47-7C (hex) Annapurna labs
+C00000-CFFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+38-73-EA (hex) Annapurna labs
+D00000-DFFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+D4-25-CC (hex) TAKUMI JAPAN LTD
+900000-9FFFFF (base 16) TAKUMI JAPAN LTD
+ 3-9-3 Uchiyama building 7F Nishishinbashi
+ Minato-ku Tokyo Tokyo 1050003
+ JP
+
+D4-25-CC (hex) DOLBY LABORATORIES, INC.
+800000-8FFFFF (base 16) DOLBY LABORATORIES, INC.
+ 100 Potrero Avenue
+ San Francisco CA 94103-4938
+ US
+
+D4-25-CC (hex) E-MetroTel
+A00000-AFFFFF (base 16) E-MetroTel
+ 2828 West Parker Unit B201
+ Plano TX 75075
+ US
+
+D4-25-CC (hex) MusicLens Inc.
+200000-2FFFFF (base 16) MusicLens Inc.
+ 311 E VALLEY BLVD#112 PMB27
+ SANGABRIEL CA 91776
+ US
+
+CC-D3-9D (hex) SHENZHEN ROYOLE TECHNOLOGIES CO., LTD.
+500000-5FFFFF (base 16) SHENZHEN ROYOLE TECHNOLOGIES CO., LTD.
+ Building #43, Dayun Software Town, No.8288 Longgang Road, Henggang Street, Longgang District,
+ Shenzhen Guangdong 518000
+ CN
+
+CC-D3-9D (hex) Shenzhen Chenggu Technology Co., Ltd
+400000-4FFFFF (base 16) Shenzhen Chenggu Technology Co., Ltd
+ Longgang District Jihua street Zhonghaixin Industrial Innovation City 19A 3F
+ Shenzhen Guagndong 518112
+ CN
+
+CC-D3-9D (hex) Hangzhou Scooper Technology Co.,Ltd.
+C00000-CFFFFF (base 16) Hangzhou Scooper Technology Co.,Ltd.
+ Room706,707,5th,No.998,Wenyi West Raod,Wuchang Street,Yuhang District,Hangzhou
+ HangZhou ZheJiang 311121
+ CN
+
+38-B1-9E (hex) Star Electronics GmbH & CoKG
+500000-5FFFFF (base 16) Star Electronics GmbH & CoKG
+ Jahnstraße, 86
+ Göppingen 73037
+ DE
+
+38-B1-9E (hex) Freedompro Srl
+100000-1FFFFF (base 16) Freedompro Srl
+ Via Frova, 34
+ Cinisello Balsamo 20092
+ IT
+
+D8-86-0B (hex) Library Ideas
+B00000-BFFFFF (base 16) Library Ideas
+ PO Box 9
+ Vienna VA 22183
+ US
+
+D8-86-0B (hex) Krspace
+100000-1FFFFF (base 16) Krspace
+ Building 1, Junhao Central Park Plaza
+ ChaoYang District Beijing 100125
+ CN
+
+D8-86-0B (hex) SCANMATIK
+600000-6FFFFF (base 16) SCANMATIK
+ Letnaya 18-2
+ Mytischi Moscow 141008
+ RU
+
+E0-5A-9F (hex) Contemporary Amperex Technology Co., Limited
+A00000-AFFFFF (base 16) Contemporary Amperex Technology Co., Limited
+ No.2 Xingang Road, Zhangwan Town, Jiaocheng District
+ Ningde Fujian 352000
+ CN
+
+E0-5A-9F (hex) Fujian Newland Auto-ID Tech. Co.,Ltd.
+800000-8FFFFF (base 16) Fujian Newland Auto-ID Tech. Co.,Ltd.
+ Newland Science & Technology Park, No.1 Rujiang West Rd,Mawei,Fuzhou, P.R.China
+ Fuzhou Fujian 350015
+ CN
+
+E0-5A-9F (hex) Shenzhen Rongan Networks Technology Co.,Ltd
+B00000-BFFFFF (base 16) Shenzhen Rongan Networks Technology Co.,Ltd
+ Room 1903, Industrial and Research building of Sun Yat-Sen university,no.1, Yuexing 4th road, Yuehai street,Nanshan district
+ Shenzhen Guangdong 518057
+ CN
+
+E0-5A-9F (hex) Link of Things Co., Ltd.
+300000-3FFFFF (base 16) Link of Things Co., Ltd.
+ 9F, Park St. 3-2, NanKang Software Park, Taipei, Taiwan
+ Taipei 115
+ TW
+
+4C-BC-98 (hex) Nemon Co., Ltd.
+400000-4FFFFF (base 16) Nemon Co., Ltd.
+ B-834, 164 Tancheonsangro, Bundang
+ Seongnam Gyeonggi 13631
+ KR
+
+4C-BC-98 (hex) Machine Max
+300000-3FFFFF (base 16) Machine Max
+ Shell Centre
+ London SE1 7NA
+ GB
+
+38-B8-EB (hex) Private
+700000-7FFFFF (base 16) Private
+
+2C-26-5F (hex) Private
+F00000-FFFFFF (base 16) Private
+
+28-FD-80 (hex) Private
+F00000-FFFFFF (base 16) Private
+
+A0-BB-3E (hex) Private
+F00000-FFFFFF (base 16) Private
+
+2C-6A-6F (hex) Private
+F00000-FFFFFF (base 16) Private
+
+E4-4C-C7 (hex) Beijing Zhongchuangwei Nanjing Quantum Communication Technology Co., Ltd.
+400000-4FFFFF (base 16) Beijing Zhongchuangwei Nanjing Quantum Communication Technology Co., Ltd.
+ No. 88, pubin avenue, jiangpu street, pukou district, nanjing city
+ nanjing Jiangsu 210000
+ CN
+
+E4-4C-C7 (hex) ACS-Solutions GmbH
+100000-1FFFFF (base 16) ACS-Solutions GmbH
+ Science Park 2
+ Saarbrücken 66123
+ DE
+
+E4-4C-C7 (hex) CE LABS, LLC
+500000-5FFFFF (base 16) CE LABS, LLC
+ 3209 WOOD DRIVE
+ GARLAND TX 75041
+ US
+
+E4-4C-C7 (hex) FLK information security technology Co,. Ltd
+E00000-EFFFFF (base 16) FLK information security technology Co,. Ltd
+ Room 1801, Yinfeng Building, No. 1505, Binsheng Road, Binjiang District
+ Hangzhou Zhejiang 310051
+ CN
+
+74-5B-C5 (hex) Yekani Manufacturing PTY Ltd
+600000-6FFFFF (base 16) Yekani Manufacturing PTY Ltd
+ Fourways Golf Park,Selbourne Building,Roos St
+ Witkoppen Gauteng 5214
+ ZA
+
+A0-28-33 (hex) Kryptus Information Security S/A
+700000-7FFFFF (base 16) Kryptus Information Security S/A
+ Rua Maria Tereza Dias da Silva 270
+ Cidade Universitaria Campinas-SP CEP 13083-820
+ BR
+
+74-5B-C5 (hex) Qingdao Wintec System Co., Ltd
+E00000-EFFFFF (base 16) Qingdao Wintec System Co., Ltd
+ Wintec Park, Xinye Road, High-tech Zone, Qingdao, China 266114
+ QING DAO shan dong of China 6805
+ CN
+
+FC-D2-B6 (hex) Coet Costruzioni Elettrotecniche
+300000-3FFFFF (base 16) Coet Costruzioni Elettrotecniche
+ Via Civesio 12
+ San Donato Milanese Mi 20097
+ IT
+
+FC-D2-B6 (hex) Bee Smart(Changzhou) Information Technology Co., Ltd
+D00000-DFFFFF (base 16) Bee Smart(Changzhou) Information Technology Co., Ltd
+ Changwu Middle Road
+ Changzhou Jiangsu 213100
+ CN
+
1C-87-74 (hex) Philips Personal Health Solutions
000000-0FFFFF (base 16) Philips Personal Health Solutions
High Tech Campus, HTC37 floor 0
@@ -10502,12 +11807,6 @@ D00000-DFFFFF (base 16) XIAMEN LEELEN TECHNOLOGY CO.,LTD
Chaoyang District Beijing 100107
CN
-D0-22-12 (hex) Schleifenbauer Holding BV
-B00000-BFFFFF (base 16) Schleifenbauer Holding BV
- Kievitsven 62B
- Rosmalen Noord-Brabant 5249 JK
- NL
-
D0-22-12 (hex) Shenzhen SIC Technology. Co., Ltd.
800000-8FFFFF (base 16) Shenzhen SIC Technology. Co., Ltd.
Room 601-31,Floor 6,Meinian International Square
@@ -10871,12 +12170,6 @@ A00000-AFFFFF (base 16) Groupeer Technologies
Paris Ile-de-France 75008
FR
-58-E8-76 (hex) annapurnalabs
-B00000-BFFFFF (base 16) annapurnalabs
- HACRMEL 2 st Brosh BLDG FLOOR3
- YOKNEAM. POB 218 Israel 26814
- IL
-
58-E8-76 (hex) SHENZHEN DIGISSIN TECHNOLOGY
A00000-AFFFFF (base 16) SHENZHEN DIGISSIN TECHNOLOGY
Room 315, Zixinda Building, No. 1053, BAOYUAN Road, Xixiang, Baoan District,
@@ -11177,9 +12470,6 @@ AC-1D-DF (hex) Solare Datensysteme GmbH
Geislingen-Binsdorf Baden-Wuerttemberg 72351
DE
-80-0A-80 (hex) Private
-F00000-FFFFFF (base 16) Private
-
34-D0-B8 (hex) OROSOUND SAS
B00000-BFFFFF (base 16) OROSOUND SAS
48 RUE AMELOT
@@ -11300,12 +12590,6 @@ A00000-AFFFFF (base 16) SinePulse GmbH
shenzhen Guangdong 518131
CN
-A4-4F-29 (hex) Private
-F00000-FFFFFF (base 16) Private
-
-0C-EF-AF (hex) Private
-F00000-FFFFFF (base 16) Private
-
28-2C-02 (hex) EFENTO T P SZYDÅOWSKI K ZARĘBA SPÓÅKA JAWNA
400000-4FFFFF (base 16) EFENTO T P SZYDÅOWSKI K ZARĘBA SPÓÅKA JAWNA
Dietla 93/6
@@ -12128,9 +13412,6 @@ B00000-BFFFFF (base 16) HangZhou iMagic Technology Co., Ltd
Hangzhou Zhejiang 310012
CN
-8C-C8-F4 (hex) Private
-700000-7FFFFF (base 16) Private
-
D4-7C-44 (hex) OPTiM Corporation
B00000-BFFFFF (base 16) OPTiM Corporation
1 Honjo-machi
@@ -12245,12 +13526,6 @@ F00000-FFFFFF (base 16) Private
Rancho Cucamonga CA 91730
US
-9C-F6-DD (hex) annapurnalabs
-000000-0FFFFF (base 16) annapurnalabs
- HACRMEL 2 st Brosh BLDG FLOOR3
- YOKNEAM. POB 218 Israel 26814
- IL
-
9C-F6-DD (hex) Guangzhou LANGO Electronics Technology Co., Ltd.
B00000-BFFFFF (base 16) Guangzhou LANGO Electronics Technology Co., Ltd.
136#, Gaopu Road, Tianhe District
@@ -12316,3 +13591,429 @@ A00000-AFFFFF (base 16) Guangzhou Maxfaith Communication Technology Co.,LTD
3rd Floor,Office of Youtong Zone,No.139,Zhongshan Avenue
Guangzhou 510665
CN
+
+30-0A-60 (hex) Advanced Electronic Designs, Inc.
+200000-2FFFFF (base 16) Advanced Electronic Designs, Inc.
+ 233 Enterprise Blvd
+ Bozeman MT 59718
+ US
+
+A4-ED-43 (hex) TOEC TECHNOLOGY CO.,LTD.
+E00000-EFFFFF (base 16) TOEC TECHNOLOGY CO.,LTD.
+ 6 Taishan Road Hexi District Tianjin China
+ Tian Jin Tian Jin 300211
+ CN
+
+30-0A-60 (hex) Thermo Process Instruments, LP
+C00000-CFFFFF (base 16) Thermo Process Instruments, LP
+ 27 Forge Parkway
+ Frankllin MA 02038
+ US
+
+30-0A-60 (hex) Newtons4th Ltd
+700000-7FFFFF (base 16) Newtons4th Ltd
+ 1 Bede Island Road
+ Leicester LE2 7EA
+ GB
+
+3C-6A-2C (hex) Eltov System
+C00000-CFFFFF (base 16) Eltov System
+ #1309 Gyeonggi Venture Yeonsung Univ Center. 111, Anyang-ro, Manan-gu,
+ Anyang-si 14093
+ KR
+
+3C-6A-2C (hex) Metro
+A00000-AFFFFF (base 16) Metro
+ 189 rue de la Jonchere
+ Boege 74420
+ FR
+
+3C-6A-2C (hex) Bosch Automotive Products (Suzhou) Co., Ltd.
+200000-2FFFFF (base 16) Bosch Automotive Products (Suzhou) Co., Ltd.
+ No. 455 Xing Long Street
+ Suzhou 215000
+ CN
+
+A8-3F-A1 (hex) Imecon Engineering SrL
+000000-0FFFFF (base 16) Imecon Engineering SrL
+ via Gerola 13/15
+ Fiesco CR 26010
+ IT
+
+1C-FD-08 (hex) LABEL
+900000-9FFFFF (base 16) LABEL
+ 18 ALLEE DE MONTREAL
+ ANNEMASSE 74100
+ FR
+
+6C-5C-3D (hex) Hangzhou Netease Yanxuan Trading Co.,Ltd
+600000-6FFFFF (base 16) Hangzhou Netease Yanxuan Trading Co.,Ltd
+ Room 410,No.4 Building,No.599 Road Wangshang,
+ Hangzhou, Binjiang District, Zhejiang Province 310052
+ CN
+
+6C-5C-3D (hex) HTI Co., LTD.
+400000-4FFFFF (base 16) HTI Co., LTD.
+ Rm741, Kyungin Ctr., 20, Baekbeom-ro 577, Bupyung-Gu
+ Incheon 21449
+ KR
+
+6C-5C-3D (hex) Vertiv Industrial Systems
+200000-2FFFFF (base 16) Vertiv Industrial Systems
+ 30 avenue Montgolfier
+ Chassieu 69684
+ FR
+
+6C-5C-3D (hex) GUANGZHOU GUANGRI ELEVATOR INDUSTRY CO.,LTD
+800000-8FFFFF (base 16) GUANGZHOU GUANGRI ELEVATOR INDUSTRY CO.,LTD
+ 636 South of Guomao Road,Shilou County,Panyu District
+ Guangzhou Guangdong 511047
+ CN
+
+6C-5C-3D (hex) ShenZhen Hugsun Technology Co.,Ltd.
+000000-0FFFFF (base 16) ShenZhen Hugsun Technology Co.,Ltd.
+ 413~415 Room, 4/F, No.6 Bldg., TongFuYu Industrial Park, Dalang Street, 518109, Longhua New District,
+ ShengZhen GuangDong 518109
+ CN
+
+80-0A-80 (hex) Private
+F00000-FFFFFF (base 16) Private
+
+6C-5C-3D (hex) Clinton Electronics Corporation
+E00000-EFFFFF (base 16) Clinton Electronics Corporation
+ 6701 Clinton Road
+ Loves Park IL 61111
+ US
+
+D0-22-12 (hex) Schleifenbauer Holding BV
+B00000-BFFFFF (base 16) Schleifenbauer Holding BV
+ Rietwaard 15
+ Hertogenbosch 5236WC
+ NL
+
+0C-FE-5D (hex) Vermes Microdispensing GmbH
+700000-7FFFFF (base 16) Vermes Microdispensing GmbH
+ Palnkamer Strasse 18
+ Otterfing Bayern 83624
+ DE
+
+0C-FE-5D (hex) Fender Musical Instrument
+100000-1FFFFF (base 16) Fender Musical Instrument
+ 17600 N Perimeter DR #100
+ Scottsdale AZ 85255
+ US
+
+98-F9-C7 (hex) Pozyx NV
+200000-2FFFFF (base 16) Pozyx NV
+ Vrijdagmarkt 10/201
+ Gent Oost-Vlaanderen 9000
+ BE
+
+0C-FE-5D (hex) NEWGREEN TECH CO., LTD.
+E00000-EFFFFF (base 16) NEWGREEN TECH CO., LTD.
+ 2F., No.200, Sec. 2, Gaotie N. Rd., Dayuan Dist., Taoyuan City 337, Taiwan (R.O.C.)
+ Taoyuan City 33743
+ TW
+
+0C-FE-5D (hex) CTK Contact Electronics co., Ltd.
+800000-8FFFFF (base 16) CTK Contact Electronics co., Ltd.
+ 6F-5, No79, Sec 1, Xintai 5th RD, Xizhi Dist
+ New Taipei City 22101
+ TW
+
+98-F9-C7 (hex) HIROIA Communications Pte. Ltd. Taiwan Branch
+B00000-BFFFFF (base 16) HIROIA Communications Pte. Ltd. Taiwan Branch
+ 7F., No.189, Xinhu 3rd Rd., Neihu Dist.,
+ Taipei City 11494
+ TW
+
+98-F9-C7 (hex) Renalsense
+800000-8FFFFF (base 16) Renalsense
+ Hamarpe 3
+ Jerusalem 9777403
+ IL
+
+7C-BC-84 (hex) Xiamen Mage Information Technology Co.,Ltd.
+100000-1FFFFF (base 16) Xiamen Mage Information Technology Co.,Ltd.
+ Room 302B, No.40 Guanri Road, Software Park II
+ Xiamen Fujian 361008
+ CN
+
+7C-BC-84 (hex) Nanning auto digital technology co.,LTD
+500000-5FFFFF (base 16) Nanning auto digital technology co.,LTD
+ 3/f, no.6 guichun road, qingxiu district
+ Nanning Guangxi 530021
+ CN
+
+7C-BC-84 (hex) 3S Technology Co., Ltd.
+200000-2FFFFF (base 16) 3S Technology Co., Ltd.
+ 301-1103, 345, Seokcheon-ro
+ Bucheon-si Gyeonggi-do 14501
+ KR
+
+7C-BC-84 (hex) Société de Transport de Montréal
+600000-6FFFFF (base 16) Société de Transport de Montréal
+ 800, de La Gauchetière, bureau 8440
+ Montréal Québec H5A 1J6
+ CA
+
+7C-BC-84 (hex) Guangzhou Puppyrobot Technology Co.Ltd Beijing Branch
+B00000-BFFFFF (base 16) Guangzhou Puppyrobot Technology Co.Ltd Beijing Branch
+ R&F center,63 East middle 3rd ring road
+ beijing 10000
+ CN
+
+A4-4F-29 (hex) Private
+F00000-FFFFFF (base 16) Private
+
+0C-EF-AF (hex) Private
+F00000-FFFFFF (base 16) Private
+
+6C-DF-FB (hex) CELL System Co.,Ltd.
+B00000-BFFFFF (base 16) CELL System Co.,Ltd.
+ 1-2-8 Azamino-minami, Aobaku
+ Yokohama Kanagawa 225-0012
+ JP
+
+6C-DF-FB (hex) Greenbird Vertriebs GmbH
+500000-5FFFFF (base 16) Greenbird Vertriebs GmbH
+ Georg Sigl-Straße 40-42
+ Breitenfurt bei Wien Niederösterreich 2384
+ AT
+
+4C-91-7A (hex) Hangzhou Hangtu Technology Co.,Ltd.
+900000-9FFFFF (base 16) Hangzhou Hangtu Technology Co.,Ltd.
+ Room A301, no.11, xiyuan eighth road, xihu district
+ Hangzhou Zhejiang 310000
+ CN
+
+9C-69-B4 (hex) Appareo Systems, LLC
+300000-3FFFFF (base 16) Appareo Systems, LLC
+ 1830 NDSU Research Circle N
+ Fargo ND 58102
+ US
+
+9C-69-B4 (hex) Elesta GmbH
+500000-5FFFFF (base 16) Elesta GmbH
+ Heuteilstrasse 18
+ Bad Ragaz St. Gallen 7310
+ CH
+
+9C-69-B4 (hex) MOZI (Shenzhen) Artificial Intelligence Technology Co., Ltd.
+200000-2FFFFF (base 16) MOZI (Shenzhen) Artificial Intelligence Technology Co., Ltd.
+ Room 1305, Building A Phase I Innovation and Technology Square, 4 Tairan Road, Futian
+ Shenzhen Guangdong 518000
+ CN
+
+9C-69-B4 (hex) Shenzhen jiahua zhongli technology co.LTD
+600000-6FFFFF (base 16) Shenzhen jiahua zhongli technology co.LTD
+ 3/F,building B,JINHU building,industrial road,longhua new district
+ Shenzhen Guangdong 518109
+ CN
+
+58-E8-76 (hex) Annapurna labs
+B00000-BFFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+9C-F6-DD (hex) Annapurna labs
+000000-0FFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+9C-69-B4 (hex) BEIJING PICOHOOD TECHNOLOGY CO.,LTD
+A00000-AFFFFF (base 16) BEIJING PICOHOOD TECHNOLOGY CO.,LTD
+ Room 504A, Building E, Lize Middle Yard, Wangjing Science and Technology Venture Park, Chaoyang District
+ BEIJING BEIJING 100102
+ CN
+
+9C-69-B4 (hex) Intellect module LLC
+D00000-DFFFFF (base 16) Intellect module LLC
+ Krasnogo kursanta str. 25 lit. J
+ Saint-Petersburg 197110
+ RU
+
+9C-69-B4 (hex) NINGBO SHEN LINK COMMUNICATION TECHNOLOGY CO., LTD
+E00000-EFFFFF (base 16) NINGBO SHEN LINK COMMUNICATION TECHNOLOGY CO., LTD
+ NO.87,FENGMINGROAD,LIZHOUSTREET, YUYAO, ZHEJIANG
+ NINGBO ZHEJIANG 315400
+ CN
+
+D4-25-CC (hex) Eware Information Technology com.,Ltd
+100000-1FFFFF (base 16) Eware Information Technology com.,Ltd
+ No.402,Building 5,Software Park,Keji Mid 3nd Road,Nanshan District
+ Shenzhen Guangdong 518057
+ CN
+
+D4-25-CC (hex) POSNET Polska S.A.
+C00000-CFFFFF (base 16) POSNET Polska S.A.
+ ul. Municypalna 33
+ Warszawa 02-281
+ PL
+
+CC-D3-9D (hex) Bejing Nexsec Inc.
+900000-9FFFFF (base 16) Bejing Nexsec Inc.
+ Floor 5, Room 5001,Buliding No.4,Fengxian Middle Road No.7
+ (Beike Industrial Park),Haidian District, Beijing 100094
+ CN
+
+38-B1-9E (hex) BoCo Inc.
+800000-8FFFFF (base 16) BoCo Inc.
+ Isshin Bldg 6F,Yaesu 2-11-7
+ Chuo-ku, Tokyo 104-0028
+ JP
+
+38-B1-9E (hex) Dallas Delta Corporation
+D00000-DFFFFF (base 16) Dallas Delta Corporation
+ 102 Albert Street
+ Brunswick East Victoria 3057
+ AU
+
+38-B1-9E (hex) System Q Ltd
+B00000-BFFFFF (base 16) System Q Ltd
+ Turnoaks Business Park, Hasland
+ Chesterfield Derbyshire S40 2WB
+ GB
+
+D8-86-0B (hex) GLO Science
+A00000-AFFFFF (base 16) GLO Science
+ 10 West 37th Street, 1001
+ New York NY 10018
+ US
+
+D8-86-0B (hex) Auvidea GmbH
+300000-3FFFFF (base 16) Auvidea GmbH
+ Kellerberg 3
+ Denklingen 86920
+ DE
+
+D8-86-0B (hex) DIGITAL CONCEPTS
+900000-9FFFFF (base 16) DIGITAL CONCEPTS
+ 3108 RIVERPORT TECH CENTER DR
+ MARYLAND HEIGHTS MO 630434825
+ US
+
+D8-86-0B (hex) CAMTRACE
+500000-5FFFFF (base 16) CAMTRACE
+ 26 BIS BOULEVARD HENRI SELLIER
+ SURESNES 92150
+ FR
+
+E0-5A-9F (hex) AITEC SYSTEM CO., LTD.
+100000-1FFFFF (base 16) AITEC SYSTEM CO., LTD.
+ 466-1 Aoto-cho, Midori-ku
+ Yokohama-shi Kanagawa 226-0022
+ JP
+
+E0-5A-9F (hex) Annapurna labs
+000000-0FFFFF (base 16) Annapurna labs
+ Matam Scientific Industries Center, Building 8.2
+ Mail box 15123 Haifa 3508409
+ IL
+
+E0-5A-9F (hex) Fibrain
+600000-6FFFFF (base 16) Fibrain
+ Zaczernie 190F
+ Zaczernie Subcarpathia 36-062
+ PL
+
+E0-5A-9F (hex) TRYEN
+500000-5FFFFF (base 16) TRYEN
+ Manan-gu Anyang-ro 110
+ Anyang Kyeong-gi 14035
+ KR
+
+E0-5A-9F (hex) ShenZhen Mornsun Smartlinker Limited Co., LTD
+C00000-CFFFFF (base 16) ShenZhen Mornsun Smartlinker Limited Co., LTD
+ South 2nd Flr, 23 Bldg, Yuanxi Industrial Zone, Kefa Rd, Nanshan Keji Yuan
+ ShenZhen GuangDong 518058
+ CN
+
+E0-5A-9F (hex) ShenZhen Arts Changhua Intelligent Technology Co., Ltd
+E00000-EFFFFF (base 16) ShenZhen Arts Changhua Intelligent Technology Co., Ltd
+ 15i, Taibang Science & Technology Building, Science and Technology South 8th Road, Hi-Tech Park South Area
+ Shenzhen Guangdong 518057
+ CN
+
+4C-BC-98 (hex) Quake Global Inc
+200000-2FFFFF (base 16) Quake Global Inc
+ 4711 VIEWRIDGE AVE., STE 150
+ SAN DIEGO CA 92123
+ US
+
+4C-BC-98 (hex) Humanplus Intelligent Robotics Technology Co.,Ltd.
+600000-6FFFFF (base 16) Humanplus Intelligent Robotics Technology Co.,Ltd.
+ Room507,5th Floor, Zhongguancun Frontier Technology Innovation Building, No. 67 North Fourth Ring Road,Haidian District,
+ Beijing Beijing 100089
+ CN
+
+4C-BC-98 (hex) Dongguan SmartAction Technology Co.,Ltd
+B00000-BFFFFF (base 16) Dongguan SmartAction Technology Co.,Ltd
+ Room1109,Building D,First Place,Nancheng District, Dongguan
+ Dongguan Guangdong 523000
+ CN
+
+8C-C8-F4 (hex) Private
+700000-7FFFFF (base 16) Private
+
+4C-BC-98 (hex) JSC NIC
+100000-1FFFFF (base 16) JSC NIC
+ Nauchny proezd, 6
+ Moscow 117246
+ RU
+
+E4-4C-C7 (hex) Ottomate International Pvt. Ltd.
+900000-9FFFFF (base 16) Ottomate International Pvt. Ltd.
+ Awfis Building, 5th Floor, Plot # 7, Sector-44
+ Gurgaon Haryana 122003
+ IN
+
+E4-4C-C7 (hex) Doowon Electronics & Telecom Co.,Ltd
+200000-2FFFFF (base 16) Doowon Electronics & Telecom Co.,Ltd
+ IT 301-408, Ssangyong 3Cha, Bucheon Technopark, 397, Seokcheon-ro, Ojeong-gu
+ Bucheon-si Gyeonggi-do 14449
+ KR
+
+E4-4C-C7 (hex) Alert Alarm AB
+000000-0FFFFF (base 16) Alert Alarm AB
+ Råsundavägen 4
+ Solna Stockholm 16967
+ SE
+
+E4-4C-C7 (hex) IAG GROUP LTD
+800000-8FFFFF (base 16) IAG GROUP LTD
+ IAG Industrial Park, Jiuwei, Xixiang Town, Baoan, Shenzhen, China.
+ shenzhen 518000
+ CN
+
+74-5B-C5 (hex) IRS Systementwicklung GmbH
+000000-0FFFFF (base 16) IRS Systementwicklung GmbH
+ Pfaffenthanner Weg, 5
+ Brennberg 93179
+ DE
+
+74-5B-C5 (hex) Fournie Grospaud Energie SASU
+A00000-AFFFFF (base 16) Fournie Grospaud Energie SASU
+ 220 rue du chene vert
+ LABEGE 31670
+ FR
+
+74-5B-C5 (hex) Haikou Frun Flash&Mcu Microcontrol Technology Development Co.,Ltd
+900000-9FFFFF (base 16) Haikou Frun Flash&Mcu Microcontrol Technology Development Co.,Ltd
+ 5 floor A1-9, A building, incubation center, Haikou national hi tech Development Zone
+ Haikou Hainan 570206
+ CN
+
+74-5B-C5 (hex) CELYSS SAS
+D00000-DFFFFF (base 16) CELYSS SAS
+ 7 allee des ginkgos
+ Bron 69500
+ FR
+
+FC-D2-B6 (hex) NREAL TECHNOLOGY LIMITED
+A00000-AFFFFF (base 16) NREAL TECHNOLOGY LIMITED
+ RM 1901,19/F LEE GARDEN ONE 33 HYSAN AVENUE CAUSEWAY BAY
+ HONG KONG 999077
+ HK
diff --git a/hwdb/ma-small.txt b/hwdb/ma-small.txt
index 6364a7e1a9..c1131adff3 100644
--- a/hwdb/ma-small.txt
+++ b/hwdb/ma-small.txt
@@ -116,12 +116,6 @@ FC1000-FC1FFF (base 16) InDiCor
Culross Fife KY12 8HL
GB
-70-B3-D5 (hex) APG Cash Drawer
-C9D000-C9DFFF (base 16) APG Cash Drawer
- 5250 Industrial Blvd NE
- Minneapolis MN 55421
- US
-
70-B3-D5 (hex) GLT Exports Ltd
34C000-34CFFF (base 16) GLT Exports Ltd
Detection House
@@ -2558,9 +2552,6 @@ BEA000-BEAFFF (base 16) Virtuosys Ltd
Malmö 211 33
SE
-70-B3-D5 (hex) Private
-591000-591FFF (base 16) Private
-
70-B3-D5 (hex) Vidisys GmbH
133000-133FFF (base 16) Vidisys GmbH
Rudolf-Diesel-Ring 30
@@ -2933,6 +2924,174 @@ DF1000-DF1FFF (base 16) CoXlab Inc.
70-B3-D5 (hex) Private
FFE000-FFEFFF (base 16) Private
+70-B3-D5 (hex) Private
+738000-738FFF (base 16) Private
+
+70-B3-D5 (hex) Private
+591000-591FFF (base 16) Private
+
+70-B3-D5 (hex) PSL ELEKTRONÄ°K SANAYÄ° VE TÄ°CARET A.S.
+510000-510FFF (base 16) PSL ELEKTRONÄ°K SANAYÄ° VE TÄ°CARET A.S.
+ Antalya OSB 3. Kısım 25. Cadde No:30
+ ANTALYA 07190
+ TR
+
+70-B3-D5 (hex) Amber Kinetics Inc
+05C000-05CFFF (base 16) Amber Kinetics Inc
+ 32920 Alvarado-Niles Rd #250
+ Union City CA 94587
+ US
+
+70-B3-D5 (hex) Toshiba Electron Tubes & Devices Co., Ltd.
+D28000-D28FFF (base 16) Toshiba Electron Tubes & Devices Co., Ltd.
+ 1385, Shimoishigami
+ Otawara-shi Tochigi 324-8550
+ JP
+
+70-B3-D5 (hex) Virtual Control Systems Ltd
+EC3000-EC3FFF (base 16) Virtual Control Systems Ltd
+ 27 Main Street, Overton
+ Morecambe Lancashire LA3 3HF
+ GB
+
+70-B3-D5 (hex) Shishido Electrostatic, Ltd.
+22A000-22AFFF (base 16) Shishido Electrostatic, Ltd.
+ 4-7-21 Chigasaki-higashi, Tsuzuki-ku
+ Yokohama Kanagawa Prefecture 224-0033
+ JP
+
+70-B3-D5 (hex) Datamars SA
+AA9000-AA9FFF (base 16) Datamars SA
+ Via Industria 16
+ Lamone 6814
+ CH
+
+70-B3-D5 (hex) Cardinal Scales Manufacturing Co
+30F000-30FFFF (base 16) Cardinal Scales Manufacturing Co
+ 203 East Daugherty Street
+ Webb City MO 64870
+ US
+
+70-B3-D5 (hex) Blue Skies Global LLC
+1D8000-1D8FFF (base 16) Blue Skies Global LLC
+ PO Box 796
+ Williams Bay WI 53191
+ US
+
+70-B3-D5 (hex) chiconypower
+3A0000-3A0FFF (base 16) chiconypower
+ 23F, No.69, Sec. 2, Guangfu Rd., Sanchong Dist., New Taipei City 241, Taiwan (R.O.C.)
+ New Taipei Taiwan 241
+ TW
+
+70-B3-D5 (hex) TAALEX Systemtechnik GmbH
+553000-553FFF (base 16) TAALEX Systemtechnik GmbH
+ Schubertstrasse 27
+ Stadtlohn NRW 48703
+ DE
+
+70-B3-D5 (hex) Unmukti Technology Pvt Ltd
+24A000-24AFFF (base 16) Unmukti Technology Pvt Ltd
+ 2/288, Vishwas Khand, Gomti Nagar
+ Lucknow Uttar Pradesh 226010
+ IN
+
+70-B3-D5 (hex) Coral Telecom Limited
+C8E000-C8EFFF (base 16) Coral Telecom Limited
+ E-2, Sector 63
+ Noida Uttar Pradesh 201301
+ IN
+
+70-B3-D5 (hex) LINEAGE POWER PVT LTD.,
+9C5000-9C5FFF (base 16) LINEAGE POWER PVT LTD.,
+ 30-A1, KIADB, 1ST PHASE INDUSTRIAL ESTATE,KUMBALGODU, BANGALORE-MYSORE ROAD
+ BANGALORE KARNATAKA 560074
+ IN
+
+70-B3-D5 (hex) Capgemini Netherlands
+0B5000-0B5FFF (base 16) Capgemini Netherlands
+ Reykjavikplein 1
+ Utrecht 3543KA
+ NL
+
+70-B3-D5 (hex) Audiodo AB
+1FF000-1FFFFF (base 16) Audiodo AB
+ Östra Varvsgatan 4
+ Malmö 21114
+ SE
+
+70-B3-D5 (hex) FARECO
+8AE000-8AEFFF (base 16) FARECO
+ 8 EUROPARC de la Sainte Victoire
+ MEYREUIL 13590
+ FR
+
+70-B3-D5 (hex) Magnetek
+C70000-C70FFF (base 16) Magnetek
+ N49W13650 Campbell Dr
+ Menomonee Falls WI 53051
+ US
+
+70-B3-D5 (hex) Applied Silver
+239000-239FFF (base 16) Applied Silver
+ 26254 Eden Landing Road
+ Hayward CA 94545
+ US
+
+70-B3-D5 (hex) 4Jtech s.r.o.
+16A000-16AFFF (base 16) 4Jtech s.r.o.
+ Ringhofferova 115/1
+ Prague Czech Republic 155 21
+ CZ
+
+70-B3-D5 (hex) Avid Controls Inc
+447000-447FFF (base 16) Avid Controls Inc
+ 41261 Park 290 Dr
+ WALLER TX 77484
+ US
+
+70-B3-D5 (hex) Camwell India LLP
+E78000-E78FFF (base 16) Camwell India LLP
+ Plot no 72/11/2, Ground Floor
+ Mundka DELHI 110041
+ IN
+
+70-B3-D5 (hex) Pano0ramic Power
+669000-669FFF (base 16) Pano0ramic Power
+ 15 Atir Yeda
+ Kfar Saba 4464312
+ IL
+
+70-B3-D5 (hex) Algodue Elettronica Srl
+89A000-89AFFF (base 16) Algodue Elettronica Srl
+ Via P. Gobetti, 16F
+ Maggiora NO 28014
+ IT
+
+70-B3-D5 (hex) MAC Solutions (UK) Ltd
+E6A000-E6AFFF (base 16) MAC Solutions (UK) Ltd
+ Units 6 & 7, Kingfisher Business Park, Arthur Street
+ Redditch Worcestershire B98 8LG
+ GB
+
+70-B3-D5 (hex) Microsoft Research
+4AC000-4ACFFF (base 16) Microsoft Research
+ 1 Microsoft Way
+ Redmond WA 98052
+ US
+
+70-B3-D5 (hex) BIGHOUSE.,INC.
+B80000-B80FFF (base 16) BIGHOUSE.,INC.
+ 72-11, Pyeongchangmunwha-ro
+ SEOUL KOREA 03011
+ KR
+
+70-B3-D5 (hex) Changshu Ruite Electric Co.,Ltd.
+391000-391FFF (base 16) Changshu Ruite Electric Co.,Ltd.
+ No.2,Qingdao Road,Yushan High-tech Industrial Development Zone
+ Changshu Jiangshu 215500
+ CN
+
70-B3-D5 (hex) Vocality international T/A Cubic
75F000-75FFFF (base 16) Vocality international T/A Cubic
Lydling Barn,Lydling Farm,, Puttenham Lane
@@ -3191,6 +3350,114 @@ C64000-C64FFF (base 16) SYS TEC electronic GmbH
San Francisco CA 94103-4938
US
+70-B3-D5 (hex) BLUE-SOLUTIONS CANADA INC.
+EE7000-EE7FFF (base 16) BLUE-SOLUTIONS CANADA INC.
+ 1560 RUE DE COULOMB
+ BOUCHERVILLE QUEBEC J4B 7Z7
+ CA
+
+70-B3-D5 (hex) AEV Broadcast Srl
+0EA000-0EAFFF (base 16) AEV Broadcast Srl
+ Via Della Tecnica, 33
+ Argelato BO - VAT IT03167451206 40050
+ IT
+
+70-B3-D5 (hex) Shenzhen Hui Rui Tianyan Technology Co., Ltd.
+FA5000-FA5FFF (base 16) Shenzhen Hui Rui Tianyan Technology Co., Ltd.
+ 7th Floor, Yuemei Building, Gaoxin South Road, Nanshan District
+ Shenzhen Guangdong 518000
+ CN
+
+70-B3-D5 (hex) LINEAGE POWER PVT LTD.,
+516000-516FFF (base 16) LINEAGE POWER PVT LTD.,
+ 30-A1, KIADB, 1ST PHASE INDUSTRIAL ESTATE,KUMBALGODU, BANGALORE-MYSORE ROAD
+ BANGALORE KARNATAKA 560074
+ IN
+
+70-B3-D5 (hex) Satixfy Israel Ltd.
+978000-978FFF (base 16) Satixfy Israel Ltd.
+ 12 Hamada st.
+ Rehovot 7670300
+ IL
+
+70-B3-D5 (hex) ANALOGICS TECH INDIA LTD
+B49000-B49FFF (base 16) ANALOGICS TECH INDIA LTD
+ PLOT # 9/10, NACHARAM INDUSTRIAL ESTATE
+ HYDERABAD TELANGANA 500076
+ IN
+
+70-B3-D5 (hex) BlueBox Video Limited
+4C6000-4C6FFF (base 16) BlueBox Video Limited
+ Drysdale View, Fenny Bentley
+ Ashbourne DE6 1LA
+ GB
+
+70-B3-D5 (hex) Ya Batho Trading (Pty) Ltd
+AE6000-AE6FFF (base 16) Ya Batho Trading (Pty) Ltd
+ 9 Estee Ackerman Street
+ Jet Park Gauteng 1462
+ ZA
+
+70-B3-D5 (hex) APG Cash Drawer, LLC
+C9D000-C9DFFF (base 16) APG Cash Drawer, LLC
+ 5250 Industrial Blvd NE
+ Minneapolis MN 55421
+ US
+
+70-B3-D5 (hex) APG Cash Drawer, LLC
+ACF000-ACFFFF (base 16) APG Cash Drawer, LLC
+ 5250 Industrial Blvd NE
+ Minneapolis MN 55421
+ US
+
+70-B3-D5 (hex) Armstrong International, Inc.
+15B000-15BFFF (base 16) Armstrong International, Inc.
+ 816 Maple Street
+ Three Rivers MI 49093
+ US
+
+70-B3-D5 (hex) aelettronica group srl
+9C4000-9C4FFF (base 16) aelettronica group srl
+ via matteotti,22
+ gaggiano milano 20083
+ IT
+
+70-B3-D5 (hex) Power Electronics Espana, S.L.
+AB2000-AB2FFF (base 16) Power Electronics Espana, S.L.
+ C/ Leonardo Da Vinci, 24-26
+ Paterna Valencia 46980
+ ES
+
+70-B3-D5 (hex) hiSky S.C.S LTD
+2E1000-2E1FFF (base 16) hiSky S.C.S LTD
+ 24 Ammal
+ Rosh Hain 4809268
+ IL
+
+70-B3-D5 (hex) IMP-TELEKOMUNIKACIJE DOO
+38D000-38DFFF (base 16) IMP-TELEKOMUNIKACIJE DOO
+ Volgina 15
+ Belgrade 11060
+ RS
+
+70-B3-D5 (hex) Shenzhen Luxurite Smart Home Ltd
+121000-121FFF (base 16) Shenzhen Luxurite Smart Home Ltd
+ 13th Floor, Kenuo Building, No. 7 road, TongGuan Street, GuangMing
+ Shenzhen GuangDong 518000
+ CN
+
+70-B3-D5 (hex) EGISTECH CO.,LTD.
+603000-603FFF (base 16) EGISTECH CO.,LTD.
+ 611 2Dong 98, Gasan digital 2-ro
+ Geumcheon-gu Seoul 08506
+ KR
+
+70-B3-D5 (hex) EXASCEND (Wuhan) Co., Ltd
+851000-851FFF (base 16) EXASCEND (Wuhan) Co., Ltd
+ West 2nd-3rd Floor, No. 2 Building, Guan Nan Industrial Park,Te 1 Hao, Gao Xing 2 Lu,Dong Hu New Technology Development District
+ Wuhan Hubei 430000
+ CN
+
70-B3-D5 (hex) Flintab AB
D60000-D60FFF (base 16) Flintab AB
Kabelvägen 4
@@ -4625,12 +4892,6 @@ F08000-F08FFF (base 16) Szabo Software & Engineering UK Ltd
Webb City MO 64870
US
-70-B3-D5 (hex) Elbit Systems of America - Fort Worth Operations
-B7E000-B7EFFF (base 16) Elbit Systems of America - Fort Worth Operations
- 4700 Marine Creek Parkway
- Fort Worth TX 76179
- US
-
70-B3-D5 (hex) Xiamen Point Circle Technologh Co,ltd
DB5000-DB5FFF (base 16) Xiamen Point Circle Technologh Co,ltd
S307SouthBuildingWeiYeBuildinPioneering Park,Huli District,Xia M XIAMEN
@@ -5135,9 +5396,6 @@ A4E000-A4EFFF (base 16) Array Technologies Inc.
Glastonbury 06033
US
-70-B3-D5 (hex) Private
-580000-580FFF (base 16) Private
-
70-B3-D5 (hex) Alma
BA9000-BA9FFF (base 16) Alma
4A, Bd de la Gare, Porte 1
@@ -5204,9 +5462,6 @@ B98000-B98FFF (base 16) GSF Corporation Pte Ltd
Krakow malopolska 31-989
PL
-70-B3-D5 (hex) Private
-E17000-E17FFF (base 16) Private
-
70-B3-D5 (hex) megatec electronic GmbH
8A8000-8A8FFF (base 16) megatec electronic GmbH
Lehenhammer 14
@@ -5237,9 +5492,6 @@ E17000-E17FFF (base 16) Private
Oak Ridge TN 37930
US
-70-B3-D5 (hex) Private
-A03000-A03FFF (base 16) Private
-
70-B3-D5 (hex) Xacti Corporation
828000-828FFF (base 16) Xacti Corporation
Tower East 28F, Umeda Sky bldg, 1-1-88, Oyodonaka, Kita-ku,
@@ -6245,9 +6497,6 @@ F06000-F06FFF (base 16) WARECUBE,INC
Rivalta Scrivia AL 15050
IT
-00-1B-C5 (hex) Private
-015000-015FFF (base 16) Private
-
70-B3-D5 (hex) Redcap Solutions s.r.o.
027000-027FFF (base 16) Redcap Solutions s.r.o.
Na ViniÄních Horách 16
@@ -6524,6 +6773,402 @@ BC1000-BC1FFF (base 16) Abionic
Reutlingen 72766
DE
+70-B3-D5 (hex) dA Tomato Limited
+966000-966FFF (base 16) dA Tomato Limited
+ 8/2 Paribag, Hatirpool, Motaleb Tower, Tower 1, 11A
+ Dhaka DAC 1000
+ BD
+
+70-B3-D5 (hex) Dr. Zinngrebe GmbH
+42E000-42EFFF (base 16) Dr. Zinngrebe GmbH
+ Schillerstraße 1/15
+ Ulm Baden-Württemberg 89077
+ DE
+
+70-B3-D5 (hex) Cooltera Limited
+DD5000-DD5FFF (base 16) Cooltera Limited
+ Fircroft House, Fircroft Way
+ Edenbridge Kent TN8 6EJ
+ GB
+
+70-B3-D5 (hex) TATTILE SRL
+937000-937FFF (base 16) TATTILE SRL
+ VIA DONIZETTI, 1/3/5
+ MAIRANO BRESCIA 25030
+ IT
+
+70-B3-D5 (hex) DAVE SRL
+189000-189FFF (base 16) DAVE SRL
+ VIA TALPONEDO 29/A
+ PORCIA PORDENONE 330850
+ IT
+
+70-B3-D5 (hex) MB connect line GmbH Fernwartungssysteme
+BD8000-BD8FFF (base 16) MB connect line GmbH Fernwartungssysteme
+ Winnettener Straße 6
+ Dinkelsbuehl Bavaria 91550
+ DE
+
+70-B3-D5 (hex) winsun AG
+F67000-F67FFF (base 16) winsun AG
+ Beeschi Mattenstrasse 2
+ Steg Wallis 3940
+ CH
+
+70-B3-D5 (hex) Daatrics LTD
+A5F000-A5FFFF (base 16) Daatrics LTD
+ 4th Floor, 86-90 Paul Street
+ LONDON EC2A 4NE
+ GB
+
+70-B3-D5 (hex) Videoport S.A.
+441000-441FFF (base 16) Videoport S.A.
+ Sigma Business Center, Building A Nivel 2
+ San Pedro San Jose 11501
+ CR
+
+70-B3-D5 (hex) Dokuen Co. Ltd.
+334000-334FFF (base 16) Dokuen Co. Ltd.
+ 12-3 minami-matsunoki-cho higashi-kujo minami-ku
+ kyoto 6018023
+ JP
+
+70-B3-D5 (hex) NIRIT- Xinwei Telecom Technology Co., Ltd.
+F27000-F27FFF (base 16) NIRIT- Xinwei Telecom Technology Co., Ltd.
+ 2-й КожуховÑкий проезд, д.12, ÑÑ‚Ñ€.2
+ Moscow 115432
+ RU
+
+70-B3-D5 (hex) AML
+759000-759FFF (base 16) AML
+ 2190 Regal Parkway
+ Euless TX 76040
+ US
+
+70-B3-D5 (hex) Beijing HuaLian Technology Co, Ltd.
+623000-623FFF (base 16) Beijing HuaLian Technology Co, Ltd.
+ Floor4 16C, north district of Ufida software park, No. 68 Beiqing road, HaiDian district.
+ Beijing Beijing 100094
+ CN
+
+70-B3-D5 (hex) Private
+B71000-B71FFF (base 16) Private
+
+70-B3-D5 (hex) MicroElectronics System Co.Ltd
+8DA000-8DAFFF (base 16) MicroElectronics System Co.Ltd
+ 29 uchihata-cho Nishinokyo Nakagyoku
+ Kyoto City Kyoto-fu 604-8411
+ JP
+
+70-B3-D5 (hex) Software Systems Plus
+7DC000-7DCFFF (base 16) Software Systems Plus
+ 9924 N. Ash Avenue
+ Kansas City 64157
+ US
+
+70-B3-D5 (hex) Telefire
+2E8000-2E8FFF (base 16) Telefire
+ 43 hasivim
+ Petah Tikva Israel 49000
+ IL
+
+70-B3-D5 (hex) Opti-Sciences, Inc.
+338000-338FFF (base 16) Opti-Sciences, Inc.
+ 8 Winn Ave
+ Hudson NH 03051
+ US
+
+70-B3-D5 (hex) eze System, Inc.
+2F5000-2F5FFF (base 16) eze System, Inc.
+ 785 Orchard Dr #100
+ Folsom CA 95630
+ US
+
+00-1B-C5 (hex) Corporate Systems Engineering
+015000-015FFF (base 16) Corporate Systems Engineering
+ 1215 Brookville Way
+ Indianapolis IN 46239
+ US
+
+70-B3-D5 (hex) Private
+580000-580FFF (base 16) Private
+
+70-B3-D5 (hex) WICELL TECHNOLOGY
+BB0000-BB0FFF (base 16) WICELL TECHNOLOGY
+ 61 Dien Bien Phu
+ Ho Chi Minh 700000
+ VN
+
+70-B3-D5 (hex) Waldo System
+CA1000-CA1FFF (base 16) Waldo System
+ 4th Floor, 658, Yangcheon-ro, Gangseo-gu,
+ Seoul 07554
+ KR
+
+70-B3-D5 (hex) DogWatch Inc
+302000-302FFF (base 16) DogWatch Inc
+ 10 Michigan Drive
+ Natick MA 01760
+ US
+
+70-B3-D5 (hex) Zumbach Electronic AG
+B10000-B10FFF (base 16) Zumbach Electronic AG
+ Hauptstrasse 93
+ Orpund Bern 2552
+ CH
+
+70-B3-D5 (hex) EvoLogics GmbH
+F9B000-F9BFFF (base 16) EvoLogics GmbH
+ Ackerstr. 76
+ Berlin 13355
+ DE
+
+70-B3-D5 (hex) SOREL GmbH Mikroelektronik
+A84000-A84FFF (base 16) SOREL GmbH Mikroelektronik
+ REME-Str. 12
+ Wetter 58300
+ DE
+
+70-B3-D5 (hex) Qingdao CNR HITACH Railway Signal&communication co.,ltd
+802000-802FFF (base 16) Qingdao CNR HITACH Railway Signal&communication co.,ltd
+ 231-2 Ruichang Road
+ Qingdao 266031
+ CN
+
+70-B3-D5 (hex) WiSuite USA
+19A000-19AFFF (base 16) WiSuite USA
+ 13201 Stephens Road Suite E
+ Warren MI 48089
+ US
+
+70-B3-D5 (hex) Lightdrop
+072000-072FFF (base 16) Lightdrop
+ MatiÄní 730/3
+ Ostrava 70200
+ CZ
+
+70-B3-D5 (hex) dds
+F21000-F21FFF (base 16) dds
+ 606, Woolim Lions Valley 2Cha, 2, Gasan digital 1-ro Geumcheon-gu
+ Seoul 08591
+ KR
+
+70-B3-D5 (hex) COPPERNIC SAS
+25F000-25FFFF (base 16) COPPERNIC SAS
+ 185 avenue Archimede
+ Aix en Provence 13857
+ FR
+
+70-B3-D5 (hex) Rhythm Engineering, LLC.
+57A000-57AFFF (base 16) Rhythm Engineering, LLC.
+ 11228 Thompson Ave.
+ Lenexa KS 66219
+ US
+
+70-B3-D5 (hex) Taejin InforTech
+A75000-A75FFF (base 16) Taejin InforTech
+ 40, Imi-ro, A-411
+ Uiwang-si Gyeonggi-do 16006
+ KR
+
+70-B3-D5 (hex) MEGGITT
+1EE000-1EEFFF (base 16) MEGGITT
+ 14600 MYFORD RD
+ IRVINE CA 92606
+ US
+
+70-B3-D5 (hex) Smashtag Ltd
+F6F000-F6FFFF (base 16) Smashtag Ltd
+ Unit B6 Beech House, Melbourn Science Park
+ Royston Hertfordshire SG8 6HB
+ GB
+
+70-B3-D5 (hex) YUYAMA MFG Co.,Ltd
+2DE000-2DEFFF (base 16) YUYAMA MFG Co.,Ltd
+ 3-3-1
+ TOYONAKASHI OSAKA 561-0841
+ JP
+
+70-B3-D5 (hex) CRDMDEVEOPPEMENTS
+B5F000-B5FFFF (base 16) CRDMDEVEOPPEMENTS
+ 13 Petit chemin de la generale
+ Villenave d'Ornon Gironde 33140
+ FR
+
+70-B3-D5 (hex) Open System Solutions Limited
+FAB000-FABFFF (base 16) Open System Solutions Limited
+ Unit 33, Mitchell Point, Ensign Way
+ Southampton Hampshire SO31 4RF
+ GB
+
+70-B3-D5 (hex) SOFTLAND INDIA LTD
+C29000-C29FFF (base 16) SOFTLAND INDIA LTD
+ #14A, KINFRA SMALL INDUSTRIES PARK, MENAMKULAM, KAZHAKOOTTAM
+ TRIVANDRUM KERALA 695586
+ IN
+
+70-B3-D5 (hex) Scame Sistemi srl
+F04000-F04FFF (base 16) Scame Sistemi srl
+ Via Lombardia 5
+ Arluno Milan 20010
+ IT
+
+70-B3-D5 (hex) SYS TEC electronic GmbH
+41B000-41BFFF (base 16) SYS TEC electronic GmbH
+ Am Windrad 2
+ Heinsdorfergrund D-08468
+ DE
+
+70-B3-D5 (hex) Alcohol Countermeasure Systems
+FD0000-FD0FFF (base 16) Alcohol Countermeasure Systems
+ 60 International Blvd
+ Toronto Ontario M9W 6J2
+ CA
+
+70-B3-D5 (hex) PEEK TRAFFIC
+0C7000-0C7FFF (base 16) PEEK TRAFFIC
+ 5401 N SAM HOUSTON PKWY W
+ HOUSTON 77086
+ US
+
+70-B3-D5 (hex) Project Service S.r.l.
+A2D000-A2DFFF (base 16) Project Service S.r.l.
+ Via Paderno 31/C
+ Seriate (BG) 24068
+ IT
+
+70-B3-D5 (hex) SwineTech, Inc.
+DC2000-DC2FFF (base 16) SwineTech, Inc.
+ 230 2nd Street SE, Ste 302
+ Cedar Rapids IA 52401
+ US
+
+70-B3-D5 (hex) OSMOZIS
+A9B000-A9BFFF (base 16) OSMOZIS
+ 7 AVENUE DE L'EUROPE
+ CLAPIERS LANGUEDOC ROUSSSILLON 34830
+ FR
+
+70-B3-D5 (hex) Electrónica Falcón S.A.U
+36E000-36EFFF (base 16) Electrónica Falcón S.A.U
+ Polígono Industrial Escopar, Calle E, Nº 1
+ Peralta Navarra 31350
+ ES
+
+70-B3-D5 (hex) WAVES SYSTEM
+CE4000-CE4FFF (base 16) WAVES SYSTEM
+ La Ville en Bois
+ BOUAYE Loire Atlantique 44830
+ FR
+
+70-B3-D5 (hex) HeadsafeIP PTY LTD
+800000-800FFF (base 16) HeadsafeIP PTY LTD
+ 231 Birrell st
+ bronte nsw 2024
+ AU
+
+70-B3-D5 (hex) Tieline Research Pty Ltd
+FCB000-FCBFFF (base 16) Tieline Research Pty Ltd
+ PO Box 2092
+ MALAGA Western Australia 6944
+ AU
+
+70-B3-D5 (hex) LG Electronics
+4F1000-4F1FFF (base 16) LG Electronics
+ Science Park W5, 10, Magokjungang 10-ro, Gangseo-gu
+ Seoul 07796
+ KR
+
+70-B3-D5 (hex) Zhiye Electronics Co., Ltd.
+8E2000-8E2FFF (base 16) Zhiye Electronics Co., Ltd.
+ No. 1117, Pioneer Road, High-tech Zone
+ Jinan City Shandong Province 250101
+ CN
+
+70-B3-D5 (hex) ifak technology + service GmbH
+264000-264FFF (base 16) ifak technology + service GmbH
+ Ludwig-Erhard-Allee 10
+ Karlsruhe 76131
+ DE
+
+70-B3-D5 (hex) ABB S.p.A.
+5A7000-5A7FFF (base 16) ABB S.p.A.
+ Via Pisani 16
+ Milano MI 20124
+ IT
+
+70-B3-D5 (hex) OOO Alyans
+107000-107FFF (base 16) OOO Alyans
+ 9 maya, 20
+ Krasnoyarsk Krasnoyarski Krai 660125
+ RU
+
+70-B3-D5 (hex) Alere Technologies AS
+91C000-91CFFF (base 16) Alere Technologies AS
+ Kjelsaasveien 161
+ Oslo Oslo 0382
+ NO
+
+70-B3-D5 (hex) Transas Marine Limited
+ED8000-ED8FFF (base 16) Transas Marine Limited
+ 10 Eastgate Avenue, Eastgate Business Park
+ Little Island, Cork 0
+ IE
+
+70-B3-D5 (hex) Private
+A03000-A03FFF (base 16) Private
+
+70-B3-D5 (hex) SA Photonics
+E17000-E17FFF (base 16) SA Photonics
+ 120 Knowles Drive
+ Los Gatos CA 95032
+ US
+
+70-B3-D5 (hex) Private
+9EE000-9EEFFF (base 16) Private
+
+70-B3-D5 (hex) Elbit Systems of America
+B7E000-B7EFFF (base 16) Elbit Systems of America
+ 4700 Marine Creek Parkway
+ Fort Worth TX 76179
+ US
+
+70-B3-D5 (hex) QuestHouse, Inc.
+84B000-84BFFF (base 16) QuestHouse, Inc.
+ Rm 204, 5 B/D, 20 Techno 1-ro, Yuseong-gu
+ Daejeon 34016
+ KR
+
+70-B3-D5 (hex) Monnit Corporation
+D1A000-D1AFFF (base 16) Monnit Corporation
+ 3400 S West Temple
+ Taylorsville UT 84115
+ US
+
+70-B3-D5 (hex) Smart Controls LLC
+199000-199FFF (base 16) Smart Controls LLC
+ 10000 St. Clair Ave.
+ Fairview Heights IL 62208
+ US
+
+70-B3-D5 (hex) ENTEC Electric & Electronic Co., LTD.
+1DF000-1DFFFF (base 16) ENTEC Electric & Electronic Co., LTD.
+ 78-2 Buncheon-ri, Bongdam-eup
+ Hwaseong-city Gyungki-do 445-894
+ KR
+
+70-B3-D5 (hex) GMI Ltd
+C93000-C93FFF (base 16) GMI Ltd
+ Inchinnan Business Park
+ Renfre PA4 9RG
+ GB
+
+70-B3-D5 (hex) YUYAMA MFG Co.,Ltd
+150000-150FFF (base 16) YUYAMA MFG Co.,Ltd
+ 3-3-1
+ TOYONAKASHI OSAKA 561-0841
+ JP
+
70-B3-D5 (hex) Schildknecht AG
494000-494FFF (base 16) Schildknecht AG
Haugweg 26
@@ -6788,12 +7433,6 @@ D57000-D57FFF (base 16) TRIUMPH BOARD a.s.
Santander Cantabria 39011
ES
-70-B3-D5 (hex) ComNav Technology Ltd.
-E3B000-E3BFFF (base 16) ComNav Technology Ltd.
- Building E, No.50 Alley 2080 Lianhua Road
- Shanghai Shanghai 201103
- CN
-
70-B3-D5 (hex) StromIdee GmbH
703000-703FFF (base 16) StromIdee GmbH
Walzmühlestrasse 51
@@ -7889,12 +8528,6 @@ FE2000-FE2FFF (base 16) Galileo Tıp Teknolojileri San. ve Tic. A.S.
Malatya 44000
TR
-70-B3-D5 (hex) Elbit Systems of America - Fort Worth Operations
-C24000-C24FFF (base 16) Elbit Systems of America - Fort Worth Operations
- 4700 Marine Creek Parkway
- Fort Worth TX 76179
- US
-
70-B3-D5 (hex) SMTC Corporation
AC6000-AC6FFF (base 16) SMTC Corporation
2302 Trade Zone Blvd
@@ -8963,12 +9596,6 @@ C42000-C42FFF (base 16) CRDE
Wichita KS 67203
US
-70-B3-D5 (hex) Algodue Elettronica Srl
-49B000-49BFFF (base 16) Algodue Elettronica Srl
- Via Passerina 3/A
- Fontaneto d'Agogna Novara 28010
- IT
-
70-B3-D5 (hex) Shenzhen INVT Electric Co.,Ltd
1D0000-1D0FFF (base 16) Shenzhen INVT Electric Co.,Ltd
INVT Bldg., GaoFa Scientific Park, Longjing, Nanshan, Shenzhen.
@@ -9200,9 +9827,6 @@ FFF000-FFFFFF (base 16) Private
Pärnu 80016
EE
-70-B3-D5 (hex) Private
-6CF000-6CFFFF (base 16) Private
-
70-B3-D5 (hex) Touchnet/OneCard
478000-478FFF (base 16) Touchnet/OneCard
2115 Chapman Rd. Suite 159
@@ -9218,21 +9842,12 @@ FFF000-FFFFFF (base 16) Private
70-B3-D5 (hex) Private
4F8000-4F8FFF (base 16) Private
-70-B3-D5 (hex) Private
-376000-376FFF (base 16) Private
-
70-B3-D5 (hex) ENTEC Electric & Electronic Co., LTD.
92B000-92BFFF (base 16) ENTEC Electric & Electronic Co., LTD.
78-2 Buncheon-ri, Bongdam-eup
Hwaseong-city Gyungki-do 445-894
KR
-70-B3-D5 (hex) CSS Inc.
-7F9000-7F9FFF (base 16) CSS Inc.
- 6030 S 58th ST, STE C
- Litcoln NE 68516
- US
-
70-B3-D5 (hex) S.I.C.E.S. srl
EA7000-EA7FFF (base 16) S.I.C.E.S. srl
Via Molinello, 8B
@@ -9683,6 +10298,459 @@ EB4000-EB4FFF (base 16) Robotic Research, LLC
Lublin 20-234
PL
+70-B3-D5 (hex) TEKVEL Ltd.
+1DC000-1DCFFF (base 16) TEKVEL Ltd.
+ Federativny prospekt, 5-1-5
+ Moscow Moscow 111399
+ RU
+
+70-B3-D5 (hex) Rehwork GmbH
+5A5000-5A5FFF (base 16) Rehwork GmbH
+ Ivo-Hauptmann-Ring 14
+ Hamburg 22159
+ DE
+
+70-B3-D5 (hex) Dynamic Perspective GmbH
+2A8000-2A8FFF (base 16) Dynamic Perspective GmbH
+ Wehlistrasse 29/1/1
+ Vienna 1200
+ AT
+
+70-B3-D5 (hex) biosilver .co.,ltd
+43F000-43FFFF (base 16) biosilver .co.,ltd
+ 2-14-4, shinyokohama
+ yokohama kanagawa 2220033
+ JP
+
+70-B3-D5 (hex) Avidbots Corporation
+B4D000-B4DFFF (base 16) Avidbots Corporation
+ 975 Bleams Road, Unit 5
+ Kitchener Ontario N2E 3Z5
+ CA
+
+70-B3-D5 (hex) Veo Technologies
+A9C000-A9CFFF (base 16) Veo Technologies
+ Skyttegade 7, 3. tv
+ Copenhagen Denmark 2200
+ DK
+
+70-B3-D5 (hex) DOBE Computing
+9DF000-9DFFFF (base 16) DOBE Computing
+ Dong-Bu 61 Song-Dam Univ. Chang-Ui 203
+ Yong-In 17145
+ KR
+
+70-B3-D5 (hex) Solar RIg Technologies
+293000-293FFF (base 16) Solar RIg Technologies
+ 651 Garden Street
+ Carlstadt NJ 07072
+ US
+
+70-B3-D5 (hex) Sanmina Israel
+3D4000-3D4FFF (base 16) Sanmina Israel
+ Koren Industrial Zone , POBox 102
+ Maalot Israel 2101002
+ IL
+
+70-B3-D5 (hex) merkur Funksysteme AG
+B0F000-B0FFFF (base 16) merkur Funksysteme AG
+ Wassergrabe 14
+ Sursee 6210
+ CH
+
+70-B3-D5 (hex) Aural Ltd
+76C000-76CFFF (base 16) Aural Ltd
+ Aural Ltd, 3b Wellington Close, Parkgate Industrial Estate
+ KNUTSFORD Cheshire WA16 8XL
+ GB
+
+70-B3-D5 (hex) HERUTU ELECTRONICS CORPORATION
+E32000-E32FFF (base 16) HERUTU ELECTRONICS CORPORATION
+ 62-1 Toyooka-cho, Kita-ku
+ Hamamatsu Shizuoka 433-8103
+ JP
+
+70-B3-D5 (hex) BT9
+6B8000-6B8FFF (base 16) BT9
+ Dolev 33
+ Tefen 2495900
+ IL
+
+70-B3-D5 (hex) boekel
+AF8000-AF8FFF (base 16) boekel
+ 855 pennsylvania blvd
+ feasterville PA 19053
+ US
+
+70-B3-D5 (hex) Private
+F7C000-F7CFFF (base 16) Private
+
+70-B3-D5 (hex) Cambria Corporation
+B6B000-B6BFFF (base 16) Cambria Corporation
+ 1328 North 128th Street
+ Seattle WA 98133
+ US
+
+70-B3-D5 (hex) Fluid Components Intl
+9F9000-9F9FFF (base 16) Fluid Components Intl
+ 1755 La Costa Meadows Dr.
+ San Marcos CA 92078
+ US
+
+70-B3-D5 (hex) Ahrens & Birner Company GmbH
+6D2000-6D2FFF (base 16) Ahrens & Birner Company GmbH
+ Virchowstreet 19/19a
+ Nuremberg 90409
+ DE
+
+70-B3-D5 (hex) Engage Technologies
+977000-977FFF (base 16) Engage Technologies
+ 7041 Boone Avenue North
+ Brooklyn Park MN 55428
+ US
+
+70-B3-D5 (hex) CODESYSTEM Co.,Ltd
+FC0000-FC0FFF (base 16) CODESYSTEM Co.,Ltd
+ #705, Namsung Plaza(Ace9), 130 Digitalro, Keumchon-Gu
+ Seoul 08589
+ KR
+
+70-B3-D5 (hex) PEEK TRAFFIC
+718000-718FFF (base 16) PEEK TRAFFIC
+ 5401 N SAM HOUSTON PKWY W
+ HOUSTON 77086
+ US
+
+70-B3-D5 (hex) Mighty Cube Co., Ltd.
+403000-403FFF (base 16) Mighty Cube Co., Ltd.
+ UNIZO Nihonbashihoncho 3-CHome Building 5F 3-8-4 Nihonbashi-honcho
+ Chuo-ku TOKYO 103-0023
+ JP
+
+70-B3-D5 (hex) Communication Systems Solutions
+7F9000-7F9FFF (base 16) Communication Systems Solutions
+ 6030 S 58th ST, STE C
+ Litcoln NE 68516
+ US
+
+70-B3-D5 (hex) Gogo BA
+1E8000-1E8FFF (base 16) Gogo BA
+ 105 Edgeview Drive
+ Broomfield CO 80021
+ US
+
+70-B3-D5 (hex) Private
+376000-376FFF (base 16) Private
+
+70-B3-D5 (hex) Zaklad Energoelektroniki Twerd
+3FA000-3FAFFF (base 16) Zaklad Energoelektroniki Twerd
+ Aleksandrowska 28/30
+ Torun 87100
+ PL
+
+70-B3-D5 (hex) KOMZ - IZMERENIYA
+45B000-45BFFF (base 16) KOMZ - IZMERENIYA
+ Pravobulachnaya 35/2
+ Kazan 420111
+ RU
+
+70-B3-D5 (hex) Voleatech GmbH
+DA3000-DA3FFF (base 16) Voleatech GmbH
+ Grathwohlstr. 5
+ Reutlingen 72762
+ DE
+
+70-B3-D5 (hex) Energy Wall
+990000-990FFF (base 16) Energy Wall
+ 1002 New Holland Ave
+ Lancaster PA 17601
+ US
+
+70-B3-D5 (hex) KST technology
+8BB000-8BBFFF (base 16) KST technology
+ KST B/D 4-5, Wiryeseong-daero 12-gil
+ Songpa-gu Seoul 05636
+ KR
+
+70-B3-D5 (hex) Consarc Corporation
+BD6000-BD6FFF (base 16) Consarc Corporation
+ 100 Indel Ave
+ Rancocas NJ 08073
+ US
+
+70-B3-D5 (hex) Digital Media Professionals
+BC4000-BC4FFF (base 16) Digital Media Professionals
+ 16F NakanoCentralParkSouth,4-10-2 Nakano
+ Nakano-ku Tokyo 164-0001
+ JP
+
+70-B3-D5 (hex) Vision4ce Ltd
+D01000-D01FFF (base 16) Vision4ce Ltd
+ Unit 4, Wokingham Commercial Centre
+ Wokingham Berkshire RG41 2RF
+ GB
+
+70-B3-D5 (hex) AUTOMATIZACION Y CONECTIVIDAD SA DE CV
+59B000-59BFFF (base 16) AUTOMATIZACION Y CONECTIVIDAD SA DE CV
+ LA GARITA ANDADOR 6 DUPLEX 1 CASA 2
+ CDMX TLALPAN 14390
+ MX
+
+70-B3-D5 (hex) HD Vision Systems GmbH
+F18000-F18FFF (base 16) HD Vision Systems GmbH
+ Berliner Str. 43
+ Heidelberg 69120
+ DE
+
+70-B3-D5 (hex) ANYROAM
+003000-003FFF (base 16) ANYROAM
+ 2450 E.J. Chapman DR
+ Knoxville TN 37996
+ US
+
+70-B3-D5 (hex) Airmar Technology Corp
+08C000-08CFFF (base 16) Airmar Technology Corp
+ 35 Meadowbrook Dr
+ Milford NH 03055
+ US
+
+70-B3-D5 (hex) AvMap srlu
+048000-048FFF (base 16) AvMap srlu
+ Viale Zaccagna 6
+ Carrara 54033
+ IT
+
+70-B3-D5 (hex) Techno Broad,Inc
+512000-512FFF (base 16) Techno Broad,Inc
+ 2-12-12, shin-chiba,chuo-ku,
+ chiba chiba 2600031
+ JP
+
+70-B3-D5 (hex) Advice
+926000-926FFF (base 16) Advice
+ 16 Atir Yeda St
+ Kfar Saba Not applicable 4464321
+ IL
+
+70-B3-D5 (hex) RF Code
+94B000-94BFFF (base 16) RF Code
+ 2600 Longhorn Blvd. ste 111
+ Austin 78758
+ US
+
+70-B3-D5 (hex) IPG Photonics Corporation
+2E6000-2E6FFF (base 16) IPG Photonics Corporation
+ 377 Simarano Drive
+ Marlborough MA 01752
+ US
+
+70-B3-D5 (hex) AVL DiTEST
+86B000-86BFFF (base 16) AVL DiTEST
+ Fahrzeugdiagnose GmbH
+ Graz Steiermark 8020
+ AT
+
+70-B3-D5 (hex) DEVAU Lemppenau GmbH
+4A2000-4A2FFF (base 16) DEVAU Lemppenau GmbH
+ Wolbringstrasse 12
+ Bocholt NRW 46397
+ DE
+
+70-B3-D5 (hex) Plexus
+B2D000-B2DFFF (base 16) Plexus
+ 5511 Capital Center Drive, Ste 600
+ Raleigh NC 27606
+ US
+
+70-B3-D5 (hex) National Radio & Telecommunication Corporation - NRTC
+949000-949FFF (base 16) National Radio & Telecommunication Corporation - NRTC
+ T&T Complex, Haripur
+ Haripur KPK 22620
+ PK
+
+70-B3-D5 (hex) AixControl GmbH
+972000-972FFF (base 16) AixControl GmbH
+ Sonnenweg 15
+ Aachen NRW 52070
+ DE
+
+70-B3-D5 (hex) ComNav Technology Ltd.
+E3B000-E3BFFF (base 16) ComNav Technology Ltd.
+ Buliding 2,No. 618 Chengliu Middle Road
+ JiaDing District Shanghai 201801
+ CN
+
+70-B3-D5 (hex) Pengo Technology Co., Ltd
+DB7000-DB7FFF (base 16) Pengo Technology Co., Ltd
+ No. 13, Alley 7, Lane 533, Rongxing Road, Bade District
+ Taoyuan City 33463
+ TW
+
+70-B3-D5 (hex) Position Imaging
+A5D000-A5DFFF (base 16) Position Imaging
+ 22 marin way unit 1
+ stratham NH 03885
+ US
+
+70-B3-D5 (hex) Kniggendorf + Kögler Security GmbH
+3DD000-3DDFFF (base 16) Kniggendorf + Kögler Security GmbH
+ Hamburger Straße 4
+ Laatzen 30880
+ DE
+
+70-B3-D5 (hex) SHENZHEN WITLINK CO.,LTD.
+CF0000-CF0FFF (base 16) SHENZHEN WITLINK CO.,LTD.
+ 1211 Room,Satellite building,Southern Hi-Tech Zone,Nanshan District
+ ShenZhen Guangdong 518000
+ CN
+
+70-B3-D5 (hex) Elcoma
+31F000-31FFFF (base 16) Elcoma
+ Rua Barbosa Lima, 149
+ Recife Pernambuco 50030-330
+ BR
+
+70-B3-D5 (hex) ZAO ZEO
+B60000-B60FFF (base 16) ZAO ZEO
+ Khachaturiana 14a
+ Moscow 127562
+ RU
+
+70-B3-D5 (hex) Grupo Epelsa S.L.
+7DA000-7DAFFF (base 16) Grupo Epelsa S.L.
+ C/ Punto Net,3
+ Alcala de Henares Madrid 28805
+ ES
+
+70-B3-D5 (hex) DIEHL Connectivity Solutions
+1F1000-1F1FFF (base 16) DIEHL Connectivity Solutions
+ Stephanstraße 49
+ Nürnberg Bayern 90478
+ DE
+
+70-B3-D5 (hex) Agrident GmbH
+EDE000-EDEFFF (base 16) Agrident GmbH
+ Steinklippenstrasse 10
+ Barsinghausen Low Saxonia 30890
+ DE
+
+70-B3-D5 (hex) Algodue Elettronica Srl
+49B000-49BFFF (base 16) Algodue Elettronica Srl
+ Via Passerina 3/A
+ Fontaneto d'Agogna Novara 28010
+ IT
+
+70-B3-D5 (hex) IMP-Computer Systems
+354000-354FFF (base 16) IMP-Computer Systems
+ Volgina 15
+ Belgrade 11000
+ RS
+
+70-B3-D5 (hex) Elbit Systems of America
+C24000-C24FFF (base 16) Elbit Systems of America
+ 4700 Marine Creek Parkway
+ Fort Worth TX 76179
+ US
+
+70-B3-D5 (hex) AT-Automation Technology GmbH
+345000-345FFF (base 16) AT-Automation Technology GmbH
+ Hermann-Boessow-Str. 6-8
+ Bad Oldesloe D-23843
+ DE
+
+70-B3-D5 (hex) Airity Technologies Inc.
+545000-545FFF (base 16) Airity Technologies Inc.
+ 1505 Woodside Rd
+ Redwood City CA 94061
+ US
+
+70-B3-D5 (hex) Shenzhen Wesion Technology Co., Ltd
+863000-863FFF (base 16) Shenzhen Wesion Technology Co., Ltd
+ A511, Mingyou Purchasing Center, Baoyuan Road, Xixiang Street
+ Shenzhen Guangdong 518102
+ CN
+
+70-B3-D5 (hex) OutdoorLink
+6A3000-6A3FFF (base 16) OutdoorLink
+ 3058 Leeman Ferry Rd
+ Huntsville AL 35801
+ US
+
+70-B3-D5 (hex) Private
+6CF000-6CFFFF (base 16) Private
+
+70-B3-D5 (hex) ProHound Controles Eirelli
+0D0000-0D0FFF (base 16) ProHound Controles Eirelli
+ Rua Felipe José de Figueiredo, 45
+ São Paulo São Paulo 03807300
+ BR
+
+70-B3-D5 (hex) Aethera Technologies
+9B9000-9B9FFF (base 16) Aethera Technologies
+ 63 Crane Lake Drive
+ Halifax NS B3S 1B5
+ CA
+
+70-B3-D5 (hex) Sasken Technologies Ltd
+BCD000-BCDFFF (base 16) Sasken Technologies Ltd
+ 139/25, Amarjyothi Layout, Domlur
+ Bangalore 560071
+ IN
+
+70-B3-D5 (hex) Beijing Huanyu Zhilian Science &Technology Co., Ltd.
+DF5000-DF5FFF (base 16) Beijing Huanyu Zhilian Science &Technology Co., Ltd.
+ 2/F 202-030, Building 2, No. 1, Gaolizhang Road, Haidian District,Beijing
+ Beijing Beijing 100095
+ CN
+
+70-B3-D5 (hex) Nippon Marine Enterprises, Ltd.
+533000-533FFF (base 16) Nippon Marine Enterprises, Ltd.
+ 14-1, Ogawa-cho
+ Yokosuka Kanagawa 2380004
+ JP
+
+70-B3-D5 (hex) Slan
+788000-788FFF (base 16) Slan
+ 11 rue de la Senette
+ Carrieres sous Poissy 78955
+ FR
+
+70-B3-D5 (hex) Fiber Optika Technologies Pvt. Ltd.
+47E000-47EFFF (base 16) Fiber Optika Technologies Pvt. Ltd.
+ #38, 22nd Main, 14th Cross, Padmanabhanagar
+ Bangalore Karnataka 560070
+ IN
+
+70-B3-D5 (hex) Magnamed Tecnologia Medica S/A
+648000-648FFF (base 16) Magnamed Tecnologia Medica S/A
+ Rua Des. Eliseu Guilherme, 292 6 Andar
+ Sao Paulo SP 04004-030
+ BR
+
+70-B3-D5 (hex) Niron systems & Projects
+FED000-FEDFFF (base 16) Niron systems & Projects
+ PO Box 8546
+ Netania 4250407
+ IL
+
+70-B3-D5 (hex) TEX COMPUTER SRL
+1E1000-1E1FFF (base 16) TEX COMPUTER SRL
+ VIA MERCADANTE 35
+ CATTOLICA RIMINI 47841
+ IT
+
+70-B3-D5 (hex) Independent Project Engineering Lmited
+E3C000-E3CFFF (base 16) Independent Project Engineering Lmited
+ Unit 1, Saxon Way
+ Melbourn Hertfordshire SG8 6DN
+ GB
+
+70-B3-D5 (hex) Paratec Ltd.
+AEC000-AECFFF (base 16) Paratec Ltd.
+ P.O.Box 3574
+ Haifa 3103402
+ IL
+
70-B3-D5 (hex) Innitive B.V.
66B000-66BFFF (base 16) Innitive B.V.
Brouwerijstraat 20
@@ -12431,9 +13499,6 @@ D09000-D09FFF (base 16) Rishaad Brown
Paterna Valencia 46980
ES
-70-B3-D5 (hex) Private
-1D7000-1D7FFF (base 16) Private
-
70-B3-D5 (hex) Suprock Technologies
613000-613FFF (base 16) Suprock Technologies
45 Scott Hill Rd
@@ -12950,12 +14015,429 @@ BCB000-BCBFFF (base 16) Smart Vision Lights
CHINO Nagano 3910005
JP
+70-B3-D5 (hex) WAVE
+ED7000-ED7FFF (base 16) WAVE
+ 12078 University City Boulevard
+ Harrisburg NC 28075
+ US
+
70-B3-D5 (hex) HoseoTelnet Inc...
11B000-11BFFF (base 16) HoseoTelnet Inc...
- Hoseo Plaza B/D 7F, 416, Gangseo-ro, Gangseo-gu
- Seoul 07583
+ Hoseo Plaza B/D 7F, 416
+ Gangseo-ro, Gangseo-gu Seoul 07583
KR
+70-B3-D5 (hex) Laser Imagineering Vertriebs GmbH
+FDD000-FDDFFF (base 16) Laser Imagineering Vertriebs GmbH
+ Rudolf-Diesel-Weg 5
+ Moelln 23879
+ DE
+
+70-B3-D5 (hex) NAC Planning Co., Ltd.
+87F000-87FFFF (base 16) NAC Planning Co., Ltd.
+ NREG Akihabara Bldg 2F, 1-8-13, Sotokanda
+ Chiyoda-ku Tokyo 101-0021
+ JP
+
+70-B3-D5 (hex) Reckeen HDP Media sp. z o.o. sp. k.
+3A1000-3A1FFF (base 16) Reckeen HDP Media sp. z o.o. sp. k.
+ Robotnicza 68c
+ Wroclaw 53608
+ PL
+
+70-B3-D5 (hex) Orion Corporation
+97A000-97AFFF (base 16) Orion Corporation
+ 2nd Fl., Shin-Showa No.5 Bldg., 1-5-15 Higashi-Sakata
+ Kimitsu Chiba 299-1144
+ JP
+
+70-B3-D5 (hex) Schneider Electric Motion USA
+8D7000-8D7FFF (base 16) Schneider Electric Motion USA
+ 370 N. Main St.
+ Marlborough CT 06447
+ US
+
+70-B3-D5 (hex) Vocality international T/A Cubic
+4EA000-4EAFFF (base 16) Vocality international T/A Cubic
+ Lydling Barn,Lydling Farm,, Puttenham Lane
+ Godalming Surrey gu8 6ap
+ GB
+
+70-B3-D5 (hex) MG s.r.l.
+688000-688FFF (base 16) MG s.r.l.
+ via Monte Bianco, 1
+ Solbiate Olona VA 21058
+ IT
+
+70-B3-D5 (hex) Liaoyun Information Technology Co., Ltd.
+40E000-40EFFF (base 16) Liaoyun Information Technology Co., Ltd.
+ Floor 5, Building 8, No. 690 Bibo Road
+ Shanghai 201203
+ CN
+
+70-B3-D5 (hex) Last Mile Gear
+EBA000-EBAFFF (base 16) Last Mile Gear
+ 1119 11th Ave
+ LONGVIEW WA 98632
+ US
+
+70-B3-D5 (hex) ENTEC Electric & Electronic Co., LTD.
+E84000-E84FFF (base 16) ENTEC Electric & Electronic Co., LTD.
+ 78-2 Buncheon-ri, Bongdam-eup
+ Hwaseong-city Gyungki-do 445-894
+ KR
+
+70-B3-D5 (hex) eWireless
+BC3000-BC3FFF (base 16) eWireless
+ 4629 n capitol ave
+ Indianapolis IN 46208
+ US
+
+70-B3-D5 (hex) LSC Lighting Systems (Aust) Pty Ltd
+CC2000-CC2FFF (base 16) LSC Lighting Systems (Aust) Pty Ltd
+ 65-67 Discovery Road
+ Dandenong South Victoria 3175
+ AU
+
+70-B3-D5 (hex) IoT Routers Limited
+F51000-F51FFF (base 16) IoT Routers Limited
+ The Barn, 22 Brackendale
+ Bradford West Yorkshire BD10 0SJ
+ GB
+
+70-B3-D5 (hex) APG Cash Drawer, LLC
+BCF000-BCFFFF (base 16) APG Cash Drawer, LLC
+ 5250 Industrial Blvd NE
+ Minneapolis MN 55421
+ US
+
+70-B3-D5 (hex) EIDOS s.r.l.
+890000-890FFF (base 16) EIDOS s.r.l.
+ via dell'Industria, 11 Z.I. Fontaneto
+ CHIERI Turin 10023
+ IT
+
+70-B3-D5 (hex) Samwell International Inc
+A71000-A71FFF (base 16) Samwell International Inc
+ No. 317-1, Sec.2, An Kang Rd., Hsintien Dist
+ New Taipei City 231
+ TW
+
+70-B3-D5 (hex) SODAQ
+ADC000-ADCFFF (base 16) SODAQ
+ Laapersveld 75
+ Hilversum - 1213 VB
+ NL
+
+70-B3-D5 (hex) Microvideo
+4E0000-4E0FFF (base 16) Microvideo
+ Copley Hill Business Park, Cambridge Road
+ Babraham CB22 3GN
+ GB
+
+70-B3-D5 (hex) Communication Systems Solutions
+E0C000-E0CFFF (base 16) Communication Systems Solutions
+ 6030 S. 58th Street
+ Lincoln NE 68516
+ US
+
+70-B3-D5 (hex) GoTrustID Inc.
+B38000-B38FFF (base 16) GoTrustID Inc.
+ 800 N. State Street, Suite 402, Dover
+ County of Kent DE 19901
+ US
+
+70-B3-D5 (hex) Shengli Financial Software Development
+F22000-F22FFF (base 16) Shengli Financial Software Development
+ 3766 Nanhuan Road, Binjiang
+ Hangzhou 310051
+ CN
+
+70-B3-D5 (hex) Peter Huber Kaeltemaschinenbau AG
+08B000-08BFFF (base 16) Peter Huber Kaeltemaschinenbau AG
+ Werner-von-Siemens-Str. 1
+ Offenburg Ba-Wue 77656
+ DE
+
+70-B3-D5 (hex) Bacsoft
+4B3000-4B3FFF (base 16) Bacsoft
+ Hazarhan 13
+ Kiryat Gat 8258112
+ IL
+
+70-B3-D5 (hex) Access Protocol Pty Ltd
+686000-686FFF (base 16) Access Protocol Pty Ltd
+ 45 Sarich Court
+ Osborne Park Western Australia 6017
+ AU
+
+70-B3-D5 (hex) Biotage Sweden AB
+4D2000-4D2FFF (base 16) Biotage Sweden AB
+ Vimpelgatan 5
+ Uppsala 753 18
+ SE
+
+70-B3-D5 (hex) Franke Aquarotter GmbH
+BB6000-BB6FFF (base 16) Franke Aquarotter GmbH
+ Parkstraße 1-5
+ Ludwigsfelde 14974
+ DE
+
+70-B3-D5 (hex) Kawasaki Robot Service,Ltd.
+FEE000-FEEFFF (base 16) Kawasaki Robot Service,Ltd.
+ 2-1-9,Takatsukadai,Nishi-ku
+ Kobe Hyogo 651-2271
+ JP
+
+70-B3-D5 (hex) Alcodex Technologies Private Limited
+098000-098FFF (base 16) Alcodex Technologies Private Limited
+ 7/719-B, PLOT 180, Mavelipuram colony, Kakkanad
+ ernakulam Kerala 682030
+ IN
+
+70-B3-D5 (hex) Piranha EMS Inc.
+006000-006FFF (base 16) Piranha EMS Inc.
+ 2681 Zanker Road
+ San Jose CA 95134
+ US
+
+70-B3-D5 (hex) Myostat Motion Control Inc
+96E000-96EFFF (base 16) Myostat Motion Control Inc
+ 17817 Leslie St #21
+ Newmarket ON L3Y8C6
+ CA
+
+70-B3-D5 (hex) AUDI AG
+01B000-01BFFF (base 16) AUDI AG
+ Auto-Union-Strasse 1
+ Ingolstadt 85045
+ DE
+
+70-B3-D5 (hex) DEUTA-WERKE GmbH
+E47000-E47FFF (base 16) DEUTA-WERKE GmbH
+ Paffrather Str. 140
+ Bergisch Gladbach North Rhine-Westphalia 51465
+ DE
+
+70-B3-D5 (hex) YUYAMA MFG Co.,Ltd
+CBA000-CBAFFF (base 16) YUYAMA MFG Co.,Ltd
+ 3-3-1
+ TOYONAKASHI OSAKA 561-0841
+ JP
+
+70-B3-D5 (hex) robert juliat
+EE8000-EE8FFF (base 16) robert juliat
+ 32 route de beaumont
+ fresnoy en thelle Oise 60530
+ FR
+
+70-B3-D5 (hex) Telensa Ltd
+56C000-56CFFF (base 16) Telensa Ltd
+ Iconix 3, London Road
+ Pampisford Cambridgeshire CB22 3EG
+ GB
+
+70-B3-D5 (hex) David Horn Communications Ltd
+ABB000-ABBFFF (base 16) David Horn Communications Ltd
+ Enterprise way,, Bramingham Business park
+ Luton Bedfordshire LU3 4BU
+ GB
+
+70-B3-D5 (hex) SET Power Systems GmbH
+DAB000-DABFFF (base 16) SET Power Systems GmbH
+ August Braun Strasse 3
+ Wangen Baden Wuerttemberg 88239
+ DE
+
+70-B3-D5 (hex) Centro de Ingenieria y Desarrollo industrial
+73C000-73CFFF (base 16) Centro de Ingenieria y Desarrollo industrial
+ Av. Playa Pie de la cuesta No.702 Desarrollo san pablo
+ Queretaro Queretaro 76125
+ MX
+
+70-B3-D5 (hex) Tecnint HTE SRL
+ACA000-ACAFFF (base 16) Tecnint HTE SRL
+ Via della Tecnica 16/18
+ Osnago Lecco 23875
+ IT
+
+70-B3-D5 (hex) NOA Co., Ltd.
+4EE000-4EEFFF (base 16) NOA Co., Ltd.
+ #201, 96, Seokho-ro, Sangrok-gu
+ Ansan-si Gyeonggi-do 15579
+ KR
+
+70-B3-D5 (hex) Ensotech Limited
+C2D000-C2DFFF (base 16) Ensotech Limited
+ Unit F, 6 Floor, Cheung Hing Shing Centre, No.23 Sha Tsui Road
+ Tsuen Wan 0000
+ HK
+
+70-B3-D5 (hex) GRIDSMART Technologies
+D50000-D50FFF (base 16) GRIDSMART Technologies
+ 10545 Hardin Valley Rd
+ Knoxville TN 37932
+ US
+
+70-B3-D5 (hex) Crown Solar Power Fencing Systems
+9D6000-9D6FFF (base 16) Crown Solar Power Fencing Systems
+ 123/A, Ushodaya Towers Shahpurnagar, Jeedimetla
+ Hyderabad Telangana 500055
+ IN
+
+70-B3-D5 (hex) Peloton Technology
+43E000-43EFFF (base 16) Peloton Technology
+ 1060 La Avenida
+ Mountain View CA 94043
+ US
+
+70-B3-D5 (hex) MB connect line GmbH Fernwartungssysteme
+AA5000-AA5FFF (base 16) MB connect line GmbH Fernwartungssysteme
+ Winnettener Straße 6
+ Dinkelsbuehl Bavaria 91550
+ DE
+
+70-B3-D5 (hex) Fracarro srl
+E8C000-E8CFFF (base 16) Fracarro srl
+ via Cazzaro 3
+ Castelfranco Veneto 31033
+ IT
+
+70-B3-D5 (hex) Hiber
+DBE000-DBEFFF (base 16) Hiber
+ Keizersgracht 209-sous
+ Amsterdam Noord-Holland 1016DT
+ NL
+
+70-B3-D5 (hex) TruTeq Wireless (Pty) Ltd
+4AB000-4ABFFF (base 16) TruTeq Wireless (Pty) Ltd
+ Ameton House
+ Centurion Gauteng 0157
+ ZA
+
+70-B3-D5 (hex) EA Elektroautomatik GmbH & Co. KG
+8CD000-8CDFFF (base 16) EA Elektroautomatik GmbH & Co. KG
+ Helmholtzstraße 31-33
+ Viersen NRW 41747
+ DE
+
+70-B3-D5 (hex) Authenticdata
+836000-836FFF (base 16) Authenticdata
+ 12F-8, No.100, Sec. 1, Jiafeng 11th Rd., Zhubei City
+ HsinChu 30273
+ TW
+
+70-B3-D5 (hex) Ruag Defence France SAS
+598000-598FFF (base 16) Ruag Defence France SAS
+ Chemin Jean Thomas
+ Terssac 81150
+ FR
+
+70-B3-D5 (hex) MICRO DEBUG, Y.K.
+020000-020FFF (base 16) MICRO DEBUG, Y.K.
+ 5-18-15 Oowada-cho
+ hachiouji Tokyo 192-0045
+ JP
+
+70-B3-D5 (hex) ATL-SD
+B76000-B76FFF (base 16) ATL-SD
+ Ogawacho 1-1034-11
+ Kodaira Tokyo 187-0032
+ JP
+
+70-B3-D5 (hex) Siemens Mobility GmbH - MO TI SPA
+E40000-E40FFF (base 16) Siemens Mobility GmbH - MO TI SPA
+ Rudower Chaussee 29
+ Berlin Berlin 12489
+ DE
+
+70-B3-D5 (hex) Wireless Systems Solutions LLC
+F86000-F86FFF (base 16) Wireless Systems Solutions LLC
+ 630 Davis Drive Suite 250
+ Morrisville NC 27560
+ US
+
+70-B3-D5 (hex) Creative Electronics Ltd
+5EC000-5ECFFF (base 16) Creative Electronics Ltd
+ Broomwood, South Park
+ SEVENOAKS Kent TN131EL
+ GB
+
+70-B3-D5 (hex) O-Net Communications(Shenzhen)Limited
+9A2000-9A2FFF (base 16) O-Net Communications(Shenzhen)Limited
+ No. 35, Cuijing Road, Pingshan New District
+ Shenzhen Guangdong 518118
+ CN
+
+70-B3-D5 (hex) Edgeware AB
+B6E000-B6EFFF (base 16) Edgeware AB
+ Master Samuelsgatan 42
+ Stockholm 11157
+ SE
+
+70-B3-D5 (hex) SmartSafe
+A7B000-A7BFFF (base 16) SmartSafe
+ Avenida das Américas 3301 Bloco 01 Lojas 102/110
+ Rio de Janeiro RJ 22631-003
+ BR
+
+70-B3-D5 (hex) Heroic Technologies Inc.
+5E7000-5E7FFF (base 16) Heroic Technologies Inc.
+ 200-155 East Beaver Creek Rd
+ Richmond Hill Ontario L4B2N1
+ CA
+
+70-B3-D5 (hex) MondeF
+1D9000-1D9FFF (base 16) MondeF
+ Neutronstraat 7-2
+ Groningen Groningen 9743AM
+ NL
+
+70-B3-D5 (hex) THETA432
+E1F000-E1FFFF (base 16) THETA432
+ 1730 E Holly Ave suite 805
+ El Segundo CA 90245
+ US
+
+70-B3-D5 (hex) Root Automation
+6A2000-6A2FFF (base 16) Root Automation
+ 1916 Fort Jones Rd
+ Yreka CA 96097
+ US
+
+70-B3-D5 (hex) Hypex Electronics BV
+287000-287FFF (base 16) Hypex Electronics BV
+ Kattegat 8
+ Groningen Groningen 9723 JP
+ NL
+
+70-B3-D5 (hex) Private
+1D7000-1D7FFF (base 16) Private
+
+70-B3-D5 (hex) Wuhan Xingtuxinke ELectronic Co.,Ltd
+70E000-70EFFF (base 16) Wuhan Xingtuxinke ELectronic Co.,Ltd
+ NO.C3-8F,Software Park,Optics Valley,East Lake Development Zone,Wuhan,Hubei,China
+ Wuhan Hubei 430074
+ CN
+
+70-B3-D5 (hex) Samriddi Automations Pvt. Ltd.
+BBA000-BBAFFF (base 16) Samriddi Automations Pvt. Ltd.
+ F-365
+ Noida up 201307
+ IN
+
+70-B3-D5 (hex) Traffic and Parking Control Co, Inc.
+0A7000-0A7FFF (base 16) Traffic and Parking Control Co, Inc.
+ 5100 W. Brown Deer Rd.
+ Brown Deer WI 53223
+ US
+
+70-B3-D5 (hex) KBS Industrieelektronik GmbH
+860000-860FFF (base 16) KBS Industrieelektronik GmbH
+ Burkheimer Str. 10
+ Freiburg 79111
+ DE
+
70-B3-D5 (hex) EMAC, Inc.
8AB000-8ABFFF (base 16) EMAC, Inc.
2390 EMAC Way
@@ -13142,12 +14624,6 @@ FDF000-FDFFFF (base 16) NARA CONTROLS INC.
SEOUL SEOUL 100-043
KR
-70-B3-D5 (hex) Algodue Elettronica Srl
-430000-430FFF (base 16) Algodue Elettronica Srl
- Via Passerina 3/A
- Fontaneto d'Agogna Novara 28010
- IT
-
70-B3-D5 (hex) Ethical Lighting and Sensor Solutions Limited
5B6000-5B6FFF (base 16) Ethical Lighting and Sensor Solutions Limited
Unit 5 Churchill Industrial Estate
@@ -13649,12 +15125,6 @@ FF5000-FF5FFF (base 16) Prolan Process Control Co.
Montelupone Macerata 62010
IT
-70-B3-D5 (hex) Open System Solutions Limited
-44B000-44BFFF (base 16) Open System Solutions Limited
- Saltmakers House
- Southampton Hampshire SO31 4NB
- GB
-
70-B3-D5 (hex) Tokyo Communication Equipment MFG Co.,ltd.
BEC000-BECFFF (base 16) Tokyo Communication Equipment MFG Co.,ltd.
3-8-13
@@ -14816,9 +16286,6 @@ F9C000-F9CFFF (base 16) SureFlap Ltd
Cambridge Cambridgeshire CB23 8AR
GB
-70-B3-D5 (hex) Private
-279000-279FFF (base 16) Private
-
70-B3-D5 (hex) NETWAYS GmbH
73D000-73DFFF (base 16) NETWAYS GmbH
Deutschherrnstraße 15
@@ -16067,6 +17534,12 @@ E22000-E22FFF (base 16) Private
CARDANO AL CAMPO VA 21010
IT
+70-B3-D5 (hex) DEUTA-WERKE GmbH
+F84000-F84FFF (base 16) DEUTA-WERKE GmbH
+ Paffrather Str. 140
+ Bergisch Gladbach North Rhine-Westphalia 51465
+ DE
+
70-B3-D5 (hex) Code Blue Corporation
A8D000-A8DFFF (base 16) Code Blue Corporation
259 Hedcor Street
@@ -16079,11 +17552,11 @@ F58000-F58FFF (base 16) CDR SRL
GINESTRA FIORENTINA FLORENCE 50055
IT
-70-B3-D5 (hex) DEUTA-WERKE GmbH
-F84000-F84FFF (base 16) DEUTA-WERKE GmbH
- Paffrather Str. 140
- Bergisch Gladbach North Rhine-Westphalia 51465
- DE
+70-B3-D5 (hex) Sun Creative (ZheJiang) Technology INC.
+2B2000-2B2FFF (base 16) Sun Creative (ZheJiang) Technology INC.
+ 3#Building No.181 Wuchang street
+ HANGZHOU ZHE JIANG 310023
+ CN
70-B3-D5 (hex) Sicon srl
7C7000-7C7FFF (base 16) Sicon srl
@@ -16091,14 +17564,377 @@ F84000-F84FFF (base 16) DEUTA-WERKE GmbH
Isola Vicentina Vicenza 36033
IT
-70-B3-D5 (hex) Sun Creative (ZheJiang) Technology INC.
-2B2000-2B2FFF (base 16) Sun Creative (ZheJiang) Technology INC.
- 3#Building No.181 Wuchang street
- HANGZHOU ZHE JIANG 310023
- CN
-
70-B3-D5 (hex) BASF Corporation
680000-680FFF (base 16) BASF Corporation
26 Davis Drive
Research Triangle Park NC 27709
US
+
+70-B3-D5 (hex) Lumiplan Duhamel
+E94000-E94FFF (base 16) Lumiplan Duhamel
+ 2 rue de l'industrie
+ Domène Isère 38420
+ FR
+
+70-B3-D5 (hex) Centum Adetel Group
+FD7000-FD7FFF (base 16) Centum Adetel Group
+ 4 Chemin du ruisseau
+ Ecully 69130
+ FR
+
+70-B3-D5 (hex) TIAMA
+C4A000-C4AFFF (base 16) TIAMA
+ ZA des Plattes - 1 Chemin des Plattes
+ VOURLES 69390
+ FR
+
+70-B3-D5 (hex) Hongin., Ltd
+355000-355FFF (base 16) Hongin., Ltd
+ 160, Daehwa-ro, Daedeok-gu
+ Daejeon Republic of Korea 34368
+ KR
+
+70-B3-D5 (hex) CRUXELL Corp.
+518000-518FFF (base 16) CRUXELL Corp.
+ A-405 Migun techno world II,187 techno 2-ro, Yusong-gu
+ Daejeon Daejeon 34025
+ KR
+
+70-B3-D5 (hex) BRS Sistemas Eletrônicos
+F16000-F16FFF (base 16) BRS Sistemas Eletrônicos
+ Rua Gomes de Freitas, 491 / 204
+ Porto Alegre RS 91380-000
+ BR
+
+70-B3-D5 (hex) MI Inc.
+6FC000-6FCFFF (base 16) MI Inc.
+ 6F, Toto building, 5-1-4, Toranomon, Minato-ku
+ Tokyo 1050001
+ JP
+
+70-B3-D5 (hex) Sense For Innovation
+1EA000-1EAFFF (base 16) Sense For Innovation
+ Nistelrodeseweg 9A
+ Uden 5406 PT
+ NL
+
+70-B3-D5 (hex) ACD Elekronik GmbH
+673000-673FFF (base 16) ACD Elekronik GmbH
+ Engelberg 2
+ Achstetten 88480
+ DE
+
+70-B3-D5 (hex) Boffins Technologies AB
+E5D000-E5DFFF (base 16) Boffins Technologies AB
+ Russgatan 5
+ Malmö 212 35
+ SE
+
+70-B3-D5 (hex) AKIS technologies
+402000-402FFF (base 16) AKIS technologies
+ Ciurlionio g. 82
+ Vilnius Lithuania 03100
+ LT
+
+70-B3-D5 (hex) UR FOG S.R.L.
+B8E000-B8EFFF (base 16) UR FOG S.R.L.
+ Via Toscana 38
+ San Mauro Torinese TO 10099
+ IT
+
+70-B3-D5 (hex) EDFelectronics JRMM Sp z o.o. sp.k.
+234000-234FFF (base 16) EDFelectronics JRMM Sp z o.o. sp.k.
+ Rybnicka 64
+ Radlin 44-310
+ PL
+
+70-B3-D5 (hex) ODAWARAKIKI AUTO-MACHINE MFG.CO.,LTD
+F88000-F88FFF (base 16) ODAWARAKIKI AUTO-MACHINE MFG.CO.,LTD
+ 1-11-3 Nakacho
+ Odawara Kanagawa 250-0005
+ JP
+
+70-B3-D5 (hex) Silent Gliss International Ltd
+B66000-B66FFF (base 16) Silent Gliss International Ltd
+ Worbstrasse 210
+ Guemligen 3073
+ CH
+
+70-B3-D5 (hex) Gastech Australia Pty Ltd
+793000-793FFF (base 16) Gastech Australia Pty Ltd
+ 24 Baretta Road
+ Wanagra WA 6065
+ AU
+
+70-B3-D5 (hex) Digital Way
+437000-437FFF (base 16) Digital Way
+ 1 Chemin des CHAUX
+ Saint-Etienne Loire 42000
+ FR
+
+70-B3-D5 (hex) KDT
+748000-748FFF (base 16) KDT
+ 126, Haeoreum-gil, Namsan-myeon
+ Chuncheon-si Gangwon-do 200-911
+ KR
+
+70-B3-D5 (hex) Behr Technologies Inc
+C1F000-C1FFFF (base 16) Behr Technologies Inc
+ 10 York Mills Road, Suite 610
+ Toronto Ontario M2P 2G4
+ CA
+
+70-B3-D5 (hex) MOTION LIB,Inc.
+27C000-27CFFF (base 16) MOTION LIB,Inc.
+ Saiwai-ku ,Shinkawasaki 7-7-237
+ Kawasaki City Kanagawa 212-0032
+ JP
+
+70-B3-D5 (hex) Private
+279000-279FFF (base 16) Private
+
+70-B3-D5 (hex) ESYSE GmbH Embedded Systems Engineering
+008000-008FFF (base 16) ESYSE GmbH Embedded Systems Engineering
+ Ruth-Niehaus Str. 8
+ Meerbusch Nordrhein-Westfalen 40667
+ DE
+
+70-B3-D5 (hex) C21 Systems Ltd
+14B000-14BFFF (base 16) C21 Systems Ltd
+ Dunston Innovation Centre
+ Chesterfield Derbyshire S41 8NG
+ GB
+
+70-B3-D5 (hex) Leder Elektronik Design
+22D000-22DFFF (base 16) Leder Elektronik Design
+ Bruchweg 10
+ Ketsch Baden-Wuerttemberg 68775
+ DE
+
+70-B3-D5 (hex) VTC Digicom
+C4C000-C4CFFF (base 16) VTC Digicom
+ 23 Lac Trung
+ Ha Noi Ha Noi 100000
+ VN
+
+70-B3-D5 (hex) Potter Electric Signal Co. LLC
+B9A000-B9AFFF (base 16) Potter Electric Signal Co. LLC
+ 1609 Park 370 Place
+ Hazelwood MO 63042
+ US
+
+70-B3-D5 (hex) Signals and systems india pvt ltd
+2ED000-2EDFFF (base 16) Signals and systems india pvt ltd
+ 15/D-19, 3rd Main Road, SIPCOT IT PARK, SIRUSERI, OMR
+ CHENNAI TAMIL NADU 603103
+ IN
+
+70-B3-D5 (hex) Insitu, Inc
+DD2000-DD2FFF (base 16) Insitu, Inc
+ 118 E Columbia River Way
+ Bingen WA 98605
+ US
+
+70-B3-D5 (hex) MB connect line GmbH Fernwartungssysteme
+1C9000-1C9FFF (base 16) MB connect line GmbH Fernwartungssysteme
+ Winnettener Straße 6
+ Dinkelsbuehl Bavaria 91550
+ DE
+
+70-B3-D5 (hex) Sunstone Engineering
+2FF000-2FFFFF (base 16) Sunstone Engineering
+ 1693 American Way Suite 5
+ Payson UT 84651
+ US
+
+70-B3-D5 (hex) Ascenix Corporation
+3A4000-3A4FFF (base 16) Ascenix Corporation
+ 1120 Benfield Blvd. STE A
+ Millersville MD 21108
+ US
+
+70-B3-D5 (hex) VNT electronics s.r.o.
+0E9000-0E9FFF (base 16) VNT electronics s.r.o.
+ Dvorská, 605
+ Lanškroun-Ostrovské Předměstí (okres Ústí nad Orlicí) 56301
+ CZ
+
+70-B3-D5 (hex) Open System Solutions Limited
+44B000-44BFFF (base 16) Open System Solutions Limited
+ Saltmakers House
+ Southampton Hampshire SO31 4NB
+ GB
+
+70-B3-D5 (hex) Sportsbeams Lighting, Inc.
+013000-013FFF (base 16) Sportsbeams Lighting, Inc.
+ 1260 Pine Forest Cir
+ Round Rock TX 78665
+ US
+
+70-B3-D5 (hex) DORLET SAU
+4F6000-4F6FFF (base 16) DORLET SAU
+ Albert Eistein 34
+ Alava SPAIN 01510
+ ES
+
+70-B3-D5 (hex) Shenzhen Siera Technology Ltd
+1BD000-1BDFFF (base 16) Shenzhen Siera Technology Ltd
+ Room 2039, Shenhai Building, Wanzhong Village, Bulong Road, Minzhi, Longhua district, City: Shenzhen
+ Shenzhen Guangdong 518131
+ CN
+
+70-B3-D5 (hex) sohonet ltd
+0CF000-0CFFFF (base 16) sohonet ltd
+ 3-5, Soho Street
+ London London W1D 3DG
+ GB
+
+70-B3-D5 (hex) ISAC SRL
+07C000-07CFFF (base 16) ISAC SRL
+ via Maestri del Lavoro 30
+ CASCINA PISA 56021
+ IT
+
+70-B3-D5 (hex) Graphcore Ltd
+1B3000-1B3FFF (base 16) Graphcore Ltd
+ 11-19 Wine Street
+ Bristol BS1 2PH
+ GB
+
+70-B3-D5 (hex) DKI Technology Co., Ltd
+D00000-D00FFF (base 16) DKI Technology Co., Ltd
+ Room #1005, DONGHWA BLDG, 71, Yeouinaru-ro, Yeongdeungpo-gu
+ Seoul Seoul KR-11
+ KR
+
+70-B3-D5 (hex) PixelApps s.r.o.
+251000-251FFF (base 16) PixelApps s.r.o.
+ Hošťálkova 633/49
+ Praha 6 16900
+ CZ
+
+70-B3-D5 (hex) W. H. Leary Co., Inc.
+1C0000-1C0FFF (base 16) W. H. Leary Co., Inc.
+ 8440B West 183rd Pl
+ Tinley Park IL 60487
+ US
+
+70-B3-D5 (hex) amakidenki
+9FD000-9FDFFF (base 16) amakidenki
+ m.okuyama@amaki.co.jp
+ higashiyamatoshi tokyo 207-0004
+ JP
+
+70-B3-D5 (hex) Dat-Con d.o.o.
+CDC000-CDCFFF (base 16) Dat-Con d.o.o.
+ CvetliÄna ulica 52
+ Polzela 3313
+ SI
+
+70-B3-D5 (hex) JNR Sports Holdings, LLC
+3E7000-3E7FFF (base 16) JNR Sports Holdings, LLC
+ 656 NORTH RD
+ CANDIA NH 03034-2027
+ US
+
+70-B3-D5 (hex) 812th AITS
+590000-590FFF (base 16) 812th AITS
+ 300 E Yeagar Blvd
+ Edwards AFB CA 93524
+ US
+
+70-B3-D5 (hex) HongSeok Ltd.
+C40000-C40FFF (base 16) HongSeok Ltd.
+ 166, Osan-ro, Osan-myeon
+ Iksan-si Jeollabuk-do 54670
+ KR
+
+70-B3-D5 (hex) FAS Electronics (Fujian) Co.,LTD.
+B46000-B46FFF (base 16) FAS Electronics (Fujian) Co.,LTD.
+ #1 Building,Weihuang Industrial Zone, Qingkou Invest District
+ Minhou city, Fuzhou Fujian 350119
+ CN
+
+70-B3-D5 (hex) ASML
+DE5000-DE5FFF (base 16) ASML
+ 17075 Thornmint Ct
+ San Diego 92127
+ US
+
+70-B3-D5 (hex) Algodue Elettronica Srl
+430000-430FFF (base 16) Algodue Elettronica Srl
+ Via Passerina 3/A
+ Fontaneto d'Agogna Novara 28010
+ IT
+
+70-B3-D5 (hex) BP Lubricants USA, Inc.
+94E000-94EFFF (base 16) BP Lubricants USA, Inc.
+ 201 North Webster
+ White Cloud MI 49349
+ US
+
+70-B3-D5 (hex) Insitu Inc.
+D36000-D36FFF (base 16) Insitu Inc.
+ 901 E Bingen Point Way
+ Bingen WA 98605
+ US
+
+70-B3-D5 (hex) C-COM Satellite Systems Inc.
+D4F000-D4FFFF (base 16) C-COM Satellite Systems Inc.
+ 2574 Sheffield Road
+ Ottawa Ontario K1B3V7
+ CA
+
+70-B3-D5 (hex) Grossenbacher Systeme AG
+803000-803FFF (base 16) Grossenbacher Systeme AG
+ Spinnereistrasse 10
+ St. Gallen 9008
+ CH
+
+70-B3-D5 (hex) Qntra Technology
+EA1000-EA1FFF (base 16) Qntra Technology
+ Velcho Atanasov 47
+ Sofia Sofia 1505
+ BG
+
+70-B3-D5 (hex) GETRALINE
+FD4000-FD4FFF (base 16) GETRALINE
+ 15 RUE D'ANGIVILLER
+ VERSAILLES 78000
+ FR
+
+70-B3-D5 (hex) iOne
+330000-330FFF (base 16) iOne
+ 8F-2, #75, sec 1, Hsin Tai Wu Rd., Hsi Chih District
+ New Taipei City, Taiwan 22101
+ TW
+
+70-B3-D5 (hex) COMPAL ELECTRONICS, INC.
+4F2000-4F2FFF (base 16) COMPAL ELECTRONICS, INC.
+ No.500, Ruiguang Rd., Neihu District,
+ Taipei 11492
+ TW
+
+70-B3-D5 (hex) Cloud Intelligence Pty Ltd
+574000-574FFF (base 16) Cloud Intelligence Pty Ltd
+ 43/10 Gladstone Rd
+ Castle Hill NSW 2154
+ AU
+
+70-B3-D5 (hex) RCH ITALIA SPA
+5D4000-5D4FFF (base 16) RCH ITALIA SPA
+ VIA CENDON 39
+ SILEA TREVISO 31057
+ IT
+
+70-B3-D5 (hex) Acrodea, Inc.
+E79000-E79FFF (base 16) Acrodea, Inc.
+ 3F, Daisan Yamada Bldg., 22 Aizumi-cho
+ Shinjuku-ku Tokyo 1600005
+ JP
+
+70-B3-D5 (hex) vision systems safety tech
+98A000-98AFFF (base 16) vision systems safety tech
+ Route d’Irigny – ZI NORD - BP 32
+ Brignais Rhone alpes auverne 69530
+ FR
diff --git a/hwdb/meson.build b/hwdb/meson.build
index 158292c712..31ee3e7409 100644
--- a/hwdb/meson.build
+++ b/hwdb/meson.build
@@ -36,9 +36,11 @@ endif
############################################################
parse_hwdb_py = find_program('parse_hwdb.py')
-test('parse-hwdb',
- parse_hwdb_py,
- timeout : 90)
+if want_tests != 'false'
+ test('parse-hwdb',
+ parse_hwdb_py,
+ timeout : 90)
+endif
############################################################
diff --git a/hwdb/parse_hwdb.py b/hwdb/parse_hwdb.py
index 4900a25778..d84fba2221 100755
--- a/hwdb/parse_hwdb.py
+++ b/hwdb/parse_hwdb.py
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
-# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
-# SPDX-License-Identifier: MIT
+# SPDX-License-Identifier: MIT
#
# This file is distributed under the MIT license, see below.
#
@@ -30,8 +29,7 @@ import sys
import os
try:
- from pyparsing import (Word, White, Literal, ParserElement, Regex,
- LineStart, LineEnd,
+ from pyparsing import (Word, White, Literal, ParserElement, Regex, LineEnd,
OneOrMore, Combine, Or, Optional, Suppress, Group,
nums, alphanums, printables,
stringEnd, pythonStyleComment, QuotedString,
diff --git a/hwdb/pci.ids b/hwdb/pci.ids
index 40ee143fc5..757750ea33 100644
--- a/hwdb/pci.ids
+++ b/hwdb/pci.ids
@@ -1,11 +1,11 @@
#
# List of PCI ID's
#
-# Version: 2018.06.11
-# Date: 2018-06-11 03:15:02
+# Version: 2018.12.20
+# Date: 2018-12-20 03:15:02
#
# Maintained by Albert Pool, Martin Mares, and other volunteers from
-# the PCI ID Project at http://pci-ids.ucw.cz/.
+# the PCI ID Project at https://pci-ids.ucw.cz/.
#
# New data are always welcome, especially if they are accurate. If you have
# anything to contribute, please follow the instructions at the web site.
@@ -412,6 +412,7 @@
005c SAS1064A PCI-X Fusion-MPT SAS
005d MegaRAID SAS-3 3108 [Invader]
1000 9361 MegaRAID SAS 9361-8i
+ 1000 9363 MegaRAID SAS 9361-4i
1000 9364 MegaRAID SAS 9364-8i
1000 936a MegaRAID SAS 9364-8i
1028 1f41 PERC H830 Adapter
@@ -428,6 +429,7 @@
17aa 1052 ThinkServer RAID 720i
17aa 1053 ThinkServer RAID 720ix
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
8086 351e RMS3CC080 RAID Controller
8086 351f RMS3CC040 RAID Controller
@@ -481,8 +483,10 @@
0065 SAS2116 PCI-Express Fusion-MPT SAS-2 [Meteor]
006e SAS2308 PCI-Express Fusion-MPT SAS-2
0070 SAS2004 PCI-Express Fusion-MPT SAS-2 [Spitfire]
+ 1000 3010 SAS9211-4i
0071 MR SAS HBA 2004
0072 SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon]
+ 1000 30b0 9200-8e [LSI SAS 6Gb/s SAS/SATA PCIe x8 External HBA]
1028 1f1c 6Gbps SAS HBA Adapter
1028 1f1d PERC H200 Adapter
1028 1f1e PERC H200 Integrated
@@ -583,10 +587,13 @@
0084 SAS2208 PCI-Express Fusion-MPT SAS-2
0085 SAS2208 PCI-Express Fusion-MPT SAS-2
0086 SAS2308 PCI-Express Fusion-MPT SAS-2
+ 15d9 0690 Onboard MegaRAID SAS2208 [Thunderbolt]
+ 15d9 0691 Onboard SAS2308 PCI-Express Fusion-MPT SAS-2
0087 SAS2308 PCI-Express Fusion-MPT SAS-2
1000 3020 9207-8i SAS2.1 HBA
1000 3040 9207-8e SAS2.1 HBA
1000 3050 SAS9217-8i
+ 1014 0472 N2125 External Host Bus Adapter
1590 0044 H220i
8086 3000 RS25GB008 RAID Controller
8086 3060 RS25FB044 RAID Controller
@@ -624,6 +631,7 @@
8086 3020 RAID Controller RSP3GD016J
00ae SAS3508 Fusion-MPT Tri-Mode RAID On Chip (ROC)
00af SAS3408 Fusion-MPT Tri-Mode I/O Controller Chip (IOC)
+ 1000 3010 HBA 9400-8i
1d49 0200 ThinkSystem 430-8i SAS/SATA 12Gb HBA
1d49 0202 ThinkSystem 430-8e SAS/SATA 12Gb HBA
1d49 0204 ThinkSystem 430-8i SAS/SATA 12Gb Dense HBA
@@ -647,9 +655,19 @@
00d0 SAS3716 Fusion-MPT Tri-Mode RAID Controller Chip (ROC)
00d1 SAS3616 Fusion-MPT Tri-Mode I/O Controller Chip (IOC)
00d3 MegaRAID Tri-Mode SAS3716W
+ 00e0 Fusion-MPT 12GSAS/PCIe Unsupported SAS39xx
+ 00e1 Fusion-MPT 12GSAS/PCIe SAS39xx
+ 00e2 Fusion-MPT 12GSAS/PCIe Secure SAS39xx
+ 00e3 Fusion-MPT 12GSAS/PCIe Unsupported SAS39xx
+ 00e4 Fusion-MPT 12GSAS/PCIe Unsupported SAS38xx
+ 00e5 Fusion-MPT 12GSAS/PCIe SAS38xx
+ 00e6 Fusion-MPT 12GSAS/PCIe Secure SAS38xx
+ 00e7 Fusion-MPT 12GSAS/PCIe Unsupported SAS38xx
02b0 Virtual Endpoint on PCIe Switch
1d49 0001 ThinkSystem 1610-4P NVMe Switch Adapter
1d49 0002 ThinkSystem 810-4P NVMe Switch Adapter
+ 02b1 Virtual Endpoint on PCIe Switch (9749)
+ 1d49 0004 ThinkSystem 1610-8P NVMe Switch Adapter
0407 MegaRAID
1000 0530 MegaRAID 530 SCSI 320-0X RAID Controller
1000 0531 MegaRAID 531 SCSI 320-4X RAID Controller
@@ -720,6 +738,14 @@
0807 SA2020ZC
0901 61C102
1000 63C815
+ 10e0 MegaRAID 12GSAS/PCIe Unsupported SAS39xx
+ 10e1 MegaRAID 12GSAS/PCIe SAS39xx
+ 10e2 MegaRAID 12GSAS/PCIe Secure SAS39xx
+ 10e3 MegaRAID 12GSAS/PCIe Unsupported SAS39xx
+ 10e4 MegaRAID 12GSAS/PCIe Unsupported SAS38xx
+ 10e5 MegaRAID 12GSAS/PCIe SAS38xx
+ 10e6 MegaRAID 12GSAS/PCIe Secure SAS38xx
+ 10e7 MegaRAID 12GSAS/PCIe Unsupported SAS38xx
1960 MegaRAID
1000 0518 MegaRAID 518 SCSI 320-2 Controller
1000 0520 MegaRAID 520 SCSI 320-1 Controller
@@ -772,6 +798,7 @@
131b Kaveri [Radeon R4 Graphics]
131c Kaveri [Radeon R7 Graphics]
131d Kaveri [Radeon R6 Graphics]
+ 15d8 Picasso
15dd Raven Ridge [Radeon Vega Series / Radeon Vega Mobile Series]
103c 83c6 Radeon Vega 8 Mobile
1458 d000 Radeon RX Vega 11
@@ -1662,7 +1689,8 @@
106b 014b Tropo XT [Radeon R9 M380 Mac Edition]
6641 Saturn PRO [Radeon HD 8930M]
6646 Bonaire XT [Radeon R9 M280X]
- 6647 Bonaire PRO [Radeon R9 M270X]
+ 6647 Saturn PRO/XT [Radeon R9 M270X/M280X]
+ 1043 223d N551ZU laptop Radeon R9 M280X
6649 Bonaire [FirePro W5100]
1002 0b0c FirePro W4300
103c 0b0c Bonaire [FirePro W4300]
@@ -1671,6 +1699,7 @@
6650 Bonaire
6651 Bonaire
6658 Bonaire XTX [Radeon R7 260X/360]
+ 1043 04d3 AMD Radeon R7 260X
148c 0907 Radeon R7 360
1682 0907 Radeon R7 360
1682 7360 Radeon R7 360
@@ -1720,7 +1749,7 @@
66a1 Vega 20
66a2 Vega 20
66a3 Vega 20
- 66a7 Vega 20
+ 66a7 Vega 20 [Radeon Pro Vega 20]
66af Vega 20
6704 Cayman PRO GL [FirePro V7900]
6707 Cayman LE GL [FirePro V5900]
@@ -2176,6 +2205,7 @@
1028 2120 Radeon HD 6450
103c 2128 Radeon HD 6450
103c 2aee Radeon HD 7450A
+ 1092 6450 Radeon HD 6450
1462 2125 Radeon HD 6450
1462 2346 Radeon HD 7450
1462 2490 Radeon HD 6450
@@ -2322,7 +2352,7 @@
67cc Ellesmere [Polaris10]
67cf Ellesmere [Polaris10]
67d0 Ellesmere [Radeon Pro V7300X / V7350x2]
- 67df Ellesmere [Radeon RX 470/480/570/570X/580/580X]
+ 67df Ellesmere [Radeon RX 470/480/570/570X/580/580X/590]
1002 0b37 Radeon RX 480
1028 1722 Radeon RX 570X
1028 1723 Radeon RX 580X
@@ -2330,13 +2360,14 @@
1043 04b0 Radeon RX 470
1043 04fb Radeon RX 480
1043 04fd Radeon RX 480 8GB
+ 1043 056a Radeon RX 590
106b 0161 Radeon Pro 580
106b 0162 Radeon Pro 575
106b 0163 Radeon Pro 570
1458 22f0 Radeon RX 570
1458 22f7 Radeon RX 570 Gaming 4G
1462 3411 Radeon RX 470
- 1462 3413 Radeon RX 480
+ 1462 3413 Radeon RX 480 Gaming X 8GB
1462 3416 Radeon RX 570
1462 3418 Radeon RX 580 Armor 4G OC
148c 2372 Radeon RX 480
@@ -2349,8 +2380,8 @@
1787 a470 Radeon RX 470
1787 a480 Radeon RX 480
1849 5001 Phantom Gaming X RX 580 OC
- 1da2 e353 Sapphire Radeon RX 580 Pulse 8GB
- 1da2 e366 Nitro+ Radeon RX 580 4GB
+ 1da2 e353 Radeon RX 570 Pulse 4GB
+ 1da2 e366 Nitro+ Radeon RX 570/580
67e0 Baffin [Radeon Pro WX 4170]
103c 8270 Radeon Pro WX 4170
103c 8272 Radeon Pro WX 4170
@@ -2367,12 +2398,14 @@
103c 8277 Radeon Pro WX 4150
67e9 Baffin [Polaris11]
67eb Baffin [Radeon Pro V5300X]
- 67ef Baffin [Radeon RX 460/560D / Pro 450/455/460/555/560]
+ 67ef Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X]
106b 0160 Radeon Pro 460
106b 0166 Radeon Pro 455
106b 0167 Radeon Pro 450
106b 0179 Radeon Pro 560
106b 017a Radeon Pro 555
+ 106b 018f Radeon Pro 560X
+ 106b 0190 Radeon Pro 555X
1642 1727 Polaris 21 XL [Radeon RX 560D]
1682 956d Polaris 21 XL [Radeon RX 560D]
67ff Baffin [Radeon RX 550 640SP / RX 560/560X]
@@ -2458,8 +2491,9 @@
15c3 2b1e MED-X6000
6829 Cape Verde
682a Venus PRO
- 682b Venus LE / Tropo PRO-L [Radeon HD 8830M / R7 M465X]
+ 682b Cape Verde PRO / Venus LE / Tropo PRO-L [Radeon HD 8830M / R7 250 / R7 M465X]
0128 079c Radeon R7 465X
+ 1462 3012 Radeon R7 250
682c Cape Verde GL [FirePro W4100]
682d Chelsea XT GL [FirePro M4000]
682f Chelsea LP [Radeon HD 7730M]
@@ -2592,15 +2626,19 @@
6842 Thames LE [Radeon HD 7000M Series]
6843 Thames [Radeon HD 7670M]
6860 Vega 10 [Radeon Instinct MI25]
+ 1002 0c35 Radeon PRO V320
+ 1002 6c75 Radeon PRO V320
106b 017c Radeon Pro Vega 64
6861 Vega 10 XT [Radeon PRO WX 9100]
6862 Vega 10 XT [Radeon PRO SSG]
6863 Vega 10 XTX [Radeon Vega Frontier Edition]
6864 Vega
6867 Vega 10 XL [Radeon Pro Vega 56]
- 6868 Vega
+ 6868 Vega 10 [Radeon PRO WX 8100]
686c Vega 10 [Radeon Instinct MI25 MxGPU]
- 687f Vega 10 XT [Radeon RX Vega 64]
+ 687f Vega 10 XL/XT [Radeon RX Vega 56/64]
+ 6880 Lexington [Radeon HD 6550M]
+ 103c 163c Pavilion dv6 Radeon HD 6550M
6888 Cypress XT [FirePro V8800]
6889 Cypress PRO [FirePro V7800]
1002 0301 FirePro V7800P
@@ -3115,7 +3153,7 @@
# Make naming scheme consistent
174b e308 Radeon R9 380 Nitro 4G D5
694c Polaris 22 [Radeon RX Vega M GH]
- 694e Polaris 22 [Radeon RX Vega M GL]
+ 694e Polaris 22 XL [Radeon RX Vega M GL]
6980 Polaris12
6981 Polaris12
6985 Lexa XT [Radeon PRO WX 3100]
@@ -3131,6 +3169,7 @@
69a2 Vega 12
69a3 Vega 12
69af Vega 12
+ 6fdf Polaris 20 XL [Radeon RX 580 2048SP]
700f RS100 AGP Bridge
7010 RS200/RS250 AGP Bridge
7100 R520 [Radeon X1800 XT]
@@ -3273,6 +3312,8 @@
1043 049e Radeon R9 FURY
1043 04a0 Radeon R9 FURY X
174b e329 Radeon R9 FURY
+ 7310 Navi 10
+ 731f Navi 10
7833 RS350 Host Bridge
7834 RS350 [Radeon 9100 PRO/XT IGP]
7835 RS350M [Mobility Radeon 9000 IGP]
@@ -3595,11 +3636,11 @@
9919 Trinity [Radeon HD 7500G]
9920 Liverpool [Playstation 4 APU]
9921 Liverpool HDMI/DP Audio Controller
- 9990 Trinity [Radeon HD 7520G]
- 9991 Trinity [Radeon HD 7540D]
- 9992 Trinity [Radeon HD 7420G]
- 9993 Trinity [Radeon HD 7480D]
- 9994 Trinity [Radeon HD 7400G]
+ 9990 Trinity 2 [Radeon HD 7520G]
+ 9991 Trinity 2 [Radeon HD 7540D]
+ 9992 Trinity 2 [Radeon HD 7420G]
+ 9993 Trinity 2 [Radeon HD 7480D]
+ 9994 Trinity 2 [Radeon HD 7400G]
9995 Richland [Radeon HD 8450G]
9996 Richland [Radeon HD 8470D]
9997 Richland [Radeon HD 8350G]
@@ -3610,9 +3651,9 @@
999c Richland
# AMD Quad-Core A8-Series APU A8-6500T with Radeon HD 8550D
999d Richland [Radeon HD 8550D]
- 99a0 Trinity [Radeon HD 7520G]
- 99a2 Trinity [Radeon HD 7420G]
- 99a4 Trinity [Radeon HD 7400G]
+ 99a0 Trinity 2 [Radeon HD 7520G]
+ 99a2 Trinity 2 [Radeon HD 7420G]
+ 99a4 Trinity 2 [Radeon HD 7400G]
aa00 R600 HDMI Audio [Radeon HD 2900 GT/PRO/XT]
aa01 RV635 HDMI Audio [Radeon HD 3650/3730/3750]
aa08 RV630 HDMI Audio [Radeon HD 2600 PRO/XT / HD 3610]
@@ -3647,8 +3688,9 @@
# I have a Tonga card and this is the HDMI Audio part
aad8 Tonga HDMI Audio [Radeon R9 285/380]
174b aad8 Radeon R9 285/380 HDMI Audio
+ aae0 Baffin HDMI/DP Audio [Radeon RX 550 640SP / RX 560/560X]
aae8 Fiji HDMI/DP Audio [Radeon R9 Nano / FURY/FURY X]
- aaf0 Ellesmere [Radeon RX 580]
+ aaf0 Ellesmere [Radeon RX 570/580]
ac00 Theater 600 Pro
ac02 TV Wonder HD 600 PCIe
ac12 Theater HD T507 (DVB-T) TV tuner/capture device
@@ -4070,6 +4112,7 @@
03dc POWER8 Host Bridge (PHB3)
044b GenWQE Accelerator Adapter
04aa Flash Adapter 90 (PCIe2 0.9TB)
+ 04c1 POWER9 Host Bridge (PHB4)
04da PCI-E IPR SAS+ Adapter (ASIC)
1014 04fb PCIe3 x16 20GB Cache 12Gb Quad SAS RAID+ Adapter(580B)
1014 04fc PCIe3 x8 12Gb Quad SAS RAID+ Adapter(580A)
@@ -4311,10 +4354,16 @@
43a1 Hudson PCI to PCI bridge (PCIE port 1)
43a2 Hudson PCI to PCI bridge (PCIE port 2)
43a3 Hudson PCI to PCI bridge (PCIE port 3)
+ 43b0 X370 Series Chipset PCIe Upstream Port
+ 1849 43c6 Fatal1ty X370 Professional Gaming
43b1 X399 Series Chipset PCIe Bridge
43b4 300 Series Chipset PCIe Port
+ 43b5 X370 Series Chipset SATA Controller
+ 1849 43c8 Fatal1ty X370 Professional Gaming
43b6 X399 Series Chipset SATA Controller
43b7 300 Series Chipset SATA Controller
+ 43b9 X370 Series Chipset USB 3.1 xHCI Controller
+ 1849 43d0 Fatal1ty X370 Professional Gaming
43ba X399 Series Chipset USB 3.1 xHCI Controller
43bb 300 Series Chipset USB 3.1 xHCI Controller
7006 AMD-751 [Irongate] System Controller
@@ -4766,11 +4815,14 @@
1028 028d PowerEdge T410 MGA G200eW WPCM450
1028 029c PowerEdge M710 MGA G200eW WPCM450
1028 02a4 PowerEdge T310 MGA G200eW WPCM450
+ 15d9 0605 X8SIL
15d9 0624 X9SCM-F Motherboard
+ 15d9 066b X9SRL-F
15d9 a811 H8DGU
0533 MGA G200EH
103c 3381 iLO4
0534 G200eR2
+ 1028 04f7 PowerEdge R320 server
0536 Integrated Matrox G200eW3 Graphics Controller
0538 MGA G200eH3
1590 00e4 iLO5 VGA
@@ -5366,6 +5418,7 @@
3306 Integrated Lights-Out Standard Slave Instrumentation & System Support
103c 330e iLO3
103c 3381 iLO4
+ 1590 00e4 iLO5
3307 Integrated Lights-Out Standard Management Processor Support and Messaging
# HP DL380 G6
103c 3309 iLO 2
@@ -5806,10 +5859,19 @@
104d Sony Corporation
8004 DTL-H2500 [Playstation development board]
8009 CXD1947Q i.LINK Controller
+ 800c DTL-H800 [PS1 sound development board]
8039 CXD3222 i.LINK Controller
+ 8047 PS2 TOOL MRP
8056 Rockwell HCF 56K modem
808a Memory Stick Controller
+ 80ff PS2 Performance Analyzer
+ 814a PS2 Performance Analyzer
+ 8183 ATHENS [PS3 prototype developer interface card]
+ 81b0 BM-1 [PSP TOOL Board Management Device]
+ 81c3 VO-4 [PSP TOOL Video Output Device]
81ce SxS Pro memory card
+ 81ff PS3 TOOL MRP
+ 820e CXD9208GP [PS3 PS2 emulation subsystem adapter]
# 2nd ID
905c SxS Pro memory card
# 2nd ID
@@ -6245,6 +6307,8 @@
0074 U4 HT Bridge
# should be 14e4:1645
1645 Broadcom NetXtreme BCM5701 Gigabit Ethernet
+ 1801 T2 Bridge Controller
+ 1802 T2 Secure Enclave Processor
2001 S1X NVMe Controller
2002 S3ELab NVMe Controller
2003 S3X NVMe Controller
@@ -6360,6 +6424,8 @@
1077 02a8 QLE2692 Dual Port 16Gb FC to PCIe Gen3 x8 Adapter
1077 02ab QLE2740 Single Port 32Gb FC to PCIe Gen3 x8 Adapter
1077 02ac QLE2742 Dual Port 32Gb FC to PCIe Gen3 x8 Adapter
+ 1077 02b8 2x16Gb QME2692 FC HBA
+ 1077 02b9 2x32Gb QME2742 FC HBA
1590 00f9 StoreFabric SN1100Q 16Gb Single Port Fibre Channel Host Bus Adapter
1590 00fa StoreFabric SN1100Q 16Gb Dual Port Fibre Channel Host Bus Adapter
1590 0203 StoreFabric SN1600Q 32Gb Single Port Fibre Channel Host Bus Adapter
@@ -6429,10 +6495,14 @@
1077 0007 QLogic 2x1GE+2x10GE QL41264HMCU CNA
1077 0009 QLogic 2x1GE+2x10GE QL41162HMRJ CNA
1077 000b 25GE 2P QL41262HxCU-DE Adapter
+ 1077 000f 2x25GE QL41262HMKR CNA
+ 1077 0010 2x25GE QL41232HMKR NIC
1077 0011 FastLinQ QL41212HLCU 25GbE Adapter
1077 0012 FastLinQ QL41112H 10GbE Adapter
1077 0019 QL41232HOCU - Dual Port 25/10GbE SFP28 OCP Adapter
1077 0039 QLogic QL41262 PCIe 25Gb 2-Port SFP28 Ethernet Adapter
+ 1590 021a 10GbE 2P QL41162HLRJ-HP Adapter
+ 1590 021b 10GbE 2P QL41162HLRJ-HP Adapter
1590 021d 10/25GbE 2P QL41222HLCU-HP Adapter
1590 021e 10/25GbE 2P QL41162HMRJ-HP Adapter
1590 021f 10/25GbE 2P QL41262HMCU-HP Adapter
@@ -6449,6 +6519,9 @@
1077 000c QLogic 2x25GE QL41262HMCU CNA
1077 000d FastLinQ QL41262H 25GbE FCoE Adapter
1077 000e FastLinQ QL41162H 10GbE FCoE Adapter
+ 1077 000f 2x25GE QL41262HMKR CNA
+ 1590 021a 10GbE 2P QL41162HLRJ-HP Adapter
+ 1590 021b 10GbE 2P QL41162HLRJ-HP Adapter
8084 FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI)
1077 0001 10GE 2P QL41162HxRJ-DE Adapter
1077 0002 10GE 2P QL41112HxCU-DE Adapter
@@ -6462,6 +6535,9 @@
1077 000c QLogic 2x25GE QL41262HMCU CNA
1077 000d FastLinQ QL41262H 25GbE iSCSI Adapter
1077 000e FastLinQ QL41162H 10GbE iSCSI Adapter
+ 1077 000f 2x25GE QL41262HMKR 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 0001 25GE 2P QL41262HxCU-DE Adapter
1077 0002 10GE 2P QL41112HxCU-DE Adapter
@@ -6475,8 +6551,12 @@
1077 000c QLogic 2x25GE QL41262HMCU CNA
1077 000d FastLinQ QL41262H 25GbE FCoE Adapter (SR-IOV VF)
1077 000e FastLinQ QL41162H 10GbE iSCSI Adapter (SR-IOV VF)
+ 1077 000f 2x25GE QL41262HMKR CNA
+ 1077 0010 2x25GE QL41232HMKR NIC
1077 0011 FastLinQ QL41212H 25GbE Adapter (SR-IOV VF)
1077 0012 FastLinQ QL41112H 10GbE Adapter (SR-IOV VF)
+ 1590 021a 10GbE 2P QL41162HLRJ-HP Adapter
+ 1590 021b 10GbE 2P QL41162HLRJ-HP Adapter
1590 021e 10/25GbE 2P QL41162HMRJ-HP Adapter
1590 021f 10/25GbE 2P QL41262HMCU-HP Adapter
8430 ISP8324 1/10GbE Converged Network Controller (NIC VF)
@@ -7479,6 +7559,7 @@
036c Bt879(??) Video Capture
13e9 0070 Win/TV (Video Section)
036e Bt878 Video Capture
+ 0000 0001 Euresys Picolo PCIe
0070 13eb WinTV Series
0070 ff01 Viewcast Osprey 200
0071 0101 DigiTV PCI
@@ -7573,6 +7654,7 @@
1851 1851 FlyVideo'98 EZ - video
1852 1852 FlyVideo'98 (with FM Tuner)
0878 Bt878 Audio Capture
+ 0000 0001 Euresys Picolo PCIe
0070 13eb WinTV Series
0070 ff01 Viewcast Osprey 200
0071 0101 DigiTV PCI
@@ -7937,6 +8019,7 @@
1d49 0001 ThinkSystem 1610-4P NVMe Switch Adapter
1d49 0002 ThinkSystem 810-4P NVMe Switch Adapter
9749 PEX 9749 49-lane, 13-port PCI Express Gen 3 (8.0 GT/s) Switch
+ 1d49 0004 ThinkSystem 1610-8P NVMe Switch Adapter
a100 Blackmagic Design DeckLink
bb04 B&B 3PCIOSD1A Isolated PCI Serial
c001 CronyxOmega-PCI (8-port RS232)
@@ -9450,6 +9533,7 @@
040f G84GL [Quadro FX 1700]
0410 G92 [GeForce GT 330]
0414 G92 [GeForce 9800 GT]
+ 0418 G92 [GeForce GT 330 OEM]
0420 G86 [GeForce 8400 SE]
0421 G86 [GeForce 8500 GT]
1462 0960 NX8500GT-TD512EH/M2
@@ -10310,10 +10394,12 @@
0f00 GF108 [GeForce GT 630]
0f01 GF108 [GeForce GT 620]
0f02 GF108 [GeForce GT 730]
+ 0f03 GF108 [GeForce GT 610]
0f06 GF108 [GeForce GT 730]
0fb0 GM200 High Definition Audio
0fb8 GP108 High Definition Audio Controller
0fb9 GP107GL High Definition Audio Controller
+ 0fba GM206 High Definition Audio Controller
0fbb GM204 High Definition Audio Controller
0fc0 GK107 [GeForce GT 640 OEM]
0fc1 GK107 [GeForce GT 640]
@@ -10399,7 +10485,7 @@
# 06G-P4-2795-KR
3842 2795 GeForce GTX Titan SC Hydro Copper Signature
1007 GK110 [GeForce GTX 780 Rev. 2]
- 1008 GK110 [GeForce GTX 780 Ti Rev. 2]
+ 1008 GK110 [GeForce GTX 780 Ti 6GB]
100a GK110B [GeForce GTX 780 Ti]
100c GK110B [GeForce GTX TITAN Black]
101e GK110GL [Tesla K20X]
@@ -10495,6 +10581,9 @@
10ef GP102 HDMI Audio Controller
10f0 GP104 High Definition Audio Controller
10f1 GP106 High Definition Audio Controller
+ 10f7 TU102 High Definition 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]
1019 0799 GeForce 820M
1019 999f GeForce GT 720M
@@ -11033,6 +11122,7 @@
1392 GM107M [GeForce GTX 860M]
1393 GM107M [GeForce 840M]
1398 GM107M [GeForce 845M]
+ 1399 GM107M [GeForce 945M]
139a GM107M [GeForce GTX 950M]
17aa 362c GeForce GTX 950A
17aa 362f GeForce GTX 950A
@@ -11041,6 +11131,7 @@
17aa 3647 GeForce GTX 950A
17aa 36b9 GeForce GTX 950A
139b GM107M [GeForce GTX 960M]
+ 1028 06e4 XPS 15 9550
103c 2b4c GeForce GTX 960A
139c GM107M [GeForce 940M]
139d GM107M [GeForce GTX 750 Ti]
@@ -11106,14 +11197,22 @@
174d GM108M [GeForce MX130]
174e GM108M [GeForce MX110]
1789 GM107GL [GRID M3-3020]
+ 179c GM107 [GeForce 940MX]
17c2 GM200 [GeForce GTX TITAN X]
17c8 GM200 [GeForce GTX 980 Ti]
17f0 GM200GL [Quadro M6000]
17f1 GM200GL [Quadro M6000 24GB]
17fd GM200GL [Tesla M40]
+ 1ad6 TU102 USB 3.1 Controller
+ 1ad7 TU102 UCSI Controller
+ 1ada TU106 USB 3.1 Host Controller
+ 1043 8673 TURBO-RTX2070-8G
+ 1adb TU106 USB Type-C Port Policy Controller
+ 1043 8673 TURBO-RTX2070-8G
1b00 GP102 [TITAN X]
1b01 GP102
1b02 GP102 [TITAN Xp]
+ 1b04 GP102
1b06 GP102 [GeForce GTX 1080 Ti]
1b07 GP102 [P102-100]
1b30 GP102GL [Quadro P6000]
@@ -11132,23 +11231,27 @@
1462 11e8 GeForce GTX 1070 Max-Q
1462 11e9 GeForce GTX 1070 Max-Q
1558 9501 GeForce GTX 1070 Max-Q
+ 1ba2 GP104M [GeForce GTX 1070 Mobile]
1bad GP104 [GeForce GTX 1070 Engineering Sample]
1bb0 GP104GL [Quadro P5000]
1bb1 GP104GL [Quadro P4000]
1bb3 GP104GL [Tesla P4]
1bb4 GP104GL [Tesla P6]
1bb5 GP104GLM [Quadro P5200 Mobile]
+ 103c 842f P5200 [Zbook 17 G5 mobile workstation]
1bb6 GP104GLM [Quadro P5000 Mobile]
1bb7 GP104GLM [Quadro P4000 Mobile]
1462 11e9 Quadro P4000 Max-Q
1bb8 GP104GLM [Quadro P3000 Mobile]
1bb9 GP104GLM [Quadro P4200 Mobile]
+ 103c 842f P4200 [Zbook 17 G5 mobile workstation]
1bbb GP104GLM [Quadro P3200 Mobile]
+ 103c 842f P3200 [Zbook 17 G5 moble workstation]
1bc7 GP104 [P104-101]
- 1be0 GP104M [GeForce GTX 1080 Mobile]
+ 1be0 GP104BM [GeForce GTX 1080 Mobile]
1028 07c0 GeForce GTX 1080 Max-Q
1458 355b GeForce GTX 1080 Max-Q
- 1be1 GP104M [GeForce GTX 1070 Mobile]
+ 1be1 GP104BM [GeForce GTX 1070 Mobile]
1c00 GP106
1c01 GP106
1c02 GP106 [GeForce GTX 1060 3GB]
@@ -11161,19 +11264,23 @@
17aa 39b9 GeForce GTX 1060 Max-Q 3GB
1c21 GP106M [GeForce GTX 1050 Ti Mobile]
1c22 GP106M [GeForce GTX 1050 Mobile]
+ 1c23 GP106M [GeForce GTX 1060 Mobile Rev. 2]
+ 1414 0020 GTX 1060 Mobile
1c30 GP106GL [Quadro P2000]
1c35 GP106
- 1c60 GP106M [GeForce GTX 1060 Mobile 6GB]
+ 1c60 GP106BM [GeForce GTX 1060 Mobile 6GB]
103c 8390 GeForce GTX 1060 Max-Q 6GB
- 1c61 GP106M [GeForce GTX 1050 Ti Mobile]
- 1c62 GP106M [GeForce GTX 1050 Mobile]
+ 1c61 GP106BM [GeForce GTX 1050 Ti Mobile]
+ 1c62 GP106BM [GeForce GTX 1050 Mobile]
1c70 GP106GL
- 1c80 GP107 [GeForce GTX 1050 3GB]
1c81 GP107 [GeForce GTX 1050]
1c82 GP107 [GeForce GTX 1050 Ti]
+ 1c83 GP107 [GeForce GTX 1050 3GB]
1c8c GP107M [GeForce GTX 1050 Ti Mobile]
1c8d GP107M [GeForce GTX 1050 Mobile]
1c8e GP107M
+ 1c8f GP107M [GeForce GTX 1050 Ti Max-Q]
+ 1c92 GP107M [GeForce GTX 1050 Mobile]
1ca7 GP107GL
1ca8 GP107GL
1caa GP107GL
@@ -11182,21 +11289,56 @@
1cb3 GP107GL [Quadro P400]
1cb6 GP107GL [Quadro P620]
1cba GP107GLM [Quadro P2000 Mobile]
+ 103c 842c P2000 [Zbook 15 G5 mobile workstation]
+ 103c 842f P2000 [Zbook 17 G5 mobile workstation]
1cbb GP107GLM [Quadro P1000 Mobile]
+ 103c 8429 P1000 [Zbook Studio G5 mobile workstation]
+ 103c 842c P1000 [Zbook 15 G5 mobile workstation]
+ 103c 842f P1000 [Zbook 17 G5 mobile workstation]
+ 103c 8451 P1000 [Zbook Studio x360 G5 mobile workstation]
1cbc GP107GLM [Quadro P600 Mobile]
+ 1ccc GP107BM [GeForce GTX 1050 Ti Mobile]
+ 1ccd GP107BM [GeForce GTX 1050 Mobile]
1d01 GP108 [GeForce GT 1030]
1d10 GP108M [GeForce MX150]
+ 17aa 225e ThinkPad T480
1d12 GP108M [GeForce MX150]
1d72 1701 Mi Notebook Pro [GeForce MX150]
1d33 GP108GLM [Quadro P500 Mobile]
1d81 GV100 [TITAN V]
1db1 GV100GL [Tesla V100 SXM2 16GB]
+ 1db2 GV100 [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]
1dba GV100GL [Quadro GV100]
+ 10de 12eb TITAN V CEO Edition
+ 1e02 TU102 [TITAN RTX]
+ 1e04 TU102 [GeForce RTX 2080 Ti]
+ 1e07 TU102 [GeForce RTX 2080 Ti Rev. A]
+ 1462 3715 RTX 2080 Ti GAMING X TRIO
+ 1e2d TU102B
+ 1e2e TU102B
+ 1e30 TU102GL [Quadro RTX 6000]
+ 1e38 TU102GL
+ 1e3c TU102GL
+ 1e3d TU102GL
+ 1e3e TU102GL
+ 1e82 TU104 [GeForce RTX 2080]
+ 1e87 TU104 [GeForce RTX 2080 Rev. A]
+ 1eab TU104M [GeForce RTX 2080 Mobile]
+ 1eae TU104M
+ 1eb0 TU104GL [Quadro RTX 5000]
+ 1eb1 TU104GL [Quadro RTX 4000]
+ 1eb8 TU104GL [Tesla T4]
+ 1f02 TU106 [GeForce RTX 2070]
+ 1043 8673 TURBO RTX 2070
+ 1f04 TU106
+ 1f07 TU106 [GeForce RTX 2070 Rev. A]
+ 1f08 TU106
+ 1f82 TU107
10df Emulex Corporation
0720 OneConnect NIC (Skyhawk)
103c 1934 FlexFabric 20Gb 2-port 650M Adapter
@@ -11237,6 +11379,7 @@
10df e322 Lancer Gen6: LPe31000 Fibre Channel Host Adapter
10df e323 Lancer Gen6: LPe31000 Fibre Channel Host Adapter
10df e325 Lancer Gen6: LPe31000 Fibre Channel Host Adapter
+ e333 Lancer Gen6: LPe32000 Fibre Channel Host Adapter
f011 Saturn: LightPulse Fibre Channel Host Adapter
f015 Saturn: LightPulse Fibre Channel Host Adapter
f085 LP850 Fibre Channel Host Adapter
@@ -11259,6 +11402,7 @@
f400 LPe36000 Fibre Channel Host Adapter [Prism]
10df f401 LPe35000 Fibre Channel Host Adapter [Prism]
10df f402 LPe35000 Fibre Channel Host Adapter [Prism]
+ 10df f410 LPe35002-M2-D 2-Port 32Gb Fibre Channel Adapter
f700 LP7000 Fibre Channel Host Adapter
f701 LP7000 Fibre Channel Host Adapter Alternate ID (JX1:2-3, JX2:1-2)
f800 LP8000 Fibre Channel Host Adapter
@@ -11306,6 +11450,7 @@
1775 1100 VR11 Single Board Computer
0860 CA91C860 [QSpan]
0862 CA91C862A [QSpan-II]
+ 8111 Tsi381 PCIe to PCI Bridge
8260 CA91L8200B [Dual PCI PowerSpan II]
8261 CA91L8260B [Single PCI PowerSpan II]
a108 Tsi109 Host Bridge for Dual PowerPC
@@ -11381,6 +11526,8 @@
524a RTS524A PCI Express Card Reader
5250 RTS5250 PCI Express Card Reader
525a RTS525A PCI Express Card Reader
+ 1028 06dc Latitude E7470
+ 1028 06e4 XPS 15 9550
17aa 224f ThinkPad X1 Carbon 5th Gen
5286 RTS5286 PCI Express Card Reader
5287 RTL8411B PCI Express Card Reader
@@ -11540,6 +11687,7 @@
10ec 8739 Dell Wireless 1801
b822 RTL8822BE 802.11a/b/g/n/ac WiFi adapter
c821 RTL8821CE 802.11ac PCIe Wireless Network Adapter
+ d723 RTL8723DE 802.11b/g/n PCIe Adapter
10ed Ascii Corporation
7310 V7310
10ee Xilinx Corporation
@@ -11569,6 +11717,7 @@
ebf0 SED Systems Modulator/Demodulator
ebf1 SED Systems Audio Interface Card
ebf2 SED Systems Common PCI Interface
+ ebf3 SED Systems PCIe-AXI Bridge
10ef Racore Computer Products, Inc.
8154 M815x Token Ring Adapter
10f0 Peritek Corporation
@@ -12626,6 +12775,7 @@
806c PES16T4A/4T4G2 PCI Express Gen2 Switch
806e PES24T6G2 PCI Express Gen2 Switch
806f HIO524G2 PCI Express Gen2 Switch
+ 8077 89HPES64H16G2 64-Lane 16-Port PCIe Gen2 System Interconnect Switch
8088 PES32NT8BG2 PCI Express Switch
1093 752f PXIe-8383mc Device
1093 7543 PXIe-8383mc System Host
@@ -12670,7 +12820,8 @@
1127 0400 ForeRunnerHE ATM
1129 Firmworks
112a Hermes Electronics Company, Ltd.
-112b Linotype - Hell AG
+# nee Linotype - Hell AG
+112b Heidelberger Druckmaschinen AGHeidelberger Druckmaschinen AG
112c Zenith Data Systems
112d Ravicad
112e Infomedia Microelectronics Inc.
@@ -13057,6 +13208,15 @@
1137 012e VIC 1227 PCIe Ethernet NIC
1137 0137 VIC 1380 Mezzanine Ethernet NIC
1137 014d VIC 1385 PCIe Ethernet NIC
+ 1137 015d VIC 1387 MLOM Ethernet NIC
+ 1137 0215 VIC 1440 Mezzanine Ethernet NIC
+ 1137 0216 VIC 1480 MLOM Ethernet NIC
+ 1137 0217 VIC 1455 PCIe Ethernet NIC
+ 1137 0218 VIC 1457 MLOM Ethernet NIC
+ 1137 0219 VIC 1485 PCIe Ethernet NIC
+ 1137 021a VIC 1487 MLOM Ethernet NIC
+ 1137 024a VIC 1495 PCIe Ethernet NIC
+ 1137 024b VIC 1497 MLOM Ethernet NIC
0044 VIC Ethernet NIC Dynamic
1137 0047 VIC P81E PCIe Ethernet NIC Dynamic
1137 0048 VIC M81KR Mezzanine Ethernet NIC Dynamic
@@ -13102,6 +13262,7 @@
1137 012c VIC 1340 MLOM Userspace NIC
1137 012e VIC 1227 PCIe Userspace NIC
1137 0137 VIC 1380 Mezzanine Userspace NIC
+ 023e 1GigE I350 LOM
1138 Ziatech Corporation
8905 8905 [STD 32 Bridge]
1139 Dynamic Pictures, Inc
@@ -13477,6 +13638,13 @@
0102 Extended IDE Controller
0103 EX-IDE Type-B
010f NVMe Controller
+ 0110 NVMe SSD Controller Cx5
+ 1028 1ffb Express Flash NVMe 960G (RI) U.2 (CD5)
+ 1028 1ffc Express Flash NVMe 1.92T (RI) U.2 (CD5)
+ 1028 1ffd Express Flash NVMe 3.84T (RI) U.2 (CD5)
+ 1028 1ffe Express Flash NVMe 7.68T (RI) U.2 (CD5)
+ 1d49 4039 Thinksystem U.2 CM5 NVMe SSD
+ 1d49 403a Thinksystem AIC CM5 NVMe SSD
0115 XG4 NVMe SSD Controller
0404 DVD Decoder card
0406 Tecra Video Capture device
@@ -13579,7 +13747,7 @@
14ef 0220 PCD-RP-220S
17aa 201c ThinkPad X60/X60s
17aa 20c4 ThinkPad T61/R61
- 17aa 20c6 ThinkPad R61
+ 17aa 20c6 ThinkPad R61/T400
0477 RL5c477
0478 RL5c478
1014 0184 ThinkPad A30p
@@ -13614,7 +13782,7 @@
1043 1237 A6J-Q008
1043 1967 V6800V
144d c018 X20 IV
- 17aa 20ca ThinkPad T61
+ 17aa 20ca ThinkPad T61/T400
0811 R5C811
0822 R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter
1014 0556 ThinkPad X40 / X41 / X60s / Z60t
@@ -13636,7 +13804,7 @@
144d c018 X20 IV
17aa 201d ThinkPad X60/X60s
17aa 20c7 ThinkPad T61
- 17aa 20c8 ThinkPad W500
+ 17aa 20c8 ThinkPad T400/W500
0832 R5C832 IEEE 1394 Controller
1025 0121 Aspire 5920G
1028 01d7 XPS M1210
@@ -13673,6 +13841,7 @@
1043 1967 V6800V
1180 0852 Pavilion 2410us
1324 10cf P7120
+ 17aa 20cb ThinkPad T400
e230 R5U2xx (R5U230 / R5U231 / R5U241) [Memory Stick Host Controller]
e476 CardBus bridge
1028 040a Latitude E6410
@@ -13680,6 +13849,7 @@
e822 MMC/SD Host Controller
1028 040a Latitude E6410
1028 040b Latitude E6510
+ 17aa 21cf ThinkPad T520
e823 PCIe SDXC/MMC Host Controller
17aa 21cf ThinkPad T520
e832 R5C832 PCIe IEEE 1394 Controller
@@ -13801,6 +13971,10 @@
0003 FireStream 50
119f Bull HN Information Systems
1081 BXI Host Channel Adapter
+# BXI stands for Bull eXascale Interconnect
+ 1101 BXI Host Channel Adapter v1.2
+# BXI stands for Bull eXascale Interconnect
+ 1121 BXI Host Channel Adapter v1.3
11a0 Convex Computer Corporation
11a1 Hamamatsu Photonics K.K.
11a2 Sierra Research and Technology
@@ -14340,6 +14514,7 @@
7384 PM7384 [FREEDM - 84P672 Frm Engine & Datalink Mgr]
8000 PM8000 [SPC - SAS Protocol Controller]
8009 PM8009 SPCve 8x6G
+ 8018 PM8018 Adaptec SAS Adaptor ASA-70165H PCIe Gen3 x8 6 Gbps 16-lane 4x SFF-8644
8032 PM8032 Tachyon QE8
117c 003a Celerity FC-81EN Fibre Channel Adapter
117c 003b Celerity FC-82EN Fibre Channel Adapter
@@ -16195,9 +16370,10 @@
1612 0000 PCI-1612 4-port RS-232/422/485
1711 PCI-1711 16-channel data acquisition card 12-bit, 100kS/s
1733 PCI-1733 32-channel isolated digital input card
- 1752 PCI-1752
- 1754 PCI-1754
- 1756 PCI-1756
+ 1734 PCI-1734 32-channel isolated digital output card
+ 1752 PCI-1752 64-channel Isolated Digital Output Card
+ 1754 PCI-1754 64-channel Isolated Digital Input Card
+ 1756 PCI-1756 64-ch Isolated Digital I/O PCI Card
# FPGA bridge to two SJA1000
c302 MIOe-3680 2-Port CAN-Bus MIOe Module with Isolation Protection
13ff Silicon Spice Inc
@@ -16660,6 +16836,9 @@
50ab T520-50AB Unified Wire Ethernet Controller
50ac T540-50AC Unified Wire Ethernet Controller
50ad T520-50AD Unified Wire Ethernet Controller
+ 50ae T540-50AE Unified Wire Ethernet Controller
+ 50af T580-50AF Unified Wire Ethernet Controller
+ 50b0 T520-50B0 Unified Wire Ethernet Controller
5401 T520-CR Unified Wire Ethernet Controller
5402 T522-CR Unified Wire Ethernet Controller
5403 T540-CR Unified Wire Ethernet Controller
@@ -16727,6 +16906,9 @@
54ab T520-50AB Unified Wire Ethernet Controller
54ac T540-50AC Unified Wire Ethernet Controller
54ad T520-50AD Unified Wire Ethernet Controller
+ 54ae T540-50AE Unified Wire Ethernet Controller
+ 54af T580-50AF Unified Wire Ethernet Controller
+ 54b0 T520-50B0 Unified Wire Ethernet Controller
5501 T520-CR Unified Wire Storage Controller
5502 T522-CR Unified Wire Storage Controller
5503 T540-CR Unified Wire Storage Controller
@@ -16794,6 +16976,9 @@
55ab T520-50AB Unified Wire Storage Controller
55ac T540-50AC Unified Wire Storage Controller
55ad T520-50AD Unified Wire Storage Controller
+ 55ae T540-50AE Unified Wire Storage Controller
+ 55af T580-50AF Unified Wire Storage Controller
+ 55b0 T520-50B0 Unified Wire Storage Controller
5601 T520-CR Unified Wire Storage Controller
5602 T522-CR Unified Wire Storage Controller
5603 T540-CR Unified Wire Storage Controller
@@ -16861,6 +17046,9 @@
56ab T520-50AB Unified Wire Storage Controller
56ac T540-50AC Unified Wire Storage Controller
56ad T520-50AD Unified Wire Storage Controller
+ 56ae T540-50AE Unified Wire Storage Controller
+ 56af T580-50AF Unified Wire Storage Controller
+ 56b0 T520-50B0 Unified Wire Storage Controller
5701 T520-CR Unified Wire Ethernet Controller
5702 T522-CR Unified Wire Ethernet Controller
5703 T540-CR Unified Wire Ethernet Controller
@@ -16967,6 +17155,9 @@
58ab T520-50AB Unified Wire Ethernet Controller [VF]
58ac T540-50AC Unified Wire Ethernet Controller [VF]
58ad T520-50AD Unified Wire Ethernet Controller [VF]
+ 58ae T540-50AE Unified Wire Ethernet Controller [VF]
+ 58af T580-50AF Unified Wire Ethernet Controller [VF]
+ 58b0 T520-50B0 Unified Wire Ethernet Controller [VF]
6001 T6225-CR Unified Wire Ethernet Controller
6002 T6225-SO-CR Unified Wire Ethernet Controller
6003 T6425-CR Unified Wire Ethernet Controller
@@ -16990,6 +17181,7 @@
6087 T6225-6087 Unified Wire Ethernet Controller
6088 T62100-6088 Unified Wire Ethernet Controller
6089 T62100-6089 Unified Wire Ethernet Controller
+ 608a T62100-608a Unified Wire Ethernet Controller
6401 T6225-CR Unified Wire Ethernet Controller
6402 T6225-SO-CR Unified Wire Ethernet Controller
6403 T6425-CR Unified Wire Ethernet Controller
@@ -17013,6 +17205,7 @@
6487 T6225-6087 Unified Wire Ethernet Controller
6488 T62100-6088 Unified Wire Ethernet Controller
6489 T62100-6089 Unified Wire Ethernet Controller
+ 648a T62100-608a Unified Wire Ethernet Controller
6501 T6225-CR Unified Wire Storage Controller
6502 T6225-SO-CR Unified Wire Storage Controller
6503 T6425-CR Unified Wire Storage Controller
@@ -17036,6 +17229,7 @@
6587 T6225-6087 Unified Wire Storage Controller
6588 T62100-6088 Unified Wire Storage Controller
6589 T62100-6089 Unified Wire Storage Controller
+ 658a T62100-608a Unified Wire Storage Controller
6601 T6225-CR Unified Wire Storage Controller
6602 T6225-SO-CR Unified Wire Storage Controller
6603 T6425-CR Unified Wire Storage Controller
@@ -17059,6 +17253,7 @@
6687 T6225-6087 Unified Wire Storage Controller
6688 T62100-6088 Unified Wire Storage Controller
6689 T62100-6089 Unified Wire Storage Controller
+ 668a T62100-608a Unified Wire Storage Controller
6801 T6225-CR Unified Wire Ethernet Controller [VF]
6802 T6225-SO-CR Unified Wire Ethernet Controller [VF]
6803 T6425-CR Unified Wire Ethernet Controller [VF]
@@ -17082,6 +17277,7 @@
6887 T6225-6087 Unified Wire Ethernet Controller [VF]
6888 T62100-6088 Unified Wire Ethernet Controller [VF]
6889 T62100-6089 Unified Wire Ethernet Controller [VF]
+ 688a T62100-608a Unified Wire Ethernet Controller [VF]
a000 PE10K Unified Wire Ethernet Controller
1426 Storage Technology Corp.
1427 Better On-Line Solutions
@@ -17156,6 +17352,7 @@
a802 NVMe SSD Controller SM951/PM951
a804 NVMe SSD Controller SM961/PM961
a808 NVMe SSD Controller SM981/PM981
+ 1d49 403b Thinksystem U.2 PM983 NVMe SSD
a820 NVMe SSD Controller 171X
1028 1f95 Express Flash NVMe XS1715 SSD 400GB
1028 1f96 Express Flash NVMe XS1715 SSD 800GB
@@ -17176,6 +17373,9 @@
1014 0621 PCIe3 1.6TB NVMe Flash Adapter II x8
1014 0622 PCIe3 3.2TB NVMe Flash Adapter II x8
1014 0629 PCIe3 6.4TB NVMe Flash Adapter II x8
+ 1014 064a PCIe3 1.6TB NVMe Flash Adapter III x8
+ 1014 064b PCIe3 3.2TB NVMe Flash Adapter III x8
+ 1014 064c PCIe3 6.4TB NVMe Flash Adapter III x8
1028 1fd9 Express Flash PM1725a 800GB SFF
1028 1fda Express Flash PM1725a 1.6TB SFF
1028 1fdb Express Flash PM1725a 3.2TB SFF
@@ -17412,6 +17612,9 @@
e010 VScom 100HV2 1 port serial adaptor
e020 VScom 200HV2 2 port serial adaptor
14d3 CIRTECH (UK) Ltd
+ 0002 DTL-T14000 Rev. 1 [PS2 TOOL CD/DVD Emulator]
+ 0003 DTL-T14000 Rev. 2 [PS2 TOOL CD/DVD Emulator]
+ 0004 DTL-T14000 Rev. 3 [PS2 TOOL CD/DVD Emulator]
14d4 Panacom Technology Corp
14d5 Nitsuko Corporation
14d6 Accusys Inc
@@ -17446,8 +17649,7 @@
14e1 INVERTEX
14e2 INFOLIBRIA
14e3 AMTELCO
-# Formerly Broadcom Corporation
-14e4 Broadcom Limited
+14e4 Broadcom Inc. and subsidiaries
0576 BCM43224 802.11a/b/g/n
0800 Sentry5 Chipcommon I/O Controller
0804 Sentry5 PCI Bridge
@@ -17636,7 +17838,8 @@
103c 0890 NC6000 laptop
103c 099c NX6110/NC6120
10cf 1279 LifeBook E8010D
- 165f NetXtreme BCM5720 Gigabit Ethernet PCIe
+ 165f NetXtreme BCM5720 2-port Gigabit Ethernet PCIe
+ 1028 04f7 PowerEdge R320 server
1662 NetXtreme II BCM57712 10 Gigabit Ethernet
1663 NetXtreme II BCM57712 10 Gigabit Ethernet Multi Function
1665 NetXtreme BCM5717 Gigabit Ethernet PCIe
@@ -18161,7 +18364,10 @@
5840 BCM5840 Crypto Accelerator
5841 BCM5841 Crypto Accelerator
5850 BCM5850 Crypto Accelerator
+ 5e87 Valkyrie offload engine
8602 BCM7400/BCM7405 Serial ATA Controller
+ 9026 CN99xx [ThunderX2] Integrated USB 3.0 xHCI Host Controller
+ 9027 CN99xx [ThunderX2] Integrated AHCI/SATA 3 Host Controller
a8d8 BCM43224/5 Wireless Network Adapter
aa52 BCM43602 802.11ac Wireless LAN SoC
b302 BCM56302 StrataXGS 24x1GE 2x10GE Switch Controller
@@ -18179,6 +18385,11 @@
b850 Broadcom BCM56850 Switch ASIC
# Tomahawk
b960 Broadcom BCM56960 Switch ASIC
+ d802 BCM58802 Stingray 50Gb Ethernet SoC
+ 14e4 8021 Stingray Dual-Port 25Gb Ethernet PCIe SmartNIC w16GB DRAM (Part No BCM958802A8046C)
+ 14e4 8024 Stingray Dual-Port 25Gb Ethernet PCIe SmartNIC w4GB DRAM (Part No BCM958802A8044C)
+ 14e4 8028 Stingray Dual-Port 25Gb Ethernet PCIe SmartNIC w8GB DRAM (Part No BCM958802A8048C)
+ d804 BCM58804 Stingray 100Gb Ethernet SoC
14e5 Pixelfusion Ltd
14e6 SHINING Technology Inc
14e7 3CX
@@ -18836,6 +19047,7 @@
020f MT28908A0 Family [ConnectX-6 Flash Recovery]
0210 MT28908A0 Family [ConnectX-6 Secure Flash Recovery]
0211 MT416842 Family [BlueField SoC Flash Recovery]
+ 0212 MT2892 Family [ConnectX-6 Dx Flash Recovery]
024e MT53100 [Spectrum-2, Flash recovery mode]
024f MT53100 [Spectrum-2, Secure Flash recovery mode]
0262 MT27710 [ConnectX-4 Lx Programmable] EN
@@ -18917,13 +19129,16 @@
101a MT28800 Family [ConnectX-5 Ex Virtual Function]
101b MT28908 Family [ConnectX-6]
101c MT28908 Family [ConnectX-6 Virtual Function]
- 101d MT28841
- 101e MT28850
+ 101d MT2892 Family [ConnectX-6 Dx]
+ 101e MT2892 Family [ConnectX-6 Dx Virtual Function]
101f MT28851
1020 MT28860
1021 MT28861
1974 MT28800 Family [ConnectX-5 PCIe Bridge]
1975 MT416842 Family [BlueField SoC PCIe Bridge]
+ 4117 MT27712A0-FDCF-AE
+ 1bd4 0039 SN10XMP2P25
+ 1bd4 004d SN10XMP2P25,YZPC-01191-101
5274 MT21108 InfiniBridge
5a44 MT23108 InfiniHost
5a45 MT23108 [Infinihost HCA Flash Recovery]
@@ -18980,7 +19195,7 @@
cb84 MT52100
cf08 MT53236
cf6c MT53100 [Spectrum-2]
- d2f0 Switch-IB 3 HDR (200Gbps) switch
+ d2f0 Quantum HDR (200Gbps) switch
15b4 CCI/TRIAD
15b5 Cimetrics Inc
15b6 Texas Memory Systems Inc
@@ -19003,6 +19218,7 @@
15b7 Sandisk Corp
2001 Skyhawk Series NVME SSD
5001 WD Black NVMe SSD
+ 5002 WD Black 2018/PC SN720 NVMe SSD
15b8 ADDI-DATA GmbH
1001 APCI1516 SP controller (16 digi outputs)
1003 APCI1032 SP controller (32 digi inputs w/ opto coupler)
@@ -19268,6 +19484,8 @@
7191 Proc10a_48S
71a1 Proc10a_66S
71b1 Proc10A
+ 72b1 HawkEye
+ 73b1 Proc10s
165d Hsing Tech. Enterprise Co., Ltd.
165f Linux Media Labs, LLC
1020 LMLM4 MPEG-4 encoder
@@ -19298,6 +19516,7 @@
167e ONNTO Corp.
1681 Hercules
1682 XFX Pine Group Inc.
+ c580 Radeon RX 580
1688 CastleNet Technology Inc.
1170 WLAN 802.11b card
# nee Atheros Communications, Inc.
@@ -19529,6 +19748,7 @@
1a3b 2100 AW-NB100H 802.11n Wireless Mini PCIe Card
003c QCA986x/988x 802.11ac Wireless Network Adapter
003e QCA6174 802.11ac Wireless Network Adapter
+ 1a56 143a Killer 1435 Wireless-AC
1a56 1525 Killer N1525 Wireless-AC
0040 QCA9980/9990 802.11ac Wireless Network Adapter
0041 QCA6164 802.11ac Wireless Network Adapter
@@ -19613,6 +19833,8 @@
4353 PMC-DX2003 Reconfigurable FPGA with TTL and Differential I/O
4357 PMC-DX502 Reconfigurable Differential I/O Module
4457 PMC730, APC730, AcPC730 Multifunction Module
+ 4471 XMC730 Multi-function I/O module with front I/O
+ 4473 XMC730CC Multi-function I/O module with rear I/O Conduction-cooled
464d PMC408 32-Channel Digital Input/Output Module
4850 PMC220-16 12-Bit Analog Output Module
4a42 PMC483, APC483, AcPC483 Counter Timer Module
@@ -19748,6 +19970,13 @@
1760 TEDIA spol. s r. o.
0101 PCD-7004 Digital Bi-Directional Ports PCI Card
0102 PCD-7104 Digital Input & Output PCI Card
+ 0121 PCT-7303A PC card with IRC counters
+ 0122 PCT-7408A PC card with counters and timers
+ 0123 PCT-7424 PCI card with standard counters
+ 0214 PCT-7424C (F0) PC card with standard counters
+ 0215 PCT-7424C (F1) PC card with standard counters
+ 0216 PCT-7424E (F0) PC card with standard counters
+ 0217 PCT-7424E (F1) PC card with standard counters
0303 PCD-7006C Digital Input & Output PCI Card
1761 Pickering Interfaces Ltd
1771 InnoVISION Multimedia Ltd.
@@ -19850,6 +20079,8 @@
a100 THUNDERX CN88XX 48 core SoC
a200 OCTEON TX CN81XX/CN80XX
a300 OCTEON TX CN83XX
+ af00 CN99xx [ThunderX2] Integrated PCI Host bridge
+ af84 CN99xx [ThunderX2] Integrated PCI Express RP Bridge
1787 Hightech Information System Ltd.
1789 Ennyah Technologies Corp.
# also used by Struck Innovative Systeme for joint developments
@@ -19908,6 +20139,7 @@
7163 GL9701 PCIe to PCI Bridge
8083 GL880 USB 1.1 UHCI controller
8084 GL880 USB 2.0 EHCI controller
+ 9750 GL9750 SD Host Controller
17aa Lenovo
402b Intel 82599ES 10Gb 2-port Server Adapter X520-2
17ab Phillips Components
@@ -20537,16 +20769,19 @@
1924 8019 SFN8542-R2 8000 Series 10/40G Adapter
1924 801a SFN8722-R1 8000 Series OCP 10G Adapter
1924 801b SFN8522-R3 8000 Series 10G Adapter
- 0b03 SFC9250 10/25/40/50/100G Ethernet Controller
+ 1924 801c SFN8042-R3 8000 Series 10/40G Adapter
+ 1924 8021 SFN8041-R1 8000 Series 10/40G Adapter
+ 0b03 XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller
1924 801d x2522-R1 2000 Series 10/25G Adapter
1924 801e x2542-R1 2000 Series 40/100G Adapter
- 1924 8022 x2522-R2 2000 Series 10/25G Adapter
+ 1924 8022 XtremeScale X2522 10G Network Adapter
+ 1924 8028 XtremeScale X2522-25G Network Adapter
1803 SFC9020 10G Ethernet Controller (Virtual Function)
1813 SFL9021 10GBASE-T Ethernet Controller (Virtual Function)
1903 SFC9120 10G Ethernet Controller (Virtual Function)
1923 SFC9140 10/40G Ethernet Controller (Virtual Function)
1a03 SFC9220 10/40G Ethernet Controller (Virtual Function)
- 1b03 SFC9250 10/25/40/50/100G Ethernet Controller (Virtual Function)
+ 1b03 XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (Virtual Function)
6703 SFC4000 rev A iSCSI/Onload [Solarstorm]
10b8 0102 SMC10GPCIe-10BT (A2) [TigerCard]
10b8 0103 SMC10GPCIe-10BT (A3) [TigerCard]
@@ -20562,6 +20797,7 @@
1924 0500 SFE4005-A0
c101 EF1-21022T [EtherFabric]
192a BiTMICRO Networks Inc.
+ 0008 RAMPART
192e TransDimension
1931 Option N.V.
000c Qualcomm MSM6275 UMTS chip
@@ -20725,6 +20961,7 @@
2048 Attansic L2 Fast Ethernet
2060 AR8152 v1.1 Fast Ethernet
2062 AR8152 v2.0 Fast Ethernet
+ 1043 8468 Eee PC 1015PX
# E2200, E2201, E2205
e091 Killer E220x Gigabit Ethernet Controller
e0a1 Killer E2400 Gigabit Ethernet Controller
@@ -20734,10 +20971,14 @@
0102 NodalCore C-2000 Content Classification Accelerator
0105 NodalCore C-3000 Content Classification Accelerator
196d Club-3D BV
+196e PNY
1971 AGEIA Technologies, Inc.
1011 Physics Processing Unit [PhysX]
1043 0001 PhysX P1
-1974 Eberspaecher Electronics
+# nee Eberspaecher Electronics
+1974 Star Electronics GmbH & Co. KG
+ 0009 FlexCard PMC-II
+ 0011 FlexCard PMC-II Ethernet
1976 TRENDnet
1977 Parsec
197b JMicron Technology Corp.
@@ -20774,6 +21015,7 @@
1982 Distant Early Warning Communications Inc
1600 OX16C954 HOST-A
16ff OX16C954 HOST-B
+1987 Phison Electronics Corporation
1989 Montilio Inc.
0001 RapidFile Bridge
8001 RapidFile
@@ -20882,6 +21124,7 @@
a238 HiSilicon USB 3.0 Host Controller
a239 HiSilicon USB 2.0 2-port Host Controller
a23a HiSilicon USB 2.0 Host Controller
+ a23b HiSilicon USB 1.1 Host Controller
a250 HiSilicon ZIP Engine
a251 HiSilicon ZIP Engine(Virtual Function)
a255 HiSilicon SEC Engine
@@ -20902,6 +21145,7 @@
1a03 ASPEED Technology, Inc.
1150 AST1150 PCI-to-PCI Bridge
2000 ASPEED Graphics Family
+ 15d9 0832 X10SRL-F
1a07 Kvaser AB
0006 CAN interface PC104+ HS/HS
0007 CAN interface PCIcanx II HS or HS/HS
@@ -21004,7 +21248,7 @@
0009 RAIDCore Controller
000a RAIDCore Controller
1aae Global Velocity, Inc.
-1ab4 FFEI Ltd
+1ab4 Distributed Management Task Force, Inc. (DMTF)Distributed Management Task Force, Inc. (DMTF)
1ab6 CalDigit, Inc.
6201 RAID Card
# Parallels VM virtual devices
@@ -21032,7 +21276,7 @@
0a44 microEnable IV-FULL x4
0e44 microEnable IV-GigE x4
1ae9 Wilocity Ltd.
- 0101 Wil6200 PCI Express Root Port
+ 0101 Wil6200 PCI Express Upstream Port
0200 Wil6200 PCI Express Port
0201 Wil6200 Wireless PCI Express Port
0301 Wil6200 802.11ad Wireless Network Adapter
@@ -21109,8 +21353,22 @@
1080 ASM1083/1085 PCIe to PCI Bridge
1849 1080 Motherboard
1142 ASM1042A USB 3.0 Host Controller
+ 1184 ASM1184e PCIe Switch Port
+ 1849 1184 ASM1184e PCIe Switch
1242 ASM1142 USB 3.1 Host Controller
1343 ASM1143 USB 3.1 Host Controller
+ 2142 ASM2142 USB 3.1 Host Controller
+1b26 Netcope Technologies, a.s.
+ c132 COMBO-LXT155
+ c1c0 NFB-100G1-e0
+ c1c1 NFB-100G1-e1
+ c250 NFB-200G2-master
+ c251 NFB-200G2-slave
+ c2c0 NFB-100G2-e0
+ c2c1 NFB-100G2-e1
+ cb20 COMBO-20G
+ cb40 COMBO-40G
+ cb80 NFB-40G2
1b2c Opal-RT Technologies Inc.
1b36 Red Hat, Inc.
0001 QEMU PCI-PCI bridge
@@ -21146,6 +21404,7 @@
001f DSU
0020 ADQ14
0023 ADQ7
+ 0026 ADQ8
2014 TX320
2019 S6000
# now owned by HGST (a Western Digital subsidiary)
@@ -21206,6 +21465,7 @@
1d5c 1000 Anker USB 3.0 Express Card
1009 FL1009 USB 3.0 Host Controller
1100 FL1100 USB 3.0 Host Controller
+ 16b8 6e31 Allegro Pro USB 3.0 PCIe
1b74 OpenVox Communication Co. Ltd.
0115 D115P/D115E Single-port E1/T1 card
d130 D130P/D130E Single-port E1/T1 card (3rd GEN)
@@ -21222,6 +21482,7 @@
e400 PX14400 Dual Xilinx Virtex5 based Digitizer
1b96 Western Digital
1b9a XAVi Technologies Corp.
+1baa QNAP Systems, Inc.
1bad ReFLEX CES
1bb0 SimpliVity Corporation
0002 OmniCube Accelerator OA-3000
@@ -21246,8 +21507,14 @@
0100 Nytro Flash Storage
1bb1 0101 Nytro XF1440
1bb1 0103 Nytro 5000
+ 1bb1 0105 Nytro 5020
+ 1bb1 0106 Nytro 5020 TCG
1bb1 0121 Nytro XM1440
1bb1 0123 Nytro 5000
+# Kiowa M.2
+ 1bb1 0125 Nytro 5020
+# Kiowa M.2 TCG
+ 1bb1 0126 Nytro 5020
1bb1 01a1 Nytro XP7102
1bb3 Bluecherry
4304 BC-04120A MPEG4 4 port video encoder / decoder
@@ -21292,6 +21559,8 @@
4263 10G-PCIE3-8E-4S Network Adapter
4264 10G-PCIE3-8E-2S Network Adapter
4265 10G-PCIE3-8E-2S Network Adapter
+ 5000 25G-PCIE3-8A-2S Security Intelligent Adapter
+ 5001 25G-PCIE3-8B-2S Security Intelligent Adapter
1c1c Symphony
0001 82C101
1c28 Lite-On IT Corp. / Plextor
@@ -21328,8 +21597,10 @@
1014 04f5 PCIe3 1.6TB NVMe Flash Adapter
1014 04f6 PCIe3 3.2TB NVMe Flash Adapter
0023 Ultrastar SN200 Series NVMe SSD
+ 1c58 8823 Ultrastar Memory (ME200)
1c5c SK hynix
1283 PC300 NVMe Solid State Drive
+ 1504 SC300 512GB M.2 2280 SATA Solid State Drive
1c5f Beijing Memblaze Technology Co. Ltd.
0540 PBlaze4 NVMe SSD
# http://www.nicevt.ru/ (in Russian)
@@ -21360,6 +21631,8 @@
0305 Simulyzer-RT CompactPCI Serial CAN-1 card
1cd7 Nanjing Magewell Electronics Co., Ltd.
0010 Pro Capture Endpoint
+ 0014 PRO CAPTURE AIO 4K PLUS
+ 0017 PRO CAPTURE AIO 4K
1cdd secunet Security Networks AG
1ce4 Exablaze
0001 ExaNIC X4
@@ -21370,8 +21643,12 @@
0006 ExaNIC X10-HPT
0007 ExaNIC X40
0008 ExaNIC V5P
+ 0009 ExaNIC X25
+ 0100 ExaDISK FX1
+1cf0 Akitio
1cf7 Subspace Dynamics
1d00 Pure Storage
+1d05 Tongfang Hongkong Limited
1d0f Amazon.com, Inc.
cd01 NVMe SSD Controller
ec20 Elastic Network Adapter (ENA)
@@ -21442,6 +21719,7 @@
0040 Turbocard2 Accelerator
0080 Open Network Interface Card 80G
00c0 Turbocard3 Accelerator
+ 0140 Open Network Interface Card 40G
e004 AB01/EMB01 Development Board
1d40 Techman Electronics (Changshu) Co., Ltd.
1d44 DPT
@@ -21484,7 +21762,9 @@
1d72 Xiaomi
1d78 DERA
1d7c Aerotech, Inc.
+1d82 NETINT Technologies Inc.
1d87 Fuzhou Rockchip Electronics Co., Ltd
+ 1808 RK1808 Neural Network Processor Card
1d8f Enyx
1d94 Chengdu Higon IC Design Co.Ltd
1450 Root Complex
@@ -21517,9 +21797,13 @@
790b FCH SMBus Controller
790e FCH LPC Bridge
1d95 Graphcore Ltd
+ 0001 Colossus GC2 [C2]
+ 0002 Colossus GC1 [S1]
1da1 Teko Telecom S.r.l.
1da2 Sapphire Technology Limited
1dbb NGD Systems, Inc.
+1dbf Guizhou Huaxintong Semiconductor Technology Co., Ltd
+ 0401 StarDragon4800 PCI Express Root Port
1de1 Tekram Technology Co.,Ltd.
0391 TRM-S1040 [DC-315 / DC-395 series]
2020 DC-390
@@ -21529,20 +21813,25 @@
1000 IO Memory Controller
2000 NoLoad Hardware Development Kit
1def Ampere Computing, LLC
- e005 Skylark PCI Express Root Port 0 [X-Gene 3]
- e006 Skylark PCI Express Root Port 1 [X-Gene 3]
- e007 Skylark PCI Express Root Port 2 [X-Gene 3]
- e008 Skylark PCI Express Root Port 3 [X-Gene 3]
- e009 Skylark PCI Express Root Port 4 [X-Gene 3]
- e00a Skylark PCI Express Root Port 5 [X-Gene 3]
- e00b Skylark PCI Express Root Port 6 [X-Gene 3]
- e00c Skylark PCI Express Root Port 7 [X-Gene 3]
+ e005 eMAG PCI Express Root Port 0
+ e006 eMAG PCI Express Root Port 1
+ e007 eMAG PCI Express Root Port 2
+ e008 eMAG PCI Express Root Port 3
+ e009 eMAG PCI Express Root Port 4
+ e00a eMAG PCI Express Root Port 5
+ e00b eMAG PCI Express Root Port 6
+ e00c eMAG PCI Express Root Port 7
1df7 opencpi.org
0001 ml605
0002 alst4
0003 alst4x
1dfc JSC NT-COM
1181 TDM 8 Port E1/T1/J1 Adapter
+1e24 Squirrels Research Labs
+ 0101 Acorn CLE-101
+ 0215 Acorn CLE-215
+ 021f Acorn CLE-215+
+ 1525 Xilinx BCU-1525
# nee Tumsan Oy
1fc0 Ascom (Finland) Oy
0300 E2200 Dual E1/Rawpipe Card
@@ -21590,11 +21879,14 @@
1432 8102 10 Gigabit Ethernet PCI Express Adapter
1fc9 3015 Ethernet Adapter
4026 TN9610 10GbE SFP+ Ethernet Adapter
+ 4c52 1000 LREC6860AF 10 Gigabit Ethernet Adapter
4027 TN9710P 10GBase-T/NBASE-T Ethernet Adapter
1154 0368 LGY-PCIE-MG
1432 8104 10 Gigabit Ethernet PCI Express Adapter
1546 4027 GE10-PCIE4XG202P 10Gbase-T/NBASE-T Ethernet Adapter
+ 1baa 3310 PCIe Expansion Card
1fc9 3015 Ethernet Adapter
+ 4c52 1001 LREC6860BT 10 Gigabit Ethernet Adapter
4527 TN9710Q 5GBase-T/NBASE-T Ethernet Adapter
1fcc StreamLabs
f416 MS416
@@ -21610,6 +21902,7 @@
2003 Smart Link Ltd.
8800 LM-I56N
2004 Smart Link Ltd.
+2048 Beijing SpaceControl Technology Co.Ltd
20f4 TRENDnet
2116 ZyDAS Technology Corp.
21c3 21st Century Computer Corp.
@@ -21989,6 +22282,7 @@
4a14 5000 RT8029-Based Ethernet Adapter
4b10 Buslogic Inc.
4c48 LUNG HWA Electronics
+4c52 LR-Link
4c53 SBS Technologies
0000 PLUSTEST device
4c53 3000 PLUSTEST card (PC104+)
@@ -22188,14 +22482,14 @@
544c Teralogic Inc
0350 TL880-based HDTV/ATSC tuner
544d TBS Technologies
- 6178 DVB-S2 4 Tuner PCIe Card
+ 6178 DVB Tuner PCIe Card
544d 6904 TBS6904 DVB-S2 Quad Tuner PCIe Card
544d 6905 TBS6905 DVB-S2 Quad Tuner PCIe Card
6205 0001 TBS6205 DVB-T2/T/C Quad TV Tuner PCIe Card
6209 0001 TBS6209 DVB-T2/C2/T/C/ISDB-T OctaTV Tuner
5452 SCANLAB AG
3443 RTC4
-5455 Technische University Berlin
+5455 Technische Universitaet Berlin
4458 S5933
5456 GoTView
5519 Cnet Technologies, Inc.
@@ -22222,6 +22516,7 @@
c110 Virtualized HID
# Device surfaced in guests to provide 2d graphics capabilities
c147 Virtualized Graphics Device
+ c200 XCP-ng Project PCI Device for Windows Update
5854 GoTView
5ace Beholder International Ltd.
6205 TBS Technologies (wrong ID)
@@ -22415,7 +22710,7 @@
1043 108d VivoBook X202EV
0158 Xeon E3-1200 v2/Ivy Bridge DRAM Controller
1043 844d P8 series motherboard
- 8086 2010 Server Board S1200BTS
+ 8086 2010 Server Board S1200BT Family
0159 Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port
015a Xeon E3-1200 v2/Ivy Bridge Graphics Controller
015c Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller
@@ -22458,9 +22753,10 @@
0373 80333 B-Bus IOAPIC
0374 80333 Address Translation Unit
0402 Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller
- 0406 4th Gen Core Processor Integrated Graphics Controller
+ 0406 Haswell Integrated Graphics Controller
040a Xeon E3-1200 v3 Processor Integrated Graphics Controller
0412 Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller
+ 17aa 309f ThinkCentre M83
0416 4th Gen Core Processor Integrated Graphics Controller
17aa 220e ThinkPad T440p
041a Xeon E3-1200 v3 Processor Integrated Graphics Controller
@@ -22920,12 +23216,41 @@
0a2a Haswell-ULT Integrated Graphics Controller
0a2e Haswell-ULT Integrated Graphics Controller
0a53 DC P3520 SSD
- 0a54 Express Flash NVMe P4500/P4600
+ 0a54 NVMe Datacenter SSD [3DNAND, Beta Rock Controller]
1028 1fe1 Express Flash NVMe 1TB 2.5" U.2 (P4500)
1028 1fe2 Express Flash NVMe 2TB 2.5" U.2 (P4500)
1028 1fe3 Express Flash NVMe 4TB 2.5" U.2 (P4500)
1028 1fe4 Express Flash NVMe 4TB HHHL AIC (P4500)
- 0a55 Express Flash NVMe P4600
+ 1028 1fee Express Flash NVMe 1.6TB 2.5" U.2 (P4610)
+ 1028 1fef Express Flash NVMe 3.2TB 2.5" U.2 (P4610)
+ 1028 1ff0 Express Flash NVMe 6.4TB 2.5" U.2 (P4610)
+ 1028 1fff Express Flash NVMe 8.0TB 2.5" U.2 (P4510)
+ 1028 2003 Express Flash NVMe 1.0 TB 2.5" U.2 (P4510)
+ 1028 2004 Express Flash NVMe 2.0TB 2.5" U.2 (P4510)
+ 1028 2005 Express Flash NVMe 4.0TB 2.5" U.2 (P4510)
+ 108e 4870 NVMe PCIe 3.0 SSD 6.4TB AIC (P4608)
+ 108e 4871 NVMe PCIe 3.0 SSD 6.4TB 2.5-inch (P4600)
+ 108e 487a NVMe PCIe 3.0 SSD v2 6.4TB 2.5-inch (P4610)
+ 1590 025d NVMe Datacenter SSD [3DNAND] 1.0TB 2.5" U.2 (P4500)
+ 1590 025e NVMe Datacenter SSD [3DNAND] 2.0TB 2.5" U.2 (P4500)
+ 1590 025f NVMe Datacenter SSD [3DNAND] 4.0TB 2.5" U.2 (P4500)
+ 1590 0262 NVMe Datacenter SSD [3DNAND] 1.6TB 2.5" U.2 (P4600)
+ 1590 0264 NVMe Datacenter SSD [3DNAND] 3.2TB 2.5" U.2 (P4600)
+ 1590 0265 NVMe Datacenter SSD [3DNAND] 6.4TB 2.5" U.2 (P4600)
+ 1590 026c NVMe Datacenter SSD [3DNAND] 4.0TB AIC (P4500)
+ 1d49 4802 Thinksystem U.2 P4510 NVMe SSD
+ 1d49 4812 Thinksystem U.2 P4610 NVMe SSD
+ 8086 4308 Intel SSD D5-P4320 and D5-P4326
+ 8086 4702 NVMe Datacenter SSD [3DNAND] SE 2.5" U.2 (P4500)
+ 8086 4704 NVMe Datacenter SSD [3DNAND] SE AIC (P4500)
+ 8086 4712 NVMe Datacenter SSD [3DNAND] ME 2.5" U.2 (P4600)
+ 8086 4714 NVMe Datacenter SSD [3DNAND] ME AIC (P4600)
+ 8086 4802 NVMe Datacenter SSD [3DNAND] SE 2.5" U.2 (P4510)
+ 8086 4804 NVMe Datacenter SSD [3DNAND] SE AIC (P4510)
+ 8086 4805 NVMe Datacenter SSD [3DNAND] SE M.2 (P4511)
+ 8086 4812 NVMe Datacenter SSD [3DNAND] ME 2.5" U.2 (P4610)
+ 8086 4814 NVMe Datacenter SSD [3DNAND] ME AIC (P4610)
+ 0a55 NVMe DC SSD [3DNAND, Beta Rock Controller]
1028 1fe5 Express Flash NVMe 1.6TB 2.5" U.2 (P4600)
1028 1fe6 Express Flash NVMe 2TB 2.5" U.2 (P4600)
1028 1fe7 Express Flash NVMe 3.2TB 2.5" U.2 (P4600)
@@ -22958,6 +23283,7 @@
0bf6 Atom Processor D2xxx/N2xxx DRAM Controller
0bf7 Atom Processor D2xxx/N2xxx DRAM Controller
0c00 4th Gen Core Processor DRAM Controller
+ 17aa 309f ThinkCentre M83
0c01 Xeon E3-1200 v3/4th Gen Core Processor PCI Express x16 Controller
0c04 Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller
103c 1909 ZBook 15
@@ -22967,6 +23293,7 @@
0c09 Xeon E3-1200 v3/4th Gen Core Processor PCI Express x4 Controller
0c0c Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller
17aa 220e ThinkPad T440p
+ 17aa 309f ThinkCentre M83
0c46 Atom Processor S1200 PCI Express Root Port 1
0c47 Atom Processor S1200 PCI Express Root Port 2
0c48 Atom Processor S1200 PCI Express Root Port 3
@@ -23013,14 +23340,18 @@
0d26 Crystal Well Integrated Graphics Controller
0d36 Crystal Well Integrated Graphics Controller
0e00 Xeon E7 v2/Xeon E5 v2/Core i7 DMI2
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e01 Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port in DMI2 Mode
0e02 Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 1a
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
0e03 Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 1b
0e04 Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 2a
0e05 Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 2b
0e06 Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 2c
0e07 Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 2d
0e08 Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3a
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
0e09 Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3b
0e0a Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3c
0e0b Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3d
@@ -23033,24 +23364,48 @@
0e1e Xeon E7 v2/Xeon E5 v2/Core i7 UBOX Registers
0e1f Xeon E7 v2/Xeon E5 v2/Core i7 UBOX Registers
0e20 Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 0
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e21 Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 1
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e22 Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 2
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e23 Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 3
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e24 Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 4
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e25 Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 5
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e26 Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 6
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e27 Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 7
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e28 Xeon E7 v2/Xeon E5 v2/Core i7 VTd/Memory Map/Misc
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e29 Xeon E7 v2/Xeon E5 v2/Core i7 Memory Hotplug
0e2a Xeon E7 v2/Xeon E5 v2/Core i7 IIO RAS
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
+ 15d9 066b X9SRL-F
0e2c Xeon E7 v2/Xeon E5 v2/Core i7 IOAPIC
+ 15d9 066b X9SRL-F
0e2e Xeon E7 v2/Xeon E5 v2/Core i7 CBDMA
0e2f Xeon E7 v2/Xeon E5 v2/Core i7 CBDMA
0e30 Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 0
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
0e32 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 0
0e33 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 1
0e34 Xeon E7 v2/Xeon E5 v2/Core i7 R2PCIe
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
0e36 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Performance Ring Monitoring
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
0e37 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Performance Ring Monitoring
0e38 Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 1
0e3a Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 2
@@ -23077,6 +23432,7 @@
0e7f Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Registers
0e80 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 0
0e81 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Registers
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
0e83 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Reut 0
0e84 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Reut 0
0e85 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Agent Register
@@ -23086,6 +23442,7 @@
0e94 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Reut 1
0e95 Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Agent Register
0ea0 Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 0
+ 1028 04f7 Xeon E5 v2 on PowerEdge R320 server
0ea8 Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Target Address/Thermal Registers
0eaa Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel Target Address Decoder Registers
0eab Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel Target Address Decoder Registers
@@ -23363,6 +23720,8 @@
104b 82566DC Gigabit Network Connection
104c 82562V 10/100 Network Connection
104d 82566MC Gigabit Network Connection
+ 104e Ethernet Controller X710 for 10 Gigabit SFP+
+ 104f Ethernet Controller X710 for 10 Gigabit backplane
1050 82562EZ 10/100 Ethernet Controller
1028 019d Dimension 3000
1462 728c 865PE Neo2 (MS-6728)
@@ -23578,9 +23937,11 @@
1043 8369 Motherboard
1093 76e9 PCIe-8233 Ethernet Adapter
10a9 8029 Prism XL Single Port Gigabit Ethernet
+ 15d9 0605 X8SIL
15d9 060a X7SPA-H/X7SPA-HF Motherboard
15d9 060d C7SIM-Q Motherboard
8086 0001 Gigabit CT2 Desktop Adapter
+ 8086 3578 Server Board S1200BTLR
8086 357a Server Board S1200BTS
8086 a01f Gigabit CT Desktop Adapter
e4bf 50c1 PC1-GROOVE
@@ -23633,6 +23994,7 @@
8086 106f 10-Gigabit XF LR Server Adapter
8086 a06f 10-Gigabit XF LR Server Adapter
10f5 82567LM Gigabit Network Connection
+ 17aa 20ee ThinkPad T400
10f6 82574L Gigabit Network Connection
10f7 10 Gigabit BR KX4 Dual Port Network Connection
108e 7b12 Sun Dual 10GbE PCIe 2.0 FEM
@@ -23915,6 +24277,7 @@
1502 82579LM Gigabit Network Connection (Lewisville)
1028 04a3 Precision M4600
17aa 21ce ThinkPad T520
+ 8086 3578 Server Board S1200BTLR
8086 357a Server Board S1200BTS
1503 82579V Gigabit Network Connection
1043 849c P8P67 Deluxe Motherboard
@@ -23976,6 +24339,7 @@
1093 76b1 PCIe-8237R-S Ethernet Adapter
1093 775b PCIe-8237 Ethernet Adapter
10a9 802a UV2-BaseIO dual-port GbE
+ 1137 023e 1GigE I350 LOM
15d9 0652 Dual Port i350 GbE MicroLP [AOC-CGP-i2]
17aa 1074 ThinkServer I350-T4 AnyFabric
17aa 4005 I350 Gigabit Network Connection
@@ -24059,11 +24423,13 @@
1059 0130 T4009 1GbE interface
1059 0140 T2035 1GbE interface
1059 0150 RD-01068 1GbE interface
+ 1059 0170 RD-01213 10GbE interface
1538 I210 Gigabit Network Connection
1539 I211 Gigabit Network Connection
153a Ethernet Connection I217-LM
103c 1909 ZBook 15
17aa 220e ThinkPad T440p
+ 17aa 309f ThinkCentre M83
153b Ethernet Connection I217-V
1547 DSL3510 Thunderbolt Controller [Cactus Ridge 4C 2012]
1548 DSL3310 Thunderbolt Controller [Cactus Ridge 2C 2012]
@@ -24119,6 +24485,7 @@
156c DSL5520 Thunderbolt 2 NHI [Falcon Ridge 4C 2013]
156d DSL5520 Thunderbolt 2 Bridge [Falcon Ridge 4C 2013]
156f Ethernet Connection I219-LM
+ 1028 06dc Latitude E7470
1570 Ethernet Connection I219-V
1571 Ethernet Virtual Function 700 Series
1572 Ethernet Controller X710 for 10GbE SFP+
@@ -24170,6 +24537,7 @@
1028 1f98 Ethernet 10G 4P X710-k bNDC
1028 1f9e Ethernet 10G 2P X710-k bNDC
1059 0150 RD-01068 10GbE-KR interface
+ 1059 0170 RD-01213 10GbE interface
1590 0000 Ethernet 2-port 563i Adapter
1590 00f8 Ethernet 2-port 563i Adapter
8086 0000 Ethernet Converged Network Adapter XL710-Q2
@@ -24212,8 +24580,12 @@
8086 00a0 Ethernet Converged Network Adapter X710-T4
8086 1003 Ethernet Converged Network Adapter X710-T
158a Ethernet Controller XXV710 for 25GbE backplane
+ 1590 0000 10/25Gb Ethernet Adapter
+ 1590 0286 Synergy 4610C 10/25Gb Ethernet Adapter
8086 000a Ethernet 25G 2P XXV710 Mezz
158b Ethernet Controller XXV710 for 25GbE SFP28
+ 1137 0000 Ethernet Network Adapter XXV710
+ 1137 0225 Ethernet Network Adapter XXV710
8086 0000 Ethernet Network Adapter XXV710
8086 0001 Ethernet Network Adapter XXV710-2
8086 0002 Ethernet Network Adapter XXV710-2
@@ -24225,6 +24597,9 @@
8086 0008 Ethernet Network Adapter OCP XXV710-1
8086 0009 Ethernet 25G 2P XXV710 Adapter
8086 4001 Ethernet Network Adapter XXV710-2
+ 1591 Ethernet Controller E810-C for backplane
+ 1592 Ethernet Controller E810-C for QSFP
+ 1593 Ethernet Controller E810-C for SFP
15a0 Ethernet Connection (2) I218-LM
15a1 Ethernet Connection (2) I218-V
15a2 Ethernet Connection (3) I218-LM
@@ -24235,8 +24610,9 @@
15a9 X552 Virtual Function
15aa Ethernet Connection X552 10 GbE Backplane
1059 0120 T4008 10GbE interface
- 1059 0150 RD-01068 10GbE interface
15ab Ethernet Connection X552 10 GbE Backplane
+ 1059 0150 RD-01068 10GbE interface
+ 1059 0170 RD-01213 10GbE interface
15ac Ethernet Connection X552 10 GbE SFP+
1059 0160 RD-01167 10GbE interface
15ad Ethernet Connection X552/X557-AT 10GBASE-T
@@ -24280,6 +24656,7 @@
15d8 Ethernet Connection (4) I219-V
17aa 2247 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
+ 17aa 225d ThinkPad T480
15d9 JHL6340 Thunderbolt 3 NHI (C step) [Alpine Ridge 2C 2016]
15da JHL6340 Thunderbolt 3 Bridge (C step) [Alpine Ridge 2C 2016]
15df Ethernet Connection (8) I219-LM
@@ -24297,6 +24674,7 @@
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]
+ 15ff Ethernet Controller X710 for 10GBASE-T
1600 Broadwell-U Host Bridge -OPI
1601 Broadwell-U PCI Express x16 Controller
1602 Broadwell-U Integrated Graphics
@@ -24340,7 +24718,11 @@
1901 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor PCIe Controller (x16)
1902 HD Graphics 510
1903 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem
+ 1028 06dc Latitude E7470
+ 1028 06e4 XPS 15 9550
+ 17aa 225d ThinkPad T480
1904 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
+ 1028 06dc Latitude E7470
1028 06f3 Latitude 3570
17aa 382a B51-80 Laptop
1905 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor PCIe Controller (x8)
@@ -24351,15 +24733,19 @@
190c Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
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
1911 Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th Gen Core Processor Gaussian Mixture Model
17aa 2247 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
+ 17aa 225d ThinkPad T480
1912 HD Graphics 530
1916 Skylake GT2 [HD Graphics 520]
+ 1028 06dc Latitude E7470
1028 06f3 Latitude 3570
1918 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
1919 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Imaging Unit
191b HD Graphics 530
+ 1028 06e4 XPS 15 9550
191d HD Graphics P530
191e HD Graphics 515
191f Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
@@ -24399,6 +24785,7 @@
e4bf 3100 CX1-BAND
1962 80960RM (i960RM) Microprocessor
105a 0000 SuperTrak SX6000 I2O CPU
+ 1964 80960RN (i960RN) Microprocessor
19ac Atom Processor C3000 Series SMBus Contoller - Host
19b0 Atom Processor C3000 Series SATA Controller 0
19b1 Atom Processor C3000 Series SATA Controller 0
@@ -24447,7 +24834,7 @@
1c02 6 Series/C200 Series Chipset Family 6 port Desktop SATA AHCI Controller
1028 04aa XPS 8300
1043 844d P8 series motherboard
- 8086 7270 Server Board S1200BTS
+ 8086 7270 Server Board S1200BT Family
1c03 6 Series/C200 Series Chipset Family 6 port Mobile SATA AHCI Controller
1028 04a3 Precision M4600
1028 04b2 Vostro 3350
@@ -24505,7 +24892,7 @@
1028 04da Vostro 3750
1043 844d P8 series motherboard
17aa 21cf ThinkPad T520
- 8086 7270 Server Board S1200BTS / Apple MacBook Pro 8,1/8,2
+ 8086 7270 Server Board S1200BT Family / Apple MacBook Pro 8,1/8,2
1c24 6 Series/C200 Series Chipset Family Thermal Management Controller
1c25 6 Series/C200 Series Chipset Family DMI to PCI Bridge
1c26 6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1
@@ -24515,7 +24902,7 @@
1028 04da Vostro 3750
1043 844d P8 series motherboard
17aa 21cf ThinkPad T520
- 8086 7270 Server Board S1200BTS / Apple MacBook Pro 8,1/8,2
+ 8086 7270 Server Board S1200BT Family / Apple MacBook Pro 8,1/8,2
1c27 6 Series/C200 Series Chipset Family USB Universal Host Controller #1
8086 7270 Apple MacBookPro8,2 [Core i7, 15", 2011]
1c2c 6 Series/C200 Series Chipset Family USB Universal Host Controller #5
@@ -24527,7 +24914,7 @@
1028 04da Vostro 3750
1043 844d P8 series motherboard
17aa 21cf ThinkPad T520
- 8086 7270 Server Board S1200BTS / Apple MacBook Pro 8,1/8,2
+ 8086 7270 Server Board S1200BT Family / Apple MacBook Pro 8,1/8,2
1c33 6 Series/C200 Series Chipset Family LAN Controller
1c35 6 Series/C200 Series Chipset Family VECI Controller
1c3a 6 Series/C200 Series Chipset Family MEI Controller #1
@@ -24545,50 +24932,52 @@
1c41 Mobile SFF 6 Series Chipset Family LPC Controller
1c42 6 Series/C200 Series Chipset Family LPC Controller
1c43 Mobile 6 Series Chipset Family LPC Controller
- 1c44 Z68 Express Chipset Family LPC Controller
+ 1c44 Z68 Express Chipset LPC Controller
1c45 6 Series/C200 Series Chipset Family LPC Controller
- 1c46 P67 Express Chipset Family LPC Controller
+ 1c46 P67 Express Chipset LPC Controller
1043 844d P8P67 Deluxe Motherboard
- 1c47 UM67 Express Chipset Family LPC Controller
+ 1c47 UM67 Express Chipset LPC Controller
1c48 6 Series/C200 Series Chipset Family LPC Controller
- 1c49 HM65 Express Chipset Family LPC Controller
+ 1c49 HM65 Express Chipset LPC Controller
8086 7270 Apple MacBookPro8,2 [Core i7, 15", 2011]
- 1c4a H67 Express Chipset Family LPC Controller
+ 1c4a H67 Express Chipset LPC Controller
1028 04aa XPS 8300
1043 844d P8H67 Series Motherboard
- 1c4b HM67 Express Chipset Family LPC Controller
+ 1c4b HM67 Express Chipset LPC Controller
1028 04b2 Vostro 3350
1028 04da Vostro 3750
- 1c4c Q65 Express Chipset Family LPC Controller
- 1c4d QS67 Express Chipset Family LPC Controller
- 1c4e Q67 Express Chipset Family LPC Controller
- 1c4f QM67 Express Chipset Family LPC Controller
+ 1c4c Q65 Express Chipset LPC Controller
+ 1c4d QS67 Express Chipset LPC Controller
+ 1c4e Q67 Express Chipset LPC Controller
+ 1c4f QM67 Express Chipset LPC Controller
1028 04a3 Precision M4600
17aa 21cf ThinkPad T520
- 1c50 B65 Express Chipset Family LPC Controller
+ 1c50 B65 Express Chipset LPC Controller
1c51 6 Series/C200 Series Chipset Family LPC Controller
- 1c52 C202 Chipset Family LPC Controller
+ 1c52 C202 Chipset LPC Controller
8086 7270 Server Board S1200BTS
1c53 6 Series/C200 Series Chipset Family LPC Controller
- 1c54 C204 Chipset Family LPC Controller
+ 1c54 C204 Chipset LPC Controller
1c55 6 Series/C200 Series Chipset Family LPC Controller
- 1c56 C206 Chipset Family LPC Controller
+ 1c56 C206 Chipset LPC Controller
1043 844d P8B WS Motherboard
1c57 6 Series/C200 Series Chipset Family LPC Controller
- 1c58 Upgraded B65 Express Chipset Family LPC Controller
- 1c59 Upgraded HM67 Express Chipset Family LPC Controller
- 1c5a Upgraded Q67 Express Chipset Family LPC Controller
+ 1c58 Upgraded B65 Express Chipset LPC Controller
+ 1c59 Upgraded HM67 Express Chipset LPC Controller
+ 1c5a Upgraded Q67 Express Chipset LPC Controller
1c5b 6 Series/C200 Series Chipset Family LPC Controller
- 1c5c H61 Express Chipset Family LPC Controller
+ 1c5c H61 Express Chipset LPC Controller
1c5d 6 Series/C200 Series Chipset Family LPC Controller
1c5e 6 Series/C200 Series Chipset Family LPC Controller
1c5f 6 Series/C200 Series Chipset Family LPC Controller
1d00 C600/X79 series chipset 4-Port SATA IDE Controller
1d02 C600/X79 series chipset 6-Port SATA AHCI Controller
+ 1028 04f7 C602J on PowerEdge R320 server
1d04 C600/X79 series chipset SATA RAID Controller
1d06 C600/X79 series chipset SATA Premium RAID Controller
1d08 C600/X79 series chipset 2-Port SATA IDE Controller
1d10 C600/X79 series chipset PCI Express Root Port 1
+ 1028 04f7 C602J on PowerEdge R320 server
1d11 C600/X79 series chipset PCI Express Root Port 1
1d12 C600/X79 series chipset PCI Express Root Port 2
1d13 C600/X79 series chipset PCI Express Root Port 2
@@ -24597,29 +24986,44 @@
1d16 C600/X79 series chipset PCI Express Root Port 4
1d17 C600/X79 series chipset PCI Express Root Port 4
1d18 C600/X79 series chipset PCI Express Root Port 5
+ 1028 04f7 C602J on PowerEdge R320 server
1d19 C600/X79 series chipset PCI Express Root Port 5
1d1a C600/X79 series chipset PCI Express Root Port 6
1d1b C600/X79 series chipset PCI Express Root Port 6
1d1c C600/X79 series chipset PCI Express Root Port 7
1d1d C600/X79 series chipset PCI Express Root Port 7
1d1e C600/X79 series chipset PCI Express Root Port 8
+ 1028 04f7 C602J on PowerEdge R320 server
1d1f C600/X79 series chipset PCI Express Root Port 8
1d20 C600/X79 series chipset High Definition Audio Controller
1d22 C600/X79 series chipset SMBus Host Controller
+ 15d9 066b X9SRL-F
1d24 C600/X79 series chipset Thermal Management Controller
+ 15d9 066b X9SRL-F
1d25 C600/X79 series chipset DMI to PCI Bridge
1d26 C600/X79 series chipset USB2 Enhanced Host Controller #1
+ 1028 04f7 C602J on PowerEdge R320 server
+ 15d9 066b X9SRL-F
1d2d C600/X79 series chipset USB2 Enhanced Host Controller #2
+ 1028 04f7 C602J on PowerEdge R320 server
+ 15d9 066b X9SRL-F
1d33 C600/X79 series chipset LAN Controller
1d35 C600/X79 series chipset VECI Controller
1d3a C600/X79 series chipset MEI Controller #1
+ 1028 04f7 C602J on PowerEdge R320 server
+ 15d9 066b X9SRL-F
1d3b C600/X79 series chipset MEI Controller #2
+ 1028 04f7 C602J on PowerEdge R320 server
+ 15d9 066b X9SRL-F
1d3c C600/X79 series chipset IDE-r Controller
1d3d C600/X79 series chipset KT Controller
1d3e C600/X79 series chipset PCI Express Virtual Root Port
+ 1028 04f7 C602J on PowerEdge R320 server
1d3f C608/C606/X79 series chipset PCI Express Virtual Switch Port
1d40 C600/X79 series chipset LPC Controller
1d41 C600/X79 series chipset LPC Controller
+ 1028 04f7 C602J on PowerEdge R320 server
+ 15d9 066b X9SRL-F
1d50 C608 chipset Dual 4-Port SATA/SAS Storage Control Unit
1d54 C600/X79 series chipset Dual 4-Port SATA/SAS Storage Control Unit
1d55 C600/X79 series chipset 4-Port SATA/SAS Storage Control Unit
@@ -24732,6 +25136,7 @@
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
1043 84ca P8 series motherboard
+ 17aa 21f3 ThinkPad T430
1849 1e31 Motherboard
1e33 7 Series/C210 Series Chipset Family LAN Controller
1e3a 7 Series/C216 Chipset Family MEI Controller #1
@@ -24779,9 +25184,9 @@
1e5c 7 Series Chipset Family LPC Controller
1e5d HM75 Express Chipset LPC Controller
144d c652 NP300E5C series laptop
- 1e5e 7 Series Chipset Family LPC Controller
+ 1e5e HM70 Express Chipset LPC Controller
1043 108d VivoBook X202EV
- 1e5f 7 Series Chipset Family LPC Controller
+ 1e5f NM70 Express Chipset LPC Controller
1f00 Atom processor C2000 SoC Transaction Router
1f01 Atom processor C2000 SoC Transaction Router
1f02 Atom processor C2000 SoC Transaction Router
@@ -24831,6 +25236,7 @@
1f3a Atom processor C2000 PCU
1f3b Atom processor C2000 PCU
1f3c Atom processor C2000 PCU SMBus
+ 1f3d Atom Processor C2000 PECI SMBus
1f3e Atom processor C2000 RAID SATA3 Controller
1f3f Atom processor C2000 RAID SATA3 Controller
1f40 Ethernet Connection I354 1.0 GbE Backplane
@@ -24846,6 +25252,7 @@
201a Sky Lake-E Non-Transparent Bridge Registers
201c Sky Lake-E Non-Transparent Bridge Registers
2020 Sky Lake-E DMI3 Registers
+ 15d9 095d X11SPM-TF
2021 Sky Lake-E CBDMA Registers
2024 Sky Lake-E MM/Vt-d Configuration Registers
2030 Sky Lake-E PCI Express Root Port A
@@ -25109,6 +25516,7 @@
1028 020d Inspiron 530
1028 0211 Optiplex 755
1028 02da OptiPlex 980
+ 1028 04f7 PowerEdge R320 server
103c 2a3b Pavilion A1512X
103c 2a6f Asus IPIBL-LB Motherboard
103c 31fe ProLiant DL140 G3
@@ -25610,6 +26018,8 @@
24fd Wireless 8265 / 8275
# Windstorm Peak
8086 0010 Dual Band Wireless-AC 8265
+ 8086 0150 Dual Band Wireless-AC 8265
+ 8086 1010 Dual Band Wireless-AC 8265
8086 1130 Dual Band Wireless-AC 8265
2500 82820 820 (Camino) Chipset Host Bridge (MCH)
1028 0095 Precision Workstation 220 Chipset
@@ -26116,20 +26526,25 @@
2700 Optane SSD 900P Series
8086 3900 900P Series [Add-in Card]
8086 3901 900P Series [2.5" SFF]
- 2701 Optane DC P4800X Series SSD
- 8086 3904 DC P4800X Series [Add-in Card]
- 8086 3905 DC P4800X Series [2.5" SFF]
+ 2701 NVMe Datacenter SSD [Optane]
+ 1028 2000 Express Flash NVMe [Optane] 375GB 2.5" U.2 (P4800X)
+ 1028 2001 Express Flash NVMe [Optane] 750GB 2.5" U.2 (P4800X)
+ 1028 2002 Express Flash NVMe [Optane] 750GB AIC (P4800X)
+ 8086 3904 NVMe Datacenter SSD [Optane] x4 AIC (P4800X)
+ 8086 3905 NVMe Datacenter SSD [Optane] 15mm 2.5" U.2 (P4800X)
2770 82945G/GZ/P/PL Memory Controller Hub
1028 01ad OptiPlex GX620
103c 2a3b Pavilion A1512X
1043 817a P5LD2-VM Mainboard
107b 5048 E4500
1462 7418 Wind PC MS-7418
+ 1849 2770 ConRoe1333-D667
8086 544e DeskTop Board D945GTP
2771 82945G/GZ/P/PL PCI Express Root Port
2772 82945G/GZ Integrated Graphics Controller
103c 2a3b Pavilion A1512X
1462 7418 Wind PC MS-7418
+ 1849 2772 ConRoe1333-D667
8086 544e DeskTop Board D945GTP
8086 d605 Desktop Board D945GCCR
2774 82955X Memory Controller Hub
@@ -26205,6 +26620,7 @@
10f7 8338 Panasonic CF-Y5 laptop
17aa 2009 ThinkPad R60/T60/X60 series
27bc NM10 Family LPC Controller
+ 1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
144d c072 Notebook N150P
1458 5001 GA-D525TUD
@@ -26228,6 +26644,7 @@
27c1 NM10/ICH7 Family SATA Controller [AHCI mode]
1028 01df PowerEdge SC440
103c 2a3b Pavilion A1512X
+ 1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
144d c072 Notebook N150P
1458 b005 GA-D525TUD
@@ -26262,6 +26679,7 @@
103c 30d5 530 Laptop
1043 1237 A6J-Q008
1043 8179 P5KPL-VM,P5LD2-VM Mainboard
+ 1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
107b 5048 E4500
@@ -26286,6 +26704,7 @@
103c 30a3 Compaq nw8440
1043 1237 A6J-Q008
1043 8179 P5KPL-VM,P5LD2-VM Mainboard
+ 1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
107b 5048 E4500
@@ -26310,6 +26729,7 @@
103c 30a3 Compaq nw8440
1043 1237 A6J-Q008
1043 8179 P5KPL-VM,P5LD2-VM Mainboard
+ 1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
107b 5048 E4500
@@ -26332,6 +26752,7 @@
103c 30a3 Compaq nw8440
1043 1237 A6J-Q008
1043 8179 P5KPL-VM,P5LD2-VM Mainboard
+ 1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
107b 5048 E4500
@@ -26356,6 +26777,7 @@
103c 30d5 530 Laptop
1043 1237 A6J-Q008
1043 8179 P5KPL-VM,P5LD2-VM Mainboard
+ 1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
144d c072 Notebook N150P
@@ -26414,6 +26836,7 @@
1043 817f P5LD2-VM Mainboard (Realtek ALC 882 codec)
1043 8290 P5KPL-VM Motherboard
1043 82ea P5KPL-CM Motherboard
+ 1043 8437 Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8207 Medion MIM 2240 Notebook PC [MD98100]
107b 5048 E4500
@@ -26740,6 +27163,7 @@
1462 7345 MS-7345 Motherboard
8086 5044 Desktop Board DP35DP
2917 ICH9M-E LPC Interface Controller
+ 17aa 20f5 ThinkPad T400
e4bf cc4d CCM-BOOGIE
2918 82801IB (ICH9) LPC Interface Controller
1028 0236 PowerEdge R610 82801IB (ICH9) LPC Interface Controller
@@ -26779,6 +27203,7 @@
2928 82801IBM/IEM (ICH9M/ICH9M-E) 2 port SATA Controller [IDE mode]
2929 82801IBM/IEM (ICH9M/ICH9M-E) 4 port SATA Controller [AHCI mode]
103c 3628 dv6-1190en
+ 17aa 20f8 ThinkPad T400
e4bf cc4d CCM-BOOGIE
292c 82801IEM (ICH9M-E) SATA Controller [RAID mode]
292d 82801IBM/IEM (ICH9M/ICH9M-E) 2 port SATA Controller [IDE mode]
@@ -26791,6 +27216,7 @@
1043 8277 P5K PRO Motherboard: 82801IR [ICH9R]
1462 7345 MS-7345 Motherboard: Intel 82801I/IR [ICH9/ICH9R]
1462 7360 G33/P35 Neo
+ 17aa 20f9 ThinkPad T400
1af4 1100 QEMU Virtual Machine
8086 5044 Desktop Board DP35DP
e4bf cc4d CCM-BOOGIE
@@ -26812,6 +27238,7 @@
1043 8277 P5K PRO Motherboard: 82801IR [ICH9R]
1462 7345 MS-7345 Motherboard: Intel 82801I/IR [ICH9/ICH9R]
1462 7360 G33/P35 Neo
+ 17aa 20f0 ThinkPad T400
1af4 1100 QEMU Virtual Machine
8086 5044 Desktop Board DP35DP
e4bf cc4d CCM-BOOGIE
@@ -26830,6 +27257,7 @@
1043 8277 P5K PRO Motherboard: 82801IR [ICH9R]
1462 7345 MS-7345 Motherboard: Intel 82801I/IR [ICH9/ICH9R]
1462 7360 G33/P35 Neo
+ 17aa 20f0 ThinkPad T400
1af4 1100 QEMU Virtual Machine
8086 5044 Desktop Board DP35DP
e4bf cc4d CCM-BOOGIE
@@ -26846,6 +27274,7 @@
1043 8277 P5K PRO Motherboard: 82801IR [ICH9R]
1462 7345 MS-7345 Motherboard: Intel 82801I/IR [ICH9/ICH9R]
1462 7360 G33/P35 Neo
+ 17aa 20f0 ThinkPad T400
1af4 1100 QEMU Virtual Machine
8086 5044 Desktop Board DP35DP
e4bf cc4d CCM-BOOGIE
@@ -26862,6 +27291,7 @@
1043 8277 P5K PRO Motherboard: 82801IR [ICH9R]
1462 7345 MS-7345 Motherboard: Intel 82801I/IR [ICH9/ICH9R]
1462 7360 G33/P35 Neo
+ 17aa 20f0 ThinkPad T400
1af4 1100 QEMU Virtual Machine
8086 2937 Optiplex 755
8086 2942 828011 (ICH9 Family ) USB UHCI Controller
@@ -26879,6 +27309,7 @@
1043 8277 P5K PRO Motherboard: 82801IR [ICH9R]
1462 7345 MS-7345 Motherboard: Intel 82801I/IR [ICH9/ICH9R]
1462 7360 G33/P35 Neo
+ 17aa 20f0 ThinkPad T400
1af4 1100 QEMU Virtual Machine
8086 2938 Optiplex 755
8086 5044 Desktop Board DP35DP
@@ -26891,6 +27322,7 @@
1043 8277 P5K PRO Motherboard: 82801IR [ICH9R]
1462 7345 MS-7345 Motherboard: Intel 82801I/IR [ICH9/ICH9R]
1462 7360 G33/P35 Neo
+ 17aa 20f0 ThinkPad T400
1af4 1100 QEMU Virtual Machine
8086 5044 Desktop Board DP35DP
e4bf cc4d CCM-BOOGIE
@@ -26909,6 +27341,7 @@
1043 8277 P5K PRO Motherboard: 82801IR [ICH9R]
1462 7345 MS-7345 Motherboard: Intel 82801I/IR [ICH9/ICH9R]
1462 7360 G33/P35 Neo
+ 17aa 20f1 ThinkPad T400
1af4 1100 QEMU Virtual Machine
8086 5044 Desktop Board DP35DP
e4bf cc4d CCM-BOOGIE
@@ -26924,6 +27357,7 @@
1043 8277 P5K PRO Motherboard: 82801IR [ICH9R]
1462 7345 MS-7345 Motherboard: Intel 82801I/IR [ICH9/ICH9R]
1462 7360 G33/P35 Neo
+ 17aa 20f1 ThinkPad T400
1af4 1100 QEMU Virtual Machine
8086 293c Optiplex 755
8086 5044 Desktop Board DP35DP
@@ -26936,6 +27370,7 @@
1043 829f P5K PRO Motherboard: 82801IR [ICH9R]
1462 735a MS-7345 Motherboard: Intel 82801I/IR [ICH9/ICH9R]
1462 7360 G33/P35 Neo
+ 17aa 20f2 ThinkPad T400
1af4 1100 QEMU Virtual Machine
8086 293e Optiplex 755
8086 2940 Optiplex 755
@@ -27111,14 +27546,18 @@
2a16 Mobile GME965/GLE960 PT IDER Controller
2a17 Mobile GME965/GLE960 KT Controller
2a40 Mobile 4 Series Chipset Memory Controller Hub
+ 17aa 20e0 ThinkPad T400
e4bf cc4d CCM-BOOGIE
2a41 Mobile 4 Series Chipset PCI Express Graphics Port
e4bf cc4d CCM-BOOGIE
2a42 Mobile 4 Series Chipset Integrated Graphics Controller
+ 17aa 2112 ThinkPad T400
e4bf cc4d CCM-BOOGIE
2a43 Mobile 4 Series Chipset Integrated Graphics Controller
+ 17aa 2112 ThinkPad T400
e4bf cc4d CCM-BOOGIE
2a44 Mobile 4 Series Chipset MEI Controller
+ 17aa 20e6 ThinkPad T400
2a45 Mobile 4 Series Chipset MEI Controller
2a46 Mobile 4 Series Chipset PT IDER Controller
2a47 Mobile 4 Series Chipset AMT SOL Redirection
@@ -27525,6 +27964,20 @@
# Stone Peak 1x1
8086 4210 Dual Band Wireless AC 3165
3166 Dual Band Wireless-AC 3165 Plus Bluetooth
+ 3184 UHD Graphics 605
+ 318c Celeron/Pentium Silver Processor Dynamic Platform and Thermal Framework Processor Participant
+ 318e Celeron/Pentium Silver Processor NorthPeak
+ 319a Celeron/Pentium Silver Processor Trusted Execution Engine Interface
+ 31ac Celeron/Pentium Silver Processor Serial IO I2C Host Controller
+ 31ae Celeron/Pentium Silver Processor Serial IO I2C Host Controller
+ 31bc Celeron/Pentium Silver Processor Serial IO UART Host Controller
+ 31be Celeron/Pentium Silver Processor Serial IO UART Host Controller
+ 31c0 Celeron/Pentium Silver Processor Serial IO UART Host Controller
+ 31c2 Celeron/Pentium Silver Processor Serial IO SPI Host Controller
+ 31c4 Celeron/Pentium Silver Processor Serial IO SPI Host Controller
+ 31c6 Celeron/Pentium Silver Processor Serial IO SPI Host Controller
+ 31d4 Celeron/Pentium Silver Processor Gaussian Mixture Model
+ 31ee Celeron/Pentium Silver Processor Serial IO UART Host Controller
3200 GD31244 PCI-X SATA HBA
1775 c200 C2K onboard SATA host bus adapter
3310 IOP348 I/O Processor
@@ -27600,6 +28053,24 @@
3432 5520/5500/X58 Chipset QuickData Technology Device
3433 5520/5500/X58 Chipset QuickData Technology Device
3438 7500/5520/5500/X58 I/O Hub Throttle Registers
+ 3482 Ice Lake-LP LPC Controller
+ 34a3 Ice Lake-LP SMBus Controller
+ 34a4 Ice Lake-LP SPI Controller
+ 34a8 Ice Lake-LP Serial IO UART Controller #0
+ 34a9 Ice Lake-LP Serial IO UART Controller #1
+ 34aa Ice Lake-LP Serial IO SPI Controller #0
+ 34ab Ice Lake-LP Serial IO SPI Controller #1
+ 34b0 Ice Lake-LP PCI Express Root Port #9
+ 34bc Ice Lake-LP PCI Express Root Port #5
+ 34c5 Ice Lake-LP Serial IO I2c Controller #4
+ 34c6 Ice Lake-LP Serial IO I2c Controller #5
+ 34d3 Ice Lake-LP SATA Controller [AHCI mode]
+ 34e8 Ice Lake-LP Serial IO I2C Controller #0
+ 34e9 Ice Lake-LP Serial IO I2C Controller #1
+ 34ea Ice Lake-LP Serial IO I2C Controller #2
+ 34eb Ice Lake-LP Serial IO I2C Controller #3
+ 34ed Ice Lake-LP USB 3.1 xHCI Host Controller
+ 34f8 Ice Lake-LP SD Controller
3500 6311ESB/6321ESB PCI Express Upstream Port
103c 31fe ProLiant DL140 G3
15d9 9680 X7DBN Motherboard
@@ -27982,6 +28453,7 @@
3b12 3400 Series Chipset LPC Interface Controller
3b13 5 Series/3400 Series Chipset LPC Interface Controller
3b14 3420 Chipset LPC Interface Controller
+ 15d9 0605 X8SIL
3b15 5 Series/3400 Series Chipset LPC Interface Controller
3b16 3450 Chipset LPC Interface Controller
3b17 5 Series/3400 Series Chipset LPC Interface Controller
@@ -27997,6 +28469,7 @@
3b21 5 Series/3400 Series Chipset 2 port SATA IDE Controller
3b22 5 Series/3400 Series Chipset 6 port SATA AHCI Controller
1028 02da OptiPlex 980
+ 15d9 0605 X8SIL
15d9 060d C7SIM-Q Motherboard
3b23 5 Series/3400 Series Chipset 4 port SATA AHCI Controller
3b25 5 Series/3400 Series Chipset SATA RAID Controller
@@ -28028,6 +28501,7 @@
1043 3838 P7P55-M Motherboard
1043 8383 P7P55-M Motherboard
144d c06a R730 Laptop
+ 15d9 0605 X8SIL
15d9 060d C7SIM-Q Motherboard
17c0 10d2 Medion Akoya E7214 Notebook PC [MD98410]
e4bf 50c1 PC1-GROOVE
@@ -28042,6 +28516,7 @@
1028 040a Latitude E6410
1028 040b Latitude E6510
144d c06a R730 Laptop
+ 15d9 0605 X8SIL
15d9 060d C7SIM-Q Motherboard
17c0 10d2 Medion Akoya E7214 Notebook PC [MD98410]
e4bf 50c1 PC1-GROOVE
@@ -28057,6 +28532,7 @@
1028 040a Latitude E6410
1028 040b Latitude E6510
144d c06a R730 Laptop
+ 15d9 0605 X8SIL
15d9 060d C7SIM-Q Motherboard
17c0 10d2 Medion Akoya E7214 Notebook PC [MD98410]
e4bf 50c1 PC1-GROOVE
@@ -28193,6 +28669,10 @@
3e85 8th Gen Core Processor PCIe Controller (x8)
3e89 8th Gen Core Processor PCIe Controller (x4)
3e91 8th Gen Core Processor Gaussian Mixture Model
+ 3e92 UHD Graphics 630 (Desktop)
+ 3e9b UHD Graphics 630 (Mobile)
+ 3ea0 UHD Graphics 620 (Whiskey Lake)
+ 3ea5 Iris Plus Graphics 655
3ec2 8th Gen Core Processor Host Bridge/DRAM Registers
3ec4 8th Gen Core Processor Host Bridge/DRAM Registers
3ec6 8th Gen Core Processor Host Bridge/DRAM Registers
@@ -28374,20 +28854,35 @@
530d 80310 (IOP) IO Processor
5845 QEMU NVM Express Controller
1af4 1100 QEMU Virtual Machine
+ 5900 Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+ 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
17aa 2247 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
+ 5905 Xeon E3-1200 v6/7th Gen Core Processor PCIe Controller (x8)
+ 5909 Xeon E3-1200 v6/7th Gen Core Processor PCIe Controller (x4)
+ 590c Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
590f Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
5910 Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+ 5911 Xeon E3-1200 v6/7th Gen Core Processor Gaussian Mixture Model
5912 HD Graphics 630
5914 Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+ 17aa 225d ThinkPad T480
5916 HD Graphics 620
17aa 2248 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
5917 UHD Graphics 620
+ 17aa 225e ThinkPad T480
+ 5918 Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+ 591b HD Graphics 630
+ 591c UHD Graphics 615
591d HD Graphics P630
- 591f Intel Kaby Lake Host Bridge
+ 591e HD Graphics 615
+ 591f Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+ 5923 HD Graphics 635
+ 5926 Iris Plus Graphics 640
+ 5927 Iris Plus Graphics 650
5a84 Celeron N3350/Pentium N4200/Atom E3900 Series Integrated Graphics Controller
5a88 Celeron N3350/Pentium N4200/Atom E3900 Series Imaging Unit
5a98 Celeron N3350/Pentium N4200/Atom E3900 Series Audio Cluster
@@ -28444,6 +28939,7 @@
65fa 5100 Chipset PCI Express x16 Port 4-7
65ff 5100 Chipset DMA Engine
6f00 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DMI2
+ 15d9 0832 X10SRL-F
6f01 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 0
6f02 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 1
6f03 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 1
@@ -28472,17 +28968,29 @@
6f1e Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Ubox
6f1f Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Ubox
6f20 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 0
+ 15d9 0832 X10SRL-F
6f21 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 1
+ 15d9 0832 X10SRL-F
6f22 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 2
+ 15d9 0832 X10SRL-F
6f23 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 3
+ 15d9 0832 X10SRL-F
6f24 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 4
+ 15d9 0832 X10SRL-F
6f25 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 5
+ 15d9 0832 X10SRL-F
6f26 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 6
+ 15d9 0832 X10SRL-F
6f27 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 7
+ 15d9 0832 X10SRL-F
6f28 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Map/VTd_Misc/System Management
+ 15d9 0832 X10SRL-F
6f29 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Hot Plug
+ 15d9 0832 X10SRL-F
6f2a Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO RAS/Control Status/Global Errors
+ 15d9 0832 X10SRL-F
6f2c Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D I/O APIC
+ 15d9 0832 X10SRL-F
6f30 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Home Agent 0
6f32 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 0
6f33 Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 1
@@ -28696,21 +29204,23 @@
8086 0100 Intel740 Graphics Accelerator
8002 Trusted Execution Technology Registers
8003 Trusted Execution Technology Registers
- 8100 System Controller Hub (SCH Poulsbo)
- 8108 System Controller Hub (SCH Poulsbo) Graphics Controller
- 8110 System Controller Hub (SCH Poulsbo) PCI Express Port 1
- 8112 System Controller Hub (SCH Poulsbo) PCI Express Port 2
- 8114 System Controller Hub (SCH Poulsbo) USB UHCI #1
- 8115 System Controller Hub (SCH Poulsbo) USB UHCI #2
- 8116 System Controller Hub (SCH Poulsbo) USB UHCI #3
- 8117 System Controller Hub (SCH Poulsbo) USB EHCI #1
- 8118 System Controller Hub (SCH Poulsbo) USB Client Controller
- 8119 System Controller Hub (SCH Poulsbo) LPC Bridge
- 811a System Controller Hub (SCH Poulsbo) IDE Controller
- 811b System Controller Hub (SCH Poulsbo) HD Audio Controller
- 811c System Controller Hub (SCH Poulsbo) SDIO Controller #1
- 811d System Controller Hub (SCH Poulsbo) SDIO Controller #2
- 811e System Controller Hub (SCH Poulsbo) SDIO Controller #3
+ 8100 US15W/US15X SCH [Poulsbo] Host Bridge
+ 8101 US15L/UL11L SCH [Poulsbo] Host Bridge
+ 8108 US15W/US15X SCH [Poulsbo] Graphics Controller
+ 8109 US15L/UL11L SCH [Poulsbo] Graphics Controller
+ 8110 US15W/US15X/US15L/UL11L SCH [Poulsbo] PCI Express Port 1
+ 8112 US15W/US15X/US15L/UL11L SCH [Poulsbo] PCI Express Port 2
+ 8114 US15W/US15X/US15L/UL11L SCH [Poulsbo] USB UHCI Controller #1
+ 8115 US15W/US15X/US15L/UL11L SCH [Poulsbo] USB UHCI Controller #2
+ 8116 US15W/US15X/US15L/UL11L SCH [Poulsbo] USB UHCI Controller #3
+ 8117 US15W/US15X/US15L/UL11L SCH [Poulsbo] USB EHCI Controller
+ 8118 US15W/US15X/US15L/UL11L SCH [Poulsbo] USB Client Controller
+ 8119 US15W/US15X/US15L/UL11L SCH [Poulsbo] LPC Bridge
+ 811a US15W/US15X/US15L/UL11L SCH [Poulsbo] IDE Controller
+ 811b US15W/US15X/US15L/UL11L SCH [Poulsbo] HD Audio Controller
+ 811c US15W/US15X/US15L/UL11L SCH [Poulsbo] SDIO/MMC Controller #1
+ 811d US15W/US15X/US15L/UL11L SCH [Poulsbo] SDIO/MMC Controller #2
+ 811e US15W/US15X/US15L/UL11L SCH [Poulsbo] SDIO/MMC Controller #3
8180 Atom Processor E6xx PCI Express Port 3
8181 Atom Processor E6xx PCI Express Port 4
8182 Atom Processor E6xx Integrated Graphics Controller
@@ -28733,6 +29243,7 @@
1993 0ded mGuard-PCI AV#2
1993 0dee mGuard-PCI AV#1
1993 0def mGuard-PCI AV#0
+ 87c0 UHD Graphics 617
8800 Platform Controller Hub EG20T PCI Express Port
8801 Platform Controller Hub EG20T Packet Hub
8802 Platform Controller Hub EG20T Gigabit Ethernet Controller
@@ -28762,6 +29273,7 @@
8c00 8 Series/C220 Series Chipset Family 4-port SATA Controller 1 [IDE mode]
8c01 8 Series Chipset Family 4-port SATA Controller 1 [IDE mode] - Mobile
8c02 8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode]
+ 17aa 309f ThinkCentre M83
8c03 8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode]
103c 1909 ZBook 15
17aa 220e ThinkPad T440p
@@ -28794,28 +29306,34 @@
8c20 8 Series/C220 Series Chipset High Definition Audio Controller
103c 1909 ZBook 15
17aa 220e ThinkPad T440p
+ 17aa 309f ThinkCentre M83
8c21 8 Series/C220 Series Chipset High Definition Audio Controller
8c22 8 Series/C220 Series Chipset Family SMBus Controller
103c 1909 ZBook 15
17aa 220e ThinkPad T440p
+ 17aa 309f ThinkCentre M83
8c23 8 Series Chipset Family CHAP Counters
8c24 8 Series Chipset Family Thermal Management Controller
8c26 8 Series/C220 Series Chipset Family USB EHCI #1
103c 1909 ZBook 15
17aa 220e ThinkPad T440p
17aa 2210 ThinkPad T540p
+ 17aa 309f ThinkCentre M83
2210 17aa ThinkPad T540p
8c2d 8 Series/C220 Series Chipset Family USB EHCI #2
103c 1909 ZBook 15
17aa 220e ThinkPad T440p
+ 17aa 309f ThinkCentre M83
8c31 8 Series/C220 Series Chipset Family USB xHCI
103c 1909 ZBook 15
17aa 220e ThinkPad T440p
+ 17aa 309f ThinkCentre M83
8c33 8 Series/C220 Series Chipset Family LAN Controller
8c34 8 Series/C220 Series Chipset Family NAND Controller
8c3a 8 Series/C220 Series Chipset Family MEI Controller #1
103c 1909 ZBook 15
17aa 220e ThinkPad T440p
+ 17aa 309f ThinkCentre M83
8c3b 8 Series/C220 Series Chipset Family MEI Controller #2
8c3c 8 Series/C220 Series Chipset Family IDE-r Controller
8c3d 8 Series/C220 Series Chipset Family KT Controller
@@ -28832,6 +29350,7 @@
8c4a H87 Express LPC Controller
8c4b HM87 Express LPC Controller
8c4c Q85 Express LPC Controller
+ 17aa 309f ThinkCentre M83
8c4d 8 Series/C220 Series Chipset Family LPC Controller
8c4e Q87 Express LPC Controller
8c4f QM87 Express LPC Controller
@@ -28849,7 +29368,7 @@
8c59 8 Series/C220 Series Chipset Family LPC Controller
8c5a 8 Series/C220 Series Chipset Family LPC Controller
8c5b 8 Series/C220 Series Chipset Family LPC Controller
- 8c5c C220 Series Chipset Family H81 Express LPC Controller
+ 8c5c H81 Express LPC Controller
8c5d 8 Series/C220 Series Chipset Family LPC Controller
8c5e 8 Series/C220 Series Chipset Family LPC Controller
8c5f 8 Series/C220 Series Chipset Family LPC Controller
@@ -28886,9 +29405,10 @@
8cbd 9 Series Chipset Family KT Controller
8cc1 9 Series Chipset Family LPC Controller
8cc2 9 Series Chipset Family LPC Controller
- 8cc3 9 Series Chipset Family HM97 LPC Controller
- 8cc4 9 Series Chipset Family Z97 LPC Controller
- 8cc6 9 Series Chipset Family H97 Controller
+ 8cc3 HM97 Chipset LPC Controller
+ 8cc4 Z97 Chipset LPC Controller
+ 8cc5 QM97 Chipset LPC Controller
+ 8cc6 H97 Chipset LPC Controller
8d00 C610/X99 series chipset 4-port SATA Controller [IDE mode]
8d02 C610/X99 series chipset 6-Port SATA Controller [AHCI mode]
8d04 C610/X99 series chipset SATA Controller [RAID mode]
@@ -28915,14 +29435,20 @@
8d20 C610/X99 series chipset HD Audio Controller
8d21 C610/X99 series chipset HD Audio Controller
8d22 C610/X99 series chipset SMBus Controller
+ 15d9 0832 X10SRL-F
8d24 C610/X99 series chipset Thermal Subsystem
8d26 C610/X99 series chipset USB Enhanced Host Controller #1
+ 15d9 0832 X10SRL-F
8d2d C610/X99 series chipset USB Enhanced Host Controller #2
+ 15d9 0832 X10SRL-F
8d31 C610/X99 series chipset USB xHCI Host Controller
+ 15d9 0832 X10SRL-F
8d33 C610/X99 series chipset LAN Controller
8d34 C610/X99 series chipset NAND Controller
8d3a C610/X99 series chipset MEI Controller #1
+ 15d9 0832 X10SRL-F
8d3b C610/X99 series chipset MEI Controller #2
+ 15d9 0832 X10SRL-F
8d3c C610/X99 series chipset IDE-r Controller
8d3d C610/X99 series chipset KT Controller
8d40 C610/X99 series chipset LPC Controller
@@ -28930,6 +29456,7 @@
8d42 C610/X99 series chipset LPC Controller
8d43 C610/X99 series chipset LPC Controller
8d44 C610/X99 series chipset LPC Controller
+ 15d9 0832 X10SRL-F
8d45 C610/X99 series chipset LPC Controller
8d46 C610/X99 series chipset LPC Controller
8d47 C610/X99 series chipset LPC Controller
@@ -28948,6 +29475,7 @@
8d68 C610/X99 series chipset sSATA Controller [IDE mode]
8d6e C610/X99 series chipset sSATA Controller [RAID mode]
8d7c C610/X99 series chipset SPSR
+ 15d9 0832 X10SRL-F
8d7d C610/X99 series chipset MS SMBus 0
8d7e C610/X99 series chipset MS SMBus 1
8d7f C610/X99 series chipset MS SMBus 2
@@ -29065,7 +29593,9 @@
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]
+ 1028 06dc Latitude E7470
1028 06f3 Latitude 3570
+ 17aa 225d ThinkPad T480
17aa 382a B51-80 Laptop
9d10 Sunrise Point-LP PCI Express Root Port #1
9d12 Sunrise Point-LP PCI Express Root Port #3
@@ -29079,13 +29609,17 @@
17aa 382a B51-80 Laptop
9d19 Sunrise Point-LP PCI Express Root Port #10
9d21 Sunrise Point-LP PMC
+ 1028 06dc Latitude E7470
1028 06f3 Latitude 3570
17aa 224f ThinkPad X1 Carbon 5th Gen
+ 17aa 225d ThinkPad T480
17aa 382a B51-80 Laptop
9d23 Sunrise Point-LP SMBus
+ 1028 06dc Latitude E7470
1028 06f3 Latitude 3570
17aa 2247 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
+ 17aa 225d ThinkPad T480
17aa 382a B51-80 Laptop
9d27 Sunrise Point-LP Serial IO UART Controller #0
9d28 Sunrise Point-LP Serial IO UART Controller #1
@@ -29093,31 +29627,40 @@
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
+ 1028 06dc Latitude E7470
1028 06f3 Latitude 3570
17aa 2247 ThinkPad T570
+ 17aa 225d ThinkPad T480
17aa 382a B51-80 Laptop
9d31 Sunrise Point-LP Thermal subsystem
+ 1028 06dc Latitude E7470
1028 06f3 Latitude 3570
17aa 2247 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
+ 17aa 225d ThinkPad T480
17aa 382a B51-80 Laptop
9d35 Sunrise Point-LP Integrated Sensor Hub
9d3a Sunrise Point-LP CSME HECI #1
+ 1028 06dc Latitude E7470
1028 06f3 Latitude 3570
17aa 2247 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
+ 17aa 225d ThinkPad T480
17aa 382a B51-80 Laptop
9d43 Sunrise Point-LP LPC Controller
17aa 382a B51-80 Laptop
9d48 Sunrise Point-LP LPC Controller
+ 1028 06dc Latitude E7470
1028 06f3 Latitude 3570
9d4e Intel(R) 100 Series Chipset Family LPC Controller/eSPI Controller - 9D4E
+ 17aa 225d ThinkPad T480
9d56 Sunrise Point-LP LPC Controller
9d58 Sunrise Point-LP LPC Controller
17aa 2247 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
9d60 Sunrise Point-LP Serial IO I2C Controller #0
1028 06f3 Latitude 3570
+ 17aa 225d ThinkPad T480
8086 9d60 100 Series PCH/Sunrise Point PCH I2C0 [Skylake/Kaby Lake LPSS I2C]
9d61 Sunrise Point-LP Serial IO I2C Controller #1
9d62 Sunrise Point-LP Serial IO I2C Controller #2
@@ -29126,9 +29669,11 @@
9d65 Sunrise Point-LP Serial IO I2C Controller #5
9d66 Sunrise Point-LP Serial IO UART Controller #2
9d70 Sunrise Point-LP HD Audio
+ 1028 06dc Latitude E7470
1028 06f3 Latitude 3570
17aa 382a B51-80 Laptop
9d71 Sunrise Point-LP HD Audio
+ 17aa 225d ThinkPad T480
a000 Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge
1458 5000 GA-D525TUD
8086 4f4d DeskTop Board D510MO
@@ -29140,75 +29685,86 @@
a002 Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller
a003 Atom Processor D4xx/D5xx/N4xx/N5xx CHAPS counter
a010 Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge
+ 1043 83ac Eee PC 1015PX
144d c072 Notebook N150P
a011 Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller
+ 1043 83ac Eee PC 1015PX
144d c072 Notebook N150P
a012 Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller
+ 1043 83ac Eee PC 1015PX
144d c072 Notebook N150P
a013 Atom Processor D4xx/D5xx/N4xx/N5xx CHAPS counter
- a102 Sunrise Point-H SATA controller [AHCI mode]
- a103 Sunrise Point-H SATA Controller [AHCI mode]
+ 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
a105 Sunrise Point-H SATA Controller [RAID mode]
- a107 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]
a10f Sunrise Point-H SATA Controller [RAID mode]
- a110 Sunrise Point-H PCI Express Root Port #1
- a111 Sunrise Point-H PCI Express Root Port #2
- a112 Sunrise Point-H PCI Express Root Port #3
- a113 Sunrise Point-H PCI Express Root Port #4
- a114 Sunrise Point-H PCI Express Root Port #5
- a115 Sunrise Point-H PCI Express Root Port #6
- a116 Sunrise Point-H PCI Express Root Port #7
- a117 Sunrise Point-H PCI Express Root Port #8
- a118 Sunrise Point-H PCI Express Root Port #9
- a119 Sunrise Point-H PCI Express Root Port #10
- a11a Sunrise Point-H PCI Express Root Port #11
- a11b Sunrise Point-H PCI Express Root Port #12
- a11c Sunrise Point-H PCI Express Root Port #13
- a11d Sunrise Point-H PCI Express Root Port #14
- a11e Sunrise Point-H PCI Express Root Port #15
- a11f Sunrise Point-H PCI Express Root Port #16
- a120 Sunrise Point-H P2SB
- a121 Sunrise Point-H PMC
+ a110 100 Series/C230 Series Chipset Family PCI Express Root Port #1
+ a111 100 Series/C230 Series Chipset Family PCI Express Root Port #2
+ a112 100 Series/C230 Series Chipset Family PCI Express Root Port #3
+ a113 100 Series/C230 Series Chipset Family PCI Express Root Port #4
+ a114 100 Series/C230 Series Chipset Family PCI Express Root Port #5
+ a115 100 Series/C230 Series Chipset Family PCI Express Root Port #6
+ a116 100 Series/C230 Series Chipset Family PCI Express Root Port #7
+ a117 100 Series/C230 Series Chipset Family PCI Express Root Port #8
+ a118 100 Series/C230 Series Chipset Family PCI Express Root Port #9
+ a119 100 Series/C230 Series Chipset Family PCI Express Root Port #10
+ a11a 100 Series/C230 Series Chipset Family PCI Express Root Port #11
+ a11b 100 Series/C230 Series Chipset Family PCI Express Root Port #12
+ a11c 100 Series/C230 Series Chipset Family PCI Express Root Port #13
+ a11d 100 Series/C230 Series Chipset Family PCI Express Root Port #14
+ a11e 100 Series/C230 Series Chipset Family PCI Express Root Port #15
+ a11f 100 Series/C230 Series Chipset Family PCI Express Root Port #16
+ a120 100 Series/C230 Series Chipset Family P2SB
+ a121 100 Series/C230 Series Chipset Family Power Management Controller
+ 1028 06e4 XPS 15 9550
a122 Sunrise Point-H cAVS
- a123 Sunrise Point-H SMBus
- a124 Sunrise Point-H SPI Controller
- a125 Sunrise Point-H Gigabit Ethernet Controller
- a126 Sunrise Point-H Northpeak
- a127 Sunrise Point-H Serial IO UART #0
- a128 Sunrise Point-H Serial IO UART #1
- a129 Sunrise Point-H Serial IO SPI #0
- a12a Sunrise Point-H Serial IO SPI #1
- a12f Sunrise Point-H USB 3.0 xHCI Controller
- a130 Sunrise Point-H USB Device Controller (OTG)
- a131 Sunrise Point-H Thermal subsystem
+ a123 100 Series/C230 Series Chipset Family SMBus
+ 1028 06e4 XPS 15 9550
+ 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
+ a127 100 Series/C230 Series Chipset Family Serial IO UART #0
+ a128 100 Series/C230 Series Chipset Family Serial IO UART #1
+ a129 100 Series/C230 Series Chipset Family Serial IO GSPI #0
+ 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
+ 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
a133 Sunrise Point-H Northpeak ACPI Function
- a135 Sunrise Point-H Integrated Sensor Hub
- a13a Sunrise Point-H CSME HECI #1
- a13b Sunrise Point-H CSME HECI #2
- a13c Sunrise Point-H CSME IDE Redirection
- a13d Sunrise Point-H KT Redirection
- a13e Sunrise Point-H CSME HECI #3
+ 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
+ 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
+ a13e 100 Series/C230 Series Chipset Family MEI Controller #3
a140 Sunrise Point-H LPC Controller
a141 Sunrise Point-H LPC Controller
a142 Sunrise Point-H LPC Controller
- a143 Sunrise Point-H LPC Controller
- a144 Sunrise Point-H LPC Controller
- a145 Sunrise Point-H LPC Controller
- a146 Sunrise Point-H LPC Controller
- a147 Sunrise Point-H LPC Controller
- a148 Sunrise Point-H LPC Controller
- a149 Sunrise Point-H LPC Controller
- a14a Sunrise Point-H LPC Controller
+ a143 H110 Chipset LPC/eSPI Controller
+ a144 H170 Chipset LPC/eSPI Controller
+ a145 Z170 Chipset LPC/eSPI Controller
+ a146 Q170 Chipset LPC/eSPI Controller
+ a147 Q150 Chipset LPC/eSPI Controller
+ a148 B150 Chipset LPC/eSPI Controller
+ a149 C236 Chipset LPC/eSPI Controller
+ a14a C232 Chipset LPC/eSPI Controller
a14b Sunrise Point-H LPC Controller
a14c Sunrise Point-H LPC Controller
- a14d Sunrise Point-H LPC Controller
- a14e Sunrise Point-H LPC Controller
+ a14d QM170 Chipset LPC/eSPI Controller
+ a14e HM170 Chipset LPC/eSPI Controller
+ 1028 06e4 XPS 15 9550
a14f Sunrise Point-H LPC Controller
- a150 Sunrise Point-H LPC Controller
+ a150 CM236 Chipset LPC/eSPI Controller
a151 Sunrise Point-H LPC Controller
- a152 Sunrise Point-H LPC Controller
- a153 Sunrise Point-H LPC Controller
- a154 Sunrise Point-H LPC Controller
+ a152 HM175 Chipset LPC/eSPI Controller
+ a153 QM175 Chipset LPC/eSPI Controller
+ a154 CM238 Chipset LPC/eSPI Controller
a155 Sunrise Point-H LPC Controller
a156 Sunrise Point-H LPC Controller
a157 Sunrise Point-H LPC Controller
@@ -29220,60 +29776,74 @@
a15d Sunrise Point-H LPC Controller
a15e Sunrise Point-H LPC Controller
a15f Sunrise Point-H LPC Controller
- a160 Sunrise Point-H Serial IO I2C Controller #0
- a161 Sunrise Point-H Serial IO I2C Controller #1
- a166 Sunrise Point-H Serial IO UART Controller #2
- a167 Sunrise Point-H PCI Root Port #17
- a168 Sunrise Point-H PCI Root Port #18
- a169 Sunrise Point-H PCI Root Port #19
- a16a Sunrise Point-H PCI Root Port #20
- a170 Sunrise Point-H HD Audio
+ a160 100 Series/C230 Series Chipset Family Serial IO I2C Controller #0
+ 1028 06e4 XPS 15 9550
+ 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
+ a163 100 Series/C230 Series Chipset Family Serial IO I2C Controller #3
+ a166 100 Series/C230 Series Chipset Family Serial IO UART Controller #2
+ a167 100 Series/C230 Series Chipset Family PCI Express Root Port #17
+ a168 100 Series/C230 Series Chipset Family PCI Express Root Port #18
+ a169 100 Series/C230 Series Chipset Family PCI Express Root Port #19
+ a16a 100 Series/C230 Series Chipset Family PCI Express Root Port #20
+ a170 100 Series/C230 Series Chipset Family HD Audio Controller
a171 CM238 HD Audio Controller
- a182 Lewisburg SATA Controller [AHCI mode]
- a186 Lewisburg SATA Controller [RAID mode]
- a190 Lewisburg PCI Express Root Port #1
- a191 Lewisburg PCI Express Root Port #2
- a192 Lewisburg PCI Express Root Port #3
- a193 Lewisburg PCI Express Root Port #4
- a194 Lewisburg PCI Express Root Port #5
- a195 Lewisburg PCI Express Root Port #6
- a196 Lewisburg PCI Express Root Port #7
- a197 Lewisburg PCI Express Root Port #8
- a198 Lewisburg PCI Express Root Port #9
- a199 Lewisburg PCI Express Root Port #10
- a19a Lewisburg PCI Express Root Port #11
- a19b Lewisburg PCI Express Root Port #12
- a19c Lewisburg PCI Express Root Port #13
- a19d Lewisburg PCI Express Root Port #14
- a19e Lewisburg PCI Express Root Port #15
- a19f Lewisburg PCI Express Root Port #16
- a1a0 Lewisburg P2SB
- a1a1 Lewisburg PMC
- a1a2 Lewisburg cAVS
- a1a3 Lewisburg SMBus
- a1a4 Lewisburg SPI Controller
- a1af Lewisburg USB 3.0 xHCI Controller
- a1b1 Lewisburg Thermal Subsystem
- a1ba Lewisburg CSME: HECI #1
- a1bb Lewisburg CSME: HECI #2
- a1bc Lewisburg CSME: IDE-r
- a1bd Lewisburg CSME: KT Controller
- a1be Lewisburg CSME: HECI #3
- a1c1 Lewisburg LPC Controller
- a1c2 Lewisburg LPC Controller
- a1c3 Lewisburg LPC Controller
- a1c4 Lewisburg LPC Controller
- a1c5 Lewisburg LPC Controller
- a1c6 Lewisburg LPC Controller
- a1c7 Lewisburg LPC Controller
- a1d2 Lewisburg SSATA Controller [AHCI mode]
- a1d6 Lewisburg SSATA Controller [RAID mode]
- a1e7 Lewisburg PCI Express Root Port #17
- a1e8 Lewisburg PCI Express Root Port #18
- a1e9 Lewisburg PCI Express Root Port #19
- a1ea Lewisburg PCI Express Root Port #20
- a1f0 Lewisburg MROM 0
- a1f1 Lewisburg MROM 1
+ a182 C620 Series Chipset Family SATA Controller [AHCI mode]
+ a186 C620 Series Chipset Family SATA Controller [RAID mode]
+ a190 C620 Series Chipset Family PCI Express Root Port #1
+ a191 C620 Series Chipset Family PCI Express Root Port #2
+ a192 C620 Series Chipset Family PCI Express Root Port #3
+ a193 C620 Series Chipset Family PCI Express Root Port #4
+ a194 C620 Series Chipset Family PCI Express Root Port #5
+ a195 C620 Series Chipset Family PCI Express Root Port #6
+ a196 C620 Series Chipset Family PCI Express Root Port #7
+ a197 C620 Series Chipset Family PCI Express Root Port #8
+ a198 C620 Series Chipset Family PCI Express Root Port #9
+ a199 C620 Series Chipset Family PCI Express Root Port #10
+ a19a C620 Series Chipset Family PCI Express Root Port #11
+ a19b C620 Series Chipset Family PCI Express Root Port #12
+ a19c C620 Series Chipset Family PCI Express Root Port #13
+ a19d C620 Series Chipset Family PCI Express Root Port #14
+ a19e C620 Series Chipset Family PCI Express Root Port #15
+ a19f C620 Series Chipset Family PCI Express Root Port #16
+ a1a0 C620 Series Chipset Family P2SB
+ a1a1 C620 Series Chipset Family Power Management Controller
+ 15d9 095d X11SPM-TF
+ a1a2 C620 Series Chipset Family cAVS
+ a1a3 C620 Series Chipset Family SMBus
+ 15d9 095d X11SPM-TF
+ a1a4 C620 Series Chipset Family SPI Controller
+ 15d9 095d X11SPM-TF
+ a1a6 C620 Series Chipset Family Trace Hub
+ a1af C620 Series Chipset Family USB 3.0 xHCI Controller
+ 15d9 095d X11SPM-TF
+ a1b1 C620 Series Chipset Family Thermal Subsystem
+ 15d9 095d X11SPM-TF
+ a1ba C620 Series Chipset Family MEI Controller #1
+ 15d9 095d X11SPM-TF
+ a1bb C620 Series Chipset Family MEI Controller #2
+ 15d9 095d X11SPM-TF
+ a1bc C620 Series Chipset Family IDE Redirection
+ a1bd C620 Series Chipset Family KT Redirection
+ a1be C620 Series Chipset Family MEI Controller #3
+ 15d9 095d X11SPM-TF
+ a1c1 C621 Series Chipset LPC/eSPI Controller
+ a1c2 C622 Series Chipset LPC/eSPI Controller
+ 15d9 095d X11SPM-TF
+ a1c3 C624 Series Chipset LPC/eSPI Controller
+ a1c4 C625 Series Chipset LPC/eSPI Controller
+ a1c5 C626 Series Chipset LPC/eSPI Controller
+ a1c6 C627 Series Chipset LPC/eSPI Controller
+ a1c7 C628 Series Chipset LPC/eSPI Controller
+ a1d2 C620 Series Chipset Family SSATA Controller [AHCI mode]
+ a1d6 C620 Series Chipset Family SSATA Controller [RAID mode]
+ a1e7 C620 Series Chipset Family PCI Express Root Port #17
+ a1e8 C620 Series Chipset Family PCI Express Root Port #18
+ a1e9 C620 Series Chipset Family PCI Express Root Port #19
+ a1ea C620 Series Chipset Family PCI Express Root Port #20
+ a1ec C620 Series Chipset Family MROM 0
+ a1ed C620 Series Chipset Family MROM 1
a1f8 Lewisburg IE: HECI #1
a1f9 Lewisburg IE: HECI #2
a1fa Lewisburg IE: IDE-r
@@ -29305,13 +29875,17 @@
a29d 200 Series PCH PCI Express Root Port #14
a29e 200 Series PCH PCI Express Root Port #15
a29f 200 Series PCH PCI Express Root Port #16
- a2a1 200 Series PCH PMC
- a2a3 200 Series PCH SMBus Controller
- a2a7 200 Series PCH Serial IO UART Controller #0
- a2a8 200 Series PCH Serial IO UART Controller #1
- a2a9 200 Series PCH Serial IO SPI Controller #0
- a2aa 200 Series PCH Serial IO SPI Controller #1
- a2af 200 Series PCH USB 3.0 xHCI Controller
+ a2a0 200 Series/Z370 Chipset Family P2SB
+ a2a1 200 Series/Z370 Chipset Family Power Management Controller
+ a2a3 200 Series/Z370 Chipset Family SMBus Controller
+ a2a4 200 Series/Z370 Chipset Family SPI Controller
+ a2a5 200 Series/Z370 Chipset Family Gigabit Ethernet Controller
+ a2a6 200 Series/Z370 Chipset Family Trace Hub
+ a2a7 200 Series/Z370 Chipset Family Serial IO UART Controller #0
+ a2a8 200 Series/Z370 Chipset Family Serial IO UART Controller #1
+ a2a9 200 Series/Z370 Chipset Family Serial IO SPI Controller #0
+ a2aa 200 Series/Z370 Chipset Family Serial IO SPI Controller #1
+ a2af 200 Series/Z370 Chipset Family USB 3.0 xHCI Controller
a2b1 200 Series PCH Thermal Subsystem
a2ba 200 Series PCH CSME HECI #1
a2bb 200 Series PCH CSME HECI #2
@@ -29320,6 +29894,9 @@
a2c6 200 Series PCH LPC Controller (Q270)
a2c7 200 Series PCH LPC Controller (Q250)
a2c8 200 Series PCH LPC Controller (B250)
+ a2c9 Z370 Chipset LPC/eSPI Controller
+ a2d2 X299 Chipset LPC/eSPI Controller
+ a2d3 C422 Chipset LPC/eSPI Controller
a2e0 200 Series PCH Serial IO I2C Controller #0
a2e1 200 Series PCH Serial IO I2C Controller #1
a2e2 200 Series PCH Serial IO I2C Controller #2
@@ -29337,13 +29914,34 @@
a304 H370 Chipset LPC/eSPI Controller
a323 Cannon Lake PCH SMBus Controller
a324 Cannon Lake PCH SPI Controller
- a32c Cannon Lake PCH PCI Express Root Port 21
- a330 Cannon Lake PCH PCI Express Root Port 9
- a342 Cannon Lake PCH PCI Express Root Port 19
- a343 Cannon Lake PCH PCI Express Root Port 20
+ 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
+ a32f Cannon Lake PCH PCI Express Root Port #24
+ a330 Cannon Lake PCH PCI Express Root Port #9
+ a331 Cannon Lake PCH PCI Express Root Port #10
+ a332 Cannon Lake PCH PCI Express Root Port #11
+ a333 Cannon Lake PCH PCI Express Root Port #12
+ a334 Cannon Lake PCH PCI Express Root Port #13
+ a335 Cannon Lake PCH PCI Express Root Port #14
+ a336 Cannon Lake PCH PCI Express Root Port #15
+ a337 Cannon Lake PCH PCI Express Root Port #16
+ a338 Cannon Lake PCH PCI Express Root Port #1
+ a339 Cannon Lake PCH PCI Express Root Port #2
+ a33a Cannon Lake PCH PCI Express Root Port #3
+ a33b Cannon Lake PCH PCI Express Root Port #4
+ a33c Cannon Lake PCH PCI Express Root Port #5
+ a33d Cannon Lake PCH PCI Express Root Port #6
+ a33e Cannon Lake PCH PCI Express Root Port #7
+ a33f Cannon Lake PCH PCI Express Root Port #8
+ a340 Cannon Lake PCH PCI Express Root Port #17
+ a341 Cannon Lake PCH PCI Express Root Port #18
+ a342 Cannon Lake PCH PCI Express Root Port #19
+ a343 Cannon Lake PCH PCI Express Root Port #20
a348 Cannon Lake PCH cAVS
a352 Cannon Lake PCH SATA AHCI Controller
a360 Cannon Lake PCH HECI Controller
+ a363 Cannon Lake PCH Active Management Technology - SOL
a36d Cannon Lake PCH USB 3.1 xHCI Host Controller
a36f Cannon Lake PCH Shared SRAM
a370 Wireless-AC 9560 [Jefferson Peak]
@@ -29362,6 +29960,7 @@
4c53 1051 CE7 mainboard
e4bf 1000 CC8-1-BLUES
d130 Core Processor DMI
+ 15d9 0605 X8SIL
d131 Core Processor DMI
1028 02da OptiPlex 980
15d9 060d C7SIM-Q Motherboard
@@ -29385,6 +29984,7 @@
d156 Core Processor Semaphore and Scratchpad Registers
d157 Core Processor System Control and Status Registers
d158 Core Processor Miscellaneous Registers
+ f1a5 SSD 600P Series
f1a6 SSD Pro 7600p/760p/E 6100p Series
80ee InnoTek Systemberatung GmbH
beef VirtualBox Graphics Adapter
@@ -29516,7 +30116,7 @@
9004 7888 AHA-2930UW SCSI Controller
8b78 ABA-1030
ec78 AHA-4944W/UW
-# acquired by Microsemi
+# Acquired by Microsemi
9005 Adaptec
0010 AHA-2940U2/U2W
9005 2180 AHA-2940U2 SCSI Controller
@@ -29733,6 +30333,7 @@
152d 8a24 QS-8236-16i
152d 8a36 QS-8240-24i
152d 8a37 QS-8242-24i
+ 9005 0608 SmartRAID 3162-8i /e
9005 0800 SmartRAID 3154-8i
9005 0801 SmartRAID 3152-8i
9005 0802 SmartRAID 3151-4i
@@ -29956,7 +30557,9 @@ bdbd Blackmagic Design
a142 UltraStudio HD Mini
a143 DeckLink Mini Recorder 4K
a144 DeckLink Mini Monitor 4K
+ a148 DeckLink SDI Micro
a14b DeckLink 8K Pro
+ a1ff eGPU RX580
c001 TSI Telsys
c0a9 Micron/Crucial Technology
c0de Motorola
@@ -29970,6 +30573,7 @@ caed Canny Edge
cafe Chrysalis-ITS
0003 Luna K3 Hardware Security Module
0006 Luna PCI-e 3000 Hardware Security Module
+cc53 ScaleFlux Inc.
cccc Catapult Communications
ccec Curtiss-Wright Controls Embedded Computing
cddd Tyzx, Inc.
diff --git a/hwdb/pnp_id_registry.html b/hwdb/pnp_id_registry.html
index 11a6f9ff6d..77bb0271f9 100644
--- a/hwdb/pnp_id_registry.html
+++ b/hwdb/pnp_id_registry.html
@@ -2258,7 +2258,7 @@
<tr class="even"><td>Wildfire Communications Inc</td><td>WLD</td><td>02/13/1997</td> </tr>
<tr class="odd"><td>WillNet Inc.</td><td>WNI</td><td>04/19/2000</td> </tr>
<tr class="even"><td>Winbond Electronics Corporation</td><td>WEC</td><td>11/29/1996</td> </tr>
- <tr class="odd"><td>Wincor Nixdorf International GmbH</td><td>WNX</td><td>09/20/2004</td> </tr>
+ <tr class="odd"><td>Diebold Nixdorf Systems GmbH</td><td>WNX</td><td>09/20/2004</td> </tr>
<tr class="even"><td>Winmate Communication Inc</td><td>WMT</td><td>03/15/2001</td> </tr>
<tr class="odd"><td>Winnov L.P.</td><td>WNV</td><td>03/07/1997</td> </tr>
<tr class="even"><td>WiNRADiO Communications</td><td>WRC</td><td>09/11/1997</td> </tr>
@@ -2303,7 +2303,7 @@
<tr class="odd"><td>Z3 Technology</td><td>ZTT</td><td>12/14/2010</td> </tr>
<tr class="even"><td>Zalman Tech Co., Ltd.</td><td>ZMT</td><td>05/07/2007</td> </tr>
<tr class="odd"><td>Zandar Technologies plc</td><td>ZAN</td><td>12/03/2003</td> </tr>
- <tr class="even"><td>Zazzle Technologies</td><td>ZAZ</td><td>01/18/2008</td> </tr>
+ <tr class="even"><td>ZeeVee, Inc.</td><td>ZAZ</td><td>01/18/2008</td> </tr>
<tr class="odd"><td>Zebra Technologies International, LLC</td><td>ZBR</td><td>09/15/2003</td> </tr>
<tr class="even"><td>Zefiro Acoustics</td><td>ZAX</td><td>11/29/1996</td> </tr>
<tr class="odd"><td>ZeitControl cardsystems GmbH</td><td>ZCT</td><td>01/20/1999</td> </tr>
@@ -2434,6 +2434,14 @@
<tr class="even"><td>Varjo Technologies</td><td>VRT</td><td>11/17/2017</td> </tr>
<tr class="odd"><td>Japan E.M.Solutions Co., Ltd.</td><td>JEM</td><td>05/24/2018</td> </tr>
<tr class="even"><td>QD Laser, Inc.</td><td>QDL</td><td>05/31/2018</td> </tr>
+ <tr class="odd"><td>VADATECH INC</td><td>VAT</td><td>07/09/2018</td> </tr>
+ <tr class="even"><td>Medicaroid Corporation</td><td>MCJ</td><td>08/20/2018</td> </tr>
+ <tr class="odd"><td>Razer Taiwan Co. Ltd.</td><td>RZR</td><td>08/20/2018</td> </tr>
+ <tr class="even"><td>GIGA-BYTE TECHNOLOGY CO., LTD.</td><td>GBT</td><td>09/05/2018</td> </tr>
+ <tr class="odd"><td>Kontron GmbH</td><td>KOM</td><td>09/05/2018</td> </tr>
+ <tr class="even"><td>Convergent Engineering, Inc.</td><td>CIE</td><td>09/05/2018</td> </tr>
+ <tr class="odd"><td>WyreStorm Technologies LLC </td><td>WYR</td><td>09/05/2018</td> </tr>
+ <tr class="even"><td>Astro HQ LLC</td><td>AHQ </td><td>09/05/2018</td> </tr>
</tbody>
</table>
</body>
diff --git a/hwdb/usb.ids b/hwdb/usb.ids
index a1db7c6d59..52239c4580 100644
--- a/hwdb/usb.ids
+++ b/hwdb/usb.ids
@@ -9,8 +9,8 @@
# The latest version can be obtained from
# http://www.linux-usb.org/usb.ids
#
-# Version: 2018.05.04
-# Date: 2018-05-04 20:34:07
+# Version: 2018.12.04
+# Date: 2018-12-04 20:34:05
#
# Vendors, devices and interfaces. Please keep sorted.
@@ -783,6 +783,7 @@
8370 7 Port Hub
8371 PS/2 Keyboard And Mouse
8372 FT8U100AX Serial Port
+ 87d0 Cressi Dive Computer Interface
8a28 Rainforest Automation ZigBee Controller
8a98 TIAO Multi-Protocol Adapter
8b28 Alpermann+Velte TCI70
@@ -815,6 +816,7 @@
bcd8 Stellaris Development Board
bcd9 Stellaris Evaluation Board
bcda Stellaris ICDI Board
+ bd90 PICAXE Download Cable [AXE027]
bdc8 Egnite GmbH - JTAG/RS-232 adapter
bfd8 OpenDCC
bfd9 OpenDCC (Sniffer)
@@ -1191,6 +1193,7 @@
00da WLI-U2-KG54L 802.11bg [ZyDAS ZD1211B]
00db External Hard Drive HD-PF32OU2 [Buffalo Ministation]
00e8 WLI-UC-G300N Wireless LAN Adapter [Ralink RT2870]
+ 00f9 Portable DVD Writer (DVSM-PL58U2)
0105 External Hard Drive HD-CEU2 [Drive Station]
012c SATA Bridge
012e WLI-UC-AG300N Wireless LAN Adapter
@@ -1204,11 +1207,14 @@
019e WLI-UC-GNP Wireless LAN Adapter
01a1 MiniStation Metro
01a2 WLI-UC-GNM Wireless LAN Adapter [Ralink RT8070]
+ 01ba SATA Bridge
01dc Ultra-Slim Portable DVD Writer (DVSM-PC58U2V)
01de External Hard Drive HD-PCTU3 [Buffalo MiniStation]
+ 01ea SATA Bridge
01ee WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070]
01f1 SATA Adapter [HD-LBU3]
01fd WLI-UC-G450 Wireless LAN Adapter
+ 027e HD-LCU3
0412 Award Software International
0413 Leadtek Research, Inc.
1310 WinFast TV - NTSC + FM
@@ -1318,6 +1324,7 @@
3121 WoW tap chat
3220 Sound Blaster Tactic(3D) Sigma sound card
3232 Sound Blaster Premium HD [SBX]
+ 3237 SB X-Fi Surround 5.1 Pro
3f00 E-Mu Xboard 25 MIDI Controller
3f02 E-Mu 0202
3f04 E-Mu 0404
@@ -1937,6 +1944,7 @@
8140 TUSB8041 4-Port Hub
8142 TUSB8041 4-Port Hub
926b TUSB9260 Boot Loader
+ bef3 CC1352R1 Launchpad
dbc0 Device Bay Controller
e001 GraphLink [SilverLink]
e003 TI-84 Plus Calculator
@@ -2382,6 +2390,7 @@
0797 Optical Mouse 200
0799 Surface Pro embedded keyboard
07a5 Wireless Receiver 1461C
+ 07b2 2.4GHz Transceiver v8.0 used by mouse Wireless Desktop 900
07b9 Wired Keyboard 200
07c6 RTL8153 GigE [Surface Dock Ethernet]
07ca Surface Pro 3 Docking Station Audio Device
@@ -2397,6 +2406,7 @@
090b Hub
090c SD Card
091a Hub
+ 09c0 Surface Type Cover
0a00 Lumia 950 Dual SIM (RM-1118)
930a ISOUSB.SYS Intel 82930 Isochronous IO Test Board
ffca Catalina
@@ -2464,6 +2474,8 @@
4d62 HP Laser Mobile Mini Mouse
4d75 Rocketfish RF-FLBTAD Bluetooth Adapter
4d81 Dell N889 Optical Mouse
+ 4d91 Laser mouse M-D16DL
+ 4d92 Optical mouse M-D17DR
4de3 HP 5-Button Optical Comfort Mouse
4de7 webcam
4e04 Lenovo Keyboard KB1021
@@ -2525,6 +2537,7 @@
080f Webcam C120
0810 QuickCam Pro
0819 Webcam C210
+ 081a Webcam C260
081b Webcam C310
081d HD Webcam C510
0820 QuickCam VC
@@ -2728,6 +2741,7 @@
c07d G502 Mouse
c07e G402 Gaming Mouse
c083 G403 Prodigy Gaming Mouse
+ c084 G203 Gaming Mouse
c101 UltraX Media Remote
c110 Harmony 785/880/885 Remote
c111 Harmony 525 Remote
@@ -2824,9 +2838,11 @@
c31c Keyboard K120
c31d Media Keyboard K200
c31f Comfort Keyboard K290
+ c326 Washable Keyboard K310
c328 Corded Keyboard K280e
c332 G502 Proteus Spectrum Optical Mouse
c335 G910 Orion Spectrum Mechanical Keyboard
+ c33a G413 Gaming Keyboard
c401 TrackMan Marble Wheel
c402 Marble Mouse (2-button)
c403 Turbo TrackMan Marble FX
@@ -3158,6 +3174,7 @@
4254 BUA-100 Bluetooth Adapter
ac01 Savi 7xx
ad01 GameCom 777 5.1 Headset
+ af01 DA80
c008 Audio 655 DSP
c00e Blackwire C310 headset
0480 Toshiba America Inc
@@ -3172,6 +3189,7 @@
a009 Stor.E Basics
a00d STOR.E BASICS 500GB
a100 Canvio Alu 2TB 2.5" Black External Disk Model HDTH320EK3CA
+ a102 Canvio Alu 2TB 2.5" Black External Disk Model HDTH320EK3CA
a202 Canvio Basics HDD
a208 Canvio Basics 2TB USB 3.0 Portable Hard Drive
b001 Stor.E Partner
@@ -3199,6 +3217,8 @@
0483 STMicroelectronics
0137 BeWAN ADSL USB ST (blue or green)
0138 Unicorn II (ST70138B + MTC-20174TQ chipset)
+ 0adb Android Debug Bridge (ADB) device
+ 0afb Android Fastboot device
1307 Cytronix 6in1 Card Reader
163d Cool Icam Digi-MP3
2015 TouchChip® Fingerprint Reader
@@ -3210,13 +3230,18 @@
3747 ST Micro Connect Lite
3748 ST-LINK/V2
374b ST-LINK/V2.1
+ 374d STLINK-V3 Loader
+ 374e STLINK-V3
+ 374f STLINK-V3
+ 3752 ST-LINK/V2.1
+ 3753 STLINK-V3
4810 ISDN adapter
481d BT Digital Access adapter
5000 ST Micro/Ergenic ERG BT-002 Bluetooth Adapter
5001 ST Micro Bluetooth Device
5710 Joystick in FS Mode
5720 Mass Storage Device
- 5721 Hantek DDS-3X25 Arbitrary Waveform Generator
+ 5721 Interrupt Demo
5722 Bulk Demo
5730 Audio Speaker
5731 Microphone
@@ -3713,6 +3738,7 @@
1787 PIXMA MX490 Series
178a PIXMA MG3600 Series
178d PIXMA MG6853
+ 180b PIXMA MG3000 series
1900 CanoScan LiDE 90
1901 CanoScan 8800F
1904 CanoScan LiDE 100
@@ -3844,6 +3870,7 @@
26b0 MF4600 series
26b4 MF4010 series
26b5 MF4200 series
+ 26b6 FAX-L140/L130
26da LBP3010B printer
26e6 iR1024
271a LBP6000
@@ -4151,7 +4178,10 @@
329c PowerShot SX400 IS
329d PowerShot G7 X
329f PowerShot SX530 HS
+ 32a0 EOS M10
32a6 PowerShot SX710 HS
+ 32a7 PowerShot SX610 HS
+ 32a8 PowerShot G3 X
32aa Powershot ELPH 160 / IXUS 160
32ab PowerShot ELPH 350HS / IXUS 275 HS
32ac PowerShot ELPH 170 IS / IXUS 170
@@ -4163,6 +4193,11 @@
32bf PowerShot SX420 IS
32c1 PowerShot ELPH 180 / IXUS 175
32c2 PowerShot SX720 HS
+ 32c5 EOS M6
+ 32cc EOS 200D
+ 32d1 EOS M100
+ 32d2 EOS M50
+ 32d4 Powershot ELPH 185 / IXUS 185 / IXY 200
32d5 PowerShot SX430 IS
32db SELPHY CP1300
04aa DaeWoo Telecom, Ltd
@@ -4623,6 +4658,7 @@
10e1 fi-5220C
10e7 fi-5900C
10fe S500
+ 1104 KD02906 Line Thermal Printer
1150 fi-6230
125a PalmSecure Sensor Device - MP
200f Sigma DP2 (Mass Storage)
@@ -4808,6 +4844,7 @@
9004 Microchip REAL ICE
900a PICkit3
9012 PICkit4
+ 9015 ICD 4 In-Circuit Debugger
c001 PicoLCD 20x4
e11c TL866CS EEPROM Programmer [MiniPRO]
f2c4 Macareux-labs Hygrometry Temperature Sensor
@@ -4815,6 +4852,7 @@
f3aa Macareux-labs Usbce Bootloader mode
f437 SBE Tech Ultrasonic Anemometer
f4b5 SmartScope
+ f5fe TrueRNG
f8da Hughski Ltd. ColorHug
f8e8 Harmony 300/350 Remote
f91c SPROG IIv3
@@ -4846,6 +4884,7 @@
2519 Shenzhen LogoTech 2.4GHz receiver
2832 HT82A832R Audio MCU
2834 HT82A834R Audio MCU
+ 4545 Keyboard [Diatec Majestouch 2 Tenkeyless]
a01c wireless multimedia keyboard with trackball [Trust ADURA 17911]
a050 Chatman V1
a052 USB-zyTemp
@@ -4865,6 +4904,7 @@
0d0a CD-R Drive KXL-CB20AN
0d0d CDRCB03
0d0e DVD-ROM & CD-R/RW
+ 0d14 DVD-RAM MLT08
0f07 KX-MB2030 Multifunction Laser Printer
0f40 Printer
104d Elite Panaboard UB-T880 (HID)
@@ -5235,8 +5275,8 @@
685c GT-I9250 Phone [Galaxy Nexus] (Mass storage mode)
685d GT-I9100 Phone [Galaxy S II] (Download mode)
685e GT-I9100 / GT-C3350 Phones (USB Debugging mode)
- 6860 Galaxy (MTP)
- 6863 GT-I9500 [Galaxy S4] / GT-I9250 [Galaxy Nexus] (network tethering)
+ 6860 Galaxy series, misc. (MTP mode)
+ 6863 Galaxy series, misc. (tethering mode)
6864 GT-I9070 (network tethering, USB debugging enabled)
6865 Galaxy (PTP mode)
6866 Galaxy (debugging mode)
@@ -5376,8 +5416,10 @@
b3fd HD WebCam (Asus N-series)
b40e HP Truevision HD camera
b444 Lenovo Integrated Webcam
+ b563 Integrated Camera
b5ce Integrated Camera
b5cf Integrated IR Camera
+ b5db HP Webcam
04f3 Elan Microelectronics Corp.
000a Touchscreen
0103 ActiveJet K-2024 Multimedia Keyboard
@@ -5953,6 +5995,7 @@
04fd Soliton Systems, K.K.
0003 Smart Card Reader II
04fe PFU, Ltd
+ 0006 Happy Hacking Keyboard Lite2
04ff E-CMOS Corp.
0500 Siam United Hi-Tech
0001 DART Keyboard Mouse
@@ -6044,6 +6087,7 @@
0257 F5U257 Serial
0304 FSU304 USB 2.0 - 4 Ports Hub
0307 USB 2.0 - 7 ports Hub [FSU307]
+ 038c F2CU038 HDMI Adapter
0409 F5U409 Serial
0416 Staples 12416 7 port desktop hub
0551 F6C550-AVR UPS
@@ -6484,6 +6528,7 @@
07c4 ILCE-6000 (aka Alpha-6000) in Mass Storage mode
082f Walkman NWZW Series
0847 WG-C10 Portable Wireless Server
+ 0884 MDR-ZX770BN [Wireless Noise Canceling Stereo Headset]
088c Portable Headphone Amplifier
08b7 ILCE-6000 (aka Alpha-6000) in MTP mode
094e ILCE-6000 (aka Alpha-6000) in PC Remote mode
@@ -6647,7 +6692,9 @@
2802 Kbd Hub
3002 Keyboard
3004 Genius KB-29E
+ 3027 Sun-Flex ProTouch
3107 Keyboard
+ 3132 Optical mouse M-DY4DR / M-DY6DR
4006 FID 638 Mouse (Sun Microsystems)
0567 Xyratex International, Ltd
0568 Quartz Ingenierie
@@ -6832,17 +6879,20 @@
0003 Device Bay Controller
056e Elecom Co., Ltd
0002 29UO Mouse
- 0057 M-PGDL Mouse
- 005c M-PGDL Mouse
- 005d M-FGDL Mouse
- 005e M-FG2DL Mouse
- 0062 M-D18DR Mouse
- 0063 M-SODL Mouse
- 0069 M-GE1UL Mouse
- 0071 M-GE3DL Mouse
- 0072 M-LS6UL Mouse
- 0073 M-LS7UL Mouse
- 0074 M-FW1UL Mouse
+ 0057 Micro Grast Pop M-PGDL
+ 005c Micro Grast Pop M-PG2DL
+ 005d Micro Grast Fit M-FGDL
+ 005e Micro Grast Fit M-FG2DL
+ 0062 Optical mouse M-D18DR
+ 0063 Laser mouse M-SODL
+ 0069 Laser mouse M-GE1UL
+ 0071 Laser mouse M-GE3DL
+ 0072 Laser mouse M-LS6UL
+ 0073 Laser mouse M-LS7UL
+ 0074 Optical mouse M-FW1UL
+ 0075 M-FW2DL Mouse
+ 2003 JC-U3613M
+ 2004 JC-U3613M
200c LD-USB/TX
4002 Laneed 100Mbps Ethernet LD-USB/TX [pegasus]
4005 LD-USBL/TX
@@ -18256,6 +18306,8 @@
ad02 SE340D PC Remote Control
af01 AUVIO Universal Remote Receiver for PlayStation 3
1d5b Smartronix, Inc.
+1d5c Fresco Logic
+ 2000 FL2000/FL2000DX VGA/DVI/HDMI Adapter
1d6b Linux Foundation
0001 1.1 root hub
0002 2.0 root hub
@@ -18386,6 +18438,7 @@
1fc9 NXP Semiconductors
0003 LPC1343
010b PR533
+ 012b i.MX 8M Dual/8M QuadLite/8M Quad Serial Downloader
1fde ILX Lightwave Corporation
0001 UART Bridge
1fe7 Vertex Wireless Co., Ltd.
@@ -18623,6 +18676,25 @@
0001 EPOC Consumer Headset Wireless Dongle
21d6 Agecodagis SARL
0002 Seismic recorder [Tellus]
+2207 Fuzhou Rockchip Electronics Company
+ 0010 GoClever Tab R83
+ 0011 SmartTab
+ 281a RK2818 in Mask ROM mode
+ 290a RK2918 in Mask ROM mode
+ 292a RK2928 in Mask ROM mode
+ 292c RK3026 in Mask ROM mode
+ 300a RK3066 in Mask ROM mode
+ 300b RK3168 in Mask ROM mode
+ 301a RK3036 in Mask ROM mode
+ 310a RK3066B in Mask ROM mode
+ 310b RK3188 in Mask ROM mode
+ 310c RK3126/RK3128 in Mask ROM mode
+ 310d RK3126 in Mask ROM mode
+ 320a RK3288 in Mask ROM mode
+ 320b RK3228/RK3229 in Mask ROM mode
+ 320c RK3328 in Mask ROM mode
+ 330a RK3368 in Mask ROM mode
+ 330c RK3399 in Mask ROM mode
2222 MacAlly
0004 iWebKey Keyboard
2520 Mini Tablet
@@ -18841,6 +18913,9 @@
002d Alcor mouse
0047 MasterKeys Pro L
9494 Sirus Headset
+2548 Pulse-Eight
+ 1001 CEC Adapter
+ 1002 CEC Adapter
2632 TwinMOS
3209 7-in-1 Card Reader
2639 Xsens
@@ -18954,6 +19029,11 @@
0100 Dual-relay board
0500 Energy meter
0502 Precision barometer
+28de Valve Software
+ 1102 Wired Controller
+ 1142 Wireless Steam Controller
+ 2000 Lighthouse FPGA RX
+ 2101 Watchman Dongle
2931 Jolla Oy
0a01 Jolla Phone MTP
0a02 Jolla Phone Developer
@@ -18998,6 +19078,15 @@
200b MX Phone (PTP)
200c MX Phone (PTP & ADB)
2012 MX Phone (MTP & ACM)
+2ac7 Ultrahaptics Ltd.
+ 0101 Evaluation Kit [Dragonfly]
+ 0102 UHDK5
+ 0104 Touchbase
+ 0110 STRATOS Explore
+ 0111 STRATOS Explore DFU
+ 0112 STRATOS Inspire
+ 0113 STRATOS Inspire DFU
+ ffff DFU
2b24 KeepKey LLC
0001 Bitcoin hardware wallet
2c02 Planex Communications
@@ -19015,6 +19104,8 @@
2dcf Dialog Semiconductor
c952 Audio Class 2.0 Devices
2fb2 Fujitsu, Ltd
+3016 Boundary Devices, LLC
+ 0001 Nitrogen Bootloader
3125 Eagletron
0001 TrackerPod Camera Stand
3136 Navini Networks
diff --git a/man/.dir-locals.el b/man/.dir-locals.el
index 1c2512052d..c252bd3703 100644
--- a/man/.dir-locals.el
+++ b/man/.dir-locals.el
@@ -1,8 +1,5 @@
; special .c mode with reduced indentation for man pages
-((nil . ((indent-tabs-mode . nil)
- (tab-width . 8)
- (fill-column . 79)))
- (c-mode . ((fill-column . 80)
+((c-mode . ((fill-column . 80)
(c-basic-offset . 2)
(eval . (c-set-offset 'substatement-open 0))
(eval . (c-set-offset 'statement-case-open 0))
@@ -10,5 +7,8 @@
(eval . (c-set-offset 'arglist-intro '++))
(eval . (c-set-offset 'arglist-close 0))))
(nxml-mode . ((nxml-child-indent . 2)
- (fill-column . 119)))
- (meson-mode . ((meson-indent-basic . 8))))
+ (fill-column . 109)))
+ (meson-mode . ((meson-indent-basic . 8)))
+ (nil . ((indent-tabs-mode . nil)
+ (tab-width . 8)
+ (fill-column . 79))))
diff --git a/man/bootctl.xml b/man/bootctl.xml
index 9521dc9d98..9cfa9cccdf 100644
--- a/man/bootctl.xml
+++ b/man/bootctl.xml
@@ -34,10 +34,10 @@
<refsect1>
<title>Description</title>
- <para><command>bootctl</command> can check the EFI boot loader status, list
- available entries, and install, update, or remove the
- <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- boot loader on the current system.</para>
+ <para><command>bootctl</command> can check the EFI boot loader status, list available boot loaders and boot loader
+ entries, and install, update, or remove the
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> boot loader on the
+ current system.</para>
</refsect1>
<refsect1>
@@ -45,8 +45,6 @@
<para>The following options are understood:</para>
<variablelist>
- <xi:include href="standard-options.xml" xpointer="help" />
- <xi:include href="standard-options.xml" xpointer="version" />
<varlistentry>
<term><option>--path=</option></term>
<listitem><para>Path to the EFI System Partition (ESP). If not specified, <filename>/efi</filename>,
@@ -64,8 +62,12 @@
<varlistentry>
<term><option>--no-variables</option></term>
- <listitem><para>Do not touch the EFI boot variables.</para></listitem>
+ <listitem><para>Do not touch the firmware's boot loader list stored in EFI variables.</para></listitem>
</varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="no-pager"/>
+ <xi:include href="standard-options.xml" xpointer="help"/>
+ <xi:include href="standard-options.xml" xpointer="version"/>
</variablelist>
</refsect1>
@@ -76,42 +78,54 @@
<varlistentry>
<term><option>status</option></term>
- <listitem><para>Shows the currently installed versions of the boot loader binaries and all current
- EFI boot variables. If no command is specified, this is the implied default.</para></listitem>
+ <listitem><para>Shows brief information about the system firmware, the boot loader that was used to boot the
+ system, the boot loaders currently available in the ESP, the boot loaders listed in the firmware's list of boot
+ loaders and the current default boot loader entry. If no command is specified, this is the implied
+ default.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>list</option></term>
+ <term><option>install</option></term>
- <listitem><para>Shows all configured boot loader entries.</para></listitem>
+ <listitem><para>Installs systemd-boot into the EFI system partition. A copy of <command>systemd-boot</command>
+ will be stored as the EFI default/fallback loader at
+ <filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. The boot loader is then added to the
+ top of the firmware's boot loader list.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>update</option></term>
<listitem><para>Updates all installed versions of
- <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
- if the current version is newer than the version installed in the EFI system
- partition. This also includes the EFI default/fallback loader at
- <filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. A
- systemd-boot entry in the EFI boot variables is created if there is no current
- entry. The created entry will be added to the end of the boot order list.</para></listitem>
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>, if the
+ available version is newer than the version installed in the EFI system partition. This also includes the EFI
+ default/fallback loader at <filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. The boot
+ loader is then added to end of the firmware's boot loader list if missing.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>install</option></term>
+ <term><option>remove</option></term>
- <listitem><para>Installs systemd-boot into the EFI system partition. A copy of systemd-boot will
- be stored as the EFI default/fallback loader at
- <filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. A systemd-boot entry in
- the EFI boot variables is created and added to the top of the boot order list.</para></listitem>
+ <listitem><para>Removes all installed versions of <command>systemd-boot</command> from the EFI system partition
+ and the firmware's boot loader list.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>remove</option></term>
+ <term><option>list</option></term>
- <listitem><para>Removes all installed versions of systemd-boot from the EFI system partition,
- and removes systemd-boot from the EFI boot variables.</para></listitem>
+ <listitem><para>Shows all available boot loader entries implementing the <ulink
+ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
+ Specification</ulink>, as well as any other entries discovered or automatically generated by the boot
+ loader.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>set-default</option> <replaceable>ID</replaceable></term>
+ <term><option>set-oneshot</option> <replaceable>ID</replaceable></term>
+
+ <listitem><para>Sets the default boot loader entry. Takes a single boot loader entry ID string as argument. The
+ <option>set-oneshot</option> command will set the default entry only for the next boot, the
+ <option>set-default</option> will set it persistently for all future boots.</para></listitem>
</varlistentry>
</variablelist>
@@ -123,11 +137,18 @@
</refsect1>
<refsect1>
+ <title>Environment</title>
+ <para>If <varname>$SYSTEMD_RELAX_ESP_CHECKS=1</varname> is set the validation checks for the ESP are relaxed, and
+ the path specified with <option>--path=</option> may refer to any kind of file system on any kind of
+ partition.</para>
+ </refsect1>
+
+ <refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
- <ulink url="https://github.com/systemd/systemd/blob/master/doc/BOOT_LOADER_SPECIFICATION.md">Boot Loader Specification</ulink>,
- <ulink url="https://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface">Boot Loader Interface</ulink>
+ <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
+ <ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>
</para>
</refsect1>
</refentry>
diff --git a/man/busctl.xml b/man/busctl.xml
index c0bbed1c87..a5e3d92cf0 100644
--- a/man/busctl.xml
+++ b/man/busctl.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -144,6 +144,28 @@
</varlistentry>
<varlistentry>
+ <term><option>--json=</option><replaceable>MODE</replaceable></term>
+
+ <listitem>
+ <para>When used with the <command>call</command> or <command>get-property</command> command, shows output
+ formatted as JSON. Expects one of <literal>short</literal> (for the shortest possible output without any
+ redundant whitespace or line breaks) or <literal>pretty</literal> (for a pretty version of the same, with
+ indentation and line breaks). Note that transformation from D-Bus marshalling to JSON is done in a loss-less
+ way, which means type information is embedded into the JSON object tree.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-j</option></term>
+
+ <listitem>
+ <para>Equivalent to <option>--json=pretty</option> when invoked interactively from a terminal. Otherwise
+ equivalent to <option>--json=short</option>, in particular when the output is piped to some other
+ program.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--expect-reply=</option><replaceable>BOOL</replaceable></term>
<listitem>
@@ -269,8 +291,9 @@
<listitem><para>Dump messages being exchanged. If
<replaceable>SERVICE</replaceable> is specified, show messages
to or from this peer, identified by its well-known or unique
- name. Otherwise, show all messages on the bus. Use Ctrl-C to
- terminate the dump.</para></listitem>
+ name. Otherwise, show all messages on the bus. Use
+ <keycombo><keycap>Ctrl</keycap><keycap>C</keycap></keycombo>
+ to terminate the dump.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/coredump.conf.xml b/man/coredump.conf.xml
index 43a3679130..ee3c1b6919 100644
--- a/man/coredump.conf.xml
+++ b/man/coredump.conf.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/coredumpctl.xml b/man/coredumpctl.xml
index caa1bb1c0f..94d5626fb5 100644
--- a/man/coredumpctl.xml
+++ b/man/coredumpctl.xml
@@ -210,7 +210,8 @@
<varlistentry>
<term><command>info</command></term>
- <listitem><para>Show detailed information about core dumps
+ <listitem><para>Show detailed information about the last core dump
+ or core dumps matching specified characteristics
captured in the journal.</para></listitem>
</varlistentry>
diff --git a/man/crypttab.xml b/man/crypttab.xml
index dcaf03d2ca..3574ce00da 100644
--- a/man/crypttab.xml
+++ b/man/crypttab.xml
@@ -251,6 +251,15 @@
</varlistentry>
<varlistentry>
+ <term><option>sector-size=</option></term>
+
+ <listitem><para>Specifies the sector size in bytes. See
+ <citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for possible values and the default value of this
+ option.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>swap</option></term>
<listitem><para>The encrypted block device will be used as a
diff --git a/man/daemon.xml b/man/daemon.xml
index 36c7c09db1..7724bb4e08 100644
--- a/man/daemon.xml
+++ b/man/daemon.xml
@@ -593,7 +593,7 @@ AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])</p
<citerefentry project='die-net'><refentrytitle>automake</refentrytitle><manvolnum>1</manvolnum></citerefentry>-based
projects:</para>
- <programlisting>DISTCHECK_CONFIGURE_FLAGS = \
+ <programlisting>AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)</programlisting>
<para>Finally, unit files should be installed in the system with an automake excerpt like the following:</para>
diff --git a/man/dnssec-trust-anchors.d.xml b/man/dnssec-trust-anchors.d.xml
index 541febc38b..d5faee2918 100644
--- a/man/dnssec-trust-anchors.d.xml
+++ b/man/dnssec-trust-anchors.d.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/hwdb.xml b/man/hwdb.xml
index e77776f35f..7d550c6d7e 100644
--- a/man/hwdb.xml
+++ b/man/hwdb.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/id128-app-specific.c b/man/id128-app-specific.c
new file mode 100644
index 0000000000..b81e50ff32
--- /dev/null
+++ b/man/id128-app-specific.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <systemd/sd-id128.h>
+
+#define OUR_APPLICATION_ID SD_ID128_MAKE(c2,73,27,73,23,db,45,4e,a6,3b,b9,6e,79,b5,3e,97)
+
+int main(int argc, char *argv[]) {
+ sd_id128_t id;
+ sd_id128_get_machine_app_specific(OUR_APPLICATION_ID, &id);
+ printf("Our application ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
+ return 0;
+}
diff --git a/man/journal-iterate-unique.c b/man/journal-iterate-unique.c
new file mode 100644
index 0000000000..fcf92e7b3f
--- /dev/null
+++ b/man/journal-iterate-unique.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <string.h>
+#include <systemd/sd-journal.h>
+
+int main(int argc, char *argv[]) {
+ sd_journal *j;
+ const void *d;
+ size_t l;
+ int r;
+
+ r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
+ if (r < 0) {
+ fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
+ return 1;
+ }
+ r = sd_journal_query_unique(j, "_SYSTEMD_UNIT");
+ if (r < 0) {
+ fprintf(stderr, "Failed to query journal: %s\n", strerror(-r));
+ return 1;
+ }
+ SD_JOURNAL_FOREACH_UNIQUE(j, d, l)
+ printf("%.*s\n", (int) l, (const char*) d);
+ sd_journal_close(j);
+ return 0;
+}
diff --git a/man/journalctl.xml b/man/journalctl.xml
index a4f9e2d7ee..58f3aa205a 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -148,9 +148,9 @@
<term><option>-a</option></term>
<term><option>--all</option></term>
- <listitem><para>Show all fields in full, even if they
- include unprintable characters or are very
- long.</para></listitem>
+ <listitem><para>Show all fields in full, even if they include unprintable characters or are very long. By
+ default, fields with unprintable characters are abbreviated as "blob data". (Note that the pager may escape
+ unprintable characters again.)</para></listitem>
</varlistentry>
<varlistentry>
@@ -316,10 +316,23 @@
<option>json</option>
</term>
<listitem>
- <para>formats entries as JSON data structures, one per
- line (see
- <ulink url="https://www.freedesktop.org/wiki/Software/systemd/json">Journal JSON Format</ulink>
- for more information).</para>
+ <para>formats entries as JSON objects, separated by newline characters (see <ulink
+ url="https://www.freedesktop.org/wiki/Software/systemd/json">Journal JSON Format</ulink> for more
+ information). Field values are generally encoded as JSON strings, with three exceptions:
+ <orderedlist>
+ <listitem><para>Fields larger than 4096 bytes are encoded as <constant>null</constant> values. (This
+ may be turned off by passing <option>--all</option>, but be aware that this may allocate overly long
+ JSON objects.) </para></listitem>
+
+ <listitem><para>Journal entries permit non-unique fields within the same log entry. JSON does not allow
+ non-unique fields within objects. Due to this, if a non-unique field is encountered a JSON array is
+ used as field value, listing all field values as elements.</para></listitem>
+
+ <listitem><para>Fields containing non-printable or non-UTF8 bytes are encoded as arrays containing
+ the raw bytes individually formatted as unsigned numbers.</para></listitem>
+ </orderedlist>
+
+ Note that this encoding is reversible (with the exception of the size limit).</para>
</listitem>
</varlistentry>
@@ -348,6 +361,19 @@
<varlistentry>
<term>
+ <option>json-seq</option>
+ </term>
+ <listitem>
+ <para>formats entries as JSON data structures, but prefixes them with an ASCII Record Separator
+ character (0x1E) and suffixes them with an ASCII Line Feed character (0x0A), in accordance with <ulink
+ url="https://tools.ietf.org/html/rfc7464">JavaScript Object Notation (JSON) Text Sequences </ulink>
+ (<literal>application/json-seq</literal>).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
<option>cat</option>
</term>
<listitem>
@@ -375,14 +401,11 @@
<varlistentry>
<term><option>--output-fields=</option></term>
- <listitem><para>A comma separated list of the fields which should
- be included in the output. This only has an effect for the output modes
- which would normally show all fields (<option>verbose</option>,
- <option>export</option>, <option>json</option>,
- <option>json-pretty</option>, and <option>json-sse</option>). The
- <literal>__CURSOR</literal>, <literal>__REALTIME_TIMESTAMP</literal>,
- <literal>__MONOTONIC_TIMESTAMP</literal>, and
- <literal>_BOOT_ID</literal> fields are always
+ <listitem><para>A comma separated list of the fields which should be included in the output. This only has an
+ effect for the output modes which would normally show all fields (<option>verbose</option>,
+ <option>export</option>, <option>json</option>, <option>json-pretty</option>, <option>json-sse</option> and
+ <option>json-seq</option>). The <literal>__CURSOR</literal>, <literal>__REALTIME_TIMESTAMP</literal>,
+ <literal>__MONOTONIC_TIMESTAMP</literal>, and <literal>_BOOT_ID</literal> fields are always
printed.</para></listitem>
</varlistentry>
@@ -706,18 +729,6 @@
</varlistentry>
<varlistentry>
- <term><option>--new-id128</option></term>
-
- <listitem><para>Instead of showing journal contents, generate
- a new 128-bit ID suitable for identifying messages. This is
- intended for usage by developers who need a new identifier for
- a new message they introduce and want to make
- recognizable. This will print the new ID in four different
- formats which can be copied into source code or similar.
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
<term><option>--header</option></term>
<listitem><para>Instead of showing journal contents, show
@@ -738,32 +749,28 @@
<term><option>--vacuum-time=</option></term>
<term><option>--vacuum-files=</option></term>
- <listitem><para>Removes the oldest archived journal files until the disk
- space they use falls below the specified size (specified with
- the usual <literal>K</literal>, <literal>M</literal>,
- <literal>G</literal> and <literal>T</literal> suffixes), or all
- archived journal files contain no data older than the specified
- timespan (specified with the usual <literal>s</literal>,
- <literal>m</literal>, <literal>h</literal>,
- <literal>days</literal>, <literal>months</literal>,
- <literal>weeks</literal> and <literal>years</literal> suffixes),
- or no more than the specified number of separate journal files
- remain. Note that running <option>--vacuum-size=</option> has
- only an indirect effect on the output shown by
- <option>--disk-usage</option>, as the latter includes active
- journal files, while the vacuuming operation only operates
- on archived journal files. Similarly,
- <option>--vacuum-files=</option> might not actually reduce the
- number of journal files to below the specified number, as it
- will not remove active journal
- files. <option>--vacuum-size=</option>,
- <option>--vacuum-time=</option> and
- <option>--vacuum-files=</option> may be combined in a single
- invocation to enforce any combination of a size, a time and a
- number of files limit on the archived journal
- files. Specifying any of these three parameters as zero is
- equivalent to not enforcing the specific limit, and is thus
- redundant.</para></listitem>
+ <listitem><para>Removes the oldest archived journal files until the disk space they use falls below the
+ specified size (specified with the usual <literal>K</literal>, <literal>M</literal>, <literal>G</literal> and
+ <literal>T</literal> suffixes), or all archived journal files contain no data older than the specified timespan
+ (specified with the usual <literal>s</literal>, <literal>m</literal>, <literal>h</literal>,
+ <literal>days</literal>, <literal>months</literal>, <literal>weeks</literal> and <literal>years</literal>
+ suffixes), or no more than the specified number of separate journal files remain. Note that running
+ <option>--vacuum-size=</option> has only an indirect effect on the output shown by
+ <option>--disk-usage</option>, as the latter includes active journal files, while the vacuuming operation only
+ operates on archived journal files. Similarly, <option>--vacuum-files=</option> might not actually reduce the
+ number of journal files to below the specified number, as it will not remove active journal
+ files.</para>
+
+ <para><option>--vacuum-size=</option>, <option>--vacuum-time=</option> and <option>--vacuum-files=</option>
+ may be combined in a single invocation to enforce any combination of a size, a time and a number of files limit
+ on the archived journal files. Specifying any of these three parameters as zero is equivalent to not enforcing
+ the specific limit, and is thus redundant.</para>
+
+ <para>These three switches may also be combined with <option>--rotate</option> into one command. If so, all
+ active files are rotated first, and the requested vacuuming operation is executed right after. The rotation has
+ the effect that all currently active files are archived (and potentially new, empty journal files opened as
+ replacement), and hence the vacuuming operation has the greatest effect as it can take all log data written so
+ far into account.</para></listitem>
</varlistentry>
<varlistentry>
@@ -885,9 +892,12 @@
<varlistentry>
<term><option>--rotate</option></term>
- <listitem><para>Asks the journal daemon to rotate journal
- files. This call does not return until the rotation operation
- is complete.</para></listitem>
+ <listitem><para>Asks the journal daemon to rotate journal files. This call does not return until the rotation
+ operation is complete. Journal file rotation has the effect that all currently active journal files are marked
+ as archived and renamed, so that they are never written to in future. New (empty) journal files are then
+ created in their place. This operation may be combined with <option>--vacuum-size=</option>,
+ <option>--vacuum-time=</option> and <option>--vacuum-file=</option> into a single command, see
+ above.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
diff --git a/man/journald.conf.xml b/man/journald.conf.xml
index ee8e8b7faf..ed874aace9 100644
--- a/man/journald.conf.xml
+++ b/man/journald.conf.xml
@@ -140,7 +140,13 @@
following units: <literal>s</literal>, <literal>min</literal>,
<literal>h</literal>, <literal>ms</literal>,
<literal>us</literal>. To turn off any kind of rate limiting,
- set either value to 0.</para></listitem>
+ set either value to 0.</para>
+
+ <para>If a service provides rate limits for itself through
+ <varname>LogRateLimitIntervalSec=</varname> and/or <varname>LogRateLimitBurst=</varname>
+ in <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ those values will override the settings specified here.</para>
+ </listitem>
</varlistentry>
<varlistentry>
@@ -195,7 +201,11 @@
subsequently something else causes the file system to fill up,
journald will stop using more space, but it will not be
removing existing files to reduce the footprint again,
- either.</para>
+ either. Also note that only archived files are deleted to reduce the
+ space occupied by journal files. This means that, in effect, there might
+ still be more space used than <varname>SystemMaxUse=</varname> or
+ <varname>RuntimeMaxUse=</varname> limit after a vacuuming operation is
+ complete.</para>
<para><varname>SystemMaxFileSize=</varname> and
<varname>RuntimeMaxFileSize=</varname> control how large
diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml
index 0545f9d84b..9d86bdf203 100644
--- a/man/kernel-command-line.xml
+++ b/man/kernel-command-line.xml
@@ -55,6 +55,7 @@
<term><varname>systemd.unit=</varname></term>
<term><varname>rd.systemd.unit=</varname></term>
<term><varname>systemd.dump_core</varname></term>
+ <term><varname>systemd.early_core_pattern=</varname></term>
<term><varname>systemd.crash_chvt</varname></term>
<term><varname>systemd.crash_shell</varname></term>
<term><varname>systemd.crash_reboot</varname></term>
@@ -91,6 +92,27 @@
</varlistentry>
<varlistentry>
+ <term><varname>systemd.run=</varname></term>
+ <term><varname>systemd.run_success_action=</varname></term>
+ <term><varname>systemd.run_failure_action=</varname></term>
+ <listitem>
+ <para>Additional parameters understood by
+ <citerefentry><refentrytitle>systemd-run-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>, to
+ run a command line specified on the kernel command line as system service after booting up.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>systemd.early_core_pattern=</varname></term>
+ <listitem>
+ <para>During early boot, the generation of core dump files is disabled until a core dump handler (if any)
+ takes over. This parameter allows to specifies an absolute path where core dump files should be stored until
+ a handler is installed. The path should be absolute and may contain specifiers, see
+ <citerefentry><refentrytitle>core</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>systemd.restore_state=</varname></term>
<listitem>
<para>This parameter is understood by several system tools
@@ -246,6 +268,7 @@
<term><varname>udev.event_timeout=</varname></term>
<term><varname>rd.udev.event_timeout=</varname></term>
<term><varname>net.ifnames=</varname></term>
+ <term><varname>net.naming-scheme=</varname></term>
<listitem>
<para>Parameters understood by the device event managing
diff --git a/man/kernel-install.xml b/man/kernel-install.xml
index cd9756662a..83e50c8d70 100644
--- a/man/kernel-install.xml
+++ b/man/kernel-install.xml
@@ -1,11 +1,9 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
SPDX-License-Identifier: LGPL-2.1+
-
- Copyright © 2013 Harald Hoyer
-->
<refentry id="kernel-install">
@@ -65,49 +63,61 @@
<varlistentry>
<term><command>add <replaceable>KERNEL-VERSION</replaceable> <replaceable>KERNEL-IMAGE</replaceable></command></term>
<listitem>
- <para><command>kernel-install</command> creates the directory
+ <para>This command expects a kernel version string and a path to a kernel image file as
+ arguments. <command>kernel-install</command> creates the directory
<filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename>
- and calls executables from
- <filename>/usr/lib/kernel/install.d/*.install</filename> and
- <filename>/etc/kernel/install.d/*.install</filename> with
- the arguments
- <programlisting>add <replaceable>KERNEL-VERSION</replaceable> \
- <filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename> <replaceable>KERNEL-IMAGE</replaceable></programlisting>
+ and calls the executables from <filename>/usr/lib/kernel/install.d/*.install</filename> and
+ <filename>/etc/kernel/install.d/*.install</filename> with the following arguments:
+
+ <programlisting>add <replaceable>KERNEL-VERSION</replaceable> <filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename> <replaceable>KERNEL-IMAGE</replaceable></programlisting>
</para>
- <para>The kernel-install plugin <filename>50-depmod.install</filename> runs depmod for the <replaceable>KERNEL-VERSION</replaceable>.</para>
-
- <para>The kernel-install plugin
- <filename>90-loaderentry.install</filename> copies
- <replaceable>KERNEL-IMAGE</replaceable> to
- <filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/linux</filename>.
- It also creates a boot loader entry according to the boot
- loader specification in
- <filename>/boot/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.conf</filename>.
- The title of the entry is the
- <replaceable>PRETTY_NAME</replaceable> parameter specified
- in <filename>/etc/os-release</filename> or
- <filename>/usr/lib/os-release</filename> (if the former is
- missing), or "Linux
- <replaceable>KERNEL-VERSION</replaceable>", if unset. If
- the file <filename>initrd</filename> is found next to the
- <filename>linux</filename> file, the initrd will be added to
- the configuration.</para>
+ <para>Two default plugins execute the following operations in this case:</para>
+
+ <itemizedlist>
+
+ <listitem><para><filename>50-depmod.install</filename> runs
+ <citerefentry><refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum></citerefentry> for the
+ <replaceable>KERNEL-VERSION</replaceable>.</para></listitem>
+
+ <listitem><para><filename>90-loaderentry.install</filename> copies <replaceable>KERNEL-IMAGE</replaceable>
+ to
+ <filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/linux</filename>.
+ It also creates a boot loader entry according to the <ulink
+ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> in
+ <filename>/boot/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.conf</filename>.
+ The title of the entry is the <replaceable>PRETTY_NAME</replaceable> parameter specified in
+ <filename>/etc/os-release</filename> or <filename>/usr/lib/os-release</filename> (if the former is
+ missing), or "Linux <replaceable>KERNEL-VERSION</replaceable>", if unset. If the file
+ <filename>initrd</filename> is found next to the kernel image file, the initrd will be added to the
+ configuration.</para></listitem>
+ </itemizedlist>
</listitem>
</varlistentry>
<varlistentry>
<term><command>remove <replaceable>KERNEL-VERSION</replaceable></command></term>
<listitem>
- <para>Calls executables from <filename>/usr/lib/kernel/install.d/*.install</filename>
- and <filename>/etc/kernel/install.d/*.install</filename> with the arguments
+ <para>This command expects a kernel version string as single argument. This calls executables from
+ <filename>/usr/lib/kernel/install.d/*.install</filename> and
+ <filename>/etc/kernel/install.d/*.install</filename> with the following arguments:
+
<programlisting>remove <replaceable>KERNEL-VERSION</replaceable> <filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename></programlisting>
</para>
- <para><command>kernel-install</command> removes the entire directory
- <filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename> afterwards.</para>
+ <para>Afterwards, <command>kernel-install</command> removes the directory
+ <filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename>
+ and its contents.</para>
+
+ <para>Two default plugins execute the following operations in this case:</para>
+
+ <itemizedlist>
+
+ <listitem><para><filename>50-depmod.install</filename> removes the files generated by <command>depmod</command> for this kernel again.</para></listitem>
+
+ <listitem><para><filename>90-loaderentry.install</filename> removes the file
+ <filename>/boot/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.conf</filename>.</para></listitem>
+ </itemizedlist>
- <para>The kernel-install plugin <filename>90-loaderentry.install</filename> removes the file
- <filename>/boot/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.conf</filename>.</para>
</listitem>
</varlistentry>
@@ -138,8 +148,22 @@
<filename>/proc/cmdline</filename>
</term>
<listitem>
- <para>The content of the file <filename>/etc/kernel/cmdline</filename> specifies the kernel command line to use.
- If that file does not exist, <filename>/proc/cmdline</filename> is used.</para>
+ <para>Read by <filename>90-loaderentry.install</filename>. The content of the file
+ <filename>/etc/kernel/cmdline</filename> specifies the kernel command line to use. If that file does not
+ exist, <filename>/proc/cmdline</filename> is used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <filename>/etc/kernel/tries</filename>
+ </term>
+ <listitem>
+ <para>Read by <filename>90-loaderentry.install</filename>. If this file exists a numeric value is read from
+ it and the naming of the generated entry file is slightly altered to include it as
+ <filename>/boot/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>+<replaceable>TRIES</replaceable>.conf</filename>. This
+ is useful for boot loaders such as
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> which
+ implement boot attempt counting with a counter embedded in the entry file name.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -167,7 +191,9 @@
<para>
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- <ulink url="https://github.com/systemd/systemd/blob/master/doc/BOOT_LOADER_SPECIFICATION.md">Boot Loader Specification</ulink>
+ <citerefentry><refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>
</para>
</refsect1>
diff --git a/man/less-variables.xml b/man/less-variables.xml
index a3faa38997..eb332b56b0 100644
--- a/man/less-variables.xml
+++ b/man/less-variables.xml
@@ -26,7 +26,13 @@
<term><varname>$SYSTEMD_LESS</varname></term>
<listitem><para>Override the options passed to <command>less</command> (by default
- <literal>FRSXMK</literal>).</para></listitem>
+ <literal>FRSXMK</literal>).</para>
+
+ <para>If the value of <varname>$SYSTEMD_LESS</varname> does not include <literal>K</literal>,
+ and the pager that is invoked is <command>less</command>,
+ <keycombo><keycap>Ctrl</keycap><keycap>C</keycap></keycombo> will be ignored by the
+ executable. This allows <command>less</command> to handle
+ <keycombo><keycap>Ctrl</keycap><keycap>C</keycap></keycombo> itself.</para></listitem>
</varlistentry>
<varlistentry id='lesscharset'>
diff --git a/man/libudev.xml b/man/libudev.xml
index 8cb4ba59fc..382c1aa25c 100644
--- a/man/libudev.xml
+++ b/man/libudev.xml
@@ -48,11 +48,9 @@
<citerefentry><refentrytitle>udev_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
It is used to track library state and link objects together. No
global state is used by libudev, everything is always linked to
- a udev context. Furthermore, multiple different udev contexts can
- be used in parallel by multiple threads. However, a single context
- must not be accessed by multiple threads in parallel. The caller
- is responsible for providing suitable locking if they intend to use
- it from multiple threads.</para>
+ a udev context.</para>
+
+ <xi:include href="threads-aware.xml" xpointer="strict"/>
<para>To introspect a local device on a system, a udev device
object can be created via
diff --git a/man/loader.conf.xml b/man/loader.conf.xml
index 6f8d0489d2..f9d98dd4d9 100644
--- a/man/loader.conf.xml
+++ b/man/loader.conf.xml
@@ -23,7 +23,7 @@
<refsynopsisdiv>
<para><filename><replaceable>ESP</replaceable>/loader/loader.conf</filename>,
- <filename><replaceable>ESP</replaceable>/loader/loader.conf.d/*.conf</filename>
+ <filename><replaceable>ESP</replaceable>/loader/entries/*.conf</filename>
</para>
</refsynopsisdiv>
@@ -32,9 +32,9 @@
<para>
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- will read <filename>/loader/loader.conf</filename> and any files with the
+ will read <filename><replaceable>ESP</replaceable>/loader/loader.conf</filename> and any files with the
<literal>.conf</literal> extension under
- <filename>/loader/loader.conf.d/</filename> on the EFI system partition (ESP).
+ <filename><replaceable>ESP</replaceable>/loader/entries/</filename> on the EFI system partition (ESP).
</para>
<para>Each configuration file must consist of an option name, followed by
@@ -50,7 +50,7 @@
<refsect1>
<title>Options</title>
- <para>The following configuration options are understood:</para>
+ <para>The following configuration options in <filename>loader.conf</filename> are understood:</para>
<variablelist>
<varlistentry>
diff --git a/man/locale.conf.xml b/man/locale.conf.xml
index e89d0bead5..2f463de6b6 100644
--- a/man/locale.conf.xml
+++ b/man/locale.conf.xml
@@ -67,7 +67,7 @@
might be checked for locale configuration as well, however only as
fallback.</para>
- <para><filename>/etc/vconsole.conf</filename> is usually created and updated
+ <para><filename>/etc/locale.conf</filename> is usually created and updated
using
<citerefentry><refentrytitle>systemd-localed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
<citerefentry project='man-pages'><refentrytitle>localectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
diff --git a/man/logind.conf.xml b/man/logind.conf.xml
index 9e88764c6f..a407858957 100644
--- a/man/logind.conf.xml
+++ b/man/logind.conf.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
@@ -185,6 +185,17 @@
</varlistentry>
<varlistentry>
+ <term><varname>UserStopDelaySec=</varname></term>
+
+ <listitem><para>Specifies how long to keep the user record and per-user service
+ <filename>user@.service</filename> around for a user after they logged out fully. If set to zero, the per-user
+ service is terminated immediately when the last session of the user has ended. If this option is configured to
+ non-zero rapid logout/login cycles are sped up, as the user's service manager is not constantly restarted. If
+ set to <literal>infinity</literal> the per-user service for a user is never terminated again after first login,
+ and continues to run until system shutdown. Defaults to 10s.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>HandlePowerKey=</varname></term>
<term><varname>HandleSuspendKey=</varname></term>
<term><varname>HandleHibernateKey=</varname></term>
diff --git a/man/machinectl.xml b/man/machinectl.xml
index affca1dec1..95823eb413 100644
--- a/man/machinectl.xml
+++ b/man/machinectl.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -70,11 +70,12 @@
top-level directories <filename>/usr</filename>,
<filename>/etc</filename>, and so on.</para></listitem>
- <listitem><para>btrfs subvolumes containing OS trees, similar to
- normal directory trees.</para></listitem>
+ <listitem><para>btrfs subvolumes containing OS trees, similar to regular directory trees.</para></listitem>
- <listitem><para>Binary "raw" disk images containing MBR or GPT
- partition tables and Linux file system partitions.</para></listitem>
+ <listitem><para>Binary "raw" disk image files containing MBR or GPT partition tables and Linux file
+ systems.</para></listitem>
+
+ <listitem><para>Similarly, block devices containing MBR or GPT partition tables and file systems.</para></listitem>
<listitem><para>The file system tree of the host OS itself.</para></listitem>
</itemizedlist>
@@ -649,22 +650,7 @@
units. If the size limit shall be disabled, specify
<literal>-</literal> as size.</para>
- <para>Note that per-container size limits are only supported
- on btrfs file systems. Also note that, if
- <command>set-limit</command> is invoked without an image
- parameter, and <filename>/var/lib/machines</filename> is
- empty, and the directory is not located on btrfs, a btrfs
- loopback file is implicitly created as
- <filename>/var/lib/machines.raw</filename> with the given
- size, and mounted to
- <filename>/var/lib/machines</filename>. The size of the
- loopback may later be readjusted with
- <command>set-limit</command>, as well. If such a
- loopback-mounted <filename>/var/lib/machines</filename>
- directory is used, <command>set-limit</command> without an image
- name alters both the quota setting within the file system as
- well as the loopback file and file system size
- itself.</para></listitem>
+ <para>Note that per-container size limits are only supported on btrfs file systems.</para></listitem>
</varlistentry>
<varlistentry>
@@ -802,12 +788,8 @@
image is read from standard input, in which case the second
argument is mandatory.</para>
- <para>Both <command>pull-tar</command> and <command>pull-raw</command>
- will resize <filename>/var/lib/machines.raw</filename> and the
- filesystem therein as necessary. Optionally, the
- <option>--read-only</option> switch may be used to create a
- read-only container or VM image. No cryptographic validation
- is done when importing the images.</para>
+ <para>Optionally, the <option>--read-only</option> switch may be used to create a read-only container or VM
+ image. No cryptographic validation is done when importing the images.</para>
<para>Much like image downloads, ongoing imports may be listed
with <command>list-transfers</command> and aborted with
@@ -815,6 +797,15 @@
</varlistentry>
<varlistentry>
+ <term><command>import-fs</command> <replaceable>DIRECTORY</replaceable> [<replaceable>NAME</replaceable>]</term>
+
+ <listitem><para>Imports a container image stored in a local directory into
+ <filename>/var/lib/machines/</filename>, operates similar to <command>import-tar</command> or
+ <command>import-raw</command>, but the first argument is the source directory. If supported, this command will
+ create btrfs snapshot or subvolume for the new image.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><command>export-tar</command> <replaceable>NAME</replaceable> [<replaceable>FILE</replaceable>]</term>
<term><command>export-raw</command> <replaceable>NAME</replaceable> [<replaceable>FILE</replaceable>]</term>
<listitem><para>Exports a TAR or RAW container or VM image and
@@ -910,18 +901,7 @@
<filename>/var/lib/machines/</filename> to make them available for
control with <command>machinectl</command>.</para>
- <para>Note that some image operations are only supported,
- efficient or atomic on btrfs file systems. Due to this, if the
- <command>pull-tar</command>, <command>pull-raw</command>,
- <command>import-tar</command>, <command>import-raw</command> and
- <command>set-limit</command> commands notice that
- <filename>/var/lib/machines</filename> is empty and not located on
- btrfs, they will implicitly set up a loopback file
- <filename>/var/lib/machines.raw</filename> containing a btrfs file
- system that is mounted to
- <filename>/var/lib/machines</filename>. The size of this loopback
- file may be controlled dynamically with
- <command>set-limit</command>.</para>
+ <para>Note that some image operations are only supported, efficient or atomic on btrfs file systems.</para>
<para>Disk images are understood by
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
diff --git a/man/meson.build b/man/meson.build
index ec05d73bc6..05197d6ef4 100644
--- a/man/meson.build
+++ b/man/meson.build
@@ -47,8 +47,8 @@ foreach tuple : xsltproc.found() ? manpages : []
manaliases = []
htmlaliases = []
foreach alias : aliases
- manaliases += [alias + '.' + section]
- htmlaliases += [alias + '.html']
+ manaliases += alias + '.' + section
+ htmlaliases += alias + '.html'
endforeach
mandirn = join_paths(get_option('mandir'), 'man' + section)
@@ -62,7 +62,7 @@ foreach tuple : xsltproc.found() ? manpages : []
depend_files : custom_entities_ent,
install : want_man,
install_dir : mandirn)
- man_pages += [p1]
+ man_pages += p1
p2 = []
foreach htmlalias : htmlaliases
@@ -75,9 +75,9 @@ foreach tuple : xsltproc.found() ? manpages : []
dst = join_paths(docdir, 'html', htmlalias)
cmd = 'ln -fs @0@ $DESTDIR@1@'.format(html, dst)
meson.add_install_script('sh', '-c', cmd)
- p2 += [link]
+ p2 += link
endif
- html_pages += [link]
+ html_pages += link
endforeach
p3 = custom_target(
@@ -89,7 +89,7 @@ foreach tuple : xsltproc.found() ? manpages : []
depends : p2,
install : want_html,
install_dir : join_paths(docdir, 'html'))
- html_pages += [p3]
+ html_pages += p3
source_xml_files += files(tuple[0] + '.xml')
else
@@ -117,8 +117,8 @@ systemd_index_xml = custom_target(
output : 'systemd.index.xml',
command : [make_man_index_py, '@OUTPUT@'] + nonindex_xml_files)
-foreach tuple : want_man or want_html ? [['systemd.directives', '7', systemd_directives_xml],
- ['systemd.index', '7', systemd_index_xml]] : []
+foreach tuple : xsltproc.found() ? [['systemd.directives', '7', systemd_directives_xml],
+ ['systemd.index', '7', systemd_index_xml]] : []
stem = tuple[0]
section = tuple[1]
xml = tuple[2]
@@ -135,7 +135,7 @@ foreach tuple : want_man or want_html ? [['systemd.directives', '7', systemd_dir
command : xslt_cmd + [custom_man_xsl, '@INPUT@'],
install : want_man and have_lxml,
install_dir : mandirn)
- man_pages += [p1]
+ man_pages += p1
p2 = []
if html == 'systemd.index.html'
@@ -149,9 +149,9 @@ foreach tuple : want_man or want_html ? [['systemd.directives', '7', systemd_dir
dst = join_paths(docdir, 'html', htmlalias)
cmd = 'ln -fs @0@ $DESTDIR@1@'.format(html, dst)
meson.add_install_script('sh', '-c', cmd)
- p2 += [link]
+ p2 += link
endif
- html_pages += [link]
+ html_pages += link
endif
p3 = custom_target(
@@ -163,10 +163,11 @@ foreach tuple : want_man or want_html ? [['systemd.directives', '7', systemd_dir
depends : p2,
install : want_html and have_lxml,
install_dir : join_paths(docdir, 'html'))
- html_pages += [p3]
+ html_pages += p3
endforeach
-# cannot use run_target until https://github.com/mesonbuild/meson/issues/1644 is resolved
+# Cannot use run_target because those targets are used in depends
+# Also see https://github.com/mesonbuild/meson/issues/368.
man = custom_target(
'man',
output : 'man',
diff --git a/man/networkctl.xml b/man/networkctl.xml
index 8c750cc1b1..7877755edf 100644
--- a/man/networkctl.xml
+++ b/man/networkctl.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -92,6 +92,88 @@
4 virbr0-nic ether off unmanaged
4 links listed.</programlisting></para>
+
+ <para>The operational status is one of the following:
+ <variablelist>
+ <varlistentry>
+ <term>off</term>
+ <listitem>
+ <para>the device is powered down</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>no-carrier</term>
+ <listitem>
+ <para>the device is powered up, but it does not yet have a carrier</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>dormant</term>
+ <listitem>
+ <para>the device has a carrier, but is not yet ready for normal traffic</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>carrier</term>
+ <listitem>
+ <para>the link has a carrier</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>degraded</term>
+ <listitem>
+ <para>the link has carrier and addresses valid on the local link configured</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>routable</term>
+ <listitem>
+ <para>the link has carrier and routable address configured</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>The setup status is one of the following:
+ <variablelist>
+ <varlistentry>
+ <term>pending</term>
+ <listitem>
+ <para>udev is still processing the link, we don't yet know if we will manage it</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>failed</term>
+ <listitem>
+ <para>networkd failed to manage the link</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>configuring</term>
+ <listitem>
+ <para>in the process of retrieving configuration or configuring the link</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>configured</term>
+ <listitem>
+ <para>link configured successfully</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>unmanaged</term>
+ <listitem>
+ <para>networkd is not handling the link</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>linger</term>
+ <listitem>
+ <para>the link is gone, but has not yet been dropped by networkd</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
</listitem>
</varlistentry>
diff --git a/man/networkd.conf.xml b/man/networkd.conf.xml
index b34ce1d3e0..c624d4de43 100644
--- a/man/networkd.conf.xml
+++ b/man/networkd.conf.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -67,7 +67,7 @@
<para>The following values are understood:
<variablelist>
<varlistentry>
- <term><option>vendor</option> </term>
+ <term><option>vendor</option></term>
<listitem><para>If <literal>DUIDType=vendor</literal>, then the DUID value will be generated using
<literal>43793</literal> as the vendor identifier (systemd) and hashed contents of
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
@@ -76,11 +76,23 @@
</varlistentry>
<varlistentry>
- <term><option>link-layer-time</option> </term>
- <term><option>link-layer</option> </term>
- <term><option>uuid</option> </term>
- <listitem><para>Those values are parsed and can be used to set the DUID type
- field, but DUID contents must be provided using <varname>DUIDRawData=</varname>.
+ <term><option>uuid</option></term>
+ <listitem><para>If <literal>DUIDType=uuid</literal>, and <varname>DUIDRawData=</varname> is not set,
+ then the product UUID is used as a DUID value. If a system does not have valid product UUID, then
+ an application-specific
+ <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ is used as a DUID value. About the application-specific machine ID, see
+ <citerefentry><refentrytitle>sd_id128_get_machine_app_specific</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>link-layer-time[:<replaceable>TIME</replaceable>]</option></term>
+ <term><option>link-layer</option></term>
+ <listitem><para>If <literal>link-layer-time</literal> or <literal>link-layer</literal> is specified,
+ then the MAC address of the interface is used as a DUID value. The value <literal>link-layer-time</literal>
+ can take additional time value after a colon, e.g. <literal>link-layer-time:2018-01-23 12:34:56 UTC</literal>.
+ The default time value is <literal>2000-01-01 00:00:00 UTC</literal>.
</para></listitem>
</varlistentry>
</variablelist>
@@ -96,8 +108,9 @@
byte separated by <literal>:</literal>. The DUID that is sent is composed of the DUID type specified by
<varname>DUIDType=</varname> and the value configured here.</para>
- <para>The DUID value specified here overrides the DUID that systemd-networkd generates using the machine-id
- from the <filename>/etc/machine-id</filename> file. To configure DUID per-network, see
+ <para>The DUID value specified here overrides the DUID that
+ <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ generates from the machine ID. To configure DUID per-network, see
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
The configured DHCP DUID should conform to the specification in
<ulink url="http://tools.ietf.org/html/rfc3315#section-9">RFC 3315</ulink>,
@@ -125,7 +138,9 @@ DUIDRawData=00:00:ab:11:f9:2a:c2:77:29:f9:5c:00</programlisting>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_id128_get_machine_app_specific</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/nss-myhostname.xml b/man/nss-myhostname.xml
index e1aabacad2..e447420f53 100644
--- a/man/nss-myhostname.xml
+++ b/man/nss-myhostname.xml
@@ -6,7 +6,7 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="nss-myhostname" conditional='ENABLE_MYHOSTNAME'>
+<refentry id="nss-myhostname" conditional='ENABLE_NSS_MYHOSTNAME'>
<refentryinfo>
<title>nss-myhostname</title>
@@ -81,6 +81,7 @@
<para>Here is an example <filename>/etc/nsswitch.conf</filename> file that enables
<command>nss-myhostname</command> correctly:</para>
+ <!-- synchronize with other nss-* man pages and factory/etc/nsswitch.conf -->
<programlisting>passwd: compat mymachines systemd
group: compat mymachines systemd
shadow: compat
diff --git a/man/nss-mymachines.xml b/man/nss-mymachines.xml
index 394a905665..5742d89779 100644
--- a/man/nss-mymachines.xml
+++ b/man/nss-mymachines.xml
@@ -6,7 +6,7 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="nss-mymachines" conditional='ENABLE_MACHINED'>
+<refentry id="nss-mymachines" conditional='ENABLE_NSS_MYMACHINES'>
<refentryinfo>
<title>nss-mymachines</title>
@@ -35,12 +35,21 @@
<para><command>nss-mymachines</command> is a plug-in module for the GNU Name Service Switch (NSS) functionality of
the GNU C Library (<command>glibc</command>), providing hostname resolution for the names of containers running
locally that are registered with
- <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. The
+ <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. The
container names are resolved to the IP addresses of the specific container, ordered by their scope. This
- functionality only applies to containers using network namespacing.</para>
-
- <para>The module also resolves user and group IDs used by containers to user and group names indicating the
- container name, and back. This functionality only applies to containers using user namespacing.</para>
+ functionality only applies to containers using network namespacing (see the description of
+ <option>--private-network</option> in
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>).
+ Note that the name that is resolved is the one registered with <command>systemd-machined</command>, which
+ may be different than the hostname configured inside of the container.</para>
+
+ <para>The module also provides name resolution for user and group identifiers mapped to containers. All names from
+ the range allocated to a given container <replaceable>container</replaceable> are exposed on the host as
+ <literal>vu-<replaceable>container</replaceable>-<replaceable>uid</replaceable></literal> and
+ <literal>vg-<replaceable>container</replaceable>-<replaceable>gid</replaceable></literal> (see example below). This
+ functionality only applies to containers using user namespacing (see the description of
+ <option>--private-users</option> in
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>).</para>
<para>To activate the NSS module, add <literal>mymachines</literal> to the lines starting with
<literal>hosts:</literal>, <literal>passwd:</literal> and <literal>group:</literal> in
@@ -53,11 +62,12 @@
</refsect1>
<refsect1>
- <title>Example</title>
+ <title>Configuration in <filename>/etc/nsswitch.conf</filename></title>
<para>Here is an example <filename>/etc/nsswitch.conf</filename> file that enables
<command>nss-mymachines</command> correctly:</para>
+ <!-- synchronize with other nss-* man pages and factory/etc/nsswitch.conf -->
<programlisting>passwd: compat <command>mymachines</command> systemd
group: compat <command>mymachines</command> systemd
shadow: compat
@@ -75,10 +85,73 @@ netgroup: nis</programlisting>
</refsect1>
<refsect1>
+ <title>Mappings provided by <filename>nss-mymachines</filename></title>
+
+ <para>The container <literal>rawhide</literal> is spawned using
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>:
+ </para>
+
+ <programlisting># systemd-nspawn -M rawhide --boot --network-veth --private-users=pick
+Spawning container rawhide on /var/lib/machines/rawhide.
+Selected user namespace base 20119552 and range 65536.
+...
+
+$ machinectl --max-addresses=3
+MACHINE CLASS SERVICE OS VERSION ADDRESSES
+rawhide container systemd-nspawn fedora 30 169.254.40.164 fe80::94aa:3aff:fe7b:d4b9
+
+$ getent passwd vu-rawhide-0 vu-rawhide-81
+vu-rawhide-0:*:20119552:65534:vu-rawhide-0:/:/sbin/nologin
+vu-rawhide-81:*:20119633:65534:vu-rawhide-81:/:/sbin/nologin
+
+$ getent group vg-rawhide-0 vg-rawhide-81
+vg-rawhide-0:*:20119552:
+vg-rawhide-81:*:20119633:
+
+$ ps -o user:15,pid,tty,command -e|grep '^vu-rawhide'
+vu-rawhide-0 692 ? /usr/lib/systemd/systemd
+vu-rawhide-0 731 ? /usr/lib/systemd/systemd-journald
+vu-rawhide-192 734 ? /usr/lib/systemd/systemd-networkd
+vu-rawhide-193 738 ? /usr/lib/systemd/systemd-resolved
+vu-rawhide-0 742 ? /usr/lib/systemd/systemd-logind
+vu-rawhide-81 744 ? /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
+vu-rawhide-0 746 ? /usr/sbin/sshd -D ...
+vu-rawhide-0 752 ? /usr/lib/systemd/systemd --user
+vu-rawhide-0 753 ? (sd-pam)
+vu-rawhide-0 1628 ? login -- zbyszek
+vu-rawhide-1000 1630 ? /usr/lib/systemd/systemd --user
+vu-rawhide-1000 1631 ? (sd-pam)
+vu-rawhide-1000 1637 pts/8 -zsh
+
+$ ping -c1 rawhide
+PING rawhide(fe80::94aa:3aff:fe7b:d4b9%ve-rawhide (fe80::94aa:3aff:fe7b:d4b9%ve-rawhide)) 56 data bytes
+64 bytes from fe80::94aa:3aff:fe7b:d4b9%ve-rawhide (fe80::94aa:3aff:fe7b:d4b9%ve-rawhide): icmp_seq=1 ttl=64 time=0.045 ms
+...
+$ ping -c1 -4 rawhide
+PING rawhide (169.254.40.164) 56(84) bytes of data.
+64 bytes from 169.254.40.164 (169.254.40.164): icmp_seq=1 ttl=64 time=0.064 ms
+...
+
+# machinectl shell rawhide /sbin/ip a
+Connected to machine rawhide. Press ^] three times within 1s to exit session.
+1: lo: &lt;LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
+ ...
+2: host0@if21: &lt;BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
+ link/ether 96:aa:3a:7b:d4:b9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
+ inet 169.254.40.164/16 brd 169.254.255.255 scope link host0
+ valid_lft forever preferred_lft forever
+ inet6 fe80::94aa:3aff:fe7b:d4b9/64 scope link
+ valid_lft forever preferred_lft forever
+Connection to machine rawhide terminated.
+</programlisting>
+ </refsect1>
+
+ <refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>nss-systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>nss-myhostname</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
diff --git a/man/nss-resolve.xml b/man/nss-resolve.xml
index 588bc04976..960d580003 100644
--- a/man/nss-resolve.xml
+++ b/man/nss-resolve.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -6,7 +6,7 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="nss-resolve" conditional='ENABLE_RESOLVE'>
+<refentry id="nss-resolve" conditional='ENABLE_NSS_RESOLVE'>
<refentryinfo>
<title>nss-resolve</title>
@@ -64,6 +64,7 @@
<para>Here is an example <filename>/etc/nsswitch.conf</filename> file that enables <command>nss-resolve</command>
correctly:</para>
+ <!-- synchronize with other nss-* man pages and factory/etc/nsswitch.conf -->
<programlisting>passwd: compat mymachines systemd
group: compat mymachines systemd
shadow: compat
diff --git a/man/nss-systemd.xml b/man/nss-systemd.xml
index 323f9ab868..d3d68437de 100644
--- a/man/nss-systemd.xml
+++ b/man/nss-systemd.xml
@@ -55,6 +55,7 @@
<para>Here is an example <filename>/etc/nsswitch.conf</filename> file that enables
<command>nss-systemd</command> correctly:</para>
+ <!-- synchronize with other nss-* man pages and factory/etc/nsswitch.conf -->
<programlisting>passwd: compat mymachines <command>systemd</command>
group: compat mymachines <command>systemd</command>
shadow: compat
diff --git a/man/os-release.xml b/man/os-release.xml
index a51edf3b8a..ea71b36c1e 100644
--- a/man/os-release.xml
+++ b/man/os-release.xml
@@ -218,14 +218,18 @@
<varlistentry>
<term><varname>HOME_URL=</varname></term>
+ <term><varname>DOCUMENTATION_URL=</varname></term>
<term><varname>SUPPORT_URL=</varname></term>
<term><varname>BUG_REPORT_URL=</varname></term>
<term><varname>PRIVACY_POLICY_URL=</varname></term>
- <listitem><para>Links to resources on the Internet related the
- operating system. <varname>HOME_URL=</varname> should refer to
- the homepage of the operating system, or alternatively some
- homepage of the specific version of the operating system.
+ <listitem><para>Links to resources on the Internet related to
+ the operating system.
+ <varname>HOME_URL=</varname> should refer to the homepage of
+ the operating system, or alternatively some homepage of the
+ specific version of the operating system.
+ <varname>DOCUMENTATION_URL=</varname> should refer to the main
+ documentation page for this operating system.
<varname>SUPPORT_URL=</varname> should refer to the main
support page for the operating system, if there is any. This
is primarily intended for operating systems which vendors
@@ -234,7 +238,7 @@
if there is any. This is primarily intended for operating
systems that rely on community QA.
<varname>PRIVACY_POLICY_URL=</varname> should refer to the
- main privacy policy page for the operation system, if there is
+ main privacy policy page for the operating system, if there is
any. These settings are optional, and providing only some of
these settings is common. These URLs are intended to be
exposed in "About this system" UIs behind links with captions
@@ -302,6 +306,22 @@
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>LOGO=</varname></term>
+
+ <listitem><para>
+ A string, specifying the name of an icon as defined by <ulink
+ url="http://standards.freedesktop.org/icon-theme-spec/latest">
+ freedesktop.org Icon Theme Specification</ulink>. This can be
+ used by graphical applications to display an operating
+ system's or distributor's logo. This field is optional and
+ may not necessarily be implemented on all systems.
+ Examples:
+ <literal>LOGO=fedora-logo</literal>,
+ <literal>LOGO=distributor-logo-opensuse</literal>
+ </para></listitem>
+ </varlistentry>
+
</variablelist>
<para>If you are reading this file from C code or a shell script
diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml
index 5eab995a52..3ce3b282bd 100644
--- a/man/pam_systemd.xml
+++ b/man/pam_systemd.xml
@@ -84,40 +84,43 @@
<varlistentry>
<term><option>class=</option></term>
- <listitem><para>Takes a string argument which sets the session
- class. The XDG_SESSION_CLASS environmental variable takes
- precedence. One of
- <literal>user</literal>,
- <literal>greeter</literal>,
- <literal>lock-screen</literal> or
- <literal>background</literal>. See
- <citerefentry><refentrytitle>sd_session_get_class</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- for details about the session class.</para></listitem>
+ <listitem><para>Takes a string argument which sets the session class. The <varname>XDG_SESSION_CLASS</varname>
+ environment variable (see below) takes precedence. One of <literal>user</literal>, <literal>greeter</literal>,
+ <literal>lock-screen</literal> or <literal>background</literal>. See
+ <citerefentry><refentrytitle>sd_session_get_class</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+ details about the session class.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>type=</option></term>
- <listitem><para>Takes a string argument which sets the session
- type. The XDG_SESSION_TYPE environmental variable takes
- precedence. One of
- <literal>unspecified</literal>,
- <literal>tty</literal>,
- <literal>x11</literal>,
- <literal>wayland</literal> or
- <literal>mir</literal>. See
- <citerefentry><refentrytitle>sd_session_get_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- for details about the session type.</para></listitem>
+ <listitem><para>Takes a string argument which sets the session type. The <varname>XDG_SESSION_TYPE</varname>
+ environment variable (see below) takes precedence. One of <literal>unspecified</literal>,
+ <literal>tty</literal>, <literal>x11</literal>, <literal>wayland</literal> or <literal>mir</literal>. See
+ <citerefentry><refentrytitle>sd_session_get_type</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+ details about the session type.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>desktop=</option></term>
+
+ <listitem><para>Takes a single, short identifier string for the desktop environment. The
+ <varname>XDG_SESSION_DESKTOP</varname> environment variable (see below) takes precedence. This may be used to
+ indicate the session desktop used, where this applies and if this information is available. For example:
+ <literal>GNOME</literal>, or <literal>KDE</literal>. It is recommended to use the same identifiers and
+ capitalization as for <varname>$XDG_CURRENT_DESKTOP</varname>, as defined by the <ulink
+ url="http://standards.freedesktop.org/desktop-entry-spec/latest/">Desktop Entry
+ Specification</ulink>. (However, note that the option only takes a single item, and not a colon-separated list
+ like <varname>$XDG_CURRENT_DESKTOP</varname>.) See
+ <citerefentry><refentrytitle>sd_session_get_desktop</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+ further details.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>debug<optional>=</optional></option></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>
+ <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>
@@ -131,20 +134,20 @@
<refsect1>
<title>Environment</title>
- <para>The following environment variables are set for the
- processes of the user's session:</para>
+ <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>$XDG_SESSION_ID</varname></term>
- <listitem><para>A session identifier, suitable to be used in
- filenames. The string itself should be considered opaque,
- although often it is just the audit session ID as reported by
- <filename>/proc/self/sessionid</filename>. Each ID will be
- assigned only once during machine uptime. It may hence be used
- to uniquely label files or other resources of this
- session.</para></listitem>
+ <listitem><para>A short session identifier, suitable to be used in filenames. The string itself should be
+ considered opaque, although often it is just the audit session ID as reported by
+ <filename>/proc/self/sessionid</filename>. Each ID will be assigned only once during machine uptime. It may
+ hence be used to uniquely label files or other resources of this session. Combine this ID with the boot
+ identifier, as returned by
+ <citerefentry><refentrytitle>sd_id128_get_boot</refentrytitle><manvolnum>3</manvolnum></citerefentry>, for a
+ globally unique identifier for the current session.</para></listitem>
</varlistentry>
<varlistentry>
@@ -174,45 +177,31 @@
</variablelist>
- <para>The following environment variables are read by the module
- and may be used by the PAM service to pass metadata to the
- module:</para>
+ <para>The following environment variables are read by the module and may be used by the PAM service to pass
+ metadata to the module. If these variables are not set when the PAM module is invoked but can be determined
+ otherwise they are set by the module, so that these variables are initialized for the session and applications if
+ known at all.</para>
<variablelist class='environment-variables'>
<varlistentry>
<term><varname>$XDG_SESSION_TYPE</varname></term>
- <listitem><para>The session type. This may be used instead of
- <option>session=</option> on the module parameter line, and is
- usually preferred.</para></listitem>
+ <listitem><para>The session type. This may be used instead of <option>session=</option> on the module parameter
+ line, and is usually preferred.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>$XDG_SESSION_CLASS</varname></term>
- <listitem><para>The session class. This may be used instead of
- <option>class=</option> on the module parameter line, and is
- usually preferred.</para></listitem>
+ <listitem><para>The session class. This may be used instead of <option>class=</option> on the module parameter
+ line, and is usually preferred.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>$XDG_SESSION_DESKTOP</varname></term>
- <listitem><para>A single, short identifier string for the
- desktop environment. This may be used to indicate the session
- desktop used, where this applies and if this information is
- available. For example: <literal>GNOME</literal>, or
- <literal>KDE</literal>. It is recommended to use the same
- identifiers and capitalization as for
- <varname>$XDG_CURRENT_DESKTOP</varname>, as defined by the
- <ulink
- url="http://standards.freedesktop.org/desktop-entry-spec/latest/">Desktop
- Entry Specification</ulink>. (However, note that
- <varname>$XDG_SESSION_DESKTOP</varname> only takes a single
- item, and not a colon-separated list like
- <varname>$XDG_CURRENT_DESKTOP</varname>.) See
- <citerefentry><refentrytitle>sd_session_get_desktop</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- for more details.</para></listitem>
+ <listitem><para>The desktop identifier. This may be used instead of <option>desktop=</option> on the module
+ parameter line, and is usually preferred.</para></listitem>
</varlistentry>
<varlistentry>
@@ -231,9 +220,9 @@
</varlistentry>
</variablelist>
- <para>If not set, <command>pam_systemd</command> will determine the
- values for <varname>$XDG_SEAT</varname> and <varname>$XDG_VTNR</varname>
- based on the <varname>$DISPLAY</varname> variable.</para>
+ <para>If not set, <command>pam_systemd</command> will initialize
+ <varname>$XDG_SEAT</varname> and <varname>$XDG_VTNR</varname>
+ based on the <varname>$DISPLAY</varname> variable (if the latter is set).</para>
</refsect1>
<refsect1>
diff --git a/man/portablectl.xml b/man/portablectl.xml
index 0926991cbe..3a5517a4ad 100644
--- a/man/portablectl.xml
+++ b/man/portablectl.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -59,7 +59,7 @@
<listitem><para>btrfs subvolumes containing OS trees, similar to normal directory trees.</para></listitem>
<listitem><para>Binary "raw" disk images containing MBR or GPT partition tables and Linux file system
- partitions.</para></listitem>
+ partitions. (These must be regular files, with the <filename>.raw</filename> suffix.)</para></listitem>
</itemizedlist>
</refsect1>
@@ -101,9 +101,9 @@
<term><option>--runtime</option></term>
<listitem><para>When specified the unit and drop-in files are placed in
- <filename>/run/systemd/system/</filename> instead of <filename>/etc/systemd/system/</filename>. Images attached
- with this option set hence remain attached only until the next reboot, while they are normally attached
- persistently.</para></listitem>
+ <filename>/run/systemd/system.attached/</filename> instead of
+ <filename>/etc/systemd/system.attached/</filename>. Images attached with this option set hence remain attached
+ only until the next reboot, while they are normally attached persistently.</para></listitem>
</varlistentry>
<varlistentry>
@@ -167,8 +167,10 @@
<listitem><para>All unit files of types <filename>.service</filename>, <filename>.socket</filename>,
<filename>.target</filename>, <filename>.timer</filename> and <filename>.path</filename> which match the
indicated unit file name prefix are copied from the image to the host's
- <filename>/etc/systemd/system/</filename> directory (or <filename>/run/systemd/system/</filename> — depending
- whether <option>--runtime</option> is specified, see above).</para></listitem>
+ <filename>/etc/systemd/system.attached/</filename> directory (or
+ <filename>/run/systemd/system.attached/</filename> — depending whether <option>--runtime</option> is
+ specified, see above), which is included in the built-in unit search path of the system service
+ manager.</para></listitem>
<listitem><para>For unit files of type <filename>.service</filename> a drop-in is added to these copies that
adds <varname>RootDirectory=</varname> or <varname>RootImage=</varname> settings (see
@@ -330,6 +332,10 @@
to place image files directly in <filename>/etc/portables/</filename> or
<filename>/run/systemd/portables/</filename> (as these are generally not suitable for storing large or non-textual
data), but use these directories only for linking images located elsewhere into the image search path.</para>
+
+ <para>When a portable service image is attached, matching unit files are copied onto the host into the
+ <filename>/etc/systemd/system.attached/</filename> and <filename>/run/systemd/system.attached/</filename>
+ directories. When an image is detached, the unit files are removed again from these directories.</para>
</refsect1>
<refsect1>
diff --git a/man/print-unit-path.c b/man/print-unit-path.c
new file mode 100644
index 0000000000..23b58a26e2
--- /dev/null
+++ b/man/print-unit-path.c
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <systemd/sd-bus.h>
+#define _cleanup_(f) __attribute__((cleanup(f)))
+
+/* This is equivalent to:
+ * busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 \
+ * org.freedesktop.systemd1.Manager GetUnitByPID $$
+ *
+ * Compile with 'cc -lsystemd print-unit-path.c'
+ */
+
+#define DESTINATION "org.freedesktop.systemd1"
+#define PATH "/org/freedesktop/systemd1"
+#define INTERFACE "org.freedesktop.systemd1.Manager"
+#define MEMBER "GetUnitByPID"
+
+static int log_error(int error, const char *message) {
+ fprintf(stderr, "%s: %s\n", message, strerror(-error));
+ return error;
+}
+
+static int print_unit_path(sd_bus *bus) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ int r;
+
+ r = sd_bus_message_new_method_call(bus, &m,
+ DESTINATION, PATH, INTERFACE, MEMBER);
+ if (r < 0)
+ return log_error(r, "Failed to create bus message");
+
+ r = sd_bus_message_append(m, "u", (unsigned) getpid());
+ if (r < 0)
+ return log_error(r, "Failed to append to bus message");
+
+ r = sd_bus_call(bus, m, -1, &error, &reply);
+ if (r < 0)
+ return log_error(r, "Call failed");
+
+ const char *ans;
+ r = sd_bus_message_read(reply, "o", &ans);
+ if (r < 0)
+ return log_error(r, "Failed to read reply");
+
+ printf("Unit path is \"%s\".\n", ans);
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r;
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_error(r, "Failed to acquire bus");
+
+ print_unit_path(bus);
+}
diff --git a/man/resolvectl.xml b/man/resolvectl.xml
index ff5b8ad101..defd592aa9 100644
--- a/man/resolvectl.xml
+++ b/man/resolvectl.xml
@@ -241,26 +241,35 @@
<varlistentry>
<term><option>dns [<replaceable>LINK</replaceable> [<replaceable>SERVER</replaceable>…]]</option></term>
<term><option>domain [<replaceable>LINK</replaceable> [<replaceable>DOMAIN</replaceable>…]]</option></term>
+ <term><option>default-route [<replaceable>LINK</replaceable> [<replaceable>BOOL</replaceable>…]]</option></term>
<term><option>llmnr [<replaceable>LINK</replaceable> [<replaceable>MODE</replaceable>]]</option></term>
<term><option>mdns [<replaceable>LINK</replaceable> [<replaceable>MODE</replaceable>]]</option></term>
<term><option>dnssec [<replaceable>LINK</replaceable> [<replaceable>MODE</replaceable>]]</option></term>
<term><option>dnsovertls [<replaceable>LINK</replaceable> [<replaceable>MODE</replaceable>]]</option></term>
<term><option>nta [<replaceable>LINK</replaceable> [<replaceable>DOMAIN</replaceable>…]]</option></term>
- <listitem><para>Get/set per-interface DNS configuration. These commands may be used to configure various DNS
- settings for network interfaces that aren't managed by
- <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. (These
- commands will fail when used on interfaces that are managed by <command>systemd-networkd</command>, please
- configure their DNS settings directly inside the <filename>.network</filename> files instead.) These commands
- may be used to inform <command>systemd-resolved</command> about per-interface DNS configuration determined
- through external means. The <option>dns</option> command expects IPv4 or IPv6 address specifications of DNS
- servers to use. The <option>domain</option> command expects valid DNS domains, possibly prefixed with
- <literal>~</literal>, and configures a per-interface search or route-only domain. The <option>llmnr</option>,
- <option>mdns</option>, <option>dnssec</option> and <option>dnsovertls</option> commands may be used to configure
- the per-interface LLMNR, MulticastDNS, DNSSEC and DNSOverTLS settings. Finally, <option>nta</option> command
- may be used to configure additional per-interface DNSSEC NTA domains. For details about these settings, their
- possible values and their effect, see the corresponding options in
- <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+ <listitem>
+ <para>Get/set per-interface DNS configuration. These commands may be used to configure various DNS settings
+ for network interfaces that aren't managed by
+ <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. (These
+ commands will fail when used on interfaces that are managed by <command>systemd-networkd</command>, please
+ configure their DNS settings directly inside the <filename>.network</filename> files instead.) These commands
+ may be used to inform <command>systemd-resolved</command> about per-interface DNS configuration determined
+ through external means. The <option>dns</option> command expects IPv4 or IPv6 address specifications of DNS
+ servers to use. The <option>domain</option> command expects valid DNS domains, possibly prefixed with
+ <literal>~</literal>, and configures a per-interface search or route-only domain. The
+ <option>default-route</option> command expects a boolean paremeter, and configures whether the link may be
+ used as default route for DNS lookups, i.e. if it is suitable for lookups on domains no other link explicitly
+ is configured for. The <option>llmnr</option>, <option>mdns</option>, <option>dnssec</option> and
+ <option>dnsovertls</option> commands may be used to configure the per-interface LLMNR, MulticastDNS, DNSSEC
+ and DNSOverTLS settings. Finally, <option>nta</option> command may be used to configure additional
+ per-interface DNSSEC NTA domains.</para>
+
+ <para>Options <option>dns</option>, <option>domain</option> and <option>nta</option> can take
+ a single empty string argument to clear their respective value lists.</para>
+
+ <para>For details about these settings, their possible values and their effect, see the corresponding options in
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
@@ -269,9 +278,10 @@
<listitem><para>Revert the per-interface DNS configuration. If the DNS configuration is reverted all
per-interface DNS setting are reset to their defaults, undoing all effects of <option>dns</option>,
- <option>domain</option>, <option>llmnr</option>, <option>mdns</option>, <option>dnssec</option>,
- <option>dnsovertls</option>, <option>nta</option>. Note that when a network interface disappears all
- configuration is lost automatically, an explicit reverting is not necessary in that case.</para></listitem>
+ <option>domain</option>, <option>default-route</option>, <option>llmnr</option>, <option>mdns</option>,
+ <option>dnssec</option>, <option>dnsovertls</option>, <option>nta</option>. Note that when a network interface
+ disappears all configuration is lost automatically, an explicit reverting is not necessary in that
+ case.</para></listitem>
</varlistentry>
</variablelist>
diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml
index c36c303abc..d37bf0d3ad 100644
--- a/man/resolved.conf.xml
+++ b/man/resolved.conf.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -91,7 +91,7 @@
<listitem><para>Takes a boolean argument or
<literal>resolve</literal>. Controls Link-Local Multicast Name
Resolution support (<ulink
- url="https://tools.ietf.org/html/rfc4795">RFC 4794</ulink>) on
+ url="https://tools.ietf.org/html/rfc4795">RFC 4795</ulink>) on
the local host. If true, enables full LLMNR responder and
resolver support. If false, disables both. If set to
<literal>resolve</literal>, only resolution support is enabled,
@@ -189,7 +189,7 @@
domains (TLDs) that are not known by the DNS root server. This
logic does not work in all private zone setups.</para>
- <para>Defaults to off.</para>
+ <para>Defaults to <literal>allow-downgrade</literal></para>
</listitem>
</varlistentry>
@@ -227,10 +227,10 @@
<varlistentry>
<term><varname>Cache=</varname></term>
- <listitem><para>Takes a boolean argument. If "yes" (the default), resolving a domain name which already got
- queried earlier will return the previous result as long as it is still valid, and thus does not result in a new
- network request. Be aware that turning off caching comes at a performance penalty, which is particularly
- high when DNSSEC is used.</para>
+ <listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default), resolving a domain name
+ which already got queried earlier will return the previous result as long as it is still valid, and thus does
+ not result in a new network request. Be aware that turning off caching comes at a performance penalty, which
+ is particularly high when DNSSEC is used.</para>
<para>Note that caching is turned off implicitly if the configured DNS server is on a host-local IP address
(such as 127.0.0.1 or ::1), in order to avoid duplicate local caching.</para></listitem>
@@ -239,15 +239,22 @@
<varlistentry>
<term><varname>DNSStubListener=</varname></term>
<listitem><para>Takes a boolean argument or one of <literal>udp</literal> and <literal>tcp</literal>. If
- <literal>udp</literal> (the default), a DNS stub resolver will listen for UDP requests on address 127.0.0.53
+ <literal>udp</literal>, a DNS stub resolver will listen for UDP requests on address 127.0.0.53
port 53. If <literal>tcp</literal>, the stub will listen for TCP requests on the same address and port. If
- <literal>yes</literal>, the stub listens for both UDP and TCP requests. If <literal>no</literal>, the stub
+ <literal>yes</literal> (the default), the stub listens for both UDP and TCP requests. If <literal>no</literal>, the stub
listener is disabled.</para>
<para>Note that the DNS stub listener is turned off implicitly when its listening address and port are already
in use.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ReadEtcHosts=</varname></term>
+ <listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default), the DNS stub resolver will read
+ <filename>/etc/hosts</filename>, and try to resolve hosts or address by using the entries in the file before
+ sending query to DNS servers.</para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff --git a/man/rules/meson.build b/man/rules/meson.build
index 9458a4012d..0c990a0c5d 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -37,9 +37,9 @@ manpages = [
['modules-load.d', '5', [], 'HAVE_KMOD'],
['networkctl', '1', [], 'ENABLE_NETWORKD'],
['networkd.conf', '5', ['networkd.conf.d'], 'ENABLE_NETWORKD'],
- ['nss-myhostname', '8', ['libnss_myhostname.so.2'], 'ENABLE_MYHOSTNAME'],
- ['nss-mymachines', '8', ['libnss_mymachines.so.2'], 'ENABLE_MACHINED'],
- ['nss-resolve', '8', ['libnss_resolve.so.2'], 'ENABLE_RESOLVE'],
+ ['nss-myhostname', '8', ['libnss_myhostname.so.2'], 'ENABLE_NSS_MYHOSTNAME'],
+ ['nss-mymachines', '8', ['libnss_mymachines.so.2'], 'ENABLE_NSS_MYMACHINES'],
+ ['nss-resolve', '8', ['libnss_resolve.so.2'], 'ENABLE_NSS_RESOLVE'],
['nss-systemd', '8', ['libnss_systemd.so.2'], 'ENABLE_NSS_SYSTEMD'],
['os-release', '5', [], ''],
['pam_systemd', '8', [], 'HAVE_PAM'],
@@ -114,6 +114,8 @@ manpages = [
'sd_bus_match_signal',
'sd_bus_match_signal_async'],
''],
+ ['sd_bus_attach_event', '3', ['sd_bus_detach_event', 'sd_bus_get_event'], ''],
+ ['sd_bus_close', '3', ['sd_bus_flush'], ''],
['sd_bus_creds_get_pid',
'3',
['sd_bus_creds_get_audit_login_uid',
@@ -166,7 +168,10 @@ manpages = [
'sd_bus_open_system',
'sd_bus_open_system_machine',
'sd_bus_open_system_remote',
- 'sd_bus_open_user'],
+ 'sd_bus_open_system_with_description',
+ 'sd_bus_open_user',
+ 'sd_bus_open_user_with_description',
+ 'sd_bus_open_with_description'],
''],
['sd_bus_error',
'3',
@@ -177,6 +182,7 @@ manpages = [
'sd_bus_error_get_errno',
'sd_bus_error_has_name',
'sd_bus_error_is_set',
+ 'sd_bus_error_move',
'sd_bus_error_set',
'sd_bus_error_set_const',
'sd_bus_error_set_errno',
@@ -188,7 +194,7 @@ manpages = [
'3',
['SD_BUS_ERROR_END', 'SD_BUS_ERROR_MAP', 'sd_bus_error_map'],
''],
- ['sd_bus_get_fd', '3', [], ''],
+ ['sd_bus_get_fd', '3', ['sd_bus_get_events', 'sd_bus_get_timeout'], ''],
['sd_bus_get_n_queued_read', '3', ['sd_bus_get_n_queued_write'], ''],
['sd_bus_is_open', '3', ['sd_bus_is_ready'], ''],
['sd_bus_message_append', '3', ['sd_bus_message_appendv'], ''],
@@ -204,32 +210,111 @@ manpages = [
['sd_bus_message_append_string_iovec', 'sd_bus_message_append_string_space'],
''],
['sd_bus_message_append_strv', '3', [], ''],
+ ['sd_bus_message_copy', '3', [], ''],
['sd_bus_message_get_cookie', '3', ['sd_bus_message_get_reply_cookie'], ''],
['sd_bus_message_get_monotonic_usec',
'3',
['sd_bus_message_get_realtime_usec', 'sd_bus_message_get_seqnum'],
''],
+ ['sd_bus_message_get_signature',
+ '3',
+ ['sd_bus_message_has_signature', 'sd_bus_message_is_empty'],
+ ''],
+ ['sd_bus_message_get_type',
+ '3',
+ ['sd_bus_message_is_method_call',
+ 'sd_bus_message_is_method_error',
+ 'sd_bus_message_is_signal'],
+ ''],
+ ['sd_bus_message_new',
+ '3',
+ ['SD_BUS_MESSAGE_METHOD_CALL',
+ 'SD_BUS_MESSAGE_METHOD_ERROR',
+ 'SD_BUS_MESSAGE_METHOD_RETURN',
+ 'SD_BUS_MESSAGE_SIGNAL',
+ 'sd_bus_message_get_bus',
+ 'sd_bus_message_ref',
+ 'sd_bus_message_unref',
+ 'sd_bus_message_unrefp'],
+ ''],
+ ['sd_bus_message_new_method_call',
+ '3',
+ ['sd_bus_message_new_method_return'],
+ ''],
+ ['sd_bus_message_new_method_error',
+ '3',
+ ['sd_bus_message_new_method_errno',
+ 'sd_bus_message_new_method_errnof',
+ 'sd_bus_message_new_method_errorf'],
+ ''],
+ ['sd_bus_message_new_signal', '3', [], ''],
+ ['sd_bus_message_read', '3', ['sd_bus_message_readv'], ''],
+ ['sd_bus_message_read_array', '3', [], ''],
['sd_bus_message_read_basic', '3', [], ''],
- ['sd_bus_message_set_destination', '3', ['sd_bus_message_set_sender'], ''],
+ ['sd_bus_message_rewind', '3', [], ''],
+ ['sd_bus_message_set_destination',
+ '3',
+ ['sd_bus_message_get_destination',
+ 'sd_bus_message_get_interface',
+ 'sd_bus_message_get_member',
+ 'sd_bus_message_get_path',
+ 'sd_bus_message_get_sender',
+ 'sd_bus_message_set_sender'],
+ ''],
+ ['sd_bus_message_set_expect_reply',
+ '3',
+ ['sd_bus_message_get_auto_start',
+ 'sd_bus_message_get_expect_reply',
+ 'sd_bus_message_set_auto_start'],
+ ''],
+ ['sd_bus_message_skip', '3', [], ''],
+ ['sd_bus_message_verify_type', '3', [], ''],
['sd_bus_negotiate_fds',
'3',
['sd_bus_negotiate_creds', 'sd_bus_negotiate_timestamp'],
''],
- ['sd_bus_new', '3', ['sd_bus_ref', 'sd_bus_unref', 'sd_bus_unrefp'], ''],
+ ['sd_bus_new',
+ '3',
+ ['sd_bus_flush_close_unref',
+ 'sd_bus_flush_close_unrefp',
+ 'sd_bus_ref',
+ 'sd_bus_unref',
+ 'sd_bus_unrefp'],
+ ''],
['sd_bus_path_encode',
'3',
['sd_bus_path_decode', 'sd_bus_path_decode_many', 'sd_bus_path_encode_many'],
''],
['sd_bus_process', '3', [], ''],
+ ['sd_bus_reply_method_error',
+ '3',
+ ['sd_bus_reply_method_errno',
+ 'sd_bus_reply_method_errnof',
+ 'sd_bus_reply_method_errorf'],
+ ''],
['sd_bus_request_name',
'3',
['sd_bus_release_name',
'sd_bus_release_name_async',
'sd_bus_request_name_async'],
''],
+ ['sd_bus_set_close_on_exit', '3', ['sd_bus_get_close_on_exit'], ''],
['sd_bus_set_connected_signal', '3', ['sd_bus_get_connected_signal'], ''],
+ ['sd_bus_set_description',
+ '3',
+ ['sd_bus_get_allow_interactive_authorization',
+ 'sd_bus_get_description',
+ 'sd_bus_set_allow_interactive_authorization',
+ 'sd_bus_set_anonymous',
+ 'sd_bus_set_trusted'],
+ ''],
['sd_bus_set_sender', '3', ['sd_bus_get_sender'], ''],
['sd_bus_set_watch_bind', '3', ['sd_bus_get_watch_bind'], ''],
+ ['sd_bus_slot_ref',
+ '3',
+ ['sd_bus_slot_get_bus', 'sd_bus_slot_unref', 'sd_bus_slot_unrefp'],
+ ''],
+ ['sd_bus_slot_set_description', '3', ['sd_bus_slot_get_description'], ''],
['sd_bus_slot_set_destroy_callback',
'3',
['sd_bus_destroy_t',
@@ -238,6 +323,7 @@ manpages = [
'sd_bus_track_set_destroy_callback'],
''],
['sd_bus_slot_set_floating', '3', ['sd_bus_slot_get_floating'], ''],
+ ['sd_bus_slot_set_userdata', '3', ['sd_bus_slot_get_userdata'], ''],
['sd_bus_track_add_name',
'3',
['sd_bus_track_add_sender',
@@ -261,6 +347,7 @@ manpages = [
'sd_bus_track_unref',
'sd_bus_track_unrefp'],
''],
+ ['sd_bus_wait', '3', [], ''],
['sd_event_add_child',
'3',
['sd_event_child_handler_t', 'sd_event_source_get_child_pid'],
@@ -363,6 +450,7 @@ manpages = [
['sd_id128_get_machine',
'3',
['sd_id128_get_boot',
+ 'sd_id128_get_boot_app_specific',
'sd_id128_get_invocation',
'sd_id128_get_machine_app_specific'],
''],
@@ -540,6 +628,9 @@ manpages = [
['systemd-ask-password', '1', [], ''],
['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-boot', '7', ['sd-boot'], 'ENABLE_EFI'],
['systemd-cat', '1', [], ''],
['systemd-cgls', '1', [], ''],
@@ -583,6 +674,7 @@ manpages = [
'ENABLE_HIBERNATE'],
['systemd-hostnamed.service', '8', ['systemd-hostnamed'], 'ENABLE_HOSTNAMED'],
['systemd-hwdb', '8', [], 'ENABLE_HWDB'],
+ ['systemd-id128', '1', [], ''],
['systemd-importd.service', '8', ['systemd-importd'], 'ENABLE_IMPORTD'],
['systemd-inhibit', '1', [], ''],
['systemd-initctl.service',
@@ -646,6 +738,7 @@ manpages = [
'8',
['systemd-rfkill', 'systemd-rfkill.socket'],
'ENABLE_RFKILL'],
+ ['systemd-run-generator', '8', [], ''],
['systemd-run', '1', [], ''],
['systemd-sleep.conf', '5', ['sleep.conf.d'], ''],
['systemd-socket-activate', '1', [], ''],
@@ -820,6 +913,7 @@ manpages = [
''],
['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
['udevadm', '8', [], ''],
+ ['user@.service', '5', ['user-runtime-dir@.service'], ''],
['vconsole.conf', '5', [], 'ENABLE_VCONSOLE']
]
# Really, do not edit.
diff --git a/man/sd-bus-errors.xml b/man/sd-bus-errors.xml
index c834bde292..c896511541 100644
--- a/man/sd-bus-errors.xml
+++ b/man/sd-bus-errors.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -6,7 +6,8 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="sd-bus-errors">
+<refentry id="sd-bus-errors"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd-bus-errors</title>
@@ -259,15 +260,7 @@
</variablelist>
</refsect1>
- <refsect1>
- <title>Notes</title>
-
- <para>The various error definitions described here are available
- as a shared library, which can be compiled and linked to with the
- <constant>libsystemd</constant> <citerefentry
- project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- file.</para>
- </refsect1>
+ <xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>See Also</title>
diff --git a/man/sd-bus.xml b/man/sd-bus.xml
index 1f3d037c10..4620590be4 100644
--- a/man/sd-bus.xml
+++ b/man/sd-bus.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -43,34 +43,57 @@
</para>
<para>See
- <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_message_append_string_memfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_message_append_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_message_can_send</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_negotiate_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- <citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <literallayout><citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_n_queued_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_append_string_memfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_append_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_copy</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>,
+<citerefentry><refentrytitle>sd_bus_message_get_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_read_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_read_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_rewind</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_set_expect_reply</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_skip</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_verify_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_negotiate_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_reply_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_watch_bind</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+<citerefentry><refentrytitle>sd_bus_set_close_on_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+<citerefentry><refentrytitle>sd_bus_slot_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_slot_set_destroy_callback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_slot_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+</literallayout>
for more information about the functions available.</para>
</refsect1>
diff --git a/man/sd-event.xml b/man/sd-event.xml
index 65bc3f8891..2e6ab69f82 100644
--- a/man/sd-event.xml
+++ b/man/sd-event.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd-id128.xml b/man/sd-id128.xml
index fbcf4bf367..4425c45d1e 100644
--- a/man/sd-id128.xml
+++ b/man/sd-id128.xml
@@ -141,8 +141,8 @@ int main(int argc, char **argv) {
}</programlisting>
<para>Note that new, randomized IDs may be generated with
- <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
- <option>--new-id128</option> option.</para>
+ <citerefentry><refentrytitle>systemd-id128</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <command>new</command> command.</para>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
diff --git a/man/sd-journal.xml b/man/sd-journal.xml
index 8bfcb90ca0..3fa6c75b7e 100644
--- a/man/sd-journal.xml
+++ b/man/sd-journal.xml
@@ -77,16 +77,15 @@
<refsect1>
<title>Thread safety</title>
- <para>Functions that operate on the <structname>sd_journal</structname> object are thread
- agnostic — given <structname>sd_journal</structname> pointer may only be used from one thread at
- a time, but multiple threads may use multiple such objects safely. Other functions —
- those that are used to send entries to the journal, like
- <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- and similar, or those that are used to retrieve global information like
- <citerefentry><refentrytitle>sd_journal_stream_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- and
+ <para>Functions that operate on <structname>sd_journal</structname> objects are thread agnostic — given
+ <structname>sd_journal</structname> pointer may only be used from one specific thread at all times (and it has to
+ be the very same one during the entire lifetime of the object), but multiple, independent threads may use multiple,
+ independent objects safely. Other functions — those that are used to send entries to the journal, like
+ <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry> and similar,
+ or those that are used to retrieve global information like
+ <citerefentry><refentrytitle>sd_journal_stream_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
<citerefentry><refentrytitle>sd_journal_get_catalog_for_message_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- — are thread-safe and may be called from multiple threads in parallel.</para>
+ — are fully thread-safe and may be called from multiple threads in parallel.</para>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
diff --git a/man/sd_bus_add_match.xml b/man/sd_bus_add_match.xml
index 9137ef977c..c4f24aed3e 100644
--- a/man/sd_bus_add_match.xml
+++ b/man/sd_bus_add_match.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -8,7 +8,7 @@
Copyright © 2016 Julian Orth
-->
-<refentry id="sd_bus_add_match">
+<refentry id="sd_bus_add_match" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_bus_add_match</title>
@@ -154,9 +154,7 @@
<refsect1>
<title>Notes</title>
- <para><function>sd_bus_add_match()</function> and the other functions described here are available as a shared
- library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry
- project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
+ <xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
<refsect1>
diff --git a/man/sd_bus_attach_event.xml b/man/sd_bus_attach_event.xml
new file mode 100644
index 0000000000..a2f7297f21
--- /dev/null
+++ b/man/sd_bus_attach_event.xml
@@ -0,0 +1,122 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refentry id="sd_bus_attach_event"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_attach_event</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_attach_event</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_attach_event</refname>
+ <refname>sd_bus_detach_event</refname>
+ <refname>sd_bus_get_event</refname>
+
+ <refpurpose>Attach a bus connection object to an event loop</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_attach_event</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>sd_event *<parameter>e</parameter></paramdef>
+ <paramdef>int <parameter>priority</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_detach_event</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_event *<function>sd_bus_get_event</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_attach_event()</function> attaches the specified bus connection object to an
+ <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry> event loop object at
+ the specified priority (see
+ <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ for details on event loop priorities). When a bus connection object is attached to an event loop incoming messages
+ will be automatically read and processed, and outgoing messages written, whenever the event loop is run. When the
+ event loop is about to terminate, the bus connection is automatically flushed and closed (see
+ <citerefentry><refentrytitle>sd_bus_set_close_on_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+ details on this). By default bus connection objects are not attached to any event loop. When a bus connection
+ object is attached to one it is not necessary to invoke
+ <citerefentry><refentrytitle>sd_bus_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
+ <citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry> as this
+ functionality is handled automatically by the event loop.</para>
+
+ <para><function>sd_bus_detach_event()</function> detaches a bus object from its event loop.</para>
+
+ <para>The <function>sd_bus_get_event()</function> returns the event loop object the specified bus object is
+ currently attached to, or <constant>NULL</constant> if it is currently not attached to any.</para>
+
+ <para>Note that <function>sd_bus_attach_event()</function> is only one of three supported ways to implement I/O
+ event handling for bus connections. Alternatively use
+ <citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry> for hooking up a
+ bus connection object with external or manual event loops. Or use
+ <citerefentry><refentrytitle>sd_bus_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry> as a simple
+ synchronous, blocking I/O waiting call.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_bus_attach_event()</function> and <function>sd_bus_detach_event()</function> return
+ 0 or a positive integer. On failure, they return a negative errno-style error code.</para>
+
+ <para><function>sd_bus_get_event()</function> returns an event loop object or <constant>NULL</constant>.</para>
+ </refsect1>
+
+ <refsect1>
+ <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>
+ </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-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_set_close_on_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_close.xml b/man/sd_bus_close.xml
new file mode 100644
index 0000000000..369afd8747
--- /dev/null
+++ b/man/sd_bus_close.xml
@@ -0,0 +1,101 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refentry id="sd_bus_close"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_close</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_close</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_close</refname>
+ <refname>sd_bus_flush</refname>
+
+ <refpurpose>Close and flush a bus connection</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void <function>sd_bus_close</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_flush</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_close()</function> disconnects the specified bus connection. When this call is invoked and
+ the specified bus object refers to an active connection it is immediately terminated. No further messages may be
+ sent or receieved on it. Any messages queued in the bus object (both incoming and outgoing) are released. If
+ invoked on <constant>NULL</constant> bus object or when the bus connection is already closed this function executes
+ no operation. This call does not free or unreference the bus object itself. Use
+ <citerefentry><refentrytitle>sd_bus_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> for that.</para>
+
+ <para><function>sd_bus_flush()</function> synchronously writes out all outgoing queued message on a bus connection
+ if there are any. This function call may block if the peer is not processing bus messages quickly.</para>
+
+ <para>Before a program exits it is usually a good idea to flush any pending messages with
+ <function>sd_bus_flush()</function> and then close connections with <function>sd_bus_close()</function> to ensure
+ that no unwritten messages are lost, no further messages may be queued and all incoming but unprocessed messages
+ are released. After both operations have been done, it is a good idea to also drop any remaining references to the
+ bus object so that it may be freed. Since these three operations are frequently done together a helper call
+ <citerefentry><refentrytitle>sd_bus_flush_close_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> is
+ provided that combines them into one.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_bus_flush()</function> returns 0 or a positive integer. On failure, it returns a
+ negative errno-style error code.</para>
+ </refsect1>
+
+ <refsect1>
+ <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>
+ </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_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_set_close_on_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_creds_get_pid.xml b/man/sd_bus_creds_get_pid.xml
index 1ca28e7a19..c068fbfb2e 100644
--- a/man/sd_bus_creds_get_pid.xml
+++ b/man/sd_bus_creds_get_pid.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_bus_creds_new_from_pid.xml b/man/sd_bus_creds_new_from_pid.xml
index 2fc7a62c6b..eda5d17d8a 100644
--- a/man/sd_bus_creds_new_from_pid.xml
+++ b/man/sd_bus_creds_new_from_pid.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_bus_default.xml b/man/sd_bus_default.xml
index 53cc9d7768..dfb32b9b4f 100644
--- a/man/sd_bus_default.xml
+++ b/man/sd_bus_default.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -24,8 +24,11 @@
<refname>sd_bus_default_system</refname>
<refname>sd_bus_open</refname>
+ <refname>sd_bus_open_with_description</refname>
<refname>sd_bus_open_user</refname>
+ <refname>sd_bus_open_user_with_description</refname>
<refname>sd_bus_open_system</refname>
+ <refname>sd_bus_open_system_with_description</refname>
<refname>sd_bus_open_system_remote</refname>
<refname>sd_bus_open_system_machine</refname>
@@ -57,16 +60,34 @@
</funcprototype>
<funcprototype>
+ <funcdef>int <function>sd_bus_open_with_description</function></funcdef>
+ <paramdef>sd_bus **<parameter>bus</parameter></paramdef>
+ <paramdef>const char *<parameter>description</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>int <function>sd_bus_open_user</function></funcdef>
<paramdef>sd_bus **<parameter>bus</parameter></paramdef>
</funcprototype>
<funcprototype>
+ <funcdef>int <function>sd_bus_open_user_with_description</function></funcdef>
+ <paramdef>sd_bus **<parameter>bus</parameter></paramdef>
+ <paramdef>const char *<parameter>description</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>int <function>sd_bus_open_system</function></funcdef>
<paramdef>sd_bus **<parameter>bus</parameter></paramdef>
</funcprototype>
<funcprototype>
+ <funcdef>int <function>sd_bus_open_system_with_description</function></funcdef>
+ <paramdef>sd_bus **<parameter>bus</parameter></paramdef>
+ <paramdef>const char *<parameter>description</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>int <function>sd_bus_open_system_remote</function></funcdef>
<paramdef>sd_bus **<parameter>bus</parameter></paramdef>
<paramdef>const char *<parameter>host</parameter></paramdef>
@@ -126,6 +147,20 @@
<function>sd_bus_default_system()</function> to connect to the
user or system buses.</para>
+ <para><function>sd_bus_open_with_description()</function>,
+ <function>sd_bus_open_user_with_description()</function>, and
+ <function>sd_bus_open_system_with_description()</function> are similar to
+ <function>sd_bus_open()</function>, <function>sd_bus_open_user()</function>, and
+ <function>sd_bus_open_system()</function>, but allow a description string to be set, see
+ <citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ <parameter>description</parameter> may be <constant>NULL</constant>, in which case this function
+ is equivalent to <function>sd_bus_open()</function>. This description string is used in log
+ messages about the bus object, and including a "name" for the bus makes them easier to
+ understand. Some messages are emitted during bus initialization, hence using this function is
+ prefereable to setting the description later with
+ <function>sd_bus_open_with_description()</function>. The argument is copied internally and will
+ not be referenced after the function returns.</para>
+
<para>If the <varname>$DBUS_SESSION_BUS_ADDRESS</varname> environment
variable is set
(cf. <citerefentry project='man-pages'><refentrytitle>environ</refentrytitle><manvolnum>7</manvolnum></citerefentry>),
@@ -146,7 +181,8 @@
<citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
<parameter>host</parameter> consists of an optional user name followed by the
<literal>@</literal> symbol, and the hostname, optionally followed by a
- <literal>:</literal> and a machine name. If the machine name is given, a connection
+ <literal>:</literal> and a port, optionally followed by a
+ <literal>/</literal> and a machine name. If the machine name is given, a connection
is created to the system bus in the specified container on the remote machine, and
otherwise a connection to the system bus on the specified host is created.</para>
diff --git a/man/sd_bus_error.xml b/man/sd_bus_error.xml
index 77cb16e143..36cdf4c338 100644
--- a/man/sd_bus_error.xml
+++ b/man/sd_bus_error.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -31,6 +31,7 @@
<refname>sd_bus_error_set_errnofv</refname>
<refname>sd_bus_error_get_errno</refname>
<refname>sd_bus_error_copy</refname>
+ <refname>sd_bus_error_move</refname>
<refname>sd_bus_error_is_set</refname>
<refname>sd_bus_error_has_name</refname>
@@ -100,7 +101,7 @@
<paramdef>sd_bus_error *<parameter>e</parameter></paramdef>
<paramdef>int <parameter>error</parameter></paramdef>
<paramdef>const char *<parameter>format</parameter></paramdef>
- <paramdef>va_list ap</paramdef>
+ <paramdef>va_list <parameter>ap</parameter></paramdef>
</funcprototype>
<funcprototype>
@@ -115,6 +116,12 @@
</funcprototype>
<funcprototype>
+ <funcdef>int <function>sd_bus_error_move</function></funcdef>
+ <paramdef>sd_bus_error *<parameter>dst</parameter></paramdef>
+ <paramdef>sd_bus_error *<parameter>e</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>int <function>sd_bus_error_is_set</function></funcdef>
<paramdef>const sd_bus_error *<parameter>e</parameter></paramdef>
</funcprototype>
@@ -148,7 +155,7 @@
should have both fields initialized to NULL. Set an error
structure to <constant>SD_BUS_ERROR_NULL</constant> in order to
reset both fields to NULL. When no longer necessary, resources
- held by the <structname>sd_bus_error</structname>structure should
+ held by the <structname>sd_bus_error</structname> structure should
be destroyed with <function>sd_bus_error_free()</function>.</para>
<para><function>sd_bus_error_set()</function> sets an error
@@ -245,6 +252,14 @@
Otherwise, they will be copied. Returns a converted
<varname>errno</varname>-like, negative error code.</para>
+ <para><function>sd_bus_error_move()</function> is similar to <function>sd_bus_error_copy()</function>, but will
+ move any error information from <parameter>e</parameter> into <parameter>dst</parameter>, resetting the
+ former. This function cannot fail, as no new memory is allocated. Note that if <parameter>e</parameter> is not set
+ (or <constant>NULL</constant>) <parameter>dst</parameter> is initializated to
+ <constant>SD_BUS_ERROR_NULL</constant>. Moreover, if <parameter>dst</parameter> is <constant>NULL</constant> no
+ operation is executed on it and and resources held by <parameter>e</parameter> are freed and reset. Returns a
+ converted <varname>errno</varname>-like, negative error code.</para>
+
<para><function>sd_bus_error_is_set()</function> will return a
non-zero value if <parameter>e</parameter> is
non-<constant>NULL</constant> and an error has been set,
@@ -287,9 +302,8 @@
<constant>NULL</constant>, and a positive errno value mapped from
<parameter>e-&gt;name</parameter> otherwise.</para>
- <para><function>sd_bus_error_copy()</function> returns 0 or a
- positive integer on success, and a negative error value converted
- from the error name otherwise.</para>
+ <para><function>sd_bus_error_copy()</function> and <function>sd_bus_error_move()</function> return 0 or a positive
+ integer on success, and a negative error value converted from the error name otherwise.</para>
<para><function>sd_bus_error_is_set()</function> returns a
non-zero value when <parameter>e</parameter> and the
diff --git a/man/sd_bus_error_add_map.xml b/man/sd_bus_error_add_map.xml
index 3eacbab660..dbe05a1892 100644
--- a/man/sd_bus_error_add_map.xml
+++ b/man/sd_bus_error_add_map.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -6,7 +6,8 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="sd_bus_error_add_map">
+<refentry id="sd_bus_error_add_map"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_bus_error_add_map</title>
@@ -123,15 +124,7 @@
</variablelist>
</refsect1>
- <refsect1>
- <title>Notes</title>
-
- <para>The various error definitions described here are available
- as a shared library, which can be compiled and linked to with the
- <constant>libsystemd</constant> <citerefentry
- project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- file.</para>
- </refsect1>
+ <xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>See Also</title>
diff --git a/man/sd_bus_get_fd.xml b/man/sd_bus_get_fd.xml
index a5d49074b3..2a81bddaa0 100644
--- a/man/sd_bus_get_fd.xml
+++ b/man/sd_bus_get_fd.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -8,7 +8,7 @@
Copyright © 2016 Julian Orth
-->
-<refentry id="sd_bus_get_fd">
+<refentry id="sd_bus_get_fd" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_bus_get_fd</title>
@@ -22,8 +22,10 @@
<refnamediv>
<refname>sd_bus_get_fd</refname>
+ <refname>sd_bus_get_events</refname>
+ <refname>sd_bus_get_timeout</refname>
- <refpurpose>Get the file descriptor connected to the message bus</refpurpose>
+ <refpurpose>Get the file descriptor, I/O events and time-out to wait for from a message bus object</refpurpose>
</refnamediv>
<refsynopsisdiv>
@@ -34,46 +36,124 @@
<funcdef>int <function>sd_bus_get_fd</function></funcdef>
<paramdef>sd_bus *<parameter>bus</parameter></paramdef>
</funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_get_events</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_get_timeout</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>uint64_t *<parameter>timeout_usec</parameter></paramdef>
+ </funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para>
- <function>sd_bus_get_fd()</function> returns the file descriptor used to
- communicate with the message bus. This descriptor can be used with
- <citerefentry
- project='die-net'><refentrytitle>select</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry
- project='die-net'><refentrytitle>poll</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- or similar functions to wait for incoming messages.
- </para>
-
- <para>
- If the bus was created with the
- <citerefentry><refentrytitle>sd_bus_set_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- function, then the <parameter>input_fd</parameter> used in that call is
- returned.
- </para>
+ <para><function>sd_bus_get_fd()</function> returns the file descriptor used to communicate from a message bus
+ object. This descriptor can be used with <citerefentry
+ project='man-pages'><refentrytitle>poll</refentrytitle><manvolnum>3</manvolnum></citerefentry> or a similar
+ function to wait for I/O events on the specified bus connection object. If the bus object was configured with the
+ <citerefentry><refentrytitle>sd_bus_set_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry> function, then
+ the <parameter>input_fd</parameter> file descriptor used in that call is returned.</para>
+
+ <para><function>sd_bus_get_events()</function> returns the I/O events to wait for, suitable for passing to
+ <function>poll()</function> or a similar call. Returns a combination of <constant>POLLIN</constant>,
+ <constant>POLLOUT</constant>, … events, or negative on error.</para>
+
+ <para><function>sd_bus_get_timeout()</function> returns the time-out in µs to pass to to
+ <function>poll()</function> or a similar call when waiting for events on the specified bus connection. The returned
+ time-out may be zero, in which case a subsequent I/O polling call should be invoked in non-blocking mode. The
+ returned timeout may be <constant>UINT64_MAX</constant> in which case the I/O polling call may block indefinitely,
+ without any applied time-out. Note that the returned time-out should be considered only a maximum sleeping time. It
+ is permissible (and even expected) that shorter time-outs are used by the calling program, in case other event
+ sources are polled in the same event loop. Note that the returned time-value is relative and specified in
+ microseconds. When converting this value in order to pass it as third argument to <function>poll()</function>
+ (which expects milliseconds), care should be taken to use a division that rounds up to ensure the I/O polling
+ operation doesn't sleep for shorter than necessary, which might result in unintended busy looping (alternatively,
+ use <citerefentry project='man-pages'><refentrytitle>ppoll</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ instead of plain <function>poll()</function>, which understands time-outs with nano-second granularity).</para>
+
+ <para>These three functions are useful to hook up a bus connection object with an external or manual event loop
+ involving <function>poll()</function> or a similar I/O polling call. Before each invocation of the I/O polling
+ call, all three functions should be invoked: the file descriptor returned by <function>sd_bus_get_fd()</function>
+ should be polled for the events indicated by <function>sd_bus_get_events()</function>, and the I/O call should
+ block for that up to the time-out returned by <function>sd_bus_get_timeout()</function>. After each I/O polling
+ call the bus connection needs to process incoming or outgoing data, by invoking
+ <citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+ <para>Note that these function are only one of three supported ways to implement I/O event handling for bus
+ connections. Alternatively use
+ <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry> to attach a
+ bus connection to an <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ event loop. Or use <citerefentry><refentrytitle>sd_bus_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ as a simple synchronous, blocking I/O waiting call.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
- <para>
- Returns the file descriptor used for incoming messages from the message
- bus.
- </para>
+ <para><function>sd_bus_get_fd()</function> returns the file descriptor used for communication, or a negative
+ <varname>errno</varname>-style error code on error.</para>
+
+ <para><function>sd_bus_get_events()</function> returns the I/O event mask to use for I/O event watching, or a
+ negative <varname>errno</varname>-style error code on error.</para>
+
+ <para><function>sd_bus_get_timeout()</function> returns zero or positive on success, or a negative
+ <varname>errno</varname>-style error code on error.</para>
</refsect1>
<refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>An invalid bus object was passed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ECHILD</constant></term>
+
+ <listitem><para>The bus connection was allocated in a parent process and is being reused in a child process
+ after <function>fork()</function>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOTCONN</constant></term>
+
+ <listitem><para>The bus connection has been terminated.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem><para>Two distinct file descriptors were passed for input and output using
+ <function>sd_bus_set_fd()</function>, which <function>sd_bus_get_fd()</function> cannot
+ return.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </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_set_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>poll</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/sd_bus_get_n_queued_read.xml b/man/sd_bus_get_n_queued_read.xml
index b8788769ec..76ad659681 100644
--- a/man/sd_bus_get_n_queued_read.xml
+++ b/man/sd_bus_get_n_queued_read.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_bus_is_open.xml b/man/sd_bus_is_open.xml
index 69cbb29284..0388db82a6 100644
--- a/man/sd_bus_is_open.xml
+++ b/man/sd_bus_is_open.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -6,7 +6,8 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="sd_bus_is_open">
+<refentry id="sd_bus_is_open"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_bus_is_open</title>
@@ -88,13 +89,7 @@
</variablelist>
</refsect1>
- <refsect1>
- <title>Notes</title>
-
- <para><function>sd_bus_is_open()</function> and <function>sd_bus_is_ready()</function> are available as
- a shared library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry
- project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
- </refsect1>
+ <xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>See Also</title>
diff --git a/man/sd_bus_message_append.xml b/man/sd_bus_message_append.xml
index b70ec8309c..1fdda51dcc 100644
--- a/man/sd_bus_message_append.xml
+++ b/man/sd_bus_message_append.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -94,13 +94,12 @@
values for each entry matching the element type of
the dictionary entries.</para>
- <para>The <function>sd_bus_message_appendv()</function> is equivalent to
- the function <function>sd_bus_message_append()</function>,
- except that it is called with a <literal>va_list</literal> instead of
- a variable number of arguments. This function does not call the
- <function>va_end()</function> macro. Because it invokes the
- <function>va_arg()</function> macro, the value of ap
- is undefined after the call.</para>
+ <para>The <function>sd_bus_message_appendv()</function> is equivalent to the
+ <function>sd_bus_message_append()</function>, except that it is called with
+ a <literal>va_list</literal> instead of a variable number of arguments. This
+ function does not call the <function>va_end()</function> macro. Because it
+ invokes the <function>va_arg()</function> macro, the value of
+ <parameter>ap</parameter> is undefined after the call.</para>
<para>For further details on the D-Bus type system, please consult
the <ulink
diff --git a/man/sd_bus_message_append_array.xml b/man/sd_bus_message_append_array.xml
index 4c1142eeb0..746f9e3cc8 100644
--- a/man/sd_bus_message_append_array.xml
+++ b/man/sd_bus_message_append_array.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_bus_message_append_basic.xml b/man/sd_bus_message_append_basic.xml
index 6c76569012..abd3baef36 100644
--- a/man/sd_bus_message_append_basic.xml
+++ b/man/sd_bus_message_append_basic.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -255,6 +255,7 @@
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_read_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<ulink url="http://dbus.freedesktop.org/doc/dbus-specification.html">The D-Bus specification</ulink>
</para>
diff --git a/man/sd_bus_message_append_string_memfd.xml b/man/sd_bus_message_append_string_memfd.xml
index 37be79c976..4224af0a2e 100644
--- a/man/sd_bus_message_append_string_memfd.xml
+++ b/man/sd_bus_message_append_string_memfd.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_bus_message_append_strv.xml b/man/sd_bus_message_append_strv.xml
index 9cd4513ab1..5f1a4f567f 100644
--- a/man/sd_bus_message_append_strv.xml
+++ b/man/sd_bus_message_append_strv.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_bus_message_copy.xml b/man/sd_bus_message_copy.xml
new file mode 100644
index 0000000000..ac2a4f32b9
--- /dev/null
+++ b/man/sd_bus_message_copy.xml
@@ -0,0 +1,115 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refentry id="sd_bus_message_copy" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_message_copy</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_copy</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_copy</refname>
+
+ <refpurpose>Copy the contents of one message to another</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int sd_bus_message_copy</funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ <paramdef>sd_bus_message *<parameter>source</parameter></paramdef>
+ <paramdef>int <parameter>all</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_message_copy()</function> copies the contents from
+ message <parameter>source</parameter> to <parameter>m</parameter>. If
+ <parameter>all</parameter> is false, a single complete type is copied
+ (basic or container). If <parameter>all</parameter> is true, the contents
+ are copied until the end of the currently open container or the end
+ of <parameter>source</parameter>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, this call returns true if anything was copied, and false if
+ there was nothing to copy. On failure, it returns a negative errno-style error
+ code.</para>
+ </refsect1>
+
+ <refsect1 id='errors'>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para><parameter>source</parameter> or <parameter>m</parameter> are
+ <constant>NULL</constant>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem><para>Message <parameter>m</parameter> has been sealed or
+ <parameter>source</parameter> has <emphasis>not</emphasis> been sealed.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ESTALE</constant></term>
+
+ <listitem><para>Destination message is in invalid state.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENXIO</constant></term>
+
+ <listitem><para>Destination message cannot be appended to.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_get_signature.xml b/man/sd_bus_message_get_signature.xml
new file mode 100644
index 0000000000..bee6778190
--- /dev/null
+++ b/man/sd_bus_message_get_signature.xml
@@ -0,0 +1,111 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_get_signature" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <refentryinfo>
+ <title>sd_bus_message_get_signature</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_get_signature</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_get_signature</refname>
+ <refname>sd_bus_message_is_empty</refname>
+ <refname>sd_bus_message_has_signature</refname>
+
+ <refpurpose>Query bus message signature</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>const char* <function>sd_bus_message_get_signature</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ <paramdef>int <parameter>complete</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_is_empty</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_has_signature</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ <paramdef>const char *<parameter>signature</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_message_get_signature()</function> returns the signature of message
+ <parameter>message</parameter>. If <parameter>complete</parameter> is true, the signature of the
+ whole message is returned, and just the signature of the currently open container otherwise.
+ </para>
+
+ <para><function>sd_bus_message_is_empty()</function> returns true if the message is empty,
+ i.e. when its signature is empty.</para>
+
+ <para><function>sd_bus_message_has_signature()</function> returns true if the signature of the
+ message <parameter>message</parameter> matches given <parameter>signature</parameter>. Parameter
+ <parameter>signature</parameter> may be <constant>NULL</constant>, this is treated the same as
+ an empty string, which is equivalent to calling <function>sd_bus_message_is_empty()</function>.
+ </para>
+</refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_bus_message_get_signature()</function> returns
+ the signature, and <constant>NULL</constant> on error.</para>
+
+ <para>The other functions return 0 or a positive integer on success. On failure, they return a
+ negative errno-style error code.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The <parameter>message</parameter> parameter is <constant>NULL</constant>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>NULL</constant></term>
+
+ <listitem><para>The <parameter>message</parameter> parameter is <constant>NULL</constant>.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_get_type.xml b/man/sd_bus_message_get_type.xml
new file mode 100644
index 0000000000..6f6b7d0ba3
--- /dev/null
+++ b/man/sd_bus_message_get_type.xml
@@ -0,0 +1,129 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_get_type" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_message_get_type</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_get_type</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_get_type</refname>
+ <refname>sd_bus_message_is_signal</refname>
+ <refname>sd_bus_message_is_method_call</refname>
+ <refname>sd_bus_message_is_method_error</refname>
+
+ <refpurpose>Query bus message addressing metadata</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_get_type</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ <paramdef>uint8_t *<parameter>type</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_is_signal</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ <paramdef>const char *<parameter>interface</parameter></paramdef>
+ <paramdef>const char *<parameter>member</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_is_method_call</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ <paramdef>const char *<parameter>interface</parameter></paramdef>
+ <paramdef>const char *<parameter>member</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_is_method_error</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_message_get_type()</function> returns the type of a message in the output
+ parameter <parameter>type</parameter>, one of <constant>SD_BUS_MESSAGE_METHOD_CALL</constant>,
+ <constant>SD_BUS_MESSAGE_METHOD_RETURN</constant>,
+ <constant>SD_BUS_MESSAGE_METHOD_ERROR</constant>, <constant>SD_BUS_MESSAGE_SIGNAL</constant>.
+ This type is either specified as a parameter when the message is created using
+ <citerefentry><refentrytitle>sd_bus_set_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ or is set automatically when the message is created using
+ <citerefentry><refentrytitle>sd_bus_set_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_set_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_set_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ and similar functions.
+ </para>
+
+ <para><function>sd_bus_message_is_signal()</function> checks if message <parameter>m</parameter>
+ is a signal message. If <parameter>interface</parameter> is non-null, it also checks if the
+ message has the same interface set. If <parameter>member</parameter> is non-null, it also checks
+ if the message has the same member set. Also see
+ <citerefentry><refentrytitle>sd_bus_set_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>. It returns true when all checks pass.</para>
+
+ <para><function>sd_bus_message_is_method_call()</function> checks if message <parameter>m</parameter>
+ is a method call message. If <parameter>interface</parameter> is non-null, it also checks if the
+ message has the same interface set. If <parameter>member</parameter> is non-null, it also checks
+ if the message has the same member set. Also see
+ <citerefentry><refentrytitle>sd_bus_set_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>. It returns true when all checks pass.</para>
+
+ <para><function>sd_bus_message_is_method_error()</function> checks if message <parameter>m</parameter>
+ is an error reply message. If <parameter>name</parameter> is non-null, it also checks if the
+ message has the same error identifier set. Also see
+ <citerefentry><refentrytitle>sd_bus_set_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>. It returns true when all checks pass.</para>
+</refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, those functions return 0 or a positive
+ integer. On failure, it returns a negative errno-style error code.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The <parameter>message</parameter> parameter or the output parameter are
+ <constant>NULL</constant>.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_new.xml b/man/sd_bus_message_new.xml
new file mode 100644
index 0000000000..78bca8a89c
--- /dev/null
+++ b/man/sd_bus_message_new.xml
@@ -0,0 +1,189 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_new" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <refentryinfo>
+ <title>sd_bus_message_new</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_new</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_new</refname>
+ <refname>sd_bus_message_ref</refname>
+ <refname>sd_bus_message_unref</refname>
+ <refname>sd_bus_message_unrefp</refname>
+ <refname>SD_BUS_MESSAGE_METHOD_CALL</refname>
+ <refname>SD_BUS_MESSAGE_METHOD_RETURN</refname>
+ <refname>SD_BUS_MESSAGE_METHOD_ERROR</refname>
+ <refname>SD_BUS_MESSAGE_SIGNAL</refname>
+ <refname>sd_bus_message_get_bus</refname>
+
+ <refpurpose>Create a new bus message object and create or destroy references to it</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcsynopsisinfo><token>enum</token> {
+ <constant>SD_BUS_MESSAGE_METHOD_CALL</constant>,
+ <constant>SD_BUS_MESSAGE_METHOD_RETURN</constant>,
+ <constant>SD_BUS_MESSAGE_METHOD_ERROR</constant>,
+ <constant>SD_BUS_MESSAGE_SIGNAL</constant>,
+};</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_new</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>sd_bus_message **<parameter>m</parameter></paramdef>
+ <paramdef>uint8_t <parameter>type</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus_message *<function>sd_bus_message_ref</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus_message *<function>sd_bus_message_unref</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>sd_bus_message_unrefp</function></funcdef>
+ <paramdef>sd_bus_message **<parameter>mp</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus *<function>sd_bus_message_get_bus</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_message_new()</function> creates a new bus message object attached to the
+ bus <parameter>bus</parameter> and returns it in the output parameter <parameter>m</parameter>.
+ This object is reference-counted, and will be destroyed when all references are gone. Initially,
+ the caller of this function owns the sole reference to the message object. Note that the message
+ object holds a reference to the bus object, so the bus object will not be destroyed as long as
+ the message exists.</para>
+
+ <para>Note: this is a low-level call. In most cases functions like
+ <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_new_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ and <citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ that create a message of a certain type and initialize various fields are easier to use.</para>
+
+ <para>The <parameter>type</parameter> parameter specifies the type of the message. It must be
+ one of <constant>SD_BUS_MESSAGE_METHOD_CALL</constant> — a method call,
+ <constant>SD_BUS_MESSAGE_METHOD_RETURN</constant> — a method call reply,
+ <constant>SD_BUS_MESSAGE_METHOD_ERROR</constant> — an error reply to a method call,
+ <constant>SD_BUS_MESSAGE_SIGNAL</constant> — a broadcast message with no reply.
+ </para>
+
+ <para>The flag to allow interactive authorization is initialized based on the current value set
+ in the bus object, see
+ <citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ This may be changed using
+ <citerefentry><refentrytitle>sd_bus_message_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para>
+
+ <para><function>sd_bus_message_ref()</function> increases the reference counter of
+ <parameter>m</parameter> by one.</para>
+
+ <para><function>sd_bus_message_unref()</function> decreases the reference counter of
+ <parameter>m</parameter> by one. Once the reference count has dropped to zero, message object is
+ destroyed and cannot be used anymore, so further calls to
+ <function>sd_bus_message_ref()</function> or <function>sd_bus_message_unref()</function> are
+ illegal.</para>
+
+ <para><function>sd_bus_message_unrefp()</function> is similar to
+ <function>sd_bus_message_unref()</function> but takes a pointer to a
+ pointer to an <type>sd_bus_message</type> object. This call is useful in
+ conjunction with GCC's and LLVM's <ulink
+ url="https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html">Clean-up
+ Variable Attribute</ulink>. See
+ <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ for an example how to use the cleanup attribute.</para>
+
+ <para><function>sd_bus_message_ref()</function> and <function>sd_bus_message_unref()</function>
+ execute no operation if the passed in bus object address is
+ <constant>NULL</constant>. <function>sd_bus_message_unrefp()</function> will first dereference
+ its argument, which must not be <constant>NULL</constant>, and will execute no operation if
+ <emphasis>that</emphasis> is <constant>NULL</constant>.
+ </para>
+
+ <para><function>sd_bus_message_get_bus()</function> returns the bus object that message
+ <parameter>m</parameter> is attached to.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_bus_message_new()</function> returns 0 or a positive integer. On
+ failure, it returns a negative errno-style error code.</para>
+
+ <para><function>sd_bus_message_ref()</function> always returns the argument.
+ </para>
+
+ <para><function>sd_bus_message_unref()</function> always returns
+ <constant>NULL</constant>.</para>
+
+ <para><function>sd_bus_message_get_bus()</function> always returns the bus object.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>Specified <parameter>type</parameter> is invalid.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOTCONN</constant></term>
+
+ <listitem><para>The bus parameter <parameter>bus</parameter> is <constant>NULL</constant> or
+ the bus is not connected.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_new_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_new_method_call.xml b/man/sd_bus_message_new_method_call.xml
new file mode 100644
index 0000000000..c643177ba4
--- /dev/null
+++ b/man/sd_bus_message_new_method_call.xml
@@ -0,0 +1,166 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_new_method_call"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_message_new_method_call</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_new_method_call</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_new_method_call</refname>
+ <refname>sd_bus_message_new_method_return</refname>
+
+ <refpurpose>Create a method call message</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int sd_bus_message_new_method_call</funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>sd_bus_message **<parameter>m</parameter></paramdef>
+ <paramdef>const char *<parameter>destination</parameter></paramdef>
+ <paramdef>const char *<parameter>path</parameter></paramdef>
+ <paramdef>const char *<parameter>interface</parameter></paramdef>
+ <paramdef>const char *<parameter>member</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int sd_bus_message_new_method_return</funcdef>
+ <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+ <paramdef>sd_bus_message **<parameter>m</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>sd_bus_message_new_method_call()</function> function creates a new bus
+ message object that encapsulates a D-Bus method call, and returns it in the
+ <parameter>m</parameter> output parameter. The call will be made on the destination
+ <parameter>destination</parameter>, path <parameter>path</parameter>, on the interface
+ <parameter>interface</parameter>, member <parameter>member</parameter>.</para>
+
+ <para>Briefly, the <emphasis>destination</emphasis> is a dot-separated name that identifies a
+ service connected to the bus. The <emphasis>path</emphasis> is a slash-separated identifier of
+ an object within the destination that resembles a file system path. The meaning of this path is
+ defined by the destination. The <emphasis>interface</emphasis> is a dot-separated name that
+ resembles a Java interface name that identifies a group of methods and signals supported by the
+ object identified by path. Methods and signals are collectively called
+ <emphasis>members</emphasis> and are identified by a simple name composed of ASCII letters,
+ numbers, and underscores. See the <ulink
+ url="https://dbus.freedesktop.org/doc/dbus-tutorial.html#concepts">D-Bus Tutorial</ulink> for an
+ in-depth explanation.</para>
+
+ <para>The <parameter>destination</parameter> parameter may be <constant>NULL</constant>. The
+ <parameter>interface</parameter> parameter may be <constant>NULL</constant>, if the destination
+ has only a single member with the given name and there is no ambiguity if the interface name is
+ omitted.</para>
+
+ <para>The <function>sd_bus_message_new_method_call()</function> function creates a new bus
+ message object that is a reply to the method call <parameter>call</parameter> and returns it in
+ the <parameter>m</parameter> output parameter. The <parameter>call</parameter> parameter must be
+ a method call message. The sender of <parameter>call</parameter> is used as the destination.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>This function returns 0 if the message object was successfully created, and a negative
+ errno-style error code otherwise.</para>
+ </refsect1>
+
+ <refsect1 id='errors'>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The output parameter <parameter>m</parameter> is
+ <constant>NULL</constant>.</para>
+
+ <para>The <parameter>destination</parameter> parameter is non-null and is not a valid D-Bus
+ service name (<literal>org.somewhere.Something</literal>), the <parameter>path</parameter>
+ parameter is not a valid D-Bus path (<literal>/an/object/path</literal>), the
+ <parameter>interface</parameter> parameter is non-null and is not a valid D-Bus interface
+ name (<literal>an.interface.name</literal>), or the <parameter>member</parameter> parameter
+ is not a valid D-Bus member (<literal>Name</literal>).</para>
+
+ <para>The <parameter>call</parameter> parameter is not a method call object.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOTCONN</constant></term>
+
+ <listitem><para>The bus parameter <parameter>bus</parameter> is <constant>NULL</constant> or
+ the bus is not connected.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem>
+ <para>The <parameter>call</parameter> parameter is not sealed.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EOPNOTSUPP</constant></term>
+
+ <listitem>
+ <para>The <parameter>call</parameter> message does not have a cookie.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>Make a call to a D-Bus method that takes a single parameter</title>
+
+ <programlisting><xi:include href="print-unit-path.c" parse="text" /></programlisting>
+ <para>This defines a minimally useful program that will open a connection to the bus, create a
+ message object, send it, wait for the reply, and finally extract and print the answer. It does
+ error handling and proper memory management.</para>
+ </example>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_new_method_error.xml b/man/sd_bus_message_new_method_error.xml
new file mode 100644
index 0000000000..045c74f21a
--- /dev/null
+++ b/man/sd_bus_message_new_method_error.xml
@@ -0,0 +1,190 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refentry id="sd_bus_message_new_method_error"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_message_new_method_error</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_new_method_error</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_new_method_error</refname>
+ <refname>sd_bus_message_new_method_errorf</refname>
+ <refname>sd_bus_message_new_method_errno</refname>
+ <refname>sd_bus_message_new_method_errnof</refname>
+
+ <refpurpose>Create a an error reply for a method call</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int sd_bus_message_new_method_error</funcdef>
+ <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+ <paramdef>sd_bus_message **<parameter>m</parameter></paramdef>
+ <paramdef>const sd_bus_error *<parameter>e</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int sd_bus_message_new_method_errorf</funcdef>
+ <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+ <paramdef>sd_bus_message **<parameter>m</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>const char *<parameter>format</parameter></paramdef>
+ <paramdef>…</paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int sd_bus_message_new_method_errno</funcdef>
+ <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+ <paramdef>sd_bus_message **<parameter>m</parameter></paramdef>
+ <paramdef>int <parameter>error</parameter></paramdef>
+ <paramdef>const sd_bus_error *<parameter>p</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int sd_bus_message_new_method_errnof</funcdef>
+ <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+ <paramdef>sd_bus_message **<parameter>m</parameter></paramdef>
+ <paramdef>int <parameter>error</parameter></paramdef>
+ <paramdef>const char *<parameter>format</parameter></paramdef>
+ <paramdef>…</paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>sd_bus_message_new_method_error()</function> function creates
+ a new bus message object that is an error reply to the
+ <parameter>call</parameter> message, and returns it in the
+ <parameter>m</parameter> output parameter. The error information from error
+ <parameter>e</parameter> is appended: the <parameter>name</parameter> field of
+ <parameter>e</parameter> is used as the error identifier in the reply header (for
+ example an error name such as
+ <literal>org.freedesktop.DBus.Error.NotSupported</literal> or the equivalent
+ symbolic <constant>SD_BUS_ERROR_NOT_SUPPORTED</constant>), and the
+ <parameter>message</parameter> field is set as the human readable error message
+ string if present. The error <parameter>e</parameter> must have the
+ <parameter>name</parameter> field set, see
+ <citerefentry><refentrytitle>sd_bus_error_is_set</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para>
+
+ <para>The <function>sd_bus_message_new_method_errorf()</function> function
+ creates an error reply similarly to
+ <function>sd_bus_message_new_method_error()</function>, but instead of a ready
+ error structure, it takes an error identifier string <parameter>name</parameter>,
+ plus a <citerefentry
+ project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ format string <parameter>format</parameter> and corresponding arguments. An error
+ reply is sent with the error identifier <parameter>name</parameter> and the
+ formatted string as the message. <parameter>name</parameter> and
+ <parameter>format</parameter> must not be <constant>NULL</constant>.
+ </para>
+
+ <para>The <function>sd_bus_message_new_method_errno()</function> function creates
+ an error reply similarly to
+ <function>sd_bus_message_new_method_error()</function>, but in addition to the
+ error structure <parameter>p</parameter>, it takes an
+ <citerefentry><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ error value in parameter <parameter>error</parameter>. If the error
+ <parameter>p</parameter> is set (see
+ <citerefentry><refentrytitle>sd_bus_error_is_set</refentrytitle><manvolnum>3</manvolnum></citerefentry>),
+ it is used in the reply. Otherwise, <parameter>error</parameter> is translated to
+ an error identifier and used to create a new error structure using
+ <citerefentry><refentrytitle>sd_bus_error_set_errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ and that is used in the reply. (If <parameter>error</parameter> is zero, no error
+ is actually set, and an error reply with no information is created.)</para>
+
+ <para>The <function>sd_bus_message_new_method_errnof()</function> function
+ creates an error reply similarly to
+ <function>sd_bus_message_new_method_error()</function>. It takes an
+ <citerefentry><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ error value in parameter <parameter>error</parameter>, plus a <citerefentry
+ project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ format string <parameter>format</parameter> and corresponding arguments.
+ <literal>%m</literal> may be used in the format string to refer to the error
+ string corresponding to the specified errno code. The error message is initalized
+ using the error identifier generated from <constant>error</constant> and the
+ formatted string. (If <parameter>error</parameter> is zero, no error is actually
+ set, and an error reply with no information is created.)</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>These functions return 0 if the error reply was successfully created, and a
+ negative errno-style error code otherwise.</para>
+ </refsect1>
+
+ <refsect1 id='errors'>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The call message <parameter>call</parameter> or the output
+ parameter <parameter>m</parameter> are <constant>NULL</constant>.</para>
+
+ <para>Message <parameter>call</parameter> is not a method call
+ message.</para>
+
+ <para>The error <parameter>error</parameter> parameter to
+ <function>sd_bus_message_new_method_error</function> is not set, see
+ <citerefentry><refentrytitle>sd_bus_error_is_set</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem><para>Message <parameter>call</parameter> has been sealed.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOTCONN</constant></term>
+
+ <listitem><para>The bus to which message <parameter>call</parameter> is
+ attached is not connected.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_new_signal.xml b/man/sd_bus_message_new_signal.xml
new file mode 100644
index 0000000000..c9ed0bc4a4
--- /dev/null
+++ b/man/sd_bus_message_new_signal.xml
@@ -0,0 +1,120 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_new_signal"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_message_new_signal</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_new_signal</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_new_signal</refname>
+
+ <refpurpose>Create a signal message</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int sd_bus_message_new_signal</funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>sd_bus_message **<parameter>m</parameter></paramdef>
+ <paramdef>const char *<parameter>path</parameter></paramdef>
+ <paramdef>const char *<parameter>interface</parameter></paramdef>
+ <paramdef>const char *<parameter>member</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>sd_bus_message_new_signal()</function> function creates a new bus message
+ object that encapsulates a D-Bus signal, and returns it in the <parameter>m</parameter> output
+ parameter. The signal will be sent to path <parameter>path</parameter>, on the interface
+ <parameter>interface</parameter>, member <parameter>member</parameter>. When this message is
+ sent, no reply is expected. See
+ <citerefentry><refentrytitle>sd_bus_message_new_call</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ for a short description of the meaning of the <parameter>path</parameter>,
+ <parameter>interface</parameter>, and <parameter>member</parameter> parameters.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>This function returns 0 if the message object was successfully created, and a negative
+ errno-style error code otherwise.</para>
+ </refsect1>
+
+ <refsect1 id='errors'>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The output parameter <parameter>m</parameter> is
+ <constant>NULL</constant>.</para>
+
+ <para>The <parameter>path</parameter> parameter is not a valid D-Bus path
+ (<literal>/an/object/path</literal>), the <parameter>interface</parameter> parameter is not
+ a valid D-Bus interface name (<literal>an.interface.name</literal>), or the
+ <parameter>member</parameter> parameter is not a valid D-Bus member
+ (<literal>Name</literal>).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOTCONN</constant></term>
+
+ <listitem><para>The bus parameter <parameter>bus</parameter> is <constant>NULL</constant> or
+ the bus is not connected.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>Send a simple signal</title>
+
+ <programlisting><xi:include href="send-unit-files-changed.c" parse="text" /></programlisting>
+
+ <para>This function in systemd sources is used to emit the
+ <literal>UnitFilesChanged</literal> signal when the unit files have been changed.
+ </para>
+ </example>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_read.xml b/man/sd_bus_message_read.xml
new file mode 100644
index 0000000000..2815c01986
--- /dev/null
+++ b/man/sd_bus_message_read.xml
@@ -0,0 +1,232 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refentry id="sd_bus_message_read"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_message_read</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_read</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_read</refname>
+ <refname>sd_bus_message_readv</refname>
+
+ <refpurpose>Read a sequence of values from a message</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_read</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ <paramdef>char char *<parameter>types</parameter></paramdef>
+ <paramdef>...</paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_readv</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ <paramdef>char char *<parameter>types</parameter></paramdef>
+ <paramdef>va_list <parameter>ap</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_message_read()</function> reads a sequence of fields from
+ the D-Bus message object <parameter>m</parameter> and advances the read position
+ in the message. The type string <parameter>types</parameter> describes the types
+ of items expected in the message and the field arguments that follow. The type
+ string may be <constant>NULL</constant> or empty, in which case nothing is
+ read.</para>
+
+ <para>The type string is composed of the elements described in
+ <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ i.e. basic and container types. It must contain zero or more single "complete
+ types". The type string is <constant>NUL</constant>-terminated.</para>
+
+ <para>For each type specified in the type string, one or more arguments need to be specified
+ after the <parameter>types</parameter> parameter, in the same order. The arguments must be
+ pointers to appropriate types (a pointer to <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
+ 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
+ pointer, and also the pointer value that is written is only borrowed and the contents must be
+ copied if they are to be used after the end of the messages lifetime.</para>
+
+ <para>Each argument may also be <constant>NULL</constant>, in which case the value is read and
+ ignored.</para>
+
+ <table>
+ <title>Item type specifiers</title>
+
+ <tgroup cols='5'>
+ <colspec colname='specifier' />
+ <colspec colname='constant' />
+ <colspec colname='description' />
+ <colspec colname='type1' />
+ <colspec colname='type2' />
+ <thead>
+ <row>
+ <entry>Specifier</entry>
+ <entry>Constant</entry>
+ <entry>Description</entry>
+ <entry>Type of the first argument</entry>
+ <entry>Types of the subsequent arguments, if any</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <xi:include href="sd_bus_message_read_basic.xml" xpointer="xpointer(//table[@id='format-specifiers']//tbody/*)" />
+
+ <row>
+ <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><parameter>n</parameter> sets of arguments appropriate for the array element type</entry>
+ </row>
+
+ <row>
+ <entry><literal>v</literal></entry>
+ <entry><constant>SD_BUS_TYPE_VARIANT</constant></entry>
+ <entry>variant</entry>
+ <entry>signature string</entry>
+ <entry>arguments appropriate for the types specified by the signature</entry>
+ </row>
+
+ <row>
+ <entry><literal>(</literal></entry>
+ <entry><constant>SD_BUS_TYPE_STRUCT_BEGIN</constant></entry>
+ <entry>array start</entry>
+ <entry morerows="1" namest="type1" nameend="type2">arguments appropriate for the structure elements</entry>
+ </row>
+ <row>
+ <entry><literal>)</literal></entry>
+ <entry><constant>SD_BUS_TYPE_STRUCT_END</constant></entry>
+ <entry>array end</entry>
+ </row>
+
+ <row>
+ <entry><literal>{</literal></entry>
+ <entry><constant>SD_BUS_TYPE_DICT_ENTRY_BEGIN</constant></entry>
+ <entry>dictionary entry start</entry>
+ <entry morerows="1">arguments appropriate for the first type in the pair</entry>
+ <entry morerows="1">arguments appropriate for the second type in the pair</entry>
+ </row>
+ <row>
+ <entry><literal>}</literal></entry>
+ <entry><constant>SD_BUS_TYPE_DICT_ENTRY_END</constant></entry>
+ <entry>dictionary entry end</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>If objects of the specified types are not present at the current position
+ in the message, an error is returned.
+ </para>
+
+ <para>The <function>sd_bus_message_readv()</function> is equivalent to the
+ <function>sd_bus_message_read()</function>, except that it is called with a
+ <literal>va_list</literal> instead of a variable number of arguments. This
+ function does not call the <function>va_end()</function> macro. Because it
+ invokes the <function>va_arg()</function> macro, the value of
+ <parameter>ap</parameter> is undefined after the call.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>
+ On success, <function>sd_bus_message_read()</function> and
+ <function>sd_bus_message_readv()</function> return 0 or a positive integer. On
+ failure, they return a negative errno-style error code.
+ </para>
+ </refsect1>
+
+ <xi:include href="sd_bus_message_read_basic.xml" xpointer="errors" />
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>Read a single basic type (a 64-bit integer):
+ </para>
+
+ <programlisting>sd_bus_message *m;
+int64_t x;
+
+sd_bus_message_read(m, "x", &amp;x);</programlisting>
+
+ <para>Read all types of integers:</para>
+
+ <programlisting>uint8_t y;
+int16_t n;
+uint16_t q;
+int32_t i;
+uint32_t u;
+int32_t x;
+uint32_t t;
+double d;
+
+sd_bus_message_read(m, "ynqiuxtd", &amp;y, &amp;n, &amp;q, &amp;i, &amp;u, &amp;x, &amp;t, &amp;d);</programlisting>
+
+ <para>Read a structure composed of a string and a D-Bus path:</para>
+
+ <programlisting>const char *s, *p;
+
+sd_bus_message_read(m, "(so)", &amp;s, &amp;p);
+</programlisting>
+
+ <para>Read a variant, with the real type "gt" (signature, unsigned integer):
+ </para>
+
+ <programlisting>const char *s;
+uint64_t *v;
+
+sd_bus_message_read(m, "v", "gt", &amp;s, &amp;v);</programlisting>
+
+ <para>Read a dictionary containing three pairs of type {integer=>string}:
+ </para>
+
+ <programlisting>int i, j, k;
+const char *s, *t, *u;
+
+sd_bus_message_read(m, "a{is}", 3, &amp;i, &amp;s, &amp;j, &amp;t, &amp;k, &amp;u);
+ </programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_read_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_skip</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_read_array.xml b/man/sd_bus_message_read_array.xml
new file mode 100644
index 0000000000..31481272bb
--- /dev/null
+++ b/man/sd_bus_message_read_array.xml
@@ -0,0 +1,108 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_read_array">
+
+ <refentryinfo>
+ <title>sd_bus_message_read_array</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_read_array</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_read_array</refname>
+
+ <refpurpose>Access an array of elements in a message</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_read_array</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ <paramdef>char <parameter>type</parameter></paramdef>
+ <paramdef>const void **<parameter>ptr</parameter></paramdef>
+ <paramdef>size_t *<parameter>size</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_message_read_array()</function> gives access to an element array in
+ message <parameter>m</parameter>. The "read pointer" in the message must be right before an
+ array of type <parameter>type</parameter>. As a special case, <parameter>type</parameter> may be
+ <constant>NUL</constant>, in which case any type is acceptable. A pointer to the array data is
+ returned in the parameter <parameter>ptr</parameter> and the size of array data (in bytes) is
+ returned in the parameter <parameter>size</parameter>. If <parameter>size</parameter> is 0, a
+ valid non-null pointer will be returned, but it may not be dereferenced. The data is aligned as
+ 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>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>
+ On success, <function>sd_bus_message_read_array()</function> returns 0 or
+ a positive integer. On failure, it returns a negative errno-style error
+ code.
+ </para>
+ </refsect1>
+
+ <refsect1 id='errors'>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <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>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EOPNOTSUPP</constant></term>
+
+ <listitem><para>The byte order in the message is different than native byte
+ order.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem><para>The message is not sealed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EBADMSG</constant></term>
+
+ <listitem><para>The message cannot be parsed.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_read_basic.xml b/man/sd_bus_message_read_basic.xml
index c0e04667ca..774d3fcf87 100644
--- a/man/sd_bus_message_read_basic.xml
+++ b/man/sd_bus_message_read_basic.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -52,18 +52,130 @@
</para>
<para>
- If <parameter>p</parameter> is not NULL, 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 <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 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.
+ 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
+ <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
+ 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
+ types.
</para>
+ <table id='format-specifiers'>
+ <title>Item type specifiers</title>
+
+ <tgroup cols='4'>
+ <colspec colname='specifier' />
+ <colspec colname='constant' />
+ <colspec colname='description' />
+ <colspec colname='ctype' />
+ <thead>
+ <row>
+ <entry>Specifier</entry>
+ <entry>Constant</entry>
+ <entry>Description</entry>
+ <entry>Expected C Type</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>y</literal></entry>
+ <entry><constant>SD_BUS_TYPE_BYTE</constant></entry>
+ <entry>unsigned integer</entry>
+ <entry>uint8_t *</entry>
+ </row>
+
+ <row>
+ <entry><literal>b</literal></entry>
+ <entry><constant>SD_BUS_TYPE_BOOLEAN</constant></entry>
+ <entry>boolean</entry>
+ <entry>int *</entry>
+ </row>
+
+ <row>
+ <entry><literal>n</literal></entry>
+ <entry><constant>SD_BUS_TYPE_INT16</constant></entry>
+ <entry>signed integer</entry>
+ <entry>int16_t *</entry>
+ </row>
+
+ <row>
+ <entry><literal>q</literal></entry>
+ <entry><constant>SD_BUS_TYPE_UINT16</constant></entry>
+ <entry>unsigned integer</entry>
+ <entry>uint16_t *</entry>
+ </row>
+
+ <row>
+ <entry><literal>i</literal></entry>
+ <entry><constant>SD_BUS_TYPE_INT32</constant></entry>
+ <entry>signed integer</entry>
+ <entry>int32_t *</entry>
+ </row>
+
+ <row>
+ <entry><literal>u</literal></entry>
+ <entry><constant>SD_BUS_TYPE_UINT32</constant></entry>
+ <entry>unsigned integer</entry>
+ <entry>uint32_t *</entry>
+ </row>
+
+ <row>
+ <entry><literal>x</literal></entry>
+ <entry><constant>SD_BUS_TYPE_INT64</constant></entry>
+ <entry>signed integer</entry>
+ <entry>int64_t *</entry>
+ </row>
+
+ <row>
+ <entry><literal>t</literal></entry>
+ <entry><constant>SD_BUS_TYPE_UINT64</constant></entry>
+ <entry>unsigned integer</entry>
+ <entry>uint64_t *</entry>
+ </row>
+
+ <row>
+ <entry><literal>d</literal></entry>
+ <entry><constant>SD_BUS_TYPE_DOUBLE</constant></entry>
+ <entry>floating-point</entry>
+ <entry>double *</entry>
+ </row>
+
+ <row>
+ <entry><literal>s</literal></entry>
+ <entry><constant>SD_BUS_TYPE_STRING</constant></entry>
+ <entry>Unicode string</entry>
+ <entry>const char **</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>
+ </row>
+
+ <row>
+ <entry><literal>g</literal></entry>
+ <entry><constant>SD_BUS_TYPE_SIGNATURE</constant></entry>
+ <entry>signature</entry>
+ <entry>const char **</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>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
<para>
If there is no object of the specified type at the current position in the
message, an error is returned.
@@ -80,12 +192,43 @@
</para>
</refsect1>
+ <refsect1 id='errors'>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>Specified type string is invalid or the message parameter is
+ <constant>NULL</constant>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENXIO</constant></term>
+
+ <listitem><para>The message does not contain the specified type at current
+ position.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EBADMSG</constant></term>
+
+ <listitem><para>The message cannot be parsed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_skip</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/sd_bus_message_rewind.xml b/man/sd_bus_message_rewind.xml
new file mode 100644
index 0000000000..c420c2f1d5
--- /dev/null
+++ b/man/sd_bus_message_rewind.xml
@@ -0,0 +1,88 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_rewind"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_message_rewind</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_rewind</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_rewind</refname>
+
+ <refpurpose>Return to begining of message or current container</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_rewind</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ <paramdef>int <parameter>complete</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_message_rewind()</function> moves the "read pointer" in the message
+ <parameter>m</parameter> to either the begining of the message (if
+ <parameter>complete</parameter> is true) or to the beginning of the currently open container. If
+ no container is open, <parameter>complete</parameter> has no effect.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>
+ On success, this function returns 0 or a positive integer. The value is zero if the current
+ container or whole message in case no container is open is empty, and positive otherwise. On
+ failure, it returns a negative errno-style error code.
+ </para>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The <parameter>m</parameter> parameter is <constant>NULL</constant>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem><para>The message <parameter>m</parameter> has not been sealed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_set_destination.xml b/man/sd_bus_message_set_destination.xml
index 626272b0aa..e8a1206ae8 100644
--- a/man/sd_bus_message_set_destination.xml
+++ b/man/sd_bus_message_set_destination.xml
@@ -6,7 +6,6 @@
-->
<refentry id="sd_bus_message_set_destination" xmlns:xi="http://www.w3.org/2001/XInclude">
-
<refentryinfo>
<title>sd_bus_message_set_destination</title>
<productname>systemd</productname>
@@ -19,8 +18,14 @@
<refnamediv>
<refname>sd_bus_message_set_destination</refname>
+ <refname>sd_bus_message_get_destination</refname>
+ <refname>sd_bus_message_get_path</refname>
+ <refname>sd_bus_message_get_interface</refname>
+ <refname>sd_bus_message_get_member</refname>
<refname>sd_bus_message_set_sender</refname>
- <refpurpose>Set the destination or sender service name of a bus message</refpurpose>
+ <refname>sd_bus_message_get_sender</refname>
+
+ <refpurpose>Set and query bus message addressing information</refpurpose>
</refnamediv>
<refsynopsisdiv>
@@ -34,30 +39,76 @@
</funcprototype>
<funcprototype>
+ <funcdef>const char* <function>sd_bus_message_get_destination</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char* <function>sd_bus_message_get_path</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char* <function>sd_bus_message_get_interface</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char* <function>sd_bus_message_get_member</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>int <function>sd_bus_message_set_sender</function></funcdef>
<paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
<paramdef>const char *<parameter>sender</parameter></paramdef>
</funcprototype>
+
+ <funcprototype>
+ <funcdef>const char* <function>sd_bus_message_get_sender</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ </funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para><function>sd_bus_message_set_destination()</function> sets the destination service name for the specified bus
- message object. The specified name must be a valid unique or well-known service name.</para>
+ <para><function>sd_bus_message_set_destination()</function> sets the destination service name
+ for the specified bus message object. The specified name must be a valid unique or well-known
+ service name.</para>
+
+ <para><function>sd_bus_message_get_destination()</function>,
+ <function>sd_bus_message_get_path()</function>,
+ <function>sd_bus_message_get_interface()</function>, and
+ <function>sd_bus_message_get_member()</function> return the destination, path, interface, and
+ member fields from <parameter>message</parameter> header. The return value will be
+ <constant>NULL</constant> is <parameter>message</parameter> is <constant>NULL</constant> or the
+ message is of a type that doesn't use those fields or the message doesn't have them set. See
+ <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ and
+ <citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ for more discussion of those values.</para>
+
<para><function>sd_bus_message_set_sender()</function> sets the sender service name for the specified bus message
object. The specified name must be a valid unique or well-known service name. This function is useful only for
messages to send on direct connections as for connections to bus brokers the broker will fill in the destination
field anyway, and the sender field set by original sender is ignored.</para>
+
+ <para><function>sd_bus_message_get_sender()</function> returns the sender field from
+ <parameter>message</parameter>.</para>
+
+ <para>When a string is returned, it is a pointer to internal storage, and may not be modified or
+ freed. It is only valid as long as the <parameter>message</parameter> remains referenced and
+ this field hasn't been changed by a different call.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
- <para>On success, these calls return 0 or a positive integer. On failure, these calls return a negative errno-style
- error code.</para>
+ <para>On success, these calls return 0 or a positive integer. On failure, these calls return a
+ negative errno-style error code.</para>
</refsect1>
<refsect1>
@@ -69,13 +120,16 @@
<varlistentry>
<term><constant>-EINVAL</constant></term>
- <listitem><para>A specified parameter is invalid.</para></listitem>
+ <listitem><para>The <parameter>message</parameter> parameter or the output parameter are
+ <constant>NULL</constant>.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-EPERM</constant></term>
- <listitem><para>The message is already sealed.</para></listitem>
+ <listitem><para>For <function>sd_bus_message_set_destination</function> or
+ <function>sd_bus_message_set_sender</function>, the message is already
+ sealed.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/sd_bus_message_set_expect_reply.xml b/man/sd_bus_message_set_expect_reply.xml
new file mode 100644
index 0000000000..2dfabca18f
--- /dev/null
+++ b/man/sd_bus_message_set_expect_reply.xml
@@ -0,0 +1,127 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_set_expect_reply" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_message_set_expect_reply</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_set_expect_reply</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_set_expect_reply</refname>
+ <refname>sd_bus_message_get_expect_reply</refname>
+ <refname>sd_bus_message_set_auto_start</refname>
+ <refname>sd_bus_message_get_auto_start</refname>
+
+ <refpurpose>Set and query bus message metadata</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_set_expect_reply</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ <paramdef>int <parameter>b</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_get_expect_reply</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_set_auto_start</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ <paramdef>int <parameter>b</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_get_auto_start</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_message_set_expect_reply()</function> sets or clears the
+ <constant>NO_REPLY_EXPECTED</constant> flag on the message <parameter>m</parameter>. This flag
+ matters only for method call messages and is used to specify that no method return or error
+ reply is expected. It is ignored for other types. Thus, for a method call message, calling
+ <programlisting>sd_bus_message_set_expect_reply(…, 0)</programlisting> sets the flag and
+ suppresses the reply.</para>
+
+ <para><function>sd_bus_message_get_expect_reply()</function> checks if the
+ <constant>NO_REPLY_EXPECTED</constant> flag is set on the message <parameter>m</parameter>. It
+ will return positive if it is not set, and zero if it is.</para>
+
+ <para><function>sd_bus_message_set_auto_start()</function> sets or clears the
+ <constant>NO_AUTO_START</constant> flag on the message <parameter>m</parameter>. When the flag
+ is set the bus must not launch an owner for the destination name in response to this message.
+ Calling
+ <programlisting>sd_bus_message_set_auto_start(…, 0)</programlisting> sets the flag.
+ </para>
+
+ <para><function>sd_bus_message_get_auto_start()</function> checks if the
+ <constant>NO_AUTO_START</constant> flag is set on the message <parameter>m</parameter>. It
+ will return positive if it is not set, and zero if it is.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, these functions return 0 or a positive integer. On failure, they return a
+ negative errno-style error code.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The <parameter>message</parameter> parameter is
+ <constant>NULL</constant>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem><para>The message <parameter>message</parameter> is sealed
+ when trying to set a flag.</para>
+
+ <para>The message <parameter>message</parameter> has wrong
+ type.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </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_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_skip.xml b/man/sd_bus_message_skip.xml
new file mode 100644
index 0000000000..384a791494
--- /dev/null
+++ b/man/sd_bus_message_skip.xml
@@ -0,0 +1,108 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_skip" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <refentryinfo>
+ <title>sd_bus_message_skip</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_skip</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_skip</refname>
+
+ <refpurpose>Skip elements in a bus message</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_message_skip</function></funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ <paramdef>const char* <parameter>types</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_message_skip()</function> is somewhat similar to
+ <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ but instead of reading the contents of the message, it only moves the "read pointer". Subsequent
+ read operations will read the elements that are after the elements that were skipped.</para>
+
+ <para>The <parameter>types</parameter> argument has the same meaning as in
+ <function>sd_bus_message_read()</function>. It may also be <constant>NULL</constant>, to skip a
+ single element of any type.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_bus_message_skip()</function> returns 0 or a positive integer. On
+ failure, it returns a negative errno-style error code.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The <parameter>m</parameter> parameter is
+ <constant>NULL</constant>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EBADMSG</constant></term>
+
+ <listitem><para>The message cannot be parsed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem><para>The message is not sealed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENXIO</constant></term>
+
+ <listitem><para>The message end has been reached and the requested elements cannot be read.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_read_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_verify_type.xml b/man/sd_bus_message_verify_type.xml
new file mode 100644
index 0000000000..fcb9f19e15
--- /dev/null
+++ b/man/sd_bus_message_verify_type.xml
@@ -0,0 +1,99 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_verify_type" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <refentryinfo>
+ <title>sd_bus_message_verify_type</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_verify_type</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_verify_type</refname>
+
+ <refpurpose>Check if the message has specified type at the current location</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int sd_bus_message_verify_type</funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ <paramdef>char <parameter>type</parameter></paramdef>
+ <paramdef>const char* <parameter>contents</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_message_verify_type()</function> checks if the complete type at the
+ current location in the message <parameter>m</parameter> matches the specified
+ <parameter>type</parameter> and <parameter>contents</parameter>. If non-zero, parameter
+ <parameter>type</parameter> must be one of the types specified in
+ <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+ If non-null, parameter <parameter>contents</parameter> must be a valid sequence of complete
+ types. If both <parameter>type</parameter> and <parameter>contents</parameter> are specified
+ <parameter>type</parameter> must be a container type.</para>
+
+ <para>If <parameter>type</parameter> is specified, the type in the message must match. If
+ <parameter>contents</parameter> is specified, the type in the message must be a container type
+ with this signature.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, this call returns true if the type matches and zero if not (the message
+ <parameter>m</parameter> contains different data or the end of the message has been reached). On
+ failure, it returns a negative errno-style error code.</para>
+ </refsect1>
+
+ <refsect1 id='errors'>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para><parameter>m</parameter> or both <parameter>type</parameter> and
+ <parameter>contents</parameter> are <constant>NULL</constant>.</para>
+
+ <para>Arguments do not satisfy other contraints listed above.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem><para>Message <parameter>m</parameter> is not sealed.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_negotiate_fds.xml b/man/sd_bus_negotiate_fds.xml
index a106bfb16e..c8d520ecd5 100644
--- a/man/sd_bus_negotiate_fds.xml
+++ b/man/sd_bus_negotiate_fds.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_bus_new.xml b/man/sd_bus_new.xml
index 207291408e..1bc011d70a 100644
--- a/man/sd_bus_new.xml
+++ b/man/sd_bus_new.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -23,6 +23,8 @@
<refname>sd_bus_ref</refname>
<refname>sd_bus_unref</refname>
<refname>sd_bus_unrefp</refname>
+ <refname>sd_bus_flush_close_unref</refname>
+ <refname>sd_bus_flush_close_unrefp</refname>
<refpurpose>Create a new bus object and create or destroy references to it</refpurpose>
</refnamediv>
@@ -48,7 +50,17 @@
<funcprototype>
<funcdef>void <function>sd_bus_unrefp</function></funcdef>
- <paramdef>sd_bus **<parameter>bus</parameter></paramdef>
+ <paramdef>sd_bus **<parameter>busp</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus *<function>sd_bus_flush_close_unref</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>sd_bus_flush_close_unrefp</function></funcdef>
+ <paramdef>sd_bus **<parameter>busp</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
@@ -96,19 +108,33 @@
block is left:</para>
<programlisting>{
- __attribute__((cleanup(sd_bus_unrefp)) sd_bus *bus = NULL;
- int r;
- …
- r = sd_bus_default(&amp;bus);
- if (r &lt; 0)
- fprintf(stderr, "Failed to allocate bus: %s\n", strerror(-r));
- …
+ __attribute__((cleanup(sd_bus_unrefp)) sd_bus *bus = NULL;
+ int r;
+ …
+ r = sd_bus_default(&amp;bus);
+ if (r &lt; 0)
+ fprintf(stderr, "Failed to allocate bus: %s\n", strerror(-r));
+ …
}</programlisting>
- <para><function>sd_bus_ref()</function>,
- <function>sd_bus_unref()</function> and
- <function>sd_bus_unrefp()</function> execute no operation if the
- passed in bus object is <constant>NULL</constant>.</para>
+ <para><function>sd_bus_ref()</function> and <function>sd_bus_unref()</function>
+ execute no operation if the passed in bus object address is
+ <constant>NULL</constant>. <function>sd_bus_unrefp()</function> will first
+ dereference its argument, which must not be <constant>NULL</constant>, and will
+ execute no operation if <emphasis>that</emphasis> is <constant>NULL</constant>.
+ </para>
+
+ <para><function>sd_bus_flush_close_unref()</function> is similar to <function>sd_bus_unref()</function>, but first
+ executes <citerefentry><refentrytitle>sd_bus_flush</refentrytitle><manvolnum>3</manvolnum></citerefentry> as well
+ as <citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>, ensuring that
+ any pending messages are properly flushed out before the reference to the connection is dropped and possibly the
+ object freed. This call is particularly useful immediately before exiting from a program as it ensures that any
+ pending outgoing messages are written out, and unprocessed but queued incoming messages released before the
+ connection is terminated and released.</para>
+
+ <para><function>sd_bus_flush_close_unrefp()</function> is similar to
+ <function>sd_bus_flush_close_unref()</function>, but may be used in GCC's and LLVM's Clean-up Variable Attribute,
+ see above.</para>
</refsect1>
<refsect1>
@@ -121,7 +147,7 @@
<para><function>sd_bus_ref()</function> always returns the argument.
</para>
- <para><function>sd_bus_unref()</function> always returns
+ <para><function>sd_bus_unref()</function> and <function>sd_bus_flush_close_unref()</function> always return
<constant>NULL</constant>.</para>
</refsect1>
@@ -150,7 +176,8 @@
<citerefentry><refentrytitle>sd_bus_default_user</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_default_system</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_open_user</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_open_system</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>sd_bus_open_system</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/sd_bus_path_encode.xml b/man/sd_bus_path_encode.xml
index 86e255e573..03130a697b 100644
--- a/man/sd_bus_path_encode.xml
+++ b/man/sd_bus_path_encode.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -6,7 +6,7 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="sd_bus_path_encode">
+<refentry id="sd_bus_path_encode" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_bus_path_encode</title>
@@ -80,7 +80,7 @@
always be <constant>NUL</constant>-terminated. The returned string
will be the concatenation of the bus path prefix plus an escaped
version of the external identifier string. This operation may be
- reversed with <function>sd_bus_decode()</function>. It is
+ reversed with <function>sd_bus_path_decode()</function>. It is
recommended to only use external identifiers that generally
require little escaping to be turned into valid bus path
identifiers (for example, by sticking to a 7-bit ASCII character
@@ -141,15 +141,7 @@
by the caller.</para>
</refsect1>
- <refsect1>
- <title>Notes</title>
-
- <para><function>sd_bus_path_encode()</function> and
- <function>sd_bus_path_decode()</function> are available as a
- shared library, which can be compiled and linked to with the
- <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- file.</para>
- </refsect1>
+ <xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>See Also</title>
diff --git a/man/sd_bus_process.xml b/man/sd_bus_process.xml
index 33afa0a3b5..66f22c29a6 100644
--- a/man/sd_bus_process.xml
+++ b/man/sd_bus_process.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -8,7 +8,7 @@
Copyright © 2016 Julian Orth
-->
-<refentry id="sd_bus_process">
+<refentry id="sd_bus_process" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_bus_process</title>
@@ -33,7 +33,7 @@
<funcprototype>
<funcdef>int <function>sd_bus_process</function></funcdef>
<paramdef>sd_bus *<parameter>bus</parameter></paramdef>
- <paramdef>sd_bus_message **<parameter>r</parameter></paramdef>
+ <paramdef>sd_bus_message **<parameter>ret</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
@@ -41,49 +41,92 @@
<refsect1>
<title>Description</title>
- <para>
- <function>sd_bus_process()</function> drives the connection between the
- message bus and the client. That is, it handles connecting,
- authentication, and message processing. It should be called in a loop
- until no further progress can be made or an error occurs.
- </para>
-
- <para>
- Once no further progress can be made,
- <citerefentry><refentrytitle>sd_bus_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- should be called. Alternatively the user can wait for incoming data on
- the file descriptor returned by
- <citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ <para><function>sd_bus_process()</function> drives the connection between the client and the message bus. That is,
+ it handles connecting, authentication, and message processing. When invoked pending I/O work is executed, and
+ queued incoming messages are dispatched to registered callbacks. Each time it is invoked a single operation is
+ executed. It returns zero when no operations were pending and positive if a message was processed. When zero is
+ returned the caller should synchronously poll for I/O events before calling into
+ <function>sd_bus_process()</function> again. For that either user the simple, synchronous
+ <citerefentry><refentrytitle>sd_bus_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry> call, or hook up
+ the bus connection object to an external or manual event loop using
+ <citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
</para>
- <para>
- <function>sd_bus_process</function> processes at most one incoming
- message per call. If the parameter <parameter>r</parameter> is not NULL
- and the call processed a message, <code>*r</code> is set to this message.
- The caller owns a reference to this message and should call
- <citerefentry><refentrytitle>sd_bus_message_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- when the message is no longer needed. If <parameter>r</parameter> is not
- NULL, progress was made, but no message was processed, <code>*r</code> is
- set to NULL.
- </para>
+ <para><function>sd_bus_process()</function> processes at most one incoming message per call. If the parameter
+ <parameter>ret</parameter> is not <constant>NULL</constant> and the call processed a message,
+ <parameter>*ret</parameter> is set to this message. The caller owns a reference to this message and should call
+ <citerefentry><refentrytitle>sd_bus_message_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> when the
+ message is no longer needed. If <parameter>ret</parameter> is not NULL, progress was made, but no message was
+ processed, <parameter>*ret</parameter> is set to <constant>NULL</constant>.</para>
+
+ <para>If a the bus object is connected to an
+ <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry> event loop (with
+ <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>), it is not
+ necessary to call <function>sd_bus_process()</function> directly as it is invoked automatically when
+ necessary.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
- <para>
- If progress was made, a positive integer is returned. If no progress was
- made, 0 is returned. If an error occurs, a negative errno-style error code
- is returned.
- </para>
+ <para>If progress was made, a positive integer is returned. If no progress was made, 0 is returned. If an error
+ occurs, a negative <varname>errno</varname>-style error code is returned.</para>
</refsect1>
<refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>An invalid bus object was passed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ECHILD</constant></term>
+
+ <listitem><para>The bus connection was allocated in a parent process and is being reused in a child process
+ after <function>fork()</function>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOTCONN</constant></term>
+
+ <listitem><para>The bus connection has been terminated already.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ECONNRESET</constant></term>
+
+ <listitem><para>The bus connection has been terminated just now.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EBUSY</constant></term>
+
+ <listitem><para>This function is already being called, i.e. <function>sd_bus_process()</function> has been
+ called from a callback function that itself was called by
+ <function>sd_bus_process()</function>.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </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_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/sd_bus_reply_method_error.xml b/man/sd_bus_reply_method_error.xml
new file mode 100644
index 0000000000..bbb916dc32
--- /dev/null
+++ b/man/sd_bus_reply_method_error.xml
@@ -0,0 +1,161 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refentry id="sd_bus_reply_method_error"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_reply_method_error</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_reply_method_error</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_reply_method_error</refname>
+ <refname>sd_bus_reply_method_errorf</refname>
+ <refname>sd_bus_reply_method_errno</refname>
+ <refname>sd_bus_reply_method_errnof</refname>
+
+ <refpurpose>Reply with an error to a method call</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int sd_bus_reply_method_error</funcdef>
+ <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+ <paramdef>const sd_bus_error *<parameter>e</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int sd_bus_reply_method_errorf</funcdef>
+ <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>const char *<parameter>format</parameter></paramdef>
+ <paramdef>…</paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int sd_bus_reply_method_errno</funcdef>
+ <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+ <paramdef>int <parameter>error</parameter></paramdef>
+ <paramdef>const sd_bus_error *<parameter>p</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int sd_bus_reply_method_errnof</funcdef>
+ <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+ <paramdef>int <parameter>error</parameter></paramdef>
+ <paramdef>const char *<parameter>format</parameter></paramdef>
+ <paramdef>…</paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>sd_bus_reply_method_error()</function> function sends an
+ error reply to the <parameter>call</parameter> message. The error structure
+ <parameter>e</parameter> specifies the error to send, and is used as described in
+ <citerefentry><refentrytitle>sd_bus_message_new_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ If no reply is expected to <parameter>call</parameter>, this function returns
+ success without sending reply.</para>
+
+ <para>The <function>sd_bus_reply_method_errorf()</function> is to
+ <function>sd_bus_reply_method_error()</function> what
+ <function>sd_bus_message_new_method_errorf()</function> is to
+ <function>sd_bus_message_new_method_error()</function>.</para>
+
+ <para>The <function>sd_bus_reply_method_errno()</function> is to
+ <function>sd_bus_reply_method_error()</function> what
+ <function>sd_bus_message_new_method_errno()</function> is to
+ <function>sd_bus_message_new_method_error()</function>.</para>
+
+ <para>The <function>sd_bus_reply_method_errnof()</function> is to
+ <function>sd_bus_reply_method_error()</function> what
+ <function>sd_bus_message_new_method_errnof()</function> is to
+ <function>sd_bus_message_new_method_error()</function>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>These functions return 0 if the error reply was successfully sent or if
+ none was expected, and a negative errno-style error code otherwise.</para>
+ </refsect1>
+
+ <refsect1 id='errors'>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The call message <parameter>call</parameter> is
+ <constant>NULL</constant>.</para>
+
+ <para>Message <parameter>call</parameter> is not a method call message.
+ </para>
+
+ <para>Message <parameter>call</parameter> is not attached to a bus.</para>
+
+ <para>The error <parameter>error</parameter> parameter to
+ <function>sd_bus_reply_method_error</function> is not set, see
+ <citerefentry><refentrytitle>sd_bus_error_is_set</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem><para>Message <parameter>call</parameter> has been sealed.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOTCONN</constant></term>
+
+ <listitem><para>The bus to which message <parameter>call</parameter> is
+ attached is not connected.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>In addition, any error message returned by
+ <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ may be returned.</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>,
+ <citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_request_name.xml b/man/sd_bus_request_name.xml
index 54a14c877c..3c98b60c6a 100644
--- a/man/sd_bus_request_name.xml
+++ b/man/sd_bus_request_name.xml
@@ -6,7 +6,8 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="sd_bus_request_name">
+<refentry id="sd_bus_request_name"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_bus_request_name</title>
@@ -193,13 +194,7 @@
</variablelist>
</refsect1>
- <refsect1>
- <title>Notes</title>
-
- <para>The <function>sd_bus_acquire_name()</function> and the other interfaces described here are available as a
- shared library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry
- project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
- </refsect1>
+ <xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>See Also</title>
diff --git a/man/sd_bus_set_close_on_exit.xml b/man/sd_bus_set_close_on_exit.xml
new file mode 100644
index 0000000000..dc4f6a3e15
--- /dev/null
+++ b/man/sd_bus_set_close_on_exit.xml
@@ -0,0 +1,105 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refentry id="sd_bus_set_close_on_exit"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_set_close_on_exit</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_set_close_on_exit</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_set_close_on_exit</refname>
+ <refname>sd_bus_get_close_on_exit</refname>
+
+ <refpurpose>Control whether to close the bus connection during the event loop exit phase</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_set_close_on_exit</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>int <parameter>b</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_get_close_on_exit</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_set_close_on_exit()</function> may be used to enable or disable whether the bus connection
+ is automatically flushed (as in
+ <citerefentry><refentrytitle>sd_bus_flush</refentrytitle><manvolnum>3</manvolnum></citerefentry>) and closed (as in
+ <citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>) during the exit
+ phase of the event loop. This logic only applies to bus connections that are attached to an
+ <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry> event loop, see
+ <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>. By default
+ this mechanism is enabled and makes sure that any pending messages that have not been written to the bus connection
+ are written out when the event loop is shutting down. In some cases this behaviour is not desirable, for example
+ when the bus connection shall remain usable until after the event loop exited. If <parameter>b</parameter> is
+ true, the feature is enabled (which is the default), otherwise disabled.</para>
+
+ <para><function>sd_bus_get_close_on_exit()</function> may be used to query the current setting of this feature. It
+ returns zero when the feature is disabled, and positive if enabled.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_bus_set_close_on_exit()</function> returns 0 or a positive integer. On failure, it returns a negative errno-style
+ error code.</para>
+
+ <para><function>sd_bus_get_close_on_exit()</function> returns 0 if the feature is currently turned off or a
+ positive integer if it is on. On failure, it returns a negative errno-style error code.</para>
+ </refsect1>
+
+ <refsect1>
+ <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>
+ </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_flush</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_set_connected_signal.xml b/man/sd_bus_set_connected_signal.xml
index b392e0f4c4..32fc630cfe 100644
--- a/man/sd_bus_set_connected_signal.xml
+++ b/man/sd_bus_set_connected_signal.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -6,7 +6,8 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="sd_bus_set_connected_signal">
+<refentry id="sd_bus_set_connected_signal"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_bus_set_connected_signal</title>
@@ -94,13 +95,7 @@
</variablelist>
</refsect1>
- <refsect1>
- <title>Notes</title>
-
- <para><function>sd_bus_set_connected_signal()</function> and <function>sd_bus_get_connected_signal()</function> are available as
- a shared library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry
- project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
- </refsect1>
+ <xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>See Also</title>
diff --git a/man/sd_bus_set_description.xml b/man/sd_bus_set_description.xml
new file mode 100644
index 0000000000..af02c20dd8
--- /dev/null
+++ b/man/sd_bus_set_description.xml
@@ -0,0 +1,188 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refentry id="sd_bus_set_description" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_set_description</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_set_description</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_set_description</refname>
+ <refname>sd_bus_get_description</refname>
+ <refname>sd_bus_set_anonymous</refname>
+ <refname>sd_bus_set_trusted</refname>
+ <refname>sd_bus_set_allow_interactive_authorization</refname>
+ <refname>sd_bus_get_allow_interactive_authorization</refname>
+
+ <refpurpose>Set or query properties of a bus object</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_set_description</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>const char *<parameter>description</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_get_description</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>const char **<parameter>description</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_set_anonymous</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>int <parameter>b</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_set_trusted</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>int <parameter>b</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_set_allow_interactive_authorization</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>int <parameter>b</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_get_allow_interactive_authorization</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_set_description()</function> sets the description string
+ that is used in logging to the specified string. The string is copied internally
+ and freed when the bus object is deallocated. The
+ <parameter>description</parameter> argument may be <constant>NULL</constant>, in
+ which case the description is unset. This function must be called before the bus
+ has been started.</para>
+
+ <para><function>sd_bus_get_description()</function> returns a description string
+ in <parameter>description</parameter>. This string may have been previously set
+ with <function>sd_bus_set_description()</function> or
+ <citerefentry><refentrytitle>sd_bus_open_with_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ or similar. If not set this way, a default string like <literal>system</literal>
+ or <literal>user</literal> will be returned for the system or user buses,
+ and <constant>NULL</constant> otherwise.</para>
+
+ <para><function>sd_bus_set_anonymous()</function> enables or disables "anonymous
+ authentication", i.e. lack of authentication, of the bus peer. This function must
+ be called before the bus has been started. See the <ulink
+ url="view-source:https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms">Authentication
+ Mechanisms</ulink> section of the D-Bus specification for details.</para>
+
+ <para><function>sd_bus_set_trusted()</function> sets the "trusted" state on the
+ <parameter>bus</parameter> object. If true, all connections on the bus are
+ trusted and access to all privileged and unprivileged methods is granted. This
+ function must be called before the bus has been started.</para>
+
+ <para><function>sd_bus_set_allow_interactive_authorization()</function>
+ enables or disables interactive authorization for method calls. If true,
+ messages are marked with the
+ <constant>ALLOW_INTERACTIVE_AUTHORIZATION</constant> flag specified by the
+ <ulink
+ url="view-source:https://dbus.freedesktop.org/doc/dbus-specification.html">D-Bus</ulink>
+ specification, informing the receiving side that the caller is prepared to
+ wait for interactive authorization, which might take a considerable time to
+ complete. If this flag is set, the user may be queried for passwords or
+ confirmation via <ulink
+ url="http://www.freedesktop.org/wiki/Software/polkit">polkit</ulink> or a
+ similar framework.</para>
+
+ <para><function>sd_bus_get_allow_interactive_authorization()</function> returns
+ true if interactive authorization is allowed and false if not.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, these functions return 0 or a positive integer. On failure,
+ they return a negative errno-style error code.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>An argument is invalid.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-ENOPKG</constant></term>
+
+ <listitem><para>The bus cannot be resolved.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EPERM</constant></term>
+
+ <listitem><para>The bus has already been started.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-ECHILD</constant></term>
+
+ <listitem><para>The bus was created in a different process.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_default_user</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_default_system</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_open_user</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_open_system</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_set_sender.xml b/man/sd_bus_set_sender.xml
index e35af282c0..556e72cefc 100644
--- a/man/sd_bus_set_sender.xml
+++ b/man/sd_bus_set_sender.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -6,7 +6,8 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="sd_bus_set_sender">
+<refentry id="sd_bus_set_sender"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_bus_set_sender</title>
@@ -89,13 +90,7 @@
</variablelist>
</refsect1>
- <refsect1>
- <title>Notes</title>
-
- <para><function>sd_bus_set_sender()</function> and <function>sd_bus_get_sender()</function> are available as
- a shared library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry
- project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
- </refsect1>
+ <xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>See Also</title>
diff --git a/man/sd_bus_set_watch_bind.xml b/man/sd_bus_set_watch_bind.xml
index d19f6b9212..129b98c5f3 100644
--- a/man/sd_bus_set_watch_bind.xml
+++ b/man/sd_bus_set_watch_bind.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -6,7 +6,8 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="sd_bus_set_watch_bind">
+<refentry id="sd_bus_set_watch_bind"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_bus_set_watch_bind</title>
@@ -100,13 +101,7 @@
</variablelist>
</refsect1>
- <refsect1>
- <title>Notes</title>
-
- <para><function>sd_bus_set_watch_bind()</function> and <function>sd_bus_get_watch_bind()</function> are available as
- a shared library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry
- project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
- </refsect1>
+ <xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>See Also</title>
diff --git a/man/sd_bus_slot_ref.xml b/man/sd_bus_slot_ref.xml
new file mode 100644
index 0000000000..c5f050635d
--- /dev/null
+++ b/man/sd_bus_slot_ref.xml
@@ -0,0 +1,107 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_slot_ref" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <refentryinfo>
+ <title>sd_bus_slot_ref</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_slot_ref</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_slot_ref</refname>
+ <refname>sd_bus_slot_unref</refname>
+ <refname>sd_bus_slot_unrefp</refname>
+ <refname>sd_bus_slot_get_bus</refname>
+
+ <refpurpose>Create and destroy references to a bus slot object</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>sd_bus_slot *<function>sd_bus_slot_ref</function></funcdef>
+ <paramdef>sd_bus_slot *<parameter>slot</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus_slot *<function>sd_bus_slot_unref</function></funcdef>
+ <paramdef>sd_bus_slot *<parameter>slot</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>sd_bus_slot_unrefp</function></funcdef>
+ <paramdef>sd_bus_slot **<parameter>slotp</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus *<function>sd_bus_slot_get_bus</function></funcdef>
+ <paramdef>sd_bus_slot *<parameter>m</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_slot_ref()</function> increases the reference counter of
+ <parameter>slot</parameter> by one.</para>
+
+ <para><function>sd_bus_slot_unref()</function> decreases the reference counter of
+ <parameter>slot</parameter> by one. Once the reference count has dropped to zero, slot object is
+ destroyed and cannot be used anymore, so further calls to <function>sd_bus_slot_ref()</function>
+ or <function>sd_bus_slot_unref()</function> are illegal.</para>
+
+ <para><function>sd_bus_slot_unrefp()</function> is similar to
+ <function>sd_bus_slot_unref()</function> but takes a pointer to a pointer to an
+ <type>sd_bus_slot</type> object. This call is useful in conjunction with GCC's and LLVM's <ulink
+ url="https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html">Clean-up Variable
+ Attribute</ulink>. See
+ <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ for an example how to use the cleanup attribute.</para>
+
+ <para><function>sd_bus_slot_ref()</function> and <function>sd_bus_slot_unref()</function>
+ execute no operation if the passed in bus object address is
+ <constant>NULL</constant>. <function>sd_bus_slot_unrefp()</function> will first dereference
+ its argument, which must not be <constant>NULL</constant>, and will execute no operation if
+ <emphasis>that</emphasis> is <constant>NULL</constant>.
+ </para>
+
+ <para><function>sd_bus_slot_get_bus()</function> returns the bus object that message
+ <parameter>slot</parameter> is attached to.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para><function>sd_bus_slot_ref()</function> always returns the argument.</para>
+
+ <para><function>sd_bus_slot_unref()</function> always returns <constant>NULL</constant>.</para>
+
+ <para><function>sd_bus_slot_get_bus()</function> always returns the bus object.</para>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_slot_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_call_method_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_slot_set_description.xml b/man/sd_bus_slot_set_description.xml
new file mode 100644
index 0000000000..4bcb1e3611
--- /dev/null
+++ b/man/sd_bus_slot_set_description.xml
@@ -0,0 +1,109 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_slot_set_description" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <refentryinfo>
+ <title>sd_bus_slot_set_description</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_slot_set_description</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_slot_set_description</refname>
+ <refname>sd_bus_slot_get_description</refname>
+
+ <refpurpose>Set or query the description of bus slot objects</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_slot_set_description</function></funcdef>
+ <paramdef>sd_bus_slot* <parameter>slot</parameter></paramdef>
+ <paramdef>const char *<parameter>description</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_slot_get_description</function></funcdef>
+ <paramdef>sd_bus_slot* <parameter>bus</parameter></paramdef>
+ <paramdef>const char **<parameter>description</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_slot_set_description()</function> sets the description string
+ that is used in logging to the specified string. The string is copied internally
+ and freed when the bus slot object is deallocated. The
+ <parameter>description</parameter> argument may be <constant>NULL</constant>, in
+ which case the description is unset.</para>
+
+ <para><function>sd_bus_slot_get_description()</function> returns a description string in
+ <parameter>description</parameter>. If the string is not set, e.g. with
+ <function>sd_bus_slot_set_description()</function>, and the slot is a bus match callback slot,
+ the match string will be returned. Otherwise, <constant>-ENXIO</constant> is returned.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, these functions return 0 or a positive integer. On failure,
+ they return a negative errno-style error code.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>An required argument is <constant>NULL</constant>.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-ENXIO</constant></term>
+
+ <listitem><para>The bus slot object has no description.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ </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_slot_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_slot_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_slot_set_destroy_callback.xml b/man/sd_bus_slot_set_destroy_callback.xml
index a54b7e2d61..80bfd865d5 100644
--- a/man/sd_bus_slot_set_destroy_callback.xml
+++ b/man/sd_bus_slot_set_destroy_callback.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -79,7 +79,7 @@
for <parameter>slot</parameter> in the <parameter>callback</parameter> parameter.</para>
<para><function>sd_bus_track_set_destroy_callback()</function> and
- <function>sd_bus_track_get_destroy_callback</function> provide equivalent functionality for the
+ <function>sd_bus_track_get_destroy_callback()</function> provide equivalent functionality for the
<parameter>userdata</parameter> pointer associated with bus peer tracking objects. For details about bus peer
tracking objects, see
<citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
@@ -93,7 +93,7 @@
negative errno-style error code.</para>
<para><function>sd_bus_slot_get_destroy_callback()</function> and
- <function>sd_bus_track_get_destroy_callback</function> return positive if the destroy callback function is set, 0
+ <function>sd_bus_track_get_destroy_callback()</function> return positive if the destroy callback function is set, 0
if not. On failure, they return a negative errno-style error code.</para>
</refsect1>
diff --git a/man/sd_bus_slot_set_floating.xml b/man/sd_bus_slot_set_floating.xml
index 77057c26cd..2b8c900996 100644
--- a/man/sd_bus_slot_set_floating.xml
+++ b/man/sd_bus_slot_set_floating.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_bus_slot_set_userdata.xml b/man/sd_bus_slot_set_userdata.xml
new file mode 100644
index 0000000000..dad708b6ab
--- /dev/null
+++ b/man/sd_bus_slot_set_userdata.xml
@@ -0,0 +1,88 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_slot_set_userdata" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_slot_set_userdata</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_slot_set_userdata</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_slot_set_userdata</refname>
+ <refname>sd_bus_slot_get_userdata</refname>
+
+ <refpurpose>Set and query the value in the "userdata" field</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>void* <function>sd_bus_slot_set_userdata</function></funcdef>
+ <paramdef>sd_bus_slot* <parameter>slot</parameter></paramdef>
+ <paramdef>void* <parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void* <function>sd_bus_slot_get_userdata</function></funcdef>
+ <paramdef>sd_bus_slot* <parameter>slot</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The userdata pointer allows data to be passed between the point where a callback is
+ registered, for example when a filter is added using
+ <citerefentry><refentrytitle>sd_bus_add_filter</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ or an asynchronous function call is made using
+ <citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ and the point where the callback is called, without having any global state. The pointer has
+ type <type>void*</type> and is not used by the sd-bus functions in any way, except to pass to
+ the callback function.</para>
+
+ <para>Usually, the userdata field is set when the slot object is initially
+ registered. <function>sd_bus_slot_set_userdata()</function> may be used to change it later for
+ the bus slot object <parameter>slot</parameter>. Previous value of the field is returned. The
+ argument and returned value may be <constant>NULL</constant>. It will be passed as the
+ <parameter>userdata</parameter> argument to the callback function attached to the slot.</para>
+
+ <para><function>sd_bus_slot_set_userdata()</function> gets the value of the userdata field in
+ the bus slot object <parameter>slot</parameter>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, these functions return the value of the userdata field before the function
+ call. If the <parameter>slot</parameter> object is <constant>NULL</constant>,
+ <constant>NULL</constant> will be returned to signify an error, but this is not distinguishable
+ from the userdata field value being <constant>NULL</constant>.</para>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_slot_set_destroy_callback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_slot_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_track_add_name.xml b/man/sd_bus_track_add_name.xml
index 5b7f5c5b0b..ef304fc067 100644
--- a/man/sd_bus_track_add_name.xml
+++ b/man/sd_bus_track_add_name.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_bus_track_new.xml b/man/sd_bus_track_new.xml
index 0b2273ed01..711eca3fcb 100644
--- a/man/sd_bus_track_new.xml
+++ b/man/sd_bus_track_new.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_bus_wait.xml b/man/sd_bus_wait.xml
new file mode 100644
index 0000000000..e866eeb20b
--- /dev/null
+++ b/man/sd_bus_wait.xml
@@ -0,0 +1,113 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+
+ Copyright © 2016 Julian Orth
+-->
+
+<refentry id="sd_bus_wait" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_wait</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_wait</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_wait</refname>
+
+ <refpurpose>Wait for I/O on a bus connection</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_wait</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>uint64_t <parameter>timeout_usec</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_wait()</function> synchronously waits for I/O on the specified bus connection object. This
+ function is supposed to be called whenever
+ <citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry> returns zero,
+ indicating that no work is pending on the connection. Internally, this call invokes <citerefentry
+ project='man-pages'><refentrytitle>ppoll</refentrytitle><manvolnum>3</manvolnum></citerefentry>, to wait for I/O on
+ the bus connection. If the <parameter>timeout_sec</parameter> parameter is specified, the call will block at most
+ for the specified amount of time in µs. Pass <constant>UINT64_MAX</constant> to permit it to sleep
+ indefinitely.</para>
+
+ <para>After each invocation of <function>sd_bus_wait()</function> the <function>sd_bus_process()</function> call
+ should be invoked in order to process any now pending I/O work.</para>
+
+ <para>Note that <function>sd_bus_wait()</function> is suitable only for simple programs as it does not permit
+ waiting for other I/O events. For more complex programs either connect the bus connection object to an external
+ event loop using <citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ or to an <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry> event loop
+ using
+ <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>If any I/O was seen, a positive value is returned, zero otherwise. If an error occurs, a negative
+ <varname>errno</varname>-style error code is returned.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>An invalid bus object was passed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ECHILD</constant></term>
+
+ <listitem><para>The bus connection was allocated in a parent process and is being reused in a child process
+ after <function>fork()</function>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOTCONN</constant></term>
+
+ <listitem><para>The bus connection has been terminated already.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </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_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_event_add_child.xml b/man/sd_event_add_child.xml
index 770072f776..c1b38f84b8 100644
--- a/man/sd_event_add_child.xml
+++ b/man/sd_event_add_child.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_event_add_defer.xml b/man/sd_event_add_defer.xml
index d259b2e39d..82ddce6654 100644
--- a/man/sd_event_add_defer.xml
+++ b/man/sd_event_add_defer.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_event_add_inotify.xml b/man/sd_event_add_inotify.xml
index 605863c356..65765ea3ce 100644
--- a/man/sd_event_add_inotify.xml
+++ b/man/sd_event_add_inotify.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_event_add_io.xml b/man/sd_event_add_io.xml
index b4104a07be..6b3da2b9bb 100644
--- a/man/sd_event_add_io.xml
+++ b/man/sd_event_add_io.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -293,7 +293,7 @@
<citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_get_pending</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/sd_event_add_signal.xml b/man/sd_event_add_signal.xml
index 5bf7a28083..8f03d059f1 100644
--- a/man/sd_event_add_signal.xml
+++ b/man/sd_event_add_signal.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_event_add_time.xml b/man/sd_event_add_time.xml
index e139195b8e..e6dbd15a68 100644
--- a/man/sd_event_add_time.xml
+++ b/man/sd_event_add_time.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_event_exit.xml b/man/sd_event_exit.xml
index 801674c675..2af275b2be 100644
--- a/man/sd_event_exit.xml
+++ b/man/sd_event_exit.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_event_get_fd.xml b/man/sd_event_get_fd.xml
index 890a74333f..5f66fbcff7 100644
--- a/man/sd_event_get_fd.xml
+++ b/man/sd_event_get_fd.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -108,7 +108,7 @@
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/sd_event_new.xml b/man/sd_event_new.xml
index 61cf3d84bf..ddb8dac5a5 100644
--- a/man/sd_event_new.xml
+++ b/man/sd_event_new.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -100,11 +100,13 @@
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- or
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
+ <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
and then execute the event loop using
- <citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+ <citerefentry><refentrytitle>sd_event_loop</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
<para><function>sd_event_ref()</function> increases the reference
count of the specified event loop object by one.</para>
@@ -211,9 +213,8 @@
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>gettid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
</para>
diff --git a/man/sd_event_now.xml b/man/sd_event_now.xml
index 91a5a4e7c7..1c8afb270a 100644
--- a/man/sd_event_now.xml
+++ b/man/sd_event_now.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_event_run.xml b/man/sd_event_run.xml
index a2611f58f3..1a4467357a 100644
--- a/man/sd_event_run.xml
+++ b/man/sd_event_run.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -149,17 +149,18 @@
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <ulink url="https://developer.gnome.org/glib/unstable/glib-The-Main-Event-Loop.html">GLib Main Event Loop</ulink>.
+ <ulink url="https://developer.gnome.org/glib/unstable/glib-The-Main-Event-Loop.html">GLib Main Event Loop</ulink>
</para>
</refsect1>
diff --git a/man/sd_event_set_watchdog.xml b/man/sd_event_set_watchdog.xml
index e8229a4f96..7d7155db3b 100644
--- a/man/sd_event_set_watchdog.xml
+++ b/man/sd_event_set_watchdog.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -141,9 +141,8 @@
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_watchdog_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
diff --git a/man/sd_event_source_get_event.xml b/man/sd_event_source_get_event.xml
index 1a2792c637..36e6c1bb94 100644
--- a/man/sd_event_source_get_event.xml
+++ b/man/sd_event_source_get_event.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -66,8 +66,9 @@
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
diff --git a/man/sd_event_source_get_pending.xml b/man/sd_event_source_get_pending.xml
index 500fa84d5e..75f26907d5 100644
--- a/man/sd_event_source_get_pending.xml
+++ b/man/sd_event_source_get_pending.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -133,8 +133,9 @@
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
diff --git a/man/sd_event_source_set_description.xml b/man/sd_event_source_set_description.xml
index 054bf2091f..be3df3ffaa 100644
--- a/man/sd_event_source_set_description.xml
+++ b/man/sd_event_source_set_description.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -136,8 +136,9 @@
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
diff --git a/man/sd_event_source_set_destroy_callback.xml b/man/sd_event_source_set_destroy_callback.xml
index 34f7652c86..d9afb4df28 100644
--- a/man/sd_event_source_set_destroy_callback.xml
+++ b/man/sd_event_source_set_destroy_callback.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_event_source_set_enabled.xml b/man/sd_event_source_set_enabled.xml
index 0fcfefa1cc..73f01848d5 100644
--- a/man/sd_event_source_set_enabled.xml
+++ b/man/sd_event_source_set_enabled.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -74,8 +74,10 @@
<para><function>sd_event_source_get_enabled()</function> may be
used to query whether the event source object
<parameter>source</parameter> is currently enabled or not. It
- returns the enablement state in
- <parameter>enabled</parameter>.</para>
+ returns the enablement state (one of <constant>SD_EVENT_ON</constant>,
+ <constant>SD_EVENT_OFF</constant>, <constant>SD_EVENT_ONESHOT</constant>)
+ in <parameter>enabled</parameter>, if it is not <constant>NULL</constant>.
+ It also returns true if the event source is not disabled.</para>
<para>Event source objects are enabled when they are first created
with calls such as
@@ -100,10 +102,10 @@
<refsect1>
<title>Return Value</title>
- <para>On success, <function>sd_event_source_set_enabled()</function> and
- <function>sd_event_source_get_enabled()</function> return a
- non-negative integer. On failure, they return a negative
- errno-style error code.</para>
+ <para>On success, <function>sd_event_source_set_enabled()</function> returns a non-negative
+ integer. <function>sd_event_source_get_enabled()</function> returns zero if the source is
+ disabled (<constant>SD_EVENT_OFF</constant>) and a positive integer otherwise. On failure, they
+ return a negative errno-style error code.</para>
</refsect1>
<refsect1>
@@ -145,8 +147,9 @@
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
diff --git a/man/sd_event_source_set_prepare.xml b/man/sd_event_source_set_prepare.xml
index 8c7925a3c3..ae71f74f5d 100644
--- a/man/sd_event_source_set_prepare.xml
+++ b/man/sd_event_source_set_prepare.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -136,8 +136,9 @@
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
diff --git a/man/sd_event_source_set_priority.xml b/man/sd_event_source_set_priority.xml
index 5b8924a0f0..dd16628649 100644
--- a/man/sd_event_source_set_priority.xml
+++ b/man/sd_event_source_set_priority.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -162,8 +162,9 @@
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/sd_event_source_set_userdata.xml b/man/sd_event_source_set_userdata.xml
index 3bc5317ae7..e013282561 100644
--- a/man/sd_event_source_set_userdata.xml
+++ b/man/sd_event_source_set_userdata.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -85,8 +85,9 @@
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
diff --git a/man/sd_event_source_unref.xml b/man/sd_event_source_unref.xml
index d1b83c57aa..b11df3f7b5 100644
--- a/man/sd_event_source_unref.xml
+++ b/man/sd_event_source_unref.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -108,8 +108,9 @@
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
diff --git a/man/sd_event_wait.xml b/man/sd_event_wait.xml
index 6c0dfa7440..884996291c 100644
--- a/man/sd_event_wait.xml
+++ b/man/sd_event_wait.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -320,9 +320,9 @@
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_event_add_post</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_run</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>
diff --git a/man/sd_id128_get_machine.xml b/man/sd_id128_get_machine.xml
index 954fd2e6a7..0884838324 100644
--- a/man/sd_id128_get_machine.xml
+++ b/man/sd_id128_get_machine.xml
@@ -22,6 +22,7 @@
<refname>sd_id128_get_machine</refname>
<refname>sd_id128_get_machine_app_specific</refname>
<refname>sd_id128_get_boot</refname>
+ <refname>sd_id128_get_boot_app_specific</refname>
<refname>sd_id128_get_invocation</refname>
<refpurpose>Retrieve 128-bit IDs</refpurpose>
</refnamediv>
@@ -47,6 +48,12 @@
</funcprototype>
<funcprototype>
+ <funcdef>int <function>sd_id128_get_boot_app_specific</function></funcdef>
+ <paramdef>sd_id128_t <parameter>app_id</parameter></paramdef>
+ <paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>int <function>sd_id128_get_invocation</function></funcdef>
<paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
</funcprototype>
@@ -69,19 +76,25 @@
<function>sd_id128_get_machine()</function>, but retrieves a machine ID that is specific to the application that is
identified by the indicated application ID. It is recommended to use this function instead of
<function>sd_id128_get_machine()</function> when passing an ID to untrusted environments, in order to make sure
- that the original machine ID may not be determined externally. The application-specific ID should be generated via
- a tool like <command>journalctl --new-id128</command>, and may be compiled into the application. This function will
- return the same application-specific ID for each combination of machine ID and application ID. Internally, this
- function calculates HMAC-SHA256 of the application ID, keyed by the machine ID.</para>
-
- <para><function>sd_id128_get_boot()</function> returns the boot ID
- of the executing kernel. This reads and parses the
- <filename>/proc/sys/kernel/random/boot_id</filename> file exposed
- by the kernel. It is randomly generated early at boot and is
- unique for every running kernel instance. See
- <citerefentry project='man-pages'><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>
- for more information. This function also internally caches the
- returned ID to make this call a cheap operation.</para>
+ that the original machine ID may not be determined externally. This way, the ID used by the application remains
+ stable on a given machine, but cannot be easily correlated with IDs used in other applications on the same
+ machine. The application-specific ID should be generated via a tool like <command>systemd-id128 new</command>,
+ and may be compiled into the application. This function will return the same application-specific ID for each
+ combination of machine ID and application ID. Internally, this function calculates HMAC-SHA256 of the application
+ ID, keyed by the machine ID.</para>
+
+ <para><function>sd_id128_get_boot()</function> returns the boot ID of the executing kernel. This reads and parses
+ the <filename>/proc/sys/kernel/random/boot_id</filename> file exposed by the kernel. It is randomly generated early
+ at boot and is unique for every running kernel instance. See <citerefentry
+ project='man-pages'><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry> for more
+ information. This function also internally caches the returned ID to make this call a cheap operation. It is
+ recommended to use this ID as-is only in trusted environments. In untrusted environments it is recommended to
+ derive an application specific ID using <function>sd_id128_get_machine_app_specific()</function>, see below.</para>
+
+ <para><function>sd_id128_get_boot_app_specific()</function> is analogous to
+ <function>sd_id128_get_machine_app_specific()</function> but returns an ID that changes between boots. Some
+ machines may be used for a long time without rebooting, hence the boot ID may remain constant for a long time, and
+ has properties similar to the machine ID during that time.</para>
<para><function>sd_id128_get_invocation()</function> returns the invocation ID of the currently executed
service. In its current implementation, this reads and parses the <varname>$INVOCATION_ID</varname> environment
@@ -89,10 +102,11 @@
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details. The
ID is cached internally. In future a different mechanism to determine the invocation ID may be added.</para>
- <para>Note that <function>sd_id128_get_machine_app_specific()</function>, <function>sd_id128_get_boot()</function>
- and <function>sd_id128_get_invocation()</function> always return UUID v4 compatible IDs.
- <function>sd_id128_get_machine()</function> will also return a UUID v4-compatible ID on new installations but might
- not on older. It is possible to convert the machine ID into a UUID v4-compatible one. For more information, see
+ <para>Note that <function>sd_id128_get_machine_app_specific()</function>, <function>sd_id128_get_boot()</function>,
+ <function>sd_id128_get_boot_app_specific()</function>, and <function>sd_id128_get_invocation()</function> always
+ return UUID v4 compatible IDs. <function>sd_id128_get_machine()</function> will also return a UUID v4-compatible
+ ID on new installations but might not on older. It is possible to convert the machine ID into a UUID v4-compatible
+ one. For more information, see
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<para>For more information about the <literal>sd_id128_t</literal>
@@ -104,10 +118,12 @@
<title>Return Value</title>
<para>Those calls return 0 on success (in which case <parameter>ret</parameter> is filled in),
- or a negative errno-style error code. In particular, <function>sd_id128_get_machine()</function>
- and <function>sd_id128_get_machine_app_specific()</function> return <constant>-ENOENT</constant>
- if <filename>/etc/machine-id</filename> is missing, and <constant>-ENOMEDIUM</constant> if is
- empty or all zeros.</para>
+ or a negative errno-style error code. In particular,
+ <function>sd_id128_get_machine()</function>,
+ <function>sd_id128_get_machine_app_specific()</function>, and
+ <function>sd_id128_get_boot_app_specific()</function> return <constant>-ENOENT</constant> if
+ <filename>/etc/machine-id</filename> is missing, and <constant>-ENOMEDIUM</constant> if is empty
+ or all zeros.</para>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
@@ -118,19 +134,22 @@
<example>
<title>Application-specific machine ID</title>
- <para>Here's a simple example for an application specific machine ID:</para>
+ <para>First, generate the application ID:</para>
+ <programlisting>$ systemd-id128 -p new
+As string:
+c273277323db454ea63bb96e79b53e97
+
+As UUID:
+c2732773-23db-454e-a63b-b96e79b53e97
- <programlisting>#include &lt;systemd/sd-id128.h&gt;
-#include &lt;stdio.h&gt;
+As man:sd-id128(3) macro:
+#define MESSAGE_XYZ SD_ID128_MAKE(c2,73,27,73,23,db,45,4e,a6,3b,b9,6e,79,b5,3e,97)
+...
+</programlisting>
-#define OUR_APPLICATION_ID SD_ID128_MAKE(c2,73,27,73,23,db,45,4e,a6,3b,b9,6e,79,b5,3e,97)
+ <para>Then use the new identifier in an example application:</para>
-int main(int argc, char *argv[]) {
- sd_id128_t id;
- sd_id128_get_machine_app_specific(OUR_APPLICATION_ID, &amp;id);
- printf("Our application ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
- return 0;
-}</programlisting>
+ <programlisting><xi:include href="id128-app-specific.c" parse="text" /></programlisting>
</example>
</refsect1>
@@ -139,6 +158,7 @@ int main(int argc, char *argv[]) {
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-id128</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
diff --git a/man/sd_id128_randomize.xml b/man/sd_id128_randomize.xml
index 4f7cd71398..4f5b160bd9 100644
--- a/man/sd_id128_randomize.xml
+++ b/man/sd_id128_randomize.xml
@@ -52,9 +52,9 @@
type, see
<citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
- <para><citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
- <option>--new-id128</option> option may be used as a command line
- front-end for <function>sd_id128_randomize()</function>.</para>
+ <para><citerefentry><refentrytitle>systemd-id128</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <command>new</command> command may be used as a command line front-end for
+ <function>sd_id128_randomize()</function>.</para>
</refsect1>
<refsect1>
diff --git a/man/sd_journal_enumerate_fields.xml b/man/sd_journal_enumerate_fields.xml
index 95af2c1ee0..c5704f53ad 100644
--- a/man/sd_journal_enumerate_fields.xml
+++ b/man/sd_journal_enumerate_fields.xml
@@ -86,8 +86,7 @@
<refsect1>
<title>Notes</title>
- <para>All functions listed here are thread-agnostic and only a single thread may operate
- on a given <structname>sd_journal</structname> object.</para>
+ <xi:include href="threads-aware.xml" xpointer="strict" />
<xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
diff --git a/man/sd_journal_get_catalog.xml b/man/sd_journal_get_catalog.xml
index ce37e177bd..80edc08c81 100644
--- a/man/sd_journal_get_catalog.xml
+++ b/man/sd_journal_get_catalog.xml
@@ -87,9 +87,14 @@
<refsect1>
<title>Notes</title>
- <para>Function <function>sd_journal_get_catalog()</function> is thread-agnostic and only a
- single thread may operate on a given <structname>sd_journal</structname> object. Function
- <function>sd_journal_get_catalog_for_message_id()</function> is thread-safe.</para>
+ <para>Function <function>sd_journal_get_catalog()</function> is thread-agnostic and only
+ a single specific thread may operate on a given object during its entire lifetime. It's safe to allocate multiple
+ independent objects and use each from a specific thread in parallel. However, it's not safe to allocate such an
+ object in one thread, and operate or free it from any other, even if locking is used to ensure these threads don't
+ operate on it at the very same time.</para>
+
+ <para>Function <function>sd_journal_get_catalog_for_message_id()</function> is are thread-safe and may be called in
+ parallel from multiple threads.</para>
<xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
diff --git a/man/sd_journal_get_cursor.xml b/man/sd_journal_get_cursor.xml
index 6817a3cd54..d5e465b81a 100644
--- a/man/sd_journal_get_cursor.xml
+++ b/man/sd_journal_get_cursor.xml
@@ -98,8 +98,7 @@
<refsect1>
<title>Notes</title>
- <para>All functions listed here are thread-agnostic and only a single thread may operate
- on a given <structname>sd_journal</structname> object.</para>
+ <xi:include href="threads-aware.xml" xpointer="strict" />
<xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
diff --git a/man/sd_journal_get_cutoff_realtime_usec.xml b/man/sd_journal_get_cutoff_realtime_usec.xml
index dc8e32bf81..b2a0634f7d 100644
--- a/man/sd_journal_get_cutoff_realtime_usec.xml
+++ b/man/sd_journal_get_cutoff_realtime_usec.xml
@@ -96,8 +96,7 @@
<refsect1>
<title>Notes</title>
- <para>All functions listed here are thread-agnostic and only a single thread may operate
- on a given <structname>sd_journal</structname> object.</para>
+ <xi:include href="threads-aware.xml" xpointer="strict" />
<xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
diff --git a/man/sd_journal_get_data.xml b/man/sd_journal_get_data.xml
index 99f9500441..464fd16ace 100644
--- a/man/sd_journal_get_data.xml
+++ b/man/sd_journal_get_data.xml
@@ -156,7 +156,13 @@
success or a negative errno-style error code.</para>
</refsect1>
- <xi:include href="libsystemd-pkgconfig.xml" />
+ <refsect1>
+ <title>Notes</title>
+
+ <xi:include href="threads-aware.xml" xpointer="strict"/>
+
+ <xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
+ </refsect1>
<refsect1>
<title>Examples</title>
diff --git a/man/sd_journal_get_fd.xml b/man/sd_journal_get_fd.xml
index 7edbc4bc25..2186b685bf 100644
--- a/man/sd_journal_get_fd.xml
+++ b/man/sd_journal_get_fd.xml
@@ -226,14 +226,9 @@ else {
<refsect1>
<title>Notes</title>
- <para>The <function>sd_journal_get_fd()</function>,
- <function>sd_journal_get_events()</function>,
- <function>sd_journal_reliable_fd()</function>,
- <function>sd_journal_process()</function> and
- <function>sd_journal_wait()</function> interfaces are available as
- a shared library, which can be compiled and linked to with the
- <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- file.</para>
+ <xi:include href="threads-aware.xml" xpointer="strict"/>
+
+ <xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
<refsect1>
diff --git a/man/sd_journal_get_realtime_usec.xml b/man/sd_journal_get_realtime_usec.xml
index 2030e8372d..e0f5c4d2e9 100644
--- a/man/sd_journal_get_realtime_usec.xml
+++ b/man/sd_journal_get_realtime_usec.xml
@@ -89,7 +89,13 @@
<function>sd_journal_get_monotonic_usec()</function>.</para>
</refsect1>
- <xi:include href="libsystemd-pkgconfig.xml" />
+ <refsect1>
+ <title>Notes</title>
+
+ <xi:include href="threads-aware.xml" xpointer="strict"/>
+
+ <xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
+ </refsect1>
<refsect1>
<title>See Also</title>
diff --git a/man/sd_journal_get_usage.xml b/man/sd_journal_get_usage.xml
index 358a62d066..39f53dd5eb 100644
--- a/man/sd_journal_get_usage.xml
+++ b/man/sd_journal_get_usage.xml
@@ -56,8 +56,7 @@
<refsect1>
<title>Notes</title>
- <para>All functions listed here are thread-agnostic and only a single thread may operate
- on a given <structname>sd_journal</structname> object.</para>
+ <xi:include href="threads-aware.xml" xpointer="strict"/>
<xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
diff --git a/man/sd_journal_has_runtime_files.xml b/man/sd_journal_has_runtime_files.xml
index b7bbf224d4..44fdc8d186 100644
--- a/man/sd_journal_has_runtime_files.xml
+++ b/man/sd_journal_has_runtime_files.xml
@@ -66,8 +66,7 @@
<refsect1>
<title>Notes</title>
- <para>All functions listed here are thread-agnostic and only a single thread may operate
- on a given <structname>sd_journal</structname> object.</para>
+ <xi:include href="threads-aware.xml" xpointer="strict"/>
<xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
diff --git a/man/sd_journal_next.xml b/man/sd_journal_next.xml
index c0ca5a8a14..9a27d1426e 100644
--- a/man/sd_journal_next.xml
+++ b/man/sd_journal_next.xml
@@ -122,8 +122,7 @@
<refsect1>
<title>Notes</title>
- <para>All functions listed here are thread-agnostic and only a single thread may operate
- on a given <structname>sd_journal</structname> object.</para>
+ <xi:include href="threads-aware.xml" xpointer="strict"/>
<xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
diff --git a/man/sd_journal_open.xml b/man/sd_journal_open.xml
index 9f600b223f..cf787b7ea1 100644
--- a/man/sd_journal_open.xml
+++ b/man/sd_journal_open.xml
@@ -6,7 +6,8 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="sd_journal_open">
+<refentry id="sd_journal_open"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_journal_open</title>
@@ -184,15 +185,9 @@
<refsect1>
<title>Notes</title>
- <para>All functions listed here are thread-agnostic and only a single thread may operate
- on a given <structname>sd_journal</structname> object.</para>
+ <xi:include href="threads-aware.xml" xpointer="strict"/>
- <para>The <function>sd_journal_open()</function>,
- <function>sd_journal_open_directory()</function> and
- <function>sd_journal_close()</function> interfaces are available
- as a shared library, which can be compiled and linked to with the
- <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- file.</para>
+ <xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
<refsect1>
diff --git a/man/sd_journal_print.xml b/man/sd_journal_print.xml
index f8ff7ba093..e18cf88bbc 100644
--- a/man/sd_journal_print.xml
+++ b/man/sd_journal_print.xml
@@ -177,7 +177,8 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid(
<refsect1>
<title>Thread safety</title>
- <para>All functions listed here are thread-safe and may be called in parallel from multiple threads.</para>
+
+ <xi:include href="threads-aware.xml" xpointer="safe"/>
<para><function>sd_journal_sendv()</function> is "async signal safe" in the meaning of <citerefentry
project='man-pages'><refentrytitle>signal-safety</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
diff --git a/man/sd_journal_query_unique.xml b/man/sd_journal_query_unique.xml
index 0bbc479f22..9adafa1144 100644
--- a/man/sd_journal_query_unique.xml
+++ b/man/sd_journal_query_unique.xml
@@ -126,8 +126,7 @@
<refsect1>
<title>Notes</title>
- <para>All functions listed here are thread-agnostic and only a single thread may operate
- on a given <structname>sd_journal</structname> object.</para>
+ <xi:include href="threads-aware.xml" xpointer="strict"/>
<xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
@@ -140,32 +139,7 @@
following example lists all unit names referenced in the
journal:</para>
- <programlisting>#include &lt;stdio.h&gt;
-#include &lt;string.h&gt;
-#include &lt;systemd/sd-journal.h&gt;
-
-int main(int argc, char *argv[]) {
- sd_journal *j;
- const void *d;
- size_t l;
- int r;
-
- r = sd_journal_open(&amp;j, SD_JOURNAL_LOCAL_ONLY);
- if (r &lt; 0) {
- fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
- return 1;
- }
- r = sd_journal_query_unique(j, "_SYSTEMD_UNIT");
- if (r &lt; 0) {
- fprintf(stderr, "Failed to query journal: %s\n", strerror(-r));
- return 1;
- }
- SD_JOURNAL_FOREACH_UNIQUE(j, d, l)
- printf("%.*s\n", (int) l, (const char*) d);
- sd_journal_close(j);
- return 0;
-}</programlisting>
-
+ <programlisting><xi:include href="journal-iterate-unique.c" parse="text" /></programlisting>
</refsect1>
<refsect1>
diff --git a/man/sd_journal_seek_head.xml b/man/sd_journal_seek_head.xml
index 86274071f5..da88d241e8 100644
--- a/man/sd_journal_seek_head.xml
+++ b/man/sd_journal_seek_head.xml
@@ -120,8 +120,7 @@
<refsect1>
<title>Notes</title>
- <para>All functions listed here are thread-agnostic and only a single thread may operate
- on a given <structname>sd_journal</structname> object.</para>
+ <xi:include href="threads-aware.xml" xpointer="strict"/>
<xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
diff --git a/man/sd_journal_stream_fd.xml b/man/sd_journal_stream_fd.xml
index de76cabb4d..a3ff908652 100644
--- a/man/sd_journal_stream_fd.xml
+++ b/man/sd_journal_stream_fd.xml
@@ -49,7 +49,7 @@
<para><function>sd_journal_stream_fd()</function> takes a short
program identifier string as first argument, which will be written
- to the journal as _SYSLOG_IDENTIFIER= field for each log entry
+ to the journal as SYSLOG_IDENTIFIER= field for each log entry
(see
<citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for more information). The second argument shall be the default
@@ -92,8 +92,7 @@
<refsect1>
<title>Notes</title>
- <para>Function <function>sd_journal_stream_fd()</function> is thread-safe and may be called
- from multiple threads.</para>
+ <xi:include href="threads-aware.xml" xpointer="safe"/>
<xi:include href="libsystemd-pkgconfig.xml" xpointer="pkgconfig-text"/>
</refsect1>
diff --git a/man/sd_listen_fds.xml b/man/sd_listen_fds.xml
index 6dfe716a2d..3c3d2ff26e 100644
--- a/man/sd_listen_fds.xml
+++ b/man/sd_listen_fds.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_login_monitor_new.xml b/man/sd_login_monitor_new.xml
index 23f685bb31..d914e51d0d 100644
--- a/man/sd_login_monitor_new.xml
+++ b/man/sd_login_monitor_new.xml
@@ -115,13 +115,13 @@
code block is left:</para>
<programlisting>{
- __attribute__((cleanup(sd_login_monitor_unrefp)) sd_login_monitor *m = NULL;
- int r;
- …
- r = sd_login_monitor_default(&amp;m);
- if (r &lt; 0)
- fprintf(stderr, "Failed to allocate login monitor object: %s\n", strerror(-r));
- …
+ __attribute__((cleanup(sd_login_monitor_unrefp)) sd_login_monitor *m = NULL;
+ int r;
+ …
+ r = sd_login_monitor_default(&amp;m);
+ if (r &lt; 0)
+ fprintf(stderr, "Failed to allocate login monitor object: %s\n", strerror(-r));
+ …
}</programlisting>
<para><function>sd_login_monitor_flush()</function> may be used to
@@ -176,13 +176,13 @@
int msec;
sd_login_monitor_get_timeout(m, &amp;t);
if (t == (uint64_t) -1)
- msec = -1;
+ msec = -1;
else {
- struct timespec ts;
- uint64_t n;
- clock_gettime(CLOCK_MONOTONIC, &amp;ts);
- n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
- msec = t > n ? (int) ((t - n + 999) / 1000) : 0;
+ struct timespec ts;
+ uint64_t n;
+ clock_gettime(CLOCK_MONOTONIC, &amp;ts);
+ n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+ msec = t > n ? (int) ((t - n + 999) / 1000) : 0;
}</programlisting>
<para>The code above does not do any error checking for brevity's
diff --git a/man/sd_notify.xml b/man/sd_notify.xml
index 5d6ae65701..79ae84bb94 100644
--- a/man/sd_notify.xml
+++ b/man/sd_notify.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/sd_pid_get_owner_uid.xml b/man/sd_pid_get_owner_uid.xml
index 9229217b0b..f4619b6102 100644
--- a/man/sd_pid_get_owner_uid.xml
+++ b/man/sd_pid_get_owner_uid.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/send-unit-files-changed.c b/man/send-unit-files-changed.c
new file mode 100644
index 0000000000..aecfbcbed1
--- /dev/null
+++ b/man/send-unit-files-changed.c
@@ -0,0 +1,16 @@
+#include <systemd/sd-bus.h>
+#define _cleanup_(f) __attribute__((cleanup(f)))
+
+int send_unit_files_changed(sd_bus *bus) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
+ int r;
+
+ r = sd_bus_message_new_signal(bus, &message,
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "UnitFilesChanged");
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(bus, message, NULL);
+}
diff --git a/man/standard-conf.xml b/man/standard-conf.xml
index e1cb7d31ed..f5c961a0c2 100644
--- a/man/standard-conf.xml
+++ b/man/standard-conf.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version="1.0"?>
<!DOCTYPE refsection PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
diff --git a/man/standard-options.xml b/man/standard-options.xml
index bea92e89fb..51a4faf5d6 100644
--- a/man/standard-options.xml
+++ b/man/standard-options.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?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">
diff --git a/man/systemctl.xml b/man/systemctl.xml
index d95d3726af..b9077c55a1 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
@@ -314,18 +314,13 @@
<term><option>--ignore-inhibitors</option></term>
<listitem>
- <para>When system shutdown or a sleep state is requested,
- ignore inhibitor locks. Applications can establish inhibitor
- locks to avoid that certain important operations (such as CD
- burning or suchlike) are interrupted by system shutdown or a
- sleep state. Any user may take these locks and privileged
- users may override these locks. If any locks are taken,
- shutdown and sleep state requests will normally fail
- (regardless of whether privileged or not) and a list of active locks
- is printed. However, if <option>--ignore-inhibitors</option>
- is specified, the locks are ignored and not printed, and the
- operation attempted anyway, possibly requiring additional
- privileges.</para>
+ <para>When system shutdown or a sleep state is requested, ignore inhibitor locks. Applications can establish
+ inhibitor locks to avoid that certain important operations (such as CD burning or suchlike) are interrupted
+ by system shutdown or a sleep state. Any user may take these locks and privileged users may override these
+ locks. If any locks are taken, shutdown and sleep state requests will normally fail (unless privileged) and a
+ list of active locks is printed. However, if <option>--ignore-inhibitors</option> is specified, the
+ established locks are ignored and not shown, and the operation attempted anyway, possibly requiring
+ additional privileges.</para>
</listitem>
</varlistentry>
@@ -335,8 +330,8 @@
<listitem>
<para>Just print what would be done. Currently supported by verbs
<command>halt</command>, <command>poweroff</command>, <command>reboot</command>,
- <command>kexec</command>, <command>suspend</command>,
- <command>hibernate</command>, <command>hybrid-sleep</command>,
+ <command>kexec</command>, <command>suspend</command>, <command>hibernate</command>,
+ <command>hybrid-sleep</command>, <command>suspend-then-hibernate</command>,
<command>default</command>, <command>rescue</command>,
<command>emergency</command>, and <command>exit</command>.</para>
</listitem>
@@ -377,6 +372,9 @@
Note that this will wait forever if any given unit never terminates
(by itself or by getting stopped explicitly); particularly services
which use <literal>RemainAfterExit=yes</literal>.</para>
+
+ <para>When used with <command>is-system-running</command>, wait
+ until the boot process is completed before returning.</para>
</listitem>
</varlistentry>
@@ -557,19 +555,19 @@
<term><option>--runtime</option></term>
<listitem>
- <para>When used with <command>set-property</command>, make changes only
- temporarily, so that they are lost on the next reboot.</para>
-
- <para>Similarily, when used with <command>enable</command>, <command>mask</command>,
- <command>edit</command> and related commands, make temporary changes, which are lost on
- the next reboot. Changes are not made in subdirectories of <filename>/etc</filename>, but
- in <filename>/run</filename>. The immediate effect is identical, however since the latter
+ <para>When used with <command>enable</command>,
+ <command>disable</command>, <command>edit</command>,
+ (and related commands), make changes only temporarily, so
+ that they are lost on the next reboot. This will have the
+ effect that changes are not made in subdirectories of
+ <filename>/etc</filename> but in <filename>/run</filename>,
+ with identical immediate effects, however, since the latter
is lost on reboot, the changes are lost too.</para>
- <para>Note: this option cannot be used with <command>disable</command>,
- <command>unmask</command>, <command>preset</command>, or <command>preset-all</command>,
- because those operations sometimes need to remove symlinks under <filename>/etc</filename>
- to have the desired effect, which would cause a persistent change.</para>
+ <para>Similarly, when used with
+ <command>set-property</command>, make changes only
+ temporarily, so that they are lost on the next
+ reboot.</para>
</listitem>
</varlistentry>
@@ -592,9 +590,8 @@
<term><option>--lines=</option></term>
<listitem>
- <para>When used with <command>status</command>, controls the
- number of journal lines to show, counting from the most
- recent ones. Takes a positive integer argument. Defaults to
+ <para>When used with <command>status</command>, controls the number of journal lines to show, counting from
+ the most recent ones. Takes a positive integer argument, or 0 to disable journal output. Defaults to
10.</para>
</listitem>
</varlistentry>
@@ -655,7 +652,7 @@
<variablelist>
<varlistentry>
- <term><command>list-units <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
+ <term><command>list-units</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
<listitem>
<para>List units that <command>systemd</command> currently has in memory. This includes units that are
@@ -673,8 +670,8 @@
boot-efi.mount loaded active mounted /boot/efi
systemd-journald.service loaded active running Journal Service
systemd-logind.service loaded active running Login Service
-â— user@1000.service loaded active running User Manager for UID 1000
-…
+â— user@1000.service loaded failed failed User Manager for UID 1000
+ …
systemd-tmpfiles-clean.timer loaded active waiting Daily Cleanup of Temporary Directories
LOAD = Reflects whether the unit definition was properly loaded.
@@ -703,7 +700,7 @@ To show all installed unit files use 'systemctl list-unit-files'.
</varlistentry>
<varlistentry>
- <term><command>list-sockets <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
+ <term><command>list-sockets</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
<listitem>
<para>List socket units currently in memory, ordered by listening address. If one or more
@@ -726,7 +723,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>
<varlistentry>
- <term><command>list-timers <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
+ <term><command>list-timers</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
<listitem>
<para>List timer units currently in memory, ordered by the time they elapse next. If one or more
@@ -803,7 +800,7 @@ Sun 2017-02-26 20:57:49 EST 2h 3min left Sun 2017-02-26 11:56:36 EST 6h ago
<para>Note that restarting a unit with this command does not necessarily flush out all of the unit's
resources before it is started again. For example, the per-service file descriptor storage facility (see
- <varname>FileDescriptoreStoreMax=</varname> in
+ <varname>FileDescriptorStoreMax=</varname> in
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>) will
remain intact as long as the unit has a job pending, and is only cleared when the unit is fully stopped and
no jobs are pending anymore. If it is intended that the file descriptor store is flushed out, too, during a
@@ -1061,6 +1058,12 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
terminating abnormally or timing out), it will automatically enter the <literal>failed</literal> state and
its exit code and status is recorded for introspection by the administrator until the service is
stopped/re-started or reset with this command.</para>
+
+ <para>In addition to resetting the <literal>failed</literal> state of a unit it also resets various other
+ per-unit properties: the start rate limit counter of all unit types is reset to zero, as is the restart
+ counter of service units. Thus, if a unit's start limit (as configured with
+ <varname>StartLimitIntervalSec=</varname>/<varname>StartLimitBurst=</varname>) is hit and the unit refuses
+ to be started again, use this command to make it startable again.</para>
</listitem>
</varlistentry>
@@ -1088,6 +1091,10 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<option>--after</option>, <option>--before</option>
may be used to change what types of dependencies
are shown.</para>
+
+ <para>Note that this command only lists units currently loaded into memory by the service manager. In
+ particular, this command is not suitable to get a comprehensive list at all reverse dependencies on a
+ specific unit, as it won't list the dependencies declared by units currently not loaded.</para>
</listitem>
</varlistentry>
</variablelist>
@@ -1098,7 +1105,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<variablelist>
<varlistentry>
- <term><command>list-unit-files <optional><replaceable>PATTERN…</replaceable></optional></command></term>
+ <term><command>list-unit-files</command> <optional><replaceable>PATTERN…</replaceable></optional></term>
<listitem>
<para>List unit files installed on the system, in combination with their enablement state (as reported by
@@ -1477,7 +1484,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<variablelist>
<varlistentry>
- <term><command>list-machines <optional><replaceable>PATTERN</replaceable>…</optional></command></term>
+ <term><command>list-machines</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
<listitem>
<para>List the host and all running local containers with
@@ -1641,6 +1648,15 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
output, see the table below. Use <option>--quiet</option> to
suppress this output.</para>
+ <para>Use <option>--wait</option> to wait until the boot
+ process is completed before printing the current state and
+ returning the appropriate error status. If <option>--wait</option>
+ is in use, states <varname>initializing</varname> or
+ <varname>starting</varname> will not be reported, instead
+ the command will block until a later state (such as
+ <varname>running</varname> or <varname>degraded</varname>)
+ is reached.</para>
+
<table>
<title><command>is-system-running</command> output</title>
<tgroup cols='3'>
@@ -1779,7 +1795,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
</listitem>
</varlistentry>
<varlistentry>
- <term><command>reboot <optional><replaceable>arg</replaceable></optional></command></term>
+ <term><command>reboot</command> <optional><replaceable>arg</replaceable></optional></term>
<listitem>
<para>Shut down and reboot the system. This is mostly equivalent to <command>systemctl start reboot.target
@@ -1819,7 +1835,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
</varlistentry>
<varlistentry>
- <term><command>exit <optional><replaceable>EXIT_CODE</replaceable></optional></command></term>
+ <term><command>exit</command> <optional><replaceable>EXIT_CODE</replaceable></optional></term>
<listitem>
<para>Ask the service manager to quit. This is only supported for user service managers (i.e. in
@@ -1833,7 +1849,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
</varlistentry>
<varlistentry>
- <term><command>switch-root <replaceable>ROOT</replaceable> <optional><replaceable>INIT</replaceable></optional></command></term>
+ <term><command>switch-root</command> <replaceable>ROOT</replaceable> <optional><replaceable>INIT</replaceable></optional></term>
<listitem>
<para>Switches to a different root directory and executes a new system manager process below it. This is
@@ -1877,6 +1893,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
sleep operation is successfully enqueued. It will not wait for the sleep/wake-up cycle to complete.</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><command>suspend-then-hibernate</command></term>
+
+ <listitem>
+ <para>Suspend the system and hibernate it after the delay specified in <filename>systemd-sleep.conf</filename>.
+ This will trigger activation of the special target unit <filename>suspend-then-hibernate.target</filename>.
+ This command is asynchronous, and will return after the hybrid sleep operation is successfully enqueued.
+ It will not wait for the sleep/wake-up or hibernate/thaw cycle to complete.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect2>
@@ -1933,8 +1960,56 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<refsect1>
<title>Exit status</title>
- <para>On success, 0 is returned, a non-zero failure
- code otherwise.</para>
+ <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+
+ <para><command>systemctl</command> uses the return codes defined by LSB, as defined in
+ <ulink url="http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/iniscrptact.html">LSB 3.0.0</ulink>.
+ </para>
+
+ <table>
+ <title>LSB return codes</title>
+
+ <tgroup cols='3'>
+ <thead>
+ <row>
+ <entry>Value</entry>
+ <entry>Description in LSB</entry>
+ <entry>Use in systemd</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><constant>0</constant></entry>
+ <entry>"program is running or service is OK"</entry>
+ <entry>unit is active</entry>
+ </row>
+ <row>
+ <entry><constant>1</constant></entry>
+ <entry>"program is dead and <filename>/var/run</filename> pid file exists"</entry>
+ <entry>unit <emphasis>not</emphasis> failed (used by <command>is-failed</command>)</entry>
+ </row>
+ <row>
+ <entry><constant>2</constant></entry>
+ <entry>"program is dead and <filename>/var/lock</filename> lock file exists"</entry>
+ <entry>unused</entry>
+ </row>
+ <row>
+ <entry><constant>3</constant></entry>
+ <entry>"program is not running"</entry>
+ <entry>unit is not active</entry>
+ </row>
+ <row>
+ <entry><constant>4</constant></entry>
+ <entry>"program or service status is unknown"</entry>
+ <entry>no such unit</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>The mapping of LSB service states to systemd unit states is imperfect, so it is better to
+ not rely on those return values but to look for specific unit states and substates instead.
+ </para>
</refsect1>
<refsect1>
diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml
index 7aa10fc68e..7becf0133e 100644
--- a/man/systemd-analyze.xml
+++ b/man/systemd-analyze.xml
@@ -106,6 +106,18 @@
<arg choice="plain">service-watchdogs</arg>
<arg choice="opt"><replaceable>BOOL</replaceable></arg>
</cmdsynopsis>
+ <cmdsynopsis>
+ <command>systemd-analyze</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain">timespan</arg>
+ <arg choice="plain" rep="repeat"><replaceable>SPAN</replaceable></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>systemd-analyze</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain">security</arg>
+ <arg choice="plain" rep="repeat"><replaceable>UNIT</replaceable></arg>
+ </cmdsynopsis>
</refsynopsisdiv>
<refsect1>
@@ -253,6 +265,33 @@ NAutoVTs=8
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
The hardware watchdog is not affected by this setting.</para>
+ <para><command>systemd-analyze timespan</command> parses a time span and outputs the equivalent value in microseconds, and as a reformatted timespan.
+ The time span should adhere to the same syntax documented in <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+ Values without associated magnitudes are parsed as seconds.</para>
+
+ <para><command>systemd-analyze security</command> analyzes the security and sandboxing settings of one or more
+ specified service units. If at least one unit name is specified the security settings of the specified service
+ units are inspected and a detailed analysis is shown. If no unit name is specified, all currently loaded,
+ long-running service units are inspected and a terse table with results shown. The command checks for various
+ security-related service settings, assigning each a numeric "exposure level" value, depending on how important a
+ setting is. It then calculates an overall exposure level for the whole unit, which is an estimation in the range
+ 0.0…10.0 indicating how exposed a service is security-wise. High exposure levels indicate very little applied
+ sandboxing. Low exposure levels indicate tight sandboxing and strongest security restrictions. Note that this only
+ analyzes the per-service security features systemd itself implements. This means that any additional security
+ mechanisms applied by the service code itself are not accounted for. The exposure level determined this way should
+ not be misunderstood: a high exposure level neither means that there is no effective sandboxing applied by the
+ service code itself, nor that the service is actually vulnerable to remote or local attacks. High exposure levels
+ do indicate however that most likely the service might benefit from additional settings applied to them. Please
+ note that many of the security and sandboxing settings individually can be circumvented — unless combined with
+ others. For example, if a service retains the privilege to establish or undo mount points many of the sandboxing
+ options can be undone by the service code itself. Due to that is essential that each service uses the most
+ comprehensive and strict sandboxing and security settings possible. The tool will take into account some of these
+ combinations and relationships between the settings, but not all. Also note that the security and sandboxing
+ settings analyzed here only apply to the operations executed by the service code itself. If a service has access to
+ an IPC system (such as D-Bus) it might request operations from other services that are not subject to the same
+ restrictions. Any comprehensive security and sandboxing analysis is hence incomplete if the IPC access policy is
+ not validated too.</para>
+
<para>If no command is passed, <command>systemd-analyze
time</command> is implied.</para>
diff --git a/man/systemd-ask-password.xml b/man/systemd-ask-password.xml
index 7ec76fabf0..2fe3e88d74 100644
--- a/man/systemd-ask-password.xml
+++ b/man/systemd-ask-password.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/systemd-bless-boot-generator.xml b/man/systemd-bless-boot-generator.xml
new file mode 100644
index 0000000000..980941469e
--- /dev/null
+++ b/man/systemd-bless-boot-generator.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+<refentry id="systemd-bless-boot-generator" conditional='ENABLE_EFI'>
+
+ <refentryinfo>
+ <title>systemd-bless-boot-generator</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-bless-boot-generator</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-bless-boot-generator</refname>
+ <refpurpose>Pull <filename>systemd-bless-boot.service</filename> into the initial boot transaction when boot counting is in effect.</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>/usr/lib/systemd/system-generators/systemd-bless-boot-generator</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>systemd-bless-boot-generator</filename> is a generator that pulls
+ <filename>systemd-bless-boot.service</filename> into the initial boot transaction when boot counting, as
+ implemented by <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>, is
+ enabled.</para>
+
+ <para><filename>systemd-bless-boot-generator</filename> implements
+ <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/systemd-bless-boot.service.xml b/man/systemd-bless-boot.service.xml
new file mode 100644
index 0000000000..fb362cef2d
--- /dev/null
+++ b/man/systemd-bless-boot.service.xml
@@ -0,0 +1,115 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refentry id="systemd-bless-boot.service" conditional='ENABLE_EFI'
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>systemd-bless-boot.service</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-bless-boot.service</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-bless-boot.service</refname>
+ <refpurpose>Mark current boot process as successful</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>systemd-bless-boot.service</filename></para>
+ <para><filename>/usr/lib/systemd/system-bless-boot</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>systemd-bless-boot.service</filename> is a system service that marks the current boot process as
+ successful. It's automatically pulled into the initial transaction when
+ <citerefentry><refentrytitle>systemd-bless-boot-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ detects that <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> style
+ boot counting is used.</para>
+
+ <para>Internally, the service operates based on the <varname>LoaderBootCountPath</varname> EFI variable (of the
+ vendor UUID <constant>4a67b082-0a4c-41cf-b6c7-440b29bb8c4</constant>), which is passed from the boot loader to the
+ OS. It contains a file system path (relative to the EFI system partition) of the <ulink
+ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> compliant boot loader entry
+ file or unified kernel image file that was used to boot up the
+ system. <command>systemd-bless-boot.service</command> removes the two 'tries done' and 'tries left' numeric boot
+ counters from the filename, which indicates to future invocations of the boot loader that the entry has completed
+ booting successfully at least once. (This service will hence rename the boot loader entry file or unified kernel
+ image file on the first successful boot.)</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The <filename>/usr/lib/systemd/system-bless-boot</filename> executable may also be invoked from the
+ command line, taking one of the following command arguments:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>status</option></term>
+
+ <listitem><para>The current status of the boot loader entry file or unified kernel image file is shown. This
+ outputs one of <literal>good</literal>, <literal>bad</literal>, <literal>indeterminate</literal>,
+ <literal>clean</literal>, depending on the state and previous invocations of the command. The string
+ <literal>indeterminate</literal> is shown initially after boot, before it has been marked as "good" or
+ "bad". The string <literal>good</literal> is shown after the boot was marked as "good" with the
+ <option>good</option> command below, and "bad" conversely after the <option>bad</option> command was
+ invoked. The string <literal>clean</literal> is returned when boot counting is currently not in effect.</para>
+
+ <para>This command is implied if no command argument is specified.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>good</option></term>
+
+ <listitem><para>When invoked, the current boot loader entry file or unified kernel image file will be marked as
+ "good", executing the file rename operation described above. This command is intended to be invoked at the end
+ of a successful boot. The <filename>systemd-bless-boot.service</filename> unit invokes this
+ command.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>bad</option></term>
+
+ <listitem><para>When called the 'tries left' counter in the boot loader entry file name or unified kernel image
+ file name is set to zero, marking the boot loader entry or kernel image as "bad", so that the boot loader won't
+ consider it anymore on future boots (at least as long as there are other entries available that are not marked
+ "bad" yet). This command is normally not executed, but can be used to instantly put an end to the boot counting
+ logic if a problem is detected and persistently mark the boot entry as bad.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>indeterminate</option></term>
+
+ <listitem><para>This command undoes any marking of the current boot loader entry file or unified kernel image
+ file as good or bad. This is implemented by renaming the boot loader entry file or unified kernel image file
+ back to the path encoded in the <varname>LoaderBootCountPath</varname> EFI variable.</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>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/systemd-boot-check-no-failures.service.xml b/man/systemd-boot-check-no-failures.service.xml
new file mode 100644
index 0000000000..55c2adffdb
--- /dev/null
+++ b/man/systemd-boot-check-no-failures.service.xml
@@ -0,0 +1,54 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refentry id="systemd-boot-check-no-failures.service"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>systemd-boot-check-no-failures.service</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-boot-check-no-failures.service</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-boot-check-no-failures.service</refname>
+ <refpurpose>verify that the system booted up cleanly</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>systemd-boot-check-no-failures.service</filename></para>
+ <para><filename>/usr/lib/systemd/system-boot-check-no-failures</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>systemd-boot-check-no-failures.service</filename> is a system service that checks whether the
+ system booted up successfully. This service implements a very minimal test only: whether there are any failed units on
+ the system. This service is disabled by default. When enabled, it is ordered before
+ <filename>boot-complete.target</filename>, thus ensuring the target cannot be reached when the system booted up
+ with failed services.</para>
+
+ <para>Note that due the simple nature of this check this service is probably not suitable for deployment in most
+ scenarios. It is primarily useful only as example for developing more fine-grained checks to order before
+ <filename>boot-complete.target</filename>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml
index 9fadffadfe..44b0f61f22 100644
--- a/man/systemd-boot.xml
+++ b/man/systemd-boot.xml
@@ -38,13 +38,13 @@
<itemizedlist>
<listitem><para>Boot entries defined with <ulink
- url="https://github.com/systemd/systemd/blob/master/doc/BOOT_LOADER_SPECIFICATION.md">Boot Loader
+ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
Specification</ulink> description files located in <filename>/loader/entries/</filename> on the ESP. These
usually describe Linux kernel images with associated initrd images, but alternatively may also describe
arbitrary other EFI executables.</para></listitem>
<listitem><para>Unified kernel images following the <ulink
- url="https://github.com/systemd/systemd/blob/master/doc/BOOT_LOADER_SPECIFICATION.md">Boot Loader
+ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
Specification</ulink>, as executable EFI binaries in <filename>/EFI/Linux/</filename> on the ESP.
</para></listitem>
@@ -63,9 +63,8 @@
used from a running system to locate the ESP, list available entries, and install systemd-boot itself.</para>
<para>systemd-boot will provide information about the time spent in UEFI firmware using the <ulink
- url="https://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface">Boot Loader Interface</ulink>. This
- information can be displayed using
- <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+ url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>. This information can be displayed
+ using <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
</para>
</refsect1>
@@ -75,67 +74,67 @@
<variablelist>
<varlistentry>
- <term>↑ (Up)</term>
- <term>↓ (Down)</term>
- <term>j</term>
- <term>k</term>
- <term>PageUp</term>
- <term>PageDown</term>
- <term>Home</term>
- <term>End</term>
+ <term><keycap>↑</keycap> (Up)</term>
+ <term><keycap>↓</keycap> (Down)</term>
+ <term><keycap>j</keycap></term>
+ <term><keycap>k</keycap></term>
+ <term><keycap>PageUp</keycap></term>
+ <term><keycap>PageDown</keycap></term>
+ <term><keycap>Home</keycap></term>
+ <term><keycap>End</keycap></term>
<listitem><para>Navigate up/down in the entry list</para></listitem>
</varlistentry>
<varlistentry>
- <term>↵ (Enter)</term>
+ <term><keycap>↵</keycap> (Enter)</term>
<listitem><para>Boot selected entry</para></listitem>
</varlistentry>
<varlistentry>
- <term>d</term>
+ <term><keycap>d</keycap></term>
<listitem><para>Make selected entry the default</para></listitem>
</varlistentry>
<varlistentry>
- <term>e</term>
+ <term><keycap>e</keycap></term>
<listitem><para>Edit the kernel command line for selected entry</para></listitem>
</varlistentry>
<varlistentry>
- <term>+</term>
- <term>t</term>
+ <term><keycap>+</keycap></term>
+ <term><keycap>t</keycap></term>
<listitem><para>Increase the timeout before default entry is booted</para></listitem>
</varlistentry>
<varlistentry>
- <term>-</term>
- <term>T</term>
+ <term><keycap>-</keycap></term>
+ <term><keycap>T</keycap></term>
<listitem><para>Decrease the timeout</para></listitem>
</varlistentry>
<varlistentry>
- <term>v</term>
+ <term><keycap>v</keycap></term>
<listitem><para>Show systemd-boot, UEFI, and firmware versions</para></listitem>
</varlistentry>
<varlistentry>
- <term>P</term>
+ <term><keycap>P</keycap></term>
<listitem><para>Print status</para></listitem>
</varlistentry>
<varlistentry>
- <term>Q</term>
+ <term><keycap>Q</keycap></term>
<listitem><para>Quit</para></listitem>
</varlistentry>
<varlistentry>
- <term>h</term>
- <term>?</term>
+ <term><keycap>h</keycap></term>
+ <term><keycap>?</keycap></term>
<listitem><para>Show a help screen</para></listitem>
</varlistentry>
<varlistentry>
- <term>Ctrl + l</term>
+ <term><keycombo><keycap>Ctrl</keycap><keycap>l</keycap></keycombo></term>
<listitem><para>Reprint the screen</para></listitem>
</varlistentry>
</variablelist>
@@ -145,35 +144,35 @@
<variablelist>
<varlistentry>
- <term>l</term>
+ <term><keycap>l</keycap></term>
<listitem><para>Linux</para></listitem>
</varlistentry>
<varlistentry>
- <term>w</term>
+ <term><keycap>w</keycap></term>
<listitem><para>Windows</para></listitem>
</varlistentry>
<varlistentry>
- <term>a</term>
+ <term><keycap>a</keycap></term>
<listitem><para>OS X</para></listitem>
</varlistentry>
<varlistentry>
- <term>s</term>
+ <term><keycap>s</keycap></term>
<listitem><para>EFI shell</para></listitem>
</varlistentry>
<varlistentry>
- <term>1</term>
- <term>2</term>
- <term>3</term>
- <term>4</term>
- <term>5</term>
- <term>6</term>
- <term>7</term>
- <term>8</term>
- <term>9</term>
+ <term><keycap>1</keycap></term>
+ <term><keycap>2</keycap></term>
+ <term><keycap>3</keycap></term>
+ <term><keycap>4</keycap></term>
+ <term><keycap>5</keycap></term>
+ <term><keycap>6</keycap></term>
+ <term><keycap>7</keycap></term>
+ <term><keycap>8</keycap></term>
+ <term><keycap>9</keycap></term>
<listitem><para>Boot entry number 1 … 9</para></listitem>
</varlistentry>
</variablelist>
@@ -183,36 +182,36 @@
<variablelist>
<varlistentry>
- <term>↠(Left)</term>
- <term>→ (Right)</term>
- <term>Home</term>
- <term>End</term>
+ <term><keycap>â†</keycap> (Left)</term>
+ <term><keycap>→</keycap> (Right)</term>
+ <term><keycap>Home</keycap></term>
+ <term><keycap>End</keycap></term>
<listitem><para>Navigate left/right</para></listitem>
</varlistentry>
<varlistentry>
- <term>Esc</term>
+ <term><keycap>Esc</keycap></term>
<listitem><para>Abort the edit and quit the editor</para></listitem>
</varlistentry>
<varlistentry>
- <term>Ctrl + k</term>
+ <term><keycombo><keycap>Ctrl</keycap><keycap>k</keycap></keycombo></term>
<listitem><para>Clear the command line</para></listitem>
</varlistentry>
<varlistentry>
- <term>Ctrl + w</term>
- <term>Alt + Backspace</term>
+ <term><keycombo><keycap>Ctrl</keycap><keycap>w</keycap></keycombo></term>
+ <term><keycombo><keycap>Alt</keycap><keycap>Backspace</keycap></keycombo></term>
<listitem><para>Delete word backwards</para></listitem>
</varlistentry>
<varlistentry>
- <term>Alt + d </term>
+ <term><keycombo><keycap>Alt</keycap><keycap>d</keycap></keycombo></term>
<listitem><para>Delete word forwards</para></listitem>
</varlistentry>
<varlistentry>
- <term>↵ (Enter)</term>
+ <term><keycap>↵</keycap> (Enter)</term>
<listitem><para>Boot entry with the edited command line</para></listitem>
</varlistentry>
</variablelist>
@@ -231,19 +230,187 @@
<filename>/loader/loader.conf</filename> on the ESP (in combination with data read from EFI variables). See
<citerefentry><refentrytitle>loader.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Boot entry
description files following the <ulink
- url="https://github.com/systemd/systemd/blob/master/doc/BOOT_LOADER_SPECIFICATION.md">Boot Loader
+ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
Specification</ulink> are read from <filename>/loader/entries/</filename> on the ESP. Unified kernel boot entries
- following the <ulink url="https://github.com/systemd/systemd/blob/master/doc/BOOT_LOADER_SPECIFICATION.md">Boot
+ following the <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot
Loader Specification</ulink> are read from <filename>/EFI/Linux/</filename> on the ESP.</para>
</refsect1>
<refsect1>
+ <title>EFI Variables</title>
+
+ <para>The following EFI variables are defined, set and read by <command>systemd-boot</command>, under the vendor
+ UUID <literal>4a67b082-0a4c-41cf-b6c7-440b29bb8c4</literal>, for communication between the OS and the boot
+ loader:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>LoaderBootCountPath</varname></term>
+ <listitem><para>If boot counting is enabled, contains the path to the file in whose name the boot counters are
+ encoded. Set by the boot
+ loader. <citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ uses this information to mark a boot as successful as determined by the successful activation of the
+ <filename>boot-complete.target</filename> target unit.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderConfigTimeout</varname></term>
+ <term><varname>LoaderConfigTimeoutOneShot</varname></term>
+ <listitem><para>The menu timeout in seconds. Read by the boot loader. <varname>LoaderConfigTimeout</varname>
+ is maintained persistently, while <varname>LoaderConfigTimeoutOneShot</varname> is a one-time override which is
+ read once (in which case it takes precedence over <varname>LoaderConfigTimeout</varname>) and then
+ removed. <varname>LoaderConfigTimeout</varname> may be manipulated with the
+ <keycap>t</keycap>/<keycap>T</keycap> keys, see above.)</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderDevicePartUUID</varname></term>
+
+ <listitem><para>Contains the partition UUID of the EFI System Partition the boot loader was run from. Set by
+ the boot
+ loader. <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ uses this information to automatically find the disk booted from, in order to discover various other partitions
+ on the same disk automatically.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderEntries</varname></term>
+
+ <listitem><para>A list of the identifiers of all discovered boot loader entries. Set by the boot
+ loader.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderEntryDefault</varname></term>
+ <term><varname>LoaderEntryOneShot</varname></term>
+
+ <listitem><para>The identifier of the default boot loader entry. Set primarily by the OS and read by the boot
+ loader. <varname>LoaderEntryOneShot</varname> sets the default entry for the next boot only, while
+ <varname>LoaderEntryDefault</varname> sets it persistently for all future
+ boots. <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <option>set-default</option> and <option>set-oneshot</option> commands make use of these variables. The boot
+ loader modifies <varname>LoaderEntryDefault</varname> on request, when the <keycap>d</keycap> key is used, see
+ above.)</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderEntrySelected</varname></term>
+
+ <listitem><para>The identifier of the boot loader entry currently being booted. Set by the boot
+ loader.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderFeatures</varname></term>
+
+ <listitem><para>A set of flags indicating the features the boot loader supports. Set by the boot loader. Use
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view this
+ data.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderFirmwareInfo</varname></term>
+ <term><varname>LoaderFirmwareType</varname></term>
+
+ <listitem><para>Brief firmware information. Set by the boot loader. Use
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view this
+ data.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderImageIdentifier</varname></term>
+
+ <listitem><para>The path of executable of the boot loader used for the current boot, relative to the EFI System
+ Partition's root directory. Set by the boot loader. Use
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view this
+ data.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderInfo</varname></term>
+
+ <listitem><para>Brief information about the boot loader. Set by the boot loader. Use
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view this
+ data.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LoaderTimeExecUSec</varname></term>
+ <term><varname>LoaderTimeInitUSec</varname></term>
+ <term><varname>LoaderTimeMenuUsec</varname></term>
+
+ <listitem><para>Information about the time spent in various parts of the boot loader. Set by the boot
+ loader. Use <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ to view this data. These variables are defined by the <ulink
+ url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Boot Counting</title>
+
+ <para><command>systemd-boot</command> implements a simple boot counting mechanism on top of the <ulink
+ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>, for automatic and unattended
+ fallback to older kernel versions/boot loader entries when a specific entry continously fails. Any boot loader
+ entry file and unified kernel image file that contains a <literal>+</literal> followed by one or two numbers (if
+ two they need to be separated by a <literal>-</literal>), before the <filename>.conf</filename> or
+ <filename>.efi</filename> suffix is subject to boot counting: the first of the two numbers ('tries left') is
+ decreased by one on every boot attempt, the second of the two numbers ('tries done') is increased by one (if 'tries
+ done' is absent it is considered equivalent to 0). Depending on the current value of these two counters the boot
+ entry is considered to be in one of three states:</para>
+
+ <orderedlist>
+ <listitem><para>If the 'tries left' counter of an entry is greater than zero the entry is considered to be in
+ 'indeterminate' state. This means the entry has not completed booting successfully yet, but also hasn't been
+ determined not to work.</para></listitem>
+
+ <listitem><para>If the 'tries left' counter of an entry is zero it is considered to be in 'bad' state. This means
+ no further attempts to boot this item will be made (that is, unless all other boot entries are also in 'bad'
+ state), as all attempts to boot this entry have not completed successfully.</para></listitem>
+
+ <listitem><para>If the 'tries left' and 'tries done' counters of an entry are absent it is considered to be in
+ 'good' state. This means further boot counting for the entry is turned off, as it successfully booted at least
+ once. The
+ <citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ service moves the currently booted entry from 'indeterminate' into 'good' state when a boot attempt completed
+ successfully.</para></listitem>
+ </orderedlist>
+
+ <para>Generally, when new entries are added to the boot loader, they first start out in 'indeterminate' state,
+ i.e. with a 'tries left' counter greater than zero. The boot entry remains in this state until either it managed to
+ complete a full boot successfully at least once (in which case it will be in 'good' state) — or the 'tries left'
+ counter reaches zero (in which case it will be in 'bad' state).</para>
+
+ <para>Example: let's say a boot loader entry file <filename>foo.conf</filename> is set up for 3 boot tries. The
+ installer will hence create it under the name <filename>foo+3.conf</filename>. On first boot, the boot loader will
+ rename it to <filename>foo+2-1.conf</filename>. If that boot does not complete successfully, the boot loader will
+ rename it to <filename>foo+1-2.conf</filename> on the following boot. If that fails too, it will finally be renamed
+ <filename>foo+0-3.conf</filename> by the boot loader on next boot, after which it will be considered 'bad'. If the
+ boot succeeds however the entry file will be renamed to <filename>foo.conf</filename> by the OS, so that it is
+ considered 'good' from then on.</para>
+
+ <para>The boot menu takes the 'tries left' counter into account when sorting the menu entries: entries in 'bad'
+ state are ordered at the end of the list, and entries in 'good' or 'indeterminate' at the beginning. The user can
+ freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry to boot is
+ automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the top item of
+ the menu is the one booted by default), and 'bad' entries will only be considered if there are no 'good' or
+ 'indeterminate' entries left.</para>
+
+ <para>The <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry> kernel
+ install framework optionally sets the initial 'tries left' counter to the value specified in
+ <filename>/etc/kernel/tries</filename> when a boot loader entry is first created.</para>
+ </refsect1>
+
+ <refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>loader.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- <ulink url="https://github.com/systemd/systemd/blob/master/doc/BOOT_LOADER_SPECIFICATION.md">Boot Loader Specification</ulink>,
- <ulink url="https://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface">Boot Loader Interface</ulink>
+ <citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
+ <ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>
</para>
</refsect1>
</refentry>
diff --git a/man/systemd-cgtop.xml b/man/systemd-cgtop.xml
index e5bd7a96e8..32ed66b3f5 100644
--- a/man/systemd-cgtop.xml
+++ b/man/systemd-cgtop.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/systemd-coredump.xml b/man/systemd-coredump.xml
index e011bf6b8b..b25f0c4f1c 100644
--- a/man/systemd-coredump.xml
+++ b/man/systemd-coredump.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/systemd-cryptsetup-generator.xml b/man/systemd-cryptsetup-generator.xml
index c37ee76b87..e30d69bfe7 100644
--- a/man/systemd-cryptsetup-generator.xml
+++ b/man/systemd-cryptsetup-generator.xml
@@ -144,6 +144,20 @@
to the one specified by <varname>rd.luks.key=</varname> or
<varname>luks.key=</varname> of the corresponding UUID, or the
password file that was specified without a UUID.</para>
+
+ <para>It is also possible to specify an external device which
+ should be mounted before we attempt to unlock the LUKS device.
+ systemd-cryptsetup will use password file stored on that
+ device. Device containing password file is specified by
+ appending colon and a device identifier to the password file
+ path. For example,
+ <varname>rd.luks.uuid=</varname>b40f1abf-2a53-400a-889a-2eccc27eaa40
+ <varname>rd.luks.key=</varname>b40f1abf-2a53-400a-889a-2eccc27eaa40=/keyfile:LABEL=keydev.
+ Hence, in this case, we will attempt to mount file system
+ residing on the block device with label <literal>keydev</literal>.
+ This syntax is for now only supported on a per-device basis,
+ i.e. you have to specify LUKS device UUID.</para>
+
<para><varname>rd.luks.key=</varname>
is honored only by initial RAM disk
(initrd) while
diff --git a/man/systemd-debug-generator.xml b/man/systemd-debug-generator.xml
index d5cf4109b0..fa88e8ac01 100644
--- a/man/systemd-debug-generator.xml
+++ b/man/systemd-debug-generator.xml
@@ -33,27 +33,38 @@
that reads the kernel command line and understands three
options:</para>
- <para>If the <option>systemd.mask=</option> option is specified
- and followed by a unit name, this unit is masked for the runtime,
- similar to the effect of
+ <para>If the <option>systemd.mask=</option> or <option>rd.systemd.mask=</option>
+ option is specified and followed by a unit name, this unit is
+ masked for the runtime, similar to the effect of
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>mask</command> command. This is useful to boot with
certain units removed from the initial boot transaction for
- debugging system startup. May be specified more than once.</para>
+ debugging system startup. May be specified more than once.
+ <option>rd.systemd.mask=</option> is honored only by initial
+ RAM disk (initrd) while <option>systemd.mask=</option> is
+ honored only in the main system.</para>
- <para>If the <option>systemd.wants=</option> option is specified
+ <para>If the <option>systemd.wants=</option> or
+ <option>rd.systemd.wants=</option> option is specified
and followed by a unit name, a start job for this unit is added to
the initial transaction. This is useful to start one or more
- additional units at boot. May be specified more than once.</para>
+ additional units at boot. May be specified more than once.
+ <option>rd.systemd.wants=</option> is honored only by initial
+ RAM disk (initrd) while <option>systemd.wants=</option> is
+ honored only in the main system.</para>
- <para>If the <option>systemd.debug_shell</option> option is
+ <para>If the <option>systemd.debug_shell</option> or
+ <option>rd.systemd.debug_shell</option> option is
specified, the debug shell service
<literal>debug-shell.service</literal> is pulled into the boot
transaction. It will spawn a debug shell on tty9 during early
system startup. Note that the shell may also be turned on
persistently by enabling it with
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
- <command>enable</command> command.</para>
+ <command>enable</command> command.
+ <option>rd.systemd.debug_shell=</option> is honored only by initial
+ RAM disk (initrd) while <option>systemd.debug_shell</option> is
+ honored only in the main system.</para>
<para><filename>systemd-debug-generator</filename> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
diff --git a/man/systemd-escape.xml b/man/systemd-escape.xml
index 41014abc73..f61c07ae9d 100644
--- a/man/systemd-escape.xml
+++ b/man/systemd-escape.xml
@@ -76,9 +76,12 @@
<listitem><para>Inserts the escaped strings in a unit name
template. Takes a unit name template such as
- <filename>foobar@.service</filename>. May not be used in
- conjunction with <option>--suffix=</option>,
- <option>--unescape</option> or
+ <filename>foobar@.service</filename>. With
+ <option>--unescape</option>, expects instantiated unit names
+ for this template and extracts and unescapes just the instance
+ part. May not be used in conjunction with
+ <option>--suffix=</option>,
+ <option>--instance</option> or
<option>--mangle</option>.</para></listitem>
</varlistentry>
@@ -100,8 +103,7 @@
<listitem><para>Instead of escaping the specified strings,
undo the escaping, reversing the operation. May not be used in
- conjunction with <option>--suffix=</option>,
- <option>--template=</option> or
+ conjunction with <option>--suffix=</option> or
<option>--mangle</option>.</para></listitem>
</varlistentry>
@@ -117,6 +119,19 @@
<option>--unescape</option>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--instance</option></term>
+
+ <listitem><para>With <option>--unescape</option>, unescape
+ and print only the instance part of an instantiated unit name
+ template. Results in an error for an uninstantiated template
+ like <filename>ssh@.service</filename> or a non-template name
+ like <filename>ssh.service</filename>.
+ Must be used in conjunction with <option>--unescape</option>
+ and may not be used in conjunction with
+ <option>--template</option>.</para></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
@@ -141,6 +156,14 @@ tmp-waldi-foobar.mount</programlisting>
<para>To generate instance names of three strings:</para>
<programlisting>$ systemd-escape --template=systemd-nspawn@.service 'My Container 1' 'containerb' 'container/III'
systemd-nspawn@My\x20Container\x201.service systemd-nspawn@containerb.service systemd-nspawn@container-III.service</programlisting>
+
+ <para>To extract the instance part of an instantiated unit:</para>
+ <programlisting>$ systemd-escape -u --instance 'systemd-nspawn@My\x20Container\x201.service'
+My Container 1</programlisting>
+
+ <para>To extract the instance part of an instance of a particular template:</para>
+ <programlisting>$ systemd-escape -u --template=systemd-nspawn@.service 'systemd-nspawn@My\x20Container\x201.service'
+My Container 1</programlisting>
</refsect1>
<refsect1>
diff --git a/man/systemd-hibernate-resume-generator.xml b/man/systemd-hibernate-resume-generator.xml
index 86d45dc4af..8f0cc5d044 100644
--- a/man/systemd-hibernate-resume-generator.xml
+++ b/man/systemd-hibernate-resume-generator.xml
@@ -28,11 +28,13 @@
<refsect1>
<title>Description</title>
- <para><filename>systemd-hibernate-resume-generator</filename> is a
- generator that instantiates
+ <para><command>systemd-hibernate-resume-generator</command> is a
+ generator that initiates the procedure to resume the system from hibernation.
+ It instantiates the
<citerefentry><refentrytitle>systemd-hibernate-resume@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
unit according to the value of <option>resume=</option> parameter
- specified on the kernel command line.</para>
+ specified on the kernel command line, which will instruct the kernel
+ to resume the system from the hibernation image on that device.</para>
</refsect1>
<refsect1>
@@ -54,6 +56,12 @@
supported.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>noresume</varname></term>
+
+ <listitem><para>Do not try to resume from hibernation. If this parameter is
+ present, <varname>resume=</varname> is ignored.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/man/systemd-hwdb.xml b/man/systemd-hwdb.xml
index 5af868d52b..6c8487a893 100644
--- a/man/systemd-hwdb.xml
+++ b/man/systemd-hwdb.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/systemd-id128.xml b/man/systemd-id128.xml
new file mode 100644
index 0000000000..8a76cccd86
--- /dev/null
+++ b/man/systemd-id128.xml
@@ -0,0 +1,122 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="systemd-id128" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>systemd-id128</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-id128</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-id128</refname>
+ <refpurpose>Generate and print sd-128 identifiers</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>systemd-id128</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain">new</arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>systemd-id128</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain">machine-id</arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>systemd-id128</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain">boot-id</arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>systemd-id128</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain">invocation-id</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>id128</command> may be used to conveniently print
+ <citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ UUIDs. What identifier is printed depends on the specific verb.</para>
+
+ <para>With <command>new</command>, a new random identifier will be generated.</para>
+
+ <para>With <command>machine-id</command>, the identifier of the current machine will be
+ printed. See
+ <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para>
+
+ <para>With <command>boot-id</command>, the identifier of the current boot will be
+ printed.</para>
+
+ <para>Both <command>machine-id</command> and <command>boot-id</command> may be combined
+ with the <option>--app-specific=<replaceable>app-id</replaceable></option> switch to
+ generate application-specific IDs. See
+ <citerefentry><refentrytitle>sd_id128_get_machine</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ for the discussion when this is useful.</para>
+
+ <para>With <command>invocation-id</command>, the identifier of the current service invocation
+ will be printed. This is available in systemd services. See
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following options are understood:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>-p</option></term>
+ <term><option>--pretty</option></term>
+
+ <listitem><para>Generate output as programming language snippets.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-a <replaceable>app-id</replaceable></option></term>
+ <term><option>--app-specific=<replaceable>app-id</replaceable></option></term>
+
+ <listitem><para>With this option, an identifier that is the result of hashing the
+ application identifier <replaceable>app-id</replaceable> and the machine identifier will be
+ printed. The <replaceable>app-id</replaceable> argument must be a valid sd-id128 string
+ identifying the application.</para>
+ </listitem>
+ </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Exit status</title>
+
+ <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_id128_get_machine</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/systemd-inhibit.xml b/man/systemd-inhibit.xml
index 03dc06678a..3fa5acf550 100644
--- a/man/systemd-inhibit.xml
+++ b/man/systemd-inhibit.xml
@@ -119,6 +119,7 @@
</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>
diff --git a/man/systemd-journal-gatewayd.service.xml b/man/systemd-journal-gatewayd.service.xml
index 794bc10a7b..13604a041b 100644
--- a/man/systemd-journal-gatewayd.service.xml
+++ b/man/systemd-journal-gatewayd.service.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/systemd-journal-remote.service.xml b/man/systemd-journal-remote.service.xml
index f2bfc36668..6e6819be04 100644
--- a/man/systemd-journal-remote.service.xml
+++ b/man/systemd-journal-remote.service.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
diff --git a/man/systemd-journal-upload.service.xml b/man/systemd-journal-upload.service.xml
index 38fc010ab6..cdeadddda8 100644
--- a/man/systemd-journal-upload.service.xml
+++ b/man/systemd-journal-upload.service.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
diff --git a/man/systemd-journald.service.xml b/man/systemd-journald.service.xml
index 6d0aaa4587..9167993012 100644
--- a/man/systemd-journald.service.xml
+++ b/man/systemd-journald.service.xml
@@ -52,7 +52,7 @@
<listitem><para>Structured system log messages via the native
Journal API, see
- <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>4</manvolnum></citerefentry></para></listitem>
+ <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry></para></listitem>
<listitem><para>Standard output and standard error of service units. For further details see
below.</para></listitem>
@@ -206,10 +206,10 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
<para>Journal files are, by default, owned and readable by the
<literal>systemd-journal</literal> system group but are not
- writable. Adding a user to this group thus enables her/him to read
+ writable. Adding a user to this group thus enables them to read
the journal files.</para>
- <para>By default, each logged in user will get her/his own set of
+ <para>By default, each logged in user will get their own set of
journal files in <filename>/var/log/journal/</filename>. These
files will not be owned by the user, however, in order to avoid
that the user can write to them directly. Instead, file system
@@ -303,7 +303,7 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
<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><refentrytitle>sd_journal_print</refentrytitle><manvolnum>4</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<command>pydoc systemd.journal</command>
</para>
</refsect1>
diff --git a/man/systemd-logind.service.xml b/man/systemd-logind.service.xml
index 33ed8f522e..1c29b33776 100644
--- a/man/systemd-logind.service.xml
+++ b/man/systemd-logind.service.xml
@@ -45,8 +45,10 @@
a session, then this ID is reused as the session ID. Otherwise, an independent session counter is
used.</para></listitem>
- <listitem><para>Providing PolicyKit-based access for users for
- operations such as system shutdown or sleep</para></listitem>
+ <listitem><para>Providing <ulink
+ url="http://www.freedesktop.org/wiki/Software/polkit">polkit</ulink>-based
+ access for users for operations such as system shutdown or sleep</para>
+ </listitem>
<listitem><para>Implementing a shutdown/sleep inhibition logic
for applications</para></listitem>
diff --git a/man/systemd-machine-id-setup.xml b/man/systemd-machine-id-setup.xml
index 9e84fd8ccb..aea13caab9 100644
--- a/man/systemd-machine-id-setup.xml
+++ b/man/systemd-machine-id-setup.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/systemd-mount.xml b/man/systemd-mount.xml
index c75e572026..610c97f948 100644
--- a/man/systemd-mount.xml
+++ b/man/systemd-mount.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -114,7 +114,7 @@
the command line. If passed, additional metadata is read from the device to enhance the unit to create. For
example, a descriptive string for the transient units is generated from the file system label and device
model. Moreover if a removable block device (e.g. USB stick) is detected an automount unit instead of a regular
- mount unit is created, with a short idle time-out, in order to ensure the file-system is placed in a clean
+ mount unit is created, with a short idle timeout, in order to ensure the file-system is placed in a clean
state quickly after each access.</para></listitem>
</varlistentry>
diff --git a/man/systemd-networkd.service.xml b/man/systemd-networkd.service.xml
index bac43667b9..2127bf1814 100644
--- a/man/systemd-networkd.service.xml
+++ b/man/systemd-networkd.service.xml
@@ -59,7 +59,7 @@
<para>When <filename>systemd-networkd</filename> exits, it generally leaves
existing network devices and configuration intact. This makes it possible to
- transition from the initrams and to restart the service without breaking
+ transition from the initramfs and to restart the service without breaking
connectivity. This also means that when configuration is updated and
<filename>systemd-networkd</filename> is restarted, netdev interfaces for
which configuration was removed will not be dropped, and may need to be
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index 284c9b294b..ff6b6a9a8b 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY fedora_latest_version "28">
@@ -24,7 +24,7 @@
<refnamediv>
<refname>systemd-nspawn</refname>
- <refpurpose>Spawn a namespace container for debugging, testing and building</refpurpose>
+ <refpurpose>Spawn a command or OS in a light-weight container</refpurpose>
</refnamediv>
<refsynopsisdiv>
@@ -55,7 +55,7 @@
<para><command>systemd-nspawn</command> may be invoked on any directory tree containing an operating system tree,
using the <option>--directory=</option> command line option. By using the <option>--machine=</option> option an OS
tree is automatically searched for in a couple of locations, most importantly in
- <filename>/var/lib/machines</filename>, the suggested directory to place container images installed on the
+ <filename>/var/lib/machines</filename>, the suggested directory to place OS container images installed on the
system.</para>
<para>In contrast to <citerefentry
diff --git a/man/systemd-portabled.service.xml b/man/systemd-portabled.service.xml
index a6bd6c52e7..8c5e4cc5a1 100644
--- a/man/systemd-portabled.service.xml
+++ b/man/systemd-portabled.service.xml
@@ -36,7 +36,7 @@
<para>Most of <command>systemd-portabled</command>'s functionality is accessible through the
<citerefentry><refentrytitle>portablectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> command.</para>
- <para>See the <ulink url="https://github.com/systemd/systemd/blob/master/doc/PORTABLE_SERVICES.md">Portable
+ <para>See the <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable
Services Documentation</ulink> for details about the concepts this service implements.</para>
</refsect1>
diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml
index c895adaaf3..d7334e01ec 100644
--- a/man/systemd-resolved.service.xml
+++ b/man/systemd-resolved.service.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -66,14 +66,21 @@
<filename>/etc/systemd/resolved.conf</filename>, the per-link static settings in
<filename>/etc/systemd/network/*.network</filename> files (in case
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> is
- used), the per-link dynamic settings received over DHCP, and any DNS server information made available by other
- system services. See
+ used), the per-link dynamic settings received over DHCP, user request made via
+ <citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, and any DNS server
+ information made available by other system services. See
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details
about systemd's own configuration files for DNS servers. To improve compatibility,
<filename>/etc/resolv.conf</filename> is read in order to discover configured system DNS servers, but only if it is
- not a symlink to <filename>/run/systemd/resolve/stub-resolv.conf</filename> or
- <filename>/run/systemd/resolve/resolv.conf</filename> (see below).</para>
+ not a symlink to <filename>/run/systemd/resolve/stub-resolv.conf</filename>,
+ <filename>/usr/lib/systemd/resolv.conf</filename> or <filename>/run/systemd/resolve/resolv.conf</filename> (see
+ below).</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Synthetic Records</title>
<para><command>systemd-resolved</command> synthesizes DNS resource records (RRs) for the following cases:</para>
@@ -99,6 +106,10 @@
to their configured addresses and back, but they will not affect lookups for
non-address types (like MX).</para></listitem>
</itemizedlist>
+ </refsect1>
+
+ <refsect1>
+ <title>Protocols and Routing</title>
<para>Lookup requests are routed to the available DNS servers, LLMNR and MulticastDNS interfaces according to the
following rules:</para>
@@ -132,16 +143,45 @@
lookup zones on all matching interfaces). If the lookup failed on
all interfaces, the last failing response is returned.</para>
- <para>Routing of lookups may be influenced by configuring
- per-interface domain names. See
- <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details. Lookups for a hostname ending in one of the
- per-interface domains are exclusively routed to the matching
- interfaces.</para>
+ <para>Routing of lookups may be influenced by configuring per-interface domain names and other settings. See
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
+ <citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for details. The
+ following query routing logic applies for unicast DNS traffic:</para>
+
+ <itemizedlist>
+ <listitem><para>If a name to look up matches (that is: is equal to or has as suffix) any of the configured search
+ or route-only domains of any link (or the globally configured DNS settings), the "best matching"
+ search/route-only domain is determined: the matching one with the most labels. The query is then sent to all DNS
+ servers of any links or the globally configured DNS servers associated with this "best matching"
+ search/route-only domain. (Note that more than one link might have this same "best matching" search/route-only
+ domain configured, in which case the query is sent to all of them in parallel).</para></listitem>
+
+ <listitem><para>If a query does not match any configured search/route-only domain (neither per-link nor global),
+ it is sent to all DNS servers that are configured on links with the "DNS default route" option set, as well as
+ the globally configured DNS server.</para></listitem>
+
+ <listitem><para>If there is no link configured as "DNS default route" and no global DNS server configured, the
+ compiled-in fallback DNS server is used.</para></listitem>
+
+ <listitem><para>Otherwise the query is failed as no suitable DNS servers could be determined.</para></listitem>
+ </itemizedlist>
+
+ <para>The "DNS default route" option is a boolean setting configureable with <command>resolvectl</command> or in
+ <filename>.network</filename> files. If not set, it is implicitly determined based on the configured DNS domains
+ for a link: if there's any route-only domain (not matching <literal>~.</literal>) it defaults to false, otherwise
+ to true.</para>
+
+ <para>Effectively this means: in order to preferably route all DNS queries not explicitly matched by
+ search/route-only domain configuration to a specific link, configure a <literal>~.</literal> route-only domain on
+ it. This will ensure that other links will not be considered for the queries (unless they too carry such a
+ route-only domain). In order to route all such DNS queries to a specific link only in case no other link is
+ preferable, then set the "DNS default route" option for the link to true, and do not configure a
+ <literal>~.</literal> route-only domain on it. Finally, in order to ensure that a specific link never receives any
+ DNS traffic not matching any of its configured search/route-only domains, set the "DNS default route" option for it
+ to false.</para>
<para>See the <ulink url="https://www.freedesktop.org/wiki/Software/systemd/resolved"> resolved D-Bus API
Documentation</ulink> for information about the APIs <filename>systemd-resolved</filename> provides.</para>
-
</refsect1>
<refsect1>
diff --git a/man/systemd-run-generator.xml b/man/systemd-run-generator.xml
new file mode 100644
index 0000000000..20eb6916eb
--- /dev/null
+++ b/man/systemd-run-generator.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+<refentry id="systemd-run-generator">
+
+ <refentryinfo>
+ <title>systemd-run-generator</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-run-generator</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-run-generator</refname>
+ <refpurpose>Generator for invoking commands specified on the kernel command line as system service</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>/usr/lib/systemd/system-generators/systemd-run-generator</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>systemd-run-generator</filename> is a generator
+ that reads the kernel command line and understands three
+ options:</para>
+
+ <para>If the <option>systemd.run=</option> option is specified and followed by a command line, a unit named
+ <filename>kernel-command-line.service</filename> is generated for it and booted into. The service has
+ <varname>Type=oneshot</varname> set, and has <varname>SuccessAction=exit</varname> and
+ <varname>FailureAction=exit</varname> configured by default, thus ensuring that the system is shut down as soon as
+ the command completes. The exit status of the command line is propagated to the invoking container manager, if
+ this applies (which might propagate this further, to the calling shell — e.g.
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>7</manvolnum></citerefentry> does this). If
+ this option is used multiple times the unit file will contain multiple <varname>ExecStart=</varname> lines, to
+ execute all commands in order. The command is started as regular service, i.e. with
+ <varname>DefaultDependencies=</varname> on. </para>
+
+ <para>Use <option>systemd.run_success_action=</option> and <option>systemd.run_failure_action=</option> to tweak
+ how to react to the process completing. In particular assigning <literal>none</literal> will leave the system
+ running after the command completes. For further details on supported arguments, see
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
+ <para><filename>systemd-run-generator</filename> implements
+ <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Example</title>
+
+ <para>Use a command like the following to add a user to the user database inside a container run with
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>7</manvolnum></citerefentry>:</para>
+
+ <programlisting># systemd-nspawn -D mycontainer -b systemd.run='"adduser test"'</programlisting>
+ <para>(Note the requirement for double quoting in the command line above. The first level of quoting ('') is
+ processed and removed by the command shell used to invoke <command>systemd-nspawn</command>. The second level of
+ quoting ("") is propagated to the kernel command line of the container and processed and removed by
+ <command>systemd-run-generator</command>. Both together make sure both words of the specified command line
+ <command>adduser test</command> end up in the generated unit file together and are neither split apart by the
+ command shell nor by the generator.)</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/systemd-run.xml b/man/systemd-run.xml
index 1c254afae3..3c60340dc1 100644
--- a/man/systemd-run.xml
+++ b/man/systemd-run.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -83,6 +83,16 @@
<replaceable>COMMAND</replaceable> may be omitted. In this case, <command>systemd-run</command> creates only a
<filename>.path</filename>, <filename>.socket</filename>, or <filename>.timer</filename> unit that triggers the
specified unit.</para>
+
+ <para>By default, services created with <command>systemd-run</command> default to the <option>simple</option> type,
+ see the description of <varname>Type=</varname> in
+ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details. Note that when this type is used the service manager (and thus the <command>systemd-run</command> command)
+ considers service start-up successful as soon as the <function>fork()</function> for the main service process
+ succeeded, i.e. before the <function>execve()</function> is invoked, and thus even if the specified command cannot
+ be started. Consider using the <option>exec</option> service type (i.e. <option>--property=Type=exec</option>) to
+ ensure that <command>systemd-run</command> returns successfully only if the specified command line has been
+ successfully started.</para>
</refsect1>
<refsect1>
@@ -198,6 +208,23 @@
</varlistentry>
<varlistentry>
+ <term><option>--working-directory=</option></term>
+
+ <listitem><para>Runs the service process with the specified working directory. Also see
+ <varname>WorkingDirectory=</varname> in
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--same-dir</option></term>
+ <term><option>-d</option></term>
+
+ <listitem><para>Similar to <option>--working-directory=</option> but uses the current working directory of the
+ caller for the service to execute.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
<term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
@@ -248,6 +275,15 @@
</varlistentry>
<varlistentry>
+ <term><option>--shell</option></term>
+ <term><option>-S</option></term>
+
+ <listitem><para>A shortcut for <literal>--pty --same-dir --wait --collect --service-type=exec $SHELL</literal>,
+ i.e. requests an interactive shell in the current working directory, running in service context, accessible
+ with a single switch.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--quiet</option></term>
<term><option>-q</option></term>
diff --git a/man/systemd-sleep.conf.xml b/man/systemd-sleep.conf.xml
index bd1f36aa62..96e6d5e452 100644
--- a/man/systemd-sleep.conf.xml
+++ b/man/systemd-sleep.conf.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -114,6 +114,24 @@
<variablelist class='systemd-directives'>
<varlistentry>
+ <term><varname>AllowSuspend=</varname></term>
+ <term><varname>AllowHibernation=</varname></term>
+ <term><varname>AllowSuspendThenHibernate=</varname></term>
+ <term><varname>AllowHybridSleep=</varname></term>
+
+ <listitem><para>By default any power-saving mode is advertised if possible (i.e.
+ the kernel supports that mode, the necessary resources are available). Those
+ switches can be used to disable specific modes.</para>
+
+ <para>If <varname>AllowHibernation=no</varname> or <varname>AllowSuspend=no</varname> is
+ used, this implies <varname>AllowSuspendThenHibernate=no</varname> and
+ <varname>AllowHybridSleep=no</varname>, since those methods use both suspend and hibernation
+ internally. <varname>AllowSuspendThenHibernate=yes</varname> and
+ <varname>AllowHybridSleep=yes</varname> can be used to override and enable those specific
+ modes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>SuspendMode=</varname></term>
<term><varname>HibernateMode=</varname></term>
<term><varname>HybridSleepMode=</varname></term>
diff --git a/man/systemd-socket-activate.xml b/man/systemd-socket-activate.xml
index cb0065dfd3..c3707050df 100644
--- a/man/systemd-socket-activate.xml
+++ b/man/systemd-socket-activate.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/systemd-socket-proxyd.xml b/man/systemd-socket-proxyd.xml
index 34a87ac8e4..1869465a3b 100644
--- a/man/systemd-socket-proxyd.xml
+++ b/man/systemd-socket-proxyd.xml
@@ -4,8 +4,6 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
SPDX-License-Identifier: LGPL-2.1+
-
- Copyright © 2013 David Strauss
-->
<refentry id="systemd-socket-proxyd"
xmlns:xi="http://www.w3.org/2001/XInclude">
@@ -54,7 +52,7 @@
<citerefentry project='die-net'><refentrytitle>socat</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
The main differences for <command>systemd-socket-proxyd</command>
are support for socket activation with
- <literal>Accept=false</literal> and an event-driven
+ <literal>Accept=no</literal> and an event-driven
design that scales better with the number of
connections.</para>
</refsect1>
diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
index a914ef2523..35da82ab1a 100644
--- a/man/systemd-system.conf.xml
+++ b/man/systemd-system.conf.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
@@ -99,34 +99,11 @@
<varlistentry>
<term><varname>CPUAffinity=</varname></term>
- <listitem><para>Configures the initial CPU affinity for the
- init process. Takes a list of CPU indices or ranges separated
- by either whitespace or commas. CPU ranges are specified by
- the lower and upper CPU indices separated by a
- dash.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>JoinControllers=cpu,cpuacct net_cls,netprio</varname></term>
-
- <listitem><para>Configures controllers that shall be mounted
- in a single hierarchy. By default, systemd will mount all
- controllers which are enabled in the kernel in individual
- hierarchies, with the exception of those listed in this
- setting. Takes a space-separated list of comma-separated
- controller names, in order to allow multiple joined
- hierarchies. Defaults to 'cpu,cpuacct'. Pass an empty string
- to ensure that systemd mounts all controllers in separate
- hierarchies.</para>
-
- <para>Note that this option is only applied once, at very
- early boot. If you use an initial RAM disk (initrd) that uses
- systemd, it might hence be necessary to rebuild the initrd if
- this option is changed, and make sure the new configuration
- file is included in it. Otherwise, the initrd might mount the
- controller hierarchies in a different configuration than
- intended, and the main system cannot remount them
- anymore.</para></listitem>
+ <listitem><para>Configures the CPU affinity for the service manager as well as the default CPU affinity for all
+ forked off processes. Takes a list of CPU indices or ranges separated by either whitespace or commas. CPU
+ ranges are specified by the lower and upper CPU indices separated by a dash. Individual services may override
+ the CPU affinity for their processes with the <varname>CPUAffinity=</varname> setting in unit files, see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
<varlistentry>
@@ -322,15 +299,17 @@
<term><varname>DefaultBlockIOAccounting=</varname></term>
<term><varname>DefaultMemoryAccounting=</varname></term>
<term><varname>DefaultTasksAccounting=</varname></term>
+ <term><varname>DefaultIOAccounting=</varname></term>
<term><varname>DefaultIPAccounting=</varname></term>
<listitem><para>Configure the default resource accounting settings, as configured per-unit by
<varname>CPUAccounting=</varname>, <varname>BlockIOAccounting=</varname>, <varname>MemoryAccounting=</varname>,
- <varname>TasksAccounting=</varname> and <varname>IPAccounting=</varname>. See
+ <varname>TasksAccounting=</varname>, <varname>IOAccounting=</varname> and <varname>IPAccounting=</varname>. See
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details on the per-unit settings. <varname>DefaultTasksAccounting=</varname> defaults to on,
- <varname>DefaultMemoryAccounting=</varname> to &MEMORY_ACCOUNTING_DEFAULT;,
- the other three settings to off.</para></listitem>
+ for details on the per-unit settings. <varname>DefaultTasksAccounting=</varname> defaults to yes,
+ <varname>DefaultMemoryAccounting=</varname> to &MEMORY_ACCOUNTING_DEFAULT;. <varname>DefaultCPUAccounting=</varname>
+ defaults to yes if enabling CPU accounting doesn't require the CPU controller to be enabled (Linux 4.15+ using the
+ unified hierarchy for resource control), otherwise it defaults to no. The other three settings default to no.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd-sysusers.xml b/man/systemd-sysusers.xml
index 7da2c55d27..6c9b921f05 100644
--- a/man/systemd-sysusers.xml
+++ b/man/systemd-sysusers.xml
@@ -130,7 +130,8 @@
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <ulink url="https://systemd.io/UIDS-GIDS">Users, Groups, UIDs and GIDs on systemd systems</ulink>
</para>
</refsect1>
diff --git a/man/systemd-time-wait-sync.service.xml b/man/systemd-time-wait-sync.service.xml
index a967371246..5667c886ea 100644
--- a/man/systemd-time-wait-sync.service.xml
+++ b/man/systemd-time-wait-sync.service.xml
@@ -4,8 +4,6 @@
<!--
SPDX-License-Identifier: LGPL-2.1+
-
- Copyright © 2018 Peter A. Bigot
-->
<refentry id="systemd-time-wait-sync.service" conditional='ENABLE_TIMESYNCD'>
diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml
index 9ff114feef..9978d95cab 100644
--- a/man/systemd-tmpfiles.xml
+++ b/man/systemd-tmpfiles.xml
@@ -176,14 +176,12 @@
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
- <para>It is possible to combine <option>--create</option>,
- <option>--clean</option>, and <option>--remove</option> in one
- invocation. For example, during boot the following command line is
- executed to ensure that all temporary and volatile directories are
+ <para>It is possible to combine <option>--create</option>, <option>--clean</option>, and <option>--remove</option>
+ in one invocation (in which case removal and clean-up are executed before creation of new files). For example,
+ during boot the following command line is executed to ensure that all temporary and volatile directories are
removed and created according to the configuration file:</para>
<programlisting>systemd-tmpfiles --remove --create</programlisting>
-
</refsect1>
<refsect1>
diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml
index 73c77ea690..330700d7dd 100644
--- a/man/systemd-udevd.service.xml
+++ b/man/systemd-udevd.service.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -170,6 +170,26 @@
when possible. It is enabled by default; specifying 0 disables it.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>net.naming-scheme=</varname></term>
+ <listitem>
+ <para>Network interfaces are renamed to give them predictable names when possible (unless
+ <varname>net.ifnames=0</varname> is specified, see above). The names are derived from various
+ device metadata fields. Newer versions of <filename>systemd-udevd.service</filename> take more of
+ these fields into account, improving (and thus possibly changing) the names used for the same
+ devices. With this kernel command line option it is possible to pick a specific version of this
+ algorithm. It expects a naming scheme identifier as argument. Currently the following identifiers
+ are known: <literal>v238</literal>, <literal>v239</literal>, <literal>v240</literal> which each
+ implement the naming scheme that was the default in the indicated systemd version. In addition,
+ <literal>latest</literal> may be used to designate the latest scheme known (to this particular
+ version of <filename>systemd-udevd.service</filename>).</para>
+
+ <para>Note that selecting a specific scheme is not sufficient to fully stabilize interface naming:
+ the naming is generally derived from driver attributes exposed by the kernel. As the kernel is
+ updated, previously missing attributes <filename>systemd-udevd.service</filename> is checking might
+ appear, which affects older name derivation algorithms, too.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
<!-- when adding entries here, consider also adding them
in kernel-command-line.xml -->
diff --git a/man/systemd.dnssd.xml b/man/systemd.dnssd.xml
index b3a4c68e3f..dc00591fcd 100644
--- a/man/systemd.dnssd.xml
+++ b/man/systemd.dnssd.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 3bd790b485..6419bee499 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -81,6 +81,9 @@
<refsect1>
<title>Paths</title>
+ <para>The following settings may be used to change a service's view of the filesystem. Please note that the paths
+ must be absolute and must not contain a <literal>..</literal> path component.</para>
+
<variablelist class='unit-directives'>
<varlistentry>
@@ -121,7 +124,16 @@
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
- Specification</ulink>.</para></listitem>
+ Specification</ulink>.</para>
+
+ <para>When <varname>DevicePolicy=</varname> is set to <literal>closed</literal> or <literal>strict</literal>,
+ or set to <literal>auto</literal> and <varname>DeviceAllow=</varname> is set, then this setting adds
+ <filename>/dev/loop-control</filename> with <constant>rw</constant> mode, <literal>block-loop</literal> and
+ <literal>block-blkext</literal> with <constant>rwm</constant> mode to <varname>DeviceAllow=</varname>. See
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for the details about <varname>DevicePolicy=</varname> or <varname>DeviceAllow=</varname>. Also, see
+ <varname>PrivateDevices=</varname> below, as it may change the setting of <varname>DevicePolicy=</varname>.
+ </para></listitem>
</varlistentry>
<varlistentry>
@@ -738,6 +750,20 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<refsect1>
<title>Sandboxing</title>
+ <para>The following sandboxing options are an effective way to limit the exposure of the system towards the unit's
+ processes. It is recommended to turn on as many of these options for each unit as is possible without negatively
+ affecting the process' ability to operate. Note that many of these sandboxing features are gracefully turned off on
+ systems where the underlying security mechanism is not available. For example, <varname>ProtectSystem=</varname>
+ has no effect if the kernel is built without file system namespacing or if the service manager runs in a container
+ manager that makes file system namespacing unavailable to its payload. Similar,
+ <varname>RestrictRealtime=</varname> has no effect on systems that lack support for SECCOMP system call filtering,
+ or in containers where support for this is turned off.</para>
+
+ <para>Also note that some sandboxing functionality is generally not available in user services (i.e. services run
+ by the per-user service manager). Specifically, the various settings requiring file system namespacing support
+ (such as <varname>ProtectSystem=</varname>) are not available, as the underlying kernel functionality is only
+ accessible to privileged processes.</para>
+
<variablelist>
<varlistentry>
@@ -755,9 +781,9 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
recommended to enable this setting for all long-running services, unless they are involved with system updates
or need to modify the operating system in other ways. If this option is used,
<varname>ReadWritePaths=</varname> may be used to exclude specific directories from being made read-only. This
- setting is implied if <varname>DynamicUser=</varname> is set. For this setting the same restrictions regarding
- mount propagation and privileges apply as for <varname>ReadOnlyPaths=</varname> and related calls, see
- below. Defaults to off.</para></listitem>
+ setting is implied if <varname>DynamicUser=</varname> is set. This setting cannot ensure protection in all
+ cases. In general it has the same limitations as <varname>ReadOnlyPaths=</varname>, see below. Defaults to
+ off.</para></listitem>
</varlistentry>
<varlistentry>
@@ -776,11 +802,11 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<varname>ReadOnlyPaths=</varname>, and <literal>tmpfs</literal> is mostly equivalent to
<varname>TemporaryFileSystem=</varname>.</para>
- <para> It is recommended to enable this setting for all long-running services (in particular network-facing ones),
- to ensure they cannot get access to private user data, unless the services actually require access to the user's
- private data. This setting is implied if <varname>DynamicUser=</varname> is set. For this setting the same
- restrictions regarding mount propagation and privileges apply as for <varname>ReadOnlyPaths=</varname> and related
- calls, see below.</para></listitem>
+ <para> It is recommended to enable this setting for all long-running services (in particular network-facing
+ ones), to ensure they cannot get access to private user data, unless the services actually require access to
+ the user's private data. This setting is implied if <varname>DynamicUser=</varname> is set. This setting cannot
+ ensure protection in all cases. In general it has the same limitations as <varname>ReadOnlyPaths=</varname>,
+ see below.</para></listitem>
</varlistentry>
<varlistentry>
@@ -793,15 +819,18 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<listitem><para>These options take a whitespace-separated list of directory names. The specified directory
names must be relative, and may not include <literal>..</literal>. If set, one or more
directories by the specified names will be created (including their parents) below the locations
- defined in the following table, when the unit is started.</para>
+ defined in the following table, when the unit is started. Also, the corresponding environment variable
+ is defined with the full path of directories. If multiple directories are set, then int the environment variable
+ the paths are concatenated with colon (<literal>:</literal>).</para>
<table>
- <title>Automatic directory creation</title>
- <tgroup cols='3'>
+ <title>Automatic directory creation and environment variables</title>
+ <tgroup cols='4'>
<thead>
<row>
<entry>Locations</entry>
<entry>for system</entry>
<entry>for users</entry>
+ <entry>Environment variable</entry>
</row>
</thead>
<tbody>
@@ -809,26 +838,31 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<entry><varname>RuntimeDirectory=</varname></entry>
<entry><filename>/run</filename></entry>
<entry><varname>$XDG_RUNTIME_DIR</varname></entry>
+ <entry><varname>$RUNTIME_DIRECTORY</varname></entry>
</row>
<row>
<entry><varname>StateDirectory=</varname></entry>
<entry><filename>/var/lib</filename></entry>
<entry><varname>$XDG_CONFIG_HOME</varname></entry>
+ <entry><varname>$STATE_DIRECTORY</varname></entry>
</row>
<row>
<entry><varname>CacheDirectory=</varname></entry>
<entry><filename>/var/cache</filename></entry>
<entry><varname>$XDG_CACHE_HOME</varname></entry>
+ <entry><varname>$CACHE_DIRECTORY</varname></entry>
</row>
<row>
<entry><varname>LogsDirectory=</varname></entry>
<entry><filename>/var/log</filename></entry>
<entry><varname>$XDG_CONFIG_HOME</varname><filename>/log</filename></entry>
+ <entry><varname>$LOGS_DIRECTORY</varname></entry>
</row>
<row>
<entry><varname>ConfigurationDirectory=</varname></entry>
<entry><filename>/etc</filename></entry>
<entry><varname>$XDG_CONFIG_HOME</varname></entry>
+ <entry><varname>$CONFIGURATION_DIRECTORY</varname></entry>
</row>
</tbody>
</tgroup>
@@ -878,7 +912,13 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<filename>/run/foo/bar</filename>, and <filename>/run/baz</filename>. The directories
<filename>/run/foo/bar</filename> and <filename>/run/baz</filename> except <filename>/run/foo</filename> are
owned by the user and group specified in <varname>User=</varname> and <varname>Group=</varname>, and removed
- when the service is stopped.</para></listitem>
+ when the service is stopped.</para>
+
+ <para>Example: if a system service unit has the following,
+ <programlisting>RuntimeDirectory=foo/bar
+StateDirectory=aaa/bbb ccc</programlisting>
+ then the environment variable <literal>RUNTIME_DIRECTORY</literal> is set with <literal>/run/foo/bar</literal>, and
+ <literal>STATE_DIRECTORY</literal> is set with <literal>/var/lib/aaa/bbb:/var/lib/ccc</literal>.</para></listitem>
</varlistentry>
<varlistentry>
@@ -934,8 +974,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<varname>BindPaths=</varname>, or <varname>BindReadOnlyPaths=</varname> inside it. For a more flexible option,
see <varname>TemporaryFileSystem=</varname>.</para>
- <para>Note that restricting access with these options does not extend to submounts of a directory that are
- created later on. Non-directory paths may be specified as well. These options may be specified more than once,
+ <para>Non-directory paths may be specified as well. These options may be specified more than once,
in which case all paths listed will have limited access from within the namespace. If the empty string is
assigned to this option, the specific list is reset, and all prior assignments have no effect.</para>
@@ -947,11 +986,19 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<literal>+</literal> on the same path make sure to specify <literal>-</literal> first, and <literal>+</literal>
second.</para>
- <para>Note that using this setting will disconnect propagation of mounts from the service to the host
- (propagation in the opposite direction continues to work). This means that this setting may not be used for
- services which shall be able to install mount points in the main mount namespace. Note that the effect of these
- settings may be undone by privileged processes. In order to set up an effective sandboxed environment for a
- unit it is thus recommended to combine these settings with either
+ <para>Note that these settings will disconnect propagation of mounts from the unit's processes to the
+ host. This means that this setting may not be used for services which shall be able to install mount points in
+ the main mount namespace. For <varname>ReadWritePaths=</varname> and <varname>ReadOnlyPaths=</varname>
+ propagation in the other direction is not affected, i.e. mounts created on the host generally appear in the
+ unit processes' namespace, and mounts removed on the host also disappear there too. In particular, note that
+ mount propagation from host to unit will result in unmodified mounts to be created in the unit's namespace,
+ i.e. writable mounts appearing on the host will be writable in the unit's namespace too, even when propagated
+ below a path marked with <varname>ReadOnlyPaths=</varname>! Restricting access with these options hence does
+ not extend to submounts of a directory that are created later on. This means the lock-down offered by that
+ setting is not complete, and does not offer full protection. </para>
+
+ <para>Note that the effect of these settings may be undone by privileged processes. In order to set up an
+ effective sandboxed environment for a unit it is thus recommended to combine these settings with either
<varname>CapabilityBoundingSet=~CAP_SYS_ADMIN</varname> or
<varname>SystemCallFilter=~@mount</varname>.</para></listitem>
</varlistentry>
@@ -1043,9 +1090,13 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
Defaults to false. It is possible to run two or more units within the same private network namespace by using
the <varname>JoinsNamespaceOf=</varname> directive, see
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
- details. Note that this option will disconnect all socket families from the host, this includes AF_NETLINK and
- AF_UNIX. The latter has the effect that AF_UNIX sockets in the abstract socket namespace will become
- unavailable to the processes (however, those located in the file system will continue to be accessible).</para>
+ details. Note that this option will disconnect all socket families from the host, including
+ <constant>AF_NETLINK</constant> and <constant>AF_UNIX</constant>. Effectively, for
+ <constant>AF_NETLINK</constant> this means that device configuration events received from
+ <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> are
+ not delivered to the unit's processes. And for <constant>AF_UNIX</constant> this has the effect that
+ <constant>AF_UNIX</constant> sockets in the abstract socket namespace of the host will become unavailable to
+ the unit's processes (however, those located in the file system will continue to be accessible).</para>
<para>Note that the implementation of this setting might be impossible (for example if network namespaces are
not available), and the unit should be written in a way that does not solely rely on this setting for
@@ -1311,10 +1362,7 @@ RestrictNamespaces=~cgroup net</programlisting>
settings (see the discussion in <varname>PrivateMounts=</varname> above) will implicitly disable mount and
unmount propagation from the unit's processes towards the host by changing the propagation setting of all mount
points in the unit's file system namepace to <option>slave</option> first. Setting this option to
- <option>shared</option> does not reestablish propagation in that case. Conversely, if this option is set, but
- no other file system namespace setting is used, then new file system namespaces will be created for the unit's
- processes and this propagation flag will be applied right away to all mounts within it, without the
- intermediary application of <option>slave</option>.</para>
+ <option>shared</option> does not reestablish propagation in that case.</para>
<para>If not set – but file system namespaces are enabled through another file system namespace unit setting –
<option>shared</option> mount propagation is used, but — as mentioned — as <option>slave</option> is applied
@@ -1597,7 +1645,13 @@ SystemCallErrorNumber=EPERM</programlisting>
<para>
See <citerefentry
project='man-pages'><refentrytitle>environ</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details
- about environment variables.</para></listitem>
+ about environment variables.</para>
+
+ <para>Note that environment variables are not suitable for passing secrets (such as passwords, key material, …)
+ to service processes. Environment variables set for a unit are exposed to unprivileged clients via D-Bus IPC,
+ and generally not understood as being data that requires protection. Moreover, environment variables are
+ propagated down the process tree, including across security boundaries (such as setuid/setgid executables), and
+ hence might leak to processes that should not have access to the secret data.</para></listitem>
</varlistentry>
<varlistentry>
@@ -1739,7 +1793,13 @@ SystemCallErrorNumber=EPERM</programlisting>
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more
details about named file descriptors and their ordering.</para>
- <para>This setting defaults to <option>null</option>.</para></listitem>
+ <para>This setting defaults to <option>null</option>.</para>
+
+ <para>Note that services which specify <option>DefaultDependencies=no</option> and use
+ <varname>StandardInput=</varname> or <varname>StandardOutput=</varname> with
+ <option>tty</option>/<option>tty-force</option>/<option>tty-fail</option>, should specify
+ <option>After=systemd-vconsole-setup.service</option>, to make sure that the tty intialization is
+ finished before they start.</para></listitem>
</varlistentry>
<varlistentry>
@@ -1749,8 +1809,8 @@ SystemCallErrorNumber=EPERM</programlisting>
of <option>inherit</option>, <option>null</option>, <option>tty</option>, <option>journal</option>,
<option>syslog</option>, <option>kmsg</option>, <option>journal+console</option>,
<option>syslog+console</option>, <option>kmsg+console</option>,
- <option>file:<replaceable>path</replaceable></option>, <option>socket</option> or
- <option>fd:<replaceable>name</replaceable></option>.</para>
+ <option>file:<replaceable>path</replaceable></option>, <option>append:<replaceable>path</replaceable></option>,
+ <option>socket</option> or<option>fd:<replaceable>name</replaceable></option>.</para>
<para><option>inherit</option> duplicates the file descriptor of standard input for standard output.</para>
@@ -1781,11 +1841,17 @@ SystemCallErrorNumber=EPERM</programlisting>
<para>The <option>file:<replaceable>path</replaceable></option> option may be used to connect a specific file
system object to standard output. The semantics are similar to the same option of
- <varname>StandardInput=</varname>, see above. If standard input and output are directed to the same file path,
- it is opened only once, for reading as well as writing and duplicated. This is particular useful when the
- specified path refers to an <constant>AF_UNIX</constant> socket in the file system, as in that case only a
+ <varname>StandardInput=</varname>, see above. If <replaceable>path</replaceable> refers to a regular file
+ on the filesystem, it is opened (created if it doesn't exist yet) for writing at the beginning of the file,
+ but without truncating it.
+ If standard input and output are directed to the same file path, it is opened only once, for reading as well
+ as writing and duplicated. This is particularly useful when the specified path refers to an
+ <constant>AF_UNIX</constant> socket in the file system, as in that case only a
single stream connection is created for both input and output.</para>
+ <para><option>append:<replaceable>path</replaceable></option> is similar to <option>file:<replaceable>path
+ </replaceable></option> above, but it opens the file in append mode.</para>
+
<para><option>socket</option> connects standard output to a socket acquired via socket activation. The
semantics are similar to the same option of <varname>StandardInput=</varname>, see above.</para>
@@ -1906,6 +1972,22 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
</varlistentry>
<varlistentry>
+ <term><varname>LogRateLimitIntervalSec=</varname></term>
+ <term><varname>LogRateLimitBurst=</varname></term>
+
+ <listitem><para>Configures the rate limiting that is applied to messages generated by this unit. If, in the
+ time interval defined by <varname>LogRateLimitIntervalSec=</varname>, more messages than specified in
+ <varname>LogRateLimitBurst=</varname> are logged by a service, all further messages within the interval are
+ dropped until the interval is over. A message about the number of dropped messages is generated. The time
+ specification for <varname>LogRateLimitIntervalSec=</varname> may be specified in the following units: "s",
+ "min", "h", "ms", "us" (see
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details).
+ The default settings are set by <varname>RateLimitIntervalSec=</varname> and <varname>RateLimitBurst=</varname>
+ configured in <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>SyslogIdentifier=</varname></term>
<listitem><para>Sets the process name ("<command>syslog</command> tag") to prefix log lines sent to the logging
diff --git a/man/systemd.generator.xml b/man/systemd.generator.xml
index 8a7a11a862..5007563e06 100644
--- a/man/systemd.generator.xml
+++ b/man/systemd.generator.xml
@@ -260,7 +260,7 @@
<para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
converts <filename>/etc/fstab</filename> into native mount units. It uses
argv[1] as location to place the generated unit files in order to allow the
- user to override <filename>/etc/fstab</filename> with her own native unit
+ user to override <filename>/etc/fstab</filename> with their own native unit
files, but also to ensure that <filename>/etc/fstab</filename> overrides any
vendor default from <filename>/usr</filename>.</para>
diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml
index c079274c32..76e1de72ca 100644
--- a/man/systemd.journal-fields.xml
+++ b/man/systemd.journal-fields.xml
@@ -56,14 +56,10 @@
<varlistentry>
<term><varname>MESSAGE_ID=</varname></term>
<listitem>
- <para>A 128-bit message identifier ID for recognizing
- certain message types, if this is desirable. This should
- contain a 128-bit ID formatted as a lower-case hexadecimal
- string, without any separating dashes or suchlike. This is
- recommended to be a UUID-compatible ID, but this is not
- enforced, and formatted differently. Developers can generate
- a new ID for this purpose with <command>journalctl
- <option>--new-id128</option></command>.
+ <para>A 128-bit message identifier ID for recognizing certain message types, if this is desirable. This
+ should contain a 128-bit ID formatted as a lower-case hexadecimal string, without any separating dashes or
+ suchlike. This is recommended to be a UUID-compatible ID, but this is not enforced, and formatted
+ differently. Developers can generate a new ID for this purpose with <command>systemd-id128 new</command>.
</para>
</listitem>
</varlistentry>
@@ -103,16 +99,34 @@
<term><varname>SYSLOG_FACILITY=</varname></term>
<term><varname>SYSLOG_IDENTIFIER=</varname></term>
<term><varname>SYSLOG_PID=</varname></term>
+ <term><varname>SYSLOG_TIMESTAMP=</varname></term>
<listitem>
- <para>Syslog compatibility fields containing the facility
- (formatted as decimal string), the identifier string (i.e.
- "tag"), and the client PID. (Note that the tag is usually
- derived from glibc's
- <varname>program_invocation_short_name</varname> variable,
- see
+ <para>Syslog compatibility fields containing the facility (formatted as
+ decimal string), the identifier string (i.e. "tag"), the client PID, and
+ the timestamp as specified in the original datagram. (Note that the tag is
+ usually derived from glibc's
+ <varname>program_invocation_short_name</varname> variable, see
<citerefentry project='die-net'><refentrytitle>program_invocation_short_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>.)</para>
</listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>SYSLOG_RAW=</varname></term>
+ <listitem>
+ <para>The original contents of the syslog line as received in the syslog
+ datagram. This field is only included if the <varname>MESSAGE=</varname>
+ field was modified compared to the original payload or the timestamp could
+ not be located properly and is not included in
+ <varname>SYSLOG_TIMESTAMP=</varname>. Message truncation occurs when when
+ the message contains leading or trailing whitespace (trailing and leading
+ whitespace is stripped), or it contains an embedded
+ <constant>NUL</constant> byte (the <constant>NUL</constant> byte and
+ anything after it is not included). Thus, the original syslog line is
+ either stored as <varname>SYSLOG_RAW=</varname> or it can be recreated
+ based on the stored priority and facility, timestamp, identifier, and the
+ message payload in <varname>MESSAGE=</varname>.
+ </para>
+ </listitem>
</varlistentry>
</variablelist>
</refsect1>
diff --git a/man/systemd.kill.xml b/man/systemd.kill.xml
index 2112dea31a..9b264ecbf5 100644
--- a/man/systemd.kill.xml
+++ b/man/systemd.kill.xml
@@ -94,7 +94,8 @@
enabled with <varname>SendSIGHUP=</varname>). If then, after a
delay (configured via the <varname>TimeoutStopSec=</varname>
option), processes still remain, the termination request is
- repeated with the <constant>SIGKILL</constant> signal (unless
+ repeated with the <constant>SIGKILL</constant> signal or the
+ signal specified via <varname>FinalKillSignal=</varname> (unless
this is disabled via the <varname>SendSIGKILL=</varname>
option). See
<citerefentry><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry>
@@ -135,9 +136,35 @@
<varlistentry>
<term><varname>SendSIGKILL=</varname></term>
<listitem><para>Specifies whether to send
- <constant>SIGKILL</constant> to remaining processes after a
- timeout, if the normal shutdown procedure left processes of
- the service around. Takes a boolean value. Defaults to "yes".
+ <constant>SIGKILL</constant> (or the signal specified by
+ <varname>FinalKillSignal=</varname>) to remaining processes
+ after a timeout, if the normal shutdown procedure left
+ processes of the service around. Takes a boolean value.
+ Defaults to "yes".
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>FinalKillSignal=</varname></term>
+ <listitem><para>Specifies which signal to send to remaining
+ processes after a timeout if <varname>SendSIGKILL=</varname>
+ is enabled. The signal configured here should be one that is
+ not typically caught and processed by services (<constant>SIGTERM</constant>
+ is not suitable). Developers can find it useful to use this to
+ generate a coredump to troubleshoot why a service did not
+ terminate upon receiving the initial <constant>SIGTERM</constant>
+ signal. This can be achieved by configuring <varname>LimitCORE=</varname>
+ and setting <varname>FinalKillSignal=</varname> to either
+ <constant>SIGQUIT</constant> or <constant>SIGABRT</constant>
+ Defaults to <constant>SIGKILL</constant>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>WatchdogSignal=</varname></term>
+ <listitem><para>Specifies which signal to use to terminate the
+ service when the watchdog timeout expires (enabled through
+ <varname>WatchdogSec=</varname>). Defaults to <constant>SIGABRT</constant>.
</para></listitem>
</varlistentry>
diff --git a/man/systemd.link.xml b/man/systemd.link.xml
index 6708753e82..f74edd0186 100644
--- a/man/systemd.link.xml
+++ b/man/systemd.link.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -364,14 +364,13 @@
<varlistentry>
<term><varname>AutoNegotiation=</varname></term>
<listitem>
- <para>Enables or disables automatic negotiation of transmission parameters.
+ <para>Takes a boolean. If set to yes, automatic negotiation of transmission parameters is enabled.
Autonegotiation is a procedure by which two connected ethernet devices choose
common transmission parameters, such as speed, duplex mode, and flow control.
- Takes a boolean value. Unset by default, which means that the kernel default
- will be used.</para>
+ When unset, the kernel's default will be used.</para>
- <para>Note that if autonegotiation is enabled, speed and duplex settings are
- read-only. If autonegotation is disabled, speed and duplex settings are writable
+ <para>Note that if autonegotiation is enabled, speed, duplex and advertise settings are
+ read-only. If autonegotation is disabled, speed, duplex and advertise settings are writable
if the driver supports multiple link modes.</para>
</listitem>
</varlistentry>
@@ -479,43 +478,109 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><varname>Advertise=</varname></term>
+ <listitem>
+ <para>This sets what speeds and duplex modes of operation are advertised for auto-negotiation.
+ The supported values are:
+
+ <table>
+ <title>Supported advertise values</title>
+ <tgroup cols='3'>
+ <colspec colname='Advertise' />
+ <colspec colname='Speed' />
+ <colspec colname='Duplex Mode' />
+
+ <thead><row>
+ <entry>Advertise</entry>
+ <entry>Speed (Mbps)</entry>
+ <entry>Duplex Mode</entry>
+ </row></thead>
+ <tbody>
+
+ <row><entry><literal>10baset-half</literal></entry>
+ <entry>10</entry><entry>half</entry></row>
+
+ <row><entry><literal>10baset-full</literal></entry>
+ <entry>10</entry><entry>full</entry></row>
+
+ <row><entry><literal>100baset-half</literal></entry>
+ <entry>100</entry><entry>half</entry></row>
+
+ <row><entry><literal>100baset-full</literal></entry>
+ <entry>100</entry><entry>full</entry></row>
+
+ <row><entry><literal>1000baset-half</literal></entry>
+ <entry>1000</entry><entry>half</entry></row>
+
+ <row><entry><literal>1000baset-full</literal></entry>
+ <entry>1000</entry><entry>full</entry></row>
+
+ <row><entry><literal>10000baset-full</literal></entry>
+ <entry>10000</entry><entry>full</entry></row>
+
+ <row><entry><literal>2500basex-full</literal></entry>
+ <entry>2500</entry><entry>full</entry></row>
+
+ <row><entry><literal>1000basekx-full</literal></entry>
+ <entry>1000</entry><entry>full</entry></row>
+
+ <row><entry><literal>10000basekx4-full</literal></entry>
+ <entry>10000</entry><entry>full</entry></row>
+
+ <row><entry><literal>10000basekr-full</literal></entry>
+ <entry>10000</entry><entry>full</entry></row>
+
+ <row><entry><literal>10000baser-fec</literal></entry>
+ <entry>10000</entry><entry>full</entry></row>
+
+ <row><entry><literal>20000basemld2-full</literal></entry>
+ <entry>20000</entry><entry>full</entry></row>
+
+ <row><entry><literal>20000basekr2-full</literal></entry>
+ <entry>20000</entry><entry>full</entry></row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ By default this is unset, i.e. all possible modes will be advertised.
+ This option may be specified more than once, in which case all specified speeds and modes are advertised.
+ If the empty string is assigned to this option, the list is reset, and all prior assignments have no effect.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>TCPSegmentationOffload=</varname></term>
<listitem>
- <para>The TCP Segmentation Offload (TSO) when true enables
- TCP segmentation offload. Takes a boolean value.
- Defaults to "unset".</para>
+ <para>Takes a boolean. If set to true, the TCP Segmentation Offload (TSO) is enabled.
+ When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>TCP6SegmentationOffload=</varname></term>
<listitem>
- <para>The TCP6 Segmentation Offload (tx-tcp6-segmentation) when true enables
- TCP6 segmentation offload. Takes a boolean value.
- Defaults to "unset".</para>
+ <para>Takes a boolean. If set to true, the TCP6 Segmentation Offload (tx-tcp6-segmentation) is enabled.
+ When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>GenericSegmentationOffload=</varname></term>
<listitem>
- <para>The Generic Segmentation Offload (GSO) when true enables
- generic segmentation offload. Takes a boolean value.
- Defaults to "unset".</para>
+ <para>Takes a boolean. If set to true, the Generic Segmentation Offload (GSO) is enabled.
+ When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>GenericReceiveOffload=</varname></term>
<listitem>
- <para>The Generic Receive Offload (GRO) when true enables
- generic receive offload. Takes a boolean value.
- Defaults to "unset".</para>
+ <para>Takes a boolean. If set to true, the Generic Receive Offload (GRO) is enabled.
+ When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>LargeReceiveOffload=</varname></term>
<listitem>
- <para>The Large Receive Offload (LRO) when true enables
- large receive offload. Takes a boolean value.
- Defaults to "unset".</para>
+ <para>Takes a boolean. If set to true, the Large Receive Offload (LRO) is enabled.
+ When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml
index 1eb06b0ea8..6d8c873ca5 100644
--- a/man/systemd.mount.xml
+++ b/man/systemd.mount.xml
@@ -165,7 +165,7 @@
is detected by <command>systemd-fstab-generator</command> and the options
are transformed so that systemd fulfills the job-control implications of
that option. Specifically <command>systemd-fstab-generator</command> acts
- as though <literal>x-systemd.mount-timout=infinity,retry=10000</literal> was
+ as though <literal>x-systemd.mount-timeout=infinity,retry=10000</literal> was
prepended to the option list, and <literal>fg,nofail</literal> was appended.
Depending on specific requirements, it may be appropriate to provide some of
these options explicitly, or to make use of the
@@ -263,7 +263,7 @@
for details.</para></listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id='device-timeout'>
<term><option>x-systemd.device-timeout=</option></term>
<listitem><para>Configure how long systemd should wait for a
@@ -303,7 +303,7 @@
<varlistentry>
<term><option>x-systemd.makefs</option></term>
- <listitem><para>The file system or swap structure will be initialized
+ <listitem><para>The file system will be initialized
on the device. If the device is not "empty", i.e. it contains any signature,
the operation will be skipped. It is hence expected that this option
remains set even after the device has been initalized.</para>
@@ -370,12 +370,10 @@
<varlistentry>
<term><option>nofail</option></term>
- <listitem><para>With <option>nofail</option>, this mount will
- be only wanted, not required, by
- <filename>local-fs.target</filename> or
- <filename>remote-fs.target</filename>. This means that the
- boot will continue even if this mount point is not mounted
- successfully.</para>
+ <listitem><para>With <option>nofail</option>, this mount will be only wanted, not required, by
+ <filename>local-fs.target</filename> or <filename>remote-fs.target</filename>. Moreover the mount unit is not
+ ordered before these target units. This means that the boot will continue without waiting for the mount unit
+ and regardless whether the mount point can be mounted successfully.</para>
</listitem>
</varlistentry>
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index 5073258641..e17c1e3fbe 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -100,6 +100,11 @@
<row><entry><varname>gretap</varname></entry>
<entry>A Level 2 GRE tunnel over IPv4.</entry></row>
+ <row><entry><varname>erspan</varname></entry>
+ <entry>ERSPAN mirrors traffic on one or more source ports and delivers the mirrored traffic to one or more destination ports on another switch.
+ The traffic is encapsulated in generic routing encapsulation (GRE) and is therefore routable across a layer 3 network between the source switch
+ and the destination switch.</entry></row>
+
<row><entry><varname>ip6gre</varname></entry>
<entry>A Level 3 GRE tunnel over IPv6.</entry></row>
@@ -163,6 +168,10 @@
<row><entry><varname>netdevsim</varname></entry>
<entry> A simulator. This simulated networking device is used for testing various networking APIs and at this time is particularly focused on testing hardware offloading related interfaces.</entry></row>
+
+ <row><entry><varname>fou</varname></entry>
+ <entry>Foo-over-UDP tunneling.</entry></row>
+
</tbody>
</tgroup>
</table>
@@ -266,12 +275,13 @@
<varlistentry>
<term><varname>MTUBytes=</varname></term>
<listitem>
- <para>The maximum transmission unit in bytes to set for
- the device. The usual suffixes K, M, G, are supported and
- are understood to the base of 1024. This key is not
- currently supported for <literal>tun</literal> or
- <literal>tap</literal> devices.
- </para>
+ <para>The maximum transmission unit in bytes to set for the device. The usual suffixes K, M, G,
+ are supported and are understood to the base of 1024. For <literal>tun</literal> or
+ <literal>tap</literal> devices, <varname>MTUBytes=</varname> setting is not currently supported in
+ <literal>[NetDev]</literal> section. Please specify it in <literal>[Link]</literal> section of
+ corresponding
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ files.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -281,9 +291,11 @@
given, one is generated based on the interface name and
the
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
- This key is not currently supported for
- <literal>tun</literal> or <literal>tap</literal> devices.
- </para>
+ For <literal>tun</literal> or <literal>tap</literal> devices, <varname>MACAddress=</varname> setting
+ is not currently supported in <literal>[NetDev]</literal> section. Please specify it in
+ <literal>[Link]</literal> section of corresponding
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ files.</para>
</listitem>
</varlistentry>
</variablelist>
@@ -356,37 +368,36 @@
<varlistentry>
<term><varname>MulticastQuerier=</varname></term>
<listitem>
- <para>A boolean. This setting controls the IFLA_BR_MCAST_QUERIER option in the kernel.
+ <para>Takes a boolean. This setting controls the IFLA_BR_MCAST_QUERIER option in the kernel.
If enabled, the kernel will send general ICMP queries from a zero source address.
This feature should allow faster convergence on startup, but it causes some
multicast-aware switches to misbehave and disrupt forwarding of multicast packets.
- When unset, the kernel's default setting applies.
+ When unset, the kernel's default will be used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MulticastSnooping=</varname></term>
<listitem>
- <para>A boolean. This setting controls the IFLA_BR_MCAST_SNOOPING option in the kernel.
+ <para>Takes a boolean. This setting controls the IFLA_BR_MCAST_SNOOPING option in the kernel.
If enabled, IGMP snooping monitors the Internet Group Management Protocol (IGMP) traffic
- between hosts and multicast routers. When unset, the kernel's default setting applies.
+ between hosts and multicast routers. When unset, the kernel's default will be used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>VLANFiltering=</varname></term>
<listitem>
- <para>A boolean. This setting controls the IFLA_BR_VLAN_FILTERING option in the kernel.
- If enabled, the bridge will be started in VLAN-filtering mode. When unset, the kernel's
- default setting applies.
+ <para>Takes a boolean. This setting controls the IFLA_BR_VLAN_FILTERING option in the kernel.
+ If enabled, the bridge will be started in VLAN-filtering mode. When unset, the kernel's default will be used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>STP=</varname></term>
<listitem>
- <para>A boolean. This enables the bridge's Spanning Tree Protocol (STP). When unset,
- the kernel's default setting applies.
+ <para>Takes a boolean. This enables the bridge's Spanning Tree Protocol (STP).
+ When unset, the kernel's default will be used.
</para>
</listitem>
</varlistentry>
@@ -411,34 +422,35 @@
<varlistentry>
<term><varname>GVRP=</varname></term>
<listitem>
- <para>The Generic VLAN Registration Protocol (GVRP) is a protocol that
- allows automatic learning of VLANs on a network. A boolean. When unset,
- the kernel's default setting applies.</para>
+ <para>Takes a boolean. The Generic VLAN Registration Protocol (GVRP) is a protocol that
+ allows automatic learning of VLANs on a network.
+ When unset, the kernel's default will be used.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MVRP=</varname></term>
<listitem>
- <para>Multiple VLAN Registration Protocol (MVRP) formerly known as GARP VLAN
+ <para>Takes a boolean. Multiple VLAN Registration Protocol (MVRP) formerly known as GARP VLAN
Registration Protocol (GVRP) is a standards-based Layer 2 network protocol,
for automatic configuration of VLAN information on switches. It was defined
- in the 802.1ak amendment to 802.1Q-2005. A boolean. When unset, the kernel's
- default setting applies.</para>
+ in the 802.1ak amendment to 802.1Q-2005. When unset, the kernel's default will be used.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>LooseBinding=</varname></term>
<listitem>
- <para>The VLAN loose binding mode, in which only the operational state is passed
+ <para>Takes a boolean. The VLAN loose binding mode, in which only the operational state is passed
from the parent to the associated VLANs, but the VLAN device state is not changed.
- A boolean. When unset, the kernel's default setting applies.</para>
+ When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ReorderHeader=</varname></term>
<listitem>
- <para>The VLAN reorder header is set VLAN interfaces behave like physical interfaces.
- A boolean. When unset, the kernel's default setting applies.</para>
+ <para>Takes a boolean. The VLAN reorder header is set VLAN interfaces behave like physical interfaces.
+ When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
</variablelist>
@@ -547,7 +559,7 @@
<varlistentry>
<term><varname>MacLearning=</varname></term>
<listitem>
- <para>A boolean. When true, enables dynamic MAC learning
+ <para>Takes a boolean. When true, enables dynamic MAC learning
to discover remote MAC addresses.</para>
</listitem>
</varlistentry>
@@ -567,7 +579,7 @@
<varlistentry>
<term><varname>ReduceARPProxy=</varname></term>
<listitem>
- <para>A boolean. When true, bridge-connected VXLAN tunnel
+ <para>Takes a boolean. When true, bridge-connected VXLAN tunnel
endpoint answers ARP requests from the local bridge on behalf
of remote Distributed Overlay Virtual Ethernet
<ulink url="https://en.wikipedia.org/wiki/Distributed_Overlay_Virtual_Ethernet">
@@ -577,58 +589,58 @@
<varlistentry>
<term><varname>L2MissNotification=</varname></term>
<listitem>
- <para>A boolean. When true, enables netlink LLADDR miss
+ <para>Takes a boolean. When true, enables netlink LLADDR miss
notifications.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>L3MissNotification=</varname></term>
<listitem>
- <para>A boolean. When true, enables netlink IP address miss
+ <para>Takes a boolean. When true, enables netlink IP address miss
notifications.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RouteShortCircuit=</varname></term>
<listitem>
- <para>A boolean. When true, route short circuiting is turned
+ <para>Takes a boolean. When true, route short circuiting is turned
on.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>UDPChecksum=</varname></term>
<listitem>
- <para>A boolean. When true, transmitting UDP checksums when doing VXLAN/IPv4 is turned on.</para>
+ <para>Takes a boolean. When true, transmitting UDP checksums when doing VXLAN/IPv4 is turned on.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>UDP6ZeroChecksumTx=</varname></term>
<listitem>
- <para>A boolean. When true, sending zero checksums in VXLAN/IPv6 is turned on.</para>
+ <para>Takes a boolean. When true, sending zero checksums in VXLAN/IPv6 is turned on.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>UDP6ZeroChecksumRx=</varname></term>
<listitem>
- <para>A boolean. When true, receiving zero checksums in VXLAN/IPv6 is turned on.</para>
+ <para>Takes a boolean. When true, receiving zero checksums in VXLAN/IPv6 is turned on.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RemoteChecksumTx=</varname></term>
<listitem>
- <para>A boolean. When true, remote transmit checksum offload of VXLAN is turned on.</para>
+ <para>Takes a boolean. When true, remote transmit checksum offload of VXLAN is turned on.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RemoteChecksumRx=</varname></term>
<listitem>
- <para>A boolean. When true, remote receive checksum offload in VXLAN is turned on.</para>
+ <para>Takes a boolean. When true, remote receive checksum offload in VXLAN is turned on.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>GroupPolicyExtension=</varname></term>
<listitem>
- <para>A boolean. When true, it enables Group Policy VXLAN extension security label mechanism
+ <para>Takes a boolean. When true, it enables Group Policy VXLAN extension security label mechanism
across network peers based on VXLAN. For details about the Group Policy VXLAN, see the
<ulink url="https://tools.ietf.org/html/draft-smith-vxlan-group-policy">
VXLAN Group Policy </ulink> document. Defaults to false.</para>
@@ -697,19 +709,19 @@
<varlistentry>
<term><varname>UDPChecksum=</varname></term>
<listitem>
- <para>A boolean. When true, specifies if UDP checksum is calculated for transmitted packets over IPv4.</para>
+ <para>Takes a boolean. When true, specifies if UDP checksum is calculated for transmitted packets over IPv4.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>UDP6ZeroChecksumTx=</varname></term>
<listitem>
- <para>A boolean. When true, skip UDP checksum calculation for transmitted packets over IPv6.</para>
+ <para>Takes a boolean. When true, skip UDP checksum calculation for transmitted packets over IPv6.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>UDP6ZeroChecksumRx=</varname></term>
<listitem>
- <para>A boolean. When true, allows incoming UDP packets over IPv6 with zero checksum field.</para>
+ <para>Takes a boolean. When true, allows incoming UDP packets over IPv6 with zero checksum field.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -780,7 +792,7 @@
<varlistentry>
<term><varname>DiscoverPathMTU=</varname></term>
<listitem>
- <para>A boolean. When true, enables Path MTU Discovery on
+ <para>Takes a boolean. When true, enables Path MTU Discovery on
the tunnel.</para>
</listitem>
</varlistentry>
@@ -800,7 +812,7 @@
<varlistentry>
<term><varname>CopyDSCP=</varname></term>
<listitem>
- <para>A boolean. When true, the Differentiated Service Code
+ <para>Takes a boolean. When true, the Differentiated Service Code
Point (DSCP) field will be copied to the inner header from
outer header during the decapsulation of an IPv6 tunnel
packet. DSCP is a field in an IP packet that enables different
@@ -863,7 +875,7 @@
<varlistentry>
<term><varname>Independent=</varname></term>
<listitem>
- <para>A boolean. When true tunnel does not require .network file. Created as "tunnel@NONE".
+ <para>Takes a boolean. When true tunnel does not require .network file. Created as "tunnel@NONE".
Defaults to <literal>false</literal>.
</para>
</listitem>
@@ -871,13 +883,112 @@
<varlistentry>
<term><varname>AllowLocalRemote=</varname></term>
<listitem>
- <para>A boolean. When true allows tunnel traffic on <varname>ip6tnl</varname> devices where the remote endpoint is a local host address.
- Defaults to unset.
+ <para>Takes a boolean. When true allows tunnel traffic on <varname>ip6tnl</varname> devices where the remote endpoint is a local host address.
+ When unset, the kernel's default will be used.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>FooOverUDP=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Specifies whether <varname>FooOverUDP=</varname> tunnel is to be configured.
+ Defaults to false. For more detail information see
+ <ulink url="https://lwn.net/Articles/614348">Foo over UDP</ulink></para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>FOUDestinationPort=</varname></term>
+ <listitem>
+ <para>The <varname>FOUDestinationPort=</varname> specifies the UDP destination port for encapsulation.
+ This field is mandatory and is not set by default.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>FOUSourcePort=</varname></term>
+ <listitem>
+ <para>The <constant>FOUSourcePort=</constant> specifies the UDP source port for encapsulation. Defaults to <varname>0</varname>,
+ that is, the source port for packets is left to the network stack to decide.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Encapsulation=</varname></term>
+ <listitem>
+ <para>Accepts the same key as <literal>[FooOverUDP]</literal></para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>IPv6RapidDeploymentPrefix=</varname></term>
+ <listitem>
+ <para>Reconfigure the tunnel for <ulink url="https://tools.ietf.org/html/rfc5569">IPv6 Rapid
+ Deployment</ulink>, also known as 6rd. The value is an ISP-specific IPv6 prefix with a non-zero length. Only
+ applicable to SIT tunnels.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ISATAP=</varname></term>
+ <listitem>
+ <para>Takes a boolean. If set, configures the tunnel as Intra-Site Automatic Tunnel Addressing Protocol (ISATAP) tunnel.
+ Only applicable to SIT tunnels. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>SerializeTunneledPackets=</varname></term>
+ <listitem>
+ <para>Takes a boolean. If set to yes, then packets are serialized. Only applies for ERSPAN tunnel.
+ When unset, the kernel's default will be used.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ERSPANIndex=</varname></term>
+ <listitem>
+ <para>Specifies the ERSPAN index field for the interface, an integer in the range 1-1048575 associated with
+ the ERSPAN traffic's source port and direction. This field is mandatory.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
+
+ <refsect1>
+ <title>[FooOverUDP] Section Options</title>
+
+ <para>The <literal>[FooOverUDP]</literal> section only applies for
+ netdevs of kind <literal>fou</literal> and accepts the
+ following keys:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Protocol=</varname></term>
+ <listitem>
+ <para>The <varname>Protocol=</varname> specifies the protocol number of the
+ packets arriving at the UDP port. This field is mandatory and is not set by default. Valid range is 1-255.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Encapsulation=</varname></term>
+ <listitem>
+ <para>Specifies the encapsulation mechanism used to store networking packets of various protocols inside the UDP packets. Supports the following values:
+
+ <literal>FooOverUDP</literal> provides the simplest no frills model of UDP encapsulation, it simply encapsulates
+ packets directly in the UDP payload.
+ <literal>GenericUDPEncapsulation</literal> is a generic and extensible encapsulation, it allows encapsulation of packets for any IP
+ protocol and optional data as part of the encapsulation.
+ For more detailed information see <ulink url="https://lwn.net/Articles/615044">Generic UDP Encapsulation</ulink>.
+ Defaults to <literal>FooOverUDP</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Port=</varname></term>
+ <listitem>
+ <para>Specifies the port number, where the IP encapsulation packets will arrive. Please take note that the packets
+ will arrive with the encapsulation will be removed. Then they will be manually fed back into the network stack, and sent ahead
+ for delivery to the real destination. This option is mandatory.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
<refsect1>
<title>[Peer] Section Options</title>
@@ -929,7 +1040,7 @@
<variablelist class='network-directives'>
<varlistentry>
<term><varname>OneQueue=</varname></term>
- <listitem><para>Takes a boolean argument. Configures whether
+ <listitem><para>Takes a boolean. Configures whether
all packets are queued at the device (enabled), or a fixed
number of packets are queued at the device and the rest at the
<literal>qdisc</literal>. Defaults to
@@ -938,7 +1049,7 @@
</varlistentry>
<varlistentry>
<term><varname>MultiQueue=</varname></term>
- <listitem><para>Takes a boolean argument. Configures whether
+ <listitem><para>Takes a boolean. Configures whether
to use multiple file descriptors (queues) to parallelize
packets sending and receiving. Defaults to
<literal>no</literal>.</para>
@@ -946,7 +1057,7 @@
</varlistentry>
<varlistentry>
<term><varname>PacketInfo=</varname></term>
- <listitem><para>Takes a boolean argument. Configures whether
+ <listitem><para>Takes a boolean. Configures whether
packets should be prepended with four extra bytes (two flag
bytes and two protocol bytes). If disabled, it indicates that
the packets will be pure IP packets. Defaults to
@@ -955,7 +1066,7 @@
</varlistentry>
<varlistentry>
<term><varname>VNetHeader=</varname></term>
- <listitem><para>Takes a boolean argument. Configures
+ <listitem><para>Takes a boolean. Configures
IFF_VNET_HDR flag for a tap device. It allows sending
and receiving larger Generic Segmentation Offload (GSO)
packets. This may increase throughput significantly.
@@ -1003,7 +1114,7 @@
(see <citerefentry project="wireguard"><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
This option is mandatory to use WireGuard.
Note that because this information is secret, you may want to set
- the permissions of the .netdev file to be owned by <literal>root:systemd-networkd</literal>
+ the permissions of the .netdev file to be owned by <literal>root:systemd-network</literal>
with a <literal>0640</literal> file mode.</para>
</listitem>
</varlistentry>
@@ -1194,6 +1305,27 @@
</varlistentry>
<varlistentry>
+ <term><varname>AdActorSystemPriority=</varname></term>
+ <listitem>
+ <para>Specifies the 802.3ad actor system priority. Ranges [1-65535].</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>AdUserPortKey=</varname></term>
+ <listitem>
+ <para>Specifies the 802.3ad user defined portion of the port key. Ranges [0-1023].</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>AdActorSystem=</varname></term>
+ <listitem>
+ <para>Specifies the 802.3ad system mac address. This can not be either NULL or Multicast.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>FailOverMACPolicy=</varname></term>
<listitem>
<para>Specifies whether the active-backup mode should set all slaves to
@@ -1315,7 +1447,7 @@
<varlistentry>
<term><varname>AllSlavesActive=</varname></term>
<listitem>
- <para>A boolean. Specifies that duplicate frames (received on inactive ports)
+ <para>Takes a boolean. Specifies that duplicate frames (received on inactive ports)
should be dropped when false, or delivered when true. Normally, bonding will drop
duplicate frames (received on inactive ports), which is desirable for
most users. But there are some times it is nice to allow duplicate
@@ -1326,6 +1458,15 @@
</varlistentry>
<varlistentry>
+ <term><varname>DynamicTransmitLoadBalancing=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Specifies if dynamic shuffling of flows is enabled. Applies only
+ for balance-tlb mode. Defaults to unset.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>MinLinks=</varname></term>
<listitem>
<para>Specifies the minimum number of links that must be active before
@@ -1342,7 +1483,7 @@
</refsect1>
<refsect1>
- <title>Example</title>
+ <title>Examples</title>
<example>
<title>/etc/systemd/network/25-bridge.netdev</title>
@@ -1377,14 +1518,39 @@ Remote=192.169.224.239
TTL=64</programlisting>
</example>
<example>
+ <title>/etc/systemd/network/1-fou-tunnel.netdev</title>
+ <programlisting>[NetDev]
+Name=fou-tun
+Kind=fou
+
+[FooOverUDP]
+Port=5555
+Protocol=4
+ </programlisting>
+ </example>
+ <example>
+ <title>/etc/systemd/network/25-fou-ipip.netdev</title>
+ <programlisting>[NetDev]
+Name=ipip-tun
+Kind=ipip
+
+[Tunnel]
+Independent=yes
+Local=10.65.208.212
+Remote=10.65.208.211
+FooOverUDP=yes
+FOUDestinationPort=5555
+ </programlisting>
+ </example>
+ <example>
<title>/etc/systemd/network/25-tap.netdev</title>
<programlisting>[NetDev]
Name=tap-test
Kind=tap
[Tap]
-MultiQueue=true
-PacketInfo=true</programlisting> </example>
+MultiQueue=yes
+PacketInfo=yes</programlisting> </example>
<example>
<title>/etc/systemd/network/25-sit.netdev</title>
@@ -1399,6 +1565,18 @@ Remote=10.65.223.239</programlisting>
</example>
<example>
+ <title>/etc/systemd/network/25-6rd.netdev</title>
+ <programlisting>[NetDev]
+Name=6rd-tun
+Kind=sit
+MTUBytes=1480
+
+[Tunnel]
+Local=10.65.223.238
+IPv6RapidDeploymentPrefix=2602::/24</programlisting>
+ </example>
+
+ <example>
<title>/etc/systemd/network/25-gre.netdev</title>
<programlisting>[NetDev]
Name=gre-tun
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index fc8e0aea68..ee464ffff4 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -221,8 +221,8 @@
<varlistentry>
<term><varname>ARP=</varname></term>
<listitem>
- <para>A boolean. Enables or disables the ARP (low-level Address Resolution Protocol)
- for this interface. Defaults to unset, which means that the kernel default will be used.</para>
+ <para>Takes a boolean. If set to true, the ARP (low-level Address Resolution Protocol)
+ for this interface is enabled. When unset, the kernel's default will be used.</para>
<para> For example, disabling ARP is useful when creating multiple MACVLAN or VLAN virtual
interfaces atop a single lower-level physical interface, which will then only serve as a
link/"bridge" device aggregating traffic to the same physical link and not participate in
@@ -232,20 +232,20 @@
<varlistentry>
<term><varname>Multicast=</varname></term>
<listitem>
- <para>A boolean. Enables or disables the multicast flag on the device.</para>
+ <para>Takes a boolean. If set to true, the multicast flag on the device is enabled.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>AllMulticast=</varname></term>
<listitem>
- <para>A boolean. When this flag is set the driver retrieves all multicast packets from the network.
+ <para>Takes a boolean. If set to true, the driver retrieves all multicast packets from the network.
This happens when multicast routing is enabled.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Unmanaged=</varname></term>
<listitem>
- <para>A boolean. When <literal>yes</literal>, no attempts are
+ <para>Takes a boolean. When <literal>yes</literal>, no attempts are
made to bring up or configure matching links, equivalent to
when there are no matching network files. Defaults to
<literal>no</literal>.</para>
@@ -257,7 +257,7 @@
<varlistentry>
<term><varname>RequiredForOnline=</varname></term>
<listitem>
- <para>A boolean. When <literal>yes</literal>, the network is deemed
+ <para>Takes a boolean. When <literal>yes</literal>, the network is deemed
required when determining whether the system is online when running
<literal>systemd-networkd-wait-online</literal>.
When <literal>no</literal>, the network is ignored when checking for
@@ -266,7 +266,7 @@
the event that there is no address being assigned by DHCP or the
cable is not plugged in, the link will simply remain offline and be
skipped automatically by <literal>systemd-networkd-wait-online</literal>
- if <literal>RequiredForOnline=true</literal>.</para>
+ if <literal>RequiredForOnline=no</literal>.</para>
</listitem>
</varlistentry>
</variablelist>
@@ -311,7 +311,7 @@
<varlistentry>
<term><varname>DHCPServer=</varname></term>
<listitem>
- <para>A boolean. Enables DHCPv4 server support. Defaults
+ <para>Takes a boolean. If set to <literal>yes</literal>, DHCPv4 server will be start. Defaults
to <literal>no</literal>. Further settings for the DHCP
server may be set in the <literal>[DHCPServer]</literal>
section described below.</para>
@@ -329,7 +329,7 @@
<varlistentry>
<term><varname>IPv4LLRoute=</varname></term>
<listitem>
- <para>A boolean. When true, sets up the route needed for
+ <para>Takes a boolean. If set to true, sets up the route needed for
non-IPv4LL hosts to communicate with IPv4LL-only hosts. Defaults
to false.
</para>
@@ -348,7 +348,7 @@
<varlistentry>
<term><varname>LLMNR=</varname></term>
<listitem>
- <para>A boolean or <literal>resolve</literal>. When true,
+ <para>Takes a boolean or <literal>resolve</literal>. When true,
enables <ulink
url="https://tools.ietf.org/html/rfc4795">Link-Local
Multicast Name Resolution</ulink> on the link. When set to
@@ -361,7 +361,7 @@
<varlistentry>
<term><varname>MulticastDNS=</varname></term>
<listitem>
- <para>A boolean or <literal>resolve</literal>. When true,
+ <para>Takes a boolean or <literal>resolve</literal>. When true,
enables <ulink
url="https://tools.ietf.org/html/rfc6762">Multicast
DNS</ulink> support on the link. When set to
@@ -389,7 +389,7 @@
<varlistentry>
<term><varname>DNSSEC=</varname></term>
<listitem>
- <para>A boolean or
+ <para>Takes a boolean. or
<literal>allow-downgrade</literal>. When true, enables
<ulink
url="https://tools.ietf.org/html/rfc4033">DNSSEC</ulink>
@@ -548,6 +548,17 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><varname>DNSDefaultRoute=</varname></term>
+ <listitem>
+ <para>Takes a boolean argument. If true, this link's configured DNS servers are used for resolving domain
+ names that do not match any link's configured <varname>Domains=</varname> setting. If false, this link's
+ configured DNS servers are never used for such domains, and are exclusively used for resolving names that
+ match at least one of the domains configured on this link. If not specified defaults to an automatic mode:
+ queries not matching any link's configured domains will be routed to this link if it has no routing-only
+ domains configured.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>NTP=</varname></term>
<listitem>
<para>An NTP server address. This option may be specified more than once. This setting is read by
@@ -559,8 +570,8 @@
<listitem><para>Configures IP packet forwarding for the
system. If enabled, incoming packets on any network
interface will be forwarded to any other interfaces
- according to the routing table. Takes either a boolean
- argument, or the values <literal>ipv4</literal> or
+ according to the routing table. Takes a boolean,
+ or the values <literal>ipv4</literal> or
<literal>ipv6</literal>, which only enable IP packet
forwarding for the specified address family. This controls
the <filename>net.ipv4.ip_forward</filename> and
@@ -608,9 +619,9 @@
</varlistentry>
<varlistentry>
<term><varname>IPv6AcceptRA=</varname></term>
- <listitem><para>Enable or disable IPv6 Router Advertisement (RA) reception support for the interface. Takes
- a boolean parameter. If true, RAs are accepted; if false, RAs are ignored, independently of the local
- forwarding state. When not set, the kernel default is used, and RAs are accepted only when local forwarding
+ <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.
+ If unset, the kernel's default is used, and RAs are accepted only when local forwarding
is disabled for that interface. 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>
@@ -626,7 +637,7 @@
<varlistentry>
<term><varname>IPv6DuplicateAddressDetection=</varname></term>
<listitem><para>Configures the amount of IPv6 Duplicate
- Address Detection (DAD) probes to send. Defaults to unset.
+ Address Detection (DAD) probes to send. When unset, the kernel's default will be used.
</para></listitem>
</varlistentry>
<varlistentry>
@@ -634,21 +645,21 @@
<listitem><para>Configures IPv6 Hop Limit. For each router that
forwards the packet, the hop limit is decremented by 1. When the
hop limit field reaches zero, the packet is discarded.
- Defaults to unset.
+ When unset, the kernel's default will be used.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv4ProxyARP=</varname></term>
- <listitem><para>A boolean. Configures proxy ARP for IPv4. Proxy ARP is the technique in which one host,
+ <listitem><para>Takes a boolean. Configures proxy ARP for IPv4. Proxy ARP is the technique in which one host,
usually a router, answers ARP requests intended for another machine. By "faking" its identity,
the router accepts responsibility for routing packets to the "real" destination. (see <ulink
url="https://tools.ietf.org/html/rfc1027">RFC 1027</ulink>.
- Defaults to unset.
+ When unset, the kernel's default will be used.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv6ProxyNDP=</varname></term>
- <listitem><para>A boolean. Configures proxy NDP for IPv6. Proxy NDP (Neighbor Discovery
+ <listitem><para>Takes a boolean. Configures proxy NDP for IPv6. Proxy NDP (Neighbor Discovery
Protocol) is a technique for IPv6 to allow routing of addresses to a different
destination when peers expect them to be present on a certain physical link.
In this case a router answers Neighbour Advertisement messages intended for
@@ -658,7 +669,7 @@
which can also be shown by <command>ip -6 neighbour show proxy</command>.
systemd-networkd will control the per-interface `proxy_ndp` switch for each configured
interface depending on this option.
- Defautls to unset.
+ When unset, the kernel's default will be used.
</para></listitem>
</varlistentry>
<varlistentry>
@@ -666,8 +677,8 @@
<listitem><para>An IPv6 address, for which Neighbour Advertisement messages will be
proxied. This option may be specified more than once. systemd-networkd will add the
<option>IPv6ProxyNDPAddress=</option> entries to the kernel's IPv6 neighbor proxy table.
- This option implies <option>IPv6ProxyNDP=true</option> but has no effect if
- <option>IPv6ProxyNDP</option> has been set to false. Defaults to unset.
+ This option implies <option>IPv6ProxyNDP=yes</option> but has no effect if
+ <option>IPv6ProxyNDP</option> has been set to false. When unset, the kernel's default will be used.
</para></listitem>
</varlistentry>
<varlistentry>
@@ -688,7 +699,7 @@
<varlistentry>
<term><varname>IPv6MTUBytes=</varname></term>
<listitem><para>Configures IPv6 maximum transmission unit (MTU).
- An integer greater than or equal to 1280 bytes. Defaults to unset.
+ An integer greater than or equal to 1280 bytes. When unset, the kernel's default will be used.
</para></listitem>
</varlistentry>
<varlistentry>
@@ -724,6 +735,14 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><varname>IPVLAN=</varname></term>
+ <listitem>
+ <para>The name of a IPVLAN to create on the link. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ This option may be specified more than once.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>MACVLAN=</varname></term>
<listitem>
<para>The name of a MACVLAN to create on the link. See
@@ -750,7 +769,7 @@
<varlistentry>
<term><varname>ActiveSlave=</varname></term>
<listitem>
- <para>A boolean. Specifies the new active slave. The <literal>ActiveSlave=</literal>
+ <para>Takes a boolean. Specifies the new active slave. The <literal>ActiveSlave=</literal>
option is only valid for following modes:
<literal>active-backup</literal>,
<literal>balance-alb</literal> and
@@ -761,7 +780,7 @@
<varlistentry>
<term><varname>PrimarySlave=</varname></term>
<listitem>
- <para>A boolean. Specifies which slave is the primary device. The specified
+ <para>Takes a boolean. Specifies which slave is the primary device. The specified
device will always be the active slave while it is available. Only when the
primary is off-line will alternate devices be used. This is useful when
one slave is preferred over another, e.g. when one slave has higher throughput
@@ -776,7 +795,7 @@
<varlistentry>
<term><varname>ConfigureWithoutCarrier=</varname></term>
<listitem>
- <para>A boolean. Allows networkd to configure a specific link even if it has no carrier.
+ <para>Takes a boolean. Allows networkd to configure a specific link even if it has no carrier.
Defaults to false.
</para>
</listitem>
@@ -848,7 +867,7 @@
<varlistentry>
<term><varname>HomeAddress=</varname></term>
<listitem>
- <para>Takes a boolean argument. Designates this address the "home address" as defined in
+ <para>Takes a boolean. Designates this address the "home address" as defined in
<ulink url="https://tools.ietf.org/html/rfc6275">RFC 6275</ulink>.
Supported only on IPv6. Defaults to false.</para>
</listitem>
@@ -856,7 +875,7 @@
<varlistentry>
<term><varname>DuplicateAddressDetection=</varname></term>
<listitem>
- <para>Takes a boolean argument. Do not perform Duplicate Address Detection
+ <para>Takes a boolean. Do not perform Duplicate Address Detection
<ulink url="https://tools.ietf.org/html/rfc4862">RFC 4862</ulink> when adding this address.
Supported only on IPv6. Defaults to false.</para>
</listitem>
@@ -864,7 +883,7 @@
<varlistentry>
<term><varname>ManageTemporaryAddress=</varname></term>
<listitem>
- <para>Takes a boolean argument. If true the kernel manage temporary addresses created
+ <para>Takes a boolean. If true the kernel manage temporary addresses created
from this one as template on behalf of Privacy Extensions
<ulink url="https://tools.ietf.org/html/rfc3041">RFC 3041</ulink>. For this to become
active, the use_tempaddr sysctl setting has to be set to a value greater than zero.
@@ -876,7 +895,7 @@
<varlistentry>
<term><varname>PrefixRoute=</varname></term>
<listitem>
- <para>Takes a boolean argument. When adding or modifying an IPv6 address, the userspace
+ <para>Takes a boolean. When adding or modifying an IPv6 address, the userspace
application needs a way to suppress adding a prefix route. This is for example relevant
together with IFA_F_MANAGERTEMPADDR, where userspace creates autoconf generated addresses,
but depending on on-link, no route for the prefix should be added. Defaults to false.</para>
@@ -885,7 +904,7 @@
<varlistentry>
<term><varname>AutoJoin=</varname></term>
<listitem>
- <para>Takes a boolean argument. Joining multicast group on ethernet level via
+ <para>Takes a boolean. Joining multicast group on ethernet level via
<command>ip maddr</command> command would not work if we have an Ethernet switch that does
IGMP snooping since the switch would not replicate multicast packets on ports that did not
have IGMP reports for the multicast addresses. Linux vxlan interfaces created via
@@ -899,6 +918,31 @@
</variablelist>
</refsect1>
+ <refsect1>
+ <title>[Neighbor] Section Options</title>
+ <para>A <literal>[Neighbor]</literal> section accepts the
+ following keys. The neighbor section adds a permanent, static
+ entry to the neighbor table (IPv6) or ARP table (IPv4) for
+ the given hardware address on the links matched for the network.
+ Specify several <literal>[Neighbor]</literal> sections to configure
+ several static neighbors.</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Address=</varname></term>
+ <listitem>
+ <para>The IP address of the neighbor.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>MACAddress=</varname></term>
+ <listitem>
+ <para>The hardware address of the neighbor.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
<refsect1>
<title>[IPv6AddressLabel] Section Options</title>
@@ -984,6 +1028,35 @@
<para>Specifies the outgoing device to match. The outgoing interface is only available for packets originating from local sockets that are bound to a device.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>SourcePort=</varname></term>
+ <listitem>
+ <para>Specifies the source IP port or IP port range match in forwarding information base (FIB) rules.
+ A port range is specified by the lower and upper port separated by a dash. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>DestinationPort=</varname></term>
+ <listitem>
+ <para>Specifies the destination IP port or IP port range match in forwarding information base (FIB) rules.
+ A port range is specified by the lower and upper port separated by a dash. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>IPProtocol=</varname></term>
+ <listitem>
+ <para>Specifies the IP protocol to match in forwarding information base (FIB) rules. Takes IP protocol name such as <literal>tcp</literal>,
+ <literal>udp</literal> or <literal>sctp</literal>, or IP protocol number such as <literal>6</literal> for <literal>tcp</literal> or
+ <literal>17</literal> for <literal>udp</literal>.
+ Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>InvertRule=</varname></term>
+ <listitem>
+ <para>A boolean. Specifies wheather the rule to be inverted. Defaults to false.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
@@ -1003,10 +1076,10 @@
<varlistentry>
<term><varname>GatewayOnlink=</varname></term>
<listitem>
- <para>The <literal>GatewayOnlink</literal> option tells the kernel that it does not have
+ <para>Takes a boolean. If set to true, the kernel does not have
to check if the gateway is reachable directly by the current machine (i.e., the kernel does
not need to check if the gateway is attached to the local network), so that we can insert the
- route in the kernel table without it being complained about. A boolean, defaults to <literal>no</literal>.
+ route in the kernel table without it being complained about. Defaults to <literal>no</literal>.
</para>
</listitem>
</varlistentry>
@@ -1069,7 +1142,7 @@
<varlistentry>
<term><varname>Protocol=</varname></term>
<listitem>
- <para>The Protocol identifier for the route. Takes a number between 0 and 255 or the special values
+ <para>The protocol identifier for the route. Takes a number between 0 and 255 or the special values
<literal>kernel</literal>, <literal>boot</literal> and <literal>static</literal>. Defaults to
<literal>static</literal>.
</para>
@@ -1078,12 +1151,13 @@
<varlistentry>
<term><varname>Type=</varname></term>
<listitem>
- <para>The Type identifier for special route types, which can be
- <literal>unicast</literal> route to a destination network address which describes the path to the destination,
- <literal>blackhole</literal> packets are discarded silently,
- <literal>unreachable</literal> packets are discarded and the ICMP message host unreachable is generated,
- <literal>prohibit</literal> packets are discarded and the ICMP message communication administratively
- prohibited is generated. Defaults to <literal>unicast</literal>.
+ <para>Specifies the type for the route. If <literal>unicast</literal>, a regular route is defined, i.e. a
+ route indicating the path to take to a destination network address. If <literal>blackhole</literal>, packets
+ to the defined route are discarded silently. If <literal>unreachable</literal>, packets to the defined route
+ are discarded and the ICMP message "Host Unreachable" is generated. If <literal>prohibit</literal>, packets
+ to the defined route are discarded and the ICMP message "Communication Administratively Prohibited" is
+ generated. If <literal>throw</literal>, route lookup in the current routing table will fail and the route
+ selection process will return to Routing Policy Database (RPDB). Defaults to <literal>unicast</literal>.
</para>
</listitem>
</varlistentry>
@@ -1093,7 +1167,7 @@
<para>The TCP initial congestion window is used during the start of a TCP connection. During the start of a TCP
session, when a client requests a resource, the server's initial congestion window determines how many data bytes
will be sent during the initial burst of data. Takes a size in bytes between 1 and 4294967295 (2^32 - 1). The usual
- suffixes K, M, G are supported and are understood to the base of 1024. Defaults to unset.
+ suffixes K, M, G are supported and are understood to the base of 1024. When unset, the kernel's default will be used.
</para>
</listitem>
</varlistentry>
@@ -1103,14 +1177,14 @@
<para>The TCP initial advertised receive window is the amount of receive data (in bytes) that can initally be buffered at one time
on a connection. The sending host can send only that amount of data before waiting for an acknowledgment and window update
from the receiving host. Takes a size in bytes between 1 and 4294967295 (2^32 - 1). The usual suffixes K, M, G are supported
- and are understood to the base of 1024. Defaults to unset.
+ and are understood to the base of 1024. When unset, the kernel's default will be used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>QuickAck=</varname></term>
<listitem>
- <para>Takes a boolean argument. When true enables TCP quick ack mode for the route. Defaults to unset.
+ <para>Takes a boolean. When true enables TCP quick ack mode for the route. When unset, the kernel's default will be used.
</para>
</listitem>
</varlistentry>
@@ -1159,13 +1233,14 @@
<listitem>
<para>When true, the interface maximum transmission unit
from the DHCP server will be used on the current link.
+ If <varname>MTUBytes=</varname> is set, then this setting is ignored.
Defaults to false.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Anonymize=</varname></term>
<listitem>
- <para>Takes a boolean argument. When true, the options sent to the DHCP server will
+ <para>Takes a boolean. When true, the options sent to the DHCP server will
follow the <ulink url="https://tools.ietf.org/html/rfc7844">RFC 7844</ulink>
(Anonymity Profiles for DHCP Clients) to minimize disclosure of identifying information.
Defaults to false.</para>
@@ -1186,29 +1261,32 @@
<varlistentry>
<term><varname>SendHostname=</varname></term>
<listitem>
- <para>When true (the default), the machine's hostname will
- be sent to the DHCP server.</para>
+ <para>When true (the default), the machine's hostname will be sent to the DHCP server.
+ Note that the machine's hostname must consist only of 7-bit ASCII lower-case characters and
+ no spaces or dots, and be formatted as a valid DNS domain name. Otherwise, the hostname is not
+ sent even if this is set to true.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>UseHostname=</varname></term>
<listitem>
<para>When true (the default), the hostname received from
- the DHCP server will be set as the transient hostname of the system
+ the DHCP server will be set as the transient hostname of the system.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>Hostname=</varname></term>
- <listitem>
- <para>Use this value for the hostname which is sent to the
- DHCP server, instead of machine's hostname.</para>
- </listitem>
- </varlistentry>
+ <term><varname>Hostname=</varname></term>
+ <listitem>
+ <para>Use this value for the hostname which is sent to the DHCP server, instead of machine's hostname.
+ Note that the specified hostname must consist only of 7-bit ASCII lower-case characters and
+ no spaces or dots, and be formatted as a valid DNS domain name.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><varname>UseDomains=</varname></term>
<listitem>
- <para>Takes a boolean argument, or the special value <literal>route</literal>. When true, the domain name
+ <para>Takes a boolean, or the special value <literal>route</literal>. When true, the domain name
received from the DHCP server will be used as DNS search domain over this link, similar to the effect of
the <option>Domains=</option> setting. If set to <literal>route</literal>, the domain name received from
the DHCP server will be used for routing DNS queries only, but not for searching, similar to the effect of
@@ -1350,7 +1428,7 @@
<varlistentry>
<term><varname>RapidCommit=</varname></term>
<listitem>
- <para>A boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server through
+ <para>Takes a boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server through
a rapid two-message exchange (solicit and reply). When the rapid commit option is enabled by both
the DHCPv6 client and the DHCPv6 server, the two-message exchange is used, rather than the default
four-method exchange (solicit, advertise, request, and reply). The two-message exchange provides
@@ -1360,6 +1438,22 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ForceDHCPv6PDOtherInformation=</varname></term>
+ <listitem>
+ <para>Takes a boolean that enforces DHCPv6 stateful mode when the 'Other information' bit is set in
+ Router Advertisement messages. By default setting only the 'O' bit in Router Advertisements
+ makes DHCPv6 request network information in a stateless manner using a two-message Information
+ Request and Information Reply message exchange.
+ <ulink url="https://tools.ietf.org/html/rfc7084">RFC 7084</ulink>, requirement WPD-4, updates
+ this behavior for a Customer Edge router so that stateful DHCPv6 Prefix Delegation is also
+ requested when only the 'O' bit is set in Router Advertisements. This option enables such a CE
+ behavior as it is impossible to automatically distinguish the intention of the 'O' bit otherwise.
+ By default this option is set to 'false', enable it if no prefixes are delegated when the device
+ should be acting as a CE router.</para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
@@ -1384,7 +1478,7 @@
<varlistentry>
<term><varname>UseDomains=</varname></term>
<listitem>
- <para>Takes a boolean argument, or the special value <literal>route</literal>. When true, the domain name
+ <para>Takes a boolean, or the special value <literal>route</literal>. When true, the domain name
received via IPv6 Router Advertisement (RA) will be used as DNS search domain over this link, similar to
the effect of the <option>Domains=</option> setting. If set to <literal>route</literal>, the domain name
received via IPv6 RA will be used for routing DNS queries only, but not for searching, similar to the
@@ -1460,11 +1554,9 @@
<term><varname>EmitDNS=</varname></term>
<term><varname>DNS=</varname></term>
- <listitem><para>Configures whether the DHCP leases handed out
- to clients shall contain DNS server information. The
- <varname>EmitDNS=</varname> setting takes a boolean argument
- and defaults to <literal>yes</literal>. The DNS servers to
- pass to clients may be configured with the
+ <listitem><para>Takes a boolean. Configures whether the DHCP leases handed out
+ to clients shall contain DNS server information. Defaults to <literal>yes</literal>.
+ The DNS servers to pass to clients may be configured with the
<varname>DNS=</varname> option, which takes a list of IPv4
addresses. If the <varname>EmitDNS=</varname> option is
enabled but no servers configured, the servers are
@@ -1511,10 +1603,8 @@
<term><varname>EmitTimezone=</varname></term>
<term><varname>Timezone=</varname></term>
- <listitem><para>Configures whether the DHCP leases handed out
- to clients shall contain timezone information. The
- <varname>EmitTimezone=</varname> setting takes a boolean
- argument and defaults to <literal>yes</literal>. The
+ <listitem><para>Takes a boolean. Configures whether the DHCP leases handed out
+ to clients shall contain timezone information. Defaults to <literal>yes</literal>. The
<varname>Timezone=</varname> setting takes a timezone string
(such as <literal>Europe/Berlin</literal> or
<literal>UTC</literal>) to pass to clients. If no explicit
@@ -1540,11 +1630,11 @@
<term><varname>Managed=</varname></term>
<term><varname>OtherInformation=</varname></term>
- <listitem><para>Controls whether a DHCPv6 server is used to acquire IPv6
- addresses on the network link when <varname>Managed=</varname> boolean
+ <listitem><para>Takes a boolean. Controls whether a DHCPv6 server is used to acquire IPv6
+ addresses on the network link when <varname>Managed=</varname>
is set to <literal>true</literal> or if only additional network
information can be obtained via DHCPv6 for the network link when
- <varname>OtherInformation=</varname> boolean is set to
+ <varname>OtherInformation=</varname> is set to
<literal>true</literal>. Both settings default to
<literal>false</literal>, which means that a DHCPv6 server is not being
used.</para></listitem>
@@ -1553,10 +1643,9 @@
<varlistentry>
<term><varname>RouterLifetimeSec=</varname></term>
- <listitem><para>Configures the IPv6 router lifetime in seconds. If set,
+ <listitem><para>Takes a timespan. Configures the IPv6 router lifetime in seconds. If set,
this host also announces itself in Router Advertisements as an IPv6
- router for the network link. Defaults to unset, which means the host is
- not acting as a router.</para>
+ router for the network link. When unset, the host is not acting as a router.</para>
</listitem>
</varlistentry>
@@ -1630,7 +1719,7 @@
<term><varname>AddressAutoconfiguration=</varname></term>
<term><varname>OnLink=</varname></term>
- <listitem><para>Boolean values to specify whether IPv6 addresses can be
+ <listitem><para>Takes a boolean to specify whether IPv6 addresses can be
autoconfigured with this prefix and whether the prefix can be used for
onlink determination. Both settings default to <literal>true</literal>
in order to ease configuration.
@@ -1670,42 +1759,51 @@
<varlistentry>
<term><varname>UnicastFlood=</varname></term>
<listitem>
- <para>A boolean. Controls whether the bridge should flood
+ <para>Takes a boolean. Controls whether the bridge should flood
traffic for which an FDB entry is missing and the destination
- is unknown through this port. Defaults to unset.
+ is unknown through this port. When unset, the kernel's default will be used.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>MulticastToUnicast=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Multicast to unicast works on top of the multicast snooping feature of
+ the bridge. Which means unicast copies are only delivered to hosts which are interested in it.
+ When unset, the kernel's default will be used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>HairPin=</varname></term>
<listitem>
- <para>A boolean. Configures whether traffic may be sent back
- out of the port on which it was received. Defaults to unset. When this
- flag is false, and the bridge will not forward traffic back
- out of the receiving port.</para>
+ <para>Takes a boolean. Configures whether traffic may be sent back
+ out of the port on which it was received. When this flag is false, and the bridge
+ will not forward traffic back out of the receiving port.
+ When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>UseBPDU=</varname></term>
<listitem>
- <para>A boolean. Configures whether STP Bridge Protocol Data Units will be
- processed by the bridge port. Defaults to unset.</para>
+ <para>Takes a boolean. Configures whether STP Bridge Protocol Data Units will be
+ processed by the bridge port. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>FastLeave=</varname></term>
<listitem>
- <para>A boolean. This flag allows the bridge to immediately stop multicast
+ <para>Takes a boolean. This flag allows the bridge to immediately stop multicast
traffic on a port that receives an IGMP Leave message. It is only used with
- IGMP snooping if enabled on the bridge. Defaults to unset.</para>
+ IGMP snooping if enabled on the bridge. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>AllowPortToBeRoot=</varname></term>
<listitem>
- <para>A boolean. Configures whether a given port is allowed to
+ <para>Takes a boolean. Configures whether a given port is allowed to
become a root port. Only used when STP is enabled on the bridge.
- Defaults to unset.</para>
+ When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml
index 4a263d376d..f978fef235 100644
--- a/man/systemd.nspawn.xml
+++ b/man/systemd.nspawn.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
@@ -127,6 +127,16 @@
</varlistentry>
<varlistentry>
+ <term><varname>Ephemeral=</varname></term>
+
+ <listitem><para>Takes a boolean argument, which defaults to off, If enabled, the container is run with
+ a temporary snapshot of its file system that is removed immediately when the container terminates.
+ This is equivalent to the <option>--ephemeral</option> command line switch. See
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> for details
+ about the specific options supported.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>ProcessTwo=</varname></term>
<listitem><para>Takes a boolean argument, which defaults to off. If enabled, the specified program is run as
@@ -340,7 +350,7 @@
<term><varname>Timezone=</varname></term>
<listitem><para>Configures how <filename>/etc/localtime</filename> in the container shall be handled. This is
- equivalent to the <option>--localtime=</option> command line switch, and takes the same argument. See
+ equivalent to the <option>--timezone=</option> command line switch, and takes the same argument. See
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details.</para></listitem>
</varlistentry>
diff --git a/man/systemd.offline-updates.xml b/man/systemd.offline-updates.xml
index 113d74a220..13fdfc28de 100644
--- a/man/systemd.offline-updates.xml
+++ b/man/systemd.offline-updates.xml
@@ -134,7 +134,7 @@
</listitem>
<listitem>
- <para>The update service should declare <varname>DefaultDependencies=false</varname>,
+ <para>The update service should declare <varname>DefaultDependencies=no</varname>,
<varname>Requires=sysinit.target</varname>, <varname>After=sysinit.target</varname>,
<varname>After=system-update-pre.target</varname>
and explicitly pull in any other services it requires.</para>
diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml
index 370c110592..aa7d9bcd59 100644
--- a/man/systemd.resource-control.xml
+++ b/man/systemd.resource-control.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -176,8 +176,6 @@
the startup phase. Using <varname>StartupCPUWeight=</varname> allows prioritizing specific services at
boot-up differently than during normal runtime.</para>
- <para>Implies <literal>CPUAccounting=true</literal>.</para>
-
<para>These settings replace <varname>CPUShares=</varname> and <varname>StartupCPUShares=</varname>.</para>
</listitem>
</varlistentry>
@@ -192,12 +190,11 @@
<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/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.</para>
+ 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
20% CPU time on one CPU.</para>
- <para>Implies <literal>CPUAccounting=true</literal>.</para>
</listitem>
</varlistentry>
@@ -217,6 +214,25 @@
</varlistentry>
<varlistentry>
+ <term><varname>MemoryMin=<replaceable>bytes</replaceable></varname></term>
+
+ <listitem>
+ <para>Specify the memory usage protection of the executed processes in this unit. If the memory usages of
+ this unit and all its ancestors are below their minimum boundaries, this unit's memory won't be reclaimed.</para>
+
+ <para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
+ parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
+ percentage value may be specified, which is taken relative to the installed physical memory on the
+ system. 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>
+
+ <para>This setting is supported only if the unified control group hierarchy is used and disables
+ <varname>MemoryLimit=</varname>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>MemoryLow=<replaceable>bytes</replaceable></varname></term>
<listitem>
@@ -231,8 +247,6 @@
control group attribute, see <ulink
url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
- <para>Implies <literal>MemoryAccounting=true</literal>.</para>
-
<para>This setting is supported only if the unified control group hierarchy is used and disables
<varname>MemoryLimit=</varname>.</para>
</listitem>
@@ -254,8 +268,6 @@
<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>
- <para>Implies <literal>MemoryAccounting=true</literal>.</para>
-
<para>This setting is supported only if the unified control group hierarchy is used and disables
<varname>MemoryLimit=</varname>.</para>
</listitem>
@@ -277,8 +289,6 @@
<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>
- <para>Implies <literal>MemoryAccounting=true</literal>.</para>
-
<para>This setting replaces <varname>MemoryLimit=</varname>.</para>
</listitem>
</varlistentry>
@@ -295,8 +305,6 @@
<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>
- <para>Implies <literal>MemoryAccounting=true</literal>.</para>
-
<para>This setting is supported only if the unified control group hierarchy is used and disables
<varname>MemoryLimit=</varname>.</para>
</listitem>
@@ -332,7 +340,7 @@
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>
- <para>Implies <literal>TasksAccounting=true</literal>. The
+ <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>
@@ -374,8 +382,6 @@
phase. This allows prioritizing specific services at boot-up
differently than during runtime.</para>
- <para>Implies <literal>IOAccounting=true</literal>.</para>
-
<para>These settings replace <varname>BlockIOWeight=</varname> and <varname>StartupBlockIOWeight=</varname>
and disable settings prefixed with <varname>BlockIO</varname> or <varname>StartupBlockIO</varname>.</para>
</listitem>
@@ -387,15 +393,13 @@
<listitem>
<para>Set the per-device overall block I/O weight for the executed processes, if the unified control group
hierarchy is used on the system. Takes a space-separated pair of a file path and a weight value to specify
- the device specific weight value, between 1 and 10000. (Example: "/dev/sda 1000"). The file path may be
- specified as path to a block device node or as any other file, in which case the backing block 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
+ the device specific weight value, between 1 and 10000. (Example: <literal>/dev/sda 1000</literal>). The file
+ path may be specified as path to a block device node or as any other file, in which case the backing block
+ 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>
- <para>Implies <literal>IOAccounting=true</literal>.</para>
-
<para>This setting replaces <varname>BlockIODeviceWeight=</varname> and disables settings prefixed with
<varname>BlockIO</varname> or <varname>StartupBlockIO</varname>.</para>
</listitem>
@@ -419,8 +423,6 @@
url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.
</para>
- <para>Implies <literal>IOAccounting=true</literal>.</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>
@@ -445,14 +447,31 @@
url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.
</para>
- <para>Implies <literal>IOAccounting=true</literal>.</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>
</listitem>
</varlistentry>
<varlistentry>
+ <term><varname>IODeviceLatencyTargetSec=<replaceable>device</replaceable> <replaceable>target</replaceable></varname></term>
+
+ <listitem>
+ <para>Set the per-device average target I/O latency for the executed processes, if the unified control group
+ hierarchy is used on the system. Takes a file path and a timespan separated by a space to specify
+ the device specific latency target. (Example: "/dev/sda 25ms"). The file path may be specified
+ as path to a block device node or as any other file, in which case the backing block device of the file
+ 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>
+
+ <para>Implies <literal>IOAccounting=yes</literal>.</para>
+
+ <para>These settings are supported only if the unified control group hierarchy is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>IPAccounting=</varname></term>
<listitem>
@@ -711,9 +730,33 @@
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>
+
+ <para>For further details on the delegation model consult <ulink
+ url="https://systemd.io/CGROUP_DELEGATION">Control Group APIs and Delegation</ulink>.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>DisableControllers=</varname></term>
+
+ <listitem>
+ <para>Disables controllers from being enabled for a unit's children. If a controller listed is already in use
+ in its subtree, the controller will be removed from the subtree. This can be used to avoid child units being
+ able to implicitly or explicitly enable a controller. Defaults to not disabling any controllers.</para>
+
+ <para>It may not be possible to successfully disable a controller if the unit or any child of the unit in
+ question delegates controllers to its children, as any delegated subtree of the cgroup hierarchy is unmanaged
+ by systemd.</para>
+
+ <para>Multiple controllers may be specified, separated by spaces. You may also pass
+ <varname>DisableControllers=</varname> multiple times, in which case each new instance adds another controller
+ 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>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
@@ -741,7 +784,7 @@
the startup phase. Using <varname>StartupCPUShares=</varname> allows prioritizing specific services at
boot-up differently than during normal runtime.</para>
- <para>Implies <literal>CPUAccounting=true</literal>.</para>
+ <para>Implies <literal>CPUAccounting=yes</literal>.</para>
<para>These settings are deprecated. Use <varname>CPUWeight=</varname> and
<varname>StartupCPUWeight=</varname> instead.</para>
@@ -762,7 +805,7 @@
attribute, see <ulink
url="https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt">memory.txt</ulink>.</para>
- <para>Implies <literal>MemoryAccounting=true</literal>.</para>
+ <para>Implies <literal>MemoryAccounting=yes</literal>.</para>
<para>This setting is deprecated. Use <varname>MemoryMax=</varname> instead.</para>
</listitem>
@@ -803,7 +846,7 @@
boot-up differently than during runtime.</para>
<para>Implies
- <literal>BlockIOAccounting=true</literal>.</para>
+ <literal>BlockIOAccounting=yes</literal>.</para>
<para>These settings are deprecated. Use <varname>IOWeight=</varname> and <varname>StartupIOWeight=</varname>
instead.</para>
@@ -825,7 +868,7 @@
url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.</para>
<para>Implies
- <literal>BlockIOAccounting=true</literal>.</para>
+ <literal>BlockIOAccounting=yes</literal>.</para>
<para>This setting is deprecated. Use <varname>IODeviceWeight=</varname> instead.</para>
</listitem>
@@ -850,7 +893,7 @@
</para>
<para>Implies
- <literal>BlockIOAccounting=true</literal>.</para>
+ <literal>BlockIOAccounting=yes</literal>.</para>
<para>These settings are deprecated. Use <varname>IOReadBandwidthMax=</varname> and
<varname>IOWriteBandwidthMax=</varname> instead.</para>
@@ -879,6 +922,7 @@
<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>.
</para>
</refsect1>
</refentry>
diff --git a/man/systemd.scope.xml b/man/systemd.scope.xml
index cc0345abac..18560fb7ee 100644
--- a/man/systemd.scope.xml
+++ b/man/systemd.scope.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index add54524ce..ad04efeb34 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -153,77 +153,93 @@
<varlistentry>
<term><varname>Type=</varname></term>
- <listitem><para>Configures the process start-up type for this
- service unit. One of
- <option>simple</option>,
- <option>forking</option>,
- <option>oneshot</option>,
- <option>dbus</option>,
- <option>notify</option> or
- <option>idle</option>.</para>
-
- <para>If set to <option>simple</option> (the default if
- neither <varname>Type=</varname> nor
- <varname>BusName=</varname>, but <varname>ExecStart=</varname>
- are specified), it is expected that the process configured
- with <varname>ExecStart=</varname> is the main process of the
- service. In this mode, if the process offers functionality to
- other processes on the system, its communication channels
- should be installed before the daemon is started up (e.g.
- sockets set up by systemd, via socket activation), as systemd
- will immediately proceed starting follow-up units.</para>
-
- <para>If set to <option>forking</option>, it is expected that
- the process configured with <varname>ExecStart=</varname> will
- call <function>fork()</function> as part of its start-up. The
- parent process is expected to exit when start-up is complete
- and all communication channels are set up. The child continues
- to run as the main daemon process. This is the behavior of
- traditional UNIX daemons. If this setting is used, it is
- recommended to also use the <varname>PIDFile=</varname>
- option, so that systemd can identify the main process of the
- daemon. systemd will proceed with starting follow-up units as
- soon as the parent process exits.</para>
-
- <para>Behavior of <option>oneshot</option> is similar to
- <option>simple</option>; however, it is expected that the
- process has to exit before systemd starts follow-up units.
- <varname>RemainAfterExit=</varname> is particularly useful for
- this type of service. This is the implied default if neither
- <varname>Type=</varname> nor <varname>ExecStart=</varname> are
- specified.</para>
-
- <para>Behavior of <option>dbus</option> is similar to
- <option>simple</option>; however, it is expected that the
- daemon acquires a name on the D-Bus bus, as configured by
- <varname>BusName=</varname>. systemd will proceed with
- starting follow-up units after the D-Bus bus name has been
- acquired. Service units with this option configured implicitly
- gain dependencies on the <filename>dbus.socket</filename>
- unit. This type is the default if <varname>BusName=</varname>
- is specified.</para>
-
- <para>Behavior of <option>notify</option> is similar to
- <option>simple</option>; however, it is expected that the
- daemon sends a notification message via
- <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- or an equivalent call when it has finished starting up.
- systemd will proceed with starting follow-up units after this
- notification message has been sent. If this option is used,
- <varname>NotifyAccess=</varname> (see below) should be set to
- open access to the notification socket provided by systemd. If
- <varname>NotifyAccess=</varname> is missing or set to
- <option>none</option>, it will be forcibly set to
- <option>main</option>. Note that currently
- <varname>Type=</varname><option>notify</option> will not work
- if used in combination with
- <varname>PrivateNetwork=</varname><option>yes</option>.</para>
-
- <para>Behavior of <option>idle</option> is very similar to <option>simple</option>; however, actual execution
- of the service program is delayed until all active jobs are dispatched. This may be used to avoid interleaving
- of output of shell services with the status output on the console. Note that this type is useful only to
- improve console output, it is not useful as a general unit ordering tool, and the effect of this service type
- is subject to a 5s time-out, after which the service program is invoked anyway.</para>
+ <listitem>
+ <para>Configures the process start-up type for this service unit. One of <option>simple</option>,
+ <option>exec</option>, <option>forking</option>, <option>oneshot</option>, <option>dbus</option>,
+ <option>notify</option> or <option>idle</option>:</para>
+
+ <itemizedlist>
+ <listitem><para>If set to <option>simple</option> (the default if <varname>ExecStart=</varname> is
+ specified but neither <varname>Type=</varname> nor <varname>BusName=</varname> are), the service manager
+ will consider the unit started immediately after the main service process has been forked off. It is
+ expected that the process configured with <varname>ExecStart=</varname> is the main process of the
+ service. In this mode, if the process offers functionality to other processes on the system, its
+ communication channels should be installed before the service is started up (e.g. sockets set up by
+ systemd, via socket activation), as the service manager will immediately proceed starting follow-up units,
+ right after creating the main service process, and before executing the service's binary. Note that this
+ means <command>systemctl start</command> command lines for <option>simple</option> services will report
+ success even if the service's binary cannot be invoked successfully (for example because the selected
+ <varname>User=</varname> doesn't exist, or the service binary is missing).</para></listitem>
+
+ <listitem><para>The <option>exec</option> type is similar to <option>simple</option>, but the service
+ manager will consider the unit started immediately after the main service binary has been executed. The service
+ manager will delay starting of follow-up units until that point. (Or in other words:
+ <option>simple</option> proceeds with further jobs right after <function>fork()</function> returns, while
+ <option>exec</option> will not proceed before both <function>fork()</function> and
+ <function>execve()</function> in the service process succeeded.) Note that this means <command>systemctl
+ start</command> command lines for <option>exec</option> services will report failure when the service's
+ binary cannot be invoked successfully (for example because the selected <varname>User=</varname> doesn't
+ exist, or the service binary is missing).</para></listitem>
+
+ <listitem><para>If set to <option>forking</option>, it is expected that the process configured with
+ <varname>ExecStart=</varname> will call <function>fork()</function> as part of its start-up. The parent
+ process is expected to exit when start-up is complete and all communication channels are set up. The child
+ continues to run as the main service process, and the service manager will consider the unit started when
+ the parent process exits. This is the behavior of traditional UNIX services. If this setting is used, it is
+ recommended to also use the <varname>PIDFile=</varname> option, so that systemd can reliably identify the
+ main process of the service. systemd will proceed with starting follow-up units as soon as the parent
+ process exits.</para></listitem>
+
+ <listitem><para>Behavior of <option>oneshot</option> is similar to <option>simple</option>; however, the
+ service manager will consider the unit started after the main process exits. It will then start follow-up
+ units. <varname>RemainAfterExit=</varname> is particularly useful for this type of
+ service. <varname>Type=</varname><option>oneshot</option> is the implied default if neither
+ <varname>Type=</varname> nor <varname>ExecStart=</varname> are specified.</para></listitem>
+
+ <listitem><para>Behavior of <option>dbus</option> is similar to <option>simple</option>; however, it is
+ expected that the service acquires a name on the D-Bus bus, as configured by
+ <varname>BusName=</varname>. systemd will proceed with starting follow-up units after the D-Bus bus name
+ has been acquired. Service units with this option configured implicitly gain dependencies on the
+ <filename>dbus.socket</filename> unit. This type is the default if <varname>BusName=</varname> is
+ specified.</para></listitem>
+
+ <listitem><para>Behavior of <option>notify</option> is similar to <option>exec</option>; however, it is
+ expected that the service sends a notification message via
+ <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry> or an
+ equivalent call when it has finished starting up. systemd will proceed with starting follow-up units after
+ this notification message has been sent. If this option is used, <varname>NotifyAccess=</varname> (see
+ below) should be set to open access to the notification socket provided by systemd. If
+ <varname>NotifyAccess=</varname> is missing or set to <option>none</option>, it will be forcibly set to
+ <option>main</option>. Note that currently <varname>Type=</varname><option>notify</option> will not work if
+ used in combination with <varname>PrivateNetwork=</varname><option>yes</option>.</para></listitem>
+
+ <listitem><para>Behavior of <option>idle</option> is very similar to <option>simple</option>; however,
+ actual execution of the service program is delayed until all active jobs are dispatched. This may be used
+ to avoid interleaving of output of shell services with the status output on the console. Note that this
+ type is useful only to improve console output, it is not useful as a general unit ordering tool, and the
+ effect of this service type is subject to a 5s timeout, after which the service program is invoked
+ anyway.</para></listitem>
+ </itemizedlist>
+
+ <para>It is generally recommended to use <varname>Type=</varname><option>simple</option> for long-running
+ services whenever possible, as it is the simplest and fastest option. However, as this service type won't
+ propagate service start-up failures and doesn't allow ordering of other units against completion of
+ initialization of the service (which for example is useful if clients need to connect to the service through
+ some form of IPC, and the IPC channel is only established by the service itself — in contrast to doing this
+ ahead of time through socket or bus activation or similar), it might not be sufficient for many cases. If so,
+ <option>notify</option> or <option>dbus</option> (the latter only in case the service provides a D-Bus
+ interface) are the preferred options as they allow service program code to precisely schedule when to
+ consider the service started up successfully and when to proceed with follow-up units. The
+ <option>notify</option> service type requires explicit support in the service codebase (as
+ <function>sd_notify()</function> or an equivalent API needs to be invoked by the service at the appropriate
+ time) — if it's not supported, then <option>forking</option> is an alternative: it supports the traditional
+ UNIX service start-up protocol. Finally, <option>exec</option> might be an option for cases where it is
+ enough to ensure the service binary is invoked, and where the service binary itself executes no or little
+ initialization on its own (and its initialization is unlikely to fail). Note that using any type other than
+ <option>simple</option> possibly delays the boot process, as the service manager needs to wait for service
+ initialization to complete. It is hence recommended not to needlessly use any types other than
+ <option>simple</option>. (Also note it is generally not recommended to use <option>idle</option> or
+ <option>oneshot</option> for long-running services.)</para>
</listitem>
</varlistentry>
@@ -256,14 +272,15 @@
<varlistentry>
<term><varname>PIDFile=</varname></term>
- <listitem><para>Takes an absolute path referring to the PID file of the service. Usage of this option is
- recommended for services where <varname>Type=</varname> is set to <option>forking</option>. The service manager
- will read the PID of the main process of the service from this file after start-up of the service. The service
- manager will not write to the file configured here, although it will remove the file after the service has shut
- down if it still exists. The PID file does not need to be owned by a privileged user, but if it is owned by an
- unprivileged user additional safety restrictions are enforced: the file may not be a symlink to a file owned by
- a different user (neither directly nor indirectly), and the PID file must refer to a process already belonging
- to the service.</para></listitem>
+ <listitem><para>Takes a path referring to the PID file of the service. Usage of this option is recommended for
+ services where <varname>Type=</varname> is set to <option>forking</option>. The path specified typically points
+ to a file below <filename>/run/</filename>. If a relative path is specified it is hence prefixed with
+ <filename>/run/</filename>. The service manager will read the PID of the main process of the service from this
+ file after start-up of the service. The service manager will not write to the file configured here, although it
+ will remove the file after the service has shut down if it still exists. The PID file does not need to be owned
+ by a privileged user, but if it is owned by an unprivileged user additional safety restrictions are enforced:
+ the file may not be a symlink to a file owned by a different user (neither directly nor indirectly), and the
+ PID file must refer to a process already belonging to the service.</para></listitem>
</varlistentry>
<varlistentry>
@@ -318,7 +335,7 @@
<row>
<entry><literal>-</literal></entry>
- <entry>If the executable path is prefixed with <literal>-</literal>, an exit code of the command normally considered a failure (i.e. non-zero exit status or abnormal exit due to signal) is ignored and considered success.</entry>
+ <entry>If the executable path is prefixed with <literal>-</literal>, an exit code of the command normally considered a failure (i.e. non-zero exit status or abnormal exit due to signal) is recorded, but has no further effect and is considered equivalent to success.</entry>
</row>
<row>
@@ -583,7 +600,8 @@
"keep-alive ping"). If the time between two such calls is
larger than the configured time, then the service is placed in
a failed state and it will be terminated with
- <constant>SIGABRT</constant>. By setting
+ <constant>SIGABRT</constant> (or the signal specified by
+ <varname>WatchdogSignal=</varname>). By setting
<varname>Restart=</varname> to <option>on-failure</option>,
<option>on-watchdog</option>, <option>on-abnormal</option> or
<option>always</option>, the service will be automatically
@@ -815,24 +833,6 @@
</varlistentry>
<varlistentry>
- <term><varname>PermissionsStartOnly=</varname></term>
- <listitem><para>Takes a boolean argument. If true, the
- permission-related execution options, as configured with
- <varname>User=</varname> and similar options (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for more information), are only applied to the process started
- with
- <varname>ExecStart=</varname>, and not to the various other
- <varname>ExecStartPre=</varname>,
- <varname>ExecStartPost=</varname>,
- <varname>ExecReload=</varname>,
- <varname>ExecStop=</varname>, and
- <varname>ExecStopPost=</varname>
- commands. If false, the setting is applied to all configured
- commands the same way. Defaults to false.</para></listitem>
- </varlistentry>
-
- <varlistentry>
<term><varname>RootDirectoryStartOnly=</varname></term>
<listitem><para>Takes a boolean argument. If true, the root
directory, as configured with the
diff --git a/man/systemd.slice.xml b/man/systemd.slice.xml
index 37bee0ea4c..43e7c6a73a 100644
--- a/man/systemd.slice.xml
+++ b/man/systemd.slice.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml
index 19c2ca9907..fb51ef6658 100644
--- a/man/systemd.socket.xml
+++ b/man/systemd.socket.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -68,8 +68,8 @@
or it must be a template unit named the same way. Example: a
socket file <filename>foo.socket</filename> needs a matching
service <filename>foo.service</filename> if
- <option>Accept=false</option> is set. If
- <option>Accept=true</option> is set, a service template file
+ <option>Accept=no</option> is set. If
+ <option>Accept=yes</option> is set, a service template file
<filename>foo@.service</filename> must exist from which services
are instantiated for each incoming connection.</para>
@@ -94,6 +94,18 @@
socket passing (i.e. sockets passed in via standard input and
output, using <varname>StandardInput=socket</varname> in the
service file).</para>
+
+ <para>All network sockets allocated through <filename>.socket</filename> units are allocated in the host's network
+ namespace (see <citerefentry
+ project='man-pages'><refentrytitle>network_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>). This
+ does not mean however that the service activated by a configured socket unit has to be part of the host's network
+ namespace as well. It is supported and even good practice to run services in their own network namespace (for
+ example through <varname>PrivateNetwork=</varname>, see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>), receiving only
+ the sockets configured through socket-activation from the host's namespace. In such a set-up communication within
+ the host's network namespace is only permitted through the activation sockets passed in while all sockets allocated
+ from the service code itself will be associated with the service's own namespace, and thus possibly subject to a a
+ much more restrictive configuration.</para>
</refsect1>
<refsect1>
@@ -303,7 +315,7 @@
<varlistentry>
<term><varname>SocketProtocol=</varname></term>
- <listitem><para>Takes a one of <option>udplite</option>
+ <listitem><para>Takes one of <option>udplite</option>
or <option>sctp</option>. Specifies a socket protocol
(<constant>IPPROTO_UDPLITE</constant>) UDP-Lite
(<constant>IPPROTO_SCTP</constant>) SCTP socket respectively. </para>
@@ -312,7 +324,7 @@
<varlistentry>
<term><varname>BindIPv6Only=</varname></term>
- <listitem><para>Takes a one of <option>default</option>,
+ <listitem><para>Takes one of <option>default</option>,
<option>both</option> or <option>ipv6-only</option>. Controls
the IPV6_V6ONLY socket option (see
<citerefentry project='die-net'><refentrytitle>ipv6</refentrytitle><manvolnum>7</manvolnum></citerefentry>
@@ -395,17 +407,17 @@
incoming traffic. Defaults to <option>false</option>. For
performance reasons, it is recommended to write new daemons
only in a way that is suitable for
- <option>Accept=false</option>. A daemon listening on an
+ <option>Accept=no</option>. A daemon listening on an
<constant>AF_UNIX</constant> socket may, but does not need to,
call
<citerefentry><refentrytitle>close</refentrytitle><manvolnum>2</manvolnum></citerefentry>
on the received socket before exiting. However, it must not
unlink the socket from a file system. It should not invoke
<citerefentry><refentrytitle>shutdown</refentrytitle><manvolnum>2</manvolnum></citerefentry>
- on sockets it got with <varname>Accept=false</varname>, but it
+ on sockets it got with <varname>Accept=no</varname>, but it
may do so for sockets it got with
- <varname>Accept=true</varname> set. Setting
- <varname>Accept=true</varname> is mostly useful to allow
+ <varname>Accept=yes</varname> set. Setting
+ <varname>Accept=yes</varname> is mostly useful to allow
daemons designed for usage with
<citerefentry project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
to work unmodified with systemd socket
@@ -429,11 +441,11 @@
<term><varname>MaxConnections=</varname></term>
<listitem><para>The maximum number of connections to
simultaneously run services instances for, when
- <option>Accept=true</option> is set. If more concurrent
+ <option>Accept=yes</option> is set. If more concurrent
connections are coming in, they will be refused until at least
one existing connection is terminated. This setting has no
effect on sockets configured with
- <option>Accept=false</option> or datagram sockets. Defaults to
+ <option>Accept=no</option> or datagram sockets. Defaults to
64.</para></listitem>
</varlistentry>
diff --git a/man/systemd.special.xml b/man/systemd.special.xml
index fb12805fff..fd5639ba03 100644
--- a/man/systemd.special.xml
+++ b/man/systemd.special.xml
@@ -29,6 +29,7 @@
<filename>cryptsetup-pre.target</filename>,
<filename>cryptsetup.target</filename>,
<filename>ctrl-alt-del.target</filename>,
+ <filename>boot-complete.target</filename>,
<filename>default.target</filename>,
<filename>emergency.target</filename>,
<filename>exit.target</filename>,
@@ -104,942 +105,990 @@
</refsect1>
<refsect1>
- <title>Special System Units</title>
-
- <variablelist>
- <varlistentry>
- <term><filename>-.mount</filename></term>
- <listitem>
- <para>The root mount point, i.e. the mount unit for the <filename>/</filename> path. This unit is
- unconditionally active, during the entire time the system is up, as this mount point is where the basic
- userspace is running from.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><filename>basic.target</filename></term>
- <listitem>
- <para>A special target unit covering basic boot-up.</para>
-
- <para>systemd automatically adds dependency of the type
- <varname>After=</varname> for this target unit to all
- services (except for those with
- <varname>DefaultDependencies=no</varname>).</para>
-
- <para>Usually, this should pull-in all local mount points plus
- <filename>/var</filename>, <filename>/tmp</filename> and
- <filename>/var/tmp</filename>, swap devices, sockets, timers,
- path units and other basic initialization necessary for general
- purpose daemons. The mentioned mount points are special cased
- to allow them to be remote.
- </para>
-
- <para>This target usually does not pull in any non-target units
- directly, but rather does so indirectly via other early boot targets.
- It is instead meant as a synchronization point for late boot
- services. Refer to
- <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- for details on the targets involved.
- </para>
-
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>ctrl-alt-del.target</filename></term>
- <listitem>
- <para>systemd starts this target whenever Control+Alt+Del is
- pressed on the console. Usually, this should be aliased
- (symlinked) to <filename>reboot.target</filename>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>cryptsetup.target</filename></term>
- <listitem>
- <para>A target that pulls in setup services for all
- encrypted block devices.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>dbus.service</filename></term>
- <listitem>
- <para>A special unit for the D-Bus bus daemon. As soon as
- this service is fully started up systemd will connect to it
- and register its service.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>dbus.socket</filename></term>
- <listitem>
- <para>A special unit for the D-Bus system bus socket. All
- units with <varname>Type=dbus</varname> automatically gain a
- dependency on this unit.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>default.target</filename></term>
- <listitem>
- <para>The default unit systemd starts at bootup. Usually,
- this should be aliased (symlinked) to
- <filename>multi-user.target</filename> or
- <filename>graphical.target</filename>.</para>
-
- <para>The default unit systemd starts at bootup can be
- overridden with the <varname>systemd.unit=</varname> kernel
- command line option.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>display-manager.service</filename></term>
- <listitem>
- <para>The display manager service. Usually, this should be
- aliased (symlinked) to <filename>gdm.service</filename> or a
- similar display manager service.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>emergency.target</filename></term>
- <listitem>
- <para>A special target unit that starts an emergency shell on the main console. This target does not pull in
- any services or mounts. It is the most minimal version of starting the system in order to acquire an
- interactive shell; the only processes running are usually just the system manager (PID 1) and the shell
- process. This unit is supposed to be used with the kernel command line option
- <varname>systemd.unit=</varname>; it is also used when a file system check on a required file system fails,
- and boot-up cannot continue. Compare with <filename>rescue.target</filename>, which serves a similar purpose,
- but also starts the most basic services and mounts all file systems.</para>
-
- <para>Use the <literal>systemd.unit=emergency.target</literal> kernel command line option to boot into this
- mode. A short alias for this kernel command line option is <literal>emergency</literal>, for compatibility
- with SysV.</para>
-
- <para>In many ways booting into <filename>emergency.target</filename> is similar to the effect of booting
- with <literal>init=/bin/sh</literal> on the kernel command line, except that emergency mode provides you with
- the full system and service manager, and allows starting individual units in order to continue the boot
- process in steps.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>exit.target</filename></term>
- <listitem>
- <para>A special service unit for shutting down the system or
- user service manager. It is equivalent to
- <filename>poweroff.target</filename> on non-container
- systems, and also works in containers.</para>
-
- <para>systemd will start this unit when it receives the
- <constant>SIGTERM</constant> or <constant>SIGINT</constant>
- signal when running as user service daemon.</para>
-
- <para>Normally, this (indirectly) pulls in
- <filename>shutdown.target</filename>, which in turn should be
- conflicted by all units that want to be scheduled for
- shutdown when the service manager starts to exit.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>final.target</filename></term>
- <listitem>
- <para>A special target unit that is used during the shutdown
- logic and may be used to pull in late services after all
- normal services are already terminated and all mounts
- unmounted.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>getty.target</filename></term>
- <listitem>
- <para>A special target unit that pulls in statically
- configured local TTY <filename>getty</filename> instances.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>graphical.target</filename></term>
- <listitem>
- <para>A special target unit for setting up a graphical login
- screen. This pulls in
- <filename>multi-user.target</filename>.</para>
-
- <para>Units that are needed for graphical logins shall add
- <varname>Wants=</varname> dependencies for their unit to
- this unit (or <filename>multi-user.target</filename>) during
- installation. This is best configured via
- <varname>WantedBy=graphical.target</varname> in the unit's
- <literal>[Install]</literal> section.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>hibernate.target</filename></term>
- <listitem>
- <para>A special target unit for hibernating the system. This
- pulls in <filename>sleep.target</filename>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>hybrid-sleep.target</filename></term>
- <listitem>
- <para>A special target unit for hibernating and suspending
- the system at the same time. This pulls in
- <filename>sleep.target</filename>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>suspend-then-hibernate.target</filename></term>
- <listitem>
- <para>A special target unit for suspending the system for a period
- of time, waking it and putting it into hibernate. This pulls in
- <filename>sleep.target</filename>.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><filename>halt.target</filename></term>
- <listitem>
- <para>A special target unit for shutting down and halting
- the system. Note that this target is distinct from
- <filename>poweroff.target</filename> in that it generally
- really just halts the system rather than powering it
- down.</para>
-
- <para>Applications wanting to halt the system should not start this unit
- directly, but should instead execute <command>systemctl halt</command>
- (possibly with the <option>--no-block</option> option) or call
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
- <command>org.freedesktop.systemd1.Manager.Halt</command> D-Bus method
- directly.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>init.scope</filename></term>
- <listitem>
- <para>This scope unit is where the system and service manager (PID 1) itself resides. It is active as long as
- the system is running.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>initrd-fs.target</filename></term>
- <listitem>
- <para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- automatically adds dependencies of type
- <varname>Before=</varname> to
- <filename>sysroot-usr.mount</filename> and all mount points
- found in <filename>/etc/fstab</filename> that have
- <option>x-initrd.mount</option> and not have
- <option>noauto</option> mount options set.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>initrd-root-device.target</filename></term>
- <listitem>
- <para>A special initrd target unit that is reached when the root filesystem device is available, but before
- it has been mounted.
- <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- and
- <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- automatically setup the appropriate dependencies to make this happen.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>initrd-root-fs.target</filename></term>
- <listitem>
- <para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- automatically adds dependencies of type
- <varname>Before=</varname> to the
- <filename>sysroot.mount</filename> unit, which is generated
- from the kernel command line.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>kbrequest.target</filename></term>
- <listitem>
- <para>systemd starts this target whenever Alt+ArrowUp is
- pressed on the console. Note that any user with physical access
- to the machine will be able to do this, without authentication,
- so this should be used carefully.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>kexec.target</filename></term>
- <listitem>
- <para>A special target unit for shutting down and rebooting
- the system via kexec.</para>
-
- <para>Applications wanting to reboot the system should not start this unit
- directly, but should instead execute <command>systemctl kexec</command>
- (possibly with the <option>--no-block</option> option) or call
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
- <command>org.freedesktop.systemd1.Manager.KExec</command> D-Bus method
- directly.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>local-fs.target</filename></term>
- <listitem>
- <para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- automatically adds dependencies of type
- <varname>Before=</varname> to all mount units that refer to
- local mount points for this target unit. In addition, it
- adds dependencies of type <varname>Wants=</varname> to this
- target unit for those mounts listed in
- <filename>/etc/fstab</filename> that have the
- <option>auto</option> mount option set.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>machines.target</filename></term>
- <listitem>
- <para>A standard target unit for starting all the containers
- and other virtual machines. See <filename>systemd-nspawn@.service</filename>
- for an example.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>multi-user.target</filename></term>
- <listitem>
- <para>A special target unit for setting up a multi-user
- system (non-graphical). This is pulled in by
- <filename>graphical.target</filename>.</para>
-
- <para>Units that are needed for a multi-user system shall
- add <varname>Wants=</varname> dependencies for their unit to
- this unit during installation. This is best configured via
- <varname>WantedBy=multi-user.target</varname> in the unit's
- <literal>[Install]</literal> section.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>network-online.target</filename></term>
- <listitem>
- <para>Units that strictly require a configured network
- connection should pull in
- <filename>network-online.target</filename> (via a
- <varname>Wants=</varname> type dependency) and order
- themselves after it. This target unit is intended to pull in
- a service that delays further execution until the network is
- sufficiently set up. What precisely this requires is left to
- the implementation of the network managing service.</para>
-
- <para>Note the distinction between this unit and
- <filename>network.target</filename>. This unit is an active
- unit (i.e. pulled in by the consumer rather than the
- provider of this functionality) and pulls in a service which
- possibly adds substantial delays to further execution. In
- contrast, <filename>network.target</filename> is a passive
- unit (i.e. pulled in by the provider of the functionality,
- rather than the consumer) that usually does not delay
- execution much. Usually, <filename>network.target</filename>
- is part of the boot of most systems, while
- <filename>network-online.target</filename> is not, except
- when at least one unit requires it. Also see <ulink
- url="https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget">Running
- Services After the Network is up</ulink> for more
- information.</para>
-
- <para>All mount units for remote network file systems
- automatically pull in this unit, and order themselves after
- it. Note that networking daemons that simply provide
- functionality to other hosts generally do not need to pull
- this in.</para>
-
- <para>systemd automatically adds dependencies of type <varname>Wants=</varname> and <varname>After=</varname>
- for this target unit to all SysV init script service units with an LSB header referring to the
- <literal>$network</literal> facility.</para>
-
- <para>Note that this unit is only useful during the original system start-up logic. After the system has
- completed booting up, it will not track the online state of the system anymore. Due to this it cannot be used
- as a network connection monitor concept, it is purely a one-time system start-up concept.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>paths.target</filename></term>
- <listitem>
- <para>A special target unit that sets up all path units (see
- <citerefentry><refentrytitle>systemd.path</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details) that shall be active after boot.</para>
-
- <para>It is recommended that path units installed by
- applications get pulled in via <varname>Wants=</varname>
- dependencies from this unit. This is best configured via a
- <varname>WantedBy=paths.target</varname> in the path unit's
- <literal>[Install]</literal> section.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>poweroff.target</filename></term>
- <listitem>
- <para>A special target unit for shutting down and powering
- off the system.</para>
-
- <para>Applications wanting to power off the system should not start this unit
- directly, but should instead execute <command>systemctl poweroff</command>
- (possibly with the <option>--no-block</option> option) or call
- <citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
- <command>org.freedesktop.login1.Manager.PowerOff</command> D-Bus method
- directly.</para>
-
- <para><filename>runlevel0.target</filename> is an alias for
- this target unit, for compatibility with SysV.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>reboot.target</filename></term>
- <listitem>
- <para>A special target unit for shutting down and rebooting
- the system.</para>
-
- <para>Applications wanting to reboot the system should not start this unit
- directly, but should instead execute <command>systemctl reboot</command>
- (possibly with the <option>--no-block</option> option) or call
- <citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
- <command>org.freedesktop.login1.Manager.Reboot</command> D-Bus method
- directly.</para>
-
- <para><filename>runlevel6.target</filename> is an alias for
- this target unit, for compatibility with SysV.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>remote-cryptsetup.target</filename></term>
- <listitem>
- <para>Similar to <filename>cryptsetup.target</filename>, but for encrypted
- devices which are accessed over the network. It is used for
- <citerefentry><refentrytitle>crypttab</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- entries marked with <option>_netdev</option>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>remote-fs.target</filename></term>
- <listitem>
- <para>Similar to <filename>local-fs.target</filename>, but
- for remote mount points.</para>
-
- <para>systemd automatically adds dependencies of type
- <varname>After=</varname> for this target unit to all SysV
- init script service units with an LSB header referring to
- the <literal>$remote_fs</literal> facility.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>rescue.target</filename></term>
- <listitem>
- <para>A special target unit that pulls in the base system (including system mounts) and spawns a rescue
- shell. Isolate to this target in order to administer the system in single-user mode with all file systems
- mounted but with no services running, except for the most basic. Compare with
- <filename>emergency.target</filename>, which is much more reduced and does not provide the file systems or
- most basic services. Compare with <filename>multi-user.target</filename>, this target could be seen as
- <filename>single-user.target</filename>.</para>
-
- <para><filename>runlevel1.target</filename> is an alias for this target unit, for compatibility with
- SysV.</para>
-
- <para>Use the <literal>systemd.unit=rescue.target</literal> kernel command line option to boot into this
- mode. A short alias for this kernel command line option is <literal>1</literal>, for compatibility with
- SysV.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>runlevel2.target</filename></term>
- <term><filename>runlevel3.target</filename></term>
- <term><filename>runlevel4.target</filename></term>
- <term><filename>runlevel5.target</filename></term>
- <listitem>
- <para>These are targets that are called whenever the SysV
- compatibility code asks for runlevel 2, 3, 4, 5,
- respectively. It is a good idea to make this an alias for
- (i.e. symlink to) <filename>graphical.target</filename>
- (for runlevel 5) or <filename>multi-user.target</filename>
- (the others).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>shutdown.target</filename></term>
- <listitem>
- <para>A special target unit that terminates the services on
- system shutdown.</para>
-
- <para>Services that shall be terminated on system shutdown
- shall add <varname>Conflicts=</varname> and
- <varname>Before=</varname> dependencies to this unit for
- their service unit, which is implicitly done when
- <varname>DefaultDependencies=yes</varname> is set (the
- default).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>sigpwr.target</filename></term>
- <listitem>
- <para>A special target that is started when systemd receives
- the SIGPWR process signal, which is normally sent by the
- kernel or UPS daemons when power fails.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>sleep.target</filename></term>
- <listitem>
- <para>A special target unit that is pulled in by
- <filename>suspend.target</filename>,
- <filename>hibernate.target</filename> and
- <filename>hybrid-sleep.target</filename> and may be used to
- hook units into the sleep state logic.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>slices.target</filename></term>
- <listitem>
- <para>A special target unit that sets up all slice units (see
- <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
- details) that shall be active after boot. By default the generic <filename>system.slice</filename>
- slice unit, as well as the root slice unit <filename>-.slice</filename>, is pulled in and ordered before
- this unit (see below).</para>
-
- <para>It's a good idea to add <varname>WantedBy=slices.target</varname> lines to the <literal>[Install]</literal>
- section of all slices units that may be installed dynamically.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>sockets.target</filename></term>
- <listitem>
- <para>A special target unit that sets up all socket
- units (see
- <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details) that shall be active after boot.</para>
-
- <para>Services that can be socket-activated shall add
- <varname>Wants=</varname> dependencies to this unit for
- their socket unit during installation. This is best
- configured via a <varname>WantedBy=sockets.target</varname>
- in the socket unit's <literal>[Install]</literal>
- section.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>suspend.target</filename></term>
- <listitem>
- <para>A special target unit for suspending the system. This
- pulls in <filename>sleep.target</filename>.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>swap.target</filename></term>
- <listitem>
- <para>Similar to <filename>local-fs.target</filename>, but
- for swap partitions and swap files.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>sysinit.target</filename></term>
- <listitem>
- <para>systemd automatically adds dependencies of the types
- <varname>Requires=</varname> and <varname>After=</varname>
- for this target unit to all services (except for those with
- <varname>DefaultDependencies=no</varname>).</para>
-
- <para>This target pulls in the services required for system
- initialization. System services pulled in by this target should
- declare <varname>DefaultDependencies=no</varname> and specify
- all their dependencies manually, including access to anything
- more than a read only root filesystem. For details on the
- dependencies of this target, refer to
- <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>syslog.socket</filename></term>
- <listitem>
- <para>The socket unit syslog implementations should listen
- on. All userspace log messages will be made available on
- this socket. For more information about syslog integration,
- please consult the <ulink
- url="https://www.freedesktop.org/wiki/Software/systemd/syslog">Syslog
- Interface</ulink> document.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>system-update.target</filename></term>
- <term><filename>system-update-pre.target</filename></term>
- <term><filename>system-update-cleanup.service</filename></term>
- <listitem>
- <para>A special target unit that is used for offline system updates.
- <citerefentry><refentrytitle>systemd-system-update-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- will redirect the boot process to this target if <filename>/system-update</filename>
- exists. For more information see
- <citerefentry><refentrytitle>systemd.offline-updates</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
- </para>
-
- <para>Updates should happen before the <filename>system-update.target</filename> is reached, and the services
- which implement them should cause the machine to reboot. The main units executing the update should order
- themselves after <filename>system-update-pre.target</filename> but not pull it in. Services which want to run
- during system updates only, but before the actual system update is executed should order themselves before
- this unit and pull it in. As a safety measure, if this does not happen, and
- <filename>/system-update</filename> still exists after <filename>system-update.target</filename> is reached,
- <filename>system-update-cleanup.service</filename> will remove this symlink and reboot the machine.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>timers.target</filename></term>
- <listitem>
- <para>A special target unit that sets up all timer units
- (see
- <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details) that shall be active after boot.</para>
-
- <para>It is recommended that timer units installed by
- applications get pulled in via <varname>Wants=</varname>
- dependencies from this unit. This is best configured via
- <varname>WantedBy=timers.target</varname> in the timer
- unit's <literal>[Install]</literal> section.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>umount.target</filename></term>
- <listitem>
- <para>A special target unit that unmounts all mount and
- automount points on system shutdown.</para>
-
- <para>Mounts that shall be unmounted on system shutdown
- shall add Conflicts dependencies to this unit for their
- mount unit, which is implicitly done when
- <varname>DefaultDependencies=yes</varname> is set (the
- default).</para>
- </listitem>
- </varlistentry>
-
- </variablelist>
- </refsect1>
+ <title>Units managed by the system's service manager</title>
+
+ <refsect2>
+ <title>Special System Units</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><filename>-.mount</filename></term>
+ <listitem>
+ <para>The root mount point, i.e. the mount unit for the <filename>/</filename>
+ path. This unit is unconditionally active, during the entire time the system is up, as
+ this mount point is where the basic userspace is running from.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>basic.target</filename></term>
+ <listitem>
+ <para>A special target unit covering basic boot-up.</para>
+
+ <para>systemd automatically adds dependency of the type
+ <varname>After=</varname> for this target unit to all
+ services (except for those with
+ <varname>DefaultDependencies=no</varname>).</para>
+
+ <para>Usually, this should pull-in all local mount points plus
+ <filename>/var</filename>, <filename>/tmp</filename> and
+ <filename>/var/tmp</filename>, swap devices, sockets, timers,
+ path units and other basic initialization necessary for general
+ purpose daemons. The mentioned mount points are special cased
+ to allow them to be remote.
+ </para>
+
+ <para>This target usually does not pull in any non-target units
+ directly, but rather does so indirectly via other early boot targets.
+ It is instead meant as a synchronization point for late boot
+ services. Refer to
+ <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ for details on the targets involved.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>boot-complete.target</filename></term>
+ <listitem>
+ <para>This target is intended as generic synchronization point for services that shall determine or act on
+ whether the boot process completed successfully. Order units that are required to succeed for a boot process
+ to be considered successful before this unit, and add a <varname>Requires=</varname> dependency from the
+ target unit to them. Order units that shall only run when the boot process is considered successful after the
+ target unit and pull in the target from it, also with <varname>Requires=</varname>. Note that by default this
+ target unit is not part of the initial boot transaction, but is supposed to be pulled in only if required by
+ units that want to run only on successful boots.</para>
+
+ <para>See
+ <citerefentry><refentrytitle>systemd-boot-check-no-failures.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for a service that implements a generic system health check and orders itself before
+ <filename>boot-complete.target</filename>.</para>
+
+ <para>See
+ <citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for a service that propagates boot success information to the boot loader, and orders itself after
+ <filename>boot-complete.target</filename>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>ctrl-alt-del.target</filename></term>
+ <listitem>
+ <para>systemd starts this target whenever Control+Alt+Del is
+ pressed on the console. Usually, this should be aliased
+ (symlinked) to <filename>reboot.target</filename>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>cryptsetup.target</filename></term>
+ <listitem>
+ <para>A target that pulls in setup services for all
+ encrypted block devices.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>dbus.service</filename></term>
+ <listitem>
+ <para>A special unit for the D-Bus bus daemon. As soon as
+ this service is fully started up systemd will connect to it
+ and register its service.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>dbus.socket</filename></term>
+ <listitem>
+ <para>A special unit for the D-Bus system bus socket. All
+ units with <varname>Type=dbus</varname> automatically gain a
+ dependency on this unit.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>default.target</filename></term>
+ <listitem>
+ <para>The default unit systemd starts at bootup. Usually,
+ this should be aliased (symlinked) to
+ <filename>multi-user.target</filename> or
+ <filename>graphical.target</filename>.</para>
+
+ <para>The default unit systemd starts at bootup can be
+ overridden with the <varname>systemd.unit=</varname> kernel
+ command line option.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>display-manager.service</filename></term>
+ <listitem>
+ <para>The display manager service. Usually, this should be
+ aliased (symlinked) to <filename>gdm.service</filename> or a
+ similar display manager service.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>emergency.target</filename></term>
+ <listitem>
+ <para>A special target unit that starts an emergency shell on the main console. This
+ target does not pull in any services or mounts. It is the most minimal version of
+ starting the system in order to acquire an interactive shell; the only processes running
+ are usually just the system manager (PID 1) and the shell process. This unit is supposed
+ to be used with the kernel command line option <varname>systemd.unit=</varname>; it is
+ also used when a file system check on a required file system fails, and boot-up cannot
+ continue. Compare with <filename>rescue.target</filename>, which serves a similar
+ purpose, but also starts the most basic services and mounts all file systems.</para>
+
+ <para>Use the <literal>systemd.unit=emergency.target</literal> kernel command line
+ option to boot into this mode. A short alias for this kernel command line option is
+ <literal>emergency</literal>, for compatibility with SysV.</para>
+
+ <para>In many ways booting into <filename>emergency.target</filename> is similar to the
+ effect of booting with <literal>init=/bin/sh</literal> on the kernel command line,
+ except that emergency mode provides you with the full system and service manager, and
+ allows starting individual units in order to continue the boot process in steps.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>exit.target</filename></term>
+ <listitem>
+ <para>A special service unit for shutting down the system or
+ user service manager. It is equivalent to
+ <filename>poweroff.target</filename> on non-container
+ systems, and also works in containers.</para>
+
+ <para>systemd will start this unit when it receives the
+ <constant>SIGTERM</constant> or <constant>SIGINT</constant>
+ signal when running as user service daemon.</para>
+
+ <para>Normally, this (indirectly) pulls in
+ <filename>shutdown.target</filename>, which in turn should be
+ conflicted by all units that want to be scheduled for
+ shutdown when the service manager starts to exit.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>final.target</filename></term>
+ <listitem>
+ <para>A special target unit that is used during the shutdown
+ logic and may be used to pull in late services after all
+ normal services are already terminated and all mounts
+ unmounted.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>getty.target</filename></term>
+ <listitem>
+ <para>A special target unit that pulls in statically
+ configured local TTY <filename>getty</filename> instances.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>graphical.target</filename></term>
+ <listitem>
+ <para>A special target unit for setting up a graphical login
+ screen. This pulls in
+ <filename>multi-user.target</filename>.</para>
+
+ <para>Units that are needed for graphical logins shall add
+ <varname>Wants=</varname> dependencies for their unit to
+ this unit (or <filename>multi-user.target</filename>) during
+ installation. This is best configured via
+ <varname>WantedBy=graphical.target</varname> in the unit's
+ <literal>[Install]</literal> section.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>hibernate.target</filename></term>
+ <listitem>
+ <para>A special target unit for hibernating the system. This
+ pulls in <filename>sleep.target</filename>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>hybrid-sleep.target</filename></term>
+ <listitem>
+ <para>A special target unit for hibernating and suspending
+ the system at the same time. This pulls in
+ <filename>sleep.target</filename>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>suspend-then-hibernate.target</filename></term>
+ <listitem>
+ <para>A special target unit for suspending the system for a period
+ of time, waking it and putting it into hibernate. This pulls in
+ <filename>sleep.target</filename>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>halt.target</filename></term>
+ <listitem>
+ <para>A special target unit for shutting down and halting
+ the system. Note that this target is distinct from
+ <filename>poweroff.target</filename> in that it generally
+ really just halts the system rather than powering it
+ down.</para>
+
+ <para>Applications wanting to halt the system should not start this unit
+ directly, but should instead execute <command>systemctl halt</command>
+ (possibly with the <option>--no-block</option> option) or call
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <command>org.freedesktop.systemd1.Manager.Halt</command> D-Bus method
+ directly.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>init.scope</filename></term>
+ <listitem>
+ <para>This scope unit is where the system and service manager (PID 1) itself resides. It
+ is active as long as the system is running.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>initrd-fs.target</filename></term>
+ <listitem>
+ <para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ automatically adds dependencies of type
+ <varname>Before=</varname> to
+ <filename>sysroot-usr.mount</filename> and all mount points
+ found in <filename>/etc/fstab</filename> that have
+ <option>x-initrd.mount</option> and not have
+ <option>noauto</option> mount options set.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>initrd-root-device.target</filename></term>
+ <listitem>
+ <para>A special initrd target unit that is reached when the root filesystem device is available, but before
+ it has been mounted.
+ <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ and
+ <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ automatically setup the appropriate dependencies to make this happen.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>initrd-root-fs.target</filename></term>
+ <listitem>
+ <para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ automatically adds dependencies of type
+ <varname>Before=</varname> to the
+ <filename>sysroot.mount</filename> unit, which is generated
+ from the kernel command line.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>kbrequest.target</filename></term>
+ <listitem>
+ <para>systemd starts this target whenever Alt+ArrowUp is
+ pressed on the console. Note that any user with physical access
+ to the machine will be able to do this, without authentication,
+ so this should be used carefully.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>kexec.target</filename></term>
+ <listitem>
+ <para>A special target unit for shutting down and rebooting
+ the system via kexec.</para>
+
+ <para>Applications wanting to reboot the system should not start this unit
+ directly, but should instead execute <command>systemctl kexec</command>
+ (possibly with the <option>--no-block</option> option) or call
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <command>org.freedesktop.systemd1.Manager.KExec</command> D-Bus method
+ directly.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>local-fs.target</filename></term>
+ <listitem>
+ <para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ automatically adds dependencies of type
+ <varname>Before=</varname> to all mount units that refer to
+ local mount points for this target unit. In addition, it
+ adds dependencies of type <varname>Wants=</varname> to this
+ target unit for those mounts listed in
+ <filename>/etc/fstab</filename> that have the
+ <option>auto</option> mount option set.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>machines.target</filename></term>
+ <listitem>
+ <para>A standard target unit for starting all the containers
+ and other virtual machines. See <filename>systemd-nspawn@.service</filename>
+ for an example.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>multi-user.target</filename></term>
+ <listitem>
+ <para>A special target unit for setting up a multi-user
+ system (non-graphical). This is pulled in by
+ <filename>graphical.target</filename>.</para>
+
+ <para>Units that are needed for a multi-user system shall
+ add <varname>Wants=</varname> dependencies for their unit to
+ this unit during installation. This is best configured via
+ <varname>WantedBy=multi-user.target</varname> in the unit's
+ <literal>[Install]</literal> section.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>network-online.target</filename></term>
+ <listitem>
+ <para>Units that strictly require a configured network
+ connection should pull in
+ <filename>network-online.target</filename> (via a
+ <varname>Wants=</varname> type dependency) and order
+ themselves after it. This target unit is intended to pull in
+ a service that delays further execution until the network is
+ sufficiently set up. What precisely this requires is left to
+ the implementation of the network managing service.</para>
+
+ <para>Note the distinction between this unit and
+ <filename>network.target</filename>. This unit is an active
+ unit (i.e. pulled in by the consumer rather than the
+ provider of this functionality) and pulls in a service which
+ possibly adds substantial delays to further execution. In
+ contrast, <filename>network.target</filename> is a passive
+ unit (i.e. pulled in by the provider of the functionality,
+ rather than the consumer) that usually does not delay
+ execution much. Usually, <filename>network.target</filename>
+ is part of the boot of most systems, while
+ <filename>network-online.target</filename> is not, except
+ when at least one unit requires it. Also see <ulink
+ url="https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget">Running
+ Services After the Network is up</ulink> for more
+ information.</para>
+
+ <para>All mount units for remote network file systems
+ automatically pull in this unit, and order themselves after
+ it. Note that networking daemons that simply provide
+ functionality to other hosts generally do not need to pull
+ this in.</para>
+
+ <para>systemd automatically adds dependencies of type <varname>Wants=</varname> and
+ <varname>After=</varname> for this target unit to all SysV init script service units
+ with an LSB header referring to the <literal>$network</literal> facility.</para>
+
+ <para>Note that this unit is only useful during the original system start-up
+ logic. After the system has completed booting up, it will not track the online state of
+ the system anymore. Due to this it cannot be used as a network connection monitor
+ concept, it is purely a one-time system start-up concept.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>paths.target</filename></term>
+ <listitem>
+ <para>A special target unit that sets up all path units (see
+ <citerefentry><refentrytitle>systemd.path</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details) that shall be active after boot.</para>
+
+ <para>It is recommended that path units installed by
+ applications get pulled in via <varname>Wants=</varname>
+ dependencies from this unit. This is best configured via a
+ <varname>WantedBy=paths.target</varname> in the path unit's
+ <literal>[Install]</literal> section.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>poweroff.target</filename></term>
+ <listitem>
+ <para>A special target unit for shutting down and powering
+ off the system.</para>
+
+ <para>Applications wanting to power off the system should not start this unit
+ directly, but should instead execute <command>systemctl poweroff</command>
+ (possibly with the <option>--no-block</option> option) or call
+ <citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
+ <command>org.freedesktop.login1.Manager.PowerOff</command> D-Bus method
+ directly.</para>
+
+ <para><filename>runlevel0.target</filename> is an alias for
+ this target unit, for compatibility with SysV.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>reboot.target</filename></term>
+ <listitem>
+ <para>A special target unit for shutting down and rebooting
+ the system.</para>
+
+ <para>Applications wanting to reboot the system should not start this unit
+ directly, but should instead execute <command>systemctl reboot</command>
+ (possibly with the <option>--no-block</option> option) or call
+ <citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
+ <command>org.freedesktop.login1.Manager.Reboot</command> D-Bus method
+ directly.</para>
+
+ <para><filename>runlevel6.target</filename> is an alias for
+ this target unit, for compatibility with SysV.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>remote-cryptsetup.target</filename></term>
+ <listitem>
+ <para>Similar to <filename>cryptsetup.target</filename>, but for encrypted
+ devices which are accessed over the network. It is used for
+ <citerefentry><refentrytitle>crypttab</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ entries marked with <option>_netdev</option>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>remote-fs.target</filename></term>
+ <listitem>
+ <para>Similar to <filename>local-fs.target</filename>, but
+ for remote mount points.</para>
+
+ <para>systemd automatically adds dependencies of type
+ <varname>After=</varname> for this target unit to all SysV
+ init script service units with an LSB header referring to
+ the <literal>$remote_fs</literal> facility.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>rescue.target</filename></term>
+ <listitem>
+ <para>A special target unit that pulls in the base system (including system mounts) and
+ spawns a rescue shell. Isolate to this target in order to administer the system in
+ single-user mode with all file systems mounted but with no services running, except for
+ the most basic. Compare with <filename>emergency.target</filename>, which is much more
+ reduced and does not provide the file systems or most basic services. Compare with
+ <filename>multi-user.target</filename>, this target could be seen as
+ <filename>single-user.target</filename>.</para>
+
+ <para><filename>runlevel1.target</filename> is an alias for this target unit, for
+ compatibility with SysV.</para>
+
+ <para>Use the <literal>systemd.unit=rescue.target</literal> kernel command line option
+ to boot into this mode. A short alias for this kernel command line option is
+ <literal>1</literal>, for compatibility with SysV.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>runlevel2.target</filename></term>
+ <term><filename>runlevel3.target</filename></term>
+ <term><filename>runlevel4.target</filename></term>
+ <term><filename>runlevel5.target</filename></term>
+ <listitem>
+ <para>These are targets that are called whenever the SysV
+ compatibility code asks for runlevel 2, 3, 4, 5,
+ respectively. It is a good idea to make this an alias for
+ (i.e. symlink to) <filename>graphical.target</filename>
+ (for runlevel 5) or <filename>multi-user.target</filename>
+ (the others).</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>shutdown.target</filename></term>
+ <listitem>
+ <para>A special target unit that terminates the services on
+ system shutdown.</para>
+
+ <para>Services that shall be terminated on system shutdown
+ shall add <varname>Conflicts=</varname> and
+ <varname>Before=</varname> dependencies to this unit for
+ their service unit, which is implicitly done when
+ <varname>DefaultDependencies=yes</varname> is set (the
+ default).</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>sigpwr.target</filename></term>
+ <listitem>
+ <para>A special target that is started when systemd receives
+ the SIGPWR process signal, which is normally sent by the
+ kernel or UPS daemons when power fails.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>sleep.target</filename></term>
+ <listitem>
+ <para>A special target unit that is pulled in by
+ <filename>suspend.target</filename>,
+ <filename>hibernate.target</filename> and
+ <filename>hybrid-sleep.target</filename> and may be used to
+ hook units into the sleep state logic.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>slices.target</filename></term>
+ <listitem>
+ <para>A special target unit that sets up all slice units (see
+ <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details) that shall always be active after boot. By default the generic
+ <filename>system.slice</filename> slice unit as well as the root slice unit
+ <filename>-.slice</filename> are pulled in and ordered before this unit (see
+ below).</para>
+
+ <para>Adding slice units to <filename>slices.target</filename> is generally not
+ necessary. Instead, when some unit that uses <varname>Slice=</varname> is started, the
+ specified slice will be started automatically. Adding
+ <varname>WantedBy=slices.target</varname> lines to the <literal>[Install]</literal>
+ section should only be done for units that need to be always active. In that case care
+ needs to be taken to avoid creating a loop through the automatic dependencies on
+ "parent" slices.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>sockets.target</filename></term>
+ <listitem>
+ <para>A special target unit that sets up all socket
+ units (see
+ <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details) that shall be active after boot.</para>
+
+ <para>Services that can be socket-activated shall add
+ <varname>Wants=</varname> dependencies to this unit for
+ their socket unit during installation. This is best
+ configured via a <varname>WantedBy=sockets.target</varname>
+ in the socket unit's <literal>[Install]</literal>
+ section.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>suspend.target</filename></term>
+ <listitem>
+ <para>A special target unit for suspending the system. This
+ pulls in <filename>sleep.target</filename>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>swap.target</filename></term>
+ <listitem>
+ <para>Similar to <filename>local-fs.target</filename>, but
+ for swap partitions and swap files.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>sysinit.target</filename></term>
+ <listitem>
+ <para>systemd automatically adds dependencies of the types
+ <varname>Requires=</varname> and <varname>After=</varname>
+ for this target unit to all services (except for those with
+ <varname>DefaultDependencies=no</varname>).</para>
+
+ <para>This target pulls in the services required for system
+ initialization. System services pulled in by this target should
+ declare <varname>DefaultDependencies=no</varname> and specify
+ all their dependencies manually, including access to anything
+ more than a read only root filesystem. For details on the
+ dependencies of this target, refer to
+ <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>syslog.socket</filename></term>
+ <listitem>
+ <para>The socket unit syslog implementations should listen
+ on. All userspace log messages will be made available on
+ this socket. For more information about syslog integration,
+ please consult the <ulink
+ url="https://www.freedesktop.org/wiki/Software/systemd/syslog">Syslog
+ Interface</ulink> document.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>system-update.target</filename></term>
+ <term><filename>system-update-pre.target</filename></term>
+ <term><filename>system-update-cleanup.service</filename></term>
+ <listitem>
+ <para>A special target unit that is used for offline system updates.
+ <citerefentry><refentrytitle>systemd-system-update-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ will redirect the boot process to this target if <filename>/system-update</filename>
+ exists. For more information see
+ <citerefentry><refentrytitle>systemd.offline-updates</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+ </para>
+
+ <para>Updates should happen before the <filename>system-update.target</filename> is
+ reached, and the services which implement them should cause the machine to reboot. The
+ main units executing the update should order themselves after
+ <filename>system-update-pre.target</filename> but not pull it in. Services which want to
+ run during system updates only, but before the actual system update is executed should
+ order themselves before this unit and pull it in. As a safety measure, if this does not
+ happen, and <filename>/system-update</filename> still exists after
+ <filename>system-update.target</filename> is reached,
+ <filename>system-update-cleanup.service</filename> will remove this symlink and reboot
+ the machine.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>timers.target</filename></term>
+ <listitem>
+ <para>A special target unit that sets up all timer units
+ (see
+ <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details) that shall be active after boot.</para>
+
+ <para>It is recommended that timer units installed by
+ applications get pulled in via <varname>Wants=</varname>
+ dependencies from this unit. This is best configured via
+ <varname>WantedBy=timers.target</varname> in the timer
+ unit's <literal>[Install]</literal> section.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>umount.target</filename></term>
+ <listitem>
+ <para>A special target unit that unmounts all mount and
+ automount points on system shutdown.</para>
+
+ <para>Mounts that shall be unmounted on system shutdown
+ shall add Conflicts dependencies to this unit for their
+ mount unit, which is implicitly done when
+ <varname>DefaultDependencies=yes</varname> is set (the
+ default).</para>
+ </listitem>
+ </varlistentry>
- <refsect1>
- <title>Special System Units for Devices</title>
-
- <para>Some target units are automatically pulled in as devices of
- certain kinds show up in the system. These may be used to
- automatically activate various services based on the specific type
- of the available hardware.</para>
-
- <variablelist>
- <varlistentry>
- <term><filename>bluetooth.target</filename></term>
- <listitem>
- <para>This target is started automatically as soon as a
- Bluetooth controller is plugged in or becomes available at
- boot.</para>
-
- <para>This may be used to pull in Bluetooth management
- daemons dynamically when Bluetooth hardware is found.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>printer.target</filename></term>
- <listitem>
- <para>This target is started automatically as soon as a
- printer is plugged in or becomes available at boot.</para>
-
- <para>This may be used to pull in printer management daemons
- dynamically when printer hardware is found.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>smartcard.target</filename></term>
- <listitem>
- <para>This target is started automatically as soon as a
- smartcard controller is plugged in or becomes available at
- boot.</para>
-
- <para>This may be used to pull in smartcard management
- daemons dynamically when smartcard hardware is found.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>sound.target</filename></term>
- <listitem>
- <para>This target is started automatically as soon as a
- sound card is plugged in or becomes available at
- boot.</para>
-
- <para>This may be used to pull in audio management daemons
- dynamically when audio hardware is found.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
+ </variablelist>
+ </refsect2>
- <refsect1>
- <title>Special Passive System Units </title>
-
- <para>A number of special system targets are defined that can be
- used to properly order boot-up of optional services. These targets
- are generally not part of the initial boot transaction, unless
- they are explicitly pulled in by one of the implementing services.
- Note specifically that these <emphasis>passive</emphasis> target
- units are generally not pulled in by the consumer of a service,
- but by the provider of the service. This means: a consuming
- service should order itself after these targets (as appropriate),
- but not pull it in. A providing service should order itself before
- these targets (as appropriate) and pull it in (via a
- <varname>Wants=</varname> type dependency).</para>
-
- <para>Note that these passive units cannot be started manually,
- i.e. <literal>systemctl start time-sync.target</literal> will fail
- with an error. They can only be pulled in by dependency. This is
- enforced since they exist for ordering purposes only and thus are
- not useful as only unit within a transaction.</para>
-
- <variablelist>
- <varlistentry>
- <term><filename>cryptsetup-pre.target</filename></term>
- <listitem>
- <para>This passive target unit may be pulled in by services
- that want to run before any encrypted block device is set
- up. All encrypted block devices are set up after this target
- has been reached. Since the shutdown order is implicitly the
- reverse start-up order between units, this target is
- particularly useful to ensure that a service is shut down
- only after all encrypted block devices are fully
- stopped.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>getty-pre.target</filename></term>
- <listitem>
- <para>A special passive target unit. Users of this target
- are expected to pull it in the boot transaction via
- a dependency (e.g. <varname>Wants=</varname>). Order your
- unit before this unit if you want to make use of the console
- just before <filename>getty</filename> is started.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>local-fs-pre.target</filename></term>
- <listitem>
- <para>This target unit is
- automatically ordered before
- all local mount points marked
- with <option>auto</option>
- (see above). It can be used to
- execute certain units before
- all local mounts.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>network.target</filename></term>
- <listitem>
- <para>This unit is supposed to indicate when network
- functionality is available, but it is only very weakly
- defined what that is supposed to mean, with one exception:
- at shutdown, a unit that is ordered after
- <filename>network.target</filename> will be stopped before
- the network — to whatever level it might be set up then —
- is shut down. It is hence useful when writing service files
- that require network access on shutdown, which should order
- themselves after this target, but not pull it in. Also see
- <ulink url="https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget">Running
- Services After the Network is up</ulink> for more
- information. Also see
- <filename>network-online.target</filename> described
- above.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>network-pre.target</filename></term>
- <listitem>
- <para>This passive target unit may be pulled in by services
- that want to run before any network is set up, for example
- for the purpose of setting up a firewall. All network
- management software orders itself after this target, but
- does not pull it in.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>nss-lookup.target</filename></term>
- <listitem>
- <para>A target that should be used as synchronization point for all host/network name service lookups. Note
- that this is independent of UNIX user/group name lookups for which <filename>nss-user-lookup.target</filename>
- should be used. All services for which the availability of full host/network name resolution is essential
- should be ordered after this target, but not pull it in. systemd automatically adds dependencies of type
- <varname>After=</varname> for this target unit to all SysV init script service units with an LSB header
- referring to the <literal>$named</literal> facility.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>nss-user-lookup.target</filename></term>
- <listitem>
- <para>A target that should be used as synchronization point for all regular UNIX user/group name service
- lookups. Note that this is independent of host/network name lookups for which
- <filename>nss-lookup.target</filename> should be used. All services for which the availability of the full
- user/group database is essential should be ordered after this target, but not pull it in. All services which
- provide parts of the user/group database should be ordered before this target, and pull it in. Note that this
- unit is only relevant for regular users and groups — system users and groups are required to be resolvable
- during earliest boot already, and hence do not need any special ordering against this target.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>remote-fs-pre.target</filename></term>
- <listitem>
- <para>This target unit is automatically ordered before all
- mount point units (see above) and cryptsetup devices
- marked with the <option>_netdev</option>. It can be used to run
- certain units before remote encrypted devices and mounts are established.
- Note that this unit is generally not part of the initial
- transaction, unless the unit that wants to be ordered before
- all remote mounts pulls it in via a
- <varname>Wants=</varname> type dependency. If the unit wants
- to be pulled in by the first remote mount showing up, it
- should use <filename>network-online.target</filename> (see
- above).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>rpcbind.target</filename></term>
- <listitem>
- <para>The portmapper/rpcbind pulls in this target and orders
- itself before it, to indicate its availability. systemd
- automatically adds dependencies of type
- <varname>After=</varname> for this target unit to all SysV
- init script service units with an LSB header referring to
- the <literal>$portmap</literal> facility.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><filename>time-sync.target</filename></term>
- <listitem>
- <para>Services responsible for synchronizing the system
- clock from a remote source (such as NTP client
- implementations) should pull in this target and order
- themselves before it. All services where correct time is
- essential should be ordered after this unit, but not pull it
- in. systemd automatically adds dependencies of type
- <varname>After=</varname> for this target unit to all SysV
- init script service units with an LSB header referring to
- the <literal>$time</literal> facility. </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
+ <refsect2>
+ <title>Special System Units for Devices</title>
- <refsect1>
- <title>Special User Units</title>
+ <para>Some target units are automatically pulled in as devices of
+ certain kinds show up in the system. These may be used to
+ automatically activate various services based on the specific type
+ of the available hardware.</para>
- <para>When systemd runs as a user instance, the following special
- units are available, which have similar definitions as their
- system counterparts:
- <filename>exit.target</filename>,
- <filename>default.target</filename>,
- <filename>shutdown.target</filename>,
- <filename>sockets.target</filename>,
- <filename>timers.target</filename>,
- <filename>paths.target</filename>,
- <filename>bluetooth.target</filename>,
- <filename>printer.target</filename>,
- <filename>smartcard.target</filename>,
- <filename>sound.target</filename>.</para>
- </refsect1>
+ <variablelist>
+ <varlistentry>
+ <term><filename>bluetooth.target</filename></term>
+ <listitem>
+ <para>This target is started automatically as soon as a
+ Bluetooth controller is plugged in or becomes available at
+ boot.</para>
- <refsect1>
- <title>Special Passive User Units</title>
-
- <variablelist>
- <varlistentry>
- <term><filename>graphical-session.target</filename></term>
- <listitem>
- <para>This target is active whenever any graphical session is running. It is used to stop user services which
- only apply to a graphical (X, Wayland, etc.) session when the session is terminated. Such services should
- have <literal>PartOf=graphical-session.target</literal> in their <literal>[Unit]</literal> section. A target
- for a particular session (e. g. <filename>gnome-session.target</filename>) starts and stops
- <literal>graphical-session.target</literal> with <literal>BindsTo=graphical-session.target</literal>.</para>
-
- <para>Which services are started by a session target is determined by the <literal>Wants=</literal> and
- <literal>Requires=</literal> dependencies. For services that can be enabled independently, symlinks in
- <literal>.wants/</literal> and <literal>.requires/</literal> should be used, see
- <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Those
- symlinks should either be shipped in packages, or should be added dynamically after installation, for example
- using <literal>systemctl add-wants</literal>, see
- <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
- </para>
-
- <example>
- <title>Nautilus as part of a GNOME session</title>
-
- <para><literal>gnome-session.target</literal> pulls in Nautilus as top-level service:</para>
-
- <programlisting>[Unit]
-Description=User systemd services for GNOME graphical session
-Wants=nautilus.service
-BindsTo=graphical-session.target</programlisting>
-
- <para><literal>nautilus.service</literal> gets stopped when the session stops:</para>
-
- <programlisting>[Unit]
-Description=Render the desktop icons with Nautilus
-PartOf=graphical-session.target
-
-[Service]
-…</programlisting>
- </example>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><filename>graphical-session-pre.target</filename></term>
- <listitem>
- <para>This target contains services which set up the environment or global configuration of a graphical
- session, such as SSH/GPG agents (which need to export an environment variable into all desktop processes) or
- migration of obsolete d-conf keys after an OS upgrade (which needs to happen before starting any process that
- might use them). This target must be started before starting a graphical session like
- <filename>gnome-session.target</filename>.</para>
- </listitem>
- </varlistentry>
- </variablelist>
+ <para>This may be used to pull in Bluetooth management
+ daemons dynamically when Bluetooth hardware is found.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>printer.target</filename></term>
+ <listitem>
+ <para>This target is started automatically as soon as a
+ printer is plugged in or becomes available at boot.</para>
+
+ <para>This may be used to pull in printer management daemons
+ dynamically when printer hardware is found.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>smartcard.target</filename></term>
+ <listitem>
+ <para>This target is started automatically as soon as a
+ smartcard controller is plugged in or becomes available at
+ boot.</para>
+
+ <para>This may be used to pull in smartcard management
+ daemons dynamically when smartcard hardware is found.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>sound.target</filename></term>
+ <listitem>
+ <para>This target is started automatically as soon as a
+ sound card is plugged in or becomes available at
+ boot.</para>
+
+ <para>This may be used to pull in audio management daemons
+ dynamically when audio hardware is found.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+
+ <refsect2>
+ <title>Special Passive System Units </title>
+
+ <para>A number of special system targets are defined that can be
+ used to properly order boot-up of optional services. These targets
+ are generally not part of the initial boot transaction, unless
+ they are explicitly pulled in by one of the implementing services.
+ Note specifically that these <emphasis>passive</emphasis> target
+ units are generally not pulled in by the consumer of a service,
+ but by the provider of the service. This means: a consuming
+ service should order itself after these targets (as appropriate),
+ but not pull it in. A providing service should order itself before
+ these targets (as appropriate) and pull it in (via a
+ <varname>Wants=</varname> type dependency).</para>
+
+ <para>Note that these passive units cannot be started manually,
+ i.e. <literal>systemctl start time-sync.target</literal> will fail
+ with an error. They can only be pulled in by dependency. This is
+ enforced since they exist for ordering purposes only and thus are
+ not useful as only unit within a transaction.</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><filename>cryptsetup-pre.target</filename></term>
+ <listitem>
+ <para>This passive target unit may be pulled in by services
+ that want to run before any encrypted block device is set
+ up. All encrypted block devices are set up after this target
+ has been reached. Since the shutdown order is implicitly the
+ reverse start-up order between units, this target is
+ particularly useful to ensure that a service is shut down
+ only after all encrypted block devices are fully
+ stopped.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>getty-pre.target</filename></term>
+ <listitem>
+ <para>A special passive target unit. Users of this target
+ are expected to pull it in the boot transaction via
+ a dependency (e.g. <varname>Wants=</varname>). Order your
+ unit before this unit if you want to make use of the console
+ just before <filename>getty</filename> is started.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>local-fs-pre.target</filename></term>
+ <listitem>
+ <para>This target unit is
+ automatically ordered before
+ all local mount points marked
+ with <option>auto</option>
+ (see above). It can be used to
+ execute certain units before
+ all local mounts.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>network.target</filename></term>
+ <listitem>
+ <para>This unit is supposed to indicate when network
+ functionality is available, but it is only very weakly
+ defined what that is supposed to mean, with one exception:
+ at shutdown, a unit that is ordered after
+ <filename>network.target</filename> will be stopped before
+ the network — to whatever level it might be set up then —
+ is shut down. It is hence useful when writing service files
+ that require network access on shutdown, which should order
+ themselves after this target, but not pull it in. Also see
+ <ulink url="https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget">Running
+ Services After the Network is up</ulink> for more
+ information. Also see
+ <filename>network-online.target</filename> described
+ above.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>network-pre.target</filename></term>
+ <listitem>
+ <para>This passive target unit may be pulled in by services
+ that want to run before any network is set up, for example
+ for the purpose of setting up a firewall. All network
+ management software orders itself after this target, but
+ does not pull it in.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>nss-lookup.target</filename></term>
+ <listitem>
+ <para>A target that should be used as synchronization point for all host/network name
+ service lookups. Note that this is independent of UNIX user/group name lookups for which
+ <filename>nss-user-lookup.target</filename> should be used. All services for which the
+ availability of full host/network name resolution is essential should be ordered after
+ this target, but not pull it in. systemd automatically adds dependencies of type
+ <varname>After=</varname> for this target unit to all SysV init script service units
+ with an LSB header referring to the <literal>$named</literal> facility.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>nss-user-lookup.target</filename></term>
+ <listitem>
+ <para>A target that should be used as synchronization point for all regular UNIX
+ user/group name service lookups. Note that this is independent of host/network name
+ lookups for which <filename>nss-lookup.target</filename> should be used. All services
+ for which the availability of the full user/group database is essential should be
+ ordered after this target, but not pull it in. All services which provide parts of the
+ user/group database should be ordered before this target, and pull it in. Note that this
+ unit is only relevant for regular users and groups — system users and groups are
+ required to be resolvable during earliest boot already, and hence do not need any
+ special ordering against this target.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>remote-fs-pre.target</filename></term>
+ <listitem>
+ <para>This target unit is automatically ordered before all
+ mount point units (see above) and cryptsetup devices
+ marked with the <option>_netdev</option>. It can be used to run
+ certain units before remote encrypted devices and mounts are established.
+ Note that this unit is generally not part of the initial
+ transaction, unless the unit that wants to be ordered before
+ all remote mounts pulls it in via a
+ <varname>Wants=</varname> type dependency. If the unit wants
+ to be pulled in by the first remote mount showing up, it
+ should use <filename>network-online.target</filename> (see
+ above).</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>rpcbind.target</filename></term>
+ <listitem>
+ <para>The portmapper/rpcbind pulls in this target and orders
+ itself before it, to indicate its availability. systemd
+ automatically adds dependencies of type
+ <varname>After=</varname> for this target unit to all SysV
+ init script service units with an LSB header referring to
+ the <literal>$portmap</literal> facility.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>time-sync.target</filename></term>
+ <listitem>
+ <para>Services responsible for synchronizing the system
+ clock from a remote source (such as NTP client
+ implementations) should pull in this target and order
+ themselves before it. All services where correct time is
+ essential should be ordered after this unit, but not pull it
+ in. systemd automatically adds dependencies of type
+ <varname>After=</varname> for this target unit to all SysV
+ init script service units with an LSB header referring to
+ the <literal>$time</literal> facility. </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+
+ <refsect2>
+ <title>Special Slice Units</title>
+
+ <para>There are four <literal>.slice</literal> units which form the basis of the hierarchy for
+ assignment of resources for services, users, and virtual machines or containers. See
+ <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ for details about slice units.</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><filename>-.slice</filename></term>
+ <listitem>
+ <para>The root slice is the root of the slice hierarchy. It usually does not contain
+ units directly, but may be used to set defaults for the whole tree.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>system.slice</filename></term>
+ <listitem>
+ <para>By default, all system services started by
+ <command>systemd</command> are found in this slice.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>user.slice</filename></term>
+ <listitem>
+ <para>By default, all user processes and services started on
+ behalf of the user, including the per-user systemd instance
+ are found in this slice. This is pulled in by
+ <filename>systemd-logind.service</filename></para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>machine.slice</filename></term>
+ <listitem>
+ <para>By default, all virtual machines and containers
+ registered with <command>systemd-machined</command> are
+ found in this slice. This is pulled in by
+ <filename>systemd-machined.service</filename></para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
</refsect1>
<refsect1>
- <title>Special Slice Units</title>
-
- <para>There are four <literal>.slice</literal> units which form the basis of the hierarchy for assignment of
- resources for services, users, and virtual machines or containers. See
- <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details about slice
- units.</para>
-
- <variablelist>
- <varlistentry>
- <term><filename>-.slice</filename></term>
- <listitem>
- <para>The root slice is the root of the slice hierarchy. It usually does not contain units directly, but may
- be used to set defaults for the whole tree.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><filename>system.slice</filename></term>
- <listitem>
- <para>By default, all system services started by
- <command>systemd</command> are found in this slice.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><filename>user.slice</filename></term>
- <listitem>
- <para>By default, all user processes and services started on
- behalf of the user, including the per-user systemd instance
- are found in this slice. This is pulled in by
- <filename>systemd-logind.service</filename></para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><filename>machine.slice</filename></term>
- <listitem>
- <para>By default, all virtual machines and containers
- registered with <command>systemd-machined</command> are
- found in this slice. This is pulled in by
- <filename>systemd-machined.service</filename></para>
- </listitem>
- </varlistentry>
- </variablelist>
+ <title>Units managed by the user's service manager</title>
+
+ <refsect2>
+ <title>Special User Units</title>
+
+ <para>When systemd runs as a user instance, the following special
+ units are available, which have similar definitions as their
+ system counterparts:
+ <filename>exit.target</filename>,
+ <filename>default.target</filename>,
+ <filename>shutdown.target</filename>,
+ <filename>sockets.target</filename>,
+ <filename>timers.target</filename>,
+ <filename>paths.target</filename>,
+ <filename>bluetooth.target</filename>,
+ <filename>printer.target</filename>,
+ <filename>smartcard.target</filename>,
+ <filename>sound.target</filename>.</para>
+ </refsect2>
+
+ <refsect2>
+ <title>Special Passive User Units</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><filename>graphical-session.target</filename></term>
+ <listitem>
+ <para>This target is active whenever any graphical session is running. It is used to
+ stop user services which only apply to a graphical (X, Wayland, etc.) session when the
+ session is terminated. Such services should have
+ <literal>PartOf=graphical-session.target</literal> in their <literal>[Unit]</literal>
+ section. A target for a particular session (e. g.
+ <filename>gnome-session.target</filename>) starts and stops
+ <literal>graphical-session.target</literal> with
+ <literal>BindsTo=graphical-session.target</literal>.</para>
+
+ <para>Which services are started by a session target is determined by the
+ <literal>Wants=</literal> and <literal>Requires=</literal> dependencies. For services
+ that can be enabled independently, symlinks in <literal>.wants/</literal> and
+ <literal>.requires/</literal> should be used, see
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ Those symlinks should either be shipped in packages, or should be added dynamically
+ after installation, for example using <literal>systemctl add-wants</literal>, see
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+ </para>
+
+ <example>
+ <title>Nautilus as part of a GNOME session</title>
+
+ <para><literal>gnome-session.target</literal> pulls in Nautilus as top-level service:</para>
+
+ <programlisting>[Unit]
+ Description=User systemd services for GNOME graphical session
+ Wants=nautilus.service
+ BindsTo=graphical-session.target</programlisting>
+
+ <para><literal>nautilus.service</literal> gets stopped when the session stops:</para>
+
+ <programlisting>[Unit]
+ Description=Render the desktop icons with Nautilus
+ PartOf=graphical-session.target
+
+ [Service]
+ …</programlisting>
+ </example>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>graphical-session-pre.target</filename></term>
+ <listitem>
+ <para>This target contains services which set up the environment or global configuration
+ of a graphical session, such as SSH/GPG agents (which need to export an environment
+ variable into all desktop processes) or migration of obsolete d-conf keys after an OS
+ upgrade (which needs to happen before starting any process that might use them). This
+ target must be started before starting a graphical session like
+ <filename>gnome-session.target</filename>.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
</refsect1>
<refsect1>
@@ -1052,7 +1101,8 @@ PartOf=graphical-session.target
<citerefentry><refentrytitle>systemd.target</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/systemd.swap.xml b/man/systemd.swap.xml
index 998a047ec5..073c2b3a27 100644
--- a/man/systemd.swap.xml
+++ b/man/systemd.swap.xml
@@ -6,7 +6,9 @@
SPDX-License-Identifier: LGPL-2.1+
-->
-<refentry id="systemd.swap">
+<refentry id="systemd.swap"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
<refentryinfo>
<title>systemd.swap</title>
<productname>systemd</productname>
@@ -141,6 +143,26 @@
successfully.</para>
</listitem>
</varlistentry>
+
+ <xi:include href="systemd.mount.xml" xpointer="device-timeout" />
+
+ <varlistentry>
+ <term><option>x-systemd.makefs</option></term>
+
+ <listitem><para>The swap structure will be initialized on the device. If the device is not
+ "empty", i.e. it contains any signature, the operation will be skipped. It is hence expected
+ that this option remains set even after the device has been initalized.</para>
+
+ <para>Note that this option can only be used in <filename>/etc/fstab</filename>, and will be
+ ignored when part of the <varname>Options=</varname> setting in a unit file.</para>
+
+ <para>See
+ <citerefentry><refentrytitle>systemd-mkswap@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ and the discussion of
+ <citerefentry project='man-pages'><refentrytitle>wipefs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ in <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/man/systemd.syntax.xml b/man/systemd.syntax.xml
index 448ae174cb..67cd640eea 100644
--- a/man/systemd.syntax.xml
+++ b/man/systemd.syntax.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
@@ -73,7 +73,8 @@
backslash is replaced by a space character. This may be used to wrap long lines. The limit on
line length is very large (currently 1 MB), but it is recommended to avoid such long lines and
use multiple directives, variable substitution, or other mechanism as appropriate for the given
- file type.</para>
+ file type. When a comment line or lines follow a line ending with a backslash, the comment block
+ is ignored, so the continued line is concatenated with whatever follows the comment block.</para>
<example><programlisting>[Section A]
KeyOne=value 1
@@ -85,8 +86,31 @@ KeyTwo=value 2
Setting="something" "some thing" "…"
KeyTwo=value 2 \
value 2 continued
+
+[Section C]
+KeyThree=value 2\
+# this line is ignored
+; this line is ignored too
+ value 2 continued
</programlisting></example>
+ <para>Boolean arguments used in configuration files can be written in
+ various formats. For positive settings the strings
+ <option>1</option>, <option>yes</option>, <option>true</option>
+ and <option>on</option> are equivalent. For negative settings, the
+ strings <option>0</option>, <option>no</option>,
+ <option>false</option> and <option>off</option> are
+ equivalent.</para>
+
+ <para>Time span values encoded in configuration files can be written in various formats. A stand-alone
+ number specifies a time in seconds. If suffixed with a time unit, the unit is honored. A
+ concatenation of multiple values with units is supported, in which case the values are added
+ up. Example: <literal>50</literal> refers to 50 seconds; <literal>2min 200ms</literal> refers to
+ 2 minutes and 200 milliseconds, i.e. 120200 ms. The following time units are understood:
+ <literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>d</literal>,
+ <literal>w</literal>, <literal>ms</literal>, <literal>us</literal>. For details see
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+
<para>Various settings are allowed to be specified more than once, in which case the
interpretation depends on the setting. Often, multiple settings form a list, and setting to an
empty value "resets", which means that previous assignments are ignored. When this is allowed,
@@ -95,4 +119,11 @@ KeyTwo=value 2 \
file format.</para>
</refsect1>
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
</refentry>
diff --git a/man/systemd.target.xml b/man/systemd.target.xml
index 51c5ea9af9..10aa91a2ee 100644
--- a/man/systemd.target.xml
+++ b/man/systemd.target.xml
@@ -81,7 +81,8 @@
some.service, the automatic ordering will not be added.</para></listitem>
<listitem><para>Target units automatically gain <varname>Conflicts=</varname>
- dependency against <filename>shutdown.target</filename>.</para></listitem>
+ and <varname>Before=</varname> dependencies against
+ <filename>shutdown.target</filename>.</para></listitem>
</itemizedlist>
</refsect2>
</refsect1>
diff --git a/man/systemd.time.xml b/man/systemd.time.xml
index ad78b611a1..24df5ab942 100644
--- a/man/systemd.time.xml
+++ b/man/systemd.time.xml
@@ -50,7 +50,7 @@
understood:</para>
<itemizedlist>
- <listitem><para>usec, us</para></listitem>
+ <listitem><para>usec, us, µs</para></listitem>
<listitem><para>msec, ms</para></listitem>
<listitem><para>seconds, second, sec, s</para></listitem>
<listitem><para>minutes, minute, min, m</para></listitem>
@@ -74,6 +74,10 @@
1y 12month
55s500ms
300ms20s 5day</programlisting>
+
+ <para>One can use the <command>timespan</command> command of
+ <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ to normalise a textual time span for testing and validation purposes.</para>
</refsect1>
<refsect1>
diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml
index 44b257c745..d254c776bf 100644
--- a/man/systemd.timer.xml
+++ b/man/systemd.timer.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -58,23 +58,17 @@
</refsect1>
<refsect1>
- <title>Implicit Dependencies</title>
-
- <para>The following dependencies are implicitly added:</para>
-
- <itemizedlist>
- <listitem><para>Timer units automatically gain a <varname>Before=</varname>
- dependency on the service they are supposed to activate.</para></listitem>
- </itemizedlist>
- </refsect1>
-
- <refsect1>
<title>Automatic Dependencies</title>
<refsect2>
<title>Implicit Dependencies</title>
- <para>There are no implicit dependencies for timer units.</para>
+ <para>The following dependencies are implicitly added:</para>
+
+ <itemizedlist>
+ <listitem><para>Timer units automatically gain a <varname>Before=</varname>
+ dependency on the service they are supposed to activate.</para></listitem>
+ </itemizedlist>
</refsect2>
<refsect2>
@@ -213,7 +207,7 @@
distributed amount of time between 0 and the specified time
value. Defaults to 0, indicating that no randomized delay
shall be applied. Each timer unit will determine this delay
- randomly each time it is started, and the delay will simply be
+ randomly before each iteration, and the delay will simply be
added on top of the next determined elapsing time. This is
useful to stretch dispatching of similarly configured timer
events over a certain amount time, to avoid that they all fire
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 7605c43375..7e1b3cb7eb 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
@@ -39,19 +39,26 @@
<filename><replaceable>slice</replaceable>.slice</filename>,
<filename><replaceable>scope</replaceable>.scope</filename></para>
- <para><literallayout><filename>/etc/systemd/system.control/*</filename>
+ <refsect2>
+ <title>System Unit Search Path</title>
+
+ <para><literallayout><filename>/etc/systemd/system.control/*</filename>
<filename>/run/systemd/system.control/*</filename>
<filename>/run/systemd/transient/*</filename>
<filename>/run/systemd/generator.early/*</filename>
<filename>/etc/systemd/system/*</filename>
+<filename>/etc/systemd/systemd.attached/*</filename>
<filename>/run/systemd/system/*</filename>
+<filename>/run/systemd/systemd.attached/*</filename>
<filename>/run/systemd/generator/*</filename>
<filename>…</filename>
<filename>/usr/lib/systemd/system/*</filename>
-<filename>/run/systemd/generator.late/*</filename>
- </literallayout></para>
+<filename>/run/systemd/generator.late/*</filename></literallayout></para>
+ </refsect2>
- <para><literallayout><filename>~/.config/systemd/user.control/*</filename>
+ <refsect2>
+ <title>User Unit Search Path</title>
+ <para><literallayout><filename>~/.config/systemd/user.control/*</filename>
<filename>$XDG_RUNTIME_DIR/systemd/user.control/*</filename>
<filename>$XDG_RUNTIME_DIR/systemd/transient/*</filename>
<filename>$XDG_RUNTIME_DIR/systemd/generator.early/*</filename>
@@ -63,8 +70,9 @@
<filename>~/.local/share/systemd/user/*</filename>
<filename>…</filename>
<filename>/usr/lib/systemd/user/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/generator.late/*</filename>
- </literallayout></para>
+<filename>$XDG_RUNTIME_DIR/systemd/generator.late/*</filename></literallayout></para>
+ </refsect2>
+
</refsynopsisdiv>
<refsect1>
@@ -118,23 +126,6 @@
do not need the prefix. Applications may use this to include
additional information in the unit files.</para>
- <para>Boolean arguments used in unit files can be written in
- various formats. For positive settings the strings
- <option>1</option>, <option>yes</option>, <option>true</option>
- and <option>on</option> are equivalent. For negative settings, the
- strings <option>0</option>, <option>no</option>,
- <option>false</option> and <option>off</option> are
- equivalent.</para>
-
- <para>Time span values encoded in unit files can be written in various formats. A stand-alone
- number specifies a time in seconds. If suffixed with a time unit, the unit is honored. A
- concatenation of multiple values with units is supported, in which case the values are added
- up. Example: <literal>50</literal> refers to 50 seconds; <literal>2min 200ms</literal> refers to
- 2 minutes and 200 milliseconds, i.e. 120200 ms. The following time units are understood:
- <literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>d</literal>,
- <literal>w</literal>, <literal>ms</literal>, <literal>us</literal>. For details see
- <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
-
<para>Units can be aliased (have an alternative name), by creating a symlink from the new name
to the existing name in one of the unit search paths. For example,
<filename>systemd-networkd.service</filename> has the alias
@@ -334,7 +325,7 @@
<row>
<entry><filename>/run/systemd/generator.early</filename></entry>
<entry>Generated units with high priority (see <replaceable>early-dir</replaceable> in <citerefentry
- ><refentrytitle>system.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
+ ><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
</row>
<row>
<entry><filename>/etc/systemd/system</filename></entry>
@@ -347,7 +338,7 @@
<row>
<entry><filename>/run/systemd/generator</filename></entry>
<entry>Generated units with medium priority (see <replaceable>normal-dir</replaceable> in <citerefentry
- ><refentrytitle>system.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
+ ><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
</row>
<row>
<entry><filename>/usr/local/lib/systemd/system</filename></entry>
@@ -359,7 +350,7 @@
<row>
<entry><filename>/run/systemd/generator.late</filename></entry>
<entry>Generated units with low priority (see <replaceable>late-dir</replaceable> in <citerefentry
- ><refentrytitle>system.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
+ ><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
</row>
</tbody>
</tgroup>
@@ -395,7 +386,7 @@
<row>
<entry><filename>/run/systemd/generator.early</filename></entry>
<entry>Generated units with high priority (see <replaceable>early-dir</replaceable> in <citerefentry
- ><refentrytitle>system.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
+ ><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
</row>
<row>
<entry><filename>$XDG_CONFIG_HOME/systemd/user</filename> or <filename>$HOME/.config/systemd/user</filename></entry>
@@ -416,7 +407,7 @@
<row>
<entry><filename>$XDG_RUNTIME_DIR/systemd/generator</filename></entry>
<entry>Generated units with medium priority (see <replaceable>normal-dir</replaceable> in <citerefentry
- ><refentrytitle>system.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
+ ><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
</row>
<row>
<entry><filename>$XDG_DATA_HOME/systemd/user</filename> or <filename>$HOME/.local/share/systemd/user</filename></entry>
@@ -436,7 +427,7 @@
<row>
<entry><filename>$XDG_RUNTIME_DIR/systemd/generator.late</filename></entry>
<entry>Generated units with low priority (see <replaceable>late-dir</replaceable> in <citerefentry
- ><refentrytitle>system.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
+ ><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
</row>
</tbody>
</tgroup>
@@ -511,15 +502,21 @@
<varlistentry>
<term><varname>Description=</varname></term>
- <listitem><para>A free-form string describing the unit. This
- is intended for use in UIs to show descriptive information
- along with the unit name. The description should contain a
- name that means something to the end user. <literal>Apache2
- Web Server</literal> is a good example. Bad examples are
- <literal>high-performance light-weight HTTP server</literal>
- (too generic) or <literal>Apache2</literal> (too specific and
- meaningless for people who do not know
- Apache).</para></listitem>
+ <listitem><para>A human readable name for the unit. This is used by
+ <command>systemd</command> (and other UIs) as the label for the unit, so this string should
+ identify the unit rather than describe it, despite the name. <literal>Apache2 Web
+ Server</literal> is a good example. Bad examples are <literal>high-performance light-weight
+ HTTP server</literal> (too generic) or <literal>Apache2</literal> (too specific and
+ meaningless for people who do not know Apache). <command>systemd</command> will use this
+ string as a noun in status messages (<literal>Starting
+ <replaceable>description</replaceable>...</literal>, <literal>Started
+ <replaceable>description</replaceable>.</literal>, <literal>Reached target
+ <replaceable>description</replaceable>.</literal>, <literal>Failed to start
+ <replaceable>description</replaceable>.</literal>), so it should be capitalized, and should
+ not be a full sentence or a phrase with a continous verb. Bad examples include
+ <literal>exiting the container</literal> or <literal>updating the database once per
+ day.</literal>.</para>
+ </listitem>
</varlistentry>
<varlistentry>
@@ -668,10 +665,10 @@
<para>If a unit A that conflicts with a unit B is scheduled to
be started at the same time as B, the transaction will either
- fail (in case both are required part of the transaction) or be
+ fail (in case both are required parts of the transaction) or be
modified to be fixed (in case one or both jobs are not a
required part of the transaction). In the latter case, the job
- that is not the required will be removed, or in case both are
+ that is not required will be removed, or in case both are
not required, the unit that conflicts will be started and the
unit that is conflicted is stopped.</para></listitem>
</varlistentry>
@@ -874,12 +871,49 @@
</varlistentry>
<varlistentry>
+ <term><varname>FailureAction=</varname></term>
+ <term><varname>SuccessAction=</varname></term>
+
+ <listitem><para>Configure the action to take when the unit stops and enters a failed state or inactive state.
+ Takes one of <option>none</option>, <option>reboot</option>, <option>reboot-force</option>,
+ <option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option>,
+ <option>poweroff-immediate</option>, <option>exit</option>, and <option>exit-force</option>. In system mode,
+ all options are allowed. In user mode, only <option>none</option>, <option>exit</option>, and
+ <option>exit-force</option> are allowed. Both options default to <option>none</option>.</para>
+
+ <para>If <option>none</option> is set, no action will be triggered. <option>reboot</option> causes a reboot
+ following the normal shutdown procedure (i.e. equivalent to <command>systemctl reboot</command>).
+ <option>reboot-force</option> causes a forced reboot which will terminate all processes forcibly but should
+ cause no dirty file systems on reboot (i.e. equivalent to <command>systemctl reboot -f</command>) and
+ <option>reboot-immediate</option> causes immediate execution of the
+ <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call, which
+ might result in data loss (i.e. equivalent to <command>systemctl reboot -ff</command>). Similarly,
+ <option>poweroff</option>, <option>poweroff-force</option>, <option>poweroff-immediate</option> have the effect
+ of powering down the system with similar semantics. <option>exit</option> causes the manager to exit following
+ the normal shutdown procedure, and <option>exit-force</option> causes it terminate without shutting down
+ services. When <option>exit</option> or <option>exit-force</option> is used by default the exit status of the
+ main process of the unit (if this applies) is returned from the service manager. However, this may be overriden
+ with <varname>FailureActionExitStatus=</varname>/<varname>SuccessActionExitStatus=</varname>, see
+ below.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>FailureActionExitStatus=</varname></term>
+ <term><varname>SuccessActionExitStatus=</varname></term>
+
+ <listitem><para>Controls the exit status to propagate back to an invoking container manager (in case of a
+ system service) or service manager (in case of a user manager) when the
+ <varname>FailureAction=</varname>/<varname>SuccessAction=</varname> are set to <option>exit</option> or
+ <option>exit-force</option> and the action is triggered. By default the exit status of the main process of the
+ triggering unit (if this applies) is propagated. Takes a value in the range 0…255 or the empty string to
+ request default behaviour.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>JobTimeoutSec=</varname></term>
<term><varname>JobRunningTimeoutSec=</varname></term>
- <term><varname>JobTimeoutAction=</varname></term>
- <term><varname>JobTimeoutRebootArgument=</varname></term>
- <listitem><para>When a job for this unit is queued, a time-out <varname>JobTimeoutSec=</varname> may be
+ <listitem><para>When a job for this unit is queued, a timeout <varname>JobTimeoutSec=</varname> may be
configured. Similarly, <varname>JobRunningTimeoutSec=</varname> starts counting when the queued job is actually
started. If either time limit is reached, the job will be cancelled, the unit however will not change state or
even enter the <literal>failed</literal> mode. This value defaults to <literal>infinity</literal> (job timeouts
@@ -889,12 +923,20 @@
no effect on the unit itself, only on the job that might be pending for it. Or in other words: unit-specific
timeouts are useful to abort unit state changes, and revert them. The job timeout set with this option however
is useful to abort only the job waiting for the unit state to change.</para>
+ </listitem>
+ </varlistentry>
- <para><varname>JobTimeoutAction=</varname> optionally configures an additional action to take when the time-out
- is hit. It takes the same values as <varname>StartLimitAction=</varname>. Defaults to <option>none</option>.
+ <varlistentry>
+ <term><varname>JobTimeoutAction=</varname></term>
+ <term><varname>JobTimeoutRebootArgument=</varname></term>
+
+ <listitem><para><varname>JobTimeoutAction=</varname> optionally configures an additional action to take when
+ the timeout is hit, see description of <varname>JobTimeoutSec=</varname> and
+ <varname>JobRunningTimeoutSec=</varname> above. It takes the same values as
+ <varname>StartLimitAction=</varname>. Defaults to <option>none</option>.
<varname>JobTimeoutRebootArgument=</varname> configures an optional reboot string to pass to the
- <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry>
- system call.</para></listitem>
+ <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call.
+ </para></listitem>
</varlistentry>
<varlistentry>
@@ -929,29 +971,13 @@
<varlistentry>
<term><varname>StartLimitAction=</varname></term>
- <listitem><para>Configure the action to take if the rate limit configured with
- <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit. Takes one of
- <option>none</option>, <option>reboot</option>, <option>reboot-force</option>,
- <option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option> or
- <option>poweroff-immediate</option>. If <option>none</option> is set, hitting the rate limit will trigger no
- action besides that the start will not be permitted. <option>reboot</option> causes a reboot following the
- normal shutdown procedure (i.e. equivalent to <command>systemctl reboot</command>).
- <option>reboot-force</option> causes a forced reboot which will terminate all processes forcibly but should
- cause no dirty file systems on reboot (i.e. equivalent to <command>systemctl reboot -f</command>) and
- <option>reboot-immediate</option> causes immediate execution of the
- <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call, which
- might result in data loss. Similarly, <option>poweroff</option>, <option>poweroff-force</option>,
- <option>poweroff-immediate</option> have the effect of powering down the system with similar
- semantics. Defaults to <option>none</option>.</para></listitem>
+ <listitem><para>Configure an additional action to take if the rate limit configured with
+ <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit. Takes the same
+ values as the setting <varname>FailureAction=</varname>/<varname>SuccessAction=</varname> settings and executes
+ the same actions. If <option>none</option> is set, hitting the rate limit will trigger no action besides that
+ the start will not be permitted. Defaults to <option>none</option>.</para></listitem>
</varlistentry>
- <varlistentry>
- <term><varname>FailureAction=</varname></term>
- <term><varname>SuccessAction=</varname></term>
- <listitem><para>Configure the action to take when the unit stops and enters a failed state or inactive
- state. Takes the same values as the setting <varname>StartLimitAction=</varname> setting and executes the same
- actions. Both options default to <option>none</option>.</para></listitem>
- </varlistentry>
<varlistentry>
<term><varname>RebootArgument=</varname></term>
@@ -992,12 +1018,13 @@
<listitem><para>Before starting a unit, verify that the specified condition is true. If it is not true, the
starting of the unit will be (mostly silently) skipped, however all ordering dependencies of it are still
- respected. A failing condition will not result in the unit being moved into a failure state. The condition is
- checked at the time the queued start job is to be executed. Use condition expressions in order to silently skip
- units that do not apply to the local running system, for example because the kernel or runtime environment
- doesn't require its functionality. Use the various <varname>AssertArchitecture=</varname>,
- <varname>AssertVirtualization=</varname>, … options for a similar mechanism that puts the unit in a failure
- state and logs about the failed check (see below).</para>
+ respected. A failing condition will not result in the unit being moved into the <literal>failed</literal>
+ state. The condition is checked at the time the queued start job is to be executed. Use condition expressions
+ in order to silently skip units that do not apply to the local running system, for example because the kernel
+ or runtime environment doesn't require their functionality. Use the various
+ <varname>AssertArchitecture=</varname>, <varname>AssertVirtualization=</varname>, … options for a similar
+ mechanism that causes the job to fail (instead of being skipped) and results in logging about the failed check
+ (instead of being silently processed). For details about assertion conditions see below.</para>
<para><varname>ConditionArchitecture=</varname> may be used to
check whether the system is running on a specific
@@ -1226,12 +1253,12 @@
cgroup controller name (eg. <option>cpu</option>), verifying that it is
available for use on the system. For example, a particular controller
may not be available if it was disabled on the kernel command line with
- <literal>cgroup_disable=</literal><replaceable>controller</replaceable>.
- Multiple controllers may be passed with a space separating them; in
- this case the condition will only pass if all listed controllers are
- available for use. Controllers unknown to systemd are ignored. Valid
- controllers are <option>cpu</option>, <option>cpuacct</option>,
- <option>io</option>, <option>blkio</option>, <option>memory</option>,
+ <varname>cgroup_disable=controller</varname>. Multiple controllers may
+ be passed with a space separating them; in this case the condition will
+ only pass if all listed controllers are available for use. Controllers
+ unknown to systemd are ignored. 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>
<para>If multiple conditions are specified, the unit will be
@@ -1278,9 +1305,16 @@
<listitem><para>Similar to the <varname>ConditionArchitecture=</varname>,
<varname>ConditionVirtualization=</varname>, …, condition settings described above, these settings add
assertion checks to the start-up of the unit. However, unlike the conditions settings, any assertion setting
- that is not met results in failure of the start job (which means this is logged loudly). Use assertion
- expressions for units that cannot operate when specific requirements are not met, and when this is something
- the administrator or user should look into.</para></listitem>
+ that is not met results in failure of the start job (which means this is logged loudly). Note that hitting a
+ configured assertion does not cause the unit to enter the <literal>failed</literal> state (or in fact result in
+ any state change of the unit), it affects only the job queued for it. Use assertion expressions for units that
+ cannot operate when specific requirements are not met, and when this is something the administrator or user
+ should look into.</para>
+
+ <para>Note that neither assertion nor condition expressions result in unit state changes. Also note that both
+ are checked at the time the job is to be executed, i.e. long after depending jobs and it itself were
+ queued. Thus, neither condition nor assertion expressions are suitable for conditionalizing unit
+ dependencies.</para></listitem>
</varlistentry>
<varlistentry>
@@ -1495,8 +1529,8 @@
</variablelist>
<para>The following specifiers are interpreted in the Install
- section: %n, %N, %p, %i, %j, %U, %u, %m, %H, %b, %v. For their meaning
- see the next section.
+ section: %n, %N, %p, %i, %j, %g, %G, %U, %u, %m, %H, %b, %v. For their
+ meaning see the next section.
</para>
</refsect1>
@@ -1624,6 +1658,16 @@
<entry>This is either <filename>/tmp</filename> or the path <literal>$TMPDIR</literal>, <literal>$TEMP</literal> or <literal>$TMP</literal> are set to.</entry>
</row>
<row>
+ <entry><literal>%g</literal></entry>
+ <entry>User group</entry>
+ <entry>This is the name of the group running the service manager instance. In case of the system manager this resolves to <literal>root</literal>.</entry>
+ </row>
+ <row>
+ <entry><literal>%G</literal></entry>
+ <entry>User GID</entry>
+ <entry>This is the numeric GID of the user running the service manager instance. In case of the system manager this resolves to <literal>0</literal>.</entry>
+ </row>
+ <row>
<entry><literal>%u</literal></entry>
<entry>User name</entry>
<entry>This is the name of the user running the service manager instance. In case of the system manager this resolves to <literal>root</literal>.</entry>
diff --git a/man/systemd.xml b/man/systemd.xml
index 17ab59beb5..49a29f9651 100644
--- a/man/systemd.xml
+++ b/man/systemd.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -392,6 +392,25 @@
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for details about these target units.</para>
+ <para>systemd only keeps a minimal set of units loaded into memory. Specifically, the only units that are kept
+ loaded into memory are those for which at least one of the following conditions is true:</para>
+
+ <orderedlist>
+ <listitem><para>It is in an active, activating, deactivating or failed state (i.e. in any unit state except for <literal>dead</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 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>
+ </orderedlist>
+
+ <para>systemd will automatically and implicitly load units from disk — if they are not loaded yet — as soon as
+ operations are requested for them. Thus, in many respects, the fact whether a unit is loaded or not is invisible to
+ clients. Use <command>systemctl list-units --all</command> to comprehensively list all units currently loaded. Any
+ unit for which none of the conditions above applies is promptly unloaded. Note that when a unit is unloaded from
+ memory its accounting data is flushed out too. However, this data is generally not lost, as a journal log record
+ is generated declaring the consumed resources whenever a unit shuts down.</para>
+
<para>Processes systemd spawns are placed in individual Linux
control groups named after the unit which they belong to in the
private systemd hierarchy. (see <ulink
@@ -561,16 +580,15 @@
<varlistentry>
<term><constant>SIGINT</constant></term>
- <listitem><para>Upon receiving this signal the systemd system
- manager will start the
- <filename>ctrl-alt-del.target</filename> unit. This is mostly
- equivalent to <command>systemctl start ctrl-alt-del.target
- --job-mode=replace-irreversible</command>. If this signal is
- received more than 7 times per 2s, an immediate reboot is
- triggered. Note that pressing Ctrl-Alt-Del on the console
- will trigger this signal. Hence, if a reboot is hanging,
- pressing Ctrl-Alt-Del more than 7 times in 2s is a relatively
- safe way to trigger an immediate reboot.</para>
+ <listitem><para>Upon receiving this signal the systemd system manager will start the
+ <filename>ctrl-alt-del.target</filename> unit. This is mostly equivalent to
+ <command>systemctl start ctrl-alt-del.target --job-mode=replace-irreversible</command>. If
+ this signal is received more than 7 times per 2s, an immediate reboot is triggered. Note
+ that pressing
+ <keycombo><keycap>Ctrl</keycap><keycap>Alt</keycap><keycap>Del</keycap></keycombo> on the
+ console will trigger this signal. Hence, if a reboot is hanging, pressing
+ <keycombo><keycap>Ctrl</keycap><keycap>Alt</keycap><keycap>Del</keycap></keycombo> more than
+ 7 times in 2 seconds is a relatively safe way to trigger an immediate reboot.</para>
<para>systemd user managers treat this signal the same way as
<constant>SIGTERM</constant>.</para></listitem>
@@ -882,6 +900,9 @@
for more information.</para></listitem>
</varlistentry>
</variablelist>
+
+ <para>For further environment variables understood by systemd and its various components, see <ulink
+ url="https://systemd.io/ENVIRONMENT">Known Environment Variables</ulink>.</para>
</refsect1>
<refsect1>
@@ -1051,7 +1072,7 @@
hybrid or full legacy cgroup hierarchy.</para>
<para>If this option is not specified, the default behaviour is determined
- during compilation (the <option>--with-default-hierarchy=</option>
+ during compilation (the <option>-Ddefault-hierarchy=</option> meson
option). If the kernel does not support unified cgroup hierarchy, the legacy
hierarchy will be used even if this option is specified.</para>
</listitem>
@@ -1070,7 +1091,7 @@
the use of "hybrid" hierarchy.</para>
<para>If this option is not specified, the default behaviour is determined
- during compilation (the <option>--with-default-hierarchy=</option>
+ during compilation (the <option>-Ddefault-hierarchy=</option> meson
option). If the kernel does not support unified cgroup hierarchy, the legacy
hierarchy will be used even if this option is specified.</para>
</listitem>
@@ -1080,7 +1101,7 @@
<term><varname>quiet</varname></term>
<listitem><para>Turn off status output at boot, much like
- <varname>systemd.show_status=false</varname> would. Note that
+ <varname>systemd.show_status=no</varname> would. Note that
this option is also read by the kernel itself and disables
kernel log output. Passing this option hence turns off the
usual output from both the system manager and the kernel.
diff --git a/man/threads-aware.xml b/man/threads-aware.xml
new file mode 100644
index 0000000000..7985f4acd1
--- /dev/null
+++ b/man/threads-aware.xml
@@ -0,0 +1,17 @@
+<?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="strict">All functions listed here are thread-agnostic and only a single specific thread may operate on a
+given object during its entire lifetime. It's safe to allocate multiple independent objects and use each from a
+specific thread in parallel. However, it's not safe to allocate such an object in one thread, and operate or free it
+from any other, even if locking is used to ensure these threads don't operate on it at the very same time.</para>
+
+<para id="safe">All functions listed here are thread-safe and may be called in parallel from multiple threads.</para>
+
+</refsect1>
diff --git a/man/timedatectl.xml b/man/timedatectl.xml
index 39cd78666e..a62902423a 100644
--- a/man/timedatectl.xml
+++ b/man/timedatectl.xml
@@ -73,10 +73,11 @@
<varlistentry>
<term><option>--monitor</option></term>
- <listitem><para>If <command>timesync-status</command> is invoked and this option is passed,
- then <command>timedatectl</command> monitors the status of
+ <listitem><para>If <command>timesync-status</command> is invoked and this option is passed, then
+ <command>timedatectl</command> monitors the status of
<citerefentry><refentrytitle>systemd-timesyncd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- and updates the outputs. Use Ctrl-C to terminate the monitoring.</para></listitem>
+ and updates the outputs. Use <keycombo><keycap>Ctrl</keycap><keycap>C</keycap></keycombo> to terminate the
+ monitoring.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml
index e2e2eac228..5d393f3984 100644
--- a/man/tmpfiles.d.xml
+++ b/man/tmpfiles.d.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
SPDX-License-Identifier: LGPL-2.1+
@@ -40,25 +40,33 @@
<refsect1>
<title>Description</title>
- <para><command>systemd-tmpfiles</command> uses the configuration
- files from the above directories to describe the creation,
- cleaning and removal of volatile and temporary files and
- directories which usually reside in directories such as
- <filename>/run</filename> or <filename>/tmp</filename>.</para>
-
- <para>Volatile and temporary files and directories are those
- located in <filename>/run</filename> (and its alias
- <filename>/var/run</filename>), <filename>/tmp</filename>,
- <filename>/var/tmp</filename>, the API file systems such as
- <filename>/sys</filename> or <filename>/proc</filename>, as well
- as some other directories below <filename>/var</filename>.</para>
-
- <para>System daemons frequently require private runtime
- directories below <filename>/run</filename> to place communication
- sockets and similar in. For these, consider declaring them in
- their unit files using <varname>RuntimeDirectory=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details), if this is feasible.</para>
+ <para><filename>tmpfiles.d</filename> configuration files provide a generic mechanism to define the
+ <emphasis>creation</emphasis> of regular files, directories, pipes, and device nodes, adjustments to
+ their <emphasis>access mode, ownership, attributes, quota assignments, and contents</emphasis>, and
+ finally their time-based <emphasis>removal</emphasis>. It is mostly commonly used for volatile and
+ temporary files and directories (such as those located under <filename>/run</filename>,
+ <filename>/tmp</filename>, <filename>/var/tmp</filename>, the API file systems such as
+ <filename>/sys</filename> or <filename>/proc</filename>, as well as some other directories below
+ <filename>/var</filename>).</para>
+
+ <para><command>systemd-tmpfiles</command> uses this configuration to create volatile files and
+ directories during boot and to do periodic cleanup afterwards. See
+ <citerefentry><refentrytitle>systemd-tmpfiles</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ the description of <filename>systemd-tmpfiles-setup.service</filename>,
+ <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
+ <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
+ are that the configuration required by the unit is centralized in one place, and that the lifetime of the
+ directory is tied to the lifetime of the service itself. Similarly, <varname>StateDirectory=</varname>,
+ <varname>CacheDirectory=</varname>, <varname>LogsDirectory=</varname>, and
+ <varname>ConfigurationDirectory=</varname> should be used to create directories under
+ <filename>/var/lib/</filename>, <filename>/var/cache/</filename>, <filename>/var/log/</filename>, and
+ <filename>/etc/</filename>. <filename>tmpfiles.d</filename> should be used for files whose lifetime is
+ independent of any service or requires more complicated configuration.</para>
</refsect1>
<refsect1>
@@ -70,28 +78,20 @@
The second variant should be used when it is desirable to make it
easy to override just this part of configuration.</para>
- <para>Files in <filename>/etc/tmpfiles.d</filename> override files
- with the same name in <filename>/usr/lib/tmpfiles.d</filename> and
- <filename>/run/tmpfiles.d</filename>. Files in
- <filename>/run/tmpfiles.d</filename> override files with the same
- name in <filename>/usr/lib/tmpfiles.d</filename>. Packages should
- install their configuration files in
- <filename>/usr/lib/tmpfiles.d</filename>. Files in
- <filename>/etc/tmpfiles.d</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 path, the entry in the file with
- the lexicographically earliest name will be applied. All other
- conflicting entries will be logged as errors. When two lines are
- prefix and suffix of each other, then the prefix is always
- processed first, the suffix later. Lines that take globs are
- applied after those accepting no globs. If multiple operations
- shall be applied on the same file, (such as ACL, xattr, file
- attribute adjustments), these are always done in the same fixed
- order. Otherwise, the files/directories are processed in the order
- they are listed.</para>
+ <para>Files in <filename>/etc/tmpfiles.d</filename> override files with the same name in
+ <filename>/usr/lib/tmpfiles.d</filename> and <filename>/run/tmpfiles.d</filename>. Files in
+ <filename>/run/tmpfiles.d</filename> override files with the same name in
+ <filename>/usr/lib/tmpfiles.d</filename>. Packages should install their configuration files in
+ <filename>/usr/lib/tmpfiles.d</filename>. Files in <filename>/etc/tmpfiles.d</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 path, the entry in the file with the lexicographically earliest
+ name will be applied. All other conflicting entries will be logged as errors. When two lines are prefix path and
+ suffix path of each other, then the prefix line is always created first, the suffix later (and if removal applies
+ to the line, the order is reversed: the suffix is removed first, the prefix later). Lines that take globs are
+ applied after those accepting no globs. If multiple operations shall be applied on the same file (such as ACL,
+ xattr, file attribute adjustments), these are always done in the same fixed order. Except for those cases, the
+ files/directories are processed in the order they are listed.</para>
<para>If the administrator wants to disable a configuration file
supplied by the vendor, the recommended way is to place a symlink
@@ -106,9 +106,9 @@
<para>The configuration format is one line per path containing
type, path, mode, ownership, age, and argument fields:</para>
- <programlisting>#Type Path Mode UID GID Age Argument
-d /run/user 0755 root root 10d -
-L /tmp/foobar - - - - /dev/null</programlisting>
+ <programlisting>#Type Path Mode User Group Age Argument
+d /run/user 0755 root root 10d -
+L /tmp/foobar - - - - /dev/null</programlisting>
<para>Fields may be enclosed within quotes and contain C-style escapes.</para>
@@ -116,7 +116,7 @@ L /tmp/foobar - - - - /dev/null</programlisting>
<title>Type</title>
<para>The type consists of a single letter and optionally an
- exclamation mark.</para>
+ exclamation mark and/or minus sign.</para>
<para>The following line types are understood:</para>
@@ -146,107 +146,88 @@ L /tmp/foobar - - - - /dev/null</programlisting>
<varlistentry>
<term><varname>d</varname></term>
- <listitem><para>Create a directory. The mode and ownership will be adjusted if
- specified and the directory already exists. Contents of this directory are subject
- to time based cleanup if the age argument is specified.</para></listitem>
+ <listitem><para>Create a directory. The mode and ownership will be adjusted if specified. Contents
+ of this directory are subject to time based cleanup if the age argument is specified.
+ </para></listitem>
</varlistentry>
<varlistentry>
<term><varname>D</varname></term>
- <listitem><para>Similar to <varname>d</varname>, but in addition the contents
- of the directory will be removed when <option>--remove</option> is used.
- </para></listitem>
+ <listitem><para>Similar to <varname>d</varname>, but in addition the contents of the directory will
+ be removed when <option>--remove</option> is used.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>e</varname></term>
- <listitem><para>Similar to <varname>d</varname>, but the directory will not be created if
- it does not exist. Lines of this type accept shell-style globs in place of normal path
- names. For this entry to be useful, at least one of the mode, uid, gid, or age arguments
- must be specified, since otherwise this entry has no effect. If the age argument is
- <literal>0</literal>, contents of the directory will be unconditionally deleted every time
- <command>systemd-tmpfiles --clean</command> is run. This can be useful when combined with
- <varname>!</varname>, see the examples.</para></listitem>
+ <listitem><para>Adjust the mode and ownership of existing directories and remove their contents
+ based on age.
+ Lines of this type accept shell-style globs in place of normal path names. Contents of the
+ directories are subject to time based cleanup if the age argument is specified. If the age argument
+ is <literal>0</literal>, contents will be unconditionally deleted every time
+ <command>systemd-tmpfiles --clean</command> is run.</para>
+
+ <para>For this entry to be useful, at least one of the mode, user, group, or age arguments must be
+ specified, since otherwise this entry has no effect. As an exception, an entry with no effect may
+ be useful when combined with <varname>!</varname>, see the examples.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>v</varname></term>
- <listitem><para>Create a subvolume if the path does not
- exist yet, the file system supports subvolumes (btrfs), and
- the system itself is installed into a subvolume
- (specifically: the root directory <filename>/</filename> is
- itself a subvolume). Otherwise, create a normal directory, in
- the same way as <varname>d</varname>. A subvolume created
- with this line type is not assigned to any higher-level
- quota group. For that, use <varname>q</varname> or
- <varname>Q</varname>, which allow creating simple quota
- group hierarchies, see below.</para></listitem>
+ <listitem><para>Create a subvolume if the path does not exist yet, the file system supports
+ subvolumes (btrfs), and the system itself is installed into a subvolume (specifically: the root
+ directory <filename>/</filename> is itself a subvolume). Otherwise, create a normal directory, in
+ the same way as <varname>d</varname>.</para>
+
+ <para>A subvolume created with this line type is not assigned to any higher-level quota group. For
+ that, use <varname>q</varname> or <varname>Q</varname>, which allow creating simple quota group
+ hierarchies, see below.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>q</varname></term>
- <listitem><para>Similar to <varname>v</varname>. However,
- makes sure that the subvolume will be assigned to the same
- higher-level quota groups as the subvolume it has been
- created in. This ensures that higher-level limits and
- accounting applied to the parent subvolume also include the
- specified subvolume. On non-btrfs file systems, this line
- type is identical to <varname>d</varname>. If the subvolume
- already exists and is already assigned to one or more higher
- level quota groups, no change to the quota hierarchy is
- made. Also see <varname>Q</varname> below. See <citerefentry
- project='die-net'><refentrytitle>btrfs-qgroup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- for details about the btrfs quota group
- concept.</para></listitem>
+ <listitem><para>Create a subvolume or directory the same as <varname>v</varname>, but assign the
+ subvolume to the same higher-level quota groups as the parent. This ensures that higher-level
+ limits and accounting applied to the parent subvolume also include the specified subvolume. On
+ non-btrfs file systems, this line type is identical to <varname>d</varname>.</para>
+
+ <para>If the subvolume already exists, no change to the quota hierarchy is made, regardless of whether the
+ subvolume is already attached to a quota group or not. Also see <varname>Q</varname> below. See <citerefentry
+ project='die-net'><refentrytitle>btrfs-qgroup</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
+ details about the btrfs quota group concept.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Q</varname></term>
- <listitem><para>Similar to <varname>q</varname>. However,
- instead of copying the higher-level quota group assignments
- from the parent as-is, the lowest quota group of the parent
- subvolume is determined that is not the leaf quota
- group. Then, an "intermediary" quota group is inserted that
- is one level below this level, and shares the same ID part
- as the specified subvolume. If no higher-level quota group
- exists for the parent subvolume, a new quota group at level
- 255 sharing the same ID as the specified subvolume is
- inserted instead. This new intermediary quota group is then
- assigned to the parent subvolume's higher-level quota
- groups, and the specified subvolume's leaf quota group is
- assigned to it.</para>
-
- <para>Effectively, this has a similar effect as
- <varname>q</varname>, however introduces a new higher-level
- quota group for the specified subvolume that may be used to
- enforce limits and accounting to the specified subvolume and
- children subvolume created within it. Thus, by creating
- subvolumes only via <varname>q</varname> and
- <varname>Q</varname>, a concept of "subtree quotas" is
- implemented. Each subvolume for which <varname>Q</varname>
- is set will get a "subtree" quota group created, and all
- child subvolumes created within it will be assigned to
- it. Each subvolume for which <varname>q</varname> is set
- will not get such a "subtree" quota group, but it is ensured
- that they are added to the same "subtree" quota group as their
- immediate parents.</para>
-
- <para>It is recommended to use
- <varname>Q</varname> for subvolumes that typically contain
- further subvolumes, and where it is desirable to have
- accounting and quota limits on all child subvolumes
- together. Examples for <varname>Q</varname> are typically
- <filename>/home</filename> or
- <filename>/var/lib/machines</filename>. In contrast,
- <varname>q</varname> should be used for subvolumes that
- either usually do not include further subvolumes or where no
- accounting and quota limits are needed that apply to all
- child subvolumes together. Examples for <varname>q</varname>
- are typically <filename>/var</filename> or
- <filename>/var/tmp</filename>. As with <varname>Q</varname>,
- <varname>q</varname> has no effect on the quota group
- hierarchy if the subvolume exists and already has at least
- one higher-level quota group assigned.</para></listitem>
+ <listitem><para>Create the subvolume or directory the same as <varname>v</varname>, but assign the
+ new subvolume to a new leaf quota group. Instead of copying the higher-level quota group
+ assignments from the parent as is done with <varname>q</varname>, the lowest quota group of the
+ parent subvolume is determined that is not the leaf quota group. Then, an "intermediary" quota
+ group is inserted that is one level below this level, and shares the same ID part as the specified
+ subvolume. If no higher-level quota group exists for the parent subvolume, a new quota group at
+ level 255 sharing the same ID as the specified subvolume is inserted instead. This new intermediary
+ quota group is then assigned to the parent subvolume's higher-level quota groups, and the specified
+ subvolume's leaf quota group is assigned to it.</para>
+
+ <para>Effectively, this has a similar effect as <varname>q</varname>, however introduces a new higher-level
+ quota group for the specified subvolume that may be used to enforce limits and accounting to the specified
+ subvolume and children subvolume created within it. Thus, by creating subvolumes only via
+ <varname>q</varname> and <varname>Q</varname>, a concept of "subtree quotas" is implemented. Each subvolume
+ for which <varname>Q</varname> is set will get a "subtree" quota group created, and all child subvolumes
+ created within it will be assigned to it. Each subvolume for which <varname>q</varname> is set will not get
+ such a "subtree" quota group, but it is ensured that they are added to the same "subtree" quota group as
+ their immediate parents.</para>
+
+ <para>It is recommended to use <varname>Q</varname> for subvolumes that typically contain further subvolumes,
+ and where it is desirable to have accounting and quota limits on all child subvolumes together. Examples for
+ <varname>Q</varname> are typically <filename>/home</filename> or <filename>/var/lib/machines</filename>. In
+ contrast, <varname>q</varname> should be used for subvolumes that either usually do not include further
+ subvolumes or where no accounting and quota limits are needed that apply to all child subvolumes
+ together. Examples for <varname>q</varname> are typically <filename>/var</filename> or
+ <filename>/var/tmp</filename>. </para>
+
+ <para>As with <varname>q</varname>, <varname>Q</varname> has no effect on the quota group hierarchy if the
+ subvolume already exists, regardless of whether the subvolume already belong to a quota group or not.
+ </para></listitem>
</varlistentry>
<varlistentry>
@@ -352,20 +333,17 @@ L /tmp/foobar - - - - /dev/null</programlisting>
<varlistentry>
<term><varname>z</varname></term>
- <listitem><para>Adjust the access mode, group and user, and
- restore the SELinux security context of a file or directory,
- if it exists. Lines of this type accept shell-style globs in
- place of normal path names. Does not follow symlinks.</para></listitem>
+ <listitem><para>Adjust the access mode, user and group ownership, and restore the SELinux security
+ context of a file or directory, if it exists. Lines of this type accept shell-style globs in place
+ of normal path names. Does not follow symlinks.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Z</varname></term>
- <listitem><para>Recursively set the access mode, group and
- user, and restore the SELinux security context of a file or
- directory if it exists, as well as of its subdirectories and
- the files contained therein (if applicable). Lines of this
- type accept shell-style globs in place of normal path
- names. Does not follow symlinks. </para></listitem>
+ <listitem><para>Recursively set the access mode, user and group ownership, and restore the SELinux
+ security context of a file or directory if it exists, as well as of its subdirectories and the
+ files contained therein (if applicable). Lines of this type accept shell-style globs in place of
+ normal path names. Does not follow symlinks.</para></listitem>
</varlistentry>
<varlistentry>
@@ -460,6 +438,15 @@ r! /tmp/.X[0-9]*-lock</programlisting>
running system, and will only be executed with
<option>--boot</option>.</para>
+ <para>If the minus sign is used, this line failing to run
+ successfully during create (and only create) will not cause
+ the execution of <command>systemd-tmpfiles</command> to return
+ an error.</para>
+
+ <para>For example:
+ <programlisting># Modify sysfs but don't fail if we are in a container with a read-only /proc
+w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
+
<para>Note that for all line types that result in creation of any kind of file node
(i.e. <varname>f</varname>/<varname>F</varname>,
<varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,
@@ -503,18 +490,14 @@ r! /tmp/.X[0-9]*-lock</programlisting>
</refsect2>
<refsect2>
- <title>UID, GID</title>
-
- <para>The user and group to use for this file or directory. This
- may either be a numeric user/group ID or a user or group
- name. If omitted or when set to <literal>-</literal>, the
- default 0 (root) is used. For <varname>z</varname> and
- <varname>Z</varname> lines, when omitted or when set to
- <literal>-</literal>, the file ownership will not be
- modified. These parameters are ignored for <varname>x</varname>,
- <varname>r</varname>, <varname>R</varname>,
- <varname>L</varname>, <varname>t</varname>, and
- <varname>a</varname> lines.</para>
+ <title>User, Group</title>
+
+ <para>The user and group to use for this file or directory. This may either be a numeric ID or a
+ user/group name. If omitted or when set to <literal>-</literal>, the user and group of the user who
+ invokes <command>systemd-tmpfiles</command> is used. For <varname>z</varname> and <varname>Z</varname>
+ lines, when omitted or when set to <literal>-</literal>, the file ownership will not be modified. These
+ parameters are ignored for <varname>x</varname>, <varname>r</varname>, <varname>R</varname>,
+ <varname>L</varname>, <varname>t</varname>, and <varname>a</varname> lines.</para>
</refsect2>
<refsect2>
@@ -638,7 +621,7 @@ r! /tmp/.X[0-9]*-lock</programlisting>
<row>
<entry><literal>%t</literal></entry>
<entry>System or user runtime directory</entry>
- <entry>In --user mode, this is the same <varname>$XDG_RUNTIME_DIR</varname>, and <filename>/run</filename> otherwise.</entry>
+ <entry>In <option>--user</option> mode, this is the same <varname>$XDG_RUNTIME_DIR</varname>, and <filename>/run</filename> otherwise.</entry>
</row>
<row>
<entry><literal>%T</literal></entry>
@@ -646,6 +629,16 @@ r! /tmp/.X[0-9]*-lock</programlisting>
<entry>This is either <filename>/tmp</filename> or the path <literal>$TMPDIR</literal>, <literal>$TEMP</literal> or <literal>$TMP</literal> are set to.</entry>
</row>
<row>
+ <entry><literal>%g</literal></entry>
+ <entry>User group</entry>
+ <entry>This is the name of the group running the command. In case of the system instance this resolves to <literal>root</literal>.</entry>
+ </row>
+ <row>
+ <entry><literal>%G</literal></entry>
+ <entry>User GID</entry>
+ <entry>This is the numeric GID of the group running the command. In case of the system instance this resolves to <constant>0</constant>.</entry>
+ </row>
+ <row>
<entry><literal>%u</literal></entry>
<entry>User name</entry>
<entry>This is the name of the user running the command. In case of the system instance this resolves to <literal>root</literal>.</entry>
@@ -749,6 +742,13 @@ e! /var/cache/krb5rcache - - - 0
</refsect1>
<refsect1>
+ <title><filename>/run/</filename> and <filename>/var/run/</filename></title>
+ <para><filename>/var/run/</filename> is a deprecated symlink to <filename>/run/</filename>, and
+ applications should use the latter. <command>systemd-tmpfiles</command> will warn if
+ <filename>/var/run/</filename> is used.</para>
+ </refsect1>
+
+ <refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
diff --git a/man/udev.conf.xml b/man/udev.conf.xml
index 29c9743d1d..23a4595fa9 100644
--- a/man/udev.conf.xml
+++ b/man/udev.conf.xml
@@ -42,7 +42,7 @@
<variablelist>
<varlistentry>
- <term><varname>udev_log</varname></term>
+ <term><varname>udev_log=</varname></term>
<listitem>
<para>The log level. Valid values are the numerical
@@ -51,6 +51,55 @@
<option>debug</option>.</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>children_max=</varname></term>
+
+ <listitem>
+ <para>An integer. The maximum number of events executed in parallel.</para>
+
+ <para>This is the same as the <option>--children-max=</option> option.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>exec_delay=</varname></term>
+
+ <listitem>
+ <para>An integer. Delay the execution of <varname>RUN</varname>
+ instructions by the given number of seconds. This option
+ might be useful when debugging system crashes during
+ coldplug caused by loading non-working kernel
+ modules.</para>
+
+ <para>This is the same as the <option>--exec-delay=</option> option.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>event_timeout=</varname></term>
+
+ <listitem>
+ <para>An integer. The number of seconds to wait for events to finish. After
+ this time, the event will be terminated. The default is 180 seconds.</para>
+
+ <para>This is the same as the <option>--event-timeout=</option> option.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>resolve_names=</varname></term>
+
+ <listitem>
+ <para>Specifes when systemd-udevd should resolve names of users and groups. When set to
+ <option>early</option> (the default), names will be resolved when the rules are parsed.
+ When set to <option>late</option>, names will be resolved for every event. When set to
+ <option>never</option>, names will never be resolved and all devices will be owned by
+ root.</para>
+
+ <para>This is the same as the <option>--resolve-names=</option> option.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
<para>
diff --git a/man/udev.xml b/man/udev.xml
index 15e6d8eae1..08fedfc86c 100644
--- a/man/udev.xml
+++ b/man/udev.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -184,6 +184,8 @@
value itself contains trailing whitespace.
</para>
</listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>SYSCTL{<replaceable>kernel parameter</replaceable>}</varname></term>
<listitem>
<para>Match a kernel parameter value.
diff --git a/man/udevadm.xml b/man/udevadm.xml
index f292be4887..44be7b3f89 100644
--- a/man/udevadm.xml
+++ b/man/udevadm.xml
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -76,39 +76,40 @@
<refsect2><title>udevadm info
<arg choice="opt"><replaceable>options</replaceable></arg>
- <arg choice="opt"><replaceable>devpath</replaceable>|<replaceable>file</replaceable></arg>
+ <arg choice="opt" rep="repeat"><replaceable>devpath</replaceable>|<replaceable>file</replaceable>|<replaceable>unit</replaceable></arg>
</title>
- <para>Queries the udev database for device information
- stored in the udev database. It can also query the properties
- of a device from its sysfs representation to help creating udev
- rules that match this device.</para>
+ <para>Query the udev database for device information.</para>
+
+ <para>Positional arguments should be used to specify one or more devices. Each one may be a device name
+ (in which case it must start with <filename>/dev/</filename>), a sys path (in which case it must start
+ with <filename>/sys/</filename>), or a systemd device unit name (in which case it must end with
+ <literal>.device</literal>, see
+ <citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
+ </para>
+
<variablelist>
<varlistentry>
<term><option>-q</option></term>
<term><option>--query=<replaceable>TYPE</replaceable></option></term>
<listitem>
- <para>Query the database for the specified type of device
- data. It needs the <option>--path</option> or
- <option>--name</option> to identify the specified device.
+ <para>Query the database for the specified type of device data.
Valid <replaceable>TYPE</replaceable>s are:
<constant>name</constant>, <constant>symlink</constant>,
<constant>path</constant>, <constant>property</constant>,
<constant>all</constant>.</para>
</listitem>
</varlistentry>
+
<varlistentry>
<term><option>-p</option></term>
<term><option>--path=<replaceable>DEVPATH</replaceable></option></term>
<listitem>
- <para>The <filename>/sys</filename> path of the device to
- query, e.g.
- <filename><optional>/sys</optional>/class/block/sda</filename>.
- Note that this option usually is not very useful, since
- <command>udev</command> can guess the type of the
- argument, so <command>udevadm info
- --path=/class/block/sda</command> is equivalent to
- <command>udevadm info /sys/class/block/sda</command>.</para>
+ <para>The <filename>/sys</filename> path of the device to query, e.g.
+ <filename><optional>/sys</optional>/class/block/sda</filename>. This option is an alternative to
+ the positional argument with a <filename>/sys/</filename> prefix. <command>udevadm info
+ --path=/class/block/sda</command> is equivalent to <command>udevadm info
+ /sys/class/block/sda</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -116,11 +117,9 @@
<term><option>--name=<replaceable>FILE</replaceable></option></term>
<listitem>
<para>The name of the device node or a symlink to query,
- e.g. <filename><optional>/dev</optional>/sda</filename>.
- Note that this option usually is not very useful, since
- <command>udev</command> can guess the type of the
- argument, so <command>udevadm info --name=sda</command> is
- equivalent to <command>udevadm info /dev/sda</command>.</para>
+ e.g. <filename><optional>/dev</optional>/sda</filename>. This option is an alternative to the
+ positional argument with a <filename>/dev/</filename> prefix. <command>udevadm info
+ --name=sda</command> is equivalent to <command>udevadm info /dev/sda</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -179,17 +178,17 @@
<xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
-
- <para>In addition, an optional positional argument can be used
- to specify a device name or a sys path. It must start with
- <filename>/dev</filename> or <filename>/sys</filename>
- respectively.</para>
</refsect2>
<refsect2><title>udevadm trigger
<arg choice="opt"><replaceable>options</replaceable></arg>
- <arg choice="opt" rep="repeat"><replaceable>devpath</replaceable>|<replaceable>file</replaceable></arg></title>
+ <arg choice="opt" rep="repeat"><replaceable>devpath</replaceable>|<replaceable>file</replaceable>|<replaceable>unit</replaceable></arg>
+ </title>
<para>Request device events from the kernel. Primarily used to replay events at system coldplug time.</para>
+
+ <para>Takes one or more device specifications as arguments. See the description of <command>info</command>
+ above.</para>
+
<variablelist>
<varlistentry>
<term><option>-v</option></term>
diff --git a/man/user-system-options.xml b/man/user-system-options.xml
index eb875661de..4ad2d3b16d 100644
--- a/man/user-system-options.xml
+++ b/man/user-system-options.xml
@@ -33,12 +33,13 @@
<para>Execute the operation remotely. Specify a hostname, or a
username and hostname separated by <literal>@</literal>, to
connect to. The hostname may optionally be suffixed by a
- container name, separated by <literal>:</literal>, which
+ port ssh is listening on, seperated by <literal>:</literal>, and then a
+ container name, separated by <literal>/</literal>, which
connects directly to a specific container on the specified
host. This will use SSH to talk to the remote machine manager
instance. Container names may be enumerated with
<command>machinectl -H
- <replaceable>HOST</replaceable></command>.</para>
+ <replaceable>HOST</replaceable></command>. Put IPv6 addresses in brackets.</para>
</listitem>
</varlistentry>
diff --git a/man/user@.service.xml b/man/user@.service.xml
new file mode 100644
index 0000000000..30af3c8bf8
--- /dev/null
+++ b/man/user@.service.xml
@@ -0,0 +1,190 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="user@.service">
+ <refentryinfo>
+ <title>user@.service</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>user@.service</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>user@.service</refname>
+ <refname>user-runtime-dir@.service</refname>
+ <refpurpose>System units to manage user processes</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>user@<replaceable>UID</replaceable>.service</filename></para>
+ <para><filename>user-runtime-dir@<replaceable>UID</replaceable>.service</filename></para>
+ <para><filename>user-<replaceable>UID</replaceable>.slice</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ system manager (PID 1) starts user manager instances as
+ <filename>user@<replaceable>UID</replaceable>.service</filename>, where the user's numerical UID
+ is used as the instance identifier. Each <command>systemd --user</command> instance manages a
+ hierarchy of its own units. See
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ a discussion of systemd units and
+ <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ for a list of units that form the basis of the unit hierarchies of system and user units.</para>
+
+ <para><filename>user@<replaceable>UID</replaceable>.service</filename> is accompanied by the
+ system unit <filename>user-runtime-dir@<replaceable>UID</replaceable>.service</filename>, which
+ creates the user's runtime directory
+ <filename>/run/user/<replaceable>UID</replaceable></filename>, and then removes it when this
+ unit is stopped.</para>
+
+ <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
+ display manager like <command>gdm</command>, in which case they form a .scope unit (see
+ <citerefentry><refentrytitle>systemd.scope</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
+ Both <filename>user@<replaceable>UID</replaceable>.service</filename> and the scope units are
+ collected under a <filename>user-<replaceable>UID</replaceable>.slice</filename>.</para>
+
+ <para>Individual <filename>user-<replaceable>UID</replaceable>.slice</filename> slices are
+ collected under <filename>user.slice</filename>, see
+ <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Controlling resources for logged-in users</title>
+
+ <para>Options that control resources available to logged-in users can be configured at a few
+ different levels. As described in the previous section, <filename>user.slice</filename> contains
+ processes of all users, so any resource limits on that slice apply to all users together. The
+ usual way to configure them would be through drop-ins, e.g. <filename
+ noindex='true'>/etc/systemd/system/user.slice.d/resources.conf</filename>.
+ </para>
+
+ <para>The processes of a single user are collected under
+ <filename>user-<replaceable>UID</replaceable>.slice</filename>. Resource limits for that user
+ can be configured through drop-ins for that unit, e.g. <filename
+ noindex='true'>/etc/systemd/system/user-1000.slice.d/resources.conf</filename>. If the limits
+ should apply to all users instead, they may be configured through drop-ins for the truncated
+ unit name, <filename>user-.slice</filename>. For example, configuration in <filename
+ noindex='true'>/etc/systemd/system/user-.slice.d/resources.conf</filename> is included in all
+ <filename>user-<replaceable>UID</replaceable>.slice</filename> units, see
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for a discussion of the drop-in mechanism.</para>
+
+ <para>When a user logs in and a .scope unit is created for the session (see previous section),
+ the creation of the scope may be managed through
+ <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ This PAM module communicates with
+ <citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ to create the session scope and provide access to hardware resources. Resource limits for the
+ scope may be configured through the PAM module configuration, see
+ <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ Configuring them through the normal unit configuration is also possible, but since
+ the name of the slice unit is generally unpredictable, this is less useful.</para>
+
+ <para>In general any resources that apply to units may be set for
+ <filename>user@<replaceable>UID</replaceable>.service</filename> and the slice
+ units discussed above, see
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for an overview.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+ <example>
+ <title>Hierarchy of control groups with two logged in users</title>
+
+ <programlisting>$ systemd-cgls
+Control group /:
+-.slice
+├─user.slice
+│ ├─user-1000.slice
+│ │ ├─user@1000.service
+│ │ │ ├─pulseaudio.service
+│ │ │ │ └─2386 /usr/bin/pulseaudio --daemonize=no
+│ │ │ └─gnome-terminal-server.service
+│ │ │ └─init.scope
+│ │ │ ├─ 4127 /usr/libexec/gnome-terminal-server
+│ │ │ └─ 4198 zsh
+│ │ …
+│ │ └─session-4.scope
+│ │ ├─ 1264 gdm-session-worker [pam/gdm-password]
+│ │ ├─ 2339 /usr/bin/gnome-shell
+│ │ …
+│ │ ├─session-19.scope
+│ │ ├─6497 sshd: zbyszek [priv]
+│ │ ├─6502 sshd: zbyszek@pts/6
+│ │ ├─6509 -zsh
+│ │ └─6602 systemd-cgls --no-pager
+│ …
+│ └─user-1001.slice
+│ ├─session-20.scope
+│ │ ├─6675 sshd: guest [priv]
+│ │ ├─6708 sshd: guest@pts/6
+│ │ └─6717 -bash
+│ └─user@1001.service
+│ ├─init.scope
+│ │ ├─6680 /usr/lib/systemd/systemd --user
+│ │ └─6688 (sd-pam)
+│ └─sleep.service
+│ └─6706 /usr/bin/sleep 30
+…</programlisting>
+ <para>User with UID 1000 is logged in using <command>gdm</command> (<filename
+ noindex='true'>session-4.scope</filename>) and
+ <citerefentry><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ (<filename noindex='true'>session-19.scope</filename>), and also has a user manager instance
+ running (<filename noindex='true'>user@1000.service</filename>). User with UID 1001 is logged
+ in using <command>ssh</command> (<filename noindex='true'>session-20.scope</filename>) and
+ also has a user manager instance running (<filename
+ noindex='true'>user@1001.service</filename>). Those are all (leaf) system units, and form
+ part of the slice hierarchy, with <filename noindex='true'>user-1000.slice</filename> and
+ <filename noindex='true'>user-1001.slice</filename> below <filename
+ noindex='true'>user.slice</filename>. User units are visible below the
+ <filename>user@.service</filename> instances (<filename
+ noindex='true'>pulseaudio.service</filename>, <filename
+ noindex='true'>gnome-terminal-server.service</filename>, <filename
+ noindex='true'>init.scope</filename>, <filename noindex='true'>sleep.service</filename>).
+ </para>
+ </example>
+
+ <example>
+ <title>Default user resource limits</title>
+
+ <programlisting>$ systemctl cat user-1000.slice
+# /usr/lib/systemd/system/user-.slice.d/10-defaults.conf
+# …
+[Unit]
+Description=User Slice of UID %j
+After=systemd-user-sessions.service
+
+[Slice]
+TasksMax=33%</programlisting>
+ <para>The <filename>user-<replaceable>UID</replaceable>.slice</filename> units by default don't
+ have a unit file. The resource limits are set through a drop-in, which can be easily replaced
+ or extended following standard drop-in mechanisms discussed in the first section.</para>
+ </example>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/meson.build b/meson.build
index 04331dd41a..b338886c38 100644
--- a/meson.build
+++ b/meson.build
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1+
project('systemd', 'c',
- version : '239',
+ version : '240',
license : 'LGPLv2+',
default_options: [
'c_std=gnu99',
@@ -9,11 +9,11 @@ project('systemd', 'c',
'sysconfdir=/etc',
'localstatedir=/var',
],
- meson_version : '>= 0.44',
+ meson_version : '>= 0.46',
)
-libsystemd_version = '0.23.0'
-libudev_version = '1.6.11'
+libsystemd_version = '0.24.0'
+libudev_version = '1.6.12'
# We need the same data in two different formats, ugh!
# Also, for hysterical reasons, we use different variable
@@ -27,6 +27,13 @@ substs = configuration_data()
substs.set('PACKAGE_URL', 'https://www.freedesktop.org/wiki/Software/systemd')
substs.set('PACKAGE_VERSION', meson.project_version())
+want_ossfuzz = get_option('oss-fuzz')
+want_libfuzzer = get_option('llvm-fuzz')
+if want_ossfuzz and want_libfuzzer
+ error('only one of oss-fuzz and llvm-fuzz can be specified')
+endif
+fuzzer_build = want_ossfuzz or want_libfuzzer
+
#####################################################################
# Try to install the git pre-commit hook
@@ -66,6 +73,10 @@ sysvrcnd_path = get_option('sysvrcnd-path')
conf.set10('HAVE_SYSV_COMPAT', sysvinit_path != '' and sysvrcnd_path != '',
description : 'SysV init scripts and rcN.d links are supported')
+conf.set10('BUMP_PROC_SYS_FS_FILE_MAX', get_option('bump-proc-sys-fs-file-max'))
+conf.set10('BUMP_PROC_SYS_FS_NR_OPEN', get_option('bump-proc-sys-fs-nr-open'))
+conf.set('HIGH_RLIMIT_NOFILE', 512*1024)
+
# join_paths ignore the preceding arguments if an absolute component is
# encountered, so this should canonicalize various paths when they are
# absolute or relative.
@@ -90,8 +101,8 @@ if rootlibdir == ''
endif
# Dirs of external packages
-pkgconfigdatadir = join_paths(datadir, 'pkgconfig')
-pkgconfiglibdir = join_paths(libdir, 'pkgconfig')
+pkgconfigdatadir = get_option('pkgconfigdatadir') == '' ? join_paths(datadir, 'pkgconfig') : get_option('pkgconfigdatadir')
+pkgconfiglibdir = get_option('pkgconfiglibdir') == '' ? join_paths(libdir, 'pkgconfig') : get_option('pkgconfiglibdir')
polkitpolicydir = join_paths(datadir, 'polkit-1/actions')
polkitrulesdir = join_paths(datadir, 'polkit-1/rules.d')
polkitpkladir = join_paths(localstatedir, 'lib/polkit-1/localauthority/10-vendor.d')
@@ -206,6 +217,8 @@ conf.set_quoted('SYSTEM_SHUTDOWN_PATH', systemshutdowndir)
conf.set_quoted('SYSTEM_SLEEP_PATH', systemsleepdir)
conf.set_quoted('SYSTEMD_KBD_MODEL_MAP', join_paths(pkgdatadir, 'kbd-model-map'))
conf.set_quoted('SYSTEMD_LANGUAGE_FALLBACK_MAP', join_paths(pkgdatadir, 'language-fallback-map'))
+conf.set_quoted('SYSTEMD_TEST_DATA', join_paths(testsdir, 'testdata'))
+conf.set_quoted('SYSTEMD_CATALOG_DIR', catalogdir)
conf.set_quoted('UDEVLIBEXECDIR', udevlibexecdir)
conf.set_quoted('POLKIT_AGENT_BINARY_PATH', join_paths(bindir, 'pkttyagent'))
conf.set_quoted('LIBDIR', libdir)
@@ -214,16 +227,14 @@ conf.set_quoted('ROOTLIBEXECDIR', rootlibexecdir)
conf.set_quoted('BOOTLIBDIR', bootlibdir)
conf.set_quoted('SYSTEMD_PULL_PATH', join_paths(rootlibexecdir, 'systemd-pull'))
conf.set_quoted('SYSTEMD_IMPORT_PATH', join_paths(rootlibexecdir, 'systemd-import'))
+conf.set_quoted('SYSTEMD_IMPORT_FS_PATH', join_paths(rootlibexecdir, 'systemd-import-fs'))
conf.set_quoted('SYSTEMD_EXPORT_PATH', join_paths(rootlibexecdir, 'systemd-export'))
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('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default ? 'true' : 'false')
+conf.set10('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default)
conf.set_quoted('MEMORY_ACCOUNTING_DEFAULT_YES_NO', memory_accounting_default ? 'yes' : 'no')
-conf.set_quoted('ABS_BUILD_DIR', meson.build_root())
-conf.set_quoted('ABS_SRC_DIR', meson.source_root())
-
substs.set('prefix', prefixdir)
substs.set('exec_prefix', prefixdir)
substs.set('libdir', libdir)
@@ -263,6 +274,7 @@ substs.set('SYSTEM_SYSVRCND_PATH', sysvrcnd_path)
substs.set('RC_LOCAL_SCRIPT_PATH_START', get_option('rc-local'))
substs.set('RC_LOCAL_SCRIPT_PATH_STOP', get_option('halt-local'))
substs.set('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default ? 'yes' : 'no')
+substs.set('HIGH_RLIMIT_NOFILE', conf.get('HIGH_RLIMIT_NOFILE'))
#####################################################################
@@ -271,24 +283,22 @@ pkgconfig = import('pkgconfig')
check_compilation_sh = find_program('tools/meson-check-compilation.sh')
meson_build_sh = find_program('tools/meson-build.sh')
-if get_option('tests') != 'false'
- cxx = find_program('c++', required : false)
- if cxx.found()
- # Used only for tests
- add_languages('cpp')
- endif
-endif
+want_tests = get_option('tests')
+slow_tests = want_tests != 'false' and get_option('slow-tests')
+install_tests = get_option('install-tests')
-want_ossfuzz = get_option('oss-fuzz')
-want_libfuzzer = get_option('llvm-fuzz')
-fuzzer_build = want_ossfuzz or want_libfuzzer
-if want_ossfuzz and want_libfuzzer
- error('only one of oss-fuzz and llvm-fuzz can be specified')
+cxx = find_program('c++', required : fuzzer_build)
+if cxx.found()
+ # Used only for tests
+ add_languages('cpp')
+ cxx_cmd = ' '.join(meson.get_compiler('cpp').cmd_array())
+else
+ cxx_cmd = ''
endif
+
if want_libfuzzer
fuzzing_engine = meson.get_compiler('cpp').find_library('Fuzzer')
-endif
-if want_ossfuzz
+elif want_ossfuzz
fuzzing_engine = meson.get_compiler('cpp').find_library('FuzzingEngine')
endif
@@ -321,6 +331,20 @@ possible_cc_flags = [
'-Werror=shift-overflow=2',
'-Wdate-time',
'-Wnested-externs',
+
+ # negative arguments are correctly detected starting with meson 0.46.
+ '-Wno-unused-parameter',
+ '-Wno-missing-field-initializers',
+ '-Wno-unused-result',
+ '-Wno-format-signedness',
+
+ # work-around for gcc 7.1 turning this on on its own.
+ '-Wno-error=nonnull',
+
+ # Disable -Wmaybe-uninitialized, since it's noisy on gcc 8 with
+ # optimizations enabled, producing essentially false positives.
+ '-Wno-maybe-uninitialized',
+
'-ffast-math',
'-fno-common',
'-fdiagnostics-show-option',
@@ -362,19 +386,7 @@ if get_option('buildtype') != 'debug'
endif
add_project_arguments(cc.get_supported_arguments(possible_cc_flags), language : 'c')
-
-# "negative" arguments: gcc on purpose does not return an error for "-Wno-"
-# arguments, just emits a warning. So test for the "positive" version instead.
-foreach arg : ['unused-parameter',
- 'missing-field-initializers',
- 'unused-result',
- 'format-signedness',
- 'error=nonnull', # work-around for gcc 7.1 turning this on on its own
- ]
- if cc.has_argument('-W' + arg)
- add_project_arguments('-Wno-' + arg, language : 'c')
- endif
-endforeach
+add_project_link_arguments(cc.get_supported_link_arguments(possible_link_flags), language : 'c')
if cc.compiles('''
#include <time.h>
@@ -385,22 +397,10 @@ if cc.compiles('''
struct timespec now;
return 0;
}
-''', name : '-Werror=shadow with local shadowing')
+''', args: '-Werror=shadow', name : '-Werror=shadow with local shadowing')
add_project_arguments('-Werror=shadow', language : 'c')
endif
-link_test_c = files('tools/meson-link-test.c')
-
-foreach arg : possible_link_flags
- have = run_command(check_compilation_sh,
- cc.cmd_array(), '-x', 'c', arg,
- '-include', link_test_c).returncode() == 0
- message('Linking with @0@ supported: @1@'.format(arg, have ? 'yes' : 'no'))
- if have
- add_project_link_arguments(arg, language : 'c')
- endif
-endforeach
-
cpp = ' '.join(cc.cmd_array()) + ' -E'
#####################################################################
@@ -421,49 +421,118 @@ decl_headers = '''
#include <uchar.h>
#include <linux/ethtool.h>
#include <linux/fib_rules.h>
-#include <linux/stat.h>
#include <sys/stat.h>
'''
-# FIXME: key_serial_t is only defined in keyutils.h, this is bound to fail
foreach decl : ['char16_t',
'char32_t',
- 'key_serial_t',
- 'struct ethtool_link_settings',
'struct fib_rule_uid_range',
+ 'struct fib_rule_port_range',
'struct statx',
]
# We get -1 if the size cannot be determined
- have = cc.sizeof(decl, prefix : decl_headers) > 0
+ have = cc.sizeof(decl, prefix : decl_headers, args : '-D_GNU_SOURCE') > 0
+
+ if decl == 'struct statx'
+ if have
+ want_linux_stat_h = false
+ else
+ have = cc.sizeof(decl,
+ prefix : decl_headers + '#include <linux/stat.h>',
+ args : '-D_GNU_SOURCE') > 0
+ want_linux_stat_h = have
+ endif
+ endif
+
conf.set10('HAVE_' + decl.underscorify().to_upper(), have)
endforeach
-foreach decl : [['IFLA_INET6_ADDR_GEN_MODE', 'linux/if_link.h'],
- ['IN6_ADDR_GEN_MODE_STABLE_PRIVACY', 'linux/if_link.h'],
- ['IFLA_VRF_TABLE', 'linux/if_link.h'],
- ['IFLA_MACVLAN_FLAGS', 'linux/if_link.h'],
- ['IFLA_IPVLAN_FLAGS', 'linux/if_link.h'],
- ['IFLA_PHYS_PORT_ID', 'linux/if_link.h'],
- ['IFLA_BOND_AD_INFO', 'linux/if_link.h'],
- ['IFLA_VLAN_PROTOCOL', 'linux/if_link.h'],
- ['IFLA_VXLAN_REMCSUM_NOPARTIAL', 'linux/if_link.h'],
- ['IFLA_VXLAN_GPE', 'linux/if_link.h'],
- ['IFLA_GENEVE_LABEL', 'linux/if_link.h'],
+conf.set10('WANT_LINUX_STAT_H', want_linux_stat_h)
+
+foreach decl : [['ETHTOOL_LINK_MODE_10baseT_Half_BIT', 'linux/ethtool.h'],
+ ['ETHTOOL_LINK_MODE_25000baseCR_Full_BIT', 'linux/ethtool.h'],
+ ['ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT', 'linux/ethtool.h'],
+ ['ETHTOOL_LINK_MODE_1000baseX_Full_BIT', 'linux/ethtool.h'],
+ ['ETHTOOL_LINK_MODE_2500baseT_Full_BIT', 'linux/ethtool.h'],
+ ['ETHTOOL_LINK_MODE_FEC_NONE_BIT', 'linux/ethtool.h'],
+ ['FRA_TUN_ID', 'linux/fib_rules.h'],
+ ['FRA_SUPPRESS_PREFIXLEN', 'linux/fib_rules.h'],
+ ['FRA_PAD', 'linux/fib_rules.h'],
+ ['FRA_L3MDEV', 'linux/fib_rules.h'],
+ ['FRA_UID_RANGE', 'linux/fib_rules.h'],
+ ['FRA_DPORT_RANGE', 'linux/fib_rules.h'],
+ ['FOU_ATTR_REMCSUM_NOPARTIAL', 'linux/fou.h'],
+ ['FOU_CMD_GET', 'linux/fou.h'],
+ ['IFA_FLAGS', 'linux/if_addr.h'],
+ ['IFLA_BRIDGE_VLAN_TUNNEL_INFO', 'linux/if_bridge.h'],
+ ['IFLA_INET6_ADDR_GEN_MODE', 'linux/if_link.h'],
+ ['IN6_ADDR_GEN_MODE_STABLE_PRIVACY', 'linux/if_link.h'],
+ ['IN6_ADDR_GEN_MODE_RANDOM', 'linux/if_link.h'],
+ ['IFLA_IPVLAN_MODE', 'linux/if_link.h'],
+ ['IPVLAN_MODE_L3S', 'linux/if_link.h'],
+ ['IFLA_IPVLAN_FLAGS', 'linux/if_link.h'],
+ ['IFLA_PHYS_PORT_ID', 'linux/if_link.h'],
+ ['IFLA_CARRIER_CHANGES', 'linux/if_link.h'],
+ ['IFLA_PHYS_SWITCH_ID', 'linux/if_link.h'],
+ ['IFLA_LINK_NETNSID', 'linux/if_link.h'],
+ ['IFLA_PHYS_PORT_NAME', 'linux/if_link.h'],
+ ['IFLA_PROTO_DOWN', 'linux/if_link.h'],
+ ['IFLA_GSO_MAX_SIZE', 'linux/if_link.h'],
+ ['IFLA_PAD', 'linux/if_link.h'],
+ ['IFLA_XDP', 'linux/if_link.h'],
+ ['IFLA_EVENT', 'linux/if_link.h'],
+ ['IFLA_IF_NETNSID', 'linux/if_link.h'],
+ ['IFLA_TARGET_NETNSID', 'linux/if_link.h'],
+ ['IFLA_NEW_IFINDEX', 'linux/if_link.h'],
+ ['IFLA_MAX_MTU', 'linux/if_link.h'],
+ ['IFLA_BOND_ACTIVE_SLAVE', 'linux/if_link.h'],
+ ['IFLA_BOND_AD_INFO', 'linux/if_link.h'],
+ ['IFLA_BOND_AD_ACTOR_SYSTEM', 'linux/if_link.h'],
+ ['IFLA_BOND_TLB_DYNAMIC_LB', 'linux/if_link.h'],
+ ['IFLA_VXLAN_UDP_ZERO_CSUM6_RX', 'linux/if_link.h'],
+ ['IFLA_VXLAN_REMCSUM_NOPARTIAL', 'linux/if_link.h'],
+ ['IFLA_VXLAN_COLLECT_METADATA', 'linux/if_link.h'],
+ ['IFLA_VXLAN_LABEL', 'linux/if_link.h'],
+ ['IFLA_VXLAN_GPE', 'linux/if_link.h'],
+ ['IFLA_VXLAN_TTL_INHERIT', 'linux/if_link.h'],
+ ['IFLA_GENEVE_TOS', 'linux/if_link.h'],
+ ['IFLA_GENEVE_COLLECT_METADATA', 'linux/if_link.h'],
+ ['IFLA_GENEVE_REMOTE6', 'linux/if_link.h'],
+ ['IFLA_GENEVE_UDP_ZERO_CSUM6_RX', 'linux/if_link.h'],
+ ['IFLA_GENEVE_LABEL', 'linux/if_link.h'],
+ ['IFLA_GENEVE_TTL_INHERIT', 'linux/if_link.h'],
+ ['IFLA_BR_MAX_AGE', 'linux/if_link.h'],
+ ['IFLA_BR_PRIORITY', 'linux/if_link.h'],
+ ['IFLA_BR_VLAN_PROTOCOL', 'linux/if_link.h'],
+ ['IFLA_BR_VLAN_DEFAULT_PVID', 'linux/if_link.h'],
+ ['IFLA_BR_VLAN_STATS_ENABLED', 'linux/if_link.h'],
+ ['IFLA_BR_MCAST_STATS_ENABLED', 'linux/if_link.h'],
+ ['IFLA_BR_MCAST_MLD_VERSION', 'linux/if_link.h'],
+ ['IFLA_BR_VLAN_STATS_PER_PORT', 'linux/if_link.h'],
+ ['IFLA_BRPORT_LEARNING_SYNC', 'linux/if_link.h'],
+ ['IFLA_BRPORT_PROXYARP_WIFI', 'linux/if_link.h'],
+ ['IFLA_BRPORT_MULTICAST_ROUTER', 'linux/if_link.h'],
+ ['IFLA_BRPORT_PAD', 'linux/if_link.h'],
+ ['IFLA_BRPORT_MCAST_FLOOD', 'linux/if_link.h'],
+ ['IFLA_BRPORT_VLAN_TUNNEL', 'linux/if_link.h'],
+ ['IFLA_BRPORT_BCAST_FLOOD', 'linux/if_link.h'],
+ ['IFLA_BRPORT_NEIGH_SUPPRESS', 'linux/if_link.h'],
+ ['IFLA_BRPORT_ISOLATED', 'linux/if_link.h'],
+ ['IFLA_BRPORT_BACKUP_PORT', 'linux/if_link.h'],
+ ['IFLA_VRF_TABLE', 'linux/if_link.h'],
# if_tunnel.h is buggy and cannot be included on its own
- ['IFLA_VTI_REMOTE', 'linux/if_tunnel.h', '#include <net/if.h>'],
- ['IFLA_IPTUN_ENCAP_DPORT', 'linux/if_tunnel.h', '#include <net/if.h>'],
- ['IFLA_GRE_ENCAP_DPORT', 'linux/if_tunnel.h', '#include <net/if.h>'],
- ['IFLA_BRIDGE_VLAN_INFO', 'linux/if_bridge.h'],
- ['IFLA_BRPORT_PROXYARP', 'linux/if_link.h'],
- ['IFLA_BRPORT_LEARNING_SYNC', 'linux/if_link.h'],
- ['IFLA_BR_VLAN_DEFAULT_PVID', 'linux/if_link.h'],
- ['IPVLAN_F_PRIVATE', 'linux/if_link.h'],
- ['NDA_IFINDEX', 'linux/neighbour.h'],
- ['IFA_FLAGS', 'linux/if_addr.h'],
- ['FRA_UID_RANGE', 'linux/fib_rules.h'],
- ['LO_FLAGS_PARTSCAN', 'linux/loop.h'],
- ['VXCAN_INFO_PEER', 'linux/can/vxcan.h'],
+ ['IFLA_VTI_FWMARK', 'linux/if_tunnel.h', '#include <net/if.h>'],
+ ['IFLA_IPTUN_ENCAP_DPORT', 'linux/if_tunnel.h', '#include <net/if.h>'],
+ ['IFLA_IPTUN_COLLECT_METADATA', 'linux/if_tunnel.h', '#include <net/if.h>'],
+ ['IFLA_IPTUN_FWMARK', 'linux/if_tunnel.h', '#include <net/if.h>'],
+ ['IFLA_GRE_ENCAP_DPORT', 'linux/if_tunnel.h', '#include <net/if.h>'],
+ ['IFLA_GRE_COLLECT_METADATA', 'linux/if_tunnel.h', '#include <net/if.h>'],
+ ['IFLA_GRE_IGNORE_DF', 'linux/if_tunnel.h', '#include <net/if.h>'],
+ ['IFLA_GRE_FWMARK', 'linux/if_tunnel.h', '#include <net/if.h>'],
+ ['IFLA_GRE_ERSPAN_INDEX', 'linux/if_tunnel.h', '#include <net/if.h>'],
+ ['IFLA_GRE_ERSPAN_HWID', 'linux/if_tunnel.h', '#include <net/if.h>'],
+ ['LO_FLAGS_PARTSCAN', 'linux/loop.h'],
]
prefix = decl.length() > 2 ? decl[2] : ''
have = cc.has_header_symbol(decl[1], decl[0], prefix : prefix)
@@ -594,9 +663,11 @@ if not cc.has_header('sys/capability.h')
error('POSIX caps headers not found')
endif
foreach header : ['crypt.h',
- 'linux/btrfs.h',
+ 'linux/btrfs_tree.h',
+ 'linux/fou.h',
'linux/memfd.h',
'linux/vm_sockets.h',
+ 'linux/can/vxcan.h',
'sys/auxv.h',
'valgrind/memcheck.h',
'valgrind/valgrind.h',
@@ -623,53 +694,55 @@ else
conf.set('DEFAULT_HIERARCHY', 'CGROUP_UNIFIED_ALL')
endif
+default_net_naming_scheme = get_option('default-net-naming-scheme')
+conf.set_quoted('DEFAULT_NET_NAMING_SCHEME', default_net_naming_scheme)
+
time_epoch = get_option('time-epoch')
-if time_epoch == ''
+if time_epoch == -1
NEWS = files('NEWS')
- time_epoch = run_command(stat, '-c', '%Y', NEWS).stdout()
+ time_epoch = run_command(stat, '-c', '%Y', NEWS).stdout().to_int()
endif
-time_epoch = time_epoch.to_int()
conf.set('TIME_EPOCH', time_epoch)
system_uid_max = get_option('system-uid-max')
-if system_uid_max == ''
+if system_uid_max == -1
system_uid_max = run_command(
awk,
'/^\s*SYS_UID_MAX\s+/ { uid=$2 } END { print uid }',
'/etc/login.defs').stdout().strip()
if system_uid_max == ''
- system_uid_max = '999'
+ system_uid_max = 999
+ else
+ system_uid_max = system_uid_max.to_int()
endif
endif
-system_uid_max = system_uid_max.to_int()
conf.set('SYSTEM_UID_MAX', system_uid_max)
substs.set('systemuidmax', system_uid_max)
-message('maximum system UID is @0@'.format(system_uid_max))
system_gid_max = get_option('system-gid-max')
-if system_gid_max == ''
+if system_gid_max == -1
system_gid_max = run_command(
awk,
'/^\s*SYS_GID_MAX\s+/ { gid=$2 } END { print gid }',
'/etc/login.defs').stdout().strip()
if system_gid_max == ''
- system_gid_max = '999'
+ system_gid_max = 999
+ else
+ system_gid_max = system_gid_max.to_int()
endif
endif
-system_gid_max = system_gid_max.to_int()
conf.set('SYSTEM_GID_MAX', system_gid_max)
substs.set('systemgidmax', system_gid_max)
-message('maximum system GID is @0@'.format(system_gid_max))
-dynamic_uid_min = get_option('dynamic-uid-min').to_int()
-dynamic_uid_max = get_option('dynamic-uid-max').to_int()
+dynamic_uid_min = get_option('dynamic-uid-min')
+dynamic_uid_max = get_option('dynamic-uid-max')
conf.set('DYNAMIC_UID_MIN', dynamic_uid_min)
conf.set('DYNAMIC_UID_MAX', dynamic_uid_max)
substs.set('dynamicuidmin', dynamic_uid_min)
substs.set('dynamicuidmax', dynamic_uid_max)
-container_uid_base_min = get_option('container-uid-base-min').to_int()
-container_uid_base_max = get_option('container-uid-base-max').to_int()
+container_uid_base_min = get_option('container-uid-base-min')
+container_uid_base_max = get_option('container-uid-base-max')
conf.set('CONTAINER_UID_BASE_MIN', container_uid_base_min)
conf.set('CONTAINER_UID_BASE_MAX', container_uid_base_max)
substs.set('containeruidbasemin', container_uid_base_min)
@@ -678,41 +751,43 @@ substs.set('containeruidbasemax', container_uid_base_max)
nobody_user = get_option('nobody-user')
nobody_group = get_option('nobody-group')
-getent_result = run_command('getent', 'passwd', '65534')
-if getent_result.returncode() == 0
- name = getent_result.stdout().split(':')[0]
- if name != nobody_user
- warning('\n' +
- 'The local user with the UID 65534 does not match the configured user name "@0@" of the nobody user (its name is @1@).\n'.format(nobody_user, name) +
- 'Your build will result in an user table setup that is incompatible with the local system.')
+if not meson.is_cross_build()
+ getent_result = run_command('getent', 'passwd', '65534')
+ if getent_result.returncode() == 0
+ name = getent_result.stdout().split(':')[0]
+ if name != nobody_user
+ warning('\n' +
+ 'The local user with the UID 65534 does not match the configured user name "@0@" of the nobody user (its name is @1@).\n'.format(nobody_user, name) +
+ 'Your build will result in an user table setup that is incompatible with the local system.')
+ endif
endif
-endif
-id_result = run_command('id', '-u', nobody_user)
-if id_result.returncode() == 0
- id = id_result.stdout().to_int()
- if id != 65534
- warning('\n' +
- 'The local user with the configured user name "@0@" of the nobody user does not have UID 65534 (it has @1@).\n'.format(nobody_user, id) +
- 'Your build will result in an user table setup that is incompatible with the local system.')
+ id_result = run_command('id', '-u', nobody_user)
+ if id_result.returncode() == 0
+ id = id_result.stdout().to_int()
+ if id != 65534
+ warning('\n' +
+ 'The local user with the configured user name "@0@" of the nobody user does not have UID 65534 (it has @1@).\n'.format(nobody_user, id) +
+ 'Your build will result in an user table setup that is incompatible with the local system.')
+ endif
endif
-endif
-getent_result = run_command('getent', 'group', '65534')
-if getent_result.returncode() == 0
- name = getent_result.stdout().split(':')[0]
- if name != nobody_group
- warning('\n' +
- 'The local group with the GID 65534 does not match the configured group name "@0@" of the nobody group (its name is @1@).\n'.format(nobody_group, name) +
- 'Your build will result in an group table setup that is incompatible with the local system.')
+ getent_result = run_command('getent', 'group', '65534')
+ if getent_result.returncode() == 0
+ name = getent_result.stdout().split(':')[0]
+ if name != nobody_group
+ warning('\n' +
+ 'The local group with the GID 65534 does not match the configured group name "@0@" of the nobody group (its name is @1@).\n'.format(nobody_group, name) +
+ 'Your build will result in an group table setup that is incompatible with the local system.')
+ endif
endif
-endif
-id_result = run_command('id', '-g', nobody_group)
-if id_result.returncode() == 0
- id = id_result.stdout().to_int()
- if id != 65534
- warning('\n' +
- 'The local group with the configured group name "@0@" of the nobody group does not have UID 65534 (it has @1@).\n'.format(nobody_group, id) +
- 'Your build will result in an group table setup that is incompatible with the local system.')
+ id_result = run_command('id', '-g', nobody_group)
+ if id_result.returncode() == 0
+ id = id_result.stdout().to_int()
+ if id != 65534
+ warning('\n' +
+ 'The local group with the configured group name "@0@" of the nobody group does not have UID 65534 (it has @1@).\n'.format(nobody_group, id) +
+ 'Your build will result in an group table setup that is incompatible with the local system.')
+ endif
endif
endif
if nobody_user != nobody_group and not (nobody_user == 'nobody' and nobody_group == 'nogroup')
@@ -731,17 +806,15 @@ conf.set('TTY_GID', tty_gid)
substs.set('TTY_GID', tty_gid)
# Ensure provided GID argument is numeric, otherwise fallback to default assignment
-if get_option('users-gid') != ''
- users_gid = get_option('users-gid').to_int()
-else
- users_gid = '-'
-endif
-substs.set('USERS_GID', users_gid)
+users_gid = get_option('users-gid')
+substs.set('USERS_GID', users_gid < 0 ? '-' : users_gid)
conf.set10('ENABLE_ADM_GROUP', get_option('adm-group'))
conf.set10('ENABLE_WHEEL_GROUP', get_option('wheel-group'))
-substs.set('DEV_KVM_MODE', get_option('dev-kvm-mode'))
+dev_kvm_mode = get_option('dev-kvm-mode')
+substs.set('DEV_KVM_MODE', dev_kvm_mode)
+conf.set10('DEV_KVM_UACCESS', dev_kvm_mode != '0666')
substs.set('GROUP_RENDER_MODE', get_option('group-render-mode'))
kill_user_processes = get_option('default-kill-user-processes')
@@ -764,19 +837,28 @@ substs.set('DEBUGTTY', get_option('debug-tty'))
enable_debug_hashmap = false
enable_debug_mmap_cache = false
-foreach name : get_option('debug')
+enable_debug_siphash = false
+enable_debug_udev = false
+foreach name : get_option('debug-extra')
if name == 'hashmap'
enable_debug_hashmap = true
elif name == 'mmap-cache'
enable_debug_mmap_cache = true
+ elif name == 'siphash'
+ enable_debug_siphash = true
+ elif name == 'udev'
+ enable_debug_udev = true
else
message('unknown debug option "@0@", ignoring'.format(name))
endif
endforeach
conf.set10('ENABLE_DEBUG_HASHMAP', enable_debug_hashmap)
conf.set10('ENABLE_DEBUG_MMAP_CACHE', enable_debug_mmap_cache)
+conf.set10('ENABLE_DEBUG_SIPHASH', enable_debug_siphash)
+conf.set10('ENABLE_DEBUG_UDEV', enable_debug_udev)
conf.set10('VALGRIND', get_option('valgrind'))
+conf.set10('LOG_TRACE', get_option('log-trace'))
#####################################################################
@@ -922,11 +1004,17 @@ if want_libcryptsetup != 'false' and not fuzzer_build
version : '>= 1.6.0',
required : want_libcryptsetup == 'true')
have = libcryptsetup.found()
+ have_sector = cc.has_member(
+ 'struct crypt_params_plain',
+ 'sector_size',
+ prefix : '#include <libcryptsetup.h>')
else
have = false
+ have_sector = false
libcryptsetup = []
endif
conf.set10('HAVE_LIBCRYPTSETUP', have)
+conf.set10('HAVE_LIBCRYPTSETUP_SECTOR_SIZE', have_sector)
want_libcurl = get_option('libcurl')
if want_libcurl != 'false' and not fuzzer_build
@@ -1014,6 +1102,18 @@ else
endif
conf.set10('HAVE_GNUTLS', have)
+want_openssl = get_option('openssl')
+if want_openssl != 'false' and not fuzzer_build
+ libopenssl = dependency('openssl',
+ version : '>= 1.1.0',
+ required : want_openssl == 'true')
+ have = libopenssl.found()
+else
+ have = false
+ libopenssl = []
+endif
+conf.set10('HAVE_OPENSSL', have)
+
want_elfutils = get_option('elfutils')
if want_elfutils != 'false' and not fuzzer_build
libdw = dependency('libdw',
@@ -1061,6 +1161,7 @@ conf.set10('HAVE_XZ', have)
want_lz4 = get_option('lz4')
if want_lz4 != 'false' and not fuzzer_build
liblz4 = dependency('liblz4',
+ version : '>= 1.3.0',
required : want_lz4 == 'true')
have = liblz4.found()
else
@@ -1137,14 +1238,32 @@ substs.set('DEFAULT_DNSSEC_MODE', default_dnssec)
dns_over_tls = get_option('dns-over-tls')
if dns_over_tls != 'false'
- have = conf.get('HAVE_GNUTLS') == 1
- if dns_over_tls == 'true' and not have
- error('DNS-over-TLS support was requested, but dependencies are not available')
+ if dns_over_tls == 'openssl'
+ have_gnutls = false
+ else
+ have_gnutls = (conf.get('HAVE_GNUTLS') == 1 and libgnutls.version().version_compare('>= 3.5.3'))
+ if dns_over_tls == 'gnutls' and not have_gnutls
+ error('DNS-over-TLS support was requested with gnutls, but dependencies are not available')
+ endif
endif
+ if dns_over_tls == 'gnutls' or have_gnutls
+ have_openssl = false
+ else
+ have_openssl = conf.get('HAVE_OPENSSL') == 1
+ if dns_over_tls != 'auto' and not have_openssl
+ str = dns_over_tls == 'openssl' ? ' with openssl' : ''
+ error('DNS-over-TLS support was requested$0$, but dependencies are not available'.format(str))
+ endif
+ endif
+ have = have_gnutls or have_openssl
else
have = false
+ have_gnutls = false
+ have_openssl = false
endif
conf.set10('ENABLE_DNS_OVER_TLS', have)
+conf.set10('DNS_OVER_TLS_USE_GNUTLS', have_gnutls)
+conf.set10('DNS_OVER_TLS_USE_OPENSSL', have_openssl)
default_dns_over_tls = get_option('default-dns-over-tls')
if fuzzer_build
@@ -1162,7 +1281,6 @@ want_importd = get_option('importd')
if want_importd != 'false'
have = (conf.get('HAVE_LIBCURL') == 1 and
conf.get('HAVE_ZLIB') == 1 and
- conf.get('HAVE_BZIP2') == 1 and
conf.get('HAVE_XZ') == 1 and
conf.get('HAVE_GCRYPT') == 1)
if want_importd == 'true' and not have
@@ -1204,7 +1322,6 @@ foreach term : ['utmp',
'networkd',
'timedated',
'timesyncd',
- 'myhostname',
'firstboot',
'randomseed',
'backlight',
@@ -1221,17 +1338,41 @@ foreach term : ['utmp',
'smack',
'gshadow',
'idn',
+ 'nss-myhostname',
'nss-systemd']
have = get_option(term)
name = 'ENABLE_' + term.underscorify().to_upper()
conf.set10(name, have)
endforeach
+foreach tuple : [['nss-mymachines', 'machined'],
+ ['nss-resolve', 'resolve']]
+ want = get_option(tuple[0])
+ if want != 'false'
+ have = get_option(tuple[1])
+ if want == 'true' and not have
+ error('@0@ is requested but @1@ is disabled'.format(tuple[0], tuple[1]))
+ endif
+ else
+ have = false
+ endif
+ name = 'ENABLE_' + tuple[0].underscorify().to_upper()
+ conf.set10(name, have)
+endforeach
+
+enable_nss = false
+foreach term : ['ENABLE_NSS_MYHOSTNAME',
+ 'ENABLE_NSS_MYMACHINES',
+ 'ENABLE_NSS_RESOLVE',
+ 'ENABLE_NSS_SYSTEMD']
+ if conf.get(term) == 1
+ enable_nss = true
+ endif
+endforeach
+conf.set10('ENABLE_NSS', enable_nss)
+
conf.set10('ENABLE_TIMEDATECTL', get_option('timedated') or get_option('timesyncd'))
-want_tests = get_option('tests')
-install_tests = get_option('install-tests')
-slow_tests = get_option('slow-tests')
tests = []
fuzzers = []
@@ -1262,7 +1403,7 @@ if get_option('efi')
have = true
conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME)
- conf.set('SD_TPM_PCR', get_option('tpm-pcrindex').to_int())
+ conf.set('SD_TPM_PCR', get_option('tpm-pcrindex'))
else
have = false
endif
@@ -1291,15 +1432,19 @@ includes = include_directories('src/basic',
'src/core',
'src/libsystemd/sd-bus',
'src/libsystemd/sd-device',
+ 'src/libsystemd/sd-event',
'src/libsystemd/sd-hwdb',
'src/libsystemd/sd-id128',
'src/libsystemd/sd-netlink',
'src/libsystemd/sd-network',
+ 'src/libsystemd/sd-resolve',
'src/libsystemd-network',
'.')
add_project_arguments('-include', 'config.h', language : 'c')
+generate_gperfs = find_program('tools/generate-gperfs.py')
+
subdir('po')
subdir('catalog')
subdir('src/systemd')
@@ -1319,7 +1464,7 @@ libjournal_core = static_library(
libsystemd_sym_path = '@0@/@1@'.format(meson.current_source_dir(), libsystemd_sym)
libsystemd = shared_library(
'systemd',
- 'src/systemd/sd-id128.h', # pick a header file at random to work around old meson bug
+ disable_mempool_c,
version : libsystemd_version,
include_directories : includes,
link_args : ['-shared',
@@ -1345,6 +1490,7 @@ install_libsystemd_static = static_library(
journal_client_sources,
basic_sources,
basic_gcrypt_sources,
+ disable_mempool_c,
include_directories : includes,
build_by_default : static_libsystemd != 'false',
install : static_libsystemd != 'false',
@@ -1401,25 +1547,28 @@ subdir('test')
test_dlopen = executable(
'test-dlopen',
test_dlopen_c,
+ disable_mempool_c,
include_directories : includes,
link_with : [libbasic],
- dependencies : [libdl])
+ dependencies : [libdl],
+ build_by_default : want_tests != 'false')
-foreach tuple : [['myhostname', 'ENABLE_MYHOSTNAME'],
+foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'],
['systemd', 'ENABLE_NSS_SYSTEMD'],
- ['mymachines', 'ENABLE_MACHINED'],
- ['resolve', 'ENABLE_RESOLVE']]
+ ['mymachines', 'ENABLE_NSS_MYMACHINES'],
+ ['resolve', 'ENABLE_NSS_RESOLVE']]
condition = tuple[1] == '' or conf.get(tuple[1]) == 1
if condition
module = tuple[0]
sym = 'src/nss-@0@/nss-@0@.sym'.format(module)
- version_script_arg = join_paths(meson.current_source_dir(), sym)
+ version_script_arg = join_paths(meson.source_root(), sym)
nss = shared_library(
'nss_' + module,
'src/nss-@0@/nss-@0@.c'.format(module),
+ disable_mempool_c,
version : '2',
include_directories : includes,
# Note that we link NSS modules with '-z nodelete' so that mempools never get orphaned
@@ -1441,9 +1590,12 @@ foreach tuple : [['myhostname', 'ENABLE_MYHOSTNAME'],
'rm $DESTDIR@0@/libnss_@1@.so'
.format(rootlibdir, module))
- test('dlopen-nss_' + module,
- test_dlopen,
- args : [nss.full_path()]) # path to dlopen must include a slash
+ if want_tests != 'false'
+ test('dlopen-nss_' + module,
+ test_dlopen,
+ # path to dlopen must include a slash
+ args : nss.full_path())
+ endif
endif
endforeach
@@ -1481,7 +1633,7 @@ exe = executable('systemd-analyze',
libblkid],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
executable('systemd-journald',
systemd_journald_sources,
@@ -1504,7 +1656,7 @@ exe = executable('systemd-cat',
dependencies : [threads],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
exe = executable('journalctl',
journalctl_sources,
@@ -1518,7 +1670,7 @@ exe = executable('journalctl',
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
executable('systemd-getty-generator',
'src/getty-generator/getty-generator.c',
@@ -1536,6 +1688,14 @@ executable('systemd-debug-generator',
install : true,
install_dir : systemgeneratordir)
+executable('systemd-run-generator',
+ 'src/run-generator/run-generator.c',
+ include_directories : includes,
+ link_with : [libshared],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : systemgeneratordir)
+
executable('systemd-fstab-generator',
'src/fstab-generator/fstab-generator.c',
'src/core/mount-setup.c',
@@ -1580,7 +1740,7 @@ endif
if conf.get('HAVE_BLKID') == 1
executable('systemd-gpt-auto-generator',
'src/gpt-auto-generator/gpt-auto-generator.c',
- 'src/basic/blkid-util.h',
+ 'src/shared/blkid-util.h',
include_directories : includes,
link_with : [libshared],
dependencies : libblkid,
@@ -1595,7 +1755,7 @@ if conf.get('HAVE_BLKID') == 1
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
- public_programs += [exe]
+ public_programs += exe
endif
if conf.get('ENABLE_RESOLVE') == 1
@@ -1622,7 +1782,7 @@ if conf.get('ENABLE_RESOLVE') == 1
libidn],
install_rpath : rootlibexecdir,
install : true)
- public_programs += [exe]
+ public_programs += exe
meson.add_install_script(meson_make_symlink,
join_paths(bindir, 'resolvectl'),
@@ -1655,7 +1815,7 @@ if conf.get('ENABLE_LOGIND') == 1
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
- public_programs += [exe]
+ public_programs += exe
exe = executable('systemd-inhibit',
'src/login/inhibit.c',
@@ -1664,10 +1824,10 @@ if conf.get('ENABLE_LOGIND') == 1
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
- public_programs += [exe]
+ public_programs += exe
if conf.get('HAVE_PAM') == 1
- version_script_arg = join_paths(meson.current_source_dir(), pam_systemd_sym)
+ version_script_arg = join_paths(meson.source_root(), pam_systemd_sym)
pam_systemd = shared_library(
'pam_systemd',
pam_systemd_c,
@@ -1684,19 +1844,22 @@ if conf.get('ENABLE_LOGIND') == 1
install : true,
install_dir : pamlibdir)
- test('dlopen-pam_systemd',
- test_dlopen,
- args : [pam_systemd.full_path()]) # path to dlopen must include a slash
+ if want_tests != 'false'
+ test('dlopen-pam_systemd',
+ test_dlopen,
+ # path to dlopen must include a slash
+ args : pam_systemd.full_path())
+ endif
endif
-endif
-executable('systemd-user-runtime-dir',
- user_runtime_dir_sources,
- include_directories : includes,
- link_with : [libshared, liblogind_core],
- install_rpath : rootlibexecdir,
- install : true,
- install_dir : rootlibexecdir)
+ executable('systemd-user-runtime-dir',
+ user_runtime_dir_sources,
+ include_directories : includes,
+ link_with : [libshared],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootlibexecdir)
+endif
if conf.get('HAVE_PAM') == 1
executable('systemd-user-sessions',
@@ -1716,16 +1879,42 @@ if conf.get('ENABLE_EFI') == 1 and conf.get('HAVE_BLKID') == 1
dependencies : [libblkid],
install_rpath : rootlibexecdir,
install : true)
- public_programs += [exe]
+ public_programs += exe
+
+ executable('systemd-bless-boot',
+ 'src/boot/bless-boot.c',
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [libblkid],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootlibexecdir)
+
+ executable('systemd-bless-boot-generator',
+ 'src/boot/bless-boot-generator.c',
+ include_directories : includes,
+ link_with : [libshared],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : systemgeneratordir)
endif
+executable('systemd-boot-check-no-failures',
+ 'src/boot/boot-check-no-failures.c',
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [libblkid],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootlibexecdir)
+
exe = executable('systemd-socket-activate', 'src/activate/activate.c',
include_directories : includes,
link_with : [libshared],
dependencies : [threads],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
if get_option('link-systemctl-shared')
@@ -1748,7 +1937,7 @@ exe = executable('systemctl', 'src/systemctl/systemctl.c',
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
if conf.get('ENABLE_PORTABLED') == 1
executable('systemd-portabled',
@@ -1766,8 +1955,8 @@ if conf.get('ENABLE_PORTABLED') == 1
dependencies : [threads],
install_rpath : rootlibexecdir,
install : true,
- install_dir : rootlibexecdir)
- public_programs += [exe]
+ install_dir : rootbindir)
+ public_programs += exe
endif
foreach alias : ['halt', 'poweroff', 'reboot', 'runlevel', 'shutdown', 'telinit']
@@ -1875,7 +2064,7 @@ if conf.get('ENABLE_HOSTNAMED') == 1
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
- public_programs += [exe]
+ public_programs += exe
endif
if conf.get('ENABLE_LOCALED') == 1
@@ -1901,7 +2090,7 @@ if conf.get('ENABLE_LOCALED') == 1
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
- public_programs += [exe]
+ public_programs += exe
endif
if conf.get('ENABLE_TIMEDATED') == 1
@@ -1922,7 +2111,7 @@ if conf.get('ENABLE_TIMEDATECTL') == 1
link_with : [libshared],
dependencies : [libm],
install : true)
- public_programs += [exe]
+ public_programs += exe
endif
if conf.get('ENABLE_TIMESYNCD') == 1
@@ -1965,7 +2154,7 @@ if conf.get('ENABLE_MACHINED') == 1
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
- public_programs += [exe]
+ public_programs += exe
endif
if conf.get('ENABLE_IMPORTD') == 1
@@ -2003,6 +2192,14 @@ if conf.get('ENABLE_IMPORTD') == 1
install : true,
install_dir : rootlibexecdir)
+ systemd_import_fs = executable('systemd-import-fs',
+ systemd_import_fs_sources,
+ include_directories : includes,
+ link_with : [libshared],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootlibexecdir)
+
systemd_export = executable('systemd-export',
systemd_export_sources,
include_directories : includes,
@@ -2014,7 +2211,8 @@ if conf.get('ENABLE_IMPORTD') == 1
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
- public_programs += [systemd_pull, systemd_import, systemd_export]
+
+ public_programs += [systemd_pull, systemd_import, systemd_import_fs, systemd_export]
endif
if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_LIBCURL') == 1
@@ -2030,7 +2228,7 @@ if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_LIBCURL') == 1
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
- public_programs += [exe]
+ public_programs += exe
endif
if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_MICROHTTPD') == 1
@@ -2086,7 +2284,7 @@ if conf.get('ENABLE_COREDUMP') == 1
liblz4],
install_rpath : rootlibexecdir,
install : true)
- public_programs += [exe]
+ public_programs += exe
endif
if conf.get('ENABLE_BINFMT') == 1
@@ -2097,7 +2295,7 @@ if conf.get('ENABLE_BINFMT') == 1
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
- public_programs += [exe]
+ public_programs += exe
meson.add_install_script('sh', '-c',
mkdir_p.format(binfmtdir))
@@ -2196,7 +2394,7 @@ exe = executable('systemd-sysctl',
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
-public_programs += [exe]
+public_programs += exe
executable('systemd-ac-power',
'src/ac-power/ac-power.c',
@@ -2212,7 +2410,7 @@ exe = executable('systemd-detect-virt',
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
exe = executable('systemd-delta',
'src/delta/delta.c',
@@ -2220,7 +2418,7 @@ exe = executable('systemd-delta',
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
exe = executable('systemd-escape',
'src/escape/escape.c',
@@ -2229,7 +2427,7 @@ exe = executable('systemd-escape',
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
exe = executable('systemd-notify',
'src/notify/notify.c',
@@ -2238,7 +2436,7 @@ exe = executable('systemd-notify',
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
executable('systemd-volatile-root',
'src/volatile-root/volatile-root.c',
@@ -2256,13 +2454,21 @@ executable('systemd-cgroups-agent',
install : true,
install_dir : rootlibexecdir)
+exe = executable('systemd-id128',
+ 'src/id128/id128.c',
+ include_directories : includes,
+ link_with : [libshared],
+ install_rpath : rootlibexecdir,
+ install : true)
+public_programs += exe
+
exe = executable('systemd-path',
'src/path/path.c',
include_directories : includes,
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
exe = executable('systemd-ask-password',
'src/ask-password/ask-password.c',
@@ -2271,7 +2477,7 @@ exe = executable('systemd-ask-password',
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
executable('systemd-reply-password',
'src/reply-password/reply-password.c',
@@ -2288,7 +2494,7 @@ exe = executable('systemd-tty-ask-password-agent',
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
exe = executable('systemd-cgls',
'src/cgls/cgls.c',
@@ -2296,7 +2502,7 @@ exe = executable('systemd-cgls',
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
exe = executable('systemd-cgtop',
'src/cgtop/cgtop.c',
@@ -2304,7 +2510,7 @@ exe = executable('systemd-cgtop',
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
executable('systemd-initctl',
'src/initctl/initctl.c',
@@ -2320,7 +2526,7 @@ exe = executable('systemd-mount',
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
meson.add_install_script(meson_make_symlink,
'systemd-mount', join_paths(bindir, 'systemd-umount'))
@@ -2331,7 +2537,7 @@ exe = executable('systemd-run',
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
exe = executable('systemd-stdio-bridge',
'src/stdio-bridge/stdio-bridge.c',
@@ -2339,7 +2545,7 @@ exe = executable('systemd-stdio-bridge',
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
exe = executable('busctl',
'src/busctl/busctl.c',
@@ -2349,7 +2555,7 @@ exe = executable('busctl',
link_with : [libshared],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
if conf.get('ENABLE_SYSUSERS') == 1
exe = executable('systemd-sysusers',
@@ -2359,7 +2565,7 @@ if conf.get('ENABLE_SYSUSERS') == 1
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
- public_programs += [exe]
+ public_programs += exe
endif
if conf.get('ENABLE_TMPFILES') == 1
@@ -2371,12 +2577,14 @@ if conf.get('ENABLE_TMPFILES') == 1
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
- public_programs += [exe]
+ public_programs += exe
- test('test-systemd-tmpfiles',
- test_systemd_tmpfiles_py,
- args : exe.full_path())
- # https://github.com/mesonbuild/meson/issues/2681
+ if want_tests != 'false'
+ test('test-systemd-tmpfiles',
+ test_systemd_tmpfiles_py,
+ # https://github.com/mesonbuild/meson/issues/2681
+ args : exe.full_path())
+ endif
endif
if conf.get('ENABLE_HWDB') == 1
@@ -2388,7 +2596,7 @@ if conf.get('ENABLE_HWDB') == 1
install_rpath : udev_rpath,
install : true,
install_dir : rootbindir)
- public_programs += [exe]
+ public_programs += exe
endif
if conf.get('ENABLE_QUOTACHECK') == 1
@@ -2409,12 +2617,12 @@ exe = executable('systemd-socket-proxyd',
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
-public_programs += [exe]
+public_programs += exe
exe = executable('systemd-udevd',
systemd_udevd_sources,
include_directories : includes,
- c_args : ['-DLOG_REALM=LOG_REALM_UDEV'],
+ c_args : '-DLOG_REALM=LOG_REALM_UDEV',
link_with : [libudev_core,
libsystemd_network,
libudev_static],
@@ -2426,11 +2634,11 @@ exe = executable('systemd-udevd',
install_rpath : udev_rpath,
install : true,
install_dir : rootlibexecdir)
-public_programs += [exe]
+public_programs += exe
exe = executable('udevadm',
udevadm_sources,
- c_args : ['-DLOG_REALM=LOG_REALM_UDEV'],
+ c_args : '-DLOG_REALM=LOG_REALM_UDEV',
include_directories : includes,
link_with : [libudev_core,
libsystemd_network,
@@ -2443,7 +2651,7 @@ exe = executable('udevadm',
install_rpath : udev_rpath,
install : true,
install_dir : rootbindir)
-public_programs += [exe]
+public_programs += exe
executable('systemd-shutdown',
systemd_shutdown_sources,
@@ -2499,7 +2707,7 @@ exe = executable('systemd-nspawn',
dependencies : [libblkid],
install_rpath : rootlibexecdir,
install : true)
-public_programs += [exe]
+public_programs += exe
if conf.get('ENABLE_NETWORKD') == 1
executable('systemd-networkd',
@@ -2531,7 +2739,7 @@ if conf.get('ENABLE_NETWORKD') == 1
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)
- public_programs += [exe]
+ public_programs += exe
endif
executable('systemd-sulogin-shell',
@@ -2544,6 +2752,15 @@ executable('systemd-sulogin-shell',
############################################################
+custom_target(
+ 'systemd-runtest.env',
+ output : 'systemd-runtest.env',
+ command : ['sh', '-c', '{ ' +
+ 'echo SYSTEMD_TEST_DATA=@0@; '.format(join_paths(meson.current_source_dir(), 'test')) +
+ 'echo SYSTEMD_CATALOG_DIR=@0@; '.format(join_paths(meson.current_build_dir(), 'catalog')) +
+ '} >@OUTPUT@'],
+ build_by_default : true)
+
foreach tuple : tests
sources = tuple[0]
link_with = tuple[1].length() > 0 ? tuple[1] : [libshared]
@@ -2559,9 +2776,8 @@ foreach tuple : tests
timeout = type.split('=')[1].to_int()
type = ''
endif
- if want_tests == 'false'
- message('Not compiling @0@ because tests is set to false'.format(name))
- elif condition == '' or conf.get(condition) == 1
+
+ if condition == '' or conf.get(condition) == 1
exe = executable(
name,
sources,
@@ -2569,6 +2785,7 @@ foreach tuple : tests
link_with : link_with,
dependencies : dependencies,
c_args : defs,
+ build_by_default : want_tests != 'false',
install_rpath : rootlibexecdir,
install : install_tests,
install_dir : join_paths(testsdir, type))
@@ -2577,7 +2794,7 @@ foreach tuple : tests
message('@0@ is a manual test'.format(name))
elif type == 'unsafe' and want_tests != 'unsafe'
message('@0@ is an unsafe test'.format(name))
- else
+ elif want_tests != 'false'
test(name, exe,
env : test_env,
timeout : timeout)
@@ -2592,9 +2809,12 @@ exe = executable(
test_libsystemd_sym_c,
include_directories : includes,
link_with : [libsystemd],
+ build_by_default : want_tests != 'false',
install : install_tests,
install_dir : testsdir)
-test('test-libsystemd-sym', exe)
+if want_tests != 'false'
+ test('test-libsystemd-sym', exe)
+endif
exe = executable(
'test-libsystemd-static-sym',
@@ -2603,10 +2823,10 @@ exe = executable(
link_with : [install_libsystemd_static],
dependencies : [threads], # threads is already included in dependencies on the library,
# but does not seem to get propagated. Add here as a work-around.
- build_by_default : static_libsystemd_pic,
+ build_by_default : want_tests != 'false' and static_libsystemd_pic,
install : install_tests and static_libsystemd_pic,
install_dir : testsdir)
-if static_libsystemd_pic
+if want_tests != 'false' and static_libsystemd_pic
test('test-libsystemd-static-sym', exe)
endif
@@ -2614,22 +2834,25 @@ exe = executable(
'test-libudev-sym',
test_libudev_sym_c,
include_directories : includes,
- c_args : ['-Wno-deprecated-declarations'],
+ c_args : '-Wno-deprecated-declarations',
link_with : [libudev],
+ build_by_default : want_tests != 'false',
install : install_tests,
install_dir : testsdir)
-test('test-libudev-sym', exe)
+if want_tests != 'false'
+ test('test-libudev-sym', exe)
+endif
exe = executable(
'test-libudev-static-sym',
test_libudev_sym_c,
include_directories : includes,
- c_args : ['-Wno-deprecated-declarations'],
+ c_args : '-Wno-deprecated-declarations',
link_with : [install_libudev_static],
- build_by_default : static_libudev_pic,
+ build_by_default : want_tests != 'false' and static_libudev_pic,
install : install_tests and static_libudev_pic,
install_dir : testsdir)
-if static_libudev_pic
+if want_tests != 'false' and static_libudev_pic
test('test-libudev-static-sym', exe)
endif
@@ -2637,6 +2860,7 @@ endif
fuzzer_exes = []
+if get_option('tests') != 'false'
foreach tuple : fuzzers
sources = tuple[0]
link_with = tuple[1].length() > 0 ? tuple[1] : [libshared]
@@ -2661,6 +2885,7 @@ foreach tuple : fuzzers
c_args : defs,
install : false)
endforeach
+endif
run_target('fuzzers',
depends : fuzzer_exes,
@@ -2683,15 +2908,12 @@ subdir('network')
subdir('man')
subdir('shell-completion/bash')
subdir('shell-completion/zsh')
-subdir('doc/sysvinit')
-subdir('doc/var-log')
+subdir('docs/sysvinit')
+subdir('docs/var-log')
-# FIXME: figure out if the warning is true:
-# https://github.com/mesonbuild/meson/wiki/Reference-manual#install_subdir
install_subdir('factory/etc',
install_dir : factorydir)
-
install_data('xorg/50-systemd-user.sh',
install_dir : xinitrcdir)
install_data('modprobe.d/systemd.conf',
@@ -2700,13 +2922,13 @@ install_data('LICENSE.GPL2',
'LICENSE.LGPL2.1',
'NEWS',
'README',
- 'doc/CODING_STYLE',
- 'doc/DISTRO_PORTING',
- 'doc/ENVIRONMENT.md',
- 'doc/HACKING',
- 'doc/TRANSIENT-SETTINGS.md',
- 'doc/TRANSLATORS',
- 'doc/UIDS-GIDS.md',
+ 'docs/CODING_STYLE.md',
+ 'docs/DISTRO_PORTING.md',
+ 'docs/ENVIRONMENT.md',
+ 'docs/HACKING.md',
+ 'docs/TRANSIENT-SETTINGS.md',
+ 'docs/TRANSLATORS.md',
+ 'docs/UIDS-GIDS.md',
'src/libsystemd/sd-bus/GVARIANT-SERIALIZATION',
install_dir : docdir)
@@ -2719,9 +2941,11 @@ meson_check_help = find_program('tools/meson-check-help.sh')
foreach exec : public_programs
name = exec.full_path().split('/')[-1]
- test('check-help-' + name,
- meson_check_help,
- args : [exec.full_path()])
+ if want_tests != 'false'
+ test('check-help-' + name,
+ meson_check_help,
+ args : exec.full_path())
+ endif
endforeach
############################################################
@@ -2731,13 +2955,7 @@ foreach tuple : sanitizers
sanitizer = tuple[0]
build = tuple[1]
- have = run_command(check_compilation_sh,
- cc.cmd_array(), '-x', 'c',
- '-fsanitize=@0@'.format(sanitizer),
- '-include', link_test_c).returncode() == 0
- message('@0@ sanitizer supported: @1@'.format(sanitizer, have ? 'yes' : 'no'))
-
- if have
+ if cc.has_link_argument('-fsanitize=@0@'.format(sanitizer))
prev = ''
foreach p : fuzz_regression_tests
b = p.split('/')[-2]
@@ -2767,9 +2985,7 @@ foreach tuple : sanitizers
test('@0@:@1@:@2@'.format(b, c, sanitizer),
env,
args : [exe.full_path(),
- join_paths(meson.source_root(),
- 'test/fuzz-regressions',
- p)])
+ join_paths(meson.source_root(), p)])
endif
endforeach
endif
@@ -2781,7 +2997,7 @@ endforeach
if git.found()
all_files = run_command(
git,
- ['--git-dir=@0@/.git'.format(meson.current_source_dir()),
+ ['--git-dir=@0@/.git'.format(meson.source_root()),
'ls-files',
':/*.[ch]'])
all_files = files(all_files.stdout().split())
@@ -2789,10 +3005,10 @@ if git.found()
custom_target(
'tags',
output : 'tags',
- command : [env, 'etags', '-o', '@0@/TAGS'.format(meson.current_source_dir())] + all_files)
+ command : [env, 'etags', '-o', '@0@/TAGS'.format(meson.source_root())] + all_files)
run_target(
'ctags',
- command : [env, 'ctags', '-o', '@0@/tags'.format(meson.current_source_dir())] + all_files)
+ command : [env, 'ctags', '-o', '@0@/tags'.format(meson.source_root())] + all_files)
endif
if git.found()
@@ -2805,17 +3021,17 @@ endif
if git.found()
git_head = run_command(
git,
- ['--git-dir=@0@/.git'.format(meson.current_source_dir()),
+ ['--git-dir=@0@/.git'.format(meson.source_root()),
'rev-parse', 'HEAD']).stdout().strip()
git_head_short = run_command(
git,
- ['--git-dir=@0@/.git'.format(meson.current_source_dir()),
+ ['--git-dir=@0@/.git'.format(meson.source_root()),
'rev-parse', '--short=7', 'HEAD']).stdout().strip()
run_target(
'git-snapshot',
command : ['git', 'archive',
- '-o', '@0@/systemd-@1@.tar.gz'.format(meson.current_source_dir(),
+ '-o', '@0@/systemd-@1@.tar.gz'.format(meson.source_root(),
git_head_short),
'--prefix', 'systemd-@0@/'.format(git_head),
'HEAD'])
@@ -2829,6 +3045,10 @@ run_target(
depends : [man, libsystemd, libudev],
command : [meson_check_api_docs_sh, libsystemd.full_path(), libudev.full_path()])
+run_target(
+ 'make-index-md',
+ command : ['sh', '@0@/tools/make-index-md.sh'.format(meson.source_root()), meson.source_root()])
+
############################################################
status = [
@@ -2858,7 +3078,7 @@ status = [
'debug shell: @0@ @ @1@'.format(get_option('debug-shell'),
get_option('debug-tty')),
'TTY GID: @0@'.format(tty_gid),
- 'users GID: @0@'.format(users_gid),
+ 'users GID: @0@'.format(substs.get('USERS_GID')),
'maximum system UID: @0@'.format(system_uid_max),
'maximum system GID: @0@'.format(system_gid_max),
'minimum dynamic UID: @0@'.format(dynamic_uid_min),
@@ -2877,6 +3097,7 @@ status = [
'default DNSSEC mode: @0@'.format(default_dnssec),
'default DNS-over-TLS mode: @0@'.format(default_dns_over_tls),
'default cgroup hierarchy: @0@'.format(default_hierarchy),
+ 'default net.naming-scheme setting: @0@'.format(default_net_naming_scheme),
'default KillUserProcesses setting: @0@'.format(kill_user_processes)]
alt_dns_servers = '\n '.join(dns_servers.split(' '))
@@ -2890,19 +3111,22 @@ alt_time_epoch = run_command('date', '-Is', '-u', '-d',
status += [
'time epoch: @0@ (@1@)'.format(time_epoch, alt_time_epoch)]
+status += [
+ 'static libsystemd: @0@'.format(static_libsystemd),
+ 'static libudev: @0@'.format(static_libudev)]
+
# TODO:
# CFLAGS: ${OUR_CFLAGS} ${CFLAGS}
# CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS}
# LDFLAGS: ${OUR_LDFLAGS} ${LDFLAGS}
if conf.get('ENABLE_EFI') == 1
- status += [
- 'efi arch: @0@'.format(efi_arch)]
+ status += 'efi arch: @0@'.format(efi_arch)
if have_gnu_efi
status += [
'EFI machine type: @0@'.format(EFI_MACHINE_TYPE_NAME),
- 'EFI CC @0@'.format(efi_cc),
+ 'EFI CC @0@'.format(' '.join(efi_cc)),
'EFI lib directory: @0@'.format(efi_libdir),
'EFI lds directory: @0@'.format(efi_ldsdir),
'EFI include directory: @0@'.format(efi_incdir)]
@@ -2930,11 +3154,11 @@ foreach tuple : [
['qrencode'],
['microhttpd'],
['gnutls'],
+ ['openssl'],
['libcurl'],
['idn'],
['libidn2'],
['libidn'],
- ['nss-systemd'],
['libiptc'],
['elfutils'],
['binfmt'],
@@ -2957,7 +3181,8 @@ foreach tuple : [
['localed'],
['networkd'],
['resolve'],
- ['DNS-over-TLS'],
+ ['DNS-over-TLS(gnutls)', conf.get('DNS_OVER_TLS_USE_GNUTLS') == 1],
+ ['DNS-over-TLS(openssl)', conf.get('DNS_OVER_TLS_USE_OPENSSL') == 1],
['coredump'],
['polkit'],
['legacy pkla', install_polkit_pkla],
@@ -2969,7 +3194,10 @@ foreach tuple : [
['blkid'],
['dbus'],
['glib'],
- ['nss-myhostname', conf.get('ENABLE_MYHOSTNAME') == 1],
+ ['nss-myhostname'],
+ ['nss-mymachines'],
+ ['nss-resolve'],
+ ['nss-systemd'],
['hwdb'],
['tpm'],
['man pages', want_man],
@@ -2984,7 +3212,12 @@ foreach tuple : [
['gshadow'],
['debug hashmap'],
['debug mmap cache'],
+ ['debug siphash'],
+ ['debug udev'],
['valgrind', conf.get('VALGRIND') == 1],
+ ['trace logging', conf.get('LOG_TRACE') == 1],
+ ['link-udev-shared', get_option('link-udev-shared')],
+ ['link-systemctl-shared', get_option('link-systemctl-shared')],
]
if tuple.length() >= 2
@@ -2995,9 +3228,9 @@ foreach tuple : [
cond = conf.get(ident1, 0) == 1 or conf.get(ident2, 0) == 1
endif
if cond
- found += [tuple[0]]
+ found += tuple[0]
else
- missing += [tuple[0]]
+ missing += tuple[0]
endif
endforeach
diff --git a/meson_options.txt b/meson_options.txt
index 16c1f2b2fa..1423b8998e 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -33,7 +33,6 @@ option('halt-local', type : 'string',
option('quotaon-path', type : 'string', description : 'path to quotaon')
option('quotacheck-path', type : 'string', description : 'path to quotacheck')
-option('kill-path', type : 'string', description : 'path to kill')
option('kmod-path', type : 'string', description : 'path to kmod')
option('kexec-path', type : 'string', description : 'path to kexec')
option('sulogin-path', type : 'string', description : 'path to sulogin')
@@ -46,12 +45,18 @@ option('debug-shell', type : 'string', value : '/bin/sh',
description : 'path to debug shell binary')
option('debug-tty', type : 'string', value : '/dev/tty9',
description : 'specify the tty device for debug shell')
-option('debug', type : 'array', choices : ['hashmap', 'mmap-cache'], value : [],
+option('debug-extra', type : 'array', choices : ['hashmap', 'mmap-cache', 'siphash', 'udev'], value : [],
description : 'enable extra debugging')
option('memory-accounting-default', type : 'boolean',
description : 'enable MemoryAccounting= by default')
+option('bump-proc-sys-fs-file-max', type : 'boolean',
+ description : 'bump /proc/sys/fs/file-max to ULONG_MAX')
+option('bump-proc-sys-fs-nr-open', type : 'boolean',
+ description : 'bump /proc/sys/fs/nr_open to INT_MAX')
option('valgrind', type : 'boolean', value : false,
description : 'do extra operations to avoid valgrind warnings')
+option('log-trace', type : 'boolean', value : false,
+ description : 'enable low level debug logging')
option('utmp', type : 'boolean',
description : 'support for utmp/wtmp log handling')
@@ -89,8 +94,14 @@ option('timesyncd', type : 'boolean',
description : 'install the systemd-timesyncd daemon')
option('remote', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'support for "journal over the network"')
-option('myhostname', type : 'boolean',
- description : 'nss-myhostname support')
+option('nss-myhostname', type : 'boolean',
+ description : 'install nss-myhostname module')
+option('nss-mymachines', type : 'combo', choices : ['auto', 'true', 'false'],
+ description : 'install nss-mymachines module')
+option('nss-resolve', type : 'combo', choices : ['auto', 'true', 'false'],
+ description : 'install nss-resolve module')
+option('nss-systemd', type : 'boolean',
+ description : 'install nss-systemd module')
option('firstboot', type : 'boolean',
description : 'support for firstboot mechanism')
option('randomseed', type : 'boolean',
@@ -125,7 +136,7 @@ option('dbussessionservicedir', type : 'string',
description : 'D-Bus session service directory')
option('dbussystemservicedir', type : 'string',
description : 'D-Bus system service directory')
-option('pkgconfigdatadir', type : 'string', value : 'share/pkgconfig',
+option('pkgconfigdatadir', type : 'string', value : '',
description : 'directory for ')
option('pkgconfiglibdir', type : 'string', value : '',
description : 'directory for ')
@@ -145,28 +156,26 @@ option('compat-gateway-hostname', type : 'boolean', value : 'false',
option('default-hierarchy', type : 'combo',
choices : ['legacy', 'hybrid', 'unified'], value : 'hybrid',
description : 'default cgroup hierarchy')
-option('time-epoch', type : 'string',
+option('default-net-naming-scheme', type : 'combo',
+ choices : ['latest', 'v238', 'v239', 'v240'],
+ description : 'default net.naming-scheme= value')
+option('time-epoch', type : 'integer', value : '-1',
description : 'time epoch for time clients')
-option('system-uid-max', type : 'string',
+option('system-uid-max', type : 'integer', value : '-1',
description : 'maximum system UID')
-option('system-gid-max', type : 'string',
+option('system-gid-max', type : 'integer', value : '-1',
description : 'maximum system GID')
-option('dynamic-uid-min', type : 'string',
- description : 'minimum dynamic UID',
- value : '61184') # That's → 0x0000EF00 in hex
-option('dynamic-uid-max', type : 'string',
- description : 'maximum dynamic UID',
- value : '65519') # That's → 0x0000FFEF in hex
-option('container-uid-base-min', type : 'string',
- description : 'minimum container UID base',
- value : '524288') # That's → 0x00080000 in hex
-option('container-uid-base-max', type : 'string',
- description : 'maximum container UID base',
- value : '1878982656') # That's → 0x6FFF0000 in hex
-option('tty-gid', type : 'string',
- description : 'the numeric GID of the "tty" group',
- value : '5')
-option('users-gid', type : 'string',
+option('dynamic-uid-min', type : 'integer', value : 0x0000EF00,
+ description : 'minimum dynamic UID')
+option('dynamic-uid-max', type : 'integer', value : 0x0000FFEF,
+ description : 'maximum dynamic UID')
+option('container-uid-base-min', type : 'integer', value : 0x00080000,
+ description : 'minimum container UID base')
+option('container-uid-base-max', type : 'integer', value : 0x6FFF0000,
+ description : 'maximum container UID base')
+option('tty-gid', type : 'integer', value : 5,
+ description : 'the numeric GID of the "tty" group')
+option('users-gid', type : 'integer', value : '-1',
description : 'the numeric GID of the "users" group')
option('adm-group', type : 'boolean',
description : 'the ACL for adm group should be added')
@@ -195,7 +204,7 @@ option('default-dns-over-tls', type : 'combo',
description : 'default DNS-over-TLS mode',
choices : ['opportunistic', 'no'],
value : 'no')
-option('dns-over-tls', type : 'combo', choices : ['auto', 'true', 'false'],
+option('dns-over-tls', type : 'combo', choices : ['auto', 'gnutls', 'openssl', 'true', 'false'],
description : 'DNS-over-TLS support')
option('dns-servers', type : 'string',
description : 'space-separated list of default DNS servers',
@@ -221,7 +230,7 @@ option('smack', type : 'boolean',
option('smack-run-label', type : 'string',
description : 'run systemd --system itself with a specific SMACK label')
option('polkit', type : 'combo', choices : ['auto', 'true', 'false'],
- description : 'PolicyKit support')
+ description : 'polkit support')
option('ima', type : 'boolean',
description : 'IMA support')
@@ -247,8 +256,6 @@ option('libidn2', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'libidn2 support')
option('libidn', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'libidn support')
-option('nss-systemd', type : 'boolean',
- description : 'enable nss-systemd')
option('libiptc', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'libiptc support')
option('qrencode', type : 'combo', choices : ['auto', 'true', 'false'],
@@ -257,6 +264,8 @@ option('gcrypt', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'gcrypt support')
option('gnutls', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'gnutls support')
+option('openssl', type : 'combo', choices : ['auto', 'true', 'false'],
+ description : 'openssl support')
option('elfutils', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'elfutils support')
option('zlib', type : 'combo', choices : ['auto', 'true', 'false'],
@@ -278,9 +287,9 @@ option('dbus', type : 'combo', choices : ['auto', 'true', 'false'],
option('gnu-efi', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'gnu-efi support for sd-boot')
-option('efi-cc', type : 'string', value : 'gcc',
+option('efi-cc', type : 'array',
description : 'the compiler to use for EFI modules')
-option('efi-ld', type : 'string', value : 'ld',
+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')
@@ -288,7 +297,7 @@ 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 : 'string', value : '8',
+option('tpm-pcrindex', type : 'integer', value : 8,
description : 'TPM PCR register number to use')
option('bashcompletiondir', type : 'string',
diff --git a/mkosi.build b/mkosi.build
index b7b8c50a2b..114e617c85 100755
--- a/mkosi.build
+++ b/mkosi.build
@@ -90,3 +90,8 @@ cat > "$DESTDIR"/etc/issue <<EOF
Kernel \r on an \m (\l)
EOF
+
+# Manually update the boot loader from the one we just built
+mkdir -p "$DESTDIR"/boot/efi/EFI/systemd "$DESTDIR"/boot/efi/EFI/BOOT
+cp "$DESTDIR"/usr/lib/systemd/boot/efi/systemd-bootx64.efi "$DESTDIR"/boot/efi/EFI/systemd/systemd-bootx64.efi
+cp "$DESTDIR"/usr/lib/systemd/boot/efi/systemd-bootx64.efi "$DESTDIR"/boot/efi/EFI/BOOT/bootx64.efi
diff --git a/po/LINGUAS b/po/LINGUAS
index 4bd8e6642f..eeaafef2a8 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -15,6 +15,7 @@ id
it
ja
ko
+lt
pl
pt_BR
ro
diff --git a/po/be.po b/po/be.po
index a74b9a3cea..c57a4a3cad 100644
--- a/po/be.po
+++ b/po/be.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Belarusian translation for systemd.
-# This file is distributed under the same license as the systemd package.
#
#
# Viktar VaÅ­ÄkieviÄ <victorenator@gmail.com>, 2015, 2016.
diff --git a/po/be@latin.po b/po/be@latin.po
index 5085459623..eed51937a0 100644
--- a/po/be@latin.po
+++ b/po/be@latin.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Belarusian translation for systemd.
-# This file is distributed under the same license as the systemd package.
#
#
# Viktar VaÅ­ÄkieviÄ <victorenator@gmail.com>, 2015, 2016.
diff --git a/po/bg.po b/po/bg.po
index 3e3cda15ff..98edfc5f02 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -2,7 +2,6 @@
#
# Bulgarian translation of systemd po-file.
# Copyright © 2016 Alexander Shopov <ash@kambanaria.org>
-# This file is distributed under the same license as the systemd package.
# Alexander Shopov <ash@kambanaria.org>, 2016.
#
msgid ""
diff --git a/po/ca.po b/po/ca.po
index 8f1ef8f2d1..4a3e221d06 100644
--- a/po/ca.po
+++ b/po/ca.po
@@ -1,10 +1,8 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Catalan translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Walter Garcia-Fontes <walter.garcia@upf.edu>, 2016.
# Robert Antoni Buj Gelonch <rbuj@fedoraproject.org>, 2018. #zanata
-# Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>, 2018. #zanata
msgid ""
msgstr ""
"Project-Id-Version: systemd\n"
diff --git a/po/cs.po b/po/cs.po
index 7589d7ce37..8306cdcfaf 100644
--- a/po/cs.po
+++ b/po/cs.po
@@ -1,533 +1,662 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Czech translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Daniel Maixner <xskipy@gmail.com>, 2016.
-# Daniel Rusek <mail@asciiwolf.com>, 2016, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: systemd master\n"
"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2016-04-23 14:24+0200\n"
-"PO-Revision-Date: 2017-10-10 19:54+0200\n"
+"POT-Creation-Date: 2018-11-26 03:25+0000\n"
+"PO-Revision-Date: 2018-12-03 15:52+0100\n"
"Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n"
+"Language-Team: Czech\n"
"Language: cs\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==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? "
-"1 : 2);\n"
-"Language-Team: \n"
-"X-Generator: Poedit 2.0.3\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\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 "Odeslat heslo zpět do systému"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:2
-msgid "Authentication is required to send the entered passphrase back to the system."
+#: src/core/org.freedesktop.systemd1.policy.in:23
+msgid ""
+"Authentication is required to send the entered passphrase back to the system."
msgstr "Pro odeslání zadaného hesla do systému je vyžadováno ověření."
-#: ../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 "Spravovat systémové služby nebo další jednotky"
-#: ../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 "Pro správu systémových služeb nebo dalších jednotek je vyžadováno ověření."
+msgstr ""
+"Pro správu systémových služeb nebo dalších jednotek je vyžadováno ověření."
-#: ../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 "Spravovat systémové služby nebo soubory jednotek"
-#: ../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 "Pro správu systémových služeb nebo souborů jednotek je vyžadováno ověření."
+msgstr ""
+"Pro správu systémových služeb nebo souborů jednotek je vyžadováno ověření."
-#: ../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 "Nastavit nebo rušit proměnné správce systému a služeb"
-#: ../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."
-msgstr "Pro nastavení nebo rušení proměnných správce systému a služeb je vyžadováno ověření."
+"Authentication is required to set or unset system and service manager "
+"environment variables."
+msgstr ""
+"Pro nastavení nebo rušení proměnných správce systému a služeb je vyžadováno "
+"ověření."
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9
+#: src/core/org.freedesktop.systemd1.policy.in:64
msgid "Reload the systemd state"
msgstr "Znovu naÄíst stav systemd"
-#: ../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 "Pro znovu naÄtení stavu systemd je vyžadováno ověření."
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
+#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Nastavit název stroje"
-#: ../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 "Pro nastavení lokálního názvu stroje je vyžadováno ověření."
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
+#: src/hostname/org.freedesktop.hostname1.policy:30
msgid "Set static host name"
msgstr "Nastavit statický název stroje"
-#: ../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."
+"Authentication is required to set the statically configured local host name, "
+"as well as the pretty host name."
msgstr ""
-"Pro nastavení staticky konfigurovaného názvu lokálního stroje, stejně tak pro změnu "
-"uživatelsky přívětivého jména je vyžadováno ověření."
+"Pro nastavení staticky konfigurovaného názvu lokálního stroje, stejně tak "
+"pro změnu uživatelsky přívětivého jména je vyžadováno ověření."
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5
+#: src/hostname/org.freedesktop.hostname1.policy:41
msgid "Set machine information"
msgstr "Nastavit informace o stroji"
-#: ../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 "Pro nastavení informací o stroji je vyžadováno ověření."
-#: ../src/import/org.freedesktop.import1.policy.in.h:1
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "Získat UUID produktu"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "Pro získání UUID produktu je vyžadováno ověření."
+
+#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
msgstr "Importovat obraz virtuální stroje nebo kontejneru"
-#: ../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 "Pro import obrazu virtuálního stroje nebo kontejneru je vyžadováno ověření"
+msgstr ""
+"Pro import obrazu virtuálního stroje nebo kontejneru je vyžadováno ověření"
-#: ../src/import/org.freedesktop.import1.policy.in.h:3
+#: src/import/org.freedesktop.import1.policy:32
msgid "Export a VM or container image"
msgstr "Exportovat obraz virtuálního stroje nebo kontejneru"
-#: ../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 "Pro export obrazu virtuálního stroje nebo kontejneru je vyžadováno ověření"
+msgstr ""
+"Pro export obrazu virtuálního stroje nebo kontejneru je vyžadováno ověření"
-#: ../src/import/org.freedesktop.import1.policy.in.h:5
+#: src/import/org.freedesktop.import1.policy:42
msgid "Download a VM or container image"
msgstr "Stáhnout obraz virtuálního stroje nebo kontejneru"
-#: ../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 "Pro stažení obrazu virtuálního stroje nebo kontejneru je vyžadováno ověření"
+msgstr ""
+"Pro stažení obrazu virtuálního stroje nebo kontejneru je vyžadováno ověření"
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:1
+#: src/locale/org.freedesktop.locale1.policy:22
msgid "Set system locale"
msgstr "Nastavit lokalizaci systému"
-#: ../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 "Pro nastavení lokalizace systému je vyžadováno ověření."
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:3
+#: src/locale/org.freedesktop.locale1.policy:33
msgid "Set system keyboard settings"
msgstr "Nastavit systémovou konfiguraci klávesnice"
-#: ../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 "Pro nastavení systémové konfigurace klávesnice je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:1
+#: src/login/org.freedesktop.login1.policy:22
msgid "Allow applications to inhibit system shutdown"
msgstr "Povolit aplikacím zakázat vypnutí systému"
-#: ../src/login/org.freedesktop.login1.policy.in.h:2
-msgid "Authentication is required for an application to inhibit system shutdown."
+#: src/login/org.freedesktop.login1.policy:23
+msgid ""
+"Authentication is required for an application to inhibit system shutdown."
msgstr "Pro povolení aplikacím zakázat vypnutí systému je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:3
+#: src/login/org.freedesktop.login1.policy:33
msgid "Allow applications to delay system shutdown"
msgstr "Povolit aplikacím odložit vypnutí systému"
-#: ../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 "Pro povolení aplikacím odložit vypnutí systému je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:5
+#: src/login/org.freedesktop.login1.policy:44
msgid "Allow applications to inhibit system sleep"
msgstr "Povolit aplikacím zakázat uspání systému"
-#: ../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 "Pro povolení aplikacím zakázat uspání systému je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:7
+#: src/login/org.freedesktop.login1.policy:55
msgid "Allow applications to delay system sleep"
msgstr "Povolit aplikacím odložit uspání systému"
-#: ../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 "Pro povolení aplikacím odložit uspání systému je vyžadováno ověření."
-#: ../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 "Povolit aplikacím zakázat automatické vypnutí systému"
-#: ../src/login/org.freedesktop.login1.policy.in.h:10
-msgid "Authentication is required for an application to inhibit automatic system suspend."
-msgstr "Pro povolení aplikacím zakázat automatické vypnutí systému je vyžadováno ověření."
+#: src/login/org.freedesktop.login1.policy:66
+msgid ""
+"Authentication is required for an application to inhibit automatic system "
+"suspend."
+msgstr ""
+"Pro povolení aplikacím zakázat automatické vypnutí systému je vyžadováno "
+"ověření."
-#: ../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 "Povolit aplikacím zakázat chovaní systému na stisknutí vypínacího tlaÄítka"
+msgstr ""
+"Povolit aplikacím zakázat chovaní systému na stisknutí vypínacího tlaÄítka"
-#: ../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."
+"Authentication is required for an application to inhibit system handling of "
+"the power key."
msgstr ""
-"Pro povolení aplikacím zakázat chovaní systému na stisknutí vypínacího tlaÄítka je "
-"vyžadováno ověření."
+"Pro povolení aplikacím zakázat chovaní systému na stisknutí vypínacího "
+"tlaÄítka je vyžadováno ověření."
-#: ../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 "Povolit aplikacím zakázat chovaní systému na stisknutí uspávacího tlaÄítka"
+msgstr ""
+"Povolit aplikacím zakázat chovaní systému na stisknutí uspávacího tlaÄítka"
-#: ../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."
+"Authentication is required for an application to inhibit system handling of "
+"the suspend key."
msgstr ""
-"Pro povolení aplikacím zakázat chovaní systému na stisknutí uspávacího tlaÄítka je "
-"vyžadováno ověření."
+"Pro povolení aplikacím zakázat chovaní systému na stisknutí uspávacího "
+"tlaÄítka je vyžadováno ověření."
-#: ../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 "Povolit aplikacím zakázat chovaní systému na stisknutí tlaÄítka hibernace"
+msgstr ""
+"Povolit aplikacím zakázat chovaní systému na stisknutí tlaÄítka hibernace"
-#: ../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."
+"Authentication is required for an application to inhibit system handling of "
+"the hibernate key."
msgstr ""
-"Pro povolení aplikacím zakázat chovaní systému na stisknutí tlaÄítka hibernace je vyžadováno "
-"ověření."
+"Pro povolení aplikacím zakázat chovaní systému na stisknutí tlaÄítka "
+"hibernace je vyžadováno ověření."
-#: ../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 "Povolit aplikacím zakázat chovaní systému na zavření víka"
-#: ../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 "Pro povolení aplikacím zakázat chovaní systému na zavření víka je vyžadováno ověření."
+"Authentication is required for an application to inhibit system handling of "
+"the lid switch."
+msgstr ""
+"Pro povolení aplikacím zakázat chovaní systému na zavření víka je vyžadováno "
+"ověření."
-#: ../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 "Povolit nepřihlášenému uživateli spouštět programy"
-#: ../src/login/org.freedesktop.login1.policy.in.h:20
+#: src/login/org.freedesktop.login1.policy:118
msgid "Explicit request is required to run programs as a non-logged-in user."
-msgstr "Ke spuštění programů jako nepřihlášený uživatel je třeba speciální požadavek."
+msgstr ""
+"Ke spuštění programů jako nepřihlášený uživatel je třeba speciální požadavek."
-#: ../src/login/org.freedesktop.login1.policy.in.h:21
+#: src/login/org.freedesktop.login1.policy:127
msgid "Allow non-logged-in users to run programs"
msgstr "Povolit nepřihlášeným uživatelům spouštět programy"
-#: ../src/login/org.freedesktop.login1.policy.in.h:22
+#: src/login/org.freedesktop.login1.policy:128
msgid "Authentication is required to run programs as a non-logged-in user."
msgstr "Ke spuštění programů jako nepřihlášený uživatel je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:23
+#: src/login/org.freedesktop.login1.policy:137
msgid "Allow attaching devices to seats"
msgstr "Povolit připojování zařízení ke stanovištím"
-#: ../src/login/org.freedesktop.login1.policy.in.h:24
+#: src/login/org.freedesktop.login1.policy:138
msgid "Authentication is required for attaching 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.in.h:25
+#: src/login/org.freedesktop.login1.policy:148
msgid "Flush device to seat attachments"
msgstr "Odstranit přiřazení zařízení ke stanovištím"
-#: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for resetting 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í."
+#: src/login/org.freedesktop.login1.policy:149
+msgid ""
+"Authentication is required for resetting 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í."
-#: ../src/login/org.freedesktop.login1.policy.in.h:27
+#: src/login/org.freedesktop.login1.policy:158
msgid "Power off the system"
msgstr "Vypnout systém"
-#: ../src/login/org.freedesktop.login1.policy.in.h:28
+#: src/login/org.freedesktop.login1.policy:159
msgid "Authentication is required for powering off the system."
msgstr "Pro vypnutí systému je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:29
+#: src/login/org.freedesktop.login1.policy:169
msgid "Power off the system while other users are logged in"
msgstr "Vypnout systém, i když jsou přihlášeni další uživatelé"
-#: ../src/login/org.freedesktop.login1.policy.in.h:30
-msgid "Authentication is required for powering 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:170
+msgid ""
+"Authentication is required for powering 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.in.h:31
+#: src/login/org.freedesktop.login1.policy:180
msgid "Power off the system while an application asked to inhibit it"
msgstr "Vypnout systém, i když aplikace požádala o zákaz vypnutí"
-#: ../src/login/org.freedesktop.login1.policy.in.h:32
+#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application asked to inhibit "
-"it."
-msgstr "Pro vypnutí systému, když aplikace požádala o zákaz vypnutí je vyžadováno ověření."
+"Authentication is required for powering off the system while an application "
+"asked to inhibit it."
+msgstr ""
+"Pro vypnutí systému, když aplikace požádala o zákaz vypnutí je vyžadováno "
+"ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:33
+#: src/login/org.freedesktop.login1.policy:191
msgid "Reboot the system"
msgstr "Restartovat systém"
-#: ../src/login/org.freedesktop.login1.policy.in.h:34
+#: src/login/org.freedesktop.login1.policy:192
msgid "Authentication is required for rebooting the system."
msgstr "Pro restartování systému je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:35
+#: src/login/org.freedesktop.login1.policy:202
msgid "Reboot the system while other users are logged in"
msgstr "Restartovat systém, i když jsou přihlášeni další uživatelé"
-#: ../src/login/org.freedesktop.login1.policy.in.h:36
-msgid "Authentication is required for rebooting 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:203
+msgid ""
+"Authentication is required for rebooting 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.in.h:37
+#: src/login/org.freedesktop.login1.policy:213
msgid "Reboot the system while an application asked to inhibit it"
msgstr "Restartovat systém, i když aplikace požádala o zákaz restartu"
-#: ../src/login/org.freedesktop.login1.policy.in.h:38
+#: 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 for rebooting the system while an application "
+"asked to inhibit it."
msgstr ""
-"Pro restartování systému, když aplikace požádala o zákaz restartu je vyžadováno ověření."
+"Pro restartování systému, když aplikace požádala o zákaz restartu je "
+"vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:39
+#: src/login/org.freedesktop.login1.policy:224
msgid "Halt the system"
msgstr "Zastavit systém"
-#: ../src/login/org.freedesktop.login1.policy.in.h:40
+#: src/login/org.freedesktop.login1.policy:225
msgid "Authentication is required for halting the system."
msgstr "Pro zastavení systému je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:41
+#: src/login/org.freedesktop.login1.policy:235
msgid "Halt the system while other users are logged in"
msgstr "Zastavit systém, i když jsou přihlášeni další uživatelé"
-#: ../src/login/org.freedesktop.login1.policy.in.h:42
-msgid "Authentication is required for halting 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:236
+msgid ""
+"Authentication is required for halting 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.in.h:43
+#: src/login/org.freedesktop.login1.policy:246
msgid "Halt the system while an application asked to inhibit it"
msgstr "Zastavit systém, i když aplikace požádala o zákaz zastavení"
-#: ../src/login/org.freedesktop.login1.policy.in.h:44
+#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked to inhibit it."
-msgstr "Pro zastavení systému, když aplikace požádala o zákaz zastavení je vyžadováno ověření."
+"Authentication is required for halting the system while an application asked "
+"to inhibit it."
+msgstr ""
+"Pro zastavení systému, když aplikace požádala o zákaz zastavení je "
+"vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:45
+#: src/login/org.freedesktop.login1.policy:257
msgid "Suspend the system"
msgstr "Uspat systém"
-#: ../src/login/org.freedesktop.login1.policy.in.h:46
+#: src/login/org.freedesktop.login1.policy:258
msgid "Authentication is required for suspending the system."
msgstr "Pro uspání systému je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:47
+#: src/login/org.freedesktop.login1.policy:267
msgid "Suspend the system while other users are logged in"
msgstr "Uspat systém, i když jsou přihlášeni další uživatelé"
-#: ../src/login/org.freedesktop.login1.policy.in.h:48
-msgid "Authentication is required for suspending 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:268
+msgid ""
+"Authentication is required for suspending 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.in.h:49
+#: src/login/org.freedesktop.login1.policy:278
msgid "Suspend the system while an application asked to inhibit it"
msgstr "Uspat systém, i když aplikace požádala o zákaz uspání"
-#: ../src/login/org.freedesktop.login1.policy.in.h:50
+#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application asked to inhibit "
-"it."
-msgstr "Pro uspání systému, když aplikace požádala o zákaz uspání je vyžadováno ověření."
+"Authentication is required for suspending the system while an application "
+"asked to inhibit it."
+msgstr ""
+"Pro uspání systému, když aplikace požádala o zákaz uspání je vyžadováno "
+"ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:51
+#: src/login/org.freedesktop.login1.policy:289
msgid "Hibernate the system"
msgstr "Hibernovat systém"
-#: ../src/login/org.freedesktop.login1.policy.in.h:52
+#: src/login/org.freedesktop.login1.policy:290
msgid "Authentication is required for hibernating the system."
msgstr "Pro hibernaci systému je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:53
+#: src/login/org.freedesktop.login1.policy:299
msgid "Hibernate the system while other users are logged in"
msgstr "Hibernovat systém, i když jsou přihlášeni další uživatelé"
-#: ../src/login/org.freedesktop.login1.policy.in.h:54
-msgid "Authentication is required for hibernating 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:300
+msgid ""
+"Authentication is required for hibernating 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.in.h:55
+#: src/login/org.freedesktop.login1.policy:310
msgid "Hibernate the system while an application asked to inhibit it"
msgstr "Hibernovat systém, i když aplikace požádala o zákaz hibernace"
-#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application asked to inhibit "
-"it."
-msgstr "Pro hibernaci systému, když aplikace požádala o zákaz hibernace je vyžadováno ověření."
+"Authentication is required for hibernating the system while an application "
+"asked to inhibit it."
+msgstr ""
+"Pro hibernaci systému, když aplikace požádala o zákaz hibernace je "
+"vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:57
+#: src/login/org.freedesktop.login1.policy:321
msgid "Manage active sessions, users and seats"
msgstr "Spravovat aktivní sezení, uživatele a stanoviště"
-#: ../src/login/org.freedesktop.login1.policy.in.h:58
-msgid "Authentication is required for managing active sessions, users and seats."
-msgstr "Pro správu aktivních sezení, uživatelů a stanovišť je vyžadováno ověření."
+#: src/login/org.freedesktop.login1.policy:322
+msgid ""
+"Authentication is required for managing active sessions, users and seats."
+msgstr ""
+"Pro správu aktivních sezení, uživatelů a stanovišť je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:59
+#: src/login/org.freedesktop.login1.policy:331
msgid "Lock or unlock active sessions"
msgstr "Zamknout nebo odemknout aktivní sezení"
-#: ../src/login/org.freedesktop.login1.policy.in.h:60
+#: src/login/org.freedesktop.login1.policy:332
msgid "Authentication is required to lock or unlock active sessions."
msgstr "Pro zamÄení nebo odemÄení aktivních sezení je vyžadováno ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:61
+#: src/login/org.freedesktop.login1.policy:341
msgid "Allow indication to the firmware to boot to setup interface"
msgstr "Povolit indikaci firmwaru bootovat instalaÄní prostÅ™edí"
-#: ../src/login/org.freedesktop.login1.policy.in.h:62
-msgid "Authentication is required to indicate to the firmware to boot to setup interface."
-msgstr "K povolení indikace firmwaru bootovat instalaÄní prostÅ™edí je vyžadováno ověření."
+#: src/login/org.freedesktop.login1.policy:342
+msgid ""
+"Authentication is required to indicate to the firmware to boot to setup "
+"interface."
+msgstr ""
+"K povolení indikace firmwaru bootovat instalaÄní prostÅ™edí je vyžadováno "
+"ověření."
-#: ../src/login/org.freedesktop.login1.policy.in.h:63
+#: src/login/org.freedesktop.login1.policy:352
msgid "Set a wall message"
msgstr "Nastavit zprávu všem uživatelům"
-#: ../src/login/org.freedesktop.login1.policy.in.h:64
+#: src/login/org.freedesktop.login1.policy:353
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/machine/org.freedesktop.machine1.policy.in.h:1
+#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
msgstr "Přihlásit se do lokálního kontejneru"
-#: ../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 "Pro přihlášení do lokálního kontejneru je vyžadováno ověření."
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+#: src/machine/org.freedesktop.machine1.policy:32
msgid "Log into the local host"
msgstr "Přihlásit se na lokální stroj"
-#: ../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 "Pro přihlášení k lokálnímu stroji je vyžadováno ověření."
-#: ../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 "Získat shell v lokálním kontejneru"
-#: ../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 "Pro získání shellu v lokálním kontejneru je vyžadováno ověření."
-#: ../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 "Získat shell na lokálním stroji"
-#: ../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 "Pro získání shellu na lokálním stroji je vyžadováno ověření."
-#: ../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 "Získat pseudo TTY v lokálním kontejneru"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
-msgid "Authentication is required to acquire a pseudo TTY in a local container."
+#: src/machine/org.freedesktop.machine1.policy:65
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
msgstr "Pro získání pseudo TTY v lokálním kontejneru je vyžadováno ověření."
-#: ../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 "Získat pseudo TTY na lokálním stroji"
-#: ../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 "Pro získání pseudo TTY na lokálním stroji je vyžadováno ověření."
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
+#: src/machine/org.freedesktop.machine1.policy:84
msgid "Manage local virtual machines and containers"
msgstr "Spravovat lokální virtuální stroje a kontejnery"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
-msgid "Authentication is required to manage local virtual machines and containers."
-msgstr "Pro správu lokálních virtuálních strojů a kontejnerů je vyžadováno ověření."
+#: src/machine/org.freedesktop.machine1.policy:85
+msgid ""
+"Authentication is required to manage local virtual machines and containers."
+msgstr ""
+"Pro správu lokálních virtuálních strojů a kontejnerů je vyžadováno ověření."
-#: ../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 "Spravovat lokální obrazy virtuálních strojů a kontejnerů"
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
-msgid "Authentication is required to manage local virtual machine and container images."
-msgstr "Pro správu obrazů lokálních virtuálních strojů a kontejnerů je vyžadováno ověření."
+#: src/machine/org.freedesktop.machine1.policy:96
+msgid ""
+"Authentication is required to manage local virtual machine and container "
+"images."
+msgstr ""
+"Pro správu obrazů lokálních virtuálních strojů a kontejnerů 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"
+
+#: src/portable/org.freedesktop.portable1.policy:14
+msgid "Authentication is required to inspect a portable service image."
+msgstr "Pro prohlížení obrazu přenosné služby je vyžadováno ověření."
+
+#: src/portable/org.freedesktop.portable1.policy:23
+msgid "Attach or detach a portable service image"
+msgstr "Připojit nebo odpojit obraz přenosné služby"
+
+#: src/portable/org.freedesktop.portable1.policy:24
+msgid ""
+"Authentication is required to attach or detach a portable service image."
+msgstr ""
+"Pro připojení nebo odpojení obrazu přenosné služby je vyžadováno ověření."
+
+#: src/portable/org.freedesktop.portable1.policy:34
+msgid "Delete or modify portable service image"
+msgstr "Odstranit nebo upravit obraz přenosné služby"
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1
+#: src/portable/org.freedesktop.portable1.policy:35
+msgid ""
+"Authentication is required to delete or modify a portable service image."
+msgstr ""
+"Pro odstranění nebo úpravu obrazu přenosné služby je vyžadováno ověření."
+
+#: src/resolve/org.freedesktop.resolve1.policy:22
+msgid "Register a DNS-SD service"
+msgstr "Registrovat službu DNS-SD"
+
+#: src/resolve/org.freedesktop.resolve1.policy:23
+msgid "Authentication is required to register a DNS-SD service"
+msgstr "Pro registraci služby DNS-SD je vyžadováno ověření"
+
+#: src/resolve/org.freedesktop.resolve1.policy:33
+msgid "Unregister a DNS-SD service"
+msgstr "Zrušit registraci služby DNS-SD"
+
+#: src/resolve/org.freedesktop.resolve1.policy:34
+msgid "Authentication is required to unregister a DNS-SD service"
+msgstr "Pro zrušení registrace služby DNS-SD je vyžadováno ověření"
+
+#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
msgstr "Nastavit systémový Äas"
-#: ../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 "Pro nastavení systémového Äasu je vyžadováno ověření."
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:3
+#: src/timedate/org.freedesktop.timedate1.policy:33
msgid "Set system timezone"
msgstr "Nastavit systémovou Äasovou zónu"
-#: ../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 "Pro nastavení systémové Äasové zóny je vyžadováno ověření."
-#: ../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 "Nastavit RTC na lokální Äasovou zónu nebo UTC"
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:6
-msgid "Authentication is required to control whether the RTC stores the local or UTC time."
+#: src/timedate/org.freedesktop.timedate1.policy:44
+msgid ""
+"Authentication is required to control whether the RTC stores the local or "
+"UTC time."
msgstr ""
-"Pro kontrolu jestli RTC ukládá lokální Äasovou zónu nebo UTC Äas je vyžadováno ověření."
+"Pro kontrolu jestli RTC ukládá lokální Äasovou zónu nebo UTC Äas je "
+"vyžadováno ověření."
-#: ../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 "Zapnout nebo vypnout synchronizaci s Äasem ze sítÄ›"
-#: ../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."
+"Authentication is required to control whether network time synchronization "
+"shall be enabled."
msgstr "Pro kontrolu synchronizace Äasu ze sítÄ› je vyžadováno ověření."
-#: ../src/core/dbus-unit.c:458
+#: src/core/dbus-unit.c:326
msgid "Authentication is required to start '$(unit)'."
msgstr "Pro spuštění „$(unit)†je vyžadováno ověření."
-#: ../src/core/dbus-unit.c:459
+#: src/core/dbus-unit.c:327
msgid "Authentication is required to stop '$(unit)'."
msgstr "Pro vypnutí „$(unit)†je vyžadováno ověření."
-#: ../src/core/dbus-unit.c:460
+#: src/core/dbus-unit.c:328
msgid "Authentication is required to reload '$(unit)'."
msgstr "Pro znovu naÄtení „$(unit)†je vyžadováno ověření."
-#: ../src/core/dbus-unit.c:461 ../src/core/dbus-unit.c:462
+#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330
msgid "Authentication is required to restart '$(unit)'."
msgstr "Pro restart „$(unit)†je vyžadováno ověření."
-#: ../src/core/dbus-unit.c:569
-msgid "Authentication is required to kill '$(unit)'."
-msgstr "Pro ukonÄení „$(unit)†je vyžadováno ověření."
+#: src/core/dbus-unit.c:437
+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:600
+#: src/core/dbus-unit.c:468
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:633
+#: src/core/dbus-unit.c:501
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Pro nastavení vlastností na „$(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í."
diff --git a/po/da.po b/po/da.po
index 20032945a9..cc23447129 100644
--- a/po/da.po
+++ b/po/da.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Danish translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Daniel Machon <dmachon.dev@gmail.com>, 2015.
#
msgid ""
@@ -11,7 +10,7 @@ msgstr ""
"POT-Creation-Date: 2015-10-07 19:30+0000\n"
"PO-Revision-Date: 2015-10-07 19:30+0200\n"
"Last-Translator: Daniel Machon <dmachon.dev@gmail.com>\n"
-"Language-Team: danish\n"
+"Language-Team: Danish\n"
"Language: da\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
diff --git a/po/de.po b/po/de.po
index c3d15c929a..2a0fb880b1 100644
--- a/po/de.po
+++ b/po/de.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# German translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Christian Kirbach <Christian.Kirbach@gmail.com>, 2014, 2015.
# Benjamin Steinwender <b@stbe.at>, 2014.
# Bernd Homuth <dev@hmt.im>, 2015.
diff --git a/po/el.po b/po/el.po
index f4d9560af9..271a5d4203 100644
--- a/po/el.po
+++ b/po/el.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Greek translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Dimitris Spingos <dmtrs32@gmail.com>, 2014.
# Dimitris Spingos (ΔημήτÏης Σπίγγος) <dmtrs32@gmail.com>, 2014.
msgid ""
diff --git a/po/es.po b/po/es.po
index 22cdb40fc7..786e0d2ce7 100644
--- a/po/es.po
+++ b/po/es.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Spanish translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Alex Puchades <alex94puchades@gmail.com>, 2015.
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2015.
# Ãlex Puchades <alex94puchades@gmail.com>, 2015.
diff --git a/po/fr.po b/po/fr.po
index 284fcca9b0..ca35901602 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -2,15 +2,13 @@
#
# French translations for systemd package
# Traductions françaises du paquet systemd.
-# This file is distributed under the same license as the systemd package.
-# Sylvain Plantefève <sylvain.plantefeve@gmail.com>, 2013-2018
#
msgid ""
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-06-19 20:57+0200\n"
-"PO-Revision-Date: 2018-05-29 21:24+0200\n"
+"POT-Creation-Date: 2018-11-02 14:28+0100\n"
+"PO-Revision-Date: 2018-11-02 14:32+0100\n"
"Last-Translator: Sylvain Plantefève <sylvain.plantefeve@gmail.com>\n"
"Language-Team: French\n"
"Language: fr\n"
@@ -98,6 +96,14 @@ msgid "Authentication is required to set local machine information."
msgstr ""
"Authentification requise pour définir les informations sur la machine locale."
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "Obtenir l'UUID du produit"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "Authentification requise pour obtenir l'UUID du produit."
+
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
msgstr "Importer une image de machine virtuelle (VM) ou de conteneur"
@@ -688,8 +694,12 @@ msgid "Authentication is required to restart '$(unit)'."
msgstr "Authentification requise pour redémarrer « $(unit) »."
#: src/core/dbus-unit.c:437
-msgid "Authentication is required to kill '$(unit)'."
-msgstr "Authentification requise pour tuer « $(unit) »."
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
+msgstr ""
+"Authentification requise pour envoyer un signal UNIX aux processus de "
+"« $(unit) »."
#: src/core/dbus-unit.c:468
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
@@ -701,6 +711,9 @@ msgstr ""
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Authentification requise pour définir des propriétés de « $(unit) »."
+#~ msgid "Authentication is required to kill '$(unit)'."
+#~ msgstr "Authentification requise pour tuer « $(unit) »."
+
#~ msgid "Press Ctrl+C to cancel all filesystem checks in progress"
#~ msgstr ""
#~ "Appuyez sur Ctrl+C pour annuler toutes vérifications en cours du système "
diff --git a/po/gl.po b/po/gl.po
index bb7a6e678f..4b09007afb 100644
--- a/po/gl.po
+++ b/po/gl.po
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1+
#
-# This file is distributed under the same license as the systemd package.
# Fran Dieguez <frandieguez@gnome.org>, 2015.
msgid ""
msgstr ""
diff --git a/po/hu.po b/po/hu.po
index 2e4c411257..98090bab60 100644
--- a/po/hu.po
+++ b/po/hu.po
@@ -2,7 +2,6 @@
#
# Hungarian translation of systemd
# Copyright © 2015, 2016. Free Software Foundation, Inc.
-# This file is distributed under the same license as the systemd package.
#
# Gabor Kelemen <kelemeng at gnome dot hu>, 2015, 2016.
# Balázs Úr <urbalazs at gmail dot com>, 2016.
diff --git a/po/id.po b/po/id.po
index a26fd9c9c2..2e06e3bff0 100644
--- a/po/id.po
+++ b/po/id.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Indonesian translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Andika Triwidada <andika@gmail.com>, 2014.
#
msgid ""
diff --git a/po/it.po b/po/it.po
index de0b0b6ff9..40f87d6880 100644
--- a/po/it.po
+++ b/po/it.po
@@ -2,15 +2,14 @@
#
# Italian translation for systemd package
# Traduzione in italiano per il pacchetto systemd
-# This file is distributed under the same license as the systemd package.
# Daniele Medri <dmedri@gmail.com>, 2013-2018.
#
msgid ""
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-02-23 09:35+0100\n"
-"PO-Revision-Date: 2018-02-23 09:45+0100\n"
+"POT-Creation-Date: 2018-08-30 13:50+0200\n"
+"PO-Revision-Date: 2018-08-30 14:15+0200\n"
"Last-Translator: Daniele Medri <dmedri@gmail.com>\n"
"Language-Team: Italian\n"
"Language: it\n"
@@ -18,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Poedit 2.0.3\n"
+"X-Generator: Poedit 2.0.9\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
@@ -50,40 +49,40 @@ msgstr ""
"Autenticazione richiesta per gestire i file dei servizi o delle unità di "
"sistema."
-#: src/core/org.freedesktop.systemd1.policy.in:53
+#: src/core/org.freedesktop.systemd1.policy.in:54
msgid "Set or unset system and service manager environment variables"
msgstr ""
"Configura le variabili d'ambiente per la gestione dei servizi e del sistema"
-#: src/core/org.freedesktop.systemd1.policy.in:54
+#: src/core/org.freedesktop.systemd1.policy.in:55
msgid ""
"Authentication is required to set or unset system and service manager "
"environment variables."
msgstr ""
"Autenticazione richiesta per configurare le variabili d'ambiente per la "
-"gestione dei servizi e del sistema"
+"gestione dei servizi e del sistema."
-#: src/core/org.freedesktop.systemd1.policy.in:63
+#: src/core/org.freedesktop.systemd1.policy.in:64
msgid "Reload the systemd state"
msgstr "Ricarica lo stato di systemd"
-#: src/core/org.freedesktop.systemd1.policy.in:64
+#: src/core/org.freedesktop.systemd1.policy.in:65
msgid "Authentication is required to reload the systemd state."
msgstr "Autenticazione richiesta per riavviare lo stato di sistemd."
-#: src/hostname/org.freedesktop.hostname1.policy:22
+#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Configura il nome host"
-#: src/hostname/org.freedesktop.hostname1.policy:23
+#: src/hostname/org.freedesktop.hostname1.policy:21
msgid "Authentication is required to set the local host name."
msgstr "Autenticazione richiesta per configurare il nome host locale."
-#: src/hostname/org.freedesktop.hostname1.policy:32
+#: src/hostname/org.freedesktop.hostname1.policy:30
msgid "Set static host name"
msgstr "Configura il nome host statico"
-#: src/hostname/org.freedesktop.hostname1.policy:33
+#: 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."
@@ -91,16 +90,24 @@ msgstr ""
"Autenticazione richiesta per configurare staticamente il nome host locale e "
"il nome host descrittivo."
-#: src/hostname/org.freedesktop.hostname1.policy:43
+#: src/hostname/org.freedesktop.hostname1.policy:41
msgid "Set machine information"
msgstr "Configura le informazioni sulla macchina"
-#: src/hostname/org.freedesktop.hostname1.policy:44
+#: src/hostname/org.freedesktop.hostname1.policy:42
msgid "Authentication is required to set local machine information."
msgstr ""
"Autenticazione richiesta per configurare le informazioni sulla macchina "
"locale."
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "Ottieni UUID prodotto"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "Autenticazione richiesta per ottenere UUID prodotto."
+
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
msgstr "Importa un'immagine VM o un container"
@@ -564,6 +571,37 @@ msgstr ""
"Autenticazione richiesta per gestire le immagini delle virtual machine e dei "
"container locali."
+#: src/portable/org.freedesktop.portable1.policy:13
+msgid "Inspect a portable service image"
+msgstr "Ispeziona un'immagine di servizio portabile"
+
+#: src/portable/org.freedesktop.portable1.policy:14
+msgid "Authentication is required to inspect a portable service image."
+msgstr ""
+"Autenticazione richiesta per ispezionare un'immagine di servizio portabile."
+
+#: src/portable/org.freedesktop.portable1.policy:23
+msgid "Attach or detach a portable service image"
+msgstr "Collega o meno un'immagine di servizio portabile"
+
+#: src/portable/org.freedesktop.portable1.policy:24
+msgid ""
+"Authentication is required to attach or detach a portable service image."
+msgstr ""
+"Autenticazione richiesta per collegare o meno un'immagine di servizio "
+"portabile."
+
+#: src/portable/org.freedesktop.portable1.policy:34
+msgid "Delete or modify portable service image"
+msgstr "Elimina o modifica un'immagine di servizio portabile"
+
+#: src/portable/org.freedesktop.portable1.policy:35
+msgid ""
+"Authentication is required to delete or modify a portable service image."
+msgstr ""
+"Autenticazione richiesta per eliminare o modificare un'immagine di servizio "
+"portabile."
+
#: src/resolve/org.freedesktop.resolve1.policy:22
msgid "Register a DNS-SD service"
msgstr "Registra un servizio DNS-SD"
@@ -611,11 +649,11 @@ msgstr ""
"Autenticazione richiesta per verificare se l'orologio di sistema (RTC) è "
"configurato all'orario locale o al tempo civile (UTC)."
-#: src/timedate/org.freedesktop.timedate1.policy:54
+#: src/timedate/org.freedesktop.timedate1.policy:53
msgid "Turn network time synchronization on or off"
msgstr "Abilita o meno la sincronizzazione dell'orario in rete"
-#: src/timedate/org.freedesktop.timedate1.policy:55
+#: src/timedate/org.freedesktop.timedate1.policy:54
msgid ""
"Authentication is required to control whether network time synchronization "
"shall be enabled."
@@ -623,31 +661,31 @@ msgstr ""
"Autenticazione richiesta per verificare se la sincronizzazione dell'orario "
"in rete possa essere attivata."
-#: src/core/dbus-unit.c:496
+#: src/core/dbus-unit.c:326
msgid "Authentication is required to start '$(unit)'."
msgstr "Autenticazione richiesta per avviare '$(unit)'."
-#: src/core/dbus-unit.c:497
+#: src/core/dbus-unit.c:327
msgid "Authentication is required to stop '$(unit)'."
msgstr "Autenticazione richiesta per fermare '$(unit)'."
-#: src/core/dbus-unit.c:498
+#: src/core/dbus-unit.c:328
msgid "Authentication is required to reload '$(unit)'."
msgstr "Autenticazione richiesta per ricaricare '$(unit)'."
-#: src/core/dbus-unit.c:499 src/core/dbus-unit.c:500
+#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330
msgid "Authentication is required to restart '$(unit)'."
msgstr "Autenticazione richiesta per riavviare '$(unit)'."
-#: src/core/dbus-unit.c:607
+#: src/core/dbus-unit.c:437
msgid "Authentication is required to kill '$(unit)'."
msgstr "Autenticazione richiesta per terminare '$(unit)'."
-#: src/core/dbus-unit.c:638
+#: src/core/dbus-unit.c:468
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:671
+#: src/core/dbus-unit.c:501
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Autenticazione richiesta per configurare le proprietà di '$(unit)'."
diff --git a/po/ja.po b/po/ja.po
index d0799b1255..731657d690 100644
--- a/po/ja.po
+++ b/po/ja.po
@@ -1,15 +1,13 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Japanese translation for systemd.
-# This file is distributed under the same license as the systemd package.
-# Yu Watanabe <watanabe.yu+github@gmail.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2018-02-27 16:46+0900\n"
-"PO-Revision-Date: 2018-05-28 10:30+0900\n"
+"POT-Creation-Date: 2018-10-27 07:32+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"
"Language: ja\n"
@@ -86,6 +84,14 @@ msgstr "マシン情報ã®è¨­å®š"
msgid "Authentication is required to set local machine information."
msgstr "マシン情報を設定ã™ã‚‹ã«ã¯èªè¨¼ãŒå¿…è¦ã§ã™ã€‚"
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "プロダクトUUIDã‚’å–å¾—ã™ã‚‹"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "プロダクトUUIDã‚’å–å¾—ã™ã‚‹ã«ã¯èªè¨¼ãŒå¿…è¦ã§ã™ã€‚"
+
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
msgstr "仮想マシンもã—ãã¯ã‚³ãƒ³ãƒ†ãƒŠã‚¤ãƒ¡ãƒ¼ã‚¸ã®èª­è¾¼"
@@ -580,8 +586,10 @@ msgid "Authentication is required to restart '$(unit)'."
msgstr "'$(unit)'ã‚’å†èµ·å‹•ã™ã‚‹ã«ã¯èªè¨¼ãŒå¿…è¦ã§ã™ã€‚"
#: src/core/dbus-unit.c:437
-msgid "Authentication is required to kill '$(unit)'."
-msgstr "'$(unit)'を強制åœæ­¢ã™ã‚‹ã«ã¯èªè¨¼ãŒå¿…è¦ã§ã™ã€‚"
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
+msgstr "'$(unit)'ã®ãƒ—ロセスã«UNIXシグナルをé€ã‚‹ã«ã¯èªè¨¼ãŒå¿…è¦ã§ã™ã€‚"
#: src/core/dbus-unit.c:468
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
diff --git a/po/ko.po b/po/ko.po
index f05083a69c..e2805462d5 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Korean translation for the systemd.
-# This file is distributed under the same license as the systemd package.
# Seong-ho Cho <shcho@gnome.org>, 2015.
# Dongsu Park <dongsu@endocode.com>, 2015.
#
diff --git a/po/lt.po b/po/lt.po
new file mode 100644
index 0000000000..13e8f3e74f
--- /dev/null
+++ b/po/lt.po
@@ -0,0 +1,691 @@
+# Moo, 2018. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: systemd\n"
+"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
+"POT-Creation-Date: 2018-10-31 03:25+0000\n"
+"PO-Revision-Date: 2018-10-31 14:50+0200\n"
+"Last-Translator: Moo\n"
+"Language-Team: Lithuanian\n"
+"Language: lt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n"
+"%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: src/core/org.freedesktop.systemd1.policy.in:22
+msgid "Send passphrase back to system"
+msgstr "Siųsti slaptafrazę atgal į sistemą"
+
+#: src/core/org.freedesktop.systemd1.policy.in:23
+msgid ""
+"Authentication is required to send the entered passphrase back to the system."
+msgstr ""
+"Norint siųsti įvestą slaptafrazę atgal į sistemą, reikia nustatyti tapatybę."
+
+#: src/core/org.freedesktop.systemd1.policy.in:33
+msgid "Manage system services or other units"
+msgstr "Tvarkyti sistemos tarnybas ar kitus įtaisus"
+
+#: src/core/org.freedesktop.systemd1.policy.in:34
+msgid "Authentication is required to manage system services or other units."
+msgstr ""
+"Norint tvarkyti sistemos tarnybas ar kitus įtaisus, reikia nustatyti "
+"tapatybÄ™."
+
+#: src/core/org.freedesktop.systemd1.policy.in:43
+msgid "Manage system service or unit files"
+msgstr "Tvarkyti sistemos tarnybos ar įtaiso failus"
+
+#: src/core/org.freedesktop.systemd1.policy.in:44
+msgid "Authentication is required to manage system service or unit files."
+msgstr ""
+"Norint tvarkyti sistemos tarnybos ar įtaiso failus, reikia nustatyti "
+"tapatybÄ™."
+
+#: src/core/org.freedesktop.systemd1.policy.in:54
+msgid "Set or unset system and service manager environment variables"
+msgstr ""
+"Nustatyti ar atšaukti sistemos ir tarnybų tvarkytuvės aplinkos kintamųjų "
+"nustatymÄ…"
+
+#: src/core/org.freedesktop.systemd1.policy.in:55
+msgid ""
+"Authentication is required to set or unset system and service manager "
+"environment variables."
+msgstr ""
+"Norint nustatyti ar atšaukti sistemos ir tarnybų tvarkytuvės aplinkos "
+"kintamųjų nustatymą, reikia nustatyti tapatybę."
+
+#: src/core/org.freedesktop.systemd1.policy.in:64
+msgid "Reload the systemd state"
+msgstr "Iš naujo įkelti systemd būseną"
+
+#: src/core/org.freedesktop.systemd1.policy.in:65
+msgid "Authentication is required to reload the systemd state."
+msgstr "Norint iš naujo įkelti systemd būseną, reikia patvirtinti tapatybę."
+
+#: src/hostname/org.freedesktop.hostname1.policy:20
+msgid "Set host name"
+msgstr "Nustatyti serverio pavadinimÄ…"
+
+#: src/hostname/org.freedesktop.hostname1.policy:21
+msgid "Authentication is required to set the local host name."
+msgstr ""
+"Norint nustatyti vietinio serverio pavadinimÄ…, reikia nustatyti tapatybÄ™."
+
+#: src/hostname/org.freedesktop.hostname1.policy:30
+msgid "Set static host name"
+msgstr "Nustatyti statinį serverio pavadinimą"
+
+#: src/hostname/org.freedesktop.hostname1.policy:31
+msgid ""
+"Authentication is required to set the statically configured local host name, "
+"as well as the pretty host name."
+msgstr ""
+"Norint nustatyti statiškai sukonfigūruotą serverio pavadinimą, o taip pat "
+"lengvai įsimenamą serverio pavadinimą, reikia nustatyti tapatybę."
+
+#: src/hostname/org.freedesktop.hostname1.policy:41
+msgid "Set machine information"
+msgstr "Nustatyti kompiuterio informacijÄ…"
+
+#: src/hostname/org.freedesktop.hostname1.policy:42
+msgid "Authentication is required to set local machine information."
+msgstr "Norint nustatyti kompiuterio informacijÄ…, reikia nustatyti tapatybÄ™."
+
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "Gauti produkto UUID"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "Norint gauti produkto UUID, reikia nustatyti tapatybÄ™."
+
+#: src/import/org.freedesktop.import1.policy:22
+msgid "Import a VM or container image"
+msgstr "Importuoti VM ar konteinerio atvaizdį"
+
+#: src/import/org.freedesktop.import1.policy:23
+msgid "Authentication is required to import a VM or container image"
+msgstr ""
+"Norint importuoti VM ar konteinerio atvaizdį, reikia nustatyti tapatybę"
+
+#: src/import/org.freedesktop.import1.policy:32
+msgid "Export a VM or container image"
+msgstr "Eksportuoti VM ar konteinerio atvaizdį"
+
+#: src/import/org.freedesktop.import1.policy:33
+msgid "Authentication is required to export a VM or container image"
+msgstr ""
+"Norint eksportuoti VM ar konteinerio atvaizdį, reikia nustatyti tapatybę"
+
+#: src/import/org.freedesktop.import1.policy:42
+msgid "Download a VM or container image"
+msgstr "Atsisiųsti VM ar konteinerio atvaizdį"
+
+#: src/import/org.freedesktop.import1.policy:43
+msgid "Authentication is required to download a VM or container image"
+msgstr ""
+"Norint atsisiųsti VM ar konteinerio atvaizdį, reikia nustatyti tapatybę"
+
+#: src/locale/org.freedesktop.locale1.policy:22
+msgid "Set system locale"
+msgstr "Nustatyti sistemos lokalÄ™"
+
+#: src/locale/org.freedesktop.locale1.policy:23
+msgid "Authentication is required to set the system locale."
+msgstr "Norint nustatyti sistemos lokalÄ™, reikia nustatyti tapatybÄ™."
+
+#: src/locale/org.freedesktop.locale1.policy:33
+msgid "Set system keyboard settings"
+msgstr "Nustatyti sistemos klaviatūros nustatymus"
+
+#: src/locale/org.freedesktop.locale1.policy:34
+msgid "Authentication is required to set the system keyboard settings."
+msgstr ""
+"Norint nustatyti sistemos klaviatūros nustatymus, reikia nustatyti tapatybę."
+
+#: src/login/org.freedesktop.login1.policy:22
+msgid "Allow applications to inhibit system shutdown"
+msgstr "Leisti programoms sulaikyti sistemos išjungimą"
+
+#: src/login/org.freedesktop.login1.policy:23
+msgid ""
+"Authentication is required for an application to inhibit system shutdown."
+msgstr ""
+"Norint leisti programai sulaikyti sistemos išjungimą, reikia nustatyti "
+"tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:33
+msgid "Allow applications to delay system shutdown"
+msgstr "Leisti programoms atidėti sistemos išjungimą"
+
+#: src/login/org.freedesktop.login1.policy:34
+msgid "Authentication is required for an application to delay system shutdown."
+msgstr ""
+"Norint leisti programai atidėti sistemos išjungimą, reikia nustatyti "
+"tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:44
+msgid "Allow applications to inhibit system sleep"
+msgstr "Leisti programoms sulaikyti sistemos miegÄ…"
+
+#: src/login/org.freedesktop.login1.policy:45
+msgid "Authentication is required for an application to inhibit system sleep."
+msgstr ""
+"Norint leisti programai sulaikyti sistemos miegÄ…, reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:55
+msgid "Allow applications to delay system sleep"
+msgstr "Leisti programoms atidÄ—ti sistemos miegÄ…"
+
+#: src/login/org.freedesktop.login1.policy:56
+msgid "Authentication is required for an application to delay system sleep."
+msgstr ""
+"Norint leisti programai atidÄ—ti sistemos miegÄ…, reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:65
+msgid "Allow applications to inhibit automatic system suspend"
+msgstr "Leisti programoms sulaikyti automatinį sistemos pristabdymą"
+
+#: src/login/org.freedesktop.login1.policy:66
+msgid ""
+"Authentication is required for an application to inhibit automatic system "
+"suspend."
+msgstr ""
+"Norint leisti programai sulaikyti automatinį sistemos pristabdymą, reikia "
+"nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:75
+msgid "Allow applications to inhibit system handling of the power key"
+msgstr "Leisti programoms sulaikyti maitinimo rakto sisteminį apdorojimą"
+
+#: src/login/org.freedesktop.login1.policy:76
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the power key."
+msgstr ""
+"Norint leisti programai sulaikyti maitinimo rakto sisteminį apdorojimą, "
+"reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:86
+msgid "Allow applications to inhibit system handling of the suspend key"
+msgstr "Leisti programoms sulaikyti pristabdymo rakto sisteminį apdorojimą"
+
+#: src/login/org.freedesktop.login1.policy:87
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the suspend key."
+msgstr ""
+"Norint leisti programai sulaikyti pristabdymo rakto sisteminį apdorojimą, "
+"reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:97
+msgid "Allow applications to inhibit system handling of the hibernate key"
+msgstr "Leisti programoms sulaikyti užmigdymo rakto sisteminį apdorojimą"
+
+#: src/login/org.freedesktop.login1.policy:98
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the hibernate key."
+msgstr ""
+"Norint leisti programai sulaikyti užmigdymo rakto sisteminį apdorojimą, "
+"reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:107
+msgid "Allow applications to inhibit system handling of the lid switch"
+msgstr "Leisti programoms sulaikyti dangÄio perjungiklio sisteminį apdorojimÄ…"
+
+#: src/login/org.freedesktop.login1.policy:108
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the lid switch."
+msgstr ""
+"Norint leisti programai sulaikyti dangÄio perjungiklio sisteminį apdorojimÄ…, "
+"reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:117
+msgid "Allow non-logged-in user to run programs"
+msgstr "Leisti neprisijungusiam naudotojui vykdyti programas"
+
+#: src/login/org.freedesktop.login1.policy:118
+msgid "Explicit request is required to run programs as a non-logged-in user."
+msgstr ""
+"Norint vykdyti programas kaip neprisijungusiam naudotojui, reikia aiškiai "
+"išreikštos užklausos."
+
+#: src/login/org.freedesktop.login1.policy:127
+msgid "Allow non-logged-in users to run programs"
+msgstr "Leisti neprisijungusiems naudotojams vykdyti programas"
+
+#: src/login/org.freedesktop.login1.policy:128
+msgid "Authentication is required to run programs as a non-logged-in user."
+msgstr ""
+"Norint vykdyti programas kaip neprisijungusiems naudotojams, reikia "
+"nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:137
+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."
+msgstr ""
+"Norint prijungti įrenginį prie darbo vietos, reikia nustatyti tapatybę."
+
+#: src/login/org.freedesktop.login1.policy:148
+msgid "Flush device to seat attachments"
+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."
+msgstr ""
+"Norint atstatyti tai, kaip įrenginiai yra prijungti prie darbo vietų, reikia "
+"nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:158
+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."
+msgstr "Norint išjungti sistemos maitinimą, reikia nustatyti tapatybę."
+
+#: src/login/org.freedesktop.login1.policy:169
+msgid "Power off the system while other users are logged in"
+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 "
+"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"
+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."
+msgstr ""
+"Norint išjungti sistemos maitinimą, nepaisant to, kad programa paprašė tai "
+"sulaikyti, reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:191
+msgid "Reboot the system"
+msgstr "Paleisti sistemą iš naujo"
+
+#: src/login/org.freedesktop.login1.policy:192
+msgid "Authentication is required for rebooting the system."
+msgstr "Norint paleisti sistemą iš naujo, reikia nustatyti tapatybę."
+
+#: src/login/org.freedesktop.login1.policy:202
+msgid "Reboot the system while other users are logged in"
+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 "
+"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"
+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."
+msgstr ""
+"Norint paleisti sistemą iš naujo, nepaisant to, kad programa paprašė tai "
+"sulaikyti, reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:224
+msgid "Halt the system"
+msgstr "Stabdyti sistemÄ…"
+
+#: src/login/org.freedesktop.login1.policy:225
+msgid "Authentication is required for halting the system."
+msgstr "Norint stabdyti sistemÄ…, reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:235
+msgid "Halt the system while other users are logged in"
+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 "
+"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"
+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 "
+"to inhibit it."
+msgstr ""
+"Norint stabdyti sistemą, nepaisant to, kad programa paprašė tai sulaikyti, "
+"reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:257
+msgid "Suspend the system"
+msgstr "Pristabdyti sistemÄ…"
+
+#: src/login/org.freedesktop.login1.policy:258
+msgid "Authentication is required for suspending the system."
+msgstr "Norint pristabdyti sistemÄ…, reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:267
+msgid "Suspend the system while other users are logged in"
+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 "
+"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"
+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."
+msgstr ""
+"Norint pristabdyti sistemą, nepaisant to, kad programa paprašė tai "
+"sulaikyti, reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:289
+msgid "Hibernate the system"
+msgstr "Užmigdyti sistemą"
+
+#: src/login/org.freedesktop.login1.policy:290
+msgid "Authentication is required for hibernating the system."
+msgstr "Norint užmigdyti sistemą, reikia nustatyti tapatybę."
+
+#: src/login/org.freedesktop.login1.policy:299
+msgid "Hibernate the system while other users are logged in"
+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 "
+"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"
+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."
+msgstr ""
+"Norint užmigdyti sistemą, nepaisant to, kad programa paprašė tai sulaikyti, "
+"reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:321
+msgid "Manage active sessions, users and seats"
+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."
+msgstr ""
+"Norint tvarkyti aktyvius seansus, naudotojus ir darbo vietas, reikia "
+"nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:331
+msgid "Lock or unlock active sessions"
+msgstr "Užrakinti ar atrakinti aktyvius seansus"
+
+#: src/login/org.freedesktop.login1.policy:332
+msgid "Authentication is required to lock or unlock active sessions."
+msgstr ""
+"Norint užrakinti ar atrakinti aktyvius seansus, reikia nustatyti tapatybę."
+
+#: src/login/org.freedesktop.login1.policy:341
+msgid "Allow indication to the firmware to boot to setup interface"
+msgstr ""
+"Leisti nurodymą programinei aparatinei įrangai pasileisti į sąrankos sąsają"
+
+#: src/login/org.freedesktop.login1.policy:342
+msgid ""
+"Authentication is required to indicate to the firmware to boot to setup "
+"interface."
+msgstr ""
+"Norint nurodyti programinei aparatinei įrangai pasileisti į sąrankos sąsają, "
+"reikia nustatyti tapatybÄ™."
+
+#: src/login/org.freedesktop.login1.policy:351
+msgid "Set a wall message"
+msgstr "Nustatyti sienos pranešimą"
+
+#: src/login/org.freedesktop.login1.policy:352
+msgid "Authentication is required to set a wall message"
+msgstr "Norint nustatyti sienos pranešimą, reikia nustatyti tapatybę"
+
+#: src/machine/org.freedesktop.machine1.policy:22
+msgid "Log into a local container"
+msgstr "Prisijungti prie vietinio konteinerio"
+
+#: src/machine/org.freedesktop.machine1.policy:23
+msgid "Authentication is required to log into a local container."
+msgstr ""
+"Norint prisijungti prie vietinio konteinerio, reikia nustatyti tapatybÄ™."
+
+#: src/machine/org.freedesktop.machine1.policy:32
+msgid "Log into the local host"
+msgstr "Prisijungti į vietinį serverį"
+
+#: src/machine/org.freedesktop.machine1.policy:33
+msgid "Authentication is required to log into the local host."
+msgstr "Norint prisijungti į vietinį serverį, reikia nustatyti tapatybę."
+
+#: src/machine/org.freedesktop.machine1.policy:42
+msgid "Acquire a shell in a local container"
+msgstr "Įgyti apvalkalą vietiniame konteineryje"
+
+#: src/machine/org.freedesktop.machine1.policy:43
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr ""
+"Norint įgyti apvalkalą vietiniame konteineryje, reikia nustatyti tapatybę."
+
+#: src/machine/org.freedesktop.machine1.policy:53
+msgid "Acquire a shell on the local host"
+msgstr "Įgyti apvalkalą vietiniame serveryje"
+
+#: src/machine/org.freedesktop.machine1.policy:54
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr ""
+"Norint įgyti apvalkalą vietiniame serveryje, reikia nustatyti tapatybę."
+
+#: src/machine/org.freedesktop.machine1.policy:64
+msgid "Acquire a pseudo TTY in a local container"
+msgstr "Įgyti pseudo TTY vietiniame konteineryje"
+
+#: src/machine/org.freedesktop.machine1.policy:65
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr ""
+"Norint įgyti pseudo TTY vietiniame konteineryje, reikia nustatyti tapatybę."
+
+#: src/machine/org.freedesktop.machine1.policy:74
+msgid "Acquire a pseudo TTY on the local host"
+msgstr "Įgyti pseudo TTY vietiniame serveryje"
+
+#: src/machine/org.freedesktop.machine1.policy:75
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr ""
+"Norint įgyti pseudo TTY vietiniame serveryje, reikia nustatyti tapatybę."
+
+#: src/machine/org.freedesktop.machine1.policy:84
+msgid "Manage local virtual machines and containers"
+msgstr "Tvarkyti vietines virtualiąsias mašinas ir konteinerius"
+
+#: src/machine/org.freedesktop.machine1.policy:85
+msgid ""
+"Authentication is required to manage local virtual machines and containers."
+msgstr ""
+"Norint tvarkyti vietines virtualiąsias mašinas ir konteinerius, reikia "
+"nustatyti tapatybÄ™."
+
+#: src/machine/org.freedesktop.machine1.policy:95
+msgid "Manage local virtual machine and container images"
+msgstr "Tvarkyti vietinę virtualiąją mašiną ir konteinerio atvaizdžius"
+
+#: src/machine/org.freedesktop.machine1.policy:96
+msgid ""
+"Authentication is required to manage local virtual machine and container "
+"images."
+msgstr ""
+"Norint tvarkyti vietinę virtualiąją mašiną ir konteinerio atvaizdžius, "
+"reikia nustatyti tapatybÄ™."
+
+#: src/portable/org.freedesktop.portable1.policy:13
+msgid "Inspect a portable service image"
+msgstr "Išnagrinėti perkeliamos tarnybos atvaizdį"
+
+#: src/portable/org.freedesktop.portable1.policy:14
+msgid "Authentication is required to inspect a portable service image."
+msgstr ""
+"Norint išnagrinėti perkeliamos tarnybos atvaizdį, reikia nustatyti tapatybę."
+
+#: src/portable/org.freedesktop.portable1.policy:23
+msgid "Attach or detach a portable service image"
+msgstr "Prijungti ar atskirti perkeliamos tarnybos atvaizdį"
+
+#: src/portable/org.freedesktop.portable1.policy:24
+msgid ""
+"Authentication is required to attach or detach a portable service image."
+msgstr ""
+"Norint prijungti ar atskirti perkeliamos tarnybos atvaizdį, reikia nustatyti "
+"tapatybÄ™."
+
+#: src/portable/org.freedesktop.portable1.policy:34
+msgid "Delete or modify portable service image"
+msgstr "Ištrinti ar modifikuoti perkeliamos tarnybos atvaizdį"
+
+#: src/portable/org.freedesktop.portable1.policy:35
+msgid ""
+"Authentication is required to delete or modify a portable service image."
+msgstr ""
+"Norint ištrinti ar modifikuoti perkeliamos tarnybos atvaizdį, reikia "
+"nustatyti tapatybÄ™."
+
+#: src/resolve/org.freedesktop.resolve1.policy:22
+msgid "Register a DNS-SD service"
+msgstr "Registruoti DNS-SD tarnybÄ…"
+
+#: src/resolve/org.freedesktop.resolve1.policy:23
+msgid "Authentication is required to register a DNS-SD service"
+msgstr "Norint registruoti DNS-SD tarnybÄ…, reikia nustatyti tapatybÄ™"
+
+#: src/resolve/org.freedesktop.resolve1.policy:33
+msgid "Unregister a DNS-SD service"
+msgstr "IÅ¡registruoti DNS-SD tarnybÄ…"
+
+#: src/resolve/org.freedesktop.resolve1.policy:34
+msgid "Authentication is required to unregister a DNS-SD service"
+msgstr "Norint išregistruoti DNS-SD tarnybą, reikia nustatyti tapatybę"
+
+#: src/timedate/org.freedesktop.timedate1.policy:22
+msgid "Set system time"
+msgstr "Nustatyti sistemos laikÄ…"
+
+#: src/timedate/org.freedesktop.timedate1.policy:23
+msgid "Authentication is required to set the system time."
+msgstr "Norint nustatyti sistemos laikÄ…, reikia nustatyti tapatybÄ™."
+
+#: src/timedate/org.freedesktop.timedate1.policy:33
+msgid "Set system timezone"
+msgstr "Nustatyti sistemos laiko juostÄ…"
+
+#: src/timedate/org.freedesktop.timedate1.policy:34
+msgid "Authentication is required to set the system timezone."
+msgstr "Norint nustatyti sistemos laiko juostÄ…, reikia nustatyti tapatybÄ™."
+
+#: src/timedate/org.freedesktop.timedate1.policy:43
+msgid "Set RTC to local timezone or UTC"
+msgstr ""
+"Nustatyti tikrojo laiko daviklį (RTC) į vietinę laiko juostą ar suderintąjį "
+"pasaulinį laiką (UTC)"
+
+#: src/timedate/org.freedesktop.timedate1.policy:44
+msgid ""
+"Authentication is required to control whether the RTC stores the local or "
+"UTC time."
+msgstr ""
+"Norint valdyti ar tikrojo laiko daviklis (RTC) atmintyje saugos vietinį, ar "
+"suderintąjį pasaulinį laiką (UTC), reikia nustatyti tapatybę."
+
+#: src/timedate/org.freedesktop.timedate1.policy:53
+msgid "Turn network time synchronization on or off"
+msgstr "Įjungti ar išjungti tinklo laiko sinchronizavimą"
+
+#: src/timedate/org.freedesktop.timedate1.policy:54
+msgid ""
+"Authentication is required to control whether network time synchronization "
+"shall be enabled."
+msgstr ""
+"Norint valdyti ar tinklo laiko sinchronizavimas turėtų būti įjungtas, reikia "
+"nustatyti tapatybÄ™."
+
+#: src/core/dbus-unit.c:326
+msgid "Authentication is required to start '$(unit)'."
+msgstr "Norint paleisti \"$(unit)\", reikia nustatyti tapatybÄ™."
+
+#: src/core/dbus-unit.c:327
+msgid "Authentication is required to stop '$(unit)'."
+msgstr "Norint stabdyti \"$(unit)\", reikia nustatyti tapatybÄ™."
+
+#: src/core/dbus-unit.c:328
+msgid "Authentication is required to reload '$(unit)'."
+msgstr "Norint įkelti \"$(unit)\" iš naujo, reikia nustatyti tapatybę."
+
+#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330
+msgid "Authentication is required to restart '$(unit)'."
+msgstr "Norint paleisti \"$(unit)\" iš naujo, reikia nustatyti tapatybę."
+
+#: src/core/dbus-unit.c:437
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
+msgstr ""
+"Norint siųsti UNIX signalą į \"$(unit)\" procesus, reikia nustatyti tapatybę."
+
+#: src/core/dbus-unit.c:468
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr ""
+"Norint atstatyti \"$(unit)\" įtaiso \"failed\" būseną, reikia nustatyti "
+"tapatybÄ™."
+
+#: src/core/dbus-unit.c:501
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr "Norint nustatyti \"$(unit)\" savybes, reikia nustatyti tapatybÄ™."
+
+#~ msgid "Authentication is required to kill '$(unit)'."
+#~ msgstr "Norint nutraukti \"$(unit)\", reikia nustatyti tapatybÄ™."
diff --git a/po/pl.po b/po/pl.po
index 93dfbabfd9..80c9c97e97 100644
--- a/po/pl.po
+++ b/po/pl.po
@@ -1,16 +1,13 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Polish translation for systemd.
-# This file is distributed under the same license as the systemd package.
-# Piotr DrÄ…g <piotrdrag@gmail.com>, 2011, 2013-2018.
-# Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2018-06-19 15:00+0000\n"
-"PO-Revision-Date: 2018-06-19 17:05+0200\n"
+"POT-Creation-Date: 2018-10-26 19:14+0000\n"
+"PO-Revision-Date: 2018-10-26 21:15+0200\n"
"Last-Translator: Piotr DrÄ…g <piotrdrag@gmail.com>\n"
"Language-Team: Polish <trans-pl@lists.fedoraproject.org>\n"
"Language: pl\n"
@@ -101,6 +98,14 @@ msgid "Authentication is required to set local machine information."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ustawić informacje o lokalnym komputerze."
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "Uzyskanie UUID produktu"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "Wymagane jest uwierzytelnienie, aby uzyskać UUID produktu."
+
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
msgstr "Import obrazu maszyny wirtualnej lub kontenera"
@@ -667,9 +672,12 @@ msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie uruchomić jednostkÄ™ „$(unit)â€."
#: src/core/dbus-unit.c:437
-msgid "Authentication is required to kill '$(unit)'."
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
msgstr ""
-"Wymagane jest uwierzytelnienie, aby wymusić wyÅ‚Ä…czenie jednostki „$(unit)â€."
+"Wymagane jest uwierzytelnienie, aby wysłać sygnał uniksowy do procesów "
+"jednostki „$(unit)â€."
#: src/core/dbus-unit.c:468
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
diff --git a/po/pt_BR.po b/po/pt_BR.po
index c288a26bba..1f2fb3dd4e 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -1,22 +1,21 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Brazilian Portuguese translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Enrico Nicoletto <liverig@gmail.com>, 2014.
# Rafael Fontenelle <rafaelff@gnome.org>, 2015, 2017.
-# Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>, 2018. #zanata
+# Filipe Brandenburger <filbranden@gmail.com>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-02-19 13:32+0100\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
+"POT-Creation-Date: 2018-09-06 20:10-0700\n"
"PO-Revision-Date: 2018-02-19 10:36-0500\n"
-"Last-Translator: Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>\n"
+"Last-Translator: Filipe Brandenburger <filbranden@gmail.com>\n"
"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
"Language: pt-BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Zanata 3.9.6\n"
@@ -27,7 +26,6 @@ msgstr "Enviar frase secreta de volta ao sistema"
#: src/core/org.freedesktop.systemd1.policy.in:23
msgid ""
"Authentication is required to send the entered passphrase back to the system."
-""
msgstr ""
"É necessária autenticação para enviar a frase secreta informada de volta ao "
"sistema."
@@ -52,13 +50,13 @@ msgstr ""
"É necessária autenticação para gerenciar arquivos \"unit\" e \"service\" do "
"sistema."
-#: src/core/org.freedesktop.systemd1.policy.in:53
+#: src/core/org.freedesktop.systemd1.policy.in:54
msgid "Set or unset system and service manager environment variables"
msgstr ""
"Definir ou retirar definição de variáveis de ambiente de gerenciador de "
"serviço e sistema"
-#: src/core/org.freedesktop.systemd1.policy.in:54
+#: src/core/org.freedesktop.systemd1.policy.in:55
msgid ""
"Authentication is required to set or unset system and service manager "
"environment variables."
@@ -66,27 +64,27 @@ msgstr ""
"É necessária autenticação para definir ou retirar definição de variáveis de "
"ambiente de gerenciador de serviço e sistema."
-#: src/core/org.freedesktop.systemd1.policy.in:63
+#: src/core/org.freedesktop.systemd1.policy.in:64
msgid "Reload the systemd state"
msgstr "Recarregar o estado do sistema"
-#: src/core/org.freedesktop.systemd1.policy.in:64
+#: src/core/org.freedesktop.systemd1.policy.in:65
msgid "Authentication is required to reload the systemd state."
msgstr "É necessária autenticação para recarregar o estado do sistema."
-#: src/hostname/org.freedesktop.hostname1.policy:22
+#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Definir nome de máquina"
-#: src/hostname/org.freedesktop.hostname1.policy:23
+#: src/hostname/org.freedesktop.hostname1.policy:21
msgid "Authentication is required to set the local host name."
msgstr "É necessária autenticação para definir nome de máquina local."
-#: src/hostname/org.freedesktop.hostname1.policy:32
+#: src/hostname/org.freedesktop.hostname1.policy:30
msgid "Set static host name"
msgstr "Definir nome estático de máquina"
-#: src/hostname/org.freedesktop.hostname1.policy:33
+#: 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."
@@ -94,14 +92,22 @@ msgstr ""
"É necessária autenticação para definir o nome de máquina local configurado "
"estaticamente, assim como o nome apresentável de máquina."
-#: src/hostname/org.freedesktop.hostname1.policy:43
+#: src/hostname/org.freedesktop.hostname1.policy:41
msgid "Set machine information"
msgstr "Definir informações da máquina"
-#: src/hostname/org.freedesktop.hostname1.policy:44
+#: src/hostname/org.freedesktop.hostname1.policy:42
msgid "Authentication is required to set local machine information."
msgstr "É necessária autenticação para definir informações de máquina local."
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "Obter UUID do produto"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "É necessária autenticação para obter a UUID."
+
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
msgstr "Importar uma VM ou imagem contêiner"
@@ -134,7 +140,6 @@ msgstr "Definir configurações regionais do sistema"
msgid "Authentication is required to set the system locale."
msgstr ""
"É necessária autenticação para definir as configurações regionais do sistema."
-""
#: src/locale/org.freedesktop.locale1.policy:33
msgid "Set system keyboard settings"
@@ -162,8 +167,7 @@ msgid "Allow applications to delay system shutdown"
msgstr "Permitir que aplicativos atrasem o desligamento do sistema"
#: src/login/org.freedesktop.login1.policy:34
-msgid ""
-"Authentication is required for an application to delay system shutdown."
+msgid "Authentication is required for an application to delay system shutdown."
msgstr ""
"É necessária autenticação para que um aplicativo atrase o desligamento do "
"sistema."
@@ -281,8 +285,7 @@ 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."
-msgstr ""
-"É necessária autenticação para conectar um dispositivo em uma estação."
+msgstr "É necessária autenticação para conectar um dispositivo em uma estação."
#: src/login/org.freedesktop.login1.policy:148
msgid "Flush device to seat attachments"
@@ -361,31 +364,35 @@ msgstr ""
#: src/login/org.freedesktop.login1.policy:224
msgid "Halt the system"
-msgstr "Pare o sistema"
+msgstr "Parar o sistema"
#: src/login/org.freedesktop.login1.policy:225
msgid "Authentication is required for halting the system."
-msgstr ""
+msgstr "É necessária autenticação para parar o sistema."
#: src/login/org.freedesktop.login1.policy:235
msgid "Halt the system while other users are logged in"
-msgstr ""
+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 "
"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"
-msgstr ""
+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 "
"to inhibit it."
msgstr ""
+"É necessária autenticação para parar o sistema enquanto um aplicativo "
+"solicitou inibição."
#: src/login/org.freedesktop.login1.policy:257
msgid "Suspend the system"
@@ -460,7 +467,6 @@ msgid ""
"Authentication is required for managing active sessions, users and seats."
msgstr ""
"É necessária autenticação para gerenciar estações, usuários e sessões ativas."
-""
#: src/login/org.freedesktop.login1.policy:331
msgid "Lock or unlock active sessions"
@@ -568,21 +574,52 @@ msgstr ""
"É necessária autenticação para gerenciar máquinas virtuais locais e imagens "
"contêineres."
+#: src/portable/org.freedesktop.portable1.policy:13
+msgid "Inspect a portable service image"
+msgstr "Inspecionar uma imagem de serviço portável"
+
+#: src/portable/org.freedesktop.portable1.policy:14
+msgid "Authentication is required to inspect a portable service image."
+msgstr ""
+"É necessária autenticação para inspecionar uma imagem de serviço portável."
+
+#: src/portable/org.freedesktop.portable1.policy:23
+msgid "Attach or detach a portable service image"
+msgstr "Conectar ou desconectar uma imagem de serviço portável"
+
+#: src/portable/org.freedesktop.portable1.policy:24
+msgid ""
+"Authentication is required to attach or detach a portable service image."
+msgstr ""
+"É necessária autenticação para conectar ou desconectar uma imagem de serviço "
+"portável."
+
+#: src/portable/org.freedesktop.portable1.policy:34
+msgid "Delete or modify portable service image"
+msgstr "Remover ou modificar imagem de serviço portável"
+
+#: src/portable/org.freedesktop.portable1.policy:35
+msgid ""
+"Authentication is required to delete or modify a portable service image."
+msgstr ""
+"É necessária autenticação para remover ou modificar imagem de serviço "
+"portável."
+
#: src/resolve/org.freedesktop.resolve1.policy:22
msgid "Register a DNS-SD service"
-msgstr ""
+msgstr "Registrar um serviço DNS-SD"
#: src/resolve/org.freedesktop.resolve1.policy:23
msgid "Authentication is required to register a DNS-SD service"
-msgstr ""
+msgstr "É necessária autenticação para registrar um serviço DNS-SD"
#: src/resolve/org.freedesktop.resolve1.policy:33
msgid "Unregister a DNS-SD service"
-msgstr ""
+msgstr "Remover um serviço DNS-SD"
#: src/resolve/org.freedesktop.resolve1.policy:34
msgid "Authentication is required to unregister a DNS-SD service"
-msgstr ""
+msgstr "É necessária autenticação para remover um serviço DNS-SD"
#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
@@ -612,11 +649,11 @@ msgstr ""
"É necessária autenticação para controlar se o RTC deve, ou não, armazenar o "
"horário local ou de UTC."
-#: src/timedate/org.freedesktop.timedate1.policy:54
+#: src/timedate/org.freedesktop.timedate1.policy:53
msgid "Turn network time synchronization on or off"
msgstr "Ligar/desligar a sincronização do horário em rede"
-#: src/timedate/org.freedesktop.timedate1.policy:55
+#: src/timedate/org.freedesktop.timedate1.policy:54
msgid ""
"Authentication is required to control whether network time synchronization "
"shall be enabled."
@@ -624,31 +661,31 @@ msgstr ""
"É necessária autenticação para controlar se deve ser habilitada, ou não, a "
"sincronização de horário através de rede."
-#: src/core/dbus-unit.c:496
+#: src/core/dbus-unit.c:326
msgid "Authentication is required to start '$(unit)'."
msgstr "É necessária autenticação para iniciar '$(unit)'."
-#: src/core/dbus-unit.c:497
+#: src/core/dbus-unit.c:327
msgid "Authentication is required to stop '$(unit)'."
msgstr "É necessária autenticação para parar '$(unit)'."
-#: src/core/dbus-unit.c:498
+#: src/core/dbus-unit.c:328
msgid "Authentication is required to reload '$(unit)'."
msgstr "É necessária autenticação para recarregar '$(unit)'."
-#: src/core/dbus-unit.c:499 src/core/dbus-unit.c:500
+#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330
msgid "Authentication is required to restart '$(unit)'."
msgstr "É necessária autenticação para reiniciar '$(unit)'."
-#: src/core/dbus-unit.c:607
+#: src/core/dbus-unit.c:437
msgid "Authentication is required to kill '$(unit)'."
msgstr "É necessária autenticação para matar '$(unit)'."
-#: src/core/dbus-unit.c:638
+#: src/core/dbus-unit.c:468
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"É necessária autenticação para reiniciar o estado \"failed\" de '$(unit)'."
-#: src/core/dbus-unit.c:671
+#: src/core/dbus-unit.c:501
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "É necessária autenticação para definir propriedades em '$(unit)'."
diff --git a/po/ro.po b/po/ro.po
index a4d7a5a4a0..fb54fe018c 100644
--- a/po/ro.po
+++ b/po/ro.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Romanian translation for systemd.
-# This file is distributed under the same license as the systemd package.
# va511e <va511e@yahoo.com>, 2015.
# Daniel Șerbănescu <daniel [at] serbanescu [dot] dk>, 2015, 2017.
msgid ""
diff --git a/po/ru.po b/po/ru.po
index 826952b2c8..bda6a45627 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -9,7 +9,7 @@ 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-06-16 00:07+0300\n"
+"PO-Revision-Date: 2018-09-01 18:46+0300\n"
"Last-Translator: Sergey Ptashnick <0comffdiz@inbox.ru>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
@@ -69,19 +69,19 @@ msgstr ""
"Чтобы заÑтавить systemd перечитать конфигурацию, необходимо пройти "
"аутентификацию."
-#: src/hostname/org.freedesktop.hostname1.policy:22
+#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "ÐаÑтроить Ð¸Ð¼Ñ ÐºÐ¾Ð¼Ð¿ÑŒÑŽÑ‚ÐµÑ€Ð°"
-#: src/hostname/org.freedesktop.hostname1.policy:23
+#: src/hostname/org.freedesktop.hostname1.policy:21
msgid "Authentication is required to set the local host name."
msgstr "Чтобы наÑтроить Ð¸Ð¼Ñ ÐºÐ¾Ð¼Ð¿ÑŒÑŽÑ‚ÐµÑ€Ð°, необходимо пройти аутентификацию."
-#: src/hostname/org.freedesktop.hostname1.policy:32
+#: src/hostname/org.freedesktop.hostname1.policy:30
msgid "Set static host name"
msgstr "ÐаÑтроить ÑтатичеÑкое Ð¸Ð¼Ñ ÐºÐ¾Ð¼Ð¿ÑŒÑŽÑ‚ÐµÑ€Ð°"
-#: src/hostname/org.freedesktop.hostname1.policy:33
+#: 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."
@@ -89,15 +89,23 @@ msgstr ""
"Чтобы наÑтроить ÑтатичеÑкое Ð¸Ð¼Ñ ÐºÐ¾Ð¼Ð¿ÑŒÑŽÑ‚ÐµÑ€Ð°, а также его «краÑивое» имÑ, "
"необходимо пройти аутентификацию."
-#: src/hostname/org.freedesktop.hostname1.policy:43
+#: src/hostname/org.freedesktop.hostname1.policy:41
msgid "Set machine information"
msgstr "ÐаÑтроить информацию о компьютере"
-#: src/hostname/org.freedesktop.hostname1.policy:44
+#: src/hostname/org.freedesktop.hostname1.policy:42
msgid "Authentication is required to set local machine information."
msgstr ""
"Чтобы наÑтроить информацию о компьютере, необходимо пройти аутентификацию."
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "Получить UUID продукта"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "Чтобы получить UUID продукта, необходимо пройти аутентификацию."
+
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
msgstr "Импортировать образ виртуальной машины или контейнера"
@@ -602,22 +610,23 @@ msgstr ""
"аутентификацию."
#: src/portable/org.freedesktop.portable1.policy:13
-msgid "Inspect a portable service"
+msgid "Inspect a portable service image"
msgstr "Прочитать образ переноÑимой Ñлужбы"
#: src/portable/org.freedesktop.portable1.policy:14
-msgid "Authentication is required to inspect a portable service."
+msgid "Authentication is required to inspect a portable service image."
msgstr "Чтобы прочитать образ переноÑимой Ñлужбы, необходимо пройти "
"аутентификацию."
#: src/portable/org.freedesktop.portable1.policy:23
-msgid "Attach or detach a portable service"
-msgstr "Подключить или отключить переноÑимую Ñлужбу"
+msgid "Attach or detach a portable service image"
+msgstr "Подключить или отключить образ переноÑимой Ñлужбы"
#: src/portable/org.freedesktop.portable1.policy:24
-msgid "Authentication is required to attach or detach a portable service."
-msgstr "Чтобы подключить или отключить переноÑимую Ñлужбу, необходимо пройти "
-"аутентификацию."
+msgid ""
+"Authentication is required to attach or detach a portable service image."
+msgstr "Чтобы подключить или отключить образ переноÑимой Ñлужбы, необходимо "
+"пройти аутентификацию."
#: src/portable/org.freedesktop.portable1.policy:34
msgid "Delete or modify portable service image"
@@ -687,35 +696,35 @@ msgstr ""
"Чтобы включить или выключить Ñинхронизацию времени по Ñети, необходимо "
"пройти аутентификацию."
-#: src/core/dbus-unit.c:329
+#: src/core/dbus-unit.c:326
msgid "Authentication is required to start '$(unit)'."
msgstr "Чтобы запуÑтить «$(unit)», необходимо пройти аутентификацию."
-#: src/core/dbus-unit.c:330
+#: src/core/dbus-unit.c:327
msgid "Authentication is required to stop '$(unit)'."
msgstr "Чтобы оÑтановить «$(unit)», необходимо пройти аутентификацию."
-#: src/core/dbus-unit.c:331
+#: src/core/dbus-unit.c:328
msgid "Authentication is required to reload '$(unit)'."
msgstr ""
"Чтобы заÑтавить «$(unit)» перечитать конфигурацию, необходимо пройти "
"аутентификацию."
-#: src/core/dbus-unit.c:332 src/core/dbus-unit.c:333
+#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330
msgid "Authentication is required to restart '$(unit)'."
msgstr "Чтобы перезапуÑтить «$(unit)», необходимо пройти аутентификацию."
-#: src/core/dbus-unit.c:440
+#: src/core/dbus-unit.c:437
msgid "Authentication is required to kill '$(unit)'."
msgstr "Чтобы убить юнит «$(unit)», необходимо пройти аутентификацию."
-#: src/core/dbus-unit.c:471
+#: src/core/dbus-unit.c:468
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"Чтобы ÑброÑить ÑоÑтоÑние «failed» у юнита «$(unit)», необходимо пройти "
"аутентификацию."
-#: src/core/dbus-unit.c:504
+#: src/core/dbus-unit.c:501
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Чтобы изменить параметры юнита «$(unit)», необходимо пройти "
"аутентификацию."
diff --git a/po/sk.po b/po/sk.po
index 7d1d092e49..41d8279e0a 100644
--- a/po/sk.po
+++ b/po/sk.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Slovak translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Dušan Kazik <prescott66@gmail.com>, 2017.
#
msgid ""
diff --git a/po/sv.po b/po/sv.po
index a3efebea67..6aeca0874b 100644
--- a/po/sv.po
+++ b/po/sv.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Swedish translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Sebastian Rasmussen <sebras@gmail.com>, 2015.
# Andreas Henriksson <andreas@fatal.se>, 2016.
# Josef Andersson <l10nl18nsweja@gmail.com>, 2015, 2017.
diff --git a/po/tr.po b/po/tr.po
index 3c0a19af7b..e0239594af 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Turkish translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Necdet Yücel <necdetyucel@gmail.com>, 2014.
# Gökhan Gurbetoğlu <ggurbet@gmail.com>, 2015.
# Muhammet Kara <muhammetk@gnome.org>, 2015, 2016, 2017, 2018.
@@ -10,8 +9,8 @@ msgid ""
msgstr ""
"Project-Id-Version: systemd master\n"
"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2018-03-27 03:26+0000\n"
-"PO-Revision-Date: 2018-04-22 21:20+0300\n"
+"POT-Creation-Date: 2018-10-26 19:14+0000\n"
+"PO-Revision-Date: 2018-10-31 00:12+0300\n"
"Last-Translator: Muhammet Kara <muhammetk@gnome.org>\n"
"Language-Team: Turkish <gnome-turk@gnome.org>\n"
"Language: tr_TR\n"
@@ -50,11 +49,11 @@ msgstr ""
"Sistem servislerini veya birim dosyalarını yönetmek kimlik doğrulaması "
"gerektiriyor."
-#: src/core/org.freedesktop.systemd1.policy.in:53
+#: src/core/org.freedesktop.systemd1.policy.in:54
msgid "Set or unset system and service manager environment variables"
msgstr "Sistem ve servis yöneticisi ortam değişkenlerini ayarla ya da kaldır"
-#: src/core/org.freedesktop.systemd1.policy.in:54
+#: src/core/org.freedesktop.systemd1.policy.in:55
msgid ""
"Authentication is required to set or unset system and service manager "
"environment variables."
@@ -62,27 +61,27 @@ msgstr ""
"Sistem ve servis yöneticisi ortam değişkenlerini ayarlamak ya da kaldırmak "
"kimlik doğrulaması gerektiriyor."
-#: src/core/org.freedesktop.systemd1.policy.in:63
+#: src/core/org.freedesktop.systemd1.policy.in:64
msgid "Reload the systemd state"
msgstr "systemd durumunu yeniden yükle"
-#: src/core/org.freedesktop.systemd1.policy.in:64
+#: src/core/org.freedesktop.systemd1.policy.in:65
msgid "Authentication is required to reload the systemd state."
msgstr "systemd durumunu yeniden yüklemek kimlik doğrulaması gerektiriyor."
-#: src/hostname/org.freedesktop.hostname1.policy:22
+#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Makine adını ayarla"
-#: src/hostname/org.freedesktop.hostname1.policy:23
+#: src/hostname/org.freedesktop.hostname1.policy:21
msgid "Authentication is required to set the local host name."
msgstr "Yerel makine adını ayarlamak kimlik doğrulaması gerektiriyor."
-#: src/hostname/org.freedesktop.hostname1.policy:32
+#: src/hostname/org.freedesktop.hostname1.policy:30
msgid "Set static host name"
msgstr "Statik makine adı ayarla"
-#: src/hostname/org.freedesktop.hostname1.policy:33
+#: 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."
@@ -90,14 +89,22 @@ msgstr ""
"Statik olarak yapılandırılmış konak makine adını ve yerel makine adını "
"ayarlamak kimlik doğrulaması gerektiriyor."
-#: src/hostname/org.freedesktop.hostname1.policy:43
+#: src/hostname/org.freedesktop.hostname1.policy:41
msgid "Set machine information"
msgstr "Makine bilgisini ayarla"
-#: src/hostname/org.freedesktop.hostname1.policy:44
+#: src/hostname/org.freedesktop.hostname1.policy:42
msgid "Authentication is required to set local machine information."
msgstr "Yerel makine bilgisini ayarlamak kimlik doğrulaması gerektiriyor."
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "Ürün UUID'ini al"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "Ürün UUID'ini almak için kimlik doğrulaması gereklidir."
+
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
msgstr "Bir SM ya da kapsayıcı kalıbını içe aktar"
@@ -569,12 +576,42 @@ msgstr ""
"Yerel sanal makineler ve kapsayıcı kalıplarını yönetmek için kimlik "
"doğrulaması gereklidir."
+#: src/portable/org.freedesktop.portable1.policy:13
+msgid "Inspect a portable service image"
+msgstr "Bir taşınabilir hizmet kalıbını incele"
+
+#: src/portable/org.freedesktop.portable1.policy:14
+msgid "Authentication is required to inspect a portable service image."
+msgstr ""
+"Bir taşınabilir hizmet kalıbını incelemek için kimlik doğrulaması gereklidir."
+
+#: src/portable/org.freedesktop.portable1.policy:23
+msgid "Attach or detach a portable service image"
+msgstr "Bir taşınabilir hizmet kalıbını tuttur ya da ayır"
+
+#: src/portable/org.freedesktop.portable1.policy:24
+msgid ""
+"Authentication is required to attach or detach a portable service image."
+msgstr ""
+"Bir taşınabilir hizmet kalıbını tutturmak ya da ayırmak için kimlik "
+"doğrulaması gereklidir."
+
+#: src/portable/org.freedesktop.portable1.policy:34
+msgid "Delete or modify portable service image"
+msgstr "Taşınabilir hizmet kalıbını sil ya da değiştir"
+
+#: src/portable/org.freedesktop.portable1.policy:35
+msgid ""
+"Authentication is required to delete or modify a portable service image."
+msgstr ""
+"Taşınabilir hizmet kalıbını silmek ya da değiştirmek için kimlik doğrulaması "
+"gereklidir."
+
#: src/resolve/org.freedesktop.resolve1.policy:22
msgid "Register a DNS-SD service"
msgstr "Bir DNS-SD hizmeti kaydet"
#: src/resolve/org.freedesktop.resolve1.policy:23
-#| msgid "Authentication is required to set a wall message"
msgid "Authentication is required to register a DNS-SD service"
msgstr "Bir DNS-SD hizmeti kaydetmek için kimlik doğrulaması gereklidir"
@@ -583,7 +620,6 @@ msgid "Unregister a DNS-SD service"
msgstr "Bir DNS-SD hizmetinin kaydını sil"
#: src/resolve/org.freedesktop.resolve1.policy:34
-#| msgid "Authentication is required to set a wall message"
msgid "Authentication is required to unregister a DNS-SD service"
msgstr ""
"Bir DNS-SD hizmetinin kaydını silmek için kimlik doğrulaması gereklidir"
@@ -616,45 +652,53 @@ msgstr ""
"Gerçek zamanlı saat olarak yerel zaman dilimini veya UTC'yi ayarlamak kimlik "
"doğrulaması gerektiriyor."
-#: src/timedate/org.freedesktop.timedate1.policy:54
+#: src/timedate/org.freedesktop.timedate1.policy:53
msgid "Turn network time synchronization on or off"
msgstr "Ağ zaman eş zamanlamasını aç veya kapat"
-#: src/timedate/org.freedesktop.timedate1.policy:55
+#: src/timedate/org.freedesktop.timedate1.policy:54
msgid ""
"Authentication is required to control whether network time synchronization "
"shall be enabled."
msgstr ""
"Ağ zaman eş zamanlamasını kontrol etmek kimlik doğrulaması gerektiriyor."
-#: src/core/dbus-unit.c:496
+#: src/core/dbus-unit.c:326
msgid "Authentication is required to start '$(unit)'."
msgstr "'$(unit)' başlatmak için kimlik doğrulaması gereklidir."
-#: src/core/dbus-unit.c:497
+#: src/core/dbus-unit.c:327
msgid "Authentication is required to stop '$(unit)'."
msgstr "'$(unit)' durdurmak için kimlik doğrulaması gereklidir."
-#: src/core/dbus-unit.c:498
+#: src/core/dbus-unit.c:328
msgid "Authentication is required to reload '$(unit)'."
msgstr "'$(unit)' yeniden yüklemek için kimlik doğrulaması gereklidir."
-#: src/core/dbus-unit.c:499 src/core/dbus-unit.c:500
+#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330
msgid "Authentication is required to restart '$(unit)'."
msgstr "'$(unit)' yeniden başlatmak için kimlik doğrulaması gereklidir."
-#: src/core/dbus-unit.c:607
-msgid "Authentication is required to kill '$(unit)'."
-msgstr "'$(unit)' sonlandırmak için kimlik doğrulaması gereklidir."
+#: src/core/dbus-unit.c:437
+#| msgid "Authentication is required to set properties on '$(unit)'."
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
+msgstr ""
+"'$(unit)' süreçlerine bir UNIX sinyali göndermek için kimlik doğrulaması "
+"gereklidir."
-#: src/core/dbus-unit.c:638
+#: src/core/dbus-unit.c:468
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"'$(unit)'in \"failed\" (başarısız) durumunu sıfırlamak için kimlik "
"doğrulaması gereklidir."
-#: src/core/dbus-unit.c:671
+#: src/core/dbus-unit.c:501
msgid "Authentication is required to set properties on '$(unit)'."
msgstr ""
"'$(unit)' üzerindeki özellikleri ayarlamak için kimlik doğrulaması "
"gereklidir."
+
+#~ msgid "Authentication is required to kill '$(unit)'."
+#~ msgstr "'$(unit)' sonlandırmak için kimlik doğrulaması gereklidir."
diff --git a/po/uk.po b/po/uk.po
index 0862f13a7d..7674e54226 100644
--- a/po/uk.po
+++ b/po/uk.po
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1+
# Ukrainian translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Eugene Melnik <jeka7js@gmail.com>, 2014.
# Daniel Korostil <ted.korostiled@gmail.com>, 2014, 2016, 2018.
msgid ""
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 5236c49866..18c966abbd 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Simplified Chinese translation for systemd.
-# This file is distributed under the same license as the systemd package.
#
# Frank Hill <hxf.prc@gmail.com>, 2014.
# Boyuan Yang <073plan@gmail.com>, 2015.
diff --git a/po/zh_TW.po b/po/zh_TW.po
index 9c0315e884..d284955776 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# Traditional Chinese translation for systemd.
-# This file is distributed under the same license as the systemd package.
# Jeff Huang <s8321414@gmail.com>, 2015, 2016.
msgid ""
msgstr ""
diff --git a/rules/60-block.rules b/rules/60-block.rules
index 343fc06f85..a1458e9188 100644
--- a/rules/60-block.rules
+++ b/rules/60-block.rules
@@ -8,4 +8,4 @@ ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_
ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change"
# watch metadata changes, caused by tools closing the device node which was opened for writing
-ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*|pmem*|mmcblk*", OPTIONS+="watch"
+ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*|pmem*|mmcblk*|dasd*", OPTIONS+="watch"
diff --git a/rules/60-persistent-storage-tape.rules b/rules/60-persistent-storage-tape.rules
index b604864ee8..0136140a46 100644
--- a/rules/60-persistent-storage-tape.rules
+++ b/rules/60-persistent-storage-tape.rules
@@ -9,14 +9,24 @@ ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_t
SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \
SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}"
+# iSCSI devices from the same host have all the same ID_SERIAL,
+# but additionally a property named ID_SCSI_SERIAL.
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", ENV{ID_SCSI_SERIAL}=="?*", \
+ SYMLINK+="tape/by-id/scsi-$env{ID_SCSI_SERIAL}"
+
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{builtin}="path_id", \
+ SYMLINK+="tape/by-path/$env{ID_PATH}-changer"
+
SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end"
KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394"
KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id"
KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi"
-KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
-KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst"
+KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
+KERNEL=="st*[0-9]", ENV{ID_SCSI_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SCSI_SERIAL}"
+KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst"
+KERNEL=="nst*[0-9]", ENV{ID_SCSI_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SCSI_SERIAL}-nst"
# by-path (parent device path)
KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{builtin}="path_id"
diff --git a/rules/60-persistent-storage.rules b/rules/60-persistent-storage.rules
index 8ddb7577c1..1d8880ef02 100644
--- a/rules/60-persistent-storage.rules
+++ b/rules/60-persistent-storage.rules
@@ -7,7 +7,7 @@ ACTION=="remove", GOTO="persistent_storage_end"
ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_end"
SUBSYSTEM!="block", GOTO="persistent_storage_end"
-KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|scm*|pmem*|nbd*", GOTO="persistent_storage_end"
+KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|scm*|pmem*|nbd*|zd*", GOTO="persistent_storage_end"
# ignore partitions that span the entire disk
TEST=="whole_disk", GOTO="persistent_storage_end"
@@ -22,11 +22,13 @@ KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{wwid}=="?*"
KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}"
KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{wwid}=="?*", ENV{ID_WWN}="$attr{wwid}"
KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{model}=="?*", ENV{ID_MODEL}="$attr{model}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{firmware_rev}=="?*", ENV{ID_REVISION}="$attr{firmware_rev}"
KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", \
ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}"
KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}"
KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{model}=="?*", ENV{ID_MODEL}="$attr{model}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{firmware_rev}=="?*", ENV{ID_REVISION}="$attr{firmware_rev}"
KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", \
ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}-part%n"
@@ -52,6 +54,9 @@ KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="s
KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n"
+# PMEM devices
+KERNEL=="pmem*", ENV{DEVTYPE}=="disk", ATTRS{uuid}=="?*", SYMLINK+="disk/by-id/pmem-$attr{uuid}"
+
# FireWire
KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}"
KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}-part%n"
diff --git a/shell-completion/bash/bootctl b/shell-completion/bash/bootctl
index e24d113219..64bff4f879 100644
--- a/shell-completion/bash/bootctl
+++ b/shell-completion/bash/bootctl
@@ -24,14 +24,33 @@ __contains_word () {
done
}
+__get_entry_ids() {
+ bootctl --no-pager list 2>/dev/null | { while read -r a b; do [[ $a == 'id:' ]] && echo " $b"; done }
+}
+
_bootctl() {
local i verb comps
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
local -A OPTS=(
- [STANDALONE]='-h --help --no-variables -p --print-path --version'
+ [STANDALONE]='-h --help --no-variables -p --print-path --version --no-pager'
[ARG]='--path'
)
+ if __contains_word "$prev" ${OPTS[ARG]}; then
+ case $prev in
+ --path)
+ if [[ -z $cur ]]; then
+ comps=$(compgen -A directory -- "/" )
+ else
+ comps=$(compgen -A directory -- "$cur" )
+ fi
+ compopt -o filenames
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
if [[ "$cur" = -* ]]; then
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
return 0
@@ -39,6 +58,7 @@ _bootctl() {
local -A VERBS=(
[STANDALONE]='help install list remove status update'
+ [BOOTENTRY]='set-default set-oneshot'
)
for ((i=0; i < COMP_CWORD; i++)); do
@@ -52,6 +72,21 @@ _bootctl() {
comps=${VERBS[*]}
elif __contains_word "$verb" ${VERBS[STANDALONE]}; then
comps=''
+ elif __contains_word "$verb" ${VERBS[BOOTENTRY]}; then
+ name=
+ for ((i++; i < COMP_CWORD; i++)); do
+ if ! __contains_word "${COMP_WORDS[i]}" ${OPTS[*]} ${VERBS[*]} &&
+ ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then
+ name=${COMP_WORDS[i]}
+ break;
+ fi
+ done
+
+ if [[ -z $name ]]; then
+ comps=$( __get_entry_ids )
+ else
+ comps=''
+ fi
fi
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
diff --git a/shell-completion/bash/busctl b/shell-completion/bash/busctl
index c5eab88caa..476101c24c 100644
--- a/shell-completion/bash/busctl
+++ b/shell-completion/bash/busctl
@@ -85,8 +85,8 @@ _busctl() {
--show-machine --unique --acquired --activatable --list
-q --quiet --verbose --expect-reply=no --auto-start=no
--allow-interactive-authorization=no --augment-creds=no
- --watch-bind=yes'
- [ARG]='--address -H --host -M --machine --match --timeout --size'
+ --watch-bind=yes -j'
+ [ARG]='--address -H --host -M --machine --match --timeout --size --json'
)
if __contains_word "--user" ${COMP_WORDS[*]}; then
@@ -102,6 +102,10 @@ _busctl() {
;;
--machine|-M)
comps=$( __get_machines )
+ ;;
+ --json)
+ comps=$( busctl --json=help 2>/dev/null )
+ ;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
diff --git a/shell-completion/bash/coredumpctl b/shell-completion/bash/coredumpctl
index 9bd9072def..f73fd272f5 100644
--- a/shell-completion/bash/coredumpctl
+++ b/shell-completion/bash/coredumpctl
@@ -39,11 +39,11 @@ _coredumpctl() {
local i verb comps
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
local OPTS='-h --help --version --no-pager --no-legend -o --output -F --field -1
- -r --reverse -S --since -U --until -D --directory -q --quiet'
+ -r --reverse -S --since -U --until -D --directory -q --quiet --debugger'
local -A VERBS=(
[LIST]='list info'
- [DUMP]='dump gdb'
+ [DUMP]='dump debug'
)
if __contains_word "$prev" '--output -o'; then
@@ -52,12 +52,16 @@ _coredumpctl() {
elif __contains_word "$prev" '-D --directory'; then
comps=$( compgen -A directory -- "$cur" )
compopt -o filenames
+ elif __contains_word "$prev" '--debugger'; then
+ comps=$( compgen -A command -- "$cur" )
+ compopt -o filenames
elif __contains_word "$prev" '--field -F'; then
comps=$( compgen -W '${__journal_fields[*]}' -- "$cur" )
elif [[ $cur = -* ]]; then
comps=${OPTS}
elif __contains_word "$prev" ${VERBS[*]} &&
- ! __contains_word ${COMP_WORDS[COMP_CWORD-2]} '--output -o -D --directory -F --field'; then
+ ! __contains_word ${COMP_WORDS[COMP_CWORD-2]} \
+ '--output -o -D --directory -F --field --debugger'; then
compopt -o nospace
COMPREPLY=( $(compgen -W '${__journal_fields[*]}' -S= -- "$cur") )
return 0
diff --git a/shell-completion/bash/journalctl b/shell-completion/bash/journalctl
index 48781ee7ce..bcd4533a63 100644
--- a/shell-completion/bash/journalctl
+++ b/shell-completion/bash/journalctl
@@ -38,7 +38,7 @@ _journalctl() {
local -A OPTS=(
[STANDALONE]='-a --all --full --system --user
--disk-usage -f --follow --header
- -h --help -l --local --new-id128 -m --merge --no-pager
+ -h --help -l --local -m --merge --no-pager
--no-tail -q --quiet --setup-keys --verify
--version --list-catalog --update-catalog --list-boots
--show-cursor --dmesg -k --pager-end -e -r --reverse
@@ -46,9 +46,9 @@ _journalctl() {
--flush --rotate --sync --no-hostname -N --fields'
[ARG]='-b --boot -D --directory --file -F --field -t --identifier
-M --machine -o --output -u --unit --user-unit -p --priority
- --root'
+ --root --case-sensitive'
[ARGUNKNOWN]='-c --cursor --interval -n --lines -S --since -U --until
- --after-cursor --verify-key
+ --after-cursor --verify-key -g --grep
--vacuum-size --vacuum-time --vacuum-files --output-fields'
)
@@ -66,7 +66,7 @@ _journalctl() {
compopt -o filenames
;;
--output|-o)
- comps='short short-full short-iso short-iso-precise short-precise short-monotonic short-unix verbose export json json-pretty json-sse cat with-unit'
+ comps=$( journalctl --output=help 2>/dev/null )
;;
--field|-F)
comps=$(journalctl --fields | sort 2>/dev/null)
@@ -86,6 +86,9 @@ _journalctl() {
--identifier|-t)
comps=$(journalctl -F 'SYSLOG_IDENTIFIER' 2>/dev/null)
;;
+ --case-sensitive)
+ comps='yes no'
+ ;;
*)
return 0
;;
diff --git a/shell-completion/bash/kernel-install b/shell-completion/bash/kernel-install
index c76bfe6e90..ca8c0e20ef 100644
--- a/shell-completion/bash/kernel-install
+++ b/shell-completion/bash/kernel-install
@@ -3,7 +3,6 @@
#
# This file is part of systemd.
#
-# Copyright © 2013 Harald Hoyer
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
diff --git a/shell-completion/bash/loginctl b/shell-completion/bash/loginctl
index 1f5b46620c..a0d224e9e2 100644
--- a/shell-completion/bash/loginctl
+++ b/shell-completion/bash/loginctl
@@ -29,6 +29,12 @@ __get_all_sessions () { loginctl --no-legend list-sessions | { while read -r a b
__get_all_users () { loginctl --no-legend list-users | { while read -r a b; do printf "%s\n" "$b"; done; } ; }
__get_all_seats () { loginctl --no-legend list-seats | { while read -r a b; do printf "%s\n" "$a"; done; } ; }
+__get_machines() {
+ local a b
+ machinectl list --no-legend --no-pager 2>/dev/null |
+ { while read a b; do echo " $a"; done; };
+}
+
_loginctl () {
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
local i verb comps
@@ -52,9 +58,15 @@ _loginctl () {
--host|-H)
comps=$(compgen -A hostname)
;;
+ --machine|-M)
+ comps=$( __get_machines )
+ ;;
--property|-p)
comps=''
;;
+ --output|-o)
+ comps=$( loginctl --output=help 2>/dev/null )
+ ;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
diff --git a/shell-completion/bash/machinectl b/shell-completion/bash/machinectl
index aa5816bbf5..802a262603 100644
--- a/shell-completion/bash/machinectl
+++ b/shell-completion/bash/machinectl
@@ -42,7 +42,7 @@ _machinectl() {
)
local -A VERBS=(
- [STANDALONE]='list list-images clean pull-tar pull-raw list-transfers cancel-transfer'
+ [STANDALONE]='list list-images clean pull-tar pull-raw list-transfers cancel-transfer import-fs'
[MACHINES]='status show start stop login shell enable disable poweroff reboot terminate kill bind copy-to copy-from
image-status show-image clone rename read-only remove set-limit export-tar export-raw'
[FILE]='import-tar import-raw'
@@ -77,7 +77,13 @@ _machinectl() {
comps=''
;;
--output|-o)
- comps='short short-full short-iso short-iso-precise short-precise short-monotonic short-unix verbose export json json-pretty json-sse cat'
+ comps=$( machinectl --output=help 2>/dev/null )
+ ;;
+ --verify)
+ comps=$( machinectl --verify=help 2>/dev/null )
+ ;;
+ --format)
+ comps='uncompressed xz gzip bzip2'
;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
diff --git a/shell-completion/bash/meson.build b/shell-completion/bash/meson.build
index e7fabe29aa..0d3022b1e9 100644
--- a/shell-completion/bash/meson.build
+++ b/shell-completion/bash/meson.build
@@ -8,8 +8,6 @@ if bashcompletiondir == ''
else
bashcompletiondir = join_paths(datadir, 'bash-completion/completions')
endif
-
- message('bash completions: @0@'.format(bashcompletiondir))
endif
if bashcompletiondir != 'no'
diff --git a/shell-completion/bash/resolvectl b/shell-completion/bash/resolvectl
index 8d71f8b613..f8167c63d5 100644
--- a/shell-completion/bash/resolvectl
+++ b/shell-completion/bash/resolvectl
@@ -25,8 +25,8 @@ __contains_word () {
}
__get_interfaces(){
- { cd /sys/class/net && echo *; } | \
- while read -d' ' -r name; do
+ local name
+ for name in $(cd /sys/class/net && ls); do
[[ "$name" != "lo" ]] && echo "$name"
done
}
diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in
index ba51ae0d34..0e58e2ba4c 100644
--- a/shell-completion/bash/systemctl.in
+++ b/shell-completion/bash/systemctl.in
@@ -21,44 +21,34 @@ __contains_word () {
done
}
-__filter_units_by_property () {
- local mode=$1 property=$2 value=$3 ; shift 3
- local units=("$@")
- local props i
- IFS=$'\n' read -rd '' -a props < \
- <(__systemctl $mode show --property "$property" -- "${units[@]}")
- for ((i=0; $i < ${#units[*]}; i++)); do
- if [[ "${props[i]}" = "$property=$value" ]]; then
- echo " ${units[i]}"
- fi
- done
-}
-
__filter_units_by_properties () {
- local mode=$1 properties=$2 values=$3 ; shift 3
+ local mode=$1 properties=$2; shift 2
local units=("$@")
- local props i j conditions=()
- IFS=$'\n' read -rd '' -a props < \
- <(__systemctl $mode show --property "$properties" -- "${units[@]}")
- IFS=$',' read -r -a properties < <(echo $properties)
- IFS=$',' read -r -a values < <(echo $values)
- for ((i=0; i < ${#properties[*]}; i++)); do
- for ((j=0; j < ${#properties[*]}; j++)); do
- if [[ ${props[i]%%=*} == ${properties[j]} ]]; then
- conditions+=( "${properties[j]}=${values[j]}" )
+ local props i p n
+ local names= count=0
+
+ IFS=$',' read -r -a p < <(echo "Names,$properties")
+ n=${#p[*]}
+ readarray -t props < \
+ <(__systemctl $mode show --property "Names,$properties" -- "${units[@]}")
+
+ for ((i=0; i < ${#props[*]}; i++)); do
+ if [[ -z ${props[i]} ]]; then
+ if (( count == n )) && [[ -n $names ]]; then
+ echo $names
fi
- done
- done
- for ((i=0; i < ${#units[*]}; i++)); do
- for ((j=0; j < ${#conditions[*]}; j++)); do
- if [[ "${props[i * ${#conditions[*]} + j]}" != "${conditions[j]}" ]]; then
- break
+ names=
+ count=0
+ else
+ (( count++ ))
+ if [[ ${props[i]%%=*} == 'Names' ]]; then
+ names=${props[i]#*=}
fi
- done
- if (( j == ${#conditions[*]} )); then
- echo " ${units[i]}"
fi
done
+ if (( count == n )) && [[ -n $names ]]; then
+ echo $names
+ fi
}
__get_all_units () { { __systemctl $1 list-unit-files "$2*"; __systemctl $1 list-units --all "$2*"; } \
@@ -67,26 +57,47 @@ __get_non_template_units() { { __systemctl $1 list-unit-files "$2*"; __systemctl
| { while read -r a b; do [[ $a =~ @\. ]] || echo " $a"; done; }; }
__get_template_names () { __systemctl $1 list-unit-files "$2*" \
| { while read -r a b; do [[ $a =~ @\. ]] && echo " ${a%%@.*}@"; done; }; }
-
__get_active_units () { __systemctl $1 list-units "$2*" \
| { while read -r a b; do echo " $a"; done; }; }
+
+__get_not_masked_unit_files() {
+ # filter out masked, not-found, or template units.
+ __systemctl $1 list-unit-files --state enabled,enabled-runtime,linked,linked-runtime,static,indirect,disabled,generated,transient "$2*" | \
+ { while read -r a b; do [[ $a =~ @\. ]] || echo " $a"; done; }
+}
+
__get_startable_units () {
- # find startable inactive units
- __filter_units_by_properties $1 ActiveState,CanStart inactive,yes $(
- { __systemctl $1 list-unit-files --state enabled,enabled-runtime,linked,linked-runtime,static,indirect,disabled,generated,transient "$2*" | \
- { while read -r a b; do [[ $a =~ @\. ]] || echo " $a"; done; }
- __systemctl $1 list-units --state inactive,failed "$2*" | \
+ __filter_units_by_properties $1 ActiveState=inactive,CanStart=yes $(
+ { __get_not_masked_unit_files $1 $2
+ # get inactive template units
+ __systemctl $1 list-units --state inactive,failed "$2*" | \
{ while read -r a b c; do [[ $b == "loaded" ]] && echo " $a"; done; }
- } | sort -u )
+ } | sort -u )
}
__get_restartable_units () {
# filter out masked and not-found
- __filter_units_by_property $1 CanStart yes $(
- __systemctl $1 list-unit-files --state enabled,disabled,static "$2*" | \
- { while read -r a b; do [[ $a =~ @\. ]] || echo " $a"; done; }
- __systemctl $1 list-units "$2*" | \
- { while read -r a b; do echo " $a"; done; } )
+ __filter_units_by_properties $1 CanStart=yes $(
+ { __get_not_masked_unit_files $1 $2
+ __get_active_units $1 $2
+ } | sort -u )
+}
+
+__get_stoppable_units () {
+ # filter out masked and not-found
+ __filter_units_by_properties $1 ActiveState=active,CanStop=yes $(
+ { __get_not_masked_unit_files $1 $2
+ __get_active_units $1 $2
+ } | sort -u )
}
+
+__get_reloadable_units () {
+ # filter out masked and not-found
+ __filter_units_by_properties $1 ActiveState=active,CanReload=yes $(
+ { __get_not_masked_unit_files $1 $2
+ __get_active_units $1 $2
+ } | sort -u )
+}
+
__get_failed_units () { __systemctl $1 list-units "$2*" \
| { while read -r a b c d; do [[ $c == "failed" ]] && echo " $a"; done; }; }
__get_enabled_units () { __systemctl $1 list-unit-files "$2*" \
@@ -157,8 +168,7 @@ _systemctl () {
comps='full enable-only disable-only'
;;
--output|-o)
- comps='short short-full short-iso short-iso-precise short-precise short-monotonic short-unix verbose export json
- json-pretty json-sse cat'
+ comps=$( systemctl --output=help 2>/dev/null )
;;
--machine|-M)
comps=$( __get_machines )
@@ -243,17 +253,15 @@ _systemctl () {
compopt -o filenames
elif __contains_word "$verb" ${VERBS[STOPPABLE_UNITS]}; then
- comps=$( __filter_units_by_property $mode CanStop yes \
- $( __get_active_units $mode "$cur" ) )
+ comps=$( __get_stoppable_units $mode "$cur" )
compopt -o filenames
elif __contains_word "$verb" ${VERBS[RELOADABLE_UNITS]}; then
- comps=$( __filter_units_by_property $mode CanReload yes \
- $( __get_active_units $mode "$cur" ) )
+ comps=$( __get_reloadable_units $mode "$cur" )
compopt -o filenames
elif __contains_word "$verb" ${VERBS[ISOLATABLE_UNITS]}; then
- comps=$( __filter_units_by_property $mode AllowIsolate yes \
+ comps=$( __filter_units_by_properties $mode AllowIsolate=yes \
$( __get_all_units $mode "$cur" ) )
compopt -o filenames
diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze
index 21d0fcf1b8..a4e506d9df 100644
--- a/shell-completion/bash/systemd-analyze
+++ b/shell-completion/bash/systemd-analyze
@@ -4,7 +4,6 @@
# This file is part of systemd.
#
# Copyright © 2010 Ran Benita
-# Copyright © 2013 Harald Hoyer
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
@@ -31,8 +30,22 @@ __get_machines() {
machinectl list --no-legend --no-pager | { while read a b; do echo " $a"; done; };
}
+__get_services() {
+ systemctl list-units --no-legend --no-pager -t service --all $1 | \
+ { while read -r a b c; do [[ $b == "loaded" ]]; echo " $a"; done }
+}
+
+__get_syscall_sets() {
+ local line
+ systemd-analyze syscall-filter --no-pager | while IFS= read -r line; do
+ if [[ $line == @* ]]; then
+ printf '%s\n' "$line"
+ fi
+ done
+}
+
_systemd_analyze() {
- local i verb comps
+ local i verb comps mode
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
local -A OPTS=(
@@ -42,7 +55,7 @@ _systemd_analyze() {
)
local -A VERBS=(
- [STANDALONE]='time blame plot dump unit-paths calendar'
+ [STANDALONE]='time blame plot dump unit-paths calendar timespan'
[CRITICAL_CHAIN]='critical-chain'
[DOT]='dot'
[LOG_LEVEL]='log-level'
@@ -51,6 +64,7 @@ _systemd_analyze() {
[SECCOMP_FILTER]='syscall-filter'
[SERVICE_WATCHDOGS]='service-watchdogs'
[CAT_CONFIG]='cat-config'
+ [SECURITY]='security'
)
local CONFIGS='systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
@@ -122,6 +136,8 @@ _systemd_analyze() {
elif __contains_word "$verb" ${VERBS[SECCOMP_FILTER]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager'
+ else
+ comps=$( __get_syscall_sets )
fi
elif __contains_word "$verb" ${VERBS[VERIFY]}; then
@@ -149,6 +165,18 @@ _systemd_analyze() {
comps="$CONFIGS $( compgen -A file -- "$cur" )"
compopt -o filenames
fi
+
+ elif __contains_word "$verb" ${VERBS[SECURITY]}; then
+ if [[ $cur = -* ]]; then
+ comps='--help --version --no-pager --system --user -H --host -M --machine'
+ else
+ if __contains_word "--user" ${COMP_WORDS[*]}; then
+ mode=--user
+ else
+ mode=--system
+ fi
+ comps=$( __get_services $mode )
+ fi
fi
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
diff --git a/shell-completion/bash/systemd-nspawn b/shell-completion/bash/systemd-nspawn
index 2ff39b65d7..3ba2cd6ec9 100644
--- a/shell-completion/bash/systemd-nspawn
+++ b/shell-completion/bash/systemd-nspawn
@@ -45,12 +45,19 @@ __get_env() {
}
__get_interfaces(){
- { cd /sys/class/net && echo *; } | \
- while read -d' ' -r name; do
+ local name
+ for name in $(cd /sys/class/net && ls); do
[[ "$name" != "lo" ]] && echo "$name"
done
}
+__get_rlimit() {
+ local i
+ for i in $(systemd-nspawn --rlimit=help 2>/dev/null); do
+ echo " ${i}="
+ done
+}
+
_systemd_nspawn() {
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
local i verb comps
@@ -62,7 +69,8 @@ _systemd_nspawn() {
-S --slice -E --setenv -Z --selinux-context -L --selinux-apifs-context --register --network-interface --network-bridge
--personality -i --image --tmpfs --volatile --network-macvlan --kill-signal --template --notify-ready --root-hash
--chdir --pivot-root --property --private-users --network-namespace-path --network-ipvlan --network-veth-extra
- --network-zone -p --port --system-call-filter --overlay --overlay-ro --settings'
+ --network-zone -p --port --system-call-filter --overlay --overlay-ro --settings
+ --rlimit --hostname --no-new-privileges --oom-score-adjust --cpu-affinity --resolv-conf --timezone'
)
_init_completion || return
@@ -131,7 +139,7 @@ _systemd_nspawn() {
comps='x86 x86-64'
;;
--volatile)
- comps='yes state no'
+ comps=$( systemd-nspawn --volatile=help 2>/dev/null )
;;
--image|-i)
compopt -o nospace
@@ -153,6 +161,27 @@ _systemd_nspawn() {
--settings)
comps='yes no override trusted'
;;
+ --rlimit)
+ comps=$( __get_rlimit )
+ ;;
+ --hostname)
+ comps=''
+ ;;
+ --no-new-privileges)
+ comps='yes no'
+ ;;
+ --oom-score-adjust)
+ comps=''
+ ;;
+ --cpu-affinity)
+ comps=''
+ ;;
+ --resolv-conf)
+ comps=$( systemd-nspawn --resolv-conf=help 2>/dev/null )
+ ;;
+ --timezone)
+ comps=$( systemd-nspawn --timezone=help 2>/dev/null )
+ ;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
diff --git a/shell-completion/bash/systemd-resolve b/shell-completion/bash/systemd-resolve
index 86b8db2cf8..cd0231a6f4 100644
--- a/shell-completion/bash/systemd-resolve
+++ b/shell-completion/bash/systemd-resolve
@@ -24,8 +24,8 @@ __contains_word () {
}
__get_interfaces(){
- { cd /sys/class/net && echo *; } | \
- while read -d' ' -r name; do
+ local name
+ for name in $(cd /sys/class/net && ls); do
[[ "$name" != "lo" ]] && echo "$name"
done
}
diff --git a/shell-completion/bash/systemd-run b/shell-completion/bash/systemd-run
index d317466b26..0908bd9334 100644
--- a/shell-completion/bash/systemd-run
+++ b/shell-completion/bash/systemd-run
@@ -36,14 +36,15 @@ _systemd_run() {
--on-active --on-boot --on-startup --on-unit-active --on-unit-inactive
--on-calendar --timer-property --path-property --socket-property -t --pty
-q --quiet --no-block --uid --gid --nice -E --setenv -p --property
- --no-ask-password --wait -P --pipe -G --collect'
+ --no-ask-password --wait -P --pipe -G --collect --working-directory
+ -d --same-dir -S --shell'
local mode=--system
local i
local opts_with_values=(
--unit --description --slice --service-type -H --host -M --machine -p --property --on-active
--on-boot --on-startup --on-unit-active --on-unit-inactive --on-calendar --timer-property
- --path-property --socket-property --uid --gid --nice -E --setenv
+ --path-property --socket-property --uid --gid --nice -E --setenv --working-directory
)
for (( i=1; i <= COMP_CWORD; i++ )); do
if [[ ${COMP_WORDS[i]} != -* ]]; then
@@ -79,8 +80,8 @@ _systemd_run() {
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group=
DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth=
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment=
- KillSignal= LimitCPU= LimitFSIZE= LimitDATA= LimitSTACK=
- LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC=
+ KillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA=
+ LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC=
LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE=
LimitNICE= LimitRTPRIO= LimitRTTIME= PrivateTmp= PrivateDevices=
PrivateNetwork= NoNewPrivileges= WorkingDirectory= RootDirectory=
@@ -109,6 +110,17 @@ _systemd_run() {
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
;;
+ --working-directory)
+ local comps
+ if [[ -z $cur ]]; then
+ comps=$(compgen -A directory -- "/" )
+ else
+ comps=$(compgen -A directory -- "$cur" )
+ fi
+ compopt -o filenames
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur" ) )
+ return 0
+ ;;
esac
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm
index e5c11bf43c..7b33839b1e 100644
--- a/shell-completion/bash/udevadm
+++ b/shell-completion/bash/udevadm
@@ -30,61 +30,199 @@ __get_all_sysdevs() {
printf '%s\n' "${devs[@]%/}"
}
+__get_all_devs() {
+ local i
+ for i in /dev/* /dev/*/*; do
+ echo $i
+ done
+}
+
+__get_all_device_units() {
+ systemctl list-units -t device --full --no-legend --no-pager 2>/dev/null | \
+ { while read -r a b; do echo "$a"; done; }
+}
+
_udevadm() {
- local i verb comps
+ local i verb comps builtin
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
- local OPTS='-h --help --version --debug'
+ local -A OPTS=(
+ [COMMON]='-h --help -V --version'
+ [DEBUG]='-d --debug'
+ [INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db'
+ [INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file'
+ [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -w --settle'
+ [TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
+ -a --attr-match -A --attr-nomatch -p --property-match
+ -g --tag-match -y --sysname-match --name-match -b --parent-match'
+ [SETTLE]='-t --timeout -E --exit-if-exists'
+ [CONTROL_STANDALONE]='-e --exit -s --stop-exec-queue -S --start-exec-queue -R --reload'
+ [CONTROL_ARG]='-l --log-priority -p --property -m --children-max -t --timeout'
+ [MONITOR_STANDALONE]='-k --kernel -u --udev -p --property'
+ [MONITOR_ARG]='-s --subsystem-match -t --tag-match'
+ [TEST]='-a --action -N --resolve-names'
+ )
- local verbs=(info trigger settle control monitor hwdb test-builtin test)
+ local verbs=(info trigger settle control monitor test-builtin test)
+ local builtins=(blkid btrfs hwdb input_id keyboard kmod net_id net_setup_link path_id usb_id uaccess)
for ((i=0; i < COMP_CWORD; i++)); do
- if __contains_word "${COMP_WORDS[i]}" "${verbs[@]}" &&
- ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then
+ if __contains_word "${COMP_WORDS[i]}" "${verbs[@]}"; then
verb=${COMP_WORDS[i]}
break
fi
done
if [[ -z $verb ]]; then
- COMPREPLY=( $(compgen -W '${OPTS[*]} ${verbs[*]}' -- "$cur") )
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $(compgen -W '${OPTS[COMMON]} ${OPTS[DEBUG]}' -- "$cur") )
+ else
+ COMPREPLY=( $(compgen -W '${verbs[*]}' -- "$cur") )
+ fi
return 0
fi
case $verb in
'info')
+ if __contains_word "$prev" ${OPTS[INFO_ARG]}; then
+ case $prev in
+ -q|--query)
+ comps='name symlink path property all'
+ ;;
+ -p|--path)
+ comps=$( __get_all_sysdevs )
+ local IFS=$'\n'
+ ;;
+ -n|--name)
+ comps=$( __get_all_devs )
+ ;;
+ *)
+ comps=''
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
if [[ $cur = -* ]]; then
- comps='--help --query= --path= --name= --root --attribute-walk --export-db --cleanup-db'
+ comps="${OPTS[COMMON]} ${OPTS[INFO_STANDALONE]} ${OPTS[INFO_ARG]}"
else
- comps=$( __get_all_sysdevs )
+ comps=$( __get_all_sysdevs; __get_all_device_units )
+ local IFS=$'\n'
fi
;;
'trigger')
- comps='--help --verbose --dry-run --type= --action= --subsystem-match=
- --subsystem-nomatch= --attr-match= --attr-nomatch= --property-match=
- --tag-match= --sysname-match= --parent-match='
+ if __contains_word "$prev" ${OPTS[TRIGGER_ARG]}; then
+ case $prev in
+ -t|--type)
+ comps='devices subsystems'
+ ;;
+ -c|--action)
+ comps='add change remove bind unbind'
+ ;;
+ -y|--sysname-match|-b|--parent-match)
+ comps=$( __get_all_sysdevs )
+ local IFS=$'\n'
+ ;;
+ --name-match)
+ comps=$( __get_all_devs )
+ ;;
+ *)
+ comps=''
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
+ if [[ $cur = -* ]]; then
+ comps="${OPTS[COMMON]} ${OPTS[TRIGGER_STANDALONE]} ${OPTS[TRIGGER_ARG]}"
+ else
+ comps=$( __get_all_sysdevs; __get_all_device_units )
+ local IFS=$'\n'
+ fi
;;
'settle')
- comps='--help --timeout= --seq-start= --seq-end= --exit-if-exists= --quiet'
+ if __contains_word "$prev" ${OPTS[SETTLE]}; then
+ case $prev in
+ -E|--exit-if-exists)
+ comps=$( compgen -A file -- "$cur" )
+ ;;
+ *)
+ comps=''
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
+ comps="${OPTS[COMMON]} ${OPTS[SETTLE]}"
;;
'control')
- comps='--help --exit --log-priority= --stop-exec-queue --start-exec-queue
- --reload --property= --children-max= --timeout='
+ if __contains_word "$prev" ${OPTS[CONTROL_ARG]}; then
+ case $prev in
+ -l|--log-priority)
+ comps='alert crit debug emerg err info notice warning'
+ ;;
+ *)
+ comps=''
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
+ comps="${OPTS[COMMON]} ${OPTS[CONTROL_STANDALONE]} ${OPTS[CONTROL_ARG]}"
;;
'monitor')
- comps='--help --kernel --udev --property --subsystem-match= --tag-match='
- ;;
- 'hwdb')
- comps='--help --update --test='
+ if __contains_word "$prev" ${OPTS[MONITOR_ARG]}; then
+ case $prev in
+ *)
+ comps=''
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
+ comps="${OPTS[COMMON]} ${OPTS[MONITOR_STANDALONE]} ${OPTS[MONITOR_ARG]}"
;;
'test')
+ if __contains_word "$prev" ${OPTS[TEST]}; then
+ case $prev in
+ -a|--action)
+ comps='add change remove bind unbind'
+ ;;
+ -N|--resolve-names)
+ comps='early late never'
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
if [[ $cur = -* ]]; then
- comps='--help --action='
+ comps="${OPTS[COMMON]} ${OPTS[TEST]}"
else
comps=$( __get_all_sysdevs )
+ local IFS=$'\n'
fi
;;
'test-builtin')
- comps='blkid btrfs hwdb input_id keyboard kmod net_id net_setup_link path_id usb_id uaccess'
+ for ((i=0; i < COMP_CWORD; i++)); do
+ if __contains_word "${COMP_WORDS[i]}" "${builtins[@]}"; then
+ builtin=${COMP_WORDS[i]}
+ break
+ fi
+ done
+
+ if [[ -z $builtin ]]; then
+ comps="${builtins[@]}"
+ elif [[ $cur = -* ]]; then
+ comps="${OPTS[COMMON]}"
+ else
+ comps=$( __get_all_sysdevs )
+ local IFS=$'\n'
+ fi
;;
*)
comps=${VERBS[*]}
diff --git a/shell-completion/zsh/_coredumpctl b/shell-completion/zsh/_coredumpctl
index f727820660..cf24f5b361 100644
--- a/shell-completion/zsh/_coredumpctl
+++ b/shell-completion/zsh/_coredumpctl
@@ -7,7 +7,7 @@ _coredumpctl_command(){
'list:List available coredumps'
'info:Show detailed information about one or more coredumps'
'dump:Print coredump to stdout'
- 'gdb:Start gdb on a coredump'
+ 'debug:Start debugger (gdb) on a coredump'
)
if (( CURRENT == 1 )); then
_describe -t commands 'coredumpctl command' _coredumpctl_cmds
@@ -39,4 +39,5 @@ _arguments \
'--no-legend[Do not print the column headers]' \
{-h,--help}'[Show this help]' \
'--version[Show package version]' \
+ '--debugger=[Use the given debugger]' \
'*::coredumpctl commands:_coredumpctl_command'
diff --git a/shell-completion/zsh/_machinectl b/shell-completion/zsh/_machinectl
index 933f4d2e6a..a00fc91021 100644
--- a/shell-completion/zsh/_machinectl
+++ b/shell-completion/zsh/_machinectl
@@ -60,6 +60,8 @@ _available_machines() {
case $cmd in
list*|cancel-transfer|pull-tar|pull-raw)
msg="no options" ;;
+ clone)
+ _available_machines ;;
start)
_available_machines ;;
*)
@@ -87,7 +89,7 @@ _arguments \
{-H+,--host=}'[Operate on remote host.]:userathost:_sd_hosts_or_user_at_host' \
{-M+,--machine=}'[Operate on local container.]:machine:_sd_machines' \
{-p+,--property=}'[Limit output to specified property.]:property:(Name Id Timestamp TimestampMonotonic Service Scope Leader Class State RootDirectory)' \
- {-a,--all}'[Show all proerties.]' \
+ {-a,--all}'[Show all properties.]' \
{-q,--quiet}'[Suppress output.]' \
{-l,--full}'[Do not ellipsize cgroup members.]' \
'--kill-who=[Who to send signal to.]:killwho:(leader all)' \
diff --git a/shell-completion/zsh/_sd_outputmodes b/shell-completion/zsh/_sd_outputmodes
index 70ff7233af..763b106f3d 100644
--- a/shell-completion/zsh/_sd_outputmodes
+++ b/shell-completion/zsh/_sd_outputmodes
@@ -2,5 +2,5 @@
# SPDX-License-Identifier: LGPL-2.1+
local -a _output_opts
-_output_opts=(short short-full short-iso short-iso-precise short-precise short-monotonic short-unix verbose export json json-pretty json-sse cat with-unit)
+_output_opts=(short short-full short-iso short-iso-precise short-precise short-monotonic short-unix verbose export json json-pretty json-sse json-seq cat with-unit)
_describe -t output 'output mode' _output_opts || compadd "$@"
diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in
index 9f576ed77d..782d243131 100644
--- a/shell-completion/zsh/_systemctl.in
+++ b/shell-completion/zsh/_systemctl.in
@@ -63,6 +63,7 @@
"exit:Ask for user instance termination"
"switch-root:Change root directory"
"revert:Revert unit files to their vendor versions"
+ "set-property:Sets one or more properties of a unit"
)
if (( CURRENT == 1 )); then
@@ -176,7 +177,7 @@ for fun in cat mask ; do
done
# Completion functions for NONTEMPLATE_UNITS
-for fun in is-active is-failed is-enabled status show preset help list-dependencies edit revert add-wants add-requires ; do
+for fun in is-active is-failed is-enabled status show preset help list-dependencies edit revert add-wants add-requires set-property; do
(( $+functions[_systemctl_$fun] )) || _systemctl_$fun()
{
_wanted systemd-units expl unit \
diff --git a/shell-completion/zsh/_systemd-run b/shell-completion/zsh/_systemd-run
index 0ad4b27a6f..a8a8e6fe34 100644
--- a/shell-completion/zsh/_systemd-run
+++ b/shell-completion/zsh/_systemd-run
@@ -32,8 +32,8 @@ _arguments \
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \
DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= \
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= \
- KillSignal= LimitCPU= LimitFSIZE= LimitDATA= LimitSTACK= \
- LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \
+ KillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \
+ LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \
LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE= \
LimitNICE= LimitRTPRIO= LimitRTTIME= PrivateTmp= PrivateDevices= \
PrivateNetwork= NoNewPrivileges= WorkingDirectory= RootDirectory= \
diff --git a/shell-completion/zsh/meson.build b/shell-completion/zsh/meson.build
index 7fc9fcfc71..792b06122f 100644
--- a/shell-completion/zsh/meson.build
+++ b/shell-completion/zsh/meson.build
@@ -3,8 +3,6 @@
zshcompletiondir = get_option('zshcompletiondir')
if zshcompletiondir == ''
zshcompletiondir = join_paths(datadir, 'zsh/site-functions')
-
- message('zsh completions: @0@'.format(zshcompletiondir))
endif
if zshcompletiondir != 'no'
diff --git a/src/ac-power/ac-power.c b/src/ac-power/ac-power.c
index 7ca72ef5f5..90ba5d2b68 100644
--- a/src/ac-power/ac-power.c
+++ b/src/ac-power/ac-power.c
@@ -2,6 +2,7 @@
#include <getopt.h>
+#include "main-func.h"
#include "util.h"
static bool arg_verbose = false;
@@ -55,15 +56,15 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind < argc) {
- log_error("%s takes no arguments.", program_invocation_short_name);
- return -EINVAL;
- }
+ if (optind < argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s takes no arguments.",
+ program_invocation_short_name);
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
/* This is mostly intended to be used for scripts which want
@@ -74,17 +75,16 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = on_ac_power();
- if (r < 0) {
- log_error_errno(r, "Failed to read AC status: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read AC status: %m");
if (arg_verbose)
puts(yes_no(r));
-finish:
- return r < 0 ? EXIT_FAILURE : !r;
+ return r == 0;
}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/activate/activate.c b/src/activate/activate.c
index 45386a38f4..9a83bc7f24 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -14,11 +14,13 @@
#include "fd-util.h"
#include "log.h"
#include "macro.h"
+#include "pretty-print.h"
#include "process-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
+#include "terminal-util.h"
static char** arg_listen = NULL;
static bool arg_accept = false;
@@ -120,10 +122,9 @@ static int exec_process(const char* name, char **argv, char **env, int start_fd,
char **s;
int r;
- if (arg_inetd && n_fds != 1) {
- log_error("--inetd only supported for single file descriptors.");
- return -EINVAL;
- }
+ if (arg_inetd && n_fds != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--inetd only supported for single file descriptors.");
length = strv_length(arg_setenv);
@@ -248,7 +249,7 @@ static int fork_and_exec_process(const char* child, char** argv, char **env, int
if (!joined)
return log_oom();
- r = safe_fork("(activate)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid);
+ r = safe_fork("(activate)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &child_pid);
if (r < 0)
return r;
if (r == 0) {
@@ -310,7 +311,14 @@ static int install_chld_handler(void) {
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-socket-activate", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"Listen on sockets and launch child on connection.\n\n"
"Options:\n"
@@ -323,9 +331,13 @@ static void help(void) {
" -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
" --fdname=NAME[:NAME...] Specify names for file descriptors\n"
" --inetd Enable inetd file descriptor passing protocol\n"
- "\n"
- "Note: file descriptors from sd_listen_fds() will be passed through.\n"
- , program_invocation_short_name);
+ "\nNote: file descriptors from sd_listen_fds() will be passed through.\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -358,8 +370,7 @@ static int parse_argv(int argc, char *argv[]) {
while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
switch(c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -372,19 +383,17 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'd':
- if (arg_socket_type == SOCK_SEQPACKET) {
- log_error("--datagram may not be combined with --seqpacket.");
- return -EINVAL;
- }
+ if (arg_socket_type == SOCK_SEQPACKET)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--datagram may not be combined with --seqpacket.");
arg_socket_type = SOCK_DGRAM;
break;
case ARG_SEQPACKET:
- if (arg_socket_type == SOCK_DGRAM) {
- log_error("--seqpacket may not be combined with --datagram.");
- return -EINVAL;
- }
+ if (arg_socket_type == SOCK_DGRAM)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--seqpacket may not be combined with --datagram.");
arg_socket_type = SOCK_SEQPACKET;
break;
@@ -436,17 +445,15 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind == argc) {
- log_error("%s: command to execute is missing.",
- program_invocation_short_name);
- return -EINVAL;
- }
+ if (optind == argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: command to execute is missing.",
+ program_invocation_short_name);
- if (arg_socket_type == SOCK_DGRAM && arg_accept) {
- log_error("Datagram sockets do not accept connections. "
- "The --datagram and --accept options may not be combined.");
- return -EINVAL;
- }
+ if (arg_socket_type == SOCK_DGRAM && arg_accept)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Datagram sockets do not accept connections. "
+ "The --datagram and --accept options may not be combined.");
arg_args = argv + optind;
diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c
new file mode 100644
index 0000000000..1fc3c1e02f
--- /dev/null
+++ b/src/analyze/analyze-security.c
@@ -0,0 +1,2087 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sched.h>
+#include <sys/utsname.h>
+
+#include "analyze-security.h"
+#include "bus-error.h"
+#include "bus-unit-util.h"
+#include "bus-util.h"
+#include "env-util.h"
+#include "format-table.h"
+#include "in-addr-util.h"
+#include "locale-util.h"
+#include "macro.h"
+#include "missing.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#if HAVE_SECCOMP
+# include "seccomp-util.h"
+#endif
+#include "set.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "unit-def.h"
+#include "unit-name.h"
+
+struct security_info {
+ char *id;
+ char *type;
+ char *load_state;
+ char *fragment_path;
+ bool default_dependencies;
+
+ uint64_t ambient_capabilities;
+ uint64_t capability_bounding_set;
+
+ char *user;
+ char **supplementary_groups;
+ bool dynamic_user;
+
+ bool ip_address_deny_all;
+ bool ip_address_allow_localhost;
+ bool ip_address_allow_other;
+
+ char *keyring_mode;
+ bool lock_personality;
+ bool memory_deny_write_execute;
+ bool no_new_privileges;
+ char *notify_access;
+
+ bool private_devices;
+ bool private_mounts;
+ bool private_network;
+ bool private_tmp;
+ bool private_users;
+
+ bool protect_control_groups;
+ bool protect_kernel_modules;
+ bool protect_kernel_tunables;
+
+ char *protect_home;
+ char *protect_system;
+
+ bool remove_ipc;
+
+ bool restrict_address_family_inet;
+ bool restrict_address_family_unix;
+ bool restrict_address_family_netlink;
+ bool restrict_address_family_packet;
+ bool restrict_address_family_other;
+
+ uint64_t restrict_namespaces;
+ bool restrict_realtime;
+
+ char *root_directory;
+ char *root_image;
+
+ bool delegate;
+ char *device_policy;
+ bool device_allow_non_empty;
+
+ char **system_call_architectures;
+
+ bool system_call_filter_whitelist;
+ Set *system_call_filter;
+
+ uint32_t _umask;
+};
+
+struct security_assessor {
+ const char *id;
+ const char *description_good;
+ const char *description_bad;
+ const char *description_na;
+ const char *url;
+ uint64_t weight;
+ uint64_t range;
+ int (*assess)(const struct security_assessor *a, const struct security_info *info, const void *data, uint64_t *ret_badness, char **ret_description);
+ size_t offset;
+ uint64_t parameter;
+ bool default_dependencies_only;
+};
+
+static void security_info_free(struct security_info *i) {
+ if (!i)
+ return;
+
+ free(i->id);
+ free(i->type);
+ free(i->load_state);
+ free(i->fragment_path);
+
+ free(i->user);
+
+ free(i->protect_home);
+ free(i->protect_system);
+
+ free(i->root_directory);
+ free(i->root_image);
+
+ free(i->keyring_mode);
+ free(i->notify_access);
+
+ free(i->device_policy);
+
+ strv_free(i->supplementary_groups);
+ strv_free(i->system_call_architectures);
+
+ set_free_free(i->system_call_filter);
+}
+
+static bool security_info_runs_privileged(const struct security_info *i) {
+ assert(i);
+
+ if (STRPTR_IN_SET(i->user, "0", "root"))
+ return true;
+
+ if (i->dynamic_user)
+ return false;
+
+ return isempty(i->user);
+}
+
+static int assess_bool(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ const bool *b = data;
+
+ assert(b);
+ assert(ret_badness);
+ assert(ret_description);
+
+ *ret_badness = a->parameter ? *b : !*b;
+ *ret_description = NULL;
+
+ return 0;
+}
+
+static int assess_user(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ _cleanup_free_ char *d = NULL;
+ uint64_t b;
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ if (streq_ptr(info->user, NOBODY_USER_NAME)) {
+ d = strdup("Service runs under as '" NOBODY_USER_NAME "' user, which should not be used for services");
+ b = 9;
+ } else if (info->dynamic_user && !STR_IN_SET(info->user, "0", "root")) {
+ d = strdup("Service runs under a transient non-root user identity");
+ b = 0;
+ } else if (info->user && !STR_IN_SET(info->user, "0", "root", "")) {
+ d = strdup("Service runs under a static non-root user identity");
+ b = 0;
+ } else {
+ *ret_badness = 10;
+ *ret_description = NULL;
+ return 0;
+ }
+
+ if (!d)
+ return log_oom();
+
+ *ret_badness = b;
+ *ret_description = TAKE_PTR(d);
+
+ return 0;
+}
+
+static int assess_protect_home(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ const char *description;
+ uint64_t badness;
+ char *copy;
+ int r;
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ badness = 10;
+ description = "Service has full access to home directories";
+
+ r = parse_boolean(info->protect_home);
+ if (r < 0) {
+ if (streq_ptr(info->protect_home, "read-only")) {
+ badness = 5;
+ description = "Service has read-only access to home directories";
+ } else if (streq_ptr(info->protect_home, "tmpfs")) {
+ badness = 1;
+ description = "Service has access to fake empty home directories";
+ }
+ } else if (r > 0) {
+ badness = 0;
+ description = "Service has no access to home directories";
+ }
+
+ copy = strdup(description);
+ if (!copy)
+ return log_oom();
+
+ *ret_badness = badness;
+ *ret_description = copy;
+
+ return 0;
+}
+
+static int assess_protect_system(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ const char *description;
+ uint64_t badness;
+ char *copy;
+ int r;
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ badness = 10;
+ description = "Service has full access to the OS file hierarchy";
+
+ r = parse_boolean(info->protect_system);
+ if (r < 0) {
+ if (streq_ptr(info->protect_system, "full")) {
+ badness = 3;
+ description = "Service has very limited write access to the OS file hierarchy";
+ } else if (streq_ptr(info->protect_system, "strict")) {
+ badness = 0;
+ description = "Service has strict read-only access to the OS file hierarchy";
+ }
+ } else if (r > 0) {
+ badness = 5;
+ description = "Service has limited write access to the OS file hierarchy";
+ }
+
+ copy = strdup(description);
+ if (!copy)
+ return log_oom();
+
+ *ret_badness = badness;
+ *ret_description = copy;
+
+ return 0;
+}
+
+static int assess_root_directory(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ *ret_badness =
+ (isempty(info->root_directory) ||
+ path_equal(info->root_directory, "/")) &&
+ (isempty(info->root_image) ||
+ path_equal(info->root_image, "/"));
+ *ret_description = NULL;
+
+ return 0;
+}
+
+static int assess_capability_bounding_set(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ *ret_badness = !!(info->capability_bounding_set & a->parameter);
+ *ret_description = NULL;
+
+ return 0;
+}
+
+static int assess_umask(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ char *copy = NULL;
+ const char *d;
+ uint64_t b;
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ if (!FLAGS_SET(info->_umask, 0002)) {
+ d = "Files created by service are world-writable by default";
+ b = 10;
+ } else if (!FLAGS_SET(info->_umask, 0004)) {
+ d = "Files created by service are world-readable by default";
+ b = 5;
+ } else if (!FLAGS_SET(info->_umask, 0020)) {
+ d = "Files created by service are group-writable by default";
+ b = 2;
+ } else if (!FLAGS_SET(info->_umask, 0040)) {
+ d = "Files created by service are group-readable by default";
+ b = 1;
+ } else {
+ d = "Files created by service are accessible only by service's own user by default";
+ b = 0;
+ }
+
+ copy = strdup(d);
+ if (!copy)
+ return log_oom();
+
+ *ret_badness = b;
+ *ret_description = copy;
+
+ return 0;
+}
+
+static int assess_keyring_mode(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ *ret_badness = !streq_ptr(info->keyring_mode, "private");
+ *ret_description = NULL;
+
+ return 0;
+}
+
+static int assess_notify_access(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ *ret_badness = streq_ptr(info->notify_access, "all");
+ *ret_description = NULL;
+
+ return 0;
+}
+
+static int assess_remove_ipc(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ if (security_info_runs_privileged(info))
+ *ret_badness = UINT64_MAX;
+ else
+ *ret_badness = !info->remove_ipc;
+
+ *ret_description = NULL;
+ return 0;
+}
+
+static int assess_supplementary_groups(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ if (security_info_runs_privileged(info))
+ *ret_badness = UINT64_MAX;
+ else
+ *ret_badness = !strv_isempty(info->supplementary_groups);
+
+ *ret_description = NULL;
+ return 0;
+}
+
+static int assess_restrict_namespaces(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ *ret_badness = !!(info->restrict_namespaces & a->parameter);
+ *ret_description = NULL;
+
+ return 0;
+}
+
+static int assess_system_call_architectures(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ char *d;
+ uint64_t b;
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ if (strv_isempty(info->system_call_architectures)) {
+ b = 10;
+ d = strdup("Service may execute system calls with all ABIs");
+ } else if (strv_equal(info->system_call_architectures, STRV_MAKE("native"))) {
+ b = 0;
+ d = strdup("Service may execute system calls only with native ABI");
+ } else {
+ b = 8;
+ d = strdup("Service may execute system calls with multiple ABIs");
+ }
+
+ if (!d)
+ return log_oom();
+
+ *ret_badness = b;
+ *ret_description = d;
+
+ return 0;
+}
+
+#if HAVE_SECCOMP
+
+static bool syscall_names_in_filter(Set *s, bool whitelist, const SyscallFilterSet *f) {
+ const char *syscall;
+
+ NULSTR_FOREACH(syscall, f->value) {
+ bool b;
+
+ if (syscall[0] == '@') {
+ const SyscallFilterSet *g;
+ assert_se(g = syscall_filter_set_find(syscall));
+ b = syscall_names_in_filter(s, whitelist, g);
+ } else {
+ int id;
+
+ /* Let's see if the system call actually exists on this platform, before complaining */
+ id = seccomp_syscall_resolve_name(syscall);
+ if (id < 0)
+ continue;
+
+ b = set_contains(s, syscall);
+ }
+
+ if (whitelist == b) {
+ log_debug("Offending syscall filter item: %s", syscall);
+ return true; /* bad! */
+ }
+ }
+
+ return false;
+}
+
+static int assess_system_call_filter(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ const SyscallFilterSet *f;
+ char *d = NULL;
+ uint64_t b;
+
+ assert(a);
+ assert(info);
+ assert(ret_badness);
+ assert(ret_description);
+
+ assert(a->parameter < _SYSCALL_FILTER_SET_MAX);
+ f = syscall_filter_sets + a->parameter;
+
+ if (!info->system_call_filter_whitelist && set_isempty(info->system_call_filter)) {
+ d = strdup("Service does not filter system calls");
+ b = 10;
+ } else {
+ bool bad;
+
+ log_debug("Analyzing system call filter, checking against: %s", f->name);
+ bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_whitelist, f);
+ log_debug("Result: %s", bad ? "bad" : "good");
+
+ if (info->system_call_filter_whitelist) {
+ if (bad) {
+ (void) asprintf(&d, "System call whitelist defined for service, and %s is included", f->name);
+ b = 9;
+ } else {
+ (void) asprintf(&d, "System call whitelist defined for service, and %s is not included", f->name);
+ b = 0;
+ }
+ } else {
+ if (bad) {
+ (void) asprintf(&d, "System call blacklist defined for service, and %s is not included", f->name);
+ b = 10;
+ } else {
+ (void) asprintf(&d, "System call blacklist defined for service, and %s is included", f->name);
+ b = 5;
+ }
+ }
+ }
+
+ if (!d)
+ return log_oom();
+
+ *ret_badness = b;
+ *ret_description = d;
+
+ return 0;
+}
+
+#endif
+
+static int assess_ip_address_allow(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ char *d = NULL;
+ uint64_t b;
+
+ assert(info);
+ assert(ret_badness);
+ assert(ret_description);
+
+ if (!info->ip_address_deny_all) {
+ d = strdup("Service does not define an IP address whitelist");
+ b = 10;
+ } else if (info->ip_address_allow_other) {
+ d = strdup("Service defines IP address whitelist with non-localhost entries");
+ b = 5;
+ } else if (info->ip_address_allow_localhost) {
+ d = strdup("Service defines IP address whitelits with only localhost entries");
+ b = 2;
+ } else {
+ d = strdup("Service blocks all IP address ranges");
+ b = 0;
+ }
+
+ if (!d)
+ return log_oom();
+
+ *ret_badness = b;
+ *ret_description = d;
+
+ return 0;
+}
+
+static int assess_device_allow(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ char *d = NULL;
+ uint64_t b;
+
+ assert(info);
+ assert(ret_badness);
+ assert(ret_description);
+
+ if (STRPTR_IN_SET(info->device_policy, "strict", "closed")) {
+
+ if (info->device_allow_non_empty) {
+ d = strdup("Service has a device ACL with some special devices");
+ b = 5;
+ } else {
+ d = strdup("Service has a minimal device ACL");
+ b = 0;
+ }
+ } else {
+ d = strdup("Service has no device ACL");
+ b = 10;
+ }
+
+ if (!d)
+ return log_oom();
+
+ *ret_badness = b;
+ *ret_description = d;
+
+ return 0;
+}
+
+static int assess_ambient_capabilities(
+ const struct security_assessor *a,
+ const struct security_info *info,
+ const void *data,
+ uint64_t *ret_badness,
+ char **ret_description) {
+
+ assert(ret_badness);
+ assert(ret_description);
+
+ *ret_badness = info->ambient_capabilities != 0;
+ *ret_description = NULL;
+
+ return 0;
+}
+
+static const struct security_assessor security_assessor_table[] = {
+ {
+ .id = "User=/DynamicUser=",
+ .description_bad = "Service runs as root user",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
+ .weight = 2000,
+ .range = 10,
+ .assess = assess_user,
+ },
+ {
+ .id = "SupplementaryGroups=",
+ .description_good = "Service has no supplementary groups",
+ .description_bad = "Service runs with supplementary groups",
+ .description_na = "Service runs as root, option does not matter",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=",
+ .weight = 200,
+ .range = 1,
+ .assess = assess_supplementary_groups,
+ },
+ {
+ .id = "PrivateDevices=",
+ .description_good = "Service has no access to hardware devices",
+ .description_bad = "Service potentially has access to hardware devices",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, private_devices),
+ },
+ {
+ .id = "PrivateMounts=",
+ .description_good = "Service cannot install system mounts",
+ .description_bad = "Service may install system mounts",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, private_mounts),
+ },
+ {
+ .id = "PrivateNetwork=",
+ .description_good = "Service has no access to the host's network",
+ .description_bad = "Service has access to the host's network",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
+ .weight = 2500,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, private_network),
+ },
+ {
+ .id = "PrivateTmp=",
+ .description_good = "Service has no access to other software's temporary files",
+ .description_bad = "Service has access to other software's temporary files",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, private_tmp),
+ .default_dependencies_only = true,
+ },
+ {
+ .id = "PrivateUsers=",
+ .description_good = "Service does not have access to other users",
+ .description_bad = "Service has access to other users",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, private_users),
+ },
+ {
+ .id = "ProtectControlGroups=",
+ .description_good = "Service cannot modify the control group file system",
+ .description_bad = "Service may modify to the control group file system",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, protect_control_groups),
+ },
+ {
+ .id = "ProtectKernelModules=",
+ .description_good = "Service cannot load or read kernel modules",
+ .description_bad = "Service may load or read kernel modules",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, protect_kernel_modules),
+ },
+ {
+ .id = "ProtectKernelTunables=",
+ .description_good = "Service cannot alter kernel tunables (/proc/sys, …)",
+ .description_bad = "Service may alter kernel tunables",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, protect_kernel_tunables),
+ },
+ {
+ .id = "ProtectHome=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_protect_home,
+ .default_dependencies_only = true,
+ },
+ {
+ .id = "ProtectSystem=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_protect_system,
+ .default_dependencies_only = true,
+ },
+ {
+ .id = "RootDirectory=/RootImage=",
+ .description_good = "Service has its own root directory/image",
+ .description_bad = "Service runs within the host's root directory",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
+ .weight = 200,
+ .range = 1,
+ .assess = assess_root_directory,
+ .default_dependencies_only = true,
+ },
+ {
+ .id = "LockPersonality=",
+ .description_good = "Service cannot change ABI personality",
+ .description_bad = "Service may change ABI personality",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
+ .weight = 100,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, lock_personality),
+ },
+ {
+ .id = "MemoryDenyWriteExecute=",
+ .description_good = "Service cannot create writable executable memory mappings",
+ .description_bad = "Service may create writable executable memory mappings",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
+ .weight = 100,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, memory_deny_write_execute),
+ },
+ {
+ .id = "NoNewPrivileges=",
+ .description_good = "Service processes cannot acquire new privileges",
+ .description_bad = "Service processes may acquire new privileges",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, no_new_privileges),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SYS_ADMIN",
+ .description_good = "Service has no administrator privileges",
+ .description_bad = "Service has administrator privileges",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 1500,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = UINT64_C(1) << CAP_SYS_ADMIN,
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
+ .description_good = "Service cannot change UID/GID identities/capabilities",
+ .description_bad = "Service may change UID/GID identities/capabilities",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 1500,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_SETUID)|
+ (UINT64_C(1) << CAP_SETGID)|
+ (UINT64_C(1) << CAP_SETPCAP),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SYS_PTRACE",
+ .description_good = "Service has no ptrace() debugging abilities",
+ .description_bad = "Service has ptrace() debugging abilities",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 1500,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_SYS_PTRACE),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SYS_TIME",
+ .description_good = "Service processes cannot change the system clock",
+ .description_bad = "Service processes may change the system clock",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = UINT64_C(1) << CAP_SYS_TIME,
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_NET_ADMIN",
+ .description_good = "Service has no network configuration privileges",
+ .description_bad = "Service has network configuration privileges",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_NET_ADMIN),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_RAWIO",
+ .description_good = "Service has no raw I/O access",
+ .description_bad = "Service has raw I/O access",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_SYS_RAWIO),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SYS_MODULE",
+ .description_good = "Service cannot load kernel modules",
+ .description_bad = "Service may load kernel modules",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_SYS_MODULE),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_AUDIT_*",
+ .description_good = "Service has no audit subsystem access",
+ .description_bad = "Service has audit subsystem access",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_AUDIT_CONTROL) |
+ (UINT64_C(1) << CAP_AUDIT_READ) |
+ (UINT64_C(1) << CAP_AUDIT_WRITE),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SYSLOG",
+ .description_good = "Service has no access to kernel logging",
+ .description_bad = "Service has access to kernel logging",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_SYSLOG),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
+ .description_good = "Service has no privileges to change resource use parameters",
+ .description_bad = "Service has privileges to change resource use parameters",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_SYS_NICE) |
+ (UINT64_C(1) << CAP_SYS_RESOURCE),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_MKNOD",
+ .description_good = "Service cannot create device nodes",
+ .description_bad = "Service may create device nodes",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_MKNOD),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
+ .description_good = "Service cannot change file ownership/access mode/capabilities",
+ .description_bad = "Service may change file ownership/access mode/capabilities unrestricted",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_CHOWN) |
+ (UINT64_C(1) << CAP_FSETID) |
+ (UINT64_C(1) << CAP_SETFCAP),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
+ .description_good = "Service cannot override UNIX file/IPC permission checks",
+ .description_bad = "Service may override UNIX file/IPC permission checks",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_DAC_OVERRIDE) |
+ (UINT64_C(1) << CAP_DAC_READ_SEARCH) |
+ (UINT64_C(1) << CAP_FOWNER) |
+ (UINT64_C(1) << CAP_IPC_OWNER),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_KILL",
+ .description_good = "Service cannot send UNIX signals to arbitrary processes",
+ .description_bad = "Service may send UNIX signals to arbitrary processes",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_KILL),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
+ .description_good = "Service has no elevated networking privileges",
+ .description_bad = "Service has elevated networking privileges",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_NET_BIND_SERVICE) |
+ (UINT64_C(1) << CAP_NET_BROADCAST) |
+ (UINT64_C(1) << CAP_NET_RAW),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SYS_BOOT",
+ .description_good = "Service cannot issue reboot()",
+ .description_bad = "Service may issue reboot()",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 100,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_SYS_BOOT),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_MAC_*",
+ .description_good = "Service cannot adjust SMACK MAC",
+ .description_bad = "Service may adjust SMACK MAC",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 100,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_MAC_ADMIN)|
+ (UINT64_C(1) << CAP_MAC_OVERRIDE),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
+ .description_good = "Service cannot mark files immutable",
+ .description_bad = "Service may mark files immutable",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 75,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_LINUX_IMMUTABLE),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_IPC_LOCK",
+ .description_good = "Service cannot lock memory into RAM",
+ .description_bad = "Service may lock memory into RAM",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 50,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_IPC_LOCK),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SYS_CHROOT",
+ .description_good = "Service cannot issue chroot()",
+ .description_bad = "Service may issue chroot()",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 50,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_SYS_CHROOT),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
+ .description_good = "Service cannot establish wake locks",
+ .description_bad = "Service may establish wake locks",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 25,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_BLOCK_SUSPEND),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_WAKE_ALARM",
+ .description_good = "Service cannot program timers that wake up the system",
+ .description_bad = "Service may program timers that wake up the system",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 25,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_WAKE_ALARM),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_LEASE",
+ .description_good = "Service cannot create file leases",
+ .description_bad = "Service may create file leases",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 25,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_LEASE),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
+ .description_good = "Service cannot issue vhangup()",
+ .description_bad = "Service may issue vhangup()",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 25,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_SYS_TTY_CONFIG),
+ },
+ {
+ .id = "CapabilityBoundingSet=~CAP_SYS_PACCT",
+ .description_good = "Service cannot use acct()",
+ .description_bad = "Service may use acct()",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
+ .weight = 25,
+ .range = 1,
+ .assess = assess_capability_bounding_set,
+ .parameter = (UINT64_C(1) << CAP_SYS_PACCT),
+ },
+ {
+ .id = "UMask=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
+ .weight = 100,
+ .range = 10,
+ .assess = assess_umask,
+ },
+ {
+ .id = "KeyringMode=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
+ .description_good = "Service doesn't share key material with other services",
+ .description_bad = "Service shares key material with other service",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_keyring_mode,
+ },
+ {
+ .id = "NotifyAccess=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
+ .description_good = "Service child processes cannot alter service state",
+ .description_bad = "Service child processes may alter service state",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_notify_access,
+ },
+ {
+ .id = "RemoveIPC=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
+ .description_good = "Service user cannot leave SysV IPC objects around",
+ .description_bad = "Service user may leave SysV IPC objects around",
+ .description_na = "Service runs as root, option does not apply",
+ .weight = 100,
+ .range = 1,
+ .assess = assess_remove_ipc,
+ .offset = offsetof(struct security_info, remove_ipc),
+ },
+ {
+ .id = "Delegate=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
+ .description_good = "Service does not maintain its own delegated control group subtree",
+ .description_bad = "Service maintains its own delegated control group subtree",
+ .weight = 100,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, delegate),
+ .parameter = true, /* invert! */
+ },
+ {
+ .id = "RestrictRealtime=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
+ .description_good = "Service realtime scheduling access is restricted",
+ .description_bad = "Service may acquire realtime scheduling",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, restrict_realtime),
+ },
+ {
+ .id = "RestrictNamespaces=~CLONE_NEWUSER",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
+ .description_good = "Service cannot create user namespaces",
+ .description_bad = "Service may create user namespaces",
+ .weight = 1500,
+ .range = 1,
+ .assess = assess_restrict_namespaces,
+ .parameter = CLONE_NEWUSER,
+ },
+ {
+ .id = "RestrictNamespaces=~CLONE_NEWNS",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
+ .description_good = "Service cannot create file system namespaces",
+ .description_bad = "Service may create file system namespaces",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_restrict_namespaces,
+ .parameter = CLONE_NEWNS,
+ },
+ {
+ .id = "RestrictNamespaces=~CLONE_NEWIPC",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
+ .description_good = "Service cannot create IPC namespaces",
+ .description_bad = "Service may create IPC namespaces",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_restrict_namespaces,
+ .parameter = CLONE_NEWIPC,
+ },
+ {
+ .id = "RestrictNamespaces=~CLONE_NEWPID",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
+ .description_good = "Service cannot create process namespaces",
+ .description_bad = "Service may create process namespaces",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_restrict_namespaces,
+ .parameter = CLONE_NEWPID,
+ },
+ {
+ .id = "RestrictNamespaces=~CLONE_NEWCGROUP",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
+ .description_good = "Service cannot create cgroup namespaces",
+ .description_bad = "Service may create cgroup namespaces",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_restrict_namespaces,
+ .parameter = CLONE_NEWCGROUP,
+ },
+ {
+ .id = "RestrictNamespaces=~CLONE_NEWNET",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
+ .description_good = "Service cannot create network namespaces",
+ .description_bad = "Service may create network namespaces",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_restrict_namespaces,
+ .parameter = CLONE_NEWNET,
+ },
+ {
+ .id = "RestrictNamespaces=~CLONE_NEWUTS",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
+ .description_good = "Service cannot create hostname namespaces",
+ .description_bad = "Service may create hostname namespaces",
+ .weight = 100,
+ .range = 1,
+ .assess = assess_restrict_namespaces,
+ .parameter = CLONE_NEWUTS,
+ },
+ {
+ .id = "RestrictAddressFamilies=~AF_(INET|INET6)",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
+ .description_good = "Service cannot allocate Internet sockets",
+ .description_bad = "Service may allocate Internet sockets",
+ .weight = 1500,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, restrict_address_family_inet),
+ },
+ {
+ .id = "RestrictAddressFamilies=~AF_UNIX",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
+ .description_good = "Service cannot allocate local sockets",
+ .description_bad = "Service may allocate local sockets",
+ .weight = 25,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, restrict_address_family_unix),
+ },
+ {
+ .id = "RestrictAddressFamilies=~AF_NETLINK",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
+ .description_good = "Service cannot allocate netlink sockets",
+ .description_bad = "Service may allocate netlink sockets",
+ .weight = 200,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, restrict_address_family_netlink),
+ },
+ {
+ .id = "RestrictAddressFamilies=~AF_PACKET",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
+ .description_good = "Service cannot allocate packet sockets",
+ .description_bad = "Service may allocate packet sockets",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, restrict_address_family_packet),
+ },
+ {
+ .id = "RestrictAddressFamilies=~…",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
+ .description_good = "Service cannot allocate exotic sockets",
+ .description_bad = "Service may allocate exotic sockets",
+ .weight = 1250,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, restrict_address_family_other),
+ },
+ {
+ .id = "SystemCallArchitectures=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_system_call_architectures,
+ },
+#if HAVE_SECCOMP
+ {
+ .id = "SystemCallFilter=~@swap",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_SWAP,
+ },
+ {
+ .id = "SystemCallFilter=~@obsolete",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 250,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_OBSOLETE,
+ },
+ {
+ .id = "SystemCallFilter=~@clock",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_CLOCK,
+ },
+ {
+ .id = "SystemCallFilter=~@cpu-emulation",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 250,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_CPU_EMULATION,
+ },
+ {
+ .id = "SystemCallFilter=~@debug",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_DEBUG,
+ },
+ {
+ .id = "SystemCallFilter=~@mount",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_MOUNT,
+ },
+ {
+ .id = "SystemCallFilter=~@module",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_MODULE,
+ },
+ {
+ .id = "SystemCallFilter=~@raw-io",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_RAW_IO,
+ },
+ {
+ .id = "SystemCallFilter=~@reboot",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_REBOOT,
+ },
+ {
+ .id = "SystemCallFilter=~@privileged",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 700,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_PRIVILEGED,
+ },
+ {
+ .id = "SystemCallFilter=~@resources",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
+ .weight = 700,
+ .range = 10,
+ .assess = assess_system_call_filter,
+ .parameter = SYSCALL_FILTER_SET_RESOURCES,
+ },
+#endif
+ {
+ .id = "IPAddressDeny=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_ip_address_allow,
+ },
+ {
+ .id = "DeviceAllow=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
+ .weight = 1000,
+ .range = 10,
+ .assess = assess_device_allow,
+ },
+ {
+ .id = "AmbientCapabilities=",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
+ .description_good = "Service process does not receive ambient capabilities",
+ .description_bad = "Service process receives ambient capabilities",
+ .weight = 500,
+ .range = 1,
+ .assess = assess_ambient_capabilities,
+ },
+};
+
+static int assess(const struct security_info *info, Table *overview_table, AnalyzeSecurityFlags flags) {
+ static const struct {
+ uint64_t exposure;
+ const char *name;
+ const char *color;
+ SpecialGlyph smiley;
+ } badness_table[] = {
+ { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED, SPECIAL_GLYPH_DEPRESSED_SMILEY },
+ { 90, "UNSAFE", ANSI_HIGHLIGHT_RED, SPECIAL_GLYPH_UNHAPPY_SMILEY },
+ { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY },
+ { 50, "MEDIUM", NULL, SPECIAL_GLYPH_NEUTRAL_SMILEY },
+ { 10, "OK", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY },
+ { 1, "SAFE", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_HAPPY_SMILEY },
+ { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN, SPECIAL_GLYPH_ECSTATIC_SMILEY },
+ };
+
+ uint64_t badness_sum = 0, weight_sum = 0, exposure;
+ _cleanup_(table_unrefp) Table *details_table = NULL;
+ size_t i;
+ int r;
+
+ if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
+ details_table = table_new(" ", "name", "description", "weight", "badness", "range", "exposure");
+ if (!details_table)
+ return log_oom();
+
+ (void) table_set_sort(details_table, 3, 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);
+ }
+
+ for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) {
+ const struct security_assessor *a = security_assessor_table + i;
+ _cleanup_free_ char *d = NULL;
+ uint64_t badness;
+ void *data;
+
+ data = (uint8_t*) info + a->offset;
+
+ if (a->default_dependencies_only && !info->default_dependencies) {
+ badness = UINT64_MAX;
+ d = strdup("Service runs in special boot phase, option does not apply");
+ if (!d)
+ return log_oom();
+ } else {
+ r = a->assess(a, info, data, &badness, &d);
+ if (r < 0)
+ return r;
+ }
+
+ assert(a->range > 0);
+
+ if (badness != UINT64_MAX) {
+ assert(badness <= a->range);
+
+ badness_sum += DIV_ROUND_UP(badness * a->weight, a->range);
+ weight_sum += a->weight;
+ }
+
+ if (details_table) {
+ const char *checkmark, *description, *color = NULL;
+ TableCell *cell;
+
+ if (badness == UINT64_MAX) {
+ checkmark = " ";
+ description = a->description_na;
+ color = NULL;
+ } else if (badness == a->range) {
+ checkmark = special_glyph(SPECIAL_GLYPH_CROSS_MARK);
+ description = a->description_bad;
+ color = ansi_highlight_red();
+ } else if (badness == 0) {
+ checkmark = special_glyph(SPECIAL_GLYPH_CHECK_MARK);
+ description = a->description_good;
+ color = ansi_highlight_green();
+ } else {
+ checkmark = special_glyph(SPECIAL_GLYPH_CROSS_MARK);
+ description = NULL;
+ color = ansi_highlight_red();
+ }
+
+ if (d)
+ description = d;
+
+ r = table_add_cell_full(details_table, &cell, TABLE_STRING, checkmark, 1, 1, 0, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+ if (color)
+ (void) table_set_color(details_table, cell, color);
+
+ r = table_add_cell(details_table, &cell, TABLE_STRING, a->id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+ if (a->url)
+ (void) table_set_url(details_table, cell, a->url);
+
+ r = table_add_cell(details_table, NULL, TABLE_STRING, description);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+
+ r = table_add_cell(details_table, &cell, TABLE_UINT64, &a->weight);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+ (void) table_set_align_percent(details_table, cell, 100);
+
+ r = table_add_cell(details_table, &cell, TABLE_UINT64, &badness);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+ (void) table_set_align_percent(details_table, cell, 100);
+
+ r = table_add_cell(details_table, &cell, TABLE_UINT64, &a->range);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+ (void) table_set_align_percent(details_table, cell, 100);
+
+ r = table_add_cell(details_table, &cell, TABLE_EMPTY, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+ (void) table_set_align_percent(details_table, cell, 100);
+ }
+ }
+
+ if (details_table) {
+ size_t row;
+
+ for (row = 1; row < table_get_rows(details_table); row++) {
+ char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
+ const uint64_t *weight, *badness, *range;
+ TableCell *cell;
+ uint64_t x;
+
+ assert_se(weight = table_get_at(details_table, row, 3));
+ assert_se(badness = table_get_at(details_table, row, 4));
+ assert_se(range = table_get_at(details_table, row, 5));
+
+ if (*badness == UINT64_MAX || *badness == 0)
+ continue;
+
+ assert_se(cell = table_get_cell(details_table, row, 6));
+
+ x = DIV_ROUND_UP(DIV_ROUND_UP(*badness * *weight * 100U, *range), weight_sum);
+ xsprintf(buf, "%" PRIu64 ".%" PRIu64, x / 10, x % 10);
+
+ r = table_update(details_table, cell, TABLE_STRING, buf);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update cell in table: %m");
+ }
+
+ r = table_print(details_table, stdout);
+ if (r < 0)
+ return log_error_errno(r, "Failed to output table: %m");
+ }
+
+ assert(weight_sum > 0);
+ exposure = DIV_ROUND_UP(badness_sum * 100U, weight_sum);
+
+ for (i = 0; i < ELEMENTSOF(badness_table); i++)
+ if (exposure >= badness_table[i].exposure)
+ break;
+
+ assert(i < ELEMENTSOF(badness_table));
+
+ if (details_table) {
+ _cleanup_free_ char *clickable = NULL;
+ const char *name;
+
+ /* If we shall output the details table, also print the brief summary underneath */
+
+ if (info->fragment_path) {
+ r = terminal_urlify_path(info->fragment_path, info->id, &clickable);
+ if (r < 0)
+ return log_oom();
+
+ name = clickable;
+ } else
+ name = info->id;
+
+ printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64 ".%" PRIu64 " %s%s %s\n",
+ special_glyph(SPECIAL_GLYPH_ARROW),
+ ansi_highlight(),
+ name,
+ ansi_normal(),
+ colors_enabled() ? strempty(badness_table[i].color) : "",
+ exposure / 10, exposure % 10,
+ badness_table[i].name,
+ ansi_normal(),
+ special_glyph(badness_table[i].smiley));
+ }
+
+ fflush(stdout);
+
+ if (overview_table) {
+ char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1];
+ TableCell *cell;
+
+ r = table_add_cell(overview_table, &cell, TABLE_STRING, info->id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+ if (info->fragment_path) {
+ _cleanup_free_ char *url = NULL;
+
+ r = file_url_from_path(info->fragment_path, &url);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate URL from path: %m");
+
+ (void) table_set_url(overview_table, cell, url);
+ }
+
+ xsprintf(buf, "%" PRIu64 ".%" PRIu64, exposure / 10, exposure % 10);
+ r = table_add_cell(overview_table, &cell, TABLE_STRING, buf);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+ (void) table_set_align_percent(overview_table, cell, 100);
+
+ r = table_add_cell(overview_table, &cell, TABLE_STRING, badness_table[i].name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+ (void) table_set_color(overview_table, cell, strempty(badness_table[i].color));
+
+ r = table_add_cell(overview_table, NULL, TABLE_STRING, special_glyph(badness_table[i].smiley));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cell to table: %m");
+ }
+
+ return 0;
+}
+
+static int property_read_restrict_address_families(
+ sd_bus *bus,
+ const char *member,
+ sd_bus_message *m,
+ sd_bus_error *error,
+ void *userdata) {
+
+ struct security_info *info = userdata;
+ int whitelist, r;
+
+ assert(bus);
+ assert(member);
+ assert(m);
+
+ r = sd_bus_message_enter_container(m, 'r', "bas");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(m, "b", &whitelist);
+ if (r < 0)
+ return r;
+
+ info->restrict_address_family_inet =
+ info->restrict_address_family_unix =
+ info->restrict_address_family_netlink =
+ info->restrict_address_family_packet =
+ info->restrict_address_family_other = whitelist;
+
+ r = sd_bus_message_enter_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *name;
+
+ r = sd_bus_message_read(m, "s", &name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (STR_IN_SET(name, "AF_INET", "AF_INET6"))
+ info->restrict_address_family_inet = !whitelist;
+ else if (streq(name, "AF_UNIX"))
+ info->restrict_address_family_unix = !whitelist;
+ else if (streq(name, "AF_NETLINK"))
+ info->restrict_address_family_netlink = !whitelist;
+ else if (streq(name, "AF_PACKET"))
+ info->restrict_address_family_packet = !whitelist;
+ else
+ info->restrict_address_family_other = !whitelist;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_exit_container(m);
+}
+
+static int property_read_system_call_filter(
+ sd_bus *bus,
+ const char *member,
+ sd_bus_message *m,
+ sd_bus_error *error,
+ void *userdata) {
+
+ struct security_info *info = userdata;
+ int whitelist, r;
+
+ assert(bus);
+ assert(member);
+ assert(m);
+
+ r = sd_bus_message_enter_container(m, 'r', "bas");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(m, "b", &whitelist);
+ if (r < 0)
+ return r;
+
+ info->system_call_filter_whitelist = whitelist;
+
+ r = sd_bus_message_enter_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *name;
+
+ r = sd_bus_message_read(m, "s", &name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = set_ensure_allocated(&info->system_call_filter, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put_strdup(info->system_call_filter, name);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_exit_container(m);
+}
+
+static int property_read_ip_address_allow(
+ sd_bus *bus,
+ const char *member,
+ sd_bus_message *m,
+ sd_bus_error *error,
+ void *userdata) {
+
+ struct security_info *info = userdata;
+ bool deny_ipv4 = false, deny_ipv6 = false;
+ int r;
+
+ assert(bus);
+ assert(member);
+ assert(m);
+
+ r = sd_bus_message_enter_container(m, 'a', "(iayu)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const void *data;
+ size_t size;
+ int32_t family;
+ uint32_t prefixlen;
+
+ r = sd_bus_message_enter_container(m, 'r', "iayu");
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_read(m, "i", &family);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_array(m, 'y', &data, &size);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(m, "u", &prefixlen);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ if (streq(member, "IPAddressAllow")) {
+ union in_addr_union u;
+
+ if (family == AF_INET && size == 4 && prefixlen == 8)
+ memcpy(&u.in, data, size);
+ else if (family == AF_INET6 && size == 16 && prefixlen == 128)
+ memcpy(&u.in6, data, size);
+ else {
+ info->ip_address_allow_other = true;
+ continue;
+ }
+
+ if (in_addr_is_localhost(family, &u))
+ info->ip_address_allow_localhost = true;
+ else
+ info->ip_address_allow_other = true;
+ } else {
+ assert(streq(member, "IPAddressDeny"));
+
+ if (family == AF_INET && size == 4 && prefixlen == 0)
+ deny_ipv4 = true;
+ else if (family == AF_INET6 && size == 16 && prefixlen == 0)
+ deny_ipv6 = true;
+ }
+ }
+
+ info->ip_address_deny_all = deny_ipv4 && deny_ipv6;
+
+ return sd_bus_message_exit_container(m);
+}
+
+static int property_read_device_allow(
+ sd_bus *bus,
+ const char *member,
+ sd_bus_message *m,
+ sd_bus_error *error,
+ void *userdata) {
+
+ struct security_info *info = userdata;
+ size_t n = 0;
+ int r;
+
+ assert(bus);
+ assert(member);
+ assert(m);
+
+ r = sd_bus_message_enter_container(m, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *name, *policy;
+
+ r = sd_bus_message_read(m, "(ss)", &name, &policy);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ n++;
+ }
+
+ info->device_allow_non_empty = n > 0;
+
+ return sd_bus_message_exit_container(m);
+}
+
+static int acquire_security_info(sd_bus *bus, const char *name, struct security_info *info, AnalyzeSecurityFlags flags) {
+
+ static const struct bus_properties_map security_map[] = {
+ { "AmbientCapabilities", "t", NULL, offsetof(struct security_info, ambient_capabilities) },
+ { "CapabilityBoundingSet", "t", NULL, offsetof(struct security_info, capability_bounding_set) },
+ { "DefaultDependencies", "b", NULL, offsetof(struct security_info, default_dependencies) },
+ { "Delegate", "b", NULL, offsetof(struct security_info, delegate) },
+ { "DeviceAllow", "a(ss)", property_read_device_allow, 0 },
+ { "DevicePolicy", "s", NULL, offsetof(struct security_info, device_policy) },
+ { "DynamicUser", "b", NULL, offsetof(struct security_info, dynamic_user) },
+ { "FragmentPath", "s", NULL, offsetof(struct security_info, fragment_path) },
+ { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow, 0 },
+ { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow, 0 },
+ { "Id", "s", NULL, offsetof(struct security_info, id) },
+ { "KeyringMode", "s", NULL, offsetof(struct security_info, keyring_mode) },
+ { "LoadState", "s", NULL, offsetof(struct security_info, load_state) },
+ { "LockPersonality", "b", NULL, offsetof(struct security_info, lock_personality) },
+ { "MemoryDenyWriteExecute", "b", NULL, offsetof(struct security_info, memory_deny_write_execute) },
+ { "NoNewPrivileges", "b", NULL, offsetof(struct security_info, no_new_privileges) },
+ { "NotifyAccess", "s", NULL, offsetof(struct security_info, notify_access) },
+ { "PrivateDevices", "b", NULL, offsetof(struct security_info, private_devices) },
+ { "PrivateMounts", "b", NULL, offsetof(struct security_info, private_mounts) },
+ { "PrivateNetwork", "b", NULL, offsetof(struct security_info, private_network) },
+ { "PrivateTmp", "b", NULL, offsetof(struct security_info, private_tmp) },
+ { "PrivateUsers", "b", NULL, offsetof(struct security_info, private_users) },
+ { "PrivateUsers", "b", NULL, offsetof(struct security_info, private_users) },
+ { "ProtectControlGroups", "b", NULL, offsetof(struct security_info, protect_control_groups) },
+ { "ProtectHome", "s", NULL, offsetof(struct security_info, protect_home) },
+ { "ProtectKernelModules", "b", NULL, offsetof(struct security_info, protect_kernel_modules) },
+ { "ProtectKernelTunables", "b", NULL, offsetof(struct security_info, protect_kernel_tunables) },
+ { "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 },
+ { "RestrictNamespaces", "t", NULL, offsetof(struct security_info, restrict_namespaces) },
+ { "RestrictRealtime", "b", NULL, offsetof(struct security_info, restrict_realtime) },
+ { "RootDirectory", "s", NULL, offsetof(struct security_info, root_directory) },
+ { "RootImage", "s", NULL, offsetof(struct security_info, root_image) },
+ { "SupplementaryGroups", "as", NULL, offsetof(struct security_info, supplementary_groups) },
+ { "SystemCallArchitectures", "as", NULL, offsetof(struct security_info, system_call_architectures) },
+ { "SystemCallFilter", "(as)", property_read_system_call_filter, 0 },
+ { "Type", "s", NULL, offsetof(struct security_info, type) },
+ { "UMask", "u", NULL, offsetof(struct security_info, _umask) },
+ { "User", "s", NULL, offsetof(struct security_info, user) },
+ {}
+ };
+
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ /* Note: this mangles *info on failure! */
+
+ assert(bus);
+ assert(name);
+ assert(info);
+
+ path = unit_dbus_path_from_name(name);
+ if (!path)
+ return log_oom();
+
+ r = bus_map_all_properties(bus,
+ "org.freedesktop.systemd1",
+ path,
+ security_map,
+ BUS_MAP_STRDUP|BUS_MAP_BOOLEAN_AS_BOOL,
+ &error,
+ NULL,
+ info);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get unit properties: %s", bus_error_message(&error, r));
+
+ if (!streq_ptr(info->load_state, "loaded")) {
+
+ if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LOADED))
+ return -EMEDIUMTYPE;
+
+ if (streq_ptr(info->load_state, "not-found"))
+ log_error("Unit %s not found, cannot analyze.", name);
+ else if (streq_ptr(info->load_state, "masked"))
+ log_error("Unit %s is masked, cannot analyze.", name);
+ else
+ log_error("Unit %s not loaded properly, cannot analyze.", name);
+
+ return -EINVAL;
+ }
+
+ if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LONG_RUNNING) && streq_ptr(info->type, "oneshot"))
+ return -EMEDIUMTYPE;
+
+ if (info->private_devices ||
+ info->private_tmp ||
+ info->protect_control_groups ||
+ info->protect_kernel_tunables ||
+ info->protect_kernel_modules ||
+ !streq_ptr(info->protect_home, "no") ||
+ !streq_ptr(info->protect_system, "no") ||
+ info->root_image)
+ info->private_mounts = true;
+
+ if (info->protect_kernel_modules)
+ info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYS_MODULE);
+
+ if (info->private_devices)
+ info->capability_bounding_set &= ~((UINT64_C(1) << CAP_MKNOD) |
+ (UINT64_C(1) << CAP_SYS_RAWIO));
+
+ return 0;
+}
+
+static int analyze_security_one(sd_bus *bus, const char *name, Table* overview_table, AnalyzeSecurityFlags flags) {
+ _cleanup_(security_info_free) struct security_info info = {
+ .default_dependencies = true,
+ .capability_bounding_set = UINT64_MAX,
+ .restrict_namespaces = UINT64_MAX,
+ ._umask = 0002,
+ };
+ int r;
+
+ assert(bus);
+ assert(name);
+
+ r = acquire_security_info(bus, name, &info, flags);
+ if (r == -EMEDIUMTYPE) /* Ignore this one because not loaded or Type is oneshot */
+ return 0;
+ if (r < 0)
+ return r;
+
+ r = assess(&info, overview_table, flags);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags) {
+ _cleanup_(table_unrefp) Table *overview_table = NULL;
+ int ret = 0, r;
+
+ assert(bus);
+
+ if (strv_length(units) != 1) {
+ overview_table = table_new("unit", "exposure", "predicate", "happy");
+ if (!overview_table)
+ return log_oom();
+ }
+
+ if (strv_isempty(units)) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_strv_free_ char **list = NULL;
+ size_t allocated = 0, n = 0;
+ char **i;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnits",
+ &error, &reply,
+ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ for (;;) {
+ UnitInfo info;
+ char *copy = NULL;
+
+ r = bus_parse_unit_info(reply, &info);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ if (r == 0)
+ break;
+
+ if (!endswith(info.id, ".service"))
+ continue;
+
+ if (!GREEDY_REALLOC(list, allocated, n+2))
+ return log_oom();
+
+ copy = strdup(info.id);
+ if (!copy)
+ return log_oom();
+
+ list[n++] = copy;
+ list[n] = NULL;
+ }
+
+ strv_sort(list);
+
+ flags |= ANALYZE_SECURITY_SHORT|ANALYZE_SECURITY_ONLY_LOADED|ANALYZE_SECURITY_ONLY_LONG_RUNNING;
+
+ STRV_FOREACH(i, list) {
+ r = analyze_security_one(bus, *i, overview_table, flags);
+ if (r < 0 && ret >= 0)
+ ret = r;
+ }
+
+ } else {
+ char **i;
+
+ STRV_FOREACH(i, units) {
+ _cleanup_free_ char *mangled = NULL, *instance = NULL;
+ const char *name;
+
+ if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT) && i != units) {
+ putc('\n', stdout);
+ fflush(stdout);
+ }
+
+ r = unit_name_mangle_with_suffix(*i, 0, ".service", &mangled);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name '%s': %m", *i);
+
+ if (!endswith(mangled, ".service")) {
+ log_error("Unit %s is not a service unit, refusing.", *i);
+ return -EINVAL;
+ }
+
+ if (unit_name_is_valid(mangled, UNIT_NAME_TEMPLATE)) {
+ r = unit_name_replace_instance(mangled, "test-instance", &instance);
+ if (r < 0)
+ return log_oom();
+
+ name = instance;
+ } else
+ name = mangled;
+
+ r = analyze_security_one(bus, name, overview_table, flags);
+ if (r < 0 && ret >= 0)
+ ret = r;
+ }
+ }
+
+ if (overview_table) {
+ if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
+ putc('\n', stdout);
+ fflush(stdout);
+ }
+
+ r = table_print(overview_table, stdout);
+ if (r < 0)
+ return log_error_errno(r, "Failed to output table: %m");
+ }
+
+ return ret;
+}
diff --git a/src/analyze/analyze-security.h b/src/analyze/analyze-security.h
new file mode 100644
index 0000000000..c00ae7c80a
--- /dev/null
+++ b/src/analyze/analyze-security.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+typedef enum AnalyzeSecurityFlags {
+ ANALYZE_SECURITY_SHORT = 1 << 0,
+ ANALYZE_SECURITY_ONLY_LOADED = 1 << 1,
+ ANALYZE_SECURITY_ONLY_LONG_RUNNING = 1 << 2,
+} AnalyzeSecurityFlags;
+
+int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags);
diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c
index ed369532d4..1d8a1ed7b3 100644
--- a/src/analyze/analyze-verify.c
+++ b/src/analyze/analyze-verify.c
@@ -43,10 +43,7 @@ static int prepare_filename(const char *filename, char **ret) {
if (!dir)
return -ENOMEM;
- if (with_instance)
- c = path_join(NULL, dir, with_instance);
- else
- c = path_join(NULL, dir, name);
+ c = path_join(dir, with_instance ?: name);
if (!c)
return -ENOMEM;
@@ -179,9 +176,9 @@ static int verify_documentation(Unit *u, bool check_man) {
k = show_man_page(*p + 4, true);
if (k != 0) {
if (k < 0)
- log_unit_error_errno(u, r, "Can't show %s: %m", *p);
+ log_unit_error_errno(u, k, "Can't show %s: %m", *p + 4);
else {
- log_unit_error_errno(u, r, "man %s command failed with code %d", *p + 4, k);
+ log_unit_error(u, "Command 'man %s' failed with code %d", *p + 4, k);
k = -ENOEXEC;
}
if (r == 0)
@@ -225,9 +222,10 @@ static int verify_unit(Unit *u, bool check_man) {
}
int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators) {
- const uint8_t flags = MANAGER_TEST_RUN_BASIC |
- MANAGER_TEST_RUN_ENV_GENERATORS |
- run_generators * MANAGER_TEST_RUN_GENERATORS;
+ const ManagerTestRunFlags flags =
+ MANAGER_TEST_RUN_BASIC |
+ MANAGER_TEST_RUN_ENV_GENERATORS |
+ run_generators * MANAGER_TEST_RUN_GENERATORS;
_cleanup_(manager_freep) Manager *m = NULL;
Unit *units[strv_length(filenames)];
@@ -253,7 +251,7 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run
r = manager_startup(m, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to start manager: %m");
+ return r;
manager_clear_jobs(m);
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index de0fe6eba8..1f69b9fda1 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -4,6 +4,7 @@
***/
#include <getopt.h>
+#include <inttypes.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
@@ -11,28 +12,33 @@
#include "sd-bus.h"
#include "alloc-util.h"
+#include "analyze-security.h"
#include "analyze-verify.h"
#include "bus-error.h"
#include "bus-unit-util.h"
#include "bus-util.h"
#include "calendarspec.h"
-#include "def.h"
#include "conf-files.h"
#include "copy.h"
+#include "def.h"
#include "fd-util.h"
+#include "fileio.h"
#include "glob-util.h"
#include "hashmap.h"
#include "locale-util.h"
#include "log.h"
+#include "main-func.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#if HAVE_SECCOMP
-#include "seccomp-util.h"
+# include "seccomp-util.h"
#endif
#include "special.h"
#include "strv.h"
#include "strxcpyx.h"
+#include "time-util.h"
#include "terminal-util.h"
#include "unit-name.h"
#include "util.h"
@@ -41,8 +47,6 @@
#define SCALE_X (0.1 / 1000.0) /* pixels per us */
#define SCALE_Y (20.0)
-#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
-
#define svg(...) printf(__VA_ARGS__)
#define svg_bar(class, x1, x2, y) \
@@ -66,7 +70,7 @@ static enum dot {
static char** arg_dot_from_patterns = NULL;
static char** arg_dot_to_patterns = NULL;
static usec_t arg_fuzz = 0;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static const char *arg_host = NULL;
static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
@@ -74,6 +78,9 @@ static bool arg_man = true;
static bool arg_generators = false;
static const char *arg_root = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
+
struct boot_times {
usec_t firmware_time;
usec_t loader_time;
@@ -88,6 +95,12 @@ struct boot_times {
usec_t generators_finish_time;
usec_t unitsload_start_time;
usec_t unitsload_finish_time;
+ usec_t initrd_security_start_time;
+ usec_t initrd_security_finish_time;
+ usec_t initrd_generators_start_time;
+ usec_t initrd_generators_finish_time;
+ usec_t initrd_unitsload_start_time;
+ usec_t initrd_unitsload_finish_time;
/*
* If we're analyzing the user instance, all timestamps will be offset
@@ -156,10 +169,8 @@ static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *in
&error,
't', val);
- if (r < 0) {
- log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse reply: %s", bus_error_message(&error, r));
return 0;
}
@@ -181,22 +192,18 @@ static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char
property,
&error,
strv);
- if (r < 0) {
- log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get unit property %s: %s", property, bus_error_message(&error, r));
return 0;
}
-static int compare_unit_time(const void *a, const void *b) {
- return compare(((struct unit_times *)b)->time,
- ((struct unit_times *)a)->time);
+static int compare_unit_time(const struct unit_times *a, const struct unit_times *b) {
+ return CMP(b->time, a->time);
}
-static int compare_unit_start(const void *a, const void *b) {
- return compare(((struct unit_times *)a)->activating,
- ((struct unit_times *)b)->activating);
+static int compare_unit_start(const struct unit_times *a, const struct unit_times *b) {
+ return CMP(a->activating, b->activating);
}
static void unit_times_free(struct unit_times *t) {
@@ -219,75 +226,48 @@ static void subtract_timestamp(usec_t *a, usec_t b) {
}
static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
+ static const struct bus_properties_map property_map[] = {
+ { "FirmwareTimestampMonotonic", "t", NULL, offsetof(struct boot_times, firmware_time) },
+ { "LoaderTimestampMonotonic", "t", NULL, offsetof(struct boot_times, loader_time) },
+ { "KernelTimestamp", "t", NULL, offsetof(struct boot_times, kernel_time) },
+ { "InitRDTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_time) },
+ { "UserspaceTimestampMonotonic", "t", NULL, offsetof(struct boot_times, userspace_time) },
+ { "FinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, finish_time) },
+ { "SecurityStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, security_start_time) },
+ { "SecurityFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, security_finish_time) },
+ { "GeneratorsStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, generators_start_time) },
+ { "GeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, generators_finish_time) },
+ { "UnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, unitsload_start_time) },
+ { "UnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, unitsload_finish_time) },
+ { "InitRDSecurityStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_security_start_time) },
+ { "InitRDSecurityFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_security_finish_time) },
+ { "InitRDGeneratorsStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_generators_start_time) },
+ { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_generators_finish_time) },
+ { "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_unitsload_start_time) },
+ { "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_unitsload_finish_time) },
+ {},
+ };
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
static struct boot_times times;
static bool cached = false;
+ int r;
if (cached)
goto finish;
assert_cc(sizeof(usec_t) == sizeof(uint64_t));
- if (bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "FirmwareTimestampMonotonic",
- &times.firmware_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LoaderTimestampMonotonic",
- &times.loader_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "KernelTimestamp",
- &times.kernel_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "InitRDTimestampMonotonic",
- &times.initrd_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "UserspaceTimestampMonotonic",
- &times.userspace_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "FinishTimestampMonotonic",
- &times.finish_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "SecurityStartTimestampMonotonic",
- &times.security_start_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "SecurityFinishTimestampMonotonic",
- &times.security_finish_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GeneratorsStartTimestampMonotonic",
- &times.generators_start_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GeneratorsFinishTimestampMonotonic",
- &times.generators_finish_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "UnitsLoadStartTimestampMonotonic",
- &times.unitsload_start_time) < 0 ||
- bus_get_uint64_property(bus,
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "UnitsLoadFinishTimestampMonotonic",
- &times.unitsload_finish_time) < 0)
- return -EIO;
+ r = bus_map_all_properties(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ property_map,
+ BUS_MAP_STRDUP,
+ &error,
+ NULL,
+ &times);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get timestamp properties: %s", bus_error_message(&error, r));
if (times.finish_time <= 0) {
log_error("Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n"
@@ -298,23 +278,23 @@ static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
return -EINPROGRESS;
}
- if (arg_scope == UNIT_FILE_SYSTEM) {
+ if (arg_scope == UNIT_FILE_SYSTEM && times.security_start_time > 0) {
+ /* security_start_time is set when systemd is not running under container environment. */
if (times.initrd_time > 0)
times.kernel_done_time = times.initrd_time;
else
times.kernel_done_time = times.userspace_time;
} else {
/*
- * User-instance-specific timestamps processing
+ * User-instance-specific or container-system-specific timestamps processing
* (see comment to reverse_offset in struct boot_times).
*/
times.reverse_offset = times.userspace_time;
- times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
- subtract_timestamp(&times.finish_time, times.reverse_offset);
+ times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time =
+ times.security_start_time = times.security_finish_time = 0;
- subtract_timestamp(&times.security_start_time, times.reverse_offset);
- subtract_timestamp(&times.security_finish_time, times.reverse_offset);
+ subtract_timestamp(&times.finish_time, times.reverse_offset);
subtract_timestamp(&times.generators_start_time, times.reverse_offset);
subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
@@ -348,13 +328,20 @@ static void free_host_info(struct host_info *hi) {
DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info*, free_host_info);
static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
+ static const struct bus_properties_map property_map[] = {
+ { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(struct unit_times, activating) },
+ { "ActiveEnterTimestampMonotonic", "t", NULL, offsetof(struct unit_times, activated) },
+ { "ActiveExitTimestampMonotonic", "t", NULL, offsetof(struct unit_times, deactivating) },
+ { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(struct unit_times, deactivated) },
+ {},
+ };
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r, c = 0;
- struct boot_times *boot_times = NULL;
_cleanup_(unit_times_freep) struct unit_times *unit_times = NULL;
- size_t size = 0;
+ struct boot_times *boot_times = NULL;
+ size_t allocated = 0, c = 0;
UnitInfo u;
+ int r;
r = acquire_boot_times(bus, &boot_times);
if (r < 0)
@@ -368,10 +355,8 @@ static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
"ListUnits",
&error, &reply,
NULL);
- if (r < 0) {
- log_error("Failed to list units: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
if (r < 0)
@@ -380,7 +365,7 @@ static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
while ((r = bus_parse_unit_info(reply, &u)) > 0) {
struct unit_times *t;
- if (!GREEDY_REALLOC(unit_times, size, c+2))
+ if (!GREEDY_REALLOC(unit_times, allocated, c+2))
return log_oom();
unit_times[c+1].has_data = false;
@@ -389,23 +374,17 @@ static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
assert_cc(sizeof(usec_t) == sizeof(uint64_t));
- if (bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "InactiveExitTimestampMonotonic",
- &t->activating) < 0 ||
- bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "ActiveEnterTimestampMonotonic",
- &t->activated) < 0 ||
- bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "ActiveExitTimestampMonotonic",
- &t->deactivating) < 0 ||
- bus_get_uint64_property(bus, u.unit_path,
- "org.freedesktop.systemd1.Unit",
- "InactiveEnterTimestampMonotonic",
- &t->deactivated) < 0)
- return -EIO;
+ r = bus_map_all_properties(
+ bus,
+ "org.freedesktop.systemd1",
+ u.unit_path,
+ property_map,
+ BUS_MAP_STRDUP,
+ &error,
+ NULL,
+ t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s", u.id, bus_error_message(&error, r));
subtract_timestamp(&t->activating, boot_times->reverse_offset);
subtract_timestamp(&t->activated, boot_times->reverse_offset);
@@ -453,6 +432,7 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL;
_cleanup_(free_host_infop) struct host_info *host;
int r;
@@ -460,7 +440,15 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
if (!host)
return log_oom();
- r = bus_map_all_properties(bus,
+ if (arg_scope != UNIT_FILE_SYSTEM) {
+ r = bus_connect_transport(arg_transport, arg_host, false, &system_bus);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to connect to system bus, ignoring: %m");
+ goto manager;
+ }
+ }
+
+ r = bus_map_all_properties(system_bus ?: bus,
"org.freedesktop.hostname1",
"/org/freedesktop/hostname1",
hostname_map,
@@ -468,9 +456,12 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
&error,
NULL,
host);
- if (r < 0)
- log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
+ if (r < 0) {
+ log_debug_errno(r, "Failed to get host information from systemd-hostnamed, ignoring: %s", bus_error_message(&error, r));
+ sd_bus_error_free(&error);
+ }
+manager:
r = bus_map_all_properties(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -524,7 +515,7 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) {
"ActiveEnterTimestampMonotonic",
&activated_time);
if (r < 0) {
- log_info_errno(r, "Could not get time to reach default.target. Continuing...");
+ log_info_errno(r, "Could not get time to reach default.target, ignoring: %m");
activated_time = USEC_INFINITY;
}
@@ -536,21 +527,24 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) {
size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
if (t->loader_time > 0)
size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
- if (t->kernel_time > 0)
+ if (t->kernel_done_time > 0)
size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
if (t->initrd_time > 0)
size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
- if (t->kernel_time > 0)
- strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
+ if (t->kernel_done_time > 0)
+ strpcpyf(&ptr, size, "= %s ", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
+
+ if (unit_id && activated_time > 0 && activated_time != USEC_INFINITY) {
+ usec_t base = t->userspace_time > 0 ? t->userspace_time : t->reverse_offset;
- if (unit_id && activated_time > 0 && activated_time != USEC_INFINITY)
- size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id, format_timespan(ts, sizeof(ts), activated_time - t->userspace_time, USEC_PER_MSEC));
- else if (unit_id && activated_time == 0)
+ size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id,
+ format_timespan(ts, sizeof(ts), activated_time - base, USEC_PER_MSEC));
+ } else if (unit_id && activated_time == 0)
size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id);
else if (unit_id && activated_time == USEC_INFINITY)
- size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.",unit_id);
+ size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.", unit_id);
else if (!unit_id)
size = strpcpyf(&ptr, size, "\ncould not find default.target");
@@ -585,16 +579,38 @@ static void svg_graph_box(double height, double begin, double end) {
}
}
+static int plot_unit_times(struct unit_times *u, double width, int y) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ bool b;
+
+ if (!u->name)
+ return 0;
+
+ svg_bar("activating", u->activating, u->activated, y);
+ svg_bar("active", u->activated, u->deactivating, y);
+ svg_bar("deactivating", u->deactivating, u->deactivated, y);
+
+ /* place the text on the left if we have passed the half of the svg width */
+ b = u->activating * SCALE_X < width / 2;
+ if (u->time)
+ svg_text(b, u->activating, y, "%s (%s)",
+ u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
+ else
+ svg_text(b, u->activating, y, "%s", u->name);
+
+ return 1;
+}
+
static int analyze_plot(int argc, char *argv[], void *userdata) {
_cleanup_(free_host_infop) struct host_info *host = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(unit_times_freep) struct unit_times *times = NULL;
+ _cleanup_free_ char *pretty_times = NULL;
+ bool use_full_bus = arg_scope == UNIT_FILE_SYSTEM;
struct boot_times *boot;
+ struct unit_times *u;
int n, m = 1, y = 0, r;
- bool use_full_bus = true;
double width;
- _cleanup_free_ char *pretty_times = NULL;
- struct unit_times *u;
r = acquire_bus(&bus, &use_full_bus);
if (r < 0)
@@ -608,7 +624,7 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
if (n < 0)
return n;
- if (use_full_bus) {
+ if (use_full_bus || arg_scope != UNIT_FILE_SYSTEM) {
n = acquire_host_info(bus, &host);
if (n < 0)
return n;
@@ -618,7 +634,7 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
if (n <= 0)
return n;
- qsort(times, n, sizeof(struct unit_times), compare_unit_start);
+ typesafe_qsort(times, n, compare_unit_start);
width = SCALE_X * (boot->firmware_time + boot->finish_time);
if (width < 800.0)
@@ -633,14 +649,13 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
}
if (boot->initrd_time > 0)
m++;
- if (boot->kernel_time > 0)
+ if (boot->kernel_done_time > 0)
m++;
for (u = times; u->has_data; u++) {
double text_start, text_width;
- if (u->activating < boot->userspace_time ||
- u->activating > boot->finish_time) {
+ if (u->activating > boot->finish_time) {
u->name = mfree(u->name);
continue;
}
@@ -653,12 +668,13 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
if (text_width > text_start && text_width + text_start > width)
width = text_width + text_start;
- if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
- && u->activated == 0 && u->deactivating == 0)
+ if (u->deactivated > u->activating &&
+ u->deactivated <= boot->finish_time &&
+ u->activated == 0 && u->deactivating == 0)
u->activated = u->deactivating = u->deactivated;
if (u->activated < u->activating || u->activated > boot->finish_time)
u->activated = boot->finish_time;
- if (u->deactivating < u->activated || u->activated > boot->finish_time)
+ if (u->deactivating < u->activated || u->deactivating > boot->finish_time)
u->deactivating = boot->finish_time;
if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
u->deactivated = boot->finish_time;
@@ -710,7 +726,7 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
- if (use_full_bus)
+ if (host)
svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
strempty(host->hostname),
@@ -733,43 +749,40 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
svg_text(true, -(double) boot->loader_time, y, "loader");
y++;
}
- if (boot->kernel_time > 0) {
+ if (boot->kernel_done_time > 0) {
svg_bar("kernel", 0, boot->kernel_done_time, y);
svg_text(true, 0, y, "kernel");
y++;
}
if (boot->initrd_time > 0) {
svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
+ if (boot->initrd_security_start_time < boot->initrd_security_finish_time)
+ svg_bar("security", boot->initrd_security_start_time, boot->initrd_security_finish_time, y);
+ if (boot->initrd_generators_start_time < boot->initrd_generators_finish_time)
+ svg_bar("generators", boot->initrd_generators_start_time, boot->initrd_generators_finish_time, y);
+ if (boot->initrd_unitsload_start_time < boot->initrd_unitsload_finish_time)
+ svg_bar("unitsload", boot->initrd_unitsload_start_time, boot->initrd_unitsload_finish_time, y);
svg_text(true, boot->initrd_time, y, "initrd");
y++;
}
+
+ for (u = times; u->has_data; u++) {
+ if (u->activating >= boot->userspace_time)
+ break;
+
+ y += plot_unit_times(u, width, y);
+ }
+
svg_bar("active", boot->userspace_time, boot->finish_time, y);
- svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
+ if (boot->security_start_time > 0)
+ svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
svg_text(true, boot->userspace_time, y, "systemd");
y++;
- for (u = times; u->has_data; u++) {
- char ts[FORMAT_TIMESPAN_MAX];
- bool b;
-
- if (!u->name)
- continue;
-
- svg_bar("activating", u->activating, u->activated, y);
- svg_bar("active", u->activated, u->deactivating, y);
- svg_bar("deactivating", u->deactivating, u->deactivated, y);
-
- /* place the text on the left if we have passed the half of the svg width */
- b = u->activating * SCALE_X < width / 2;
- if (u->time)
- svg_text(b, u->activating, y, "%s (%s)",
- u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
- else
- svg_text(b, u->activating, y, "%s", u->name);
- y++;
- }
+ for (; u->has_data; u++)
+ y += plot_unit_times(u, width, y);
svg("</g>\n");
@@ -785,9 +798,11 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
svg_bar("deactivating", 0, 300000, y);
svg_text(true, 400000, y, "Deactivating");
y++;
- svg_bar("security", 0, 300000, y);
- svg_text(true, 400000, y, "Setting up security module");
- y++;
+ if (boot->security_start_time > 0) {
+ svg_bar("security", 0, 300000, y);
+ svg_text(true, 400000, y, "Setting up security module");
+ y++;
+ }
svg_bar("generators", 0, 300000, y);
svg_text(true, 400000, y, "Generators");
y++;
@@ -802,15 +817,15 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
return 0;
}
-static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
+static int list_dependencies_print(const char *name, unsigned level, unsigned branches,
bool last, struct unit_times *times, struct boot_times *boot) {
- unsigned int i;
+ unsigned i;
char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
for (i = level; i != 0; i--)
- printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE));
+ printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
- printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH));
+ printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH));
if (times) {
if (times->time > 0)
@@ -844,8 +859,7 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha
static Hashmap *unit_times_hashmap;
-static int list_dependencies_compare(const void *_a, const void *_b) {
- const char **a = (const char**) _a, **b = (const char**) _b;
+static int list_dependencies_compare(char * const *a, char * const *b) {
usec_t usa = 0, usb = 0;
struct unit_times *times;
@@ -856,7 +870,7 @@ static int list_dependencies_compare(const void *_a, const void *_b) {
if (times)
usb = times->activated;
- return usb - usa;
+ return CMP(usb, usa);
}
static bool times_in_range(const struct unit_times *times, const struct boot_times *boot) {
@@ -864,8 +878,8 @@ static bool times_in_range(const struct unit_times *times, const struct boot_tim
times->activated > 0 && times->activated <= boot->finish_time;
}
-static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
- unsigned int branches) {
+static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units,
+ unsigned branches) {
_cleanup_strv_free_ char **deps = NULL;
char **c;
int r = 0;
@@ -881,7 +895,7 @@ static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int lev
if (r < 0)
return r;
- qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
+ typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
r = acquire_boot_times(bus, &boot);
if (r < 0)
@@ -890,11 +904,8 @@ static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int lev
STRV_FOREACH(c, deps) {
times = hashmap_get(unit_times_hashmap, *c);
if (times_in_range(times, boot) &&
- (times->activated >= service_longest
- || service_longest == 0)) {
+ times->activated >= service_longest)
service_longest = times->activated;
- break;
- }
}
if (service_longest == 0)
@@ -967,10 +978,8 @@ static int list_dependencies(sd_bus *bus, const char *name) {
&error,
&reply,
"s");
- if (r < 0) {
- log_error("Failed to get ID: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &id);
if (r < 0)
@@ -1021,7 +1030,7 @@ static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
}
unit_times_hashmap = h;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
puts("The time after the unit is active or started is printed after the \"@\" character.\n"
"The time the unit takes to start is printed after the \"+\" character.\n");
@@ -1051,9 +1060,9 @@ static int analyze_blame(int argc, char *argv[], void *userdata) {
if (n <= 0)
return n;
- qsort(times, n, sizeof(struct unit_times), compare_unit_time);
+ typesafe_qsort(times, n, compare_unit_time);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
for (u = times; u->has_data; u++) {
char ts[FORMAT_TIMESPAN_MAX];
@@ -1229,10 +1238,8 @@ static int dot(int argc, char *argv[], void *userdata) {
&error,
&reply,
"");
- if (r < 0) {
- log_error("Failed to list units: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
if (r < 0)
@@ -1303,7 +1310,7 @@ static int dump(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to create bus connection: %m");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD))
return dump_fallback(bus);
@@ -1334,15 +1341,16 @@ static int dump(int argc, char *argv[], void *userdata) {
}
static int cat_config(int argc, char *argv[], void *userdata) {
- char **arg;
+ char **arg, **list;
int r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
- STRV_FOREACH(arg, argv + 1) {
+ list = strv_skip(argv, 1);
+ STRV_FOREACH(arg, list) {
const char *t = NULL;
- if (arg != argv + 1)
+ if (arg != list)
print_separator();
if (path_is_absolute(*arg)) {
@@ -1354,10 +1362,10 @@ static int cat_config(int argc, char *argv[], void *userdata) {
break;
}
- if (!t) {
- log_error("Path %s does not start with any known prefix.", *arg);
- return -EINVAL;
- }
+ if (!t)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Path %s does not start with any known prefix.",
+ *arg);
} else
t = *arg;
@@ -1497,29 +1505,114 @@ static int dump_unit_paths(int argc, char *argv[], void *userdata) {
}
#if HAVE_SECCOMP
+
+static int load_kernel_syscalls(Set **ret) {
+ _cleanup_(set_free_freep) Set *syscalls = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ /* Let's read the available system calls from the list of available tracing events. Slightly dirty, but good
+ * enough for analysis purposes. */
+
+ f = fopen("/sys/kernel/debug/tracing/available_events", "re");
+ if (!f)
+ return log_full_errno(IN_SET(errno, EPERM, EACCES, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno, "Can't read open /sys/kernel/debug/tracing/available_events: %m");
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ const char *e;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read system call list: %m");
+ if (r == 0)
+ break;
+
+ e = startswith(line, "syscalls:sys_enter_");
+ if (!e)
+ continue;
+
+ /* These are named differently inside the kernel than their external name for historical reasons. Let's hide them here. */
+ if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl"))
+ continue;
+
+ r = set_ensure_allocated(&syscalls, &string_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ r = set_put_strdup(syscalls, e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add system call to list: %m");
+ }
+
+ *ret = TAKE_PTR(syscalls);
+ return 0;
+}
+
+static void kernel_syscalls_remove(Set *s, const SyscallFilterSet *set) {
+ const char *syscall;
+
+ NULSTR_FOREACH(syscall, set->value) {
+ if (syscall[0] == '@')
+ continue;
+
+ (void) set_remove(s, syscall);
+ }
+}
+
static void dump_syscall_filter(const SyscallFilterSet *set) {
const char *syscall;
- printf("%s\n", set->name);
- printf(" # %s\n", set->help);
+ printf("%s%s%s\n"
+ " # %s\n",
+ ansi_highlight(),
+ set->name,
+ ansi_normal(),
+ set->help);
+
NULSTR_FOREACH(syscall, set->value)
- printf(" %s\n", syscall);
+ printf(" %s%s%s\n",
+ syscall[0] == '@' ? ansi_underline() : "",
+ syscall,
+ ansi_normal());
}
static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
bool first = true;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (strv_isempty(strv_skip(argv, 1))) {
- int i;
+ _cleanup_(set_free_freep) Set *kernel = NULL;
+ int i, k;
+
+ k = load_kernel_syscalls(&kernel);
for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
+ const SyscallFilterSet *set = syscall_filter_sets + i;
if (!first)
puts("");
- dump_syscall_filter(syscall_filter_sets + i);
+
+ dump_syscall_filter(set);
+ kernel_syscalls_remove(kernel, set);
first = false;
}
+
+ if (k < 0) {
+ fputc('\n', stdout);
+ fflush(stdout);
+ log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
+ } else if (!set_isempty(kernel)) {
+ const char *syscall;
+ Iterator j;
+
+ printf("\n"
+ "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n",
+ ansi_highlight(), ansi_normal());
+
+ SET_FOREACH(syscall, kernel, j)
+ printf("# %s\n", syscall);
+ }
} else {
char **name;
@@ -1548,11 +1641,34 @@ static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
#else
static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
- log_error("Not compiled with syscall filters, sorry.");
- return -EOPNOTSUPP;
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Not compiled with syscall filters, sorry.");
}
#endif
+static int dump_timespan(int argc, char *argv[], void *userdata) {
+ char **input_timespan;
+
+ STRV_FOREACH(input_timespan, strv_skip(argv, 1)) {
+ int r;
+ usec_t usec_magnitude = 1, output_usecs;
+ char ft_buf[FORMAT_TIMESPAN_MAX];
+
+ r = parse_time(*input_timespan, &output_usecs, USEC_PER_SEC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse time span '%s': %m", *input_timespan);
+
+ printf("Original: %s\n", *input_timespan);
+ printf(" %ss: %" PRIu64 "\n", special_glyph(SPECIAL_GLYPH_MU), output_usecs);
+ printf(" Human: %s\n", format_timespan(ft_buf, sizeof(ft_buf), output_usecs, usec_magnitude));
+
+ if (input_timespan[1])
+ putchar('\n');
+ }
+
+ return EXIT_SUCCESS;
+}
+
static int test_calendar(int argc, char *argv[], void *userdata) {
int ret = 0, r;
char **p;
@@ -1669,9 +1785,28 @@ static int do_verify(int argc, char *argv[], void *userdata) {
return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
}
+static int do_security(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r;
+
+ r = acquire_bus(&bus, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
+
+ (void) pager_open(arg_pager_flags);
+
+ return analyze_security(bus, strv_skip(argv, 1), 0);
+}
+
static int help(int argc, char *argv[], void *userdata) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ (void) pager_open(arg_pager_flags);
- (void) pager_open(arg_no_pager, false);
+ r = terminal_urlify_man("systemd-analyze", "1", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Profile systemd, show unit dependencies, check unit files.\n\n"
@@ -1689,9 +1824,9 @@ static int help(int argc, char *argv[], void *userdata) {
" --to-pattern=GLOB Show only destinations in the graph\n"
" --fuzz=SECONDS Also print also services which finished SECONDS\n"
" earlier than the latest in the branch\n"
- " --man[=BOOL] Do [not] check for existence of man pages\n\n"
- " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
- "Commands:\n"
+ " --man[=BOOL] Do [not] check for existence of man pages\n"
+ " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n"
+ "\nCommands:\n"
" time Print time spent in the kernel\n"
" blame Print list of running units ordered by time to init\n"
" critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
@@ -1706,10 +1841,14 @@ static int help(int argc, char *argv[], void *userdata) {
" verify FILE... Check unit files for correctness\n"
" calendar SPEC... Validate repetitive calendar time events\n"
" service-watchdogs [BOOL] Get/set service watchdog state\n"
- , program_invocation_short_name);
-
- /* When updating this list, including descriptions, apply
- * changes to shell-completion/bash/systemd-analyze and
+ " timespan SPAN... Validate a time span\n"
+ " security [UNIT...] Analyze security of unit\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ /* When updating this list, including descriptions, apply changes to shell-completion/bash/systemd-analyze and
* shell-completion/zsh/_systemd-analyze too. */
return 0;
@@ -1809,7 +1948,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case 'H':
@@ -1825,10 +1964,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_MAN:
if (optarg) {
r = parse_boolean(optarg);
- if (r < 0) {
- log_error("Failed to parse --man= argument.");
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse --man= argument.");
arg_man = r;
} else
@@ -1839,10 +1977,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_GENERATORS:
if (optarg) {
r = parse_boolean(optarg);
- if (r < 0) {
- log_error("Failed to parse --generators= argument.");
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse --generators= argument.");
arg_generators = r;
} else
@@ -1858,20 +1995,18 @@ static int parse_argv(int argc, char *argv[]) {
}
if (arg_scope == UNIT_FILE_GLOBAL &&
- !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify")) {
- log_error("Option --global only makes sense with verbs dot, unit-paths, verify.");
- return -EINVAL;
- }
+ !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --global only makes sense with verbs dot, unit-paths, verify.");
- if (arg_root && !streq_ptr(argv[optind], "cat-config")) {
- log_error("Option --root is only supported for cat-config right now.");
- return -EINVAL;
- }
+ if (arg_root && !streq_ptr(argv[optind], "cat-config"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --root is only supported for cat-config right now.");
return 1; /* work to do */
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
@@ -1894,6 +2029,8 @@ int main(int argc, char *argv[]) {
{ "verify", 2, VERB_ANY, 0, do_verify },
{ "calendar", 2, VERB_ANY, 0, test_calendar },
{ "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
+ { "timespan", 2, VERB_ANY, 0, dump_timespan },
+ { "security", VERB_ANY, VERB_ANY, 0, do_security },
{}
};
@@ -1907,15 +2044,9 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
-
- r = dispatch_verb(argc, argv, verbs, NULL);
-
-finish:
- pager_close();
-
- strv_free(arg_dot_from_patterns);
- strv_free(arg_dot_to_patterns);
+ return r;
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return dispatch_verb(argc, argv, verbs, NULL);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/analyze/meson.build b/src/analyze/meson.build
index 3a69a259b1..4db4dfa552 100644
--- a/src/analyze/meson.build
+++ b/src/analyze/meson.build
@@ -4,4 +4,6 @@ systemd_analyze_sources = files('''
analyze.c
analyze-verify.c
analyze-verify.h
+ analyze-security.c
+ analyze-security.h
'''.split())
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
index 8e1d834ab4..4637c32819 100644
--- a/src/ask-password/ask-password.c
+++ b/src/ask-password/ask-password.c
@@ -9,6 +9,8 @@
#include "def.h"
#include "log.h"
#include "macro.h"
+#include "main-func.h"
+#include "pretty-print.h"
#include "strv.h"
static const char *arg_icon = NULL;
@@ -20,7 +22,16 @@ static bool arg_multiple = false;
static bool arg_no_output = false;
static AskPasswordFlags arg_flags = ASK_PASSWORD_PUSH_CACHE;
-static void help(void) {
+STATIC_DESTRUCTOR_REGISTER(arg_message, freep);
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-ask-password", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] MESSAGE\n\n"
"Query the user for a system passphrase, via the TTY or an UI agent.\n\n"
" -h --help Show this help\n"
@@ -33,7 +44,12 @@ static void help(void) {
" --accept-cached Accept cached passwords\n"
" --multiple List multiple passwords if available\n"
" --no-output Do not print password to standard output\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -48,10 +64,12 @@ static int parse_argv(int argc, char *argv[]) {
ARG_ID,
ARG_KEYNAME,
ARG_NO_OUTPUT,
+ ARG_VERSION,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
{ "icon", required_argument, NULL, ARG_ICON },
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
{ "echo", no_argument, NULL, ARG_ECHO },
@@ -74,18 +92,20 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
+
+ case ARG_VERSION:
+ return version();
case ARG_ICON:
arg_icon = optarg;
break;
case ARG_TIMEOUT:
- if (parse_sec(optarg, &arg_timeout) < 0) {
- log_error("Failed to parse --timeout parameter %s", optarg);
- return -EINVAL;
- }
+ if (parse_sec(optarg, &arg_timeout) < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse --timeout parameter %s",
+ optarg);
break;
case ARG_ECHO:
@@ -132,7 +152,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_strv_free_erase_ char **l = NULL;
usec_t timeout;
char **p;
@@ -143,7 +163,7 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
if (arg_timeout > 0)
timeout = now(CLOCK_MONOTONIC) + arg_timeout;
@@ -151,10 +171,8 @@ int main(int argc, char *argv[]) {
timeout = 0;
r = ask_password_auto(arg_message, arg_icon, arg_id, arg_keyname, timeout, arg_flags, &l);
- if (r < 0) {
- log_error_errno(r, "Failed to query password: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to query password: %m");
STRV_FOREACH(p, l) {
if (!arg_no_output)
@@ -164,8 +182,7 @@ int main(int argc, char *argv[]) {
break;
}
-finish:
- free(arg_message);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c
index 2b1a1683e2..780ad56eb1 100644
--- a/src/backlight/backlight.c
+++ b/src/backlight/backlight.c
@@ -1,97 +1,111 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include "libudev.h"
+#include "sd-device.h"
#include "alloc-util.h"
-#include "def.h"
+#include "device-util.h"
#include "escape.h"
#include "fileio.h"
+#include "main-func.h"
#include "mkdir.h"
#include "parse-util.h"
#include "proc-cmdline.h"
#include "string-util.h"
-#include "udev-util.h"
+#include "strv.h"
#include "util.h"
-static struct udev_device *find_pci_or_platform_parent(struct udev_device *device) {
- struct udev_device *parent;
- const char *subsystem, *sysname;
+static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
+ const char *subsystem, *sysname, *value;
+ sd_device *parent;
+ int r;
assert(device);
+ assert(ret);
- parent = udev_device_get_parent(device);
- if (!parent)
- return NULL;
+ r = sd_device_get_parent(device, &parent);
+ if (r < 0)
+ return r;
- subsystem = udev_device_get_subsystem(parent);
- if (!subsystem)
- return NULL;
+ r = sd_device_get_subsystem(parent, &subsystem);
+ if (r < 0)
+ return r;
- sysname = udev_device_get_sysname(parent);
- if (!sysname)
- return NULL;
+ r = sd_device_get_sysname(parent, &sysname);
+ if (r < 0)
+ return r;
if (streq(subsystem, "drm")) {
const char *c;
c = startswith(sysname, "card");
if (!c)
- return NULL;
+ return -ENODATA;
c += strspn(c, DIGITS);
if (*c == '-') {
/* A connector DRM device, let's ignore all but LVDS and eDP! */
-
- if (!startswith(c, "-LVDS-") &&
- !startswith(c, "-Embedded DisplayPort-"))
- return NULL;
+ if (!STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-"))
+ return -EOPNOTSUPP;
}
- } else if (streq(subsystem, "pci")) {
- const char *value;
-
- value = udev_device_get_sysattr_value(parent, "class");
- if (value) {
- unsigned long class = 0;
+ } else if (streq(subsystem, "pci") &&
+ sd_device_get_sysattr_value(parent, "class", &value) >= 0) {
+ unsigned long class = 0;
- if (safe_atolu(value, &class) < 0) {
- log_warning("Cannot parse PCI class %s of device %s:%s.",
- value, subsystem, sysname);
- return NULL;
- }
+ r = safe_atolu(value, &class);
+ if (r < 0)
+ return log_warning_errno(r, "Cannot parse PCI class '%s' of device %s:%s: %m",
+ value, subsystem, sysname);
- /* Graphics card */
- if (class == 0x30000)
- return parent;
+ /* Graphics card */
+ if (class == 0x30000) {
+ *ret = parent;
+ return 0;
}
- } else if (streq(subsystem, "platform"))
- return parent;
+ } else if (streq(subsystem, "platform")) {
+ *ret = parent;
+ return 0;
+ }
- return find_pci_or_platform_parent(parent);
+ return find_pci_or_platform_parent(parent, ret);
}
-static bool same_device(struct udev_device *a, struct udev_device *b) {
+static int same_device(sd_device *a, sd_device *b) {
+ const char *a_val, *b_val;
+ int r;
+
assert(a);
assert(b);
- if (!streq_ptr(udev_device_get_subsystem(a), udev_device_get_subsystem(b)))
- return false;
+ r = sd_device_get_subsystem(a, &a_val);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_subsystem(b, &b_val);
+ if (r < 0)
+ return r;
- if (!streq_ptr(udev_device_get_sysname(a), udev_device_get_sysname(b)))
+ if (!streq(a_val, b_val))
return false;
- return true;
+ r = sd_device_get_sysname(a, &a_val);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_sysname(b, &b_val);
+ if (r < 0)
+ return r;
+
+ return streq(a_val, b_val);
}
-static bool validate_device(struct udev *udev, struct udev_device *device) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *enumerate = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
- struct udev_device *parent;
+static int validate_device(sd_device *device) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerate = NULL;
const char *v, *subsystem;
+ sd_device *parent, *other;
int r;
- assert(udev);
assert(device);
/* Verify whether we should actually care for a specific
@@ -108,76 +122,81 @@ static bool validate_device(struct udev *udev, struct udev_device *device) {
* that we use "raw" only if no "firmware" or "platform"
* device for the same device exists. */
- subsystem = udev_device_get_subsystem(device);
- if (!streq_ptr(subsystem, "backlight"))
+ r = sd_device_get_subsystem(device, &subsystem);
+ if (r < 0)
+ return r;
+ if (!streq(subsystem, "backlight"))
return true;
- v = udev_device_get_sysattr_value(device, "type");
- if (!streq_ptr(v, "raw"))
+ r = sd_device_get_sysattr_value(device, "type", &v);
+ if (r < 0)
+ return r;
+ if (!streq(v, "raw"))
return true;
- parent = find_pci_or_platform_parent(device);
- if (!parent)
- return true;
+ r = find_pci_or_platform_parent(device, &parent);
+ if (r < 0)
+ return r;
- subsystem = udev_device_get_subsystem(parent);
- if (!subsystem)
- return true;
+ r = sd_device_get_subsystem(parent, &subsystem);
+ if (r < 0)
+ return r;
- enumerate = udev_enumerate_new(udev);
- if (!enumerate)
- return true;
+ r = sd_device_enumerator_new(&enumerate);
+ if (r < 0)
+ return r;
- r = udev_enumerate_add_match_subsystem(enumerate, "backlight");
+ r = sd_device_enumerator_allow_uninitialized(enumerate);
if (r < 0)
- return true;
+ return r;
- r = udev_enumerate_scan_devices(enumerate);
+ r = sd_device_enumerator_add_match_subsystem(enumerate, "backlight", true);
if (r < 0)
- return true;
+ return r;
- first = udev_enumerate_get_list_entry(enumerate);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *other;
- struct udev_device *other_parent;
+ FOREACH_DEVICE(enumerate, other) {
const char *other_subsystem;
+ sd_device *other_parent;
- other = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!other)
- return true;
-
- if (same_device(device, other))
+ if (same_device(device, other) > 0)
continue;
- v = udev_device_get_sysattr_value(other, "type");
- if (!STRPTR_IN_SET(v, "platform", "firmware"))
+ if (sd_device_get_sysattr_value(other, "type", &v) < 0 ||
+ !STR_IN_SET(v, "platform", "firmware"))
continue;
/* OK, so there's another backlight device, and it's a
* platform or firmware device, so, let's see if we
- * can verify it belongs to the same device as
- * ours. */
- other_parent = find_pci_or_platform_parent(other);
- if (!other_parent)
+ * can verify it belongs to the same device as ours. */
+ if (find_pci_or_platform_parent(other, &other_parent) < 0)
continue;
if (same_device(parent, other_parent)) {
- /* Both have the same PCI parent, that means
- * we are out. */
+ const char *device_sysname = NULL, *other_sysname = NULL;
+
+ /* Both have the same PCI parent, that means we are out. */
+
+ (void) sd_device_get_sysname(device, &device_sysname);
+ (void) sd_device_get_sysname(other, &other_sysname);
+
log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.",
- udev_device_get_sysname(device),
- udev_device_get_sysname(other));
+ device_sysname, other_sysname);
return false;
}
- other_subsystem = udev_device_get_subsystem(other_parent);
- if (streq_ptr(other_subsystem, "platform") && streq_ptr(subsystem, "pci")) {
- /* The other is connected to the platform bus
- * and we are a PCI device, that also means we
- * are out. */
+ if (sd_device_get_subsystem(other_parent, &other_subsystem) < 0)
+ continue;
+
+ if (streq(other_subsystem, "platform") && streq(subsystem, "pci")) {
+ const char *device_sysname = NULL, *other_sysname = NULL;
+
+ /* The other is connected to the platform bus and we are a PCI device, that also means we are out. */
+
+ (void) sd_device_get_sysname(device, &device_sysname);
+ (void) sd_device_get_sysname(other, &other_sysname);
+
log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.",
- udev_device_get_sysname(device),
- udev_device_get_sysname(other));
+ device_sysname, other_sysname);
return false;
}
}
@@ -185,29 +204,29 @@ static bool validate_device(struct udev *udev, struct udev_device *device) {
return true;
}
-static unsigned get_max_brightness(struct udev_device *device) {
- int r;
+static int get_max_brightness(sd_device *device, unsigned *ret) {
const char *max_brightness_str;
unsigned max_brightness;
+ int r;
- max_brightness_str = udev_device_get_sysattr_value(device, "max_brightness");
- if (!max_brightness_str) {
- log_warning("Failed to read 'max_brightness' attribute.");
- return 0;
- }
+ assert(device);
+ assert(ret);
+
+ r = sd_device_get_sysattr_value(device, "max_brightness", &max_brightness_str);
+ if (r < 0)
+ return log_device_warning_errno(device, r, "Failed to read 'max_brightness' attribute: %m");
r = safe_atou(max_brightness_str, &max_brightness);
- if (r < 0) {
- log_warning_errno(r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str);
- return 0;
- }
+ if (r < 0)
+ return log_device_warning_errno(device, r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str);
if (max_brightness <= 0) {
- log_warning("Maximum brightness is 0, ignoring device.");
- return 0;
+ log_device_warning(device, "Maximum brightness is 0, ignoring device.");
+ return -EINVAL;
}
- return max_brightness;
+ *ret = max_brightness;
+ return 0;
}
/* Some systems turn the backlight all the way off at the lowest levels.
@@ -215,64 +234,69 @@ static unsigned get_max_brightness(struct udev_device *device) {
* max_brightness in case of 'backlight' subsystem. This avoids preserving
* an unreadably dim screen, which would otherwise force the user to
* disable state restoration. */
-static void clamp_brightness(struct udev_device *device, char **value, unsigned max_brightness) {
+static int clamp_brightness(sd_device *device, char **value, unsigned max_brightness) {
unsigned brightness, new_brightness, min_brightness;
const char *subsystem;
int r;
+ assert(value);
+ assert(*value);
+
r = safe_atou(*value, &brightness);
- if (r < 0) {
- log_warning_errno(r, "Failed to parse brightness \"%s\": %m", *value);
- return;
- }
+ if (r < 0)
+ return log_device_warning_errno(device, r, "Failed to parse brightness \"%s\": %m", *value);
+
+ r = sd_device_get_subsystem(device, &subsystem);
+ if (r < 0)
+ return log_device_warning_errno(device, r, "Failed to get device subsystem: %m");
- subsystem = udev_device_get_subsystem(device);
- if (streq_ptr(subsystem, "backlight"))
+ if (streq(subsystem, "backlight"))
min_brightness = MAX(1U, max_brightness/20);
else
min_brightness = 0;
new_brightness = CLAMP(brightness, min_brightness, max_brightness);
if (new_brightness != brightness) {
- char *old_value = *value;
+ char *new_value;
- r = asprintf(value, "%u", new_brightness);
- if (r < 0) {
- log_oom();
- return;
- }
+ r = asprintf(&new_value, "%u", new_brightness);
+ if (r < 0)
+ return log_oom();
- log_info("Saved brightness %s %s to %s.", old_value,
- new_brightness > brightness ?
- "too low; increasing" : "too high; decreasing",
- *value);
+ log_device_info(device, "Saved brightness %s %s to %s.", *value,
+ new_brightness > brightness ?
+ "too low; increasing" : "too high; decreasing",
+ new_value);
- free(old_value);
+ free_and_replace(*value, new_value);
}
+
+ return 0;
}
-static bool shall_clamp(struct udev_device *d) {
+static bool shall_clamp(sd_device *d) {
const char *s;
int r;
assert(d);
- s = udev_device_get_property_value(d, "ID_BACKLIGHT_CLAMP");
- if (!s)
+ r = sd_device_get_property_value(d, "ID_BACKLIGHT_CLAMP", &s);
+ if (r < 0) {
+ log_device_debug_errno(d, r, "Failed to get ID_BACKLIGHT_CLAMP property, ignoring: %m");
return true;
+ }
r = parse_boolean(s);
if (r < 0) {
- log_debug_errno(r, "Failed to parse ID_BACKLIGHT_CLAMP property, ignoring: %m");
+ log_device_debug_errno(d, r, "Failed to parse ID_BACKLIGHT_CLAMP property, ignoring: %m");
return true;
}
return r;
}
-int main(int argc, char *argv[]) {
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
- _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
+static int run(int argc, char *argv[]) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
_cleanup_free_ char *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL;
const char *sysname, *path_id, *ss, *saved;
unsigned max_brightness;
@@ -280,31 +304,21 @@ int main(int argc, char *argv[]) {
if (argc != 3) {
log_error("This program requires two arguments.");
- return EXIT_FAILURE;
+ return -EINVAL;
}
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
r = mkdir_p("/var/lib/systemd/backlight", 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m");
- return EXIT_FAILURE;
- }
-
- udev = udev_new();
- if (!udev) {
- log_oom();
- return EXIT_FAILURE;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m");
sysname = strchr(argv[2], ':');
if (!sysname) {
log_error("Requires a subsystem and sysname pair specifying a backlight device.");
- return EXIT_FAILURE;
+ return -EINVAL;
}
ss = strndupa(argv[2], sysname - argv[2]);
@@ -313,47 +327,31 @@ int main(int argc, char *argv[]) {
if (!STR_IN_SET(ss, "backlight", "leds")) {
log_error("Not a backlight or LED device: '%s:%s'", ss, sysname);
- return EXIT_FAILURE;
+ return -EINVAL;
}
- errno = 0;
- device = udev_device_new_from_subsystem_sysname(udev, ss, sysname);
- if (!device) {
- if (errno > 0)
- log_error_errno(errno, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname);
- else
- log_oom();
-
- return EXIT_FAILURE;
- }
+ r = sd_device_new_from_subsystem_sysname(&device, ss, sysname);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname);
/* If max_brightness is 0, then there is no actual backlight
* device. This happens on desktops with Asus mainboards
- * that load the eeepc-wmi module.
- */
- max_brightness = get_max_brightness(device);
- if (max_brightness == 0)
- return EXIT_SUCCESS;
+ * that load the eeepc-wmi module. */
+ if (get_max_brightness(device, &max_brightness) < 0)
+ return 0;
escaped_ss = cescape(ss);
- if (!escaped_ss) {
- log_oom();
- return EXIT_FAILURE;
- }
+ if (!escaped_ss)
+ return log_oom();
escaped_sysname = cescape(sysname);
- if (!escaped_sysname) {
- log_oom();
- return EXIT_FAILURE;
- }
+ if (!escaped_sysname)
+ return log_oom();
- path_id = udev_device_get_property_value(device, "ID_PATH");
- if (path_id) {
+ if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
escaped_path_id = cescape(path_id);
- if (!escaped_path_id) {
- log_oom();
- return EXIT_FAILURE;
- }
+ if (!escaped_path_id)
+ return log_oom();
saved = strjoina("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname);
} else
@@ -373,10 +371,10 @@ int main(int argc, char *argv[]) {
bool clamp;
if (shall_restore_state() == 0)
- return EXIT_SUCCESS;
+ return 0;
- if (!validate_device(udev, device))
- return EXIT_SUCCESS;
+ if (validate_device(device) == 0)
+ return 0;
clamp = shall_clamp(device);
@@ -387,57 +385,47 @@ int main(int argc, char *argv[]) {
/* Fallback to clamping current brightness or exit early if
* clamping is not supported/enabled. */
if (!clamp)
- return EXIT_SUCCESS;
+ return 0;
- curval = udev_device_get_sysattr_value(device, "brightness");
- if (!curval) {
- log_warning("Failed to read 'brightness' attribute.");
- return EXIT_FAILURE;
- }
+ r = sd_device_get_sysattr_value(device, "brightness", &curval);
+ if (r < 0)
+ return log_device_warning_errno(device, r, "Failed to read 'brightness' attribute: %m");
value = strdup(curval);
- if (!value) {
- log_oom();
- return EXIT_FAILURE;
- }
- } else if (r < 0) {
- log_error_errno(r, "Failed to read %s: %m", saved);
- return EXIT_FAILURE;
- }
+ if (!value)
+ return log_oom();
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to read %s: %m", saved);
if (clamp)
- clamp_brightness(device, &value, max_brightness);
+ (void) clamp_brightness(device, &value, max_brightness);
- r = udev_device_set_sysattr_value(device, "brightness", value);
- if (r < 0) {
- log_error_errno(r, "Failed to write system 'brightness' attribute: %m");
- return EXIT_FAILURE;
- }
+ r = sd_device_set_sysattr_value(device, "brightness", value);
+ if (r < 0)
+ return log_device_error_errno(device, r, "Failed to write system 'brightness' attribute: %m");
} else if (streq(argv[1], "save")) {
const char *value;
- if (!validate_device(udev, device)) {
+ if (validate_device(device) == 0) {
unlink(saved);
- return EXIT_SUCCESS;
+ return 0;
}
- value = udev_device_get_sysattr_value(device, "brightness");
- if (!value) {
- log_error("Failed to read system 'brightness' attribute");
- return EXIT_FAILURE;
- }
+ r = sd_device_get_sysattr_value(device, "brightness", &value);
+ if (r < 0)
+ return log_device_error_errno(device, r, "Failed to read system 'brightness' attribute: %m");
r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE);
- if (r < 0) {
- log_error_errno(r, "Failed to write %s: %m", saved);
- return EXIT_FAILURE;
- }
+ if (r < 0)
+ return log_device_error_errno(device, r, "Failed to write %s: %m", saved);
} else {
log_error("Unknown verb %s.", argv[1]);
- return EXIT_FAILURE;
+ return -EINVAL;
}
- return EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/basic/MurmurHash2.h b/src/basic/MurmurHash2.h
index 6104b4fbe6..1aef3afba0 100644
--- a/src/basic/MurmurHash2.h
+++ b/src/basic/MurmurHash2.h
@@ -2,8 +2,7 @@
// MurmurHash2 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
-#ifndef _MURMURHASH2_H_
-#define _MURMURHASH2_H_
+#pragma once
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
@@ -29,5 +28,3 @@ typedef unsigned __int64 uint64_t;
uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed );
//-----------------------------------------------------------------------------
-
-#endif // _MURMURHASH2_H_
diff --git a/src/basic/af-list.c b/src/basic/af-list.c
index 866a1d4317..abad221d58 100644
--- a/src/basic/af-list.c
+++ b/src/basic/af-list.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <errno.h>
#include <string.h>
#include <sys/socket.h>
@@ -16,7 +17,7 @@ const char *af_to_name(int id) {
if (id <= 0)
return NULL;
- if (id >= (int) ELEMENTSOF(af_names))
+ if ((size_t) id >= ELEMENTSOF(af_names))
return NULL;
return af_names[id];
@@ -29,7 +30,7 @@ int af_from_name(const char *name) {
sc = lookup_af(name, strlen(name));
if (!sc)
- return AF_UNSPEC;
+ return -EINVAL;
return sc->id;
}
diff --git a/src/basic/alloc-util.c b/src/basic/alloc-util.c
index 405445eac1..ab7a42c4e2 100644
--- a/src/basic/alloc-util.c
+++ b/src/basic/alloc-util.c
@@ -12,7 +12,7 @@ void* memdup(const void *p, size_t l) {
assert(l == 0 || p);
- ret = malloc(l);
+ ret = malloc(l ?: 1);
if (!ret)
return NULL;
diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h
index ebe42889ea..ff7a46793a 100644
--- a/src/basic/alloc-util.h
+++ b/src/basic/alloc-util.h
@@ -8,9 +8,11 @@
#include "macro.h"
+typedef void (*free_func_t)(void *p);
+
#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
-#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
#define newa(t, n) \
({ \
@@ -46,6 +48,21 @@ static inline void *mfree(void *memory) {
void* memdup(const void *p, size_t l) _alloc_(2);
void* memdup_suffix0(const void *p, size_t l) _alloc_(2);
+#define memdupa(p, l) \
+ ({ \
+ void *_q_; \
+ _q_ = alloca(l); \
+ memcpy(_q_, p, l); \
+ })
+
+#define memdupa_suffix0(p, l) \
+ ({ \
+ void *_q_; \
+ _q_ = alloca(l + 1); \
+ ((uint8_t*) _q_)[l] = 0; \
+ memcpy(_q_, p, l); \
+ })
+
static inline void freep(void *p) {
free(*(void**) p);
}
@@ -60,7 +77,7 @@ _malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t
if (size_multiply_overflow(size, need))
return NULL;
- return malloc(size * need);
+ return malloc(size * need ?: 1);
}
#if !HAVE_REALLOCARRAY
@@ -68,7 +85,7 @@ _alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size
if (size_multiply_overflow(size, need))
return NULL;
- return realloc(p, size * need);
+ return realloc(p, size * need ?: 1);
}
#endif
diff --git a/src/basic/arphrd-list.c b/src/basic/arphrd-list.c
index a7ae4b8162..b6e2486b67 100644
--- a/src/basic/arphrd-list.c
+++ b/src/basic/arphrd-list.c
@@ -1,10 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <errno.h>
#include <net/if_arp.h>
#include <string.h>
#include "arphrd-list.h"
#include "macro.h"
+#include "missing_network.h"
static const struct arphrd_name* lookup_arphrd(register const char *str, register GPERF_LEN_TYPE len);
@@ -16,7 +18,7 @@ const char *arphrd_to_name(int id) {
if (id <= 0)
return NULL;
- if (id >= (int) ELEMENTSOF(arphrd_names))
+ if ((size_t) id >= ELEMENTSOF(arphrd_names))
return NULL;
return arphrd_names[id];
@@ -29,7 +31,7 @@ int arphrd_from_name(const char *name) {
sc = lookup_arphrd(name, strlen(name));
if (!sc)
- return 0;
+ return -EINVAL;
return sc->id;
}
diff --git a/src/basic/async.c b/src/basic/async.c
index 1c4b575b05..c45ca01847 100644
--- a/src/basic/async.c
+++ b/src/basic/async.c
@@ -32,10 +32,7 @@ int asynchronous_job(void* (*func)(void *p), void *arg) {
goto finish;
}
- if (sigfillset(&ss) < 0) {
- r = -errno;
- goto finish;
- }
+ assert_se(sigfillset(&ss) >= 0);
/* Block all signals before forking off the thread, so that the new thread is started with all signals
* blocked. This way the existence of the new thread won't affect signal handling in other threads. */
diff --git a/src/basic/blockdev-util.c b/src/basic/blockdev-util.c
index 42b311eccd..3017ecd55d 100644
--- a/src/basic/blockdev-util.c
+++ b/src/basic/blockdev-util.c
@@ -10,12 +10,13 @@
#include "fd-util.h"
#include "fileio.h"
#include "missing.h"
+#include "parse-util.h"
#include "stat-util.h"
int block_get_whole_disk(dev_t d, dev_t *ret) {
char p[SYS_BLOCK_PATH_MAX("/partition")];
_cleanup_free_ char *s = NULL;
- unsigned n, m;
+ dev_t devt;
int r;
assert(ret);
@@ -38,16 +39,16 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
if (r < 0)
return r;
- r = sscanf(s, "%u:%u", &m, &n);
- if (r != 2)
- return -EINVAL;
+ r = parse_dev(s, &devt);
+ if (r < 0)
+ return r;
/* Only return this if it is really good enough for us. */
- xsprintf_sys_block_path(p, "/queue", makedev(m, n));
+ xsprintf_sys_block_path(p, "/queue", devt);
if (access(p, F_OK) < 0)
return -ENOENT;
- *ret = makedev(m, n);
+ *ret = devt;
return 0;
}
@@ -85,8 +86,8 @@ int block_get_originating(dev_t dt, dev_t *ret) {
_cleanup_free_ char *t = NULL;
char p[SYS_BLOCK_PATH_MAX("/slaves")];
struct dirent *de, *found = NULL;
- unsigned maj, min;
const char *q;
+ dev_t devt;
int r;
/* For the specified block device tries to chase it through the layers, in case LUKS-style DM stacking is used,
@@ -148,13 +149,14 @@ int block_get_originating(dev_t dt, dev_t *ret) {
if (r < 0)
return r;
- if (sscanf(t, "%u:%u", &maj, &min) != 2)
+ r = parse_dev(t, &devt);
+ if (r < 0)
return -EINVAL;
- if (maj == 0)
+ if (major(devt) == 0)
return -ENOENT;
- *ret = makedev(maj, min);
+ *ret = devt;
return 1;
}
diff --git a/src/basic/btrfs-ctree.h b/src/basic/btrfs-ctree.h
deleted file mode 100644
index c5a4244e37..0000000000
--- a/src/basic/btrfs-ctree.h
+++ /dev/null
@@ -1,97 +0,0 @@
-#pragma once
-
-#include "macro.h"
-#include "missing.h"
-#include "sparse-endian.h"
-
-/* Stolen from btrfs' ctree.h */
-
-struct btrfs_timespec {
- le64_t sec;
- le32_t nsec;
-} _packed_;
-
-struct btrfs_disk_key {
- le64_t objectid;
- uint8_t type;
- le64_t offset;
-} _packed_;
-
-struct btrfs_inode_item {
- le64_t generation;
- le64_t transid;
- le64_t size;
- le64_t nbytes;
- le64_t block_group;
- le32_t nlink;
- le32_t uid;
- le32_t gid;
- le32_t mode;
- le64_t rdev;
- le64_t flags;
- le64_t sequence;
- le64_t reserved[4];
- struct btrfs_timespec atime;
- struct btrfs_timespec ctime;
- struct btrfs_timespec mtime;
- struct btrfs_timespec otime;
-} _packed_;
-
-struct btrfs_root_item {
- struct btrfs_inode_item inode;
- le64_t generation;
- le64_t root_dirid;
- le64_t bytenr;
- le64_t byte_limit;
- le64_t bytes_used;
- le64_t last_snapshot;
- le64_t flags;
- le32_t refs;
- struct btrfs_disk_key drop_progress;
- uint8_t drop_level;
- uint8_t level;
- le64_t generation_v2;
- uint8_t uuid[BTRFS_UUID_SIZE];
- uint8_t parent_uuid[BTRFS_UUID_SIZE];
- uint8_t received_uuid[BTRFS_UUID_SIZE];
- le64_t ctransid;
- le64_t otransid;
- le64_t stransid;
- le64_t rtransid;
- struct btrfs_timespec ctime;
- struct btrfs_timespec otime;
- struct btrfs_timespec stime;
- struct btrfs_timespec rtime;
- le64_t reserved[8];
-} _packed_;
-
-#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
-
-struct btrfs_qgroup_info_item {
- le64_t generation;
- le64_t rfer;
- le64_t rfer_cmpr;
- le64_t excl;
- le64_t excl_cmpr;
-} _packed_;
-
-#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0)
-#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1)
-#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2)
-#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3)
-#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4)
-#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5)
-
-struct btrfs_qgroup_limit_item {
- le64_t flags;
- le64_t max_rfer;
- le64_t max_excl;
- le64_t rsv_rfer;
- le64_t rsv_excl;
-} _packed_;
-
-struct btrfs_root_ref {
- le64_t dirid;
- le64_t sequence;
- le16_t name_len;
-} _packed_;
diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
index 6d2490f3d7..d08e7546d0 100644
--- a/src/basic/btrfs-util.c
+++ b/src/basic/btrfs-util.c
@@ -15,19 +15,15 @@
#include <sys/sysmacros.h>
#include <unistd.h>
-#if HAVE_LINUX_BTRFS_H
-#include <linux/btrfs.h>
-#endif
-
#include "alloc-util.h"
#include "blockdev-util.h"
-#include "btrfs-ctree.h"
#include "btrfs-util.h"
#include "chattr-util.h"
#include "copy.h"
#include "device-nodes.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "io-util.h"
#include "macro.h"
#include "missing.h"
@@ -59,23 +55,6 @@ static int validate_subvolume_name(const char *name) {
return 0;
}
-static int open_parent(const char *path, int flags) {
- _cleanup_free_ char *parent = NULL;
- int fd;
-
- assert(path);
-
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
-
- fd = open(parent, flags);
- if (fd < 0)
- return -errno;
-
- return fd;
-}
-
static int extract_subvolume_name(const char *path, const char **subvolume) {
const char *fn;
int r;
@@ -132,21 +111,30 @@ int btrfs_is_subvol(const char *path) {
return btrfs_is_subvol_fd(fd);
}
-int btrfs_subvol_make(const char *path) {
+int btrfs_subvol_make_fd(int fd, const char *subvolume) {
struct btrfs_ioctl_vol_args args = {};
- _cleanup_close_ int fd = -1;
- const char *subvolume;
+ _cleanup_close_ int real_fd = -1;
int r;
- assert(path);
+ assert(subvolume);
- r = extract_subvolume_name(path, &subvolume);
+ r = validate_subvolume_name(subvolume);
if (r < 0)
return r;
- fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return fd;
+ r = fcntl(fd, F_GETFL);
+ if (r < 0)
+ return -errno;
+ if (FLAGS_SET(r, O_PATH)) {
+ /* An O_PATH fd was specified, let's convert here to a proper one, as btrfs ioctl's can't deal with
+ * O_PATH. */
+
+ real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (real_fd < 0)
+ return real_fd;
+
+ fd = real_fd;
+ }
strncpy(args.name, subvolume, sizeof(args.name)-1);
@@ -156,6 +144,24 @@ int btrfs_subvol_make(const char *path) {
return 0;
}
+int btrfs_subvol_make(const char *path) {
+ _cleanup_close_ int fd = -1;
+ const char *subvolume;
+ int r;
+
+ assert(path);
+
+ r = extract_subvolume_name(path, &subvolume);
+ if (r < 0)
+ return r;
+
+ fd = open_parent(path, O_CLOEXEC, 0);
+ if (fd < 0)
+ return fd;
+
+ return btrfs_subvol_make_fd(fd, subvolume);
+}
+
int btrfs_subvol_set_read_only_fd(int fd, bool b) {
uint64_t flags, nflags;
struct stat st;
@@ -393,26 +399,21 @@ static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, co
}
static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) {
+ int r;
+
assert(args);
/* Compare min and max */
- if (args->key.min_objectid < args->key.max_objectid)
- return -1;
- if (args->key.min_objectid > args->key.max_objectid)
- return 1;
-
- if (args->key.min_type < args->key.max_type)
- return -1;
- if (args->key.min_type > args->key.max_type)
- return 1;
+ r = CMP(args->key.min_objectid, args->key.max_objectid);
+ if (r != 0)
+ return r;
- if (args->key.min_offset < args->key.max_offset)
- return -1;
- if (args->key.min_offset > args->key.max_offset)
- return 1;
+ r = CMP(args->key.min_type, args->key.max_type);
+ if (r != 0)
+ return r;
- return 0;
+ return CMP(args->key.min_offset, args->key.max_offset);
}
#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \
@@ -864,96 +865,6 @@ int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, u
return btrfs_subvol_set_subtree_quota_limit_fd(fd, subvol_id, referenced_max);
}
-int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) {
- struct btrfs_ioctl_vol_args args = {};
- char p[SYS_BLOCK_PATH_MAX("/loop/backing_file")];
- _cleanup_free_ char *backing = NULL;
- _cleanup_close_ int loop_fd = -1, backing_fd = -1;
- struct stat st;
- dev_t dev = 0;
- int r;
-
- /* In contrast to btrfs quota ioctls ftruncate() cannot make sense of "infinity" or file sizes > 2^31 */
- if (!FILE_SIZE_VALID(new_size))
- return -EINVAL;
-
- /* btrfs cannot handle file systems < 16M, hence use this as minimum */
- if (new_size < 16*1024*1024)
- new_size = 16*1024*1024;
-
- r = btrfs_get_block_device_fd(fd, &dev);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENODEV;
-
- xsprintf_sys_block_path(p, "/loop/backing_file", dev);
- r = read_one_line_file(p, &backing);
- if (r == -ENOENT)
- return -ENODEV;
- if (r < 0)
- return r;
- if (isempty(backing) || !path_is_absolute(backing))
- return -ENODEV;
-
- backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY);
- if (backing_fd < 0)
- return -errno;
-
- if (fstat(backing_fd, &st) < 0)
- return -errno;
- if (!S_ISREG(st.st_mode))
- return -ENODEV;
-
- if (new_size == (uint64_t) st.st_size)
- return 0;
-
- if (grow_only && new_size < (uint64_t) st.st_size)
- return -EINVAL;
-
- xsprintf_sys_block_path(p, NULL, dev);
- loop_fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY);
- if (loop_fd < 0)
- return -errno;
-
- if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name))
- return -EINVAL;
-
- if (new_size < (uint64_t) st.st_size) {
- /* Decrease size: first decrease btrfs size, then shorten loopback */
- if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
- return -errno;
- }
-
- if (ftruncate(backing_fd, new_size) < 0)
- return -errno;
-
- if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0)
- return -errno;
-
- if (new_size > (uint64_t) st.st_size) {
- /* Increase size: first enlarge loopback, then increase btrfs size */
- if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
- return -errno;
- }
-
- /* Make sure the free disk space is correctly updated for both file systems */
- (void) fsync(fd);
- (void) fsync(backing_fd);
-
- return 1;
-}
-
-int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) {
- _cleanup_close_ int fd = -1;
-
- fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- return btrfs_resize_loopback_fd(fd, new_size, grow_only);
-}
-
int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret) {
assert(ret);
@@ -1172,6 +1083,18 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
if (subvol_fd < 0)
return -errno;
+ /* Let's check if this is actually a subvolume. Note that this is mostly redundant, as BTRFS_IOC_SNAP_DESTROY
+ * would fail anyway if it is not. However, it's a good thing to check this ahead of time so that we can return
+ * ENOTTY unconditionally in this case. This is different from the ioctl() which will return EPERM/EACCES if we
+ * don't have the privileges to remove subvolumes, regardless if the specified directory is actually a
+ * subvolume or not. In order to make it easy for callers to cover the "this is not a btrfs subvolume" case
+ * let's prefer ENOTTY over EPERM/EACCES though. */
+ r = btrfs_is_subvol_fd(subvol_fd);
+ if (r < 0)
+ return r;
+ if (r == 0) /* Not a btrfs subvolume */
+ return -ENOTTY;
+
if (subvol_id == 0) {
r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
if (r < 0)
@@ -1283,7 +1206,7 @@ int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
if (r < 0)
return r;
- fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ fd = open_parent(path, O_CLOEXEC, 0);
if (fd < 0)
return fd;
@@ -1485,7 +1408,12 @@ static int copy_subtree_quota_limits(int fd, uint64_t old_subvol, uint64_t new_s
return changed;
}
-static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_subvol_id, BtrfsSnapshotFlags flags) {
+static int subvol_snapshot_children(
+ int old_fd,
+ int new_fd,
+ const char *subvolume,
+ uint64_t old_subvol_id,
+ BtrfsSnapshotFlags flags) {
struct btrfs_ioctl_search_args args = {
.key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
@@ -1665,7 +1593,14 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
return 0;
}
-int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
+int btrfs_subvol_snapshot_fd_full(
+ int old_fd,
+ const char *new_path,
+ BtrfsSnapshotFlags flags,
+ copy_progress_path_t progress_path,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
+
_cleanup_close_ int new_fd = -1;
const char *subvolume;
int r;
@@ -1693,7 +1628,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
} else if (r < 0)
return r;
- r = copy_directory_fd(old_fd, new_path, COPY_MERGE|COPY_REFLINK);
+ r = copy_directory_fd_full(old_fd, new_path, COPY_MERGE|COPY_REFLINK, progress_path, progress_bytes, userdata);
if (r < 0)
goto fallback_fail;
@@ -1704,7 +1639,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
* it: the IMMUTABLE bit. Let's use this here, if this is requested. */
if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
- (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
+ (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
} else {
r = btrfs_subvol_set_read_only(new_path, true);
if (r < 0)
@@ -1723,14 +1658,21 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
if (r < 0)
return r;
- new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ new_fd = open_parent(new_path, O_CLOEXEC, 0);
if (new_fd < 0)
return new_fd;
return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
}
-int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
+int btrfs_subvol_snapshot_full(
+ const char *old_path,
+ const char *new_path,
+ BtrfsSnapshotFlags flags,
+ copy_progress_path_t progress_path,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
+
_cleanup_close_ int old_fd = -1;
assert(old_path);
@@ -1740,7 +1682,7 @@ int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnaps
if (old_fd < 0)
return -errno;
- return btrfs_subvol_snapshot_fd(old_fd, new_path, flags);
+ return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, progress_path, progress_bytes, userdata);
}
int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h
index a594387b5a..085aca4dbc 100644
--- a/src/basic/btrfs-util.h
+++ b/src/basic/btrfs-util.h
@@ -7,6 +7,7 @@
#include "sd-id128.h"
+#include "copy.h"
#include "time-util.h"
typedef struct BtrfsSubvolInfo {
@@ -61,13 +62,18 @@ int btrfs_quota_scan_start(int fd);
int btrfs_quota_scan_wait(int fd);
int btrfs_quota_scan_ongoing(int fd);
-int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
-int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
-
int btrfs_subvol_make(const char *path);
+int btrfs_subvol_make_fd(int fd, const char *subvolume);
+
+int btrfs_subvol_snapshot_fd_full(int old_fd, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
+ return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, NULL, NULL, NULL);
+}
-int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
-int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
+int btrfs_subvol_snapshot_full(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
+ return btrfs_subvol_snapshot_full(old_path, new_path, flags, NULL, NULL, NULL);
+}
int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags);
int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags);
diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c
index bfcda33520..29a17d9320 100644
--- a/src/basic/cap-list.c
+++ b/src/basic/cap-list.c
@@ -22,7 +22,7 @@ const char *capability_to_name(int id) {
if (id < 0)
return NULL;
- if (id >= (int) ELEMENTSOF(capability_names))
+ if ((size_t) id >= ELEMENTSOF(capability_names))
return NULL;
return capability_names[id];
@@ -37,7 +37,7 @@ int capability_from_name(const char *name) {
/* Try to parse numeric capability */
r = safe_atoi(name, &i);
if (r >= 0) {
- if (i >= 0 && i < (int) ELEMENTSOF(capability_names))
+ if (i >= 0 && (size_t) i < ELEMENTSOF(capability_names))
return i;
else
return -EINVAL;
diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c
index 6ae35e078b..a3f3ca9f52 100644
--- a/src/basic/capability-util.c
+++ b/src/basic/capability-util.c
@@ -359,3 +359,128 @@ bool ambient_capabilities_supported(void) {
return cache;
}
+
+int capability_quintet_enforce(const CapabilityQuintet *q) {
+ _cleanup_cap_free_ cap_t c = NULL;
+ int r;
+
+ if (q->ambient != (uint64_t) -1) {
+ unsigned long i;
+ bool changed = false;
+
+ c = cap_get_proc();
+ if (!c)
+ return -errno;
+
+ /* In order to raise the ambient caps set we first need to raise the matching inheritable + permitted
+ * cap */
+ for (i = 0; i <= cap_last_cap(); i++) {
+ uint64_t m = UINT64_C(1) << i;
+ cap_value_t cv = (cap_value_t) i;
+ cap_flag_value_t old_value_inheritable, old_value_permitted;
+
+ if ((q->ambient & m) == 0)
+ continue;
+
+ if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value_inheritable) < 0)
+ return -errno;
+ if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value_permitted) < 0)
+ return -errno;
+
+ if (old_value_inheritable == CAP_SET && old_value_permitted == CAP_SET)
+ continue;
+
+ if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, CAP_SET) < 0)
+ return -errno;
+
+ if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, CAP_SET) < 0)
+ return -errno;
+
+ changed = true;
+ }
+
+ if (changed)
+ if (cap_set_proc(c) < 0)
+ return -errno;
+
+ r = capability_ambient_set_apply(q->ambient, false);
+ if (r < 0)
+ return r;
+ }
+
+ if (q->inheritable != (uint64_t) -1 || q->permitted != (uint64_t) -1 || q->effective != (uint64_t) -1) {
+ bool changed = false;
+ unsigned long i;
+
+ if (!c) {
+ c = cap_get_proc();
+ if (!c)
+ return -errno;
+ }
+
+ for (i = 0; i <= cap_last_cap(); i++) {
+ uint64_t m = UINT64_C(1) << i;
+ cap_value_t cv = (cap_value_t) i;
+
+ if (q->inheritable != (uint64_t) -1) {
+ cap_flag_value_t old_value, new_value;
+
+ if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value) < 0)
+ return -errno;
+
+ new_value = (q->inheritable & m) ? CAP_SET : CAP_CLEAR;
+
+ if (old_value != new_value) {
+ changed = true;
+
+ if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, new_value) < 0)
+ return -errno;
+ }
+ }
+
+ if (q->permitted != (uint64_t) -1) {
+ cap_flag_value_t old_value, new_value;
+
+ if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value) < 0)
+ return -errno;
+
+ new_value = (q->permitted & m) ? CAP_SET : CAP_CLEAR;
+
+ if (old_value != new_value) {
+ changed = true;
+
+ if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, new_value) < 0)
+ return -errno;
+ }
+ }
+
+ if (q->effective != (uint64_t) -1) {
+ cap_flag_value_t old_value, new_value;
+
+ if (cap_get_flag(c, cv, CAP_EFFECTIVE, &old_value) < 0)
+ return -errno;
+
+ new_value = (q->effective & m) ? CAP_SET : CAP_CLEAR;
+
+ if (old_value != new_value) {
+ changed = true;
+
+ if (cap_set_flag(c, CAP_EFFECTIVE, 1, &cv, new_value) < 0)
+ return -errno;
+ }
+ }
+ }
+
+ if (changed)
+ if (cap_set_proc(c) < 0)
+ return -errno;
+ }
+
+ if (q->bounding != (uint64_t) -1) {
+ r = capability_bounding_set_drop(q->bounding, false);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h
index 4a4a86093a..02c7d5c3e2 100644
--- a/src/basic/capability-util.h
+++ b/src/basic/capability-util.h
@@ -7,6 +7,7 @@
#include <sys/types.h>
#include "macro.h"
+#include "missing_capability.h"
#include "util.h"
#define CAP_ALL (uint64_t) -1
@@ -39,3 +40,31 @@ static inline bool cap_test_all(uint64_t caps) {
}
bool ambient_capabilities_supported(void);
+
+/* Identical to linux/capability.h's CAP_TO_MASK(), but uses an unsigned 1U instead of a signed 1 for shifting left, in
+ * order to avoid complaints about shifting a signed int left by 31 bits, which would make it negative. */
+#define CAP_TO_MASK_CORRECTED(x) (1U << ((x) & 31U))
+
+typedef struct CapabilityQuintet {
+ /* Stores all five types of capabilities in one go. Note that we use (uint64_t) -1 for unset here. This hence
+ * needs to be updated as soon as Linux learns more than 63 caps. */
+ uint64_t effective;
+ uint64_t bounding;
+ uint64_t inheritable;
+ uint64_t permitted;
+ uint64_t ambient;
+} CapabilityQuintet;
+
+assert_cc(CAP_LAST_CAP < 64);
+
+#define CAPABILITY_QUINTET_NULL { (uint64_t) -1, (uint64_t) -1, (uint64_t) -1, (uint64_t) -1, (uint64_t) -1 }
+
+static inline bool capability_quintet_is_set(const CapabilityQuintet *q) {
+ return q->effective != (uint64_t) -1 ||
+ q->bounding != (uint64_t) -1 ||
+ q->inheritable != (uint64_t) -1 ||
+ q->permitted != (uint64_t) -1 ||
+ q->ambient != (uint64_t) -1;
+}
+
+int capability_quintet_enforce(const CapabilityQuintet *q);
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 038ece4b06..830a63c185 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -12,6 +12,7 @@
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <sys/xattr.h>
#include <unistd.h>
@@ -129,10 +130,12 @@ bool cg_ns_supported(void) {
if (enabled >= 0)
return enabled;
- if (access("/proc/self/ns/cgroup", F_OK) == 0)
- enabled = 1;
- else
- enabled = 0;
+ if (access("/proc/self/ns/cgroup", F_OK) < 0) {
+ if (errno != ENOENT)
+ log_debug_errno(errno, "Failed to check whether /proc/self/ns/cgroup is available, assuming not: %m");
+ enabled = false;
+ } else
+ enabled = true;
return enabled;
}
@@ -197,10 +200,8 @@ int cg_rmdir(const char *controller, const char *path) {
return -errno;
r = cg_hybrid_unified();
- if (r < 0)
+ if (r <= 0)
return r;
- if (r == 0)
- return 0;
if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
@@ -817,7 +818,7 @@ int cg_attach(const char *controller, const char *path, pid_t pid) {
xsprintf(c, PID_FMT "\n", pid);
- r = write_string_file(fs, c, 0);
+ r = write_string_file(fs, c, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return r;
@@ -985,10 +986,9 @@ int cg_get_xattr(const char *controller, const char *path, const char *name, voi
int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
_cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
const char *fs, *controller_str;
+ int unified, r;
size_t cs = 0;
- int unified;
assert(path);
assert(pid >= 0);
@@ -1018,10 +1018,15 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
- FOREACH_LINE(line, f, return -errno) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
char *e, *p;
- truncate_nl(line);
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
if (unified) {
e = startswith(line, "0:");
@@ -1095,7 +1100,7 @@ int cg_install_release_agent(const char *controller, const char *agent) {
sc = strstrip(contents);
if (isempty(sc)) {
- r = write_string_file(fs, agent, 0);
+ r = write_string_file(fs, agent, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return r;
} else if (!path_equal(sc, agent))
@@ -1113,7 +1118,7 @@ int cg_install_release_agent(const char *controller, const char *agent) {
sc = strstrip(contents);
if (streq(sc, "0")) {
- r = write_string_file(fs, "1", 0);
+ r = write_string_file(fs, "1", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return r;
@@ -1140,7 +1145,7 @@ int cg_uninstall_release_agent(const char *controller) {
if (r < 0)
return r;
- r = write_string_file(fs, "0", 0);
+ r = write_string_file(fs, "0", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return r;
@@ -1150,7 +1155,7 @@ int cg_uninstall_release_agent(const char *controller) {
if (r < 0)
return r;
- r = write_string_file(fs, "", 0);
+ r = write_string_file(fs, "", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return r;
@@ -1166,7 +1171,7 @@ int cg_is_empty(const char *controller, const char *path) {
r = cg_enumerate_processes(controller, path, &f);
if (r == -ENOENT)
- return 1;
+ return true;
if (r < 0)
return r;
@@ -1196,6 +1201,8 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
* via the "populated" attribute of "cgroup.events". */
r = cg_read_event(controller, path, "populated", &t);
+ if (r == -ENOENT)
+ return true;
if (r < 0)
return r;
@@ -1210,7 +1217,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
r = cg_enumerate_subgroups(controller, path, &d);
if (r == -ENOENT)
- return 1;
+ return true;
if (r < 0)
return r;
@@ -1845,9 +1852,7 @@ char *cg_escape(const char *p) {
* needs free()! */
if (IN_SET(p[0], 0, '_', '.') ||
- streq(p, "notify_on_release") ||
- streq(p, "release_agent") ||
- streq(p, "tasks") ||
+ STR_IN_SET(p, "notify_on_release", "release_agent", "tasks") ||
startswith(p, "cgroup."))
need_prefix = true;
else {
@@ -2007,7 +2012,7 @@ int cg_set_attribute(const char *controller, const char *path, const char *attri
if (r < 0)
return r;
- return write_string_file(p, value, 0);
+ return write_string_file(p, value, WRITE_STRING_FILE_DISABLE_BUFFER);
}
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
@@ -2102,6 +2107,7 @@ done:
int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
CGroupController c;
+ CGroupMask done;
bool created;
int r;
@@ -2117,7 +2123,7 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
if (r < 0)
return r;
- created = !!r;
+ created = r;
/* If we are in the unified hierarchy, we are done now */
r = cg_all_unified();
@@ -2126,17 +2132,28 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
if (r > 0)
return created;
+ supported &= CGROUP_MASK_V1;
+ mask = CGROUP_MASK_EXTEND_JOINED(mask);
+ done = 0;
+
/* Otherwise, do the same in the other hierarchies */
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
const char *n;
- n = cgroup_controller_to_string(c);
+ if (!FLAGS_SET(supported, bit))
+ continue;
+
+ if (FLAGS_SET(done, bit))
+ continue;
- if (mask & bit)
+ n = cgroup_controller_to_string(c);
+ if (FLAGS_SET(mask, bit))
(void) cg_create(n, path);
- else if (supported & bit)
+ else
(void) cg_trim(n, path, true);
+
+ done |= CGROUP_MASK_EXTEND_JOINED(bit);
}
return created;
@@ -2144,6 +2161,7 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
CGroupController c;
+ CGroupMask done;
int r;
r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
@@ -2156,20 +2174,26 @@ int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_m
if (r > 0)
return 0;
+ supported &= CGROUP_MASK_V1;
+ done = 0;
+
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
const char *p = NULL;
- if (!(supported & bit))
+ if (!FLAGS_SET(supported, bit))
+ continue;
+
+ if (FLAGS_SET(done, bit))
continue;
if (path_callback)
p = path_callback(bit, userdata);
-
if (!p)
p = path;
(void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
+ done |= CGROUP_MASK_EXTEND_JOINED(bit);
}
return 0;
@@ -2194,6 +2218,7 @@ int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids,
int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
CGroupController c;
+ CGroupMask done;
int r = 0, q;
if (!path_equal(from, to)) {
@@ -2208,27 +2233,34 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
if (q > 0)
return r;
+ supported &= CGROUP_MASK_V1;
+ done = 0;
+
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
const char *p = NULL;
- if (!(supported & bit))
+ if (!FLAGS_SET(supported, bit))
+ continue;
+
+ if (FLAGS_SET(done, bit))
continue;
if (to_callback)
p = to_callback(bit, userdata);
-
if (!p)
p = to;
(void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
+ done |= CGROUP_MASK_EXTEND_JOINED(bit);
}
- return 0;
+ return r;
}
int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
CGroupController c;
+ CGroupMask done;
int r, q;
r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
@@ -2241,16 +2273,23 @@ int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root)
if (q > 0)
return r;
+ supported &= CGROUP_MASK_V1;
+ done = 0;
+
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
- if (!(supported & bit))
+ if (!FLAGS_SET(supported, bit))
+ continue;
+
+ if (FLAGS_SET(done, bit))
continue;
(void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
+ done |= CGROUP_MASK_EXTEND_JOINED(bit);
}
- return 0;
+ return r;
}
int cg_mask_to_string(CGroupMask mask, char **ret) {
@@ -2270,7 +2309,7 @@ int cg_mask_to_string(CGroupMask mask, char **ret) {
const char *k;
size_t l;
- if (!(mask & CGROUP_CONTROLLER_TO_MASK(c)))
+ if (!FLAGS_SET(mask, CGROUP_CONTROLLER_TO_MASK(c)))
continue;
k = cgroup_controller_to_string(c);
@@ -2295,8 +2334,10 @@ int cg_mask_to_string(CGroupMask mask, char **ret) {
return 0;
}
-int cg_mask_from_string(const char *value, CGroupMask *mask) {
- assert(mask);
+int cg_mask_from_string(const char *value, CGroupMask *ret) {
+ CGroupMask m = 0;
+
+ assert(ret);
assert(value);
for (;;) {
@@ -2314,18 +2355,20 @@ int cg_mask_from_string(const char *value, CGroupMask *mask) {
if (v < 0)
continue;
- *mask |= CGROUP_CONTROLLER_TO_MASK(v);
+ m |= CGROUP_CONTROLLER_TO_MASK(v);
}
+
+ *ret = m;
return 0;
}
int cg_mask_supported(CGroupMask *ret) {
- CGroupMask mask = 0;
+ CGroupMask mask;
int r;
- /* Determines the mask of supported cgroup controllers. Only
- * includes controllers we can make sense of and that are
- * actually accessible. */
+ /* Determines the mask of supported cgroup controllers. Only includes controllers we can make sense of and that
+ * are actually accessible. Only covers real controllers, i.e. not the CGROUP_CONTROLLER_BPF_xyz
+ * pseudo-controllers. */
r = cg_all_unified();
if (r < 0)
@@ -2353,23 +2396,26 @@ int cg_mask_supported(CGroupMask *ret) {
if (r < 0)
return r;
- /* Currently, we support the cpu, memory, io and pids
- * controller in the unified hierarchy, mask
+ /* Currently, we support the cpu, memory, io and pids controller in the unified hierarchy, mask
* everything else off. */
- mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
+ mask &= CGROUP_MASK_V2;
} else {
CGroupController c;
- /* In the legacy hierarchy, we check whether which
- * hierarchies are mounted. */
+ /* In the legacy hierarchy, we check which hierarchies are mounted. */
+ mask = 0;
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
const char *n;
+ if (!FLAGS_SET(CGROUP_MASK_V1, bit))
+ continue;
+
n = cgroup_controller_to_string(c);
if (controller_is_accessible(n) >= 0)
- mask |= CGROUP_CONTROLLER_TO_MASK(c);
+ mask |= bit;
}
}
@@ -2384,10 +2430,9 @@ int cg_kernel_controllers(Set **ret) {
assert(ret);
- /* Determines the full list of kernel-known controllers. Might
- * include controllers we don't actually support, arbitrary
- * named hierarchies and controllers that aren't currently
- * accessible (because not mounted). */
+ /* Determines the full list of kernel-known controllers. Might include controllers we don't actually support
+ * and controllers that aren't currently accessible (because not mounted). This does not include "name="
+ * pseudo-controllers. */
controllers = set_new(&string_hash_ops);
if (!controllers)
@@ -2498,11 +2543,10 @@ static int cg_unified_update(void) {
unified_cache = CGROUP_UNIFIED_NONE;
}
}
- } else {
- log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
- (unsigned long long) fs.f_type);
- return -ENOMEDIUM;
- }
+ } else
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
+ "Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
+ (unsigned long long)fs.f_type);
return 0;
}
@@ -2549,22 +2593,45 @@ int cg_unified_flush(void) {
return cg_unified_update();
}
-int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
+int cg_enable_everywhere(
+ CGroupMask supported,
+ CGroupMask mask,
+ const char *p,
+ CGroupMask *ret_result_mask) {
+
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *fs = NULL;
CGroupController c;
+ CGroupMask ret = 0;
int r;
assert(p);
- if (supported == 0)
+ if (supported == 0) {
+ if (ret_result_mask)
+ *ret_result_mask = 0;
return 0;
+ }
r = cg_all_unified();
if (r < 0)
return r;
- if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
+ if (r == 0) {
+ /* On the legacy hiearchy there's no concept of "enabling" controllers in cgroups defined. Let's claim
+ * complete success right away. (If you wonder why we return the full mask here, rather than zero: the
+ * caller tends to use the returned mask later on to compare if all controllers where properly joined,
+ * and if not requeues realization. This use is the primary purpose of the return value, hence let's
+ * minimize surprises here and reduce triggers for re-realization by always saying we fully
+ * succeeded.) */
+ if (ret_result_mask)
+ *ret_result_mask = mask & supported & CGROUP_MASK_V2; /* If you wonder why we mask this with
+ * CGROUP_MASK_V2: The 'supported' mask
+ * might contain pure-V1 or BPF
+ * controllers, and we never want to
+ * claim that we could enable those with
+ * cgroup.subtree_control */
return 0;
+ }
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
if (r < 0)
@@ -2574,32 +2641,63 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
const char *n;
- if (!(supported & bit))
+ if (!FLAGS_SET(CGROUP_MASK_V2, bit))
+ continue;
+
+ if (!FLAGS_SET(supported, bit))
continue;
n = cgroup_controller_to_string(c);
{
char s[1 + strlen(n) + 1];
- s[0] = mask & bit ? '+' : '-';
+ s[0] = FLAGS_SET(mask, bit) ? '+' : '-';
strcpy(s + 1, n);
if (!f) {
f = fopen(fs, "we");
- if (!f) {
- log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
- break;
- }
+ if (!f)
+ return log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
}
- r = write_string_stream(f, s, 0);
+ r = write_string_stream(f, s, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0) {
- log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
+ log_debug_errno(r, "Failed to %s controller %s for %s (%s): %m",
+ FLAGS_SET(mask, bit) ? "enable" : "disable", n, p, fs);
clearerr(f);
+
+ /* If we can't turn off a controller, leave it on in the reported resulting mask. This
+ * happens for example when we attempt to turn off a controller up in the tree that is
+ * used down in the tree. */
+ if (!FLAGS_SET(mask, bit) && r == -EBUSY) /* You might wonder why we check for EBUSY
+ * only here, and not follow the same logic
+ * for other errors such as EINVAL or
+ * EOPNOTSUPP or anything else. That's
+ * because EBUSY indicates that the
+ * controllers is currently enabled and
+ * cannot be disabled because something down
+ * the hierarchy is still using it. Any other
+ * error most likely means something like "I
+ * never heard of this controller" or
+ * similar. In the former case it's hence
+ * safe to assume the controller is still on
+ * after the failed operation, while in the
+ * latter case it's safer to assume the
+ * controller is unknown and hence certainly
+ * not enabled. */
+ ret |= bit;
+ } else {
+ /* Otherwise, if we managed to turn on a controller, set the bit reflecting that. */
+ if (FLAGS_SET(mask, bit))
+ ret |= bit;
}
}
}
+ /* Let's return the precise set of controllers now enabled for the cgroup. */
+ if (ret_result_mask)
+ *ret_result_mask = ret;
+
return 0;
}
@@ -2608,6 +2706,7 @@ bool cg_is_unified_wanted(void) {
int r;
bool b;
const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
+ _cleanup_free_ char *c = NULL;
/* If we have a cached value, return that. */
if (wanted >= 0)
@@ -2618,11 +2717,19 @@ bool cg_is_unified_wanted(void) {
if (cg_unified_flush() >= 0)
return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
- /* Otherwise, let's see what the kernel command line has to say.
- * Since checking is expensive, cache a non-error result. */
+ /* If we were explicitly passed systemd.unified_cgroup_hierarchy,
+ * respect that. */
r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
+ if (r > 0)
+ return (wanted = b);
+
+ /* If we passed cgroup_no_v1=all with no other instructions, it seems
+ * highly unlikely that we want to use hybrid or legacy hierarchy. */
+ r = proc_cmdline_get_key("cgroup_no_v1", 0, &c);
+ if (r > 0 && streq_ptr(c, "all"))
+ return (wanted = true);
- return (wanted = r > 0 ? b : is_default);
+ return (wanted = is_default);
}
bool cg_is_legacy_wanted(void) {
@@ -2768,6 +2875,59 @@ static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
[CGROUP_CONTROLLER_MEMORY] = "memory",
[CGROUP_CONTROLLER_DEVICES] = "devices",
[CGROUP_CONTROLLER_PIDS] = "pids",
+ [CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall",
+ [CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
+
+CGroupMask get_cpu_accounting_mask(void) {
+ static CGroupMask needed_mask = (CGroupMask) -1;
+
+ /* On kernel ≥4.15 with unified hierarchy, cpu.stat's usage_usec is
+ * provided externally from the CPU controller, which means we don't
+ * need to enable the CPU controller just to get metrics. This is good,
+ * because enabling the CPU controller comes at a minor performance
+ * hit, especially when it's propagated deep into large hierarchies.
+ * There's also no separate CPU accounting controller available within
+ * a unified hierarchy.
+ *
+ * This combination of factors results in the desired cgroup mask to
+ * enable for CPU accounting varying as follows:
+ *
+ * â•”â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¤â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•—
+ * ║ Linux ≥4.15 │ Linux <4.15 ║
+ * â•”â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•¬â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•ªâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•£
+ * ║ Unified ║ nothing │ CGROUP_MASK_CPU ║
+ * ╟───────────────╫─────────────────────┼─────────────────────╢
+ * ║ Hybrid/Legacy ║ CGROUP_MASK_CPUACCT │ CGROUP_MASK_CPUACCT ║
+ * â•šâ•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•©â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•§â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
+ *
+ * We check kernel version here instead of manually checking whether
+ * cpu.stat is present for every cgroup, as that check in itself would
+ * already be fairly expensive.
+ *
+ * Kernels where this patch has been backported will therefore have the
+ * CPU controller enabled unnecessarily. This is more expensive than
+ * necessary, but harmless. ☺ï¸
+ */
+
+ if (needed_mask == (CGroupMask) -1) {
+ if (cg_all_unified()) {
+ struct utsname u;
+ assert_se(uname(&u) >= 0);
+
+ if (str_verscmp(u.release, "4.15") < 0)
+ needed_mask = CGROUP_MASK_CPU;
+ else
+ needed_mask = 0;
+ } else
+ needed_mask = CGROUP_MASK_CPUACCT;
+ }
+
+ return needed_mask;
+}
+
+bool cpu_accounting_is_cheap(void) {
+ return get_cpu_accounting_mask() == 0;
+}
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 1a28a8163a..ea9a333290 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -9,8 +9,6 @@
#include <sys/types.h>
#include "def.h"
-#include "hashmap.h"
-#include "macro.h"
#include "set.h"
#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd"
@@ -19,6 +17,7 @@
/* An enum of well known cgroup controllers */
typedef enum CGroupController {
+ /* Original cgroup controllers */
CGROUP_CONTROLLER_CPU,
CGROUP_CONTROLLER_CPUACCT, /* v1 only */
CGROUP_CONTROLLER_IO, /* v2 only */
@@ -26,11 +25,16 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_MEMORY,
CGROUP_CONTROLLER_DEVICES, /* v1 only */
CGROUP_CONTROLLER_PIDS,
+
+ /* BPF-based pseudo-controllers, v2 only */
+ CGROUP_CONTROLLER_BPF_FIREWALL,
+ CGROUP_CONTROLLER_BPF_DEVICES,
+
_CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -1,
} CGroupController;
-#define CGROUP_CONTROLLER_TO_MASK(c) (1 << (c))
+#define CGROUP_CONTROLLER_TO_MASK(c) (1U << (c))
/* A bit mask of well known cgroup controllers */
typedef enum CGroupMask {
@@ -41,9 +45,33 @@ typedef enum CGroupMask {
CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY),
CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES),
CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS),
+ CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL),
+ CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
+
+ /* All real cgroupv1 controllers */
+ CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
+
+ /* All real cgroupv2 controllers */
+ CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
+
+ /* All cgroupv2 BPF pseudo-controllers */
+ CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES,
+
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
} CGroupMask;
+static inline CGroupMask CGROUP_MASK_EXTEND_JOINED(CGroupMask mask) {
+ /* We always mount "cpu" and "cpuacct" in the same hierarchy. Hence, when one bit is set also set the other */
+
+ if (mask & (CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT))
+ mask |= (CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT);
+
+ return mask;
+}
+
+CGroupMask get_cpu_accounting_mask(void);
+bool cpu_accounting_is_cheap(void);
+
/* Special values for all weight knobs on unified hierarchy */
#define CGROUP_WEIGHT_INVALID ((uint64_t) -1)
#define CGROUP_WEIGHT_MIN UINT64_C(1)
@@ -217,7 +245,7 @@ int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_m
int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root);
-int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p);
+int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p, CGroupMask *ret_result_mask);
int cg_mask_supported(CGroupMask *ret);
int cg_mask_from_string(const char *s, CGroupMask *ret);
diff --git a/src/basic/chattr-util.c b/src/basic/chattr-util.c
index 4ec14515eb..235cfb9bd7 100644
--- a/src/basic/chattr-util.c
+++ b/src/basic/chattr-util.c
@@ -10,7 +10,7 @@
#include "fd-util.h"
#include "macro.h"
-int chattr_fd(int fd, unsigned value, unsigned mask) {
+int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) {
unsigned old_attr, new_attr;
struct stat st;
@@ -28,23 +28,29 @@ int chattr_fd(int fd, unsigned value, unsigned mask) {
if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
return -ENOTTY;
- if (mask == 0)
+ if (mask == 0 && !previous)
return 0;
if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
return -errno;
new_attr = (old_attr & ~mask) | (value & mask);
- if (new_attr == old_attr)
+ if (new_attr == old_attr) {
+ if (previous)
+ *previous = old_attr;
return 0;
+ }
if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
return -errno;
+ if (previous)
+ *previous = old_attr;
+
return 1;
}
-int chattr_path(const char *p, unsigned value, unsigned mask) {
+int chattr_path(const char *p, unsigned value, unsigned mask, unsigned *previous) {
_cleanup_close_ int fd = -1;
assert(p);
@@ -56,7 +62,7 @@ int chattr_path(const char *p, unsigned value, unsigned mask) {
if (fd < 0)
return -errno;
- return chattr_fd(fd, value, mask);
+ return chattr_fd(fd, value, mask, previous);
}
int read_attr_fd(int fd, unsigned *ret) {
diff --git a/src/basic/chattr-util.h b/src/basic/chattr-util.h
index 0c0816344a..7570bba2fa 100644
--- a/src/basic/chattr-util.h
+++ b/src/basic/chattr-util.h
@@ -1,8 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-int chattr_fd(int fd, unsigned value, unsigned mask);
-int chattr_path(const char *p, unsigned value, unsigned mask);
+int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous);
+int chattr_path(const char *p, unsigned value, unsigned mask, unsigned *previous);
int read_attr_fd(int fd, unsigned *ret);
int read_attr_path(const char *p, unsigned *ret);
diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c
index d6ef0e941e..b70c6e50a8 100644
--- a/src/basic/conf-files.c
+++ b/src/basic/conf-files.c
@@ -134,12 +134,8 @@ static int files_add(
return 0;
}
-static int base_cmp(const void *a, const void *b) {
- const char *s1, *s2;
-
- s1 = *(char * const *)a;
- s2 = *(char * const *)b;
- return strcmp(basename(s1), basename(s2));
+static int base_cmp(char * const *a, char * const *b) {
+ return strcmp(basename(*a), basename(*b));
}
static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, unsigned flags, char **dirs) {
@@ -176,7 +172,7 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
if (!files)
return -ENOMEM;
- qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp);
+ typesafe_qsort(files, hashmap_size(fh), base_cmp);
*strv = files;
return 0;
@@ -193,25 +189,29 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
* - do nothing if our new entry matches the existing entry,
* - replace the existing entry if our new entry has higher priority.
*/
- size_t i;
+ size_t i, n;
char *t;
int r;
- for (i = 0; i < strv_length(*strv); i++) {
+ n = strv_length(*strv);
+ for (i = 0; i < n; i++) {
int c;
- c = base_cmp(*strv + i, &path);
+ c = base_cmp((char* const*) *strv + i, (char* const*) &path);
if (c == 0) {
char **dir;
- /* Oh, we found our spot and it already contains something. */
+ /* Oh, there already is an entry with a matching name (the last component). */
+
STRV_FOREACH(dir, dirs) {
+ _cleanup_free_ char *rdir = NULL;
char *p1, *p2;
- p1 = path_startswith((*strv)[i], root);
- if (p1)
- /* Skip "/" in *dir, because p1 is without "/" too */
- p1 = path_startswith(p1, *dir + 1);
+ rdir = prefix_root(root, *dir);
+ if (!rdir)
+ return -ENOMEM;
+
+ p1 = path_startswith((*strv)[i], rdir);
if (p1)
/* Existing entry with higher priority
* or same priority, no need to do anything. */
@@ -220,7 +220,8 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
p2 = path_startswith(path, *dir);
if (p2) {
/* Our new entry has higher priority */
- t = path_join(root, path, NULL);
+
+ t = prefix_root(root, path);
if (!t)
return log_oom();
@@ -236,26 +237,16 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
/* … we are not there yet, let's continue */
}
- t = path_join(root, path, NULL);
+ /* The new file has lower priority than all the existing entries */
+ t = prefix_root(root, path);
if (!t)
- return log_oom();
+ return -ENOMEM;
r = strv_insert(strv, i, t);
if (r < 0)
free(t);
- return r;
-}
-int conf_files_insert_nulstr(char ***strv, const char *root, const char *dirs, const char *path) {
- _cleanup_strv_free_ char **d = NULL;
-
- assert(strv);
-
- d = strv_split_nulstr(dirs);
- if (!d)
- return -ENOMEM;
-
- return conf_files_insert(strv, root, d, path);
+ return r;
}
int conf_files_list_strv(char ***strv, const char *suffix, const char *root, unsigned flags, const char* const* dirs) {
@@ -322,7 +313,7 @@ int conf_files_list_with_replacement(
if (r < 0)
return log_error_errno(r, "Failed to extend config file list: %m");
- p = path_join(root, replacement, NULL);
+ p = prefix_root(root, replacement);
if (!p)
return log_oom();
}
@@ -332,36 +323,3 @@ int conf_files_list_with_replacement(
*replace_file = TAKE_PTR(p);
return 0;
}
-
-int conf_files_cat(const char *root, const char *name) {
- _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
- _cleanup_free_ char *path = NULL;
- const char *dir;
- char **t;
- int r;
-
- NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
- assert(endswith(dir, "/"));
- r = strv_extendf(&dirs, "%s%s.d", dir, name);
- if (r < 0)
- return log_error_errno(r, "Failed to build directory list: %m");
- }
-
- r = conf_files_list_strv(&files, ".conf", root, 0, (const char* const*) dirs);
- if (r < 0)
- return log_error_errno(r, "Failed to query file list: %m");
-
- path = path_join(root, "/etc", name);
- if (!path)
- return log_oom();
-
- if (DEBUG_LOGGING) {
- log_debug("Looking for configuration in:");
- log_debug(" %s", path);
- STRV_FOREACH(t, dirs)
- log_debug(" %s/*.conf", *t);
- }
-
- /* show */
- return cat_files(path, files, CAT_FLAGS_MAIN_FILE_OPTIONAL);
-}
diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h
index 9da03ed228..55ab326402 100644
--- a/src/basic/conf-files.h
+++ b/src/basic/conf-files.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include "macro.h"
enum {
CONF_FILES_EXECUTABLE = 1 << 0,
@@ -10,15 +11,13 @@ enum {
CONF_FILES_FILTER_MASKED = 1 << 4,
};
-int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir, ...);
+int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir, ...) _sentinel_;
int conf_files_list_strv(char ***ret, const char *suffix, const char *root, unsigned flags, const char* const* dirs);
int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs);
int conf_files_insert(char ***strv, const char *root, char **dirs, const char *path);
-int conf_files_insert_nulstr(char ***strv, const char *root, const char *dirs, const char *path);
int conf_files_list_with_replacement(
const char *root,
char **config_dirs,
const char *replacement,
char ***files,
char **replace_file);
-int conf_files_cat(const char *root, const char *name);
diff --git a/src/basic/copy.c b/src/basic/copy.c
index e06a503a29..34e01ea1cf 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -19,15 +19,15 @@
#include "copy.h"
#include "dirent-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
#include "macro.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
+#include "tmpfile-util.h"
#include "umask-util.h"
#include "user-util.h"
#include "xattr-util.h"
@@ -43,7 +43,7 @@ static ssize_t try_copy_file_range(
int fd_in, loff_t *off_in,
int fd_out, loff_t *off_out,
size_t len,
- unsigned int flags) {
+ unsigned flags) {
static int have = -1;
ssize_t r;
@@ -90,7 +90,9 @@ int copy_bytes_full(
uint64_t max_bytes,
CopyFlags copy_flags,
void **ret_remains,
- size_t *ret_remains_size) {
+ size_t *ret_remains_size,
+ copy_progress_bytes_t progress,
+ void *userdata) {
bool try_cfr = true, try_sendfile = true, try_splice = true;
int r, nonblock_pipe = -1;
@@ -161,8 +163,6 @@ int copy_bytes_full(
return 1; /* we copied only some number of bytes, which worked, but this means we didn't hit EOF, return 1 */
}
}
-
- log_debug_errno(r, "Reflinking didn't work, falling back to non-reflink copying: %m");
}
}
}
@@ -308,10 +308,17 @@ int copy_bytes_full(
}
next:
+ if (progress) {
+ r = progress(n, userdata);
+ if (r < 0)
+ return r;
+ }
+
if (max_bytes != (uint64_t) -1) {
assert(max_bytes >= (uint64_t) n);
max_bytes -= n;
}
+
/* sendfile accepts at most SSIZE_MAX-offset bytes to copy,
* so reduce our maximum by the amount we already copied,
* but don't go below our copy buffer size, unless we are
@@ -363,7 +370,9 @@ static int fd_copy_regular(
const char *to,
uid_t override_uid,
gid_t override_gid,
- CopyFlags copy_flags) {
+ CopyFlags copy_flags,
+ copy_progress_bytes_t progress,
+ void *userdata) {
_cleanup_close_ int fdf = -1, fdt = -1;
struct timespec ts[2];
@@ -381,7 +390,7 @@ static int fd_copy_regular(
if (fdt < 0)
return -errno;
- r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
+ r = copy_bytes_full(fdf, fdt, (uint64_t) -1, copy_flags, NULL, NULL, progress, userdata);
if (r < 0) {
(void) unlinkat(dt, to, 0);
return r;
@@ -483,7 +492,11 @@ static int fd_copy_directory(
unsigned depth_left,
uid_t override_uid,
gid_t override_gid,
- CopyFlags copy_flags) {
+ CopyFlags copy_flags,
+ const char *display_path,
+ copy_progress_path_t progress_path,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
_cleanup_close_ int fdf = -1, fdt = -1;
_cleanup_closedir_ DIR *d = NULL;
@@ -524,6 +537,8 @@ static int fd_copy_directory(
r = 0;
FOREACH_DIRENT_ALL(de, d, return -errno) {
+ const char *child_display_path = NULL;
+ _cleanup_free_ char *dp = NULL;
struct stat buf;
int q;
@@ -535,6 +550,17 @@ static int fd_copy_directory(
continue;
}
+ if (progress_path) {
+ if (display_path)
+ child_display_path = dp = strjoin(display_path, "/", de->d_name);
+ else
+ child_display_path = de->d_name;
+
+ r = progress_path(child_display_path, &buf, userdata);
+ if (r < 0)
+ return r;
+ }
+
if (S_ISDIR(buf.st_mode)) {
/*
* Don't descend into directories on other file systems, if this is requested. We do a simple
@@ -566,9 +592,9 @@ static int fd_copy_directory(
continue;
}
- q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags);
+ q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags, child_display_path, progress_path, progress_bytes, userdata);
} else if (S_ISREG(buf.st_mode))
- q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
+ q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags, progress_bytes, userdata);
else if (S_ISLNK(buf.st_mode))
q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(buf.st_mode))
@@ -606,7 +632,18 @@ static int fd_copy_directory(
return r;
}
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
+int copy_tree_at_full(
+ int fdf,
+ const char *from,
+ int fdt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags,
+ copy_progress_path_t progress_path,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
+
struct stat st;
assert(from);
@@ -616,9 +653,9 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t overr
return -errno;
if (S_ISREG(st.st_mode))
- return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
+ return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, progress_bytes, userdata);
else if (S_ISDIR(st.st_mode))
- return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags);
+ return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, progress_path, progress_bytes, userdata);
else if (S_ISLNK(st.st_mode))
return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(st.st_mode))
@@ -629,11 +666,14 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t overr
return -EOPNOTSUPP;
}
-int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
- return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags);
-}
+int copy_directory_fd_full(
+ int dirfd,
+ const char *to,
+ CopyFlags copy_flags,
+ copy_progress_path_t progress_path,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
-int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
struct stat st;
assert(dirfd >= 0);
@@ -645,10 +685,17 @@ int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
- return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags);
+ return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, progress_path, progress_bytes, userdata);
}
-int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
+int copy_directory_full(
+ const char *from,
+ const char *to,
+ CopyFlags copy_flags,
+ copy_progress_path_t progress_path,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
+
struct stat st;
assert(from);
@@ -660,10 +707,16 @@ int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
- return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags);
+ return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, progress_path, progress_bytes, userdata);
}
-int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
+int copy_file_fd_full(
+ const char *from,
+ int fdt,
+ CopyFlags copy_flags,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
+
_cleanup_close_ int fdf = -1;
int r;
@@ -674,7 +727,7 @@ int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
if (fdf < 0)
return -errno;
- r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
+ r = copy_bytes_full(fdf, fdt, (uint64_t) -1, copy_flags, NULL, NULL, progress_bytes, userdata);
(void) copy_times(fdf, fdt);
(void) copy_xattr(fdf, fdt);
@@ -682,7 +735,16 @@ int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
return r;
}
-int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
+int copy_file_full(
+ const char *from,
+ const char *to,
+ int flags,
+ mode_t mode,
+ unsigned chattr_flags,
+ CopyFlags copy_flags,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
+
int fdt = -1, r;
assert(from);
@@ -695,9 +757,9 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
}
if (chattr_flags != 0)
- (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
+ (void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL);
- r = copy_file_fd(from, fdt, copy_flags);
+ r = copy_file_fd_full(from, fdt, copy_flags, progress_bytes, userdata);
if (r < 0) {
close(fdt);
(void) unlink(to);
@@ -712,7 +774,15 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
return 0;
}
-int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
+int copy_file_atomic_full(
+ const char *from,
+ const char *to,
+ mode_t mode,
+ unsigned chattr_flags,
+ CopyFlags copy_flags,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
+
_cleanup_(unlink_and_freep) char *t = NULL;
_cleanup_close_ int fdt = -1;
int r;
@@ -743,9 +813,9 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned cha
}
if (chattr_flags != 0)
- (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
+ (void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL);
- r = copy_file_fd(from, fdt, copy_flags);
+ r = copy_file_fd_full(from, fdt, copy_flags, progress_bytes, userdata);
if (r < 0)
return r;
diff --git a/src/basic/copy.h b/src/basic/copy.h
index 6a0a6bc9b3..a41b44c70a 100644
--- a/src/basic/copy.h
+++ b/src/basic/copy.h
@@ -1,9 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
+#include <sys/stat.h>
#include <sys/types.h>
typedef enum CopyFlags {
@@ -13,16 +15,46 @@ typedef enum CopyFlags {
COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
} CopyFlags;
-int copy_file_fd(const char *from, int to, CopyFlags copy_flags);
-int copy_file(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
-int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
-int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
-int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags);
-int copy_directory(const char *from, const char *to, CopyFlags copy_flags);
-int copy_bytes_full(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags, void **ret_remains, size_t *ret_remains_size);
+typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);
+typedef int (*copy_progress_path_t)(const char *path, const struct stat *st, void *userdata);
+
+int copy_file_fd_full(const char *from, int to, CopyFlags copy_flags, copy_progress_bytes_t progress, void *userdata);
+static inline int copy_file_fd(const char *from, int to, CopyFlags copy_flags) {
+ return copy_file_fd_full(from, to, copy_flags, NULL, NULL);
+}
+
+int copy_file_full(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags, copy_progress_bytes_t progress, void *userdata);
+static inline int copy_file(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
+ return copy_file_full(from, to, open_flags, mode, chattr_flags, copy_flags, NULL, NULL);
+}
+
+int copy_file_atomic_full(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags, copy_progress_bytes_t progress, void *userdata);
+static inline int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
+ return copy_file_atomic_full(from, to, mode, chattr_flags, copy_flags, NULL, NULL);
+}
+
+int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
+ return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
+}
+static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
+ return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
+}
+
+int copy_directory_fd_full(int dirfd, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
+ return copy_directory_fd_full(dirfd, to, copy_flags, NULL, NULL, NULL);
+}
+
+int copy_directory_full(const char *from, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
+ return copy_directory_full(from, to, copy_flags, NULL, NULL, NULL);
+}
+
+int copy_bytes_full(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags, void **ret_remains, size_t *ret_remains_size, copy_progress_bytes_t progress, void *userdata);
static inline int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
- return copy_bytes_full(fdf, fdt, max_bytes, copy_flags, NULL, NULL);
+ return copy_bytes_full(fdf, fdt, max_bytes, copy_flags, NULL, NULL, NULL, NULL);
}
+
int copy_times(int fdf, int fdt);
int copy_xattr(int fdf, int fdt);
diff --git a/src/basic/crypt-util.c b/src/basic/crypt-util.c
deleted file mode 100644
index b181ba3ba0..0000000000
--- a/src/basic/crypt-util.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#if HAVE_LIBCRYPTSETUP
-#include "crypt-util.h"
-#include "log.h"
-
-void cryptsetup_log_glue(int level, const char *msg, void *usrptr) {
- log_debug("%s", msg);
-}
-#endif
diff --git a/src/basic/def.h b/src/basic/def.h
index 4d515c11b6..5be018d82e 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -74,4 +74,4 @@
"/usr/lib/" n \
_CONF_PATHS_SPLIT_USR(n))
-#define LONG_LINE_MAX (1U*1024U*1024U)
+#define HIGH_RLIMIT_MEMLOCK (1024ULL*1024ULL*64ULL)
diff --git a/src/basic/env-file.c b/src/basic/env-file.c
new file mode 100644
index 0000000000..6a7d6746a1
--- /dev/null
+++ b/src/basic/env-file.c
@@ -0,0 +1,574 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <stdio_ext.h>
+
+#include "alloc-util.h"
+#include "env-file.h"
+#include "env-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+#include "utf8.h"
+
+static int parse_env_file_internal(
+ FILE *f,
+ const char *fname,
+ int (*push) (const char *filename, unsigned line,
+ const char *key, char *value, void *userdata, int *n_pushed),
+ void *userdata,
+ int *n_pushed) {
+
+ size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
+ _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL;
+ unsigned line = 1;
+ char *p;
+ int r;
+
+ enum {
+ PRE_KEY,
+ KEY,
+ PRE_VALUE,
+ VALUE,
+ VALUE_ESCAPE,
+ SINGLE_QUOTE_VALUE,
+ SINGLE_QUOTE_VALUE_ESCAPE,
+ DOUBLE_QUOTE_VALUE,
+ DOUBLE_QUOTE_VALUE_ESCAPE,
+ COMMENT,
+ COMMENT_ESCAPE
+ } state = PRE_KEY;
+
+ if (f)
+ r = read_full_stream(f, &contents, NULL);
+ else
+ r = read_full_file(fname, &contents, NULL);
+ if (r < 0)
+ return r;
+
+ for (p = contents; *p; p++) {
+ char c = *p;
+
+ switch (state) {
+
+ case PRE_KEY:
+ if (strchr(COMMENTS, c))
+ state = COMMENT;
+ else if (!strchr(WHITESPACE, c)) {
+ state = KEY;
+ last_key_whitespace = (size_t) -1;
+
+ if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
+ return -ENOMEM;
+
+ key[n_key++] = c;
+ }
+ break;
+
+ case KEY:
+ if (strchr(NEWLINE, c)) {
+ state = PRE_KEY;
+ line++;
+ n_key = 0;
+ } else if (c == '=') {
+ state = PRE_VALUE;
+ last_value_whitespace = (size_t) -1;
+ } else {
+ if (!strchr(WHITESPACE, c))
+ last_key_whitespace = (size_t) -1;
+ else if (last_key_whitespace == (size_t) -1)
+ last_key_whitespace = n_key;
+
+ if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
+ return -ENOMEM;
+
+ key[n_key++] = c;
+ }
+
+ break;
+
+ case PRE_VALUE:
+ if (strchr(NEWLINE, c)) {
+ state = PRE_KEY;
+ line++;
+ key[n_key] = 0;
+
+ if (value)
+ value[n_value] = 0;
+
+ /* strip trailing whitespace from key */
+ if (last_key_whitespace != (size_t) -1)
+ key[last_key_whitespace] = 0;
+
+ r = push(fname, line, key, value, userdata, n_pushed);
+ if (r < 0)
+ return r;
+
+ n_key = 0;
+ value = NULL;
+ value_alloc = n_value = 0;
+
+ } else if (c == '\'')
+ state = SINGLE_QUOTE_VALUE;
+ else if (c == '\"')
+ state = DOUBLE_QUOTE_VALUE;
+ else if (c == '\\')
+ state = VALUE_ESCAPE;
+ else if (!strchr(WHITESPACE, c)) {
+ state = VALUE;
+
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
+ return -ENOMEM;
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case VALUE:
+ if (strchr(NEWLINE, c)) {
+ state = PRE_KEY;
+ line++;
+
+ key[n_key] = 0;
+
+ if (value)
+ value[n_value] = 0;
+
+ /* Chomp off trailing whitespace from value */
+ if (last_value_whitespace != (size_t) -1)
+ value[last_value_whitespace] = 0;
+
+ /* strip trailing whitespace from key */
+ if (last_key_whitespace != (size_t) -1)
+ key[last_key_whitespace] = 0;
+
+ r = push(fname, line, key, value, userdata, n_pushed);
+ if (r < 0)
+ return r;
+
+ n_key = 0;
+ value = NULL;
+ value_alloc = n_value = 0;
+
+ } else if (c == '\\') {
+ state = VALUE_ESCAPE;
+ last_value_whitespace = (size_t) -1;
+ } else {
+ if (!strchr(WHITESPACE, c))
+ last_value_whitespace = (size_t) -1;
+ else if (last_value_whitespace == (size_t) -1)
+ last_value_whitespace = n_value;
+
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
+ return -ENOMEM;
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case VALUE_ESCAPE:
+ state = VALUE;
+
+ if (!strchr(NEWLINE, c)) {
+ /* Escaped newlines we eat up entirely */
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
+ return -ENOMEM;
+
+ value[n_value++] = c;
+ }
+ break;
+
+ case SINGLE_QUOTE_VALUE:
+ if (c == '\'')
+ state = PRE_VALUE;
+ else if (c == '\\')
+ state = SINGLE_QUOTE_VALUE_ESCAPE;
+ else {
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
+ return -ENOMEM;
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case SINGLE_QUOTE_VALUE_ESCAPE:
+ state = SINGLE_QUOTE_VALUE;
+
+ if (!strchr(NEWLINE, c)) {
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
+ return -ENOMEM;
+
+ value[n_value++] = c;
+ }
+ break;
+
+ case DOUBLE_QUOTE_VALUE:
+ if (c == '\"')
+ state = PRE_VALUE;
+ else if (c == '\\')
+ state = DOUBLE_QUOTE_VALUE_ESCAPE;
+ else {
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
+ return -ENOMEM;
+
+ value[n_value++] = c;
+ }
+
+ break;
+
+ case DOUBLE_QUOTE_VALUE_ESCAPE:
+ state = DOUBLE_QUOTE_VALUE;
+
+ if (!strchr(NEWLINE, c)) {
+ if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
+ return -ENOMEM;
+
+ value[n_value++] = c;
+ }
+ break;
+
+ case COMMENT:
+ if (c == '\\')
+ state = COMMENT_ESCAPE;
+ else if (strchr(NEWLINE, c)) {
+ state = PRE_KEY;
+ line++;
+ }
+ break;
+
+ case COMMENT_ESCAPE:
+ state = COMMENT;
+ break;
+ }
+ }
+
+ if (IN_SET(state,
+ PRE_VALUE,
+ VALUE,
+ VALUE_ESCAPE,
+ SINGLE_QUOTE_VALUE,
+ SINGLE_QUOTE_VALUE_ESCAPE,
+ DOUBLE_QUOTE_VALUE,
+ DOUBLE_QUOTE_VALUE_ESCAPE)) {
+
+ key[n_key] = 0;
+
+ if (value)
+ value[n_value] = 0;
+
+ if (state == VALUE)
+ if (last_value_whitespace != (size_t) -1)
+ value[last_value_whitespace] = 0;
+
+ /* strip trailing whitespace from key */
+ if (last_key_whitespace != (size_t) -1)
+ key[last_key_whitespace] = 0;
+
+ r = push(fname, line, key, value, userdata, n_pushed);
+ if (r < 0)
+ return r;
+
+ value = NULL;
+ }
+
+ return 0;
+}
+
+static int check_utf8ness_and_warn(
+ const char *filename, unsigned line,
+ const char *key, char *value) {
+
+ if (!utf8_is_valid(key)) {
+ _cleanup_free_ char *p = NULL;
+
+ p = utf8_escape_invalid(key);
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s:%u: invalid UTF-8 in key '%s', ignoring.",
+ strna(filename), line, p);
+ }
+
+ if (value && !utf8_is_valid(value)) {
+ _cleanup_free_ char *p = NULL;
+
+ p = utf8_escape_invalid(value);
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
+ strna(filename), line, key, p);
+ }
+
+ return 0;
+}
+
+static int parse_env_file_push(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+
+ const char *k;
+ va_list aq, *ap = userdata;
+ int r;
+
+ r = check_utf8ness_and_warn(filename, line, key, value);
+ if (r < 0)
+ return r;
+
+ va_copy(aq, *ap);
+
+ while ((k = va_arg(aq, const char *))) {
+ char **v;
+
+ v = va_arg(aq, char **);
+
+ if (streq(key, k)) {
+ va_end(aq);
+ free(*v);
+ *v = value;
+
+ if (n_pushed)
+ (*n_pushed)++;
+
+ return 1;
+ }
+ }
+
+ va_end(aq);
+ free(value);
+
+ return 0;
+}
+
+int parse_env_filev(
+ FILE *f,
+ const char *fname,
+ va_list ap) {
+
+ int r, n_pushed = 0;
+ va_list aq;
+
+ va_copy(aq, ap);
+ r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed);
+ va_end(aq);
+ if (r < 0)
+ return r;
+
+ return n_pushed;
+}
+
+int parse_env_file_sentinel(
+ FILE *f,
+ const char *fname,
+ ...) {
+
+ va_list ap;
+ int r;
+
+ va_start(ap, fname);
+ r = parse_env_filev(f, fname, ap);
+ va_end(ap);
+
+ return r;
+}
+
+static int load_env_file_push(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+ char ***m = userdata;
+ char *p;
+ int r;
+
+ r = check_utf8ness_and_warn(filename, line, key, value);
+ if (r < 0)
+ return r;
+
+ p = strjoin(key, "=", value);
+ if (!p)
+ return -ENOMEM;
+
+ r = strv_env_replace(m, p);
+ if (r < 0) {
+ free(p);
+ return r;
+ }
+
+ if (n_pushed)
+ (*n_pushed)++;
+
+ free(value);
+ return 0;
+}
+
+int load_env_file(FILE *f, const char *fname, char ***rl) {
+ char **m = NULL;
+ int r;
+
+ r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL);
+ if (r < 0) {
+ strv_free(m);
+ return r;
+ }
+
+ *rl = m;
+ return 0;
+}
+
+static int load_env_file_push_pairs(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+ char ***m = userdata;
+ int r;
+
+ r = check_utf8ness_and_warn(filename, line, key, value);
+ if (r < 0)
+ return r;
+
+ r = strv_extend(m, key);
+ if (r < 0)
+ return -ENOMEM;
+
+ if (!value) {
+ r = strv_extend(m, "");
+ if (r < 0)
+ return -ENOMEM;
+ } else {
+ r = strv_push(m, value);
+ if (r < 0)
+ return r;
+ }
+
+ if (n_pushed)
+ (*n_pushed)++;
+
+ return 0;
+}
+
+int load_env_file_pairs(FILE *f, const char *fname, char ***rl) {
+ char **m = NULL;
+ int r;
+
+ r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL);
+ if (r < 0) {
+ strv_free(m);
+ return r;
+ }
+
+ *rl = m;
+ return 0;
+}
+
+static int merge_env_file_push(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+
+ char ***env = userdata;
+ char *expanded_value;
+
+ assert(env);
+
+ if (!value) {
+ log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
+ return 0;
+ }
+
+ if (!env_name_is_valid(key)) {
+ log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
+ free(value);
+ return 0;
+ }
+
+ expanded_value = replace_env(value, *env,
+ REPLACE_ENV_USE_ENVIRONMENT|
+ REPLACE_ENV_ALLOW_BRACELESS|
+ REPLACE_ENV_ALLOW_EXTENDED);
+ if (!expanded_value)
+ return -ENOMEM;
+
+ free_and_replace(value, expanded_value);
+
+ return load_env_file_push(filename, line, key, value, env, n_pushed);
+}
+
+int merge_env_file(
+ char ***env,
+ FILE *f,
+ const char *fname) {
+
+ /* NOTE: this function supports braceful and braceless variable expansions,
+ * plus "extended" substitutions, unlike other exported parsing functions.
+ */
+
+ return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL);
+}
+
+static void write_env_var(FILE *f, const char *v) {
+ const char *p;
+
+ p = strchr(v, '=');
+ if (!p) {
+ /* Fallback */
+ fputs_unlocked(v, f);
+ fputc_unlocked('\n', f);
+ return;
+ }
+
+ p++;
+ fwrite_unlocked(v, 1, p-v, f);
+
+ if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
+ fputc_unlocked('\"', f);
+
+ for (; *p; p++) {
+ if (strchr(SHELL_NEED_ESCAPE, *p))
+ fputc_unlocked('\\', f);
+
+ fputc_unlocked(*p, f);
+ }
+
+ fputc_unlocked('\"', f);
+ } else
+ fputs_unlocked(p, f);
+
+ fputc_unlocked('\n', f);
+}
+
+int write_env_file(const char *fname, char **l) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *p = NULL;
+ char **i;
+ int r;
+
+ assert(fname);
+
+ r = fopen_temporary(fname, &f, &p);
+ if (r < 0)
+ return r;
+
+ (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+ (void) fchmod_umask(fileno(f), 0644);
+
+ STRV_FOREACH(i, l)
+ write_env_var(f, *i);
+
+ r = fflush_and_check(f);
+ if (r >= 0) {
+ if (rename(p, fname) >= 0)
+ return 0;
+
+ r = -errno;
+ }
+
+ unlink(p);
+ return r;
+}
diff --git a/src/basic/env-file.h b/src/basic/env-file.h
new file mode 100644
index 0000000000..e1ca195ff0
--- /dev/null
+++ b/src/basic/env-file.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "macro.h"
+
+int parse_env_filev(FILE *f, const char *fname, va_list ap);
+int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_;
+#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL)
+int load_env_file(FILE *f, const char *fname, char ***l);
+int load_env_file_pairs(FILE *f, const char *fname, char ***l);
+
+int merge_env_file(char ***env, FILE *f, const char *fname);
+
+int write_env_file(const char *fname, char **l);
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index a784a30e1d..e494f65c98 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -21,10 +21,6 @@
DIGITS LETTERS \
"_"
-#ifndef ARG_MAX
-#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX))
-#endif
-
static bool env_name_is_valid_n(const char *e, size_t n) {
const char *p;
@@ -42,7 +38,7 @@ static bool env_name_is_valid_n(const char *e, size_t n) {
* either. Discounting the equal sign and trailing NUL this
* hence leaves ARG_MAX-2 as longest possible variable
* name. */
- if (n > ARG_MAX - 2)
+ if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
return false;
for (p = e; p < e + n; p++)
@@ -76,7 +72,7 @@ bool env_value_is_valid(const char *e) {
* either. Discounting the shortest possible variable name of
* length 1, the equal sign and trailing NUL this hence leaves
* ARG_MAX-3 as longest possible variable value. */
- if (strlen(e) > ARG_MAX - 3)
+ if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 3)
return false;
return true;
@@ -99,7 +95,7 @@ bool env_assignment_is_valid(const char *e) {
* be > ARG_MAX, hence the individual variable assignments
* cannot be either, but let's leave room for one trailing NUL
* byte. */
- if (strlen(e) > ARG_MAX - 1)
+ if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 1)
return false;
return true;
@@ -114,7 +110,7 @@ bool strv_env_is_valid(char **e) {
if (!env_assignment_is_valid(*p))
return false;
- /* Check if there are duplicate assginments */
+ /* Check if there are duplicate assignments */
k = strcspn(*p, "=");
STRV_FOREACH(q, p + 1)
if (strneq(*p, *q, k) && (*q)[k] == '=')
@@ -125,30 +121,28 @@ bool strv_env_is_valid(char **e) {
}
bool strv_env_name_is_valid(char **l) {
- char **p, **q;
+ char **p;
STRV_FOREACH(p, l) {
if (!env_name_is_valid(*p))
return false;
- STRV_FOREACH(q, p + 1)
- if (streq(*p, *q))
- return false;
+ if (strv_contains(p + 1, *p))
+ return false;
}
return true;
}
bool strv_env_name_or_assignment_is_valid(char **l) {
- char **p, **q;
+ char **p;
STRV_FOREACH(p, l) {
if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
return false;
- STRV_FOREACH(q, p + 1)
- if (streq(*p, *q))
- return false;
+ if (strv_contains(p + 1, *p))
+ return false;
}
return true;
@@ -157,20 +151,23 @@ bool strv_env_name_or_assignment_is_valid(char **l) {
static int env_append(char **r, char ***k, char **a) {
assert(r);
assert(k);
+ assert(*k >= r);
if (!a)
return 0;
- /* Add the entries of a to *k unless they already exist in *r
- * in which case they are overridden instead. This assumes
- * there is enough space in the r array. */
+ /* Expects the following arguments: 'r' shall point to the beginning of an strv we are going to append to, 'k'
+ * to a pointer pointing to the NULL entry at the end of the same array. 'a' shall point to another strv.
+ *
+ * This call adds every entry of 'a' to 'r', either overriding an existing matching entry, or appending to it.
+ *
+ * This call assumes 'r' has enough pre-allocated space to grow by all of 'a''s items. */
for (; *a; a++) {
- char **j;
+ char **j, *c;
size_t n;
n = strcspn(*a, "=");
-
if ((*a)[n] == '=')
n++;
@@ -178,24 +175,26 @@ static int env_append(char **r, char ***k, char **a) {
if (strneq(*j, *a, n))
break;
- if (j >= *k)
- (*k)++;
- else
- free(*j);
-
- *j = strdup(*a);
- if (!*j)
+ c = strdup(*a);
+ if (!c)
return -ENOMEM;
+
+ if (j >= *k) { /* Append to the end? */
+ (*k)[0] = c;
+ (*k)[1] = NULL;
+ (*k)++;
+ } else
+ free_and_replace(*j, c); /* Override existing item */
}
return 0;
}
char **strv_env_merge(size_t n_lists, ...) {
- size_t n = 0;
- char **l, **k, **r;
+ _cleanup_strv_free_ char **ret = NULL;
+ size_t n = 0, i;
+ char **l, **k;
va_list ap;
- size_t i;
/* Merges an arbitrary number of environment sets */
@@ -206,29 +205,24 @@ char **strv_env_merge(size_t n_lists, ...) {
}
va_end(ap);
- r = new(char*, n+1);
- if (!r)
+ ret = new(char*, n+1);
+ if (!ret)
return NULL;
- k = r;
+ *ret = NULL;
+ k = ret;
va_start(ap, n_lists);
for (i = 0; i < n_lists; i++) {
l = va_arg(ap, char**);
- if (env_append(r, &k, l) < 0)
- goto fail;
+ if (env_append(ret, &k, l) < 0) {
+ va_end(ap);
+ return NULL;
+ }
}
va_end(ap);
- *k = NULL;
-
- return r;
-
-fail:
- va_end(ap);
- strv_free(r);
-
- return NULL;
+ return TAKE_PTR(ret);
}
static bool env_match(const char *t, const char *pattern) {
@@ -382,22 +376,23 @@ char **strv_env_unset_many(char **l, ...) {
}
int strv_env_replace(char ***l, char *p) {
- char **f;
const char *t, *name;
+ char **f;
+ int r;
assert(p);
- /* Replace first occurrence of the env var or add a new one in the
- * string list. Drop other occurences. Edits in-place. Does not copy p.
- * p must be a valid key=value assignment.
+ /* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits
+ * in-place. Does not copy p. p must be a valid key=value assignment.
*/
t = strchr(p, '=');
- assert(t);
+ if (!t)
+ return -EINVAL;
name = strndupa(p, t - p);
- for (f = *l; f && *f; f++)
+ STRV_FOREACH(f, *l)
if (env_entry_has_name(*f, name)) {
free_and_replace(*f, p);
strv_env_unset(f + 1, *f);
@@ -405,33 +400,40 @@ int strv_env_replace(char ***l, char *p) {
}
/* We didn't find a match, we need to append p or create a new strv */
- if (strv_push(l, p) < 0)
- return -ENOMEM;
+ r = strv_push(l, p);
+ if (r < 0)
+ return r;
+
return 1;
}
char **strv_env_set(char **x, const char *p) {
+ _cleanup_strv_free_ char **ret = NULL;
+ size_t n, m;
char **k;
- _cleanup_strv_free_ char **r = NULL;
- char* m[2] = { (char*) p, NULL };
/* Overrides the env var setting of p, returns a new copy */
- r = new(char*, strv_length(x)+2);
- if (!r)
+ n = strv_length(x);
+ m = n + 2;
+ if (m < n) /* overflow? */
return NULL;
- k = r;
- if (env_append(r, &k, x) < 0)
+ ret = new(char*, m);
+ if (!ret)
return NULL;
- if (env_append(r, &k, m) < 0)
+ *ret = NULL;
+ k = ret;
+
+ if (env_append(ret, &k, x) < 0)
return NULL;
- *k = NULL;
+ if (env_append(ret, &k, STRV_MAKE(p)) < 0)
+ return NULL;
- return TAKE_PTR(r);
+ return TAKE_PTR(ret);
}
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
@@ -750,36 +752,3 @@ int getenv_bool_secure(const char *p) {
return parse_boolean(e);
}
-
-int serialize_environment(FILE *f, char **environment) {
- char **e;
-
- STRV_FOREACH(e, environment) {
- _cleanup_free_ char *ce;
-
- ce = cescape(*e);
- if (!ce)
- return -ENOMEM;
-
- fprintf(f, "env=%s\n", ce);
- }
-
- /* caller should call ferror() */
-
- return 0;
-}
-
-int deserialize_environment(char ***environment, const char *line) {
- char *uce;
- int r;
-
- assert(line);
- assert(environment);
-
- assert(startswith(line, "env="));
- r = cunescape(line + 4, 0, &uce);
- if (r < 0)
- return r;
-
- return strv_env_replace(environment, uce);
-}
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
index ef9398e618..4d21ea6bef 100644
--- a/src/basic/env-util.h
+++ b/src/basic/env-util.h
@@ -6,6 +6,7 @@
#include <stdio.h>
#include "macro.h"
+#include "string.h"
bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
@@ -44,6 +45,3 @@ char *strv_env_get(char **x, const char *n) _pure_;
int getenv_bool(const char *p);
int getenv_bool_secure(const char *p);
-
-int serialize_environment(FILE *f, char **environment);
-int deserialize_environment(char ***environment, const char *line);
diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c
index 1b72bbf3ad..44cc570539 100644
--- a/src/basic/errno-list.c
+++ b/src/basic/errno-list.c
@@ -17,7 +17,7 @@ const char *errno_to_name(int id) {
if (id < 0)
id = -id;
- if (id >= (int) ELEMENTSOF(errno_names))
+ if ((size_t) id >= ELEMENTSOF(errno_names))
return NULL;
return errno_names[id];
diff --git a/src/basic/escape.c b/src/basic/escape.c
index 5004763d97..5f715156fb 100644
--- a/src/basic/escape.c
+++ b/src/basic/escape.c
@@ -106,7 +106,6 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit)
int r = 1;
assert(p);
- assert(*p);
assert(ret);
/* Unescapes C style. Returns the unescaped character in ret.
diff --git a/src/basic/escape.h b/src/basic/escape.h
index 2e07c73b9e..515620993d 100644
--- a/src/basic/escape.h
+++ b/src/basic/escape.h
@@ -8,7 +8,7 @@
#include <uchar.h>
#include "string-util.h"
-#include "missing.h"
+#include "missing_type.h"
/* What characters are special in the shell? */
/* must be escaped outside and inside double-quotes */
diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c
index 461ace73e2..e875696a1a 100644
--- a/src/basic/ether-addr-util.c
+++ b/src/basic/ether-addr-util.c
@@ -28,21 +28,15 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR
return buffer;
}
-int ether_addr_compare(const void *a, const void *b) {
- assert(a);
- assert(b);
-
+int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
return memcmp(a, b, ETH_ALEN);
}
-static void ether_addr_hash_func(const void *p, struct siphash *state) {
+static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
siphash24_compress(p, sizeof(struct ether_addr), state);
}
-const struct hash_ops ether_addr_hash_ops = {
- .hash = ether_addr_hash_func,
- .compare = ether_addr_compare
-};
+DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
int ether_addr_from_string(const char *s, struct ether_addr *ret) {
size_t pos = 0, n, field;
diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h
index f12f1215d2..4e44b30be9 100644
--- a/src/basic/ether-addr-util.h
+++ b/src/basic/ether-addr-util.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <net/ethernet.h>
#include <stdbool.h>
@@ -13,7 +12,7 @@
#define ETHER_ADDR_TO_STRING_MAX (3*6)
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
-int ether_addr_compare(const void *a, const void *b);
+int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b);
static inline bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) {
return ether_addr_compare(a, b) == 0;
}
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index e085dc23b4..c06f2fac7e 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -23,6 +23,7 @@
#include "socket-util.h"
#include "stdio-util.h"
#include "util.h"
+#include "tmpfile-util.h"
int close_nointr(int fd) {
assert(fd >= 0);
@@ -113,7 +114,7 @@ FILE* safe_fclose(FILE *f) {
if (f) {
PROTECT_ERRNO;
- assert_se(fclose_nointr(f) != EBADF);
+ assert_se(fclose_nointr(f) != -EBADF);
}
return NULL;
@@ -277,7 +278,7 @@ int same_fd(int a, int b) {
return true;
if (r > 0)
return false;
- if (errno != ENOSYS)
+ if (!IN_SET(errno, ENOSYS, EACCES, EPERM))
return -errno;
/* We don't have kcmp(), use fstat() instead. */
@@ -353,22 +354,22 @@ bool fdname_is_valid(const char *s) {
}
int fd_get_path(int fd, char **ret) {
- _cleanup_close_ int dir = -1;
- char fdname[DECIMAL_STR_MAX(int)];
+ char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
int r;
- dir = open("/proc/self/fd/", O_CLOEXEC | O_DIRECTORY | O_PATH);
- if (dir < 0)
- /* /proc is not available or not set up properly, we're most likely
- * in some chroot environment. */
- return errno == ENOENT ? -EOPNOTSUPP : -errno;
+ xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+ r = readlink_malloc(procfs_path, ret);
+ if (r == -ENOENT) {
+ /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
+ * things debuggable and distuingish the two. */
- xsprintf(fdname, "%i", fd);
+ if (access("/proc/self/fd/", F_OK) < 0)
+ /* /proc is not available or not set up properly, we're most likely in some chroot
+ * environment. */
+ return errno == ENOENT ? -EOPNOTSUPP : -errno;
- r = readlinkat_malloc(dir, fdname, ret);
- if (r == -ENOENT)
- /* If the file doesn't exist the fd is invalid */
- return -EBADF;
+ return -EBADF; /* The directory exists, hence it's the fd that doesn't. */
+ }
return r;
}
@@ -648,7 +649,7 @@ int fd_duplicate_data_fd(int fd) {
if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
- r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size);
+ r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL);
if (r < 0 && r != -EAGAIN)
return r; /* If we get EAGAIN it could be because of the source or because of
* the destination fd, we can't know, as sendfile() and friends won't
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
index 8adc959da8..00303a7e45 100644
--- a/src/basic/fd-util.h
+++ b/src/basic/fd-util.h
@@ -78,8 +78,12 @@ int acquire_data_fd(const void *data, size_t size, unsigned flags);
int fd_duplicate_data_fd(int fd);
/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
-#define ERRNO_IS_DISCONNECT(r) \
- IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
+/* The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases.
+ * See the icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources */
+#define ERRNO_IS_DISCONNECT(r) \
+ IN_SET(r, \
+ ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, \
+ ENETUNREACH, EHOSTUNREACH, ENOPROTOOPT, EHOSTDOWN, ENONET)
/* Resource exhaustion, could be our fault or general system trouble */
#define ERRNO_IS_RESOURCE(r) \
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 6b0bad5b71..e18b842999 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
@@ -8,33 +9,22 @@
#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "alloc-util.h"
-#include "ctype.h"
-#include "def.h"
-#include "env-util.h"
-#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
-#include "hexdecoct.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
-#include "process-util.h"
-#include "random-util.h"
#include "stdio-util.h"
#include "string-util.h"
-#include "strv.h"
-#include "time-util.h"
-#include "umask-util.h"
-#include "utf8.h"
+#include "tmpfile-util.h"
#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
@@ -160,13 +150,13 @@ int write_string_file_ts(
/* We manually build our own version of fopen(..., "we") that
* works without O_CREAT */
- fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
+ fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY | ((flags & WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0));
if (fd < 0) {
r = -errno;
goto fail;
}
- f = fdopen(fd, "we");
+ f = fdopen(fd, "w");
if (!f) {
r = -errno;
safe_close(fd);
@@ -276,16 +266,20 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
return 1;
}
-int read_full_stream(FILE *f, char **contents, size_t *size) {
+int read_full_stream(
+ FILE *f,
+ char **ret_contents,
+ size_t *ret_size) {
+
_cleanup_free_ char *buf = NULL;
struct stat st;
size_t n, l;
int fd;
assert(f);
- assert(contents);
+ assert(ret_contents);
- n = LINE_MAX;
+ n = LINE_MAX; /* Start size */
fd = fileno(f);
if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's
@@ -341,11 +335,20 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
n = MIN(n * 2, READ_FULL_BYTES_MAX);
}
+ if (!ret_size) {
+ /* Safety check: if the caller doesn't want to know the size of what we just read it will rely on the
+ * trailing NUL byte. But if there's an embedded NUL byte, then we should refuse operation as otherwise
+ * there'd be ambiguity about what we just read. */
+
+ if (memchr(buf, 0, l))
+ return -EBADMSG;
+ }
+
buf[l] = 0;
- *contents = TAKE_PTR(buf);
+ *ret_contents = TAKE_PTR(buf);
- if (size)
- *size = l;
+ if (ret_size)
+ *ret_size = l;
return 0;
}
@@ -365,577 +368,6 @@ int read_full_file(const char *fn, char **contents, size_t *size) {
return read_full_stream(f, contents, size);
}
-static int parse_env_file_internal(
- FILE *f,
- const char *fname,
- const char *newline,
- int (*push) (const char *filename, unsigned line,
- const char *key, char *value, void *userdata, int *n_pushed),
- void *userdata,
- int *n_pushed) {
-
- size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
- _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL;
- unsigned line = 1;
- char *p;
- int r;
-
- enum {
- PRE_KEY,
- KEY,
- PRE_VALUE,
- VALUE,
- VALUE_ESCAPE,
- SINGLE_QUOTE_VALUE,
- SINGLE_QUOTE_VALUE_ESCAPE,
- DOUBLE_QUOTE_VALUE,
- DOUBLE_QUOTE_VALUE_ESCAPE,
- COMMENT,
- COMMENT_ESCAPE
- } state = PRE_KEY;
-
- assert(newline);
-
- if (f)
- r = read_full_stream(f, &contents, NULL);
- else
- r = read_full_file(fname, &contents, NULL);
- if (r < 0)
- return r;
-
- for (p = contents; *p; p++) {
- char c = *p;
-
- switch (state) {
-
- case PRE_KEY:
- if (strchr(COMMENTS, c))
- state = COMMENT;
- else if (!strchr(WHITESPACE, c)) {
- state = KEY;
- last_key_whitespace = (size_t) -1;
-
- if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
- return -ENOMEM;
-
- key[n_key++] = c;
- }
- break;
-
- case KEY:
- if (strchr(newline, c)) {
- state = PRE_KEY;
- line++;
- n_key = 0;
- } else if (c == '=') {
- state = PRE_VALUE;
- last_value_whitespace = (size_t) -1;
- } else {
- if (!strchr(WHITESPACE, c))
- last_key_whitespace = (size_t) -1;
- else if (last_key_whitespace == (size_t) -1)
- last_key_whitespace = n_key;
-
- if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
- return -ENOMEM;
-
- key[n_key++] = c;
- }
-
- break;
-
- case PRE_VALUE:
- if (strchr(newline, c)) {
- state = PRE_KEY;
- line++;
- key[n_key] = 0;
-
- if (value)
- value[n_value] = 0;
-
- /* strip trailing whitespace from key */
- if (last_key_whitespace != (size_t) -1)
- key[last_key_whitespace] = 0;
-
- r = push(fname, line, key, value, userdata, n_pushed);
- if (r < 0)
- return r;
-
- n_key = 0;
- value = NULL;
- value_alloc = n_value = 0;
-
- } else if (c == '\'')
- state = SINGLE_QUOTE_VALUE;
- else if (c == '\"')
- state = DOUBLE_QUOTE_VALUE;
- else if (c == '\\')
- state = VALUE_ESCAPE;
- else if (!strchr(WHITESPACE, c)) {
- state = VALUE;
-
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
- return -ENOMEM;
-
- value[n_value++] = c;
- }
-
- break;
-
- case VALUE:
- if (strchr(newline, c)) {
- state = PRE_KEY;
- line++;
-
- key[n_key] = 0;
-
- if (value)
- value[n_value] = 0;
-
- /* Chomp off trailing whitespace from value */
- if (last_value_whitespace != (size_t) -1)
- value[last_value_whitespace] = 0;
-
- /* strip trailing whitespace from key */
- if (last_key_whitespace != (size_t) -1)
- key[last_key_whitespace] = 0;
-
- r = push(fname, line, key, value, userdata, n_pushed);
- if (r < 0)
- return r;
-
- n_key = 0;
- value = NULL;
- value_alloc = n_value = 0;
-
- } else if (c == '\\') {
- state = VALUE_ESCAPE;
- last_value_whitespace = (size_t) -1;
- } else {
- if (!strchr(WHITESPACE, c))
- last_value_whitespace = (size_t) -1;
- else if (last_value_whitespace == (size_t) -1)
- last_value_whitespace = n_value;
-
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
- return -ENOMEM;
-
- value[n_value++] = c;
- }
-
- break;
-
- case VALUE_ESCAPE:
- state = VALUE;
-
- if (!strchr(newline, c)) {
- /* Escaped newlines we eat up entirely */
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
- return -ENOMEM;
-
- value[n_value++] = c;
- }
- break;
-
- case SINGLE_QUOTE_VALUE:
- if (c == '\'')
- state = PRE_VALUE;
- else if (c == '\\')
- state = SINGLE_QUOTE_VALUE_ESCAPE;
- else {
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
- return -ENOMEM;
-
- value[n_value++] = c;
- }
-
- break;
-
- case SINGLE_QUOTE_VALUE_ESCAPE:
- state = SINGLE_QUOTE_VALUE;
-
- if (!strchr(newline, c)) {
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
- return -ENOMEM;
-
- value[n_value++] = c;
- }
- break;
-
- case DOUBLE_QUOTE_VALUE:
- if (c == '\"')
- state = PRE_VALUE;
- else if (c == '\\')
- state = DOUBLE_QUOTE_VALUE_ESCAPE;
- else {
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
- return -ENOMEM;
-
- value[n_value++] = c;
- }
-
- break;
-
- case DOUBLE_QUOTE_VALUE_ESCAPE:
- state = DOUBLE_QUOTE_VALUE;
-
- if (!strchr(newline, c)) {
- if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
- return -ENOMEM;
-
- value[n_value++] = c;
- }
- break;
-
- case COMMENT:
- if (c == '\\')
- state = COMMENT_ESCAPE;
- else if (strchr(newline, c)) {
- state = PRE_KEY;
- line++;
- }
- break;
-
- case COMMENT_ESCAPE:
- state = COMMENT;
- break;
- }
- }
-
- if (IN_SET(state,
- PRE_VALUE,
- VALUE,
- VALUE_ESCAPE,
- SINGLE_QUOTE_VALUE,
- SINGLE_QUOTE_VALUE_ESCAPE,
- DOUBLE_QUOTE_VALUE,
- DOUBLE_QUOTE_VALUE_ESCAPE)) {
-
- key[n_key] = 0;
-
- if (value)
- value[n_value] = 0;
-
- if (state == VALUE)
- if (last_value_whitespace != (size_t) -1)
- value[last_value_whitespace] = 0;
-
- /* strip trailing whitespace from key */
- if (last_key_whitespace != (size_t) -1)
- key[last_key_whitespace] = 0;
-
- r = push(fname, line, key, value, userdata, n_pushed);
- if (r < 0)
- return r;
-
- value = NULL;
- }
-
- return 0;
-}
-
-static int check_utf8ness_and_warn(
- const char *filename, unsigned line,
- const char *key, char *value) {
-
- if (!utf8_is_valid(key)) {
- _cleanup_free_ char *p = NULL;
-
- p = utf8_escape_invalid(key);
- log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
- return -EINVAL;
- }
-
- if (value && !utf8_is_valid(value)) {
- _cleanup_free_ char *p = NULL;
-
- p = utf8_escape_invalid(value);
- log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int parse_env_file_push(
- const char *filename, unsigned line,
- const char *key, char *value,
- void *userdata,
- int *n_pushed) {
-
- const char *k;
- va_list aq, *ap = userdata;
- int r;
-
- r = check_utf8ness_and_warn(filename, line, key, value);
- if (r < 0)
- return r;
-
- va_copy(aq, *ap);
-
- while ((k = va_arg(aq, const char *))) {
- char **v;
-
- v = va_arg(aq, char **);
-
- if (streq(key, k)) {
- va_end(aq);
- free(*v);
- *v = value;
-
- if (n_pushed)
- (*n_pushed)++;
-
- return 1;
- }
- }
-
- va_end(aq);
- free(value);
-
- return 0;
-}
-
-int parse_env_filev(
- FILE *f,
- const char *fname,
- const char *newline,
- va_list ap) {
-
- int r, n_pushed = 0;
- va_list aq;
-
- if (!newline)
- newline = NEWLINE;
-
- va_copy(aq, ap);
- r = parse_env_file_internal(f, fname, newline, parse_env_file_push, &aq, &n_pushed);
- va_end(aq);
- if (r < 0)
- return r;
-
- return n_pushed;
-}
-
-int parse_env_file(
- FILE *f,
- const char *fname,
- const char *newline,
- ...) {
-
- va_list ap;
- int r;
-
- va_start(ap, newline);
- r = parse_env_filev(f, fname, newline, ap);
- va_end(ap);
-
- return r;
-}
-
-static int load_env_file_push(
- const char *filename, unsigned line,
- const char *key, char *value,
- void *userdata,
- int *n_pushed) {
- char ***m = userdata;
- char *p;
- int r;
-
- r = check_utf8ness_and_warn(filename, line, key, value);
- if (r < 0)
- return r;
-
- p = strjoin(key, "=", value);
- if (!p)
- return -ENOMEM;
-
- r = strv_env_replace(m, p);
- if (r < 0) {
- free(p);
- return r;
- }
-
- if (n_pushed)
- (*n_pushed)++;
-
- free(value);
- return 0;
-}
-
-int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
- char **m = NULL;
- int r;
-
- if (!newline)
- newline = NEWLINE;
-
- r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
- if (r < 0) {
- strv_free(m);
- return r;
- }
-
- *rl = m;
- return 0;
-}
-
-static int load_env_file_push_pairs(
- const char *filename, unsigned line,
- const char *key, char *value,
- void *userdata,
- int *n_pushed) {
- char ***m = userdata;
- int r;
-
- r = check_utf8ness_and_warn(filename, line, key, value);
- if (r < 0)
- return r;
-
- r = strv_extend(m, key);
- if (r < 0)
- return -ENOMEM;
-
- if (!value) {
- r = strv_extend(m, "");
- if (r < 0)
- return -ENOMEM;
- } else {
- r = strv_push(m, value);
- if (r < 0)
- return r;
- }
-
- if (n_pushed)
- (*n_pushed)++;
-
- return 0;
-}
-
-int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
- char **m = NULL;
- int r;
-
- if (!newline)
- newline = NEWLINE;
-
- r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
- if (r < 0) {
- strv_free(m);
- return r;
- }
-
- *rl = m;
- return 0;
-}
-
-static int merge_env_file_push(
- const char *filename, unsigned line,
- const char *key, char *value,
- void *userdata,
- int *n_pushed) {
-
- char ***env = userdata;
- char *expanded_value;
-
- assert(env);
-
- if (!value) {
- log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
- return 0;
- }
-
- if (!env_name_is_valid(key)) {
- log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
- free(value);
- return 0;
- }
-
- expanded_value = replace_env(value, *env,
- REPLACE_ENV_USE_ENVIRONMENT|
- REPLACE_ENV_ALLOW_BRACELESS|
- REPLACE_ENV_ALLOW_EXTENDED);
- if (!expanded_value)
- return -ENOMEM;
-
- free_and_replace(value, expanded_value);
-
- return load_env_file_push(filename, line, key, value, env, n_pushed);
-}
-
-int merge_env_file(
- char ***env,
- FILE *f,
- const char *fname) {
-
- /* NOTE: this function supports braceful and braceless variable expansions,
- * plus "extended" substitutions, unlike other exported parsing functions.
- */
-
- return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL);
-}
-
-static void write_env_var(FILE *f, const char *v) {
- const char *p;
-
- p = strchr(v, '=');
- if (!p) {
- /* Fallback */
- fputs_unlocked(v, f);
- fputc_unlocked('\n', f);
- return;
- }
-
- p++;
- fwrite_unlocked(v, 1, p-v, f);
-
- if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
- fputc_unlocked('\"', f);
-
- for (; *p; p++) {
- if (strchr(SHELL_NEED_ESCAPE, *p))
- fputc_unlocked('\\', f);
-
- fputc_unlocked(*p, f);
- }
-
- fputc_unlocked('\"', f);
- } else
- fputs_unlocked(p, f);
-
- fputc_unlocked('\n', f);
-}
-
-int write_env_file(const char *fname, char **l) {
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *p = NULL;
- char **i;
- int r;
-
- assert(fname);
-
- r = fopen_temporary(fname, &f, &p);
- if (r < 0)
- return r;
-
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
- (void) fchmod_umask(fileno(f), 0644);
-
- STRV_FOREACH(i, l)
- write_env_var(f, *i);
-
- r = fflush_and_check(f);
- if (r >= 0) {
- if (rename(p, fname) >= 0)
- return 0;
-
- r = -errno;
- }
-
- unlink(p);
- return r;
-}
-
int executable_is_script(const char *path, char **interpreter) {
_cleanup_free_ char *line = NULL;
size_t len;
@@ -1141,39 +573,6 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root
return search_and_fopen_internal(path, mode, root, s, _f);
}
-int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
- FILE *f;
- char *t;
- int r, fd;
-
- assert(path);
- assert(_f);
- assert(_temp_path);
-
- r = tempfn_xxxxxx(path, NULL, &t);
- if (r < 0)
- return r;
-
- fd = mkostemp_safe(t);
- if (fd < 0) {
- free(t);
- return -errno;
- }
-
- f = fdopen(fd, "we");
- if (!f) {
- unlink_noerrno(t);
- free(t);
- safe_close(fd);
- return -errno;
- }
-
- *_f = f;
- *_temp_path = t;
-
- return 0;
-}
-
int fflush_and_check(FILE *f) {
assert(f);
@@ -1205,134 +604,6 @@ int fflush_sync_and_check(FILE *f) {
return 0;
}
-/* This is much like mkostemp() but is subject to umask(). */
-int mkostemp_safe(char *pattern) {
- _cleanup_umask_ mode_t u = 0;
- int fd;
-
- assert(pattern);
-
- u = umask(077);
-
- fd = mkostemp(pattern, O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- return fd;
-}
-
-int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
- const char *fn;
- char *t;
-
- assert(p);
- assert(ret);
-
- /*
- * Turns this:
- * /foo/bar/waldo
- *
- * Into this:
- * /foo/bar/.#<extra>waldoXXXXXX
- */
-
- fn = basename(p);
- if (!filename_is_valid(fn))
- return -EINVAL;
-
- extra = strempty(extra);
-
- t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
- if (!t)
- return -ENOMEM;
-
- strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
-
- *ret = path_simplify(t, false);
- return 0;
-}
-
-int tempfn_random(const char *p, const char *extra, char **ret) {
- const char *fn;
- char *t, *x;
- uint64_t u;
- unsigned i;
-
- assert(p);
- assert(ret);
-
- /*
- * Turns this:
- * /foo/bar/waldo
- *
- * Into this:
- * /foo/bar/.#<extra>waldobaa2a261115984a9
- */
-
- fn = basename(p);
- if (!filename_is_valid(fn))
- return -EINVAL;
-
- extra = strempty(extra);
-
- t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
- if (!t)
- return -ENOMEM;
-
- x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
-
- u = random_u64();
- for (i = 0; i < 16; i++) {
- *(x++) = hexchar(u & 0xF);
- u >>= 4;
- }
-
- *x = 0;
-
- *ret = path_simplify(t, false);
- return 0;
-}
-
-int tempfn_random_child(const char *p, const char *extra, char **ret) {
- char *t, *x;
- uint64_t u;
- unsigned i;
- int r;
-
- assert(ret);
-
- /* Turns this:
- * /foo/bar/waldo
- * Into this:
- * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
- */
-
- if (!p) {
- r = tmp_dir(&p);
- if (r < 0)
- return r;
- }
-
- extra = strempty(extra);
-
- t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
- if (!t)
- return -ENOMEM;
-
- x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
-
- u = random_u64();
- for (i = 0; i < 16; i++) {
- *(x++) = hexchar(u & 0xF);
- u >>= 4;
- }
-
- *x = 0;
-
- *ret = path_simplify(t, false);
- return 0;
-}
-
int write_timestamp_file_atomic(const char *fn, usec_t n) {
char ln[DECIMAL_STR_MAX(n)+2];
@@ -1396,203 +667,53 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
return fputs(s, f);
}
-int open_tmpfile_unlinkable(const char *directory, int flags) {
- char *p;
- int fd, r;
+/* A bitmask of the EOL markers we know */
+typedef enum EndOfLineMarker {
+ EOL_NONE = 0,
+ EOL_ZERO = 1 << 0, /* \0 (aka NUL) */
+ EOL_TEN = 1 << 1, /* \n (aka NL, aka LF) */
+ EOL_THIRTEEN = 1 << 2, /* \r (aka CR) */
+} EndOfLineMarker;
- if (!directory) {
- r = tmp_dir(&directory);
- if (r < 0)
- return r;
- }
-
- /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
-
- /* Try O_TMPFILE first, if it is supported */
- fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
- if (fd >= 0)
- return fd;
-
- /* Fall back to unguessable name + unlinking */
- p = strjoina(directory, "/systemd-tmp-XXXXXX");
-
- fd = mkostemp_safe(p);
- if (fd < 0)
- return fd;
-
- (void) unlink(p);
+static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) {
- return fd;
-}
-
-int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
- _cleanup_free_ char *tmp = NULL;
- int r, fd;
-
- assert(target);
- assert(ret_path);
-
- /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
- assert((flags & O_EXCL) == 0);
-
- /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
- * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
- * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
-
- {
- _cleanup_free_ char *dn = NULL;
-
- dn = dirname_malloc(target);
- if (!dn)
- return -ENOMEM;
-
- fd = open(dn, O_TMPFILE|flags, 0640);
- if (fd >= 0) {
- *ret_path = NULL;
- return fd;
- }
-
- log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
+ if (!IN_SET(flags, READ_LINE_ONLY_NUL)) {
+ if (c == '\n')
+ return EOL_TEN;
+ if (c == '\r')
+ return EOL_THIRTEEN;
}
- r = tempfn_random(target, NULL, &tmp);
- if (r < 0)
- return r;
-
- fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
- if (fd < 0)
- return -errno;
-
- *ret_path = TAKE_PTR(tmp);
-
- return fd;
-}
-
-int open_serialization_fd(const char *ident) {
- int fd = -1;
+ if (c == '\0')
+ return EOL_ZERO;
- fd = memfd_create(ident, MFD_CLOEXEC);
- if (fd < 0) {
- const char *path;
-
- path = getpid_cached() == 1 ? "/run/systemd" : "/tmp";
- fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- log_debug("Serializing %s to %s.", ident, path);
- } else
- log_debug("Serializing %s to memfd.", ident);
-
- return fd;
-}
-
-int link_tmpfile(int fd, const char *path, const char *target) {
-
- assert(fd >= 0);
- assert(target);
-
- /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
- * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
- * on the directory, and renameat2() is used instead.
- *
- * Note that in both cases we will not replace existing files. This is because linkat() does not support this
- * operation currently (renameat2() does), and there is no nice way to emulate this. */
-
- if (path) {
- if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0)
- return -errno;
- } else {
- char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
-
- xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
-
- if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
- return -errno;
- }
-
- return 0;
-}
-
-int read_nul_string(FILE *f, char **ret) {
- _cleanup_free_ char *x = NULL;
- size_t allocated = 0, n = 0;
-
- assert(f);
- assert(ret);
-
- /* Reads a NUL-terminated string from the specified file. */
-
- for (;;) {
- int c;
-
- if (!GREEDY_REALLOC(x, allocated, n+2))
- return -ENOMEM;
-
- c = fgetc(f);
- if (c == 0) /* Terminate at NUL byte */
- break;
- if (c == EOF) {
- if (ferror(f))
- return -errno;
- break; /* Terminate at EOF */
- }
-
- x[n++] = (char) c;
- }
-
- if (x)
- x[n] = 0;
- else {
- x = new0(char, 1);
- if (!x)
- return -ENOMEM;
- }
-
- *ret = TAKE_PTR(x);
-
- return 0;
-}
-
-int mkdtemp_malloc(const char *template, char **ret) {
- _cleanup_free_ char *p = NULL;
- int r;
-
- assert(ret);
-
- if (template)
- p = strdup(template);
- else {
- const char *tmp;
-
- r = tmp_dir(&tmp);
- if (r < 0)
- return r;
-
- p = strjoin(tmp, "/XXXXXX");
- }
- if (!p)
- return -ENOMEM;
-
- if (!mkdtemp(p))
- return -errno;
-
- *ret = TAKE_PTR(p);
- return 0;
+ return EOL_NONE;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile);
-int read_line(FILE *f, size_t limit, char **ret) {
- _cleanup_free_ char *buffer = NULL;
+int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
size_t n = 0, allocated = 0, count = 0;
+ _cleanup_free_ char *buffer = NULL;
+ int r;
assert(f);
/* Something like a bounded version of getline().
*
- * Considers EOF, \n and \0 end of line delimiters, and does not include these delimiters in the string
- * returned.
+ * Considers EOF, \n, \r and \0 end of line delimiters (or combinations of these), and does not include these
+ * delimiters in the string returned. Specifically, recognizes the following combinations of markers as line
+ * endings:
+ *
+ * • \n (UNIX)
+ * • \r (old MacOS)
+ * • \0 (C strings)
+ * • \n\0
+ * • \r\0
+ * • \r\n (Windows)
+ * • \n\r
+ * • \r\n\0
+ * • \n\r\0
*
* Returns the number of bytes read from the files (i.e. including delimiters — this hence usually differs from
* the number of characters in the returned string). When EOF is hit, 0 is returned.
@@ -1609,34 +730,55 @@ int read_line(FILE *f, size_t limit, char **ret) {
{
_unused_ _cleanup_(funlockfilep) FILE *flocked = f;
+ EndOfLineMarker previous_eol = EOL_NONE;
flockfile(f);
for (;;) {
- int c;
+ EndOfLineMarker eol;
+ char c;
if (n >= limit)
return -ENOBUFS;
- errno = 0;
- c = fgetc_unlocked(f);
- if (c == EOF) {
- /* if we read an error, and have no data to return, then propagate the error */
- if (ferror_unlocked(f) && n == 0)
- return errno > 0 ? -errno : -EIO;
+ if (count >= INT_MAX) /* We couldn't return the counter anymore as "int", hence refuse this */
+ return -ENOBUFS;
+ r = safe_fgetc(f, &c);
+ if (r < 0)
+ return r;
+ if (r == 0) /* EOF is definitely EOL */
+ break;
+
+ eol = categorize_eol(c, flags);
+
+ if (FLAGS_SET(previous_eol, EOL_ZERO) ||
+ (eol == EOL_NONE && previous_eol != EOL_NONE) ||
+ (eol != EOL_NONE && (previous_eol & eol) != 0)) {
+ /* Previous char was a NUL? This is not an EOL, but the previous char was? This type of
+ * EOL marker has been seen right before? In either of these three cases we are
+ * done. But first, let's put this character back in the queue. (Note that we have to
+ * cast this to (unsigned char) here as ungetc() expects a positive 'int', and if we
+ * are on an architecture where 'char' equals 'signed char' we need to ensure we don't
+ * pass a negative value here. That said, to complicate things further ungetc() is
+ * actually happy with most negative characters and implicitly casts them back to
+ * positive ones as needed, except for \xff (aka -1, aka EOF), which it refuses. What a
+ * godawful API!) */
+ assert_se(ungetc((unsigned char) c, f) != EOF);
break;
}
count++;
- if (IN_SET(c, '\n', 0)) /* Reached a delimiter */
- break;
+ if (eol != EOL_NONE) {
+ previous_eol |= eol;
+ continue;
+ }
if (ret) {
if (!GREEDY_REALLOC(buffer, allocated, n + 2))
return -ENOMEM;
- buffer[n] = (char) c;
+ buffer[n] = c;
}
n++;
@@ -1651,3 +793,30 @@ int read_line(FILE *f, size_t limit, char **ret) {
return (int) count;
}
+
+int safe_fgetc(FILE *f, char *ret) {
+ int k;
+
+ assert(f);
+
+ /* A safer version of plain fgetc(): let's propagate the error that happened while reading as such, and
+ * separate the EOF condition from the byte read, to avoid those confusion signed/unsigned issues fgetc()
+ * has. */
+
+ errno = 0;
+ k = fgetc(f);
+ if (k == EOF) {
+ if (ferror(f))
+ return errno > 0 ? -errno : -EIO;
+
+ if (ret)
+ *ret = 0;
+
+ return 0;
+ }
+
+ if (ret)
+ *ret = k;
+
+ return 1;
+}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 77e6206e95..53e3f4ef5f 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -10,6 +10,8 @@
#include "macro.h"
#include "time-util.h"
+#define LONG_LINE_MAX (1U*1024U*1024U)
+
typedef enum {
WRITE_STRING_FILE_CREATE = 1 << 0,
WRITE_STRING_FILE_ATOMIC = 1 << 1,
@@ -17,6 +19,7 @@ typedef enum {
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 3,
WRITE_STRING_FILE_SYNC = 1 << 4,
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5,
+ WRITE_STRING_FILE_NOFOLLOW = 1 << 6,
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
@@ -41,15 +44,6 @@ int read_full_stream(FILE *f, char **contents, size_t *size);
int verify_file(const char *fn, const char *blob, bool accept_extra_nl);
-int parse_env_filev(FILE *f, const char *fname, const char *separator, va_list ap);
-int parse_env_file(FILE *f, const char *fname, const char *separator, ...) _sentinel_;
-int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
-int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
-
-int merge_env_file(char ***env, FILE *f, const char *fname);
-
-int write_env_file(const char *fname, char **l);
-
int executable_is_script(const char *path, char **interpreter);
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
@@ -59,38 +53,26 @@ DIR *xopendirat(int dirfd, const char *name, int flags);
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
-#define FOREACH_LINE(line, f, on_error) \
- for (;;) \
- if (!fgets(line, sizeof(line), f)) { \
- if (ferror(f)) { \
- on_error; \
- } \
- break; \
- } else
-
int fflush_and_check(FILE *f);
int fflush_sync_and_check(FILE *f);
-int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
-int mkostemp_safe(char *pattern);
-
-int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
-int tempfn_random(const char *p, const char *extra, char **ret);
-int tempfn_random_child(const char *p, const char *extra, char **ret);
-
int write_timestamp_file_atomic(const char *fn, usec_t n);
int read_timestamp_file(const char *fn, usec_t *ret);
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
-int open_tmpfile_unlinkable(const char *directory, int flags);
-int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
-int open_serialization_fd(const char *ident);
+typedef enum ReadLineFlags {
+ READ_LINE_ONLY_NUL = 1 << 0,
+} ReadLineFlags;
-int link_tmpfile(int fd, const char *path, const char *target);
+int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret);
-int read_nul_string(FILE *f, char **ret);
+static inline int read_line(FILE *f, size_t limit, char **ret) {
+ return read_line_full(f, limit, 0, ret);
+}
-int mkdtemp_malloc(const char *template, char **ret);
+static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
+ return read_line_full(f, limit, READ_LINE_ONLY_NUL, ret);
+}
-int read_line(FILE *f, size_t limit, char **ret);
+int safe_fgetc(FILE *f, char *ret);
diff --git a/src/basic/format-util.h b/src/basic/format-util.h
index 160550cd68..dece5d3a67 100644
--- a/src/basic/format-util.h
+++ b/src/basic/format-util.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <inttypes.h>
#if SIZEOF_PID_T == 4
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 3a8b32d881..f25bf2cbbc 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -13,8 +13,8 @@
#include "alloc-util.h"
#include "dirent-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
+#include "locale-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
@@ -27,6 +27,7 @@
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
@@ -89,47 +90,50 @@ int rmdir_parents(const char *path, const char *stop) {
}
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
- struct stat buf;
- int ret;
+ int r;
- ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
- if (ret >= 0)
+ /* Try the ideal approach first */
+ if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0)
return 0;
- /* renameat2() exists since Linux 3.15, btrfs added support for it later.
- * If it is not implemented, fallback to another method. */
- if (!IN_SET(errno, EINVAL, ENOSYS))
+ /* renameat2() exists since Linux 3.15, btrfs and FAT added support for it later. If it is not implemented,
+ * fall back to a different method. */
+ if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY))
return -errno;
- /* The link()/unlink() fallback does not work on directories. But
- * renameat() without RENAME_NOREPLACE gives the same semantics on
- * directories, except when newpath is an *empty* directory. This is
- * good enough. */
- ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
- if (ret >= 0 && S_ISDIR(buf.st_mode)) {
- ret = renameat(olddirfd, oldpath, newdirfd, newpath);
- return ret >= 0 ? 0 : -errno;
+ /* Let's try to use linkat()+unlinkat() as fallback. This doesn't work on directories and on some file systems
+ * that do not support hard links (such as FAT, most prominently), but for files it's pretty close to what we
+ * want — though not atomic (i.e. for a short period both the new and the old filename will exist). */
+ if (linkat(olddirfd, oldpath, newdirfd, newpath, 0) >= 0) {
+
+ if (unlinkat(olddirfd, oldpath, 0) < 0) {
+ r = -errno; /* Backup errno before the following unlinkat() alters it */
+ (void) unlinkat(newdirfd, newpath, 0);
+ return r;
+ }
+
+ return 0;
}
- /* If it is not a directory, use the link()/unlink() fallback. */
- ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
- if (ret < 0)
+ if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM)) /* FAT returns EPERM on link()… */
return -errno;
- ret = unlinkat(olddirfd, oldpath, 0);
- if (ret < 0) {
- /* backup errno before the following unlinkat() alters it */
- ret = errno;
- (void) unlinkat(newdirfd, newpath, 0);
- errno = ret;
+ /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fallback to the racy TOCTOU
+ * vulnerable accessat(F_OK) check followed by classic, replacing renameat(), we have nothing better. */
+
+ if (faccessat(newdirfd, newpath, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
+ return -EEXIST;
+ if (errno != ENOENT)
+ return -errno;
+
+ if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0)
return -errno;
- }
return 0;
}
int readlinkat_malloc(int fd, const char *p, char **ret) {
- size_t l = 100;
+ size_t l = FILENAME_MAX+1;
int r;
assert(p);
@@ -208,31 +212,62 @@ 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) {
+ char fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+ _cleanup_close_ int fd = -1;
assert(path);
- /* Under the assumption that we are running privileged we
- * first change the access mode and only then hand out
+ /* Under the assumption that we are running privileged we first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
- if (mode != MODE_INVALID)
- if (chmod(path, mode) < 0)
+ fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change mode/owner
+ * on the same file */
+ if (fd < 0)
+ return -errno;
+
+ xsprintf(fd_path, "/proc/self/fd/%i", fd);
+
+ if (mode != MODE_INVALID) {
+
+ if ((mode & S_IFMT) != 0) {
+ struct stat st;
+
+ if (stat(fd_path, &st) < 0)
+ return -errno;
+
+ if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
+ return -EINVAL;
+ }
+
+ if (chmod(fd_path, mode & 07777) < 0)
return -errno;
+ }
if (uid != UID_INVALID || gid != GID_INVALID)
- if (chown(path, uid, gid) < 0)
+ if (chown(fd_path, uid, gid) < 0)
return -errno;
return 0;
}
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
- /* Under the assumption that we are running privileged we
- * first change the access mode and only then hand out
+ /* Under the assumption that we are running privileged we first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
- if (mode != MODE_INVALID)
- if (fchmod(fd, mode) < 0)
+ if (mode != MODE_INVALID) {
+
+ if ((mode & S_IFMT) != 0) {
+ struct stat st;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
+ return -EINVAL;
+ }
+
+ if (fchmod(fd, mode & 0777) < 0)
return -errno;
+ }
if (uid != UID_INVALID || gid != GID_INVALID)
if (fchown(fd, uid, gid) < 0)
@@ -260,7 +295,6 @@ int fchmod_opath(int fd, mode_t m) {
* fchownat() does. */
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-
if (chmod(procfs_path, m) < 0)
return -errno;
@@ -346,12 +380,27 @@ int touch(const char *path) {
return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
}
-int symlink_idempotent(const char *from, const char *to) {
+int symlink_idempotent(const char *from, const char *to, bool make_relative) {
+ _cleanup_free_ char *relpath = NULL;
int r;
assert(from);
assert(to);
+ if (make_relative) {
+ _cleanup_free_ char *parent = NULL;
+
+ parent = dirname_malloc(to);
+ if (!parent)
+ return -ENOMEM;
+
+ r = path_make_relative(parent, from, &relpath);
+ if (r < 0)
+ return r;
+
+ from = relpath;
+ }
+
if (symlink(from, to) < 0) {
_cleanup_free_ char *p = NULL;
@@ -435,6 +484,31 @@ int mkfifo_atomic(const char *path, mode_t mode) {
return 0;
}
+int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(path);
+
+ if (path_is_absolute(path))
+ return mkfifo_atomic(path, mode);
+
+ /* We're only interested in the (random) filename. */
+ r = tempfn_random_child("", NULL, &t);
+ if (r < 0)
+ return r;
+
+ if (mkfifoat(dirfd, t, mode) < 0)
+ return -errno;
+
+ if (renameat(dirfd, t, dirfd, path) < 0) {
+ unlink_noerrno(t);
+ return -errno;
+ }
+
+ return 0;
+}
+
int get_files_in_directory(const char *path, char ***list) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
@@ -590,15 +664,42 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
return r;
}
-static bool safe_transition(const struct stat *a, const struct stat *b) {
+static bool unsafe_transition(const struct stat *a, const struct stat *b) {
/* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
* privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
* making us believe we read something safe even though it isn't safe in the specific context we open it in. */
if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
- return true;
+ return false;
+
+ return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */
+}
+
+static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
+ _cleanup_free_ char *n1 = NULL, *n2 = NULL;
+
+ if (!FLAGS_SET(flags, CHASE_WARN))
+ return -ENOLINK;
- return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
+ (void) fd_get_path(a, &n1);
+ (void) fd_get_path(b, &n2);
+
+ return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
+ "Detected unsafe path transition %s %s %s during canonicalization of %s.",
+ n1, special_glyph(SPECIAL_GLYPH_ARROW), n2, path);
+}
+
+static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
+ _cleanup_free_ char *n1 = NULL;
+
+ if (!FLAGS_SET(flags, CHASE_WARN))
+ return -EREMOTE;
+
+ (void) fd_get_path(fd, &n1);
+
+ return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE),
+ "Detected autofs mount point %s during canonicalization of %s.",
+ n1, path);
}
int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
@@ -661,6 +762,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
* path is fully normalized, and == 0 for each normalization step. This may be combined with
* CHASE_NONEXISTENT, in which case 1 is returned when a component is not found.
*
+ * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from
+ * unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If
+ * CHASE_WARN is also set a warning describing the unsafe transition is emitted.
+ *
+ * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, the path normalization is
+ * aborted and -EREMOTE is returned. If CHASE_WARN is also set a warning showing the path of the mount point
+ * is emitted.
+ *
* */
/* A root directory of "/" or "" is identical to none */
@@ -670,7 +779,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN|CHASE_STEP)) == CHASE_OPEN) {
/* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set
* and doesn't care about any of the other special features we provide either. */
- r = open(path, O_PATH|O_CLOEXEC);
+ r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
if (r < 0)
return -errno;
@@ -775,8 +884,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fstat(fd_parent, &st) < 0)
return -errno;
- if (!safe_transition(&previous_stat, &st))
- return -EPERM;
+ if (unsafe_transition(&previous_stat, &st))
+ return log_unsafe_transition(fd, fd_parent, path, flags);
previous_stat = st;
}
@@ -816,16 +925,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fstat(child, &st) < 0)
return -errno;
if ((flags & CHASE_SAFE) &&
- !safe_transition(&previous_stat, &st))
- return -EPERM;
+ unsafe_transition(&previous_stat, &st))
+ return log_unsafe_transition(fd, child, path, flags);
previous_stat = st;
if ((flags & CHASE_NO_AUTOFS) &&
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
- return -EREMOTE;
+ return log_autofs_mount_point(child, path, flags);
- if (S_ISLNK(st.st_mode)) {
+ if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
char *joined;
_cleanup_free_ char *destination = NULL;
@@ -855,8 +964,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fstat(fd, &st) < 0)
return -errno;
- if (!safe_transition(&previous_stat, &st))
- return -EPERM;
+ if (unsafe_transition(&previous_stat, &st))
+ return log_unsafe_transition(child, fd, path, flags);
previous_stat = st;
}
@@ -1130,7 +1239,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
return 0;
if (fstat(truncate_fd, &st) < 0) {
- log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring.", name);
+ log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name);
return 0;
}
@@ -1156,7 +1265,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
}
int fsync_directory_of_file(int fd) {
- _cleanup_free_ char *path = NULL, *dn = NULL;
+ _cleanup_free_ char *path = NULL;
_cleanup_close_ int dfd = -1;
int r;
@@ -1182,16 +1291,68 @@ int fsync_directory_of_file(int fd) {
if (!path_is_absolute(path))
return -EINVAL;
- dn = dirname_malloc(path);
- if (!dn)
- return -ENOMEM;
-
- dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ dfd = open_parent(path, O_CLOEXEC, 0);
if (dfd < 0)
- return -errno;
+ return dfd;
if (fsync(dfd) < 0)
return -errno;
return 0;
}
+
+int fsync_path_at(int at_fd, const char *path) {
+ _cleanup_close_ int opened_fd = -1;
+ int fd;
+
+ if (isempty(path)) {
+ if (at_fd == AT_FDCWD) {
+ opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+ if (opened_fd < 0)
+ return -errno;
+
+ fd = opened_fd;
+ } else
+ fd = at_fd;
+ } else {
+
+ opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC);
+ if (opened_fd < 0)
+ return -errno;
+
+ fd = opened_fd;
+ }
+
+ if (fsync(fd) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int open_parent(const char *path, int flags, mode_t mode) {
+ _cleanup_free_ char *parent = NULL;
+ int fd;
+
+ if (isempty(path))
+ return -EINVAL;
+ if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */
+ return -EINVAL;
+
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
+
+ /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an
+ * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */
+
+ if ((flags & O_PATH) == O_PATH)
+ flags |= O_DIRECTORY;
+ else if ((flags & O_TMPFILE) != O_TMPFILE)
+ flags |= O_DIRECTORY|O_RDONLY;
+
+ fd = open(parent, flags, mode);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 28566773c6..7ad030be5d 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -37,11 +37,12 @@ int fd_warn_permissions(const char *path, int fd);
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
int touch(const char *path);
-int symlink_idempotent(const char *from, const char *to);
+int symlink_idempotent(const char *from, const char *to, bool make_relative);
int symlink_atomic(const char *from, const char *to);
int mknod_atomic(const char *path, mode_t mode, dev_t dev);
int mkfifo_atomic(const char *path, mode_t mode);
+int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode);
int get_files_in_directory(const char *path, char ***list);
@@ -72,6 +73,8 @@ enum {
CHASE_OPEN = 1 << 4, /* If set, return an O_PATH object to the final component */
CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */
CHASE_STEP = 1 << 6, /* If set, just execute a single step of the normalization */
+ CHASE_NOFOLLOW = 1 << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */
+ CHASE_WARN = 1 << 8, /* Emit an appropriate warning when an error is encountered */
};
/* How many iterations to execute before returning -ELOOP */
@@ -103,3 +106,6 @@ void unlink_tempfilep(char (*p)[]);
int unlinkat_deallocate(int fd, const char *name, int flags);
int fsync_directory_of_file(int fd);
+int fsync_path_at(int at_fd, const char *path);
+
+int open_parent(const char *path, int flags, mode_t mode);
diff --git a/src/basic/generate-af-list.sh b/src/basic/generate-af-list.sh
index 39e2dad5e7..5bf244c49d 100755
--- a/src/basic/generate-af-list.sh
+++ b/src/basic/generate-af-list.sh
@@ -1,6 +1,6 @@
#!/bin/sh
set -eu
-$1 -E -dM -include sys/socket.h - </dev/null | \
+$1 -E -dM -include sys/socket.h -include "$2" -include "$3" - </dev/null | \
grep -Ev 'AF_UNSPEC|AF_MAX' | \
awk '/^#define[ \t]+AF_[^ \t]+[ \t]+[AP]F_[^ \t]/ { print $2; }'
diff --git a/src/basic/generate-arphrd-list.sh b/src/basic/generate-arphrd-list.sh
index e4e7271c4d..e6e874a8fd 100755
--- a/src/basic/generate-arphrd-list.sh
+++ b/src/basic/generate-arphrd-list.sh
@@ -1,6 +1,6 @@
#!/bin/sh
set -eu
-$1 -dM -include net/if_arp.h - </dev/null | \
+$1 -dM -include net/if_arp.h -include "$2" -include "$3" - </dev/null | \
awk '/^#define[ \t]+ARPHRD_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \
sed -e 's/ARPHRD_//'
diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c
index db48437be7..1be43d41a9 100644
--- a/src/basic/hash-funcs.c
+++ b/src/basic/hash-funcs.c
@@ -5,21 +5,13 @@
#include "hash-funcs.h"
#include "path-util.h"
-void string_hash_func(const void *p, struct siphash *state) {
+void string_hash_func(const char *p, struct siphash *state) {
siphash24_compress(p, strlen(p) + 1, state);
}
-int string_compare_func(const void *a, const void *b) {
- return strcmp(a, b);
-}
-
-const struct hash_ops string_hash_ops = {
- .hash = string_hash_func,
- .compare = string_compare_func
-};
+DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func);
-void path_hash_func(const void *p, struct siphash *state) {
- const char *q = p;
+void path_hash_func(const char *q, struct siphash *state) {
size_t n;
assert(q);
@@ -57,58 +49,43 @@ void path_hash_func(const void *p, struct siphash *state) {
}
}
-int path_compare_func(const void *a, const void *b) {
+int path_compare_func(const char *a, const char *b) {
return path_compare(a, b);
}
-const struct hash_ops path_hash_ops = {
- .hash = path_hash_func,
- .compare = path_compare_func
-};
+DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare_func);
void trivial_hash_func(const void *p, struct siphash *state) {
siphash24_compress(&p, sizeof(p), state);
}
int trivial_compare_func(const void *a, const void *b) {
- return a < b ? -1 : (a > b ? 1 : 0);
+ return CMP(a, b);
}
const struct hash_ops trivial_hash_ops = {
.hash = trivial_hash_func,
- .compare = trivial_compare_func
+ .compare = trivial_compare_func,
};
-void uint64_hash_func(const void *p, struct siphash *state) {
+void uint64_hash_func(const uint64_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(uint64_t), state);
}
-int uint64_compare_func(const void *_a, const void *_b) {
- uint64_t a, b;
- a = *(const uint64_t*) _a;
- b = *(const uint64_t*) _b;
- return a < b ? -1 : (a > b ? 1 : 0);
+int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
+ return CMP(*a, *b);
}
-const struct hash_ops uint64_hash_ops = {
- .hash = uint64_hash_func,
- .compare = uint64_compare_func
-};
+DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func);
#if SIZEOF_DEV_T != 8
-void devt_hash_func(const void *p, struct siphash *state) {
+void devt_hash_func(const dev_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(dev_t), state);
}
-int devt_compare_func(const void *_a, const void *_b) {
- dev_t a, b;
- a = *(const dev_t*) _a;
- b = *(const dev_t*) _b;
- return a < b ? -1 : (a > b ? 1 : 0);
+int devt_compare_func(const dev_t *a, const dev_t *b) {
+ return CMP(*a, *b);
}
-const struct hash_ops devt_hash_ops = {
- .hash = devt_hash_func,
- .compare = devt_compare_func
-};
+DEFINE_HASH_OPS(devt_hash_ops, dev_t, devt_hash_func, devt_compare_func);
#endif
diff --git a/src/basic/hash-funcs.h b/src/basic/hash-funcs.h
index 5e5989f021..3d2ae4b55e 100644
--- a/src/basic/hash-funcs.h
+++ b/src/basic/hash-funcs.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
+#include "alloc-util.h"
#include "macro.h"
#include "siphash24.h"
@@ -11,14 +11,74 @@ typedef int (*compare_func_t)(const void *a, const void *b);
struct hash_ops {
hash_func_t hash;
compare_func_t compare;
+ free_func_t free_key;
+ free_func_t free_value;
};
-void string_hash_func(const void *p, struct siphash *state);
-int string_compare_func(const void *a, const void *b) _pure_;
+#define _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, free_key_func, free_value_func, scope) \
+ _unused_ static void (* UNIQ_T(static_hash_wrapper, uq))(const type *, struct siphash *) = hash_func; \
+ _unused_ static int (* UNIQ_T(static_compare_wrapper, uq))(const type *, const type *) = compare_func; \
+ scope const struct hash_ops name = { \
+ .hash = (hash_func_t) hash_func, \
+ .compare = (compare_func_t) compare_func, \
+ .free_key = free_key_func, \
+ .free_value = free_value_func, \
+ }
+
+#define _DEFINE_FREE_FUNC(uq, type, wrapper_name, func) \
+ /* Type-safe free function */ \
+ static void UNIQ_T(wrapper_name, uq)(void *a) { \
+ type *_a = a; \
+ func(_a); \
+ }
+
+#define _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(uq, name, type, hash_func, compare_func, free_func, scope) \
+ _DEFINE_FREE_FUNC(uq, type, static_free_wrapper, free_func); \
+ _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \
+ UNIQ_T(static_free_wrapper, uq), NULL, scope)
+
+#define _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(uq, name, type, hash_func, compare_func, type_value, free_func, scope) \
+ _DEFINE_FREE_FUNC(uq, type_value, static_free_wrapper, free_func); \
+ _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \
+ NULL, UNIQ_T(static_free_wrapper, uq), scope)
+
+#define _DEFINE_HASH_OPS_FULL(uq, name, type, hash_func, compare_func, free_key_func, type_value, free_value_func, scope) \
+ _DEFINE_FREE_FUNC(uq, type, static_free_key_wrapper, free_key_func); \
+ _DEFINE_FREE_FUNC(uq, type_value, static_free_value_wrapper, free_value_func); \
+ _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \
+ UNIQ_T(static_free_key_wrapper, uq), \
+ UNIQ_T(static_free_value_wrapper, uq), scope)
+
+#define DEFINE_HASH_OPS(name, type, hash_func, compare_func) \
+ _DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL,)
+
+#define DEFINE_PRIVATE_HASH_OPS(name, type, hash_func, compare_func) \
+ _DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL, static)
+
+#define DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \
+ _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func,)
+
+#define DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \
+ _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func, static)
+
+#define DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \
+ _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func,)
+
+#define DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \
+ _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func, static)
+
+#define DEFINE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \
+ _DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func,)
+
+#define DEFINE_PRIVATE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \
+ _DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func, static)
+
+void string_hash_func(const char *p, struct siphash *state);
+#define string_compare_func strcmp
extern const struct hash_ops string_hash_ops;
-void path_hash_func(const void *p, struct siphash *state);
-int path_compare_func(const void *a, const void *b) _pure_;
+void path_hash_func(const char *p, struct siphash *state);
+int path_compare_func(const char *a, const char *b) _pure_;
extern const struct hash_ops path_hash_ops;
/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings
@@ -29,15 +89,15 @@ extern const struct hash_ops trivial_hash_ops;
/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
* values indirectly, since they don't fit in a pointer. */
-void uint64_hash_func(const void *p, struct siphash *state);
-int uint64_compare_func(const void *a, const void *b) _pure_;
+void uint64_hash_func(const uint64_t *p, struct siphash *state);
+int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_;
extern const struct hash_ops uint64_hash_ops;
/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on
* 64bit archs. Yuck! */
#if SIZEOF_DEV_T != 8
-void devt_hash_func(const void *p, struct siphash *state) _pure_;
-int devt_compare_func(const void *a, const void *b) _pure_;
+void devt_hash_func(const dev_t *p, struct siphash *state) _pure_;
+int devt_compare_func(const dev_t *a, const dev_t *b) _pure_;
extern const struct hash_ops devt_hash_ops;
#else
#define devt_hash_func uint64_hash_func
diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c
index 69a7d70b04..5a4bb37b82 100644
--- a/src/basic/hashmap.c
+++ b/src/basic/hashmap.c
@@ -6,8 +6,8 @@
#include <string.h>
#include "alloc-util.h"
-#include "hashmap.h"
#include "fileio.h"
+#include "hashmap.h"
#include "macro.h"
#include "mempool.h"
#include "process-util.h"
@@ -276,7 +276,7 @@ static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
};
#if VALGRIND
-__attribute__((destructor)) static void cleanup_pools(void) {
+_destructor_ static void cleanup_pools(void) {
_cleanup_free_ char *t = NULL;
int r;
@@ -769,18 +769,17 @@ static void reset_direct_storage(HashmapBase *h) {
static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
HashmapBase *h;
const struct hashmap_type_info *hi = &hashmap_type_info[type];
- bool use_pool;
-
- use_pool = is_main_thread();
+ bool up;
- h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
+ up = mempool_enabled();
+ h = up ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
if (!h)
return NULL;
h->type = type;
- h->from_pool = use_pool;
- h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
+ h->from_pool = up;
+ h->hash_ops = hash_ops ?: &trivial_hash_ops;
if (type == HASHMAP_TYPE_ORDERED) {
OrderedHashmap *lh = (OrderedHashmap*)h;
@@ -849,7 +848,7 @@ int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASH
static void hashmap_free_no_clear(HashmapBase *h) {
assert(!h->has_indirect);
- assert(!h->n_direct_entries);
+ assert(h->n_direct_entries == 0);
#if ENABLE_DEBUG_HASHMAP
assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
@@ -857,52 +856,49 @@ static void hashmap_free_no_clear(HashmapBase *h) {
assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0);
#endif
- if (h->from_pool)
+ if (h->from_pool) {
+ /* Ensure that the object didn't get migrated between threads. */
+ assert_se(is_main_thread());
mempool_free_tile(hashmap_type_info[h->type].mempool, h);
- else
+ } else
free(h);
}
-HashmapBase *internal_hashmap_free(HashmapBase *h) {
-
- /* Free the hashmap, but nothing in it */
-
+HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
if (h) {
- internal_hashmap_clear(h);
+ internal_hashmap_clear(h, default_free_key, default_free_value);
hashmap_free_no_clear(h);
}
return NULL;
}
-HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
-
- /* Free the hashmap and all data objects in it, but not the
- * keys */
+void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
+ free_func_t free_key, free_value;
+ if (!h)
+ return;
- if (h) {
- internal_hashmap_clear_free(h);
- hashmap_free_no_clear(h);
- }
+ free_key = h->hash_ops->free_key ?: default_free_key;
+ free_value = h->hash_ops->free_value ?: default_free_value;
- return NULL;
-}
+ if (free_key || free_value) {
-Hashmap *hashmap_free_free_free(Hashmap *h) {
+ /* If destructor calls are defined, let's destroy things defensively: let's take the item out of the
+ * hash table, and only then call the destructor functions. If these destructors then try to unregister
+ * themselves from our hash table a second time, the entry is already gone. */
- /* Free the hashmap and all data and key objects in it */
+ while (internal_hashmap_size(h) > 0) {
+ void *v, *k;
- if (h) {
- hashmap_clear_free_free(h);
- hashmap_free_no_clear(HASHMAP_BASE(h));
- }
+ v = internal_hashmap_first_key_and_value(h, true, &k);
- return NULL;
-}
+ if (free_key)
+ free_key(k);
-void internal_hashmap_clear(HashmapBase *h) {
- if (!h)
- return;
+ if (free_value)
+ free_value(v);
+ }
+ }
if (h->has_indirect) {
free(h->indirect.storage);
@@ -920,35 +916,6 @@ void internal_hashmap_clear(HashmapBase *h) {
base_set_dirty(h);
}
-void internal_hashmap_clear_free(HashmapBase *h) {
- unsigned idx;
-
- if (!h)
- return;
-
- for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
- idx = skip_free_buckets(h, idx + 1))
- free(entry_value(h, bucket_at(h, idx)));
-
- internal_hashmap_clear(h);
-}
-
-void hashmap_clear_free_free(Hashmap *h) {
- unsigned idx;
-
- if (!h)
- return;
-
- for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
- idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
- struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
- free((void*)e->b.key);
- free(e->value);
- }
-
- internal_hashmap_clear(HASHMAP_BASE(h));
-}
-
static int resize_buckets(HashmapBase *h, unsigned entries_add);
/*
@@ -1512,8 +1479,8 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_
return 0;
}
-void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
- struct plain_hashmap_entry *e;
+void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value) {
+ struct hashmap_base_entry *e;
unsigned hash, idx;
if (!h)
@@ -1524,8 +1491,8 @@ void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
if (idx == IDX_NIL)
return NULL;
- e = plain_bucket_at(h, idx);
- if (e->value != value)
+ e = bucket_at(h, idx);
+ if (entry_value(h, e) != value)
return NULL;
remove_entry(h, idx);
@@ -1542,18 +1509,9 @@ static unsigned find_first_entry(HashmapBase *h) {
return hashmap_iterate_entry(h, &i);
}
-void *internal_hashmap_first(HashmapBase *h) {
- unsigned idx;
-
- idx = find_first_entry(h);
- if (idx == IDX_NIL)
- return NULL;
-
- return entry_value(h, bucket_at(h, idx));
-}
-
-void *internal_hashmap_first_key(HashmapBase *h) {
+void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) {
struct hashmap_base_entry *e;
+ void *key, *data;
unsigned idx;
idx = find_first_entry(h);
@@ -1561,39 +1519,16 @@ void *internal_hashmap_first_key(HashmapBase *h) {
return NULL;
e = bucket_at(h, idx);
- return (void*) e->key;
-}
-
-void *internal_hashmap_steal_first(HashmapBase *h) {
- struct hashmap_base_entry *e;
- void *data;
- unsigned idx;
-
- idx = find_first_entry(h);
- if (idx == IDX_NIL)
- return NULL;
-
- e = bucket_at(h, idx);
+ key = (void*) e->key;
data = entry_value(h, e);
- remove_entry(h, idx);
- return data;
-}
+ if (remove)
+ remove_entry(h, idx);
-void *internal_hashmap_steal_first_key(HashmapBase *h) {
- struct hashmap_base_entry *e;
- void *key;
- unsigned idx;
+ if (ret_key)
+ *ret_key = key;
- idx = find_first_entry(h);
- if (idx == IDX_NIL)
- return NULL;
-
- e = bucket_at(h, idx);
- key = (void*) e->key;
- remove_entry(h, idx);
-
- return key;
+ return data;
}
unsigned internal_hashmap_size(HashmapBase *h) {
@@ -1771,7 +1706,7 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) {
}
if (r < 0) {
- internal_hashmap_free(copy);
+ internal_hashmap_free(copy, false, false);
return NULL;
}
diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h
index 5c70c102d7..5bf807a76f 100644
--- a/src/basic/hashmap.h
+++ b/src/basic/hashmap.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
@@ -16,13 +15,15 @@
* necessary to instantiate an object for each Hashmap use.
*
* If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
- * the implemention will:
+ * the implementation will:
* - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
* - perform extra checks for invalid use of iterators
*/
#define HASH_KEY_SIZE 16
+typedef void* (*hashmap_destroy_t)(void *p);
+
/* The base type for all hashmap and set types. Many functions in the
* implementation take (HashmapBase*) parameters and are run-time polymorphic,
* though the API is not meant to be polymorphic (do not call functions
@@ -88,25 +89,33 @@ OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HA
#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
-HashmapBase *internal_hashmap_free(HashmapBase *h);
+HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
static inline Hashmap *hashmap_free(Hashmap *h) {
- return (void*)internal_hashmap_free(HASHMAP_BASE(h));
+ return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
}
static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
- return (void*)internal_hashmap_free(HASHMAP_BASE(h));
+ return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
}
-HashmapBase *internal_hashmap_free_free(HashmapBase *h);
static inline Hashmap *hashmap_free_free(Hashmap *h) {
- return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
+ return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
}
static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
- return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
+ return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
+}
+
+static inline Hashmap *hashmap_free_free_key(Hashmap *h) {
+ return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
+}
+static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) {
+ return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
}
-Hashmap *hashmap_free_free_free(Hashmap *h);
+static inline Hashmap *hashmap_free_free_free(Hashmap *h) {
+ return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
+}
static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
- return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
+ return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
}
IteratedCache *iterated_cache_free(IteratedCache *cache);
@@ -182,7 +191,11 @@ static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key,
return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
}
-void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
+void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value);
+static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
+ return internal_hashmap_remove_value(HASHMAP_BASE(h), key, value);
+}
+
static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
}
@@ -259,25 +272,33 @@ static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void
return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
}
-void internal_hashmap_clear(HashmapBase *h);
+void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
static inline void hashmap_clear(Hashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h));
+ internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
}
static inline void ordered_hashmap_clear(OrderedHashmap *h) {
- internal_hashmap_clear(HASHMAP_BASE(h));
+ internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
}
-void internal_hashmap_clear_free(HashmapBase *h);
static inline void hashmap_clear_free(Hashmap *h) {
- internal_hashmap_clear_free(HASHMAP_BASE(h));
+ internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
}
static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
- internal_hashmap_clear_free(HASHMAP_BASE(h));
+ internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
}
-void hashmap_clear_free_free(Hashmap *h);
+static inline void hashmap_clear_free_key(Hashmap *h) {
+ internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
+}
+static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) {
+ internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
+}
+
+static inline void hashmap_clear_free_free(Hashmap *h) {
+ internal_hashmap_clear(HASHMAP_BASE(h), free, free);
+}
static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
- hashmap_clear_free_free(PLAIN_HASHMAP(h));
+ internal_hashmap_clear(HASHMAP_BASE(h), free, free);
}
/*
@@ -291,36 +312,51 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
* the first entry is O(1).
*/
-void *internal_hashmap_steal_first(HashmapBase *h);
+void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key);
+static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) {
+ return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
+}
+static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) {
+ return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
+}
+static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) {
+ return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
+}
+static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) {
+ return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
+}
+
+
static inline void *hashmap_steal_first(Hashmap *h) {
- return internal_hashmap_steal_first(HASHMAP_BASE(h));
+ return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
}
static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
- return internal_hashmap_steal_first(HASHMAP_BASE(h));
+ return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
+}
+static inline void *hashmap_first(Hashmap *h) {
+ return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
+}
+static inline void *ordered_hashmap_first(OrderedHashmap *h) {
+ return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
}
-void *internal_hashmap_steal_first_key(HashmapBase *h);
+static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) {
+ void *key = NULL;
+
+ (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key);
+ return key;
+}
static inline void *hashmap_steal_first_key(Hashmap *h) {
- return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+ return internal_hashmap_first_key(HASHMAP_BASE(h), true);
}
static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
- return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+ return internal_hashmap_first_key(HASHMAP_BASE(h), true);
}
-
-void *internal_hashmap_first_key(HashmapBase *h) _pure_;
static inline void *hashmap_first_key(Hashmap *h) {
- return internal_hashmap_first_key(HASHMAP_BASE(h));
+ return internal_hashmap_first_key(HASHMAP_BASE(h), false);
}
static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
- return internal_hashmap_first_key(HASHMAP_BASE(h));
-}
-
-void *internal_hashmap_first(HashmapBase *h) _pure_;
-static inline void *hashmap_first(Hashmap *h) {
- return internal_hashmap_first(HASHMAP_BASE(h));
-}
-static inline void *ordered_hashmap_first(OrderedHashmap *h) {
- return internal_hashmap_first(HASHMAP_BASE(h));
+ return internal_hashmap_first_key(HASHMAP_BASE(h), false);
}
#define hashmap_clear_with_destructor(_s, _f) \
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
index 7748e8352c..c0f96409fd 100644
--- a/src/basic/hexdecoct.c
+++ b/src/basic/hexdecoct.c
@@ -79,7 +79,7 @@ static int unhex_next(const char **p, size_t *l) {
assert(l);
/* Find the next non-whitespace character, and decode it. We
- * greedily skip all preceeding and all following whitespace. */
+ * greedily skip all preceding and all following whitespace. */
for (;;) {
if (*l == 0)
@@ -592,8 +592,7 @@ static int base64_append_width(
_cleanup_free_ char *x = NULL;
char *t, *s;
- ssize_t slen, len, avail;
- int line, lines;
+ ssize_t len, slen, avail, line, lines;
len = base64mem(p, l, &x);
if (len <= 0)
@@ -602,6 +601,9 @@ static int base64_append_width(
lines = DIV_ROUND_UP(len, width);
slen = strlen_ptr(sep);
+ if (lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1))
+ return -ENOMEM;
+
t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines);
if (!t)
return -ENOMEM;
@@ -647,7 +649,7 @@ static int unbase64_next(const char **p, size_t *l) {
assert(l);
/* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We
- * greedily skip all preceeding and all following whitespace. */
+ * greedily skip all preceding and all following whitespace. */
for (;;) {
if (*l == 0)
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index 09fabe077b..5bfa028b39 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -8,7 +8,6 @@
#include <unistd.h>
#include "alloc-util.h"
-#include "def.h"
#include "fd-util.h"
#include "fileio.h"
#include "hostname-util.h"
@@ -70,12 +69,12 @@ int gethostname_strict(char **ret) {
return 0;
}
-static bool hostname_valid_char(char c) {
+bool valid_ldh_char(char c) {
return
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
- IN_SET(c, '-', '_', '.');
+ c == '-';
}
/**
@@ -91,7 +90,7 @@ static bool hostname_valid_char(char c) {
bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
unsigned n_dots = 0;
const char *p;
- bool dot;
+ bool dot, hyphen;
if (isempty(s))
return false;
@@ -101,23 +100,34 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
* sequence. Also ensures that the length stays below
* HOST_NAME_MAX. */
- for (p = s, dot = true; *p; p++) {
+ for (p = s, dot = hyphen = true; *p; p++)
if (*p == '.') {
- if (dot)
+ if (dot || hyphen)
return false;
dot = true;
+ hyphen = false;
n_dots++;
+
+ } else if (*p == '-') {
+ if (dot)
+ return false;
+
+ dot = false;
+ hyphen = true;
+
} else {
- if (!hostname_valid_char(*p))
+ if (!valid_ldh_char(*p))
return false;
dot = false;
+ hyphen = false;
}
- }
if (dot && (n_dots < 2 || !allow_trailing_dot))
return false;
+ if (hyphen)
+ return false;
if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
* Linux, but DNS allows domain names
@@ -129,29 +139,38 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
char* hostname_cleanup(char *s) {
char *p, *d;
- bool dot;
+ bool dot, hyphen;
assert(s);
- strshorten(s, HOST_NAME_MAX);
-
- for (p = s, d = s, dot = true; *p; p++) {
+ for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++)
if (*p == '.') {
- if (dot)
+ if (dot || hyphen)
continue;
*(d++) = '.';
dot = true;
- } else if (hostname_valid_char(*p)) {
+ hyphen = false;
+
+ } else if (*p == '-') {
+ if (dot)
+ continue;
+
+ *(d++) = '-';
+ dot = false;
+ hyphen = true;
+
+ } else if (valid_ldh_char(*p)) {
*(d++) = *p;
dot = false;
+ hyphen = false;
}
- }
- if (dot && d > s)
- d[-1] = 0;
- else
- *d = 0;
+ if (d > s && IN_SET(d[-1], '-', '.'))
+ /* The dot can occur at most once, but we might have multiple
+ * hyphens, hence the loop */
+ d--;
+ *d = 0;
return s;
}
diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h
index 749481723d..7ba386a0fd 100644
--- a/src/basic/hostname-util.h
+++ b/src/basic/hostname-util.h
@@ -11,6 +11,7 @@ bool hostname_is_set(void);
char* gethostname_malloc(void);
int gethostname_strict(char **ret);
+bool valid_ldh_char(char c) _const_;
bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_;
char* hostname_cleanup(char *s);
diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c
index aed7601d50..411efb242b 100644
--- a/src/basic/in-addr-util.c
+++ b/src/basic/in-addr-util.c
@@ -314,6 +314,7 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union
}
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) {
+ _cleanup_free_ char *buf = NULL;
const char *suffix;
int r, ifi = 0;
@@ -341,7 +342,11 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u
}
}
- s = strndupa(s, suffix - s);
+ buf = strndup(s, suffix - s);
+ if (!buf)
+ return -ENOMEM;
+
+ s = buf;
}
r = in_addr_from_string_auto(s, family, ret);
@@ -357,7 +362,7 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
assert(addr);
- return 32 - u32ctz(be32toh(addr->s_addr));
+ return 32U - u32ctz(be32toh(addr->s_addr));
}
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
@@ -490,12 +495,14 @@ int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) {
return 0;
}
-int in_addr_prefix_from_string(
+int in_addr_prefix_from_string_internal(
const char *p,
+ bool use_default_prefixlen,
int family,
union in_addr_union *ret_prefix,
unsigned char *ret_prefixlen) {
+ _cleanup_free_ char *str = NULL;
union in_addr_union buffer;
const char *e, *l;
unsigned char k;
@@ -507,9 +514,13 @@ int in_addr_prefix_from_string(
return -EAFNOSUPPORT;
e = strchr(p, '/');
- if (e)
- l = strndupa(p, e - p);
- else
+ if (e) {
+ str = strndup(p, e - p);
+ if (!str)
+ return -ENOMEM;
+
+ l = str;
+ } else
l = p;
r = in_addr_from_string(family, l, &buffer);
@@ -520,6 +531,13 @@ int in_addr_prefix_from_string(
r = in_addr_parse_prefixlen(family, e+1, &k);
if (r < 0)
return r;
+ } else if (use_default_prefixlen) {
+ if (family == AF_INET) {
+ r = in4_addr_default_prefixlen(&buffer.in, &k);
+ if (r < 0)
+ return r;
+ } else
+ k = 0;
} else
k = FAMILY_ADDRESS_SIZE(family) * 8;
@@ -531,12 +549,14 @@ int in_addr_prefix_from_string(
return 0;
}
-int in_addr_prefix_from_string_auto(
+int in_addr_prefix_from_string_auto_internal(
const char *p,
+ bool use_default_prefixlen,
int *ret_family,
union in_addr_union *ret_prefix,
unsigned char *ret_prefixlen) {
+ _cleanup_free_ char *str = NULL;
union in_addr_union buffer;
const char *e, *l;
unsigned char k;
@@ -545,9 +565,13 @@ int in_addr_prefix_from_string_auto(
assert(p);
e = strchr(p, '/');
- if (e)
- l = strndupa(p, e - p);
- else
+ if (e) {
+ str = strndup(p, e - p);
+ if (!str)
+ return -ENOMEM;
+
+ l = str;
+ } else
l = p;
r = in_addr_from_string_auto(l, &family, &buffer);
@@ -558,6 +582,13 @@ int in_addr_prefix_from_string_auto(
r = in_addr_parse_prefixlen(family, e+1, &k);
if (r < 0)
return r;
+ } else if (use_default_prefixlen) {
+ if (family == AF_INET) {
+ r = in4_addr_default_prefixlen(&buffer.in, &k);
+ if (r < 0)
+ return r;
+ } else
+ k = 0;
} else
k = FAMILY_ADDRESS_SIZE(family) * 8;
@@ -571,3 +602,20 @@ int in_addr_prefix_from_string_auto(
return 0;
}
+
+static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
+ siphash24_compress(&a->family, sizeof(a->family), state);
+ siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
+}
+
+static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
+ int r;
+
+ r = CMP(x->family, y->family);
+ if (r != 0)
+ return r;
+
+ return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
+}
+
+DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
index 956c00a850..5de85cc422 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -5,6 +5,7 @@
#include <stddef.h>
#include <sys/socket.h>
+#include "hash-funcs.h"
#include "macro.h"
#include "util.h"
@@ -44,12 +45,28 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
-int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
-int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
+int in_addr_prefix_from_string_internal(const char *p, bool use_default_prefixlen, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
+int in_addr_prefix_from_string_auto_internal(const char *p, bool use_default_prefixlen, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
+static inline int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
+ return in_addr_prefix_from_string_internal(p, false, family, ret_prefix, ret_prefixlen);
+}
+static inline int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
+ return in_addr_prefix_from_string_auto_internal(p, false, ret_family, ret_prefix, ret_prefixlen);
+}
+static inline int in_addr_default_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
+ return in_addr_prefix_from_string_internal(p, true, family, ret_prefix, ret_prefixlen);
+}
+static inline int in_addr_default_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
+ return in_addr_prefix_from_string_auto_internal(p, true, ret_family, ret_prefix, ret_prefixlen);
+}
static inline size_t FAMILY_ADDRESS_SIZE(int family) {
assert(IN_SET(family, AF_INET, AF_INET6));
return family == AF_INET6 ? 16 : 4;
}
-#define IN_ADDR_NULL ((union in_addr_union) {})
+/* Workaround for clang, explicitly specify the maximum-size element here.
+ * See also oss-fuzz#11344. */
+#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
+
+extern const struct hash_ops in_addr_data_hash_ops;
diff --git a/src/basic/label.h b/src/basic/label.h
index 08fd109bcf..594fd65974 100644
--- a/src/basic/label.h
+++ b/src/basic/label.h
@@ -12,6 +12,7 @@ typedef enum LabelFixFlags {
int label_fix(const char *path, LabelFixFlags flags);
int mkdir_label(const char *path, mode_t mode);
+int mkdirat_label(int dirfd, const char *path, mode_t mode);
int symlink_label(const char *old_path, const char *new_path);
int btrfs_subvol_make_label(const char *path);
diff --git a/src/basic/list.h b/src/basic/list.h
index 643e0bea88..f7f97000e0 100644
--- a/src/basic/list.h
+++ b/src/basic/list.h
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include "macro.h"
+
/* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */
#define LIST_HEAD(t,name) \
@@ -13,8 +15,8 @@
/* Initialize the list's head */
#define LIST_HEAD_INIT(head) \
do { \
- (head) = NULL; } \
- while (false)
+ (head) = NULL; \
+ } while (false)
/* Initialize a list item */
#define LIST_INIT(name,item) \
@@ -38,9 +40,9 @@
/* Append an item to the list */
#define LIST_APPEND(name,head,item) \
do { \
- typeof(*(head)) *_tail; \
- LIST_FIND_TAIL(name,head,_tail); \
- LIST_INSERT_AFTER(name,head,_tail,item); \
+ typeof(*(head)) **_hhead = &(head), *_tail; \
+ LIST_FIND_TAIL(name, *_hhead, _tail); \
+ LIST_INSERT_AFTER(name, *_hhead, _tail, item); \
} while (false)
/* Remove an item from the list */
diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c
index 3ad352f22f..fc1577a83f 100644
--- a/src/basic/locale-util.c
+++ b/src/basic/locale-util.c
@@ -16,6 +16,7 @@
#include "def.h"
#include "dirent-util.h"
+#include "env-util.h"
#include "fd-util.h"
#include "hashmap.h"
#include "locale-util.h"
@@ -347,6 +348,24 @@ bool keymap_is_valid(const char *name) {
return true;
}
+static bool emoji_enabled(void) {
+ static int cached_emoji_enabled = -1;
+
+ if (cached_emoji_enabled < 0) {
+ int val;
+
+ val = getenv_bool("SYSTEMD_EMOJI");
+ if (val < 0)
+ cached_emoji_enabled =
+ is_locale_utf8() &&
+ !STRPTR_IN_SET(getenv("TERM"), "dumb", "linux");
+ else
+ cached_emoji_enabled = val;
+ }
+
+ return cached_emoji_enabled;
+}
+
const char *special_glyph(SpecialGlyph code) {
/* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be
@@ -359,32 +378,66 @@ const char *special_glyph(SpecialGlyph code) {
static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = {
/* ASCII fallback */
[false] = {
- [TREE_VERTICAL] = "| ",
- [TREE_BRANCH] = "|-",
- [TREE_RIGHT] = "`-",
- [TREE_SPACE] = " ",
- [TRIANGULAR_BULLET] = ">",
- [BLACK_CIRCLE] = "*",
- [ARROW] = "->",
- [MDASH] = "-",
- [ELLIPSIS] = "..."
+ [SPECIAL_GLYPH_TREE_VERTICAL] = "| ",
+ [SPECIAL_GLYPH_TREE_BRANCH] = "|-",
+ [SPECIAL_GLYPH_TREE_RIGHT] = "`-",
+ [SPECIAL_GLYPH_TREE_SPACE] = " ",
+ [SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">",
+ [SPECIAL_GLYPH_BLACK_CIRCLE] = "*",
+ [SPECIAL_GLYPH_BULLET] = "*",
+ [SPECIAL_GLYPH_ARROW] = "->",
+ [SPECIAL_GLYPH_MDASH] = "-",
+ [SPECIAL_GLYPH_ELLIPSIS] = "...",
+ [SPECIAL_GLYPH_MU] = "u",
+ [SPECIAL_GLYPH_CHECK_MARK] = "+",
+ [SPECIAL_GLYPH_CROSS_MARK] = "-",
+ [SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]",
+ [SPECIAL_GLYPH_HAPPY_SMILEY] = ":-}",
+ [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)",
+ [SPECIAL_GLYPH_NEUTRAL_SMILEY] = ":-|",
+ [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
+ [SPECIAL_GLYPH_UNHAPPY_SMILEY] = ":-{ï¸",
+ [SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[",
},
/* UTF-8 */
[true] = {
- [TREE_VERTICAL] = "\342\224\202 ", /* │ */
- [TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
- [TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
- [TREE_SPACE] = " ", /* */
- [TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */
- [BLACK_CIRCLE] = "\342\227\217", /* â— */
- [ARROW] = "\342\206\222", /* → */
- [MDASH] = "\342\200\223", /* – */
- [ELLIPSIS] = "\342\200\246", /* … */
+ [SPECIAL_GLYPH_TREE_VERTICAL] = "\342\224\202 ", /* │ */
+ [SPECIAL_GLYPH_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
+ [SPECIAL_GLYPH_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
+ [SPECIAL_GLYPH_TREE_SPACE] = " ", /* */
+ [SPECIAL_GLYPH_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */
+ [SPECIAL_GLYPH_BLACK_CIRCLE] = "\342\227\217", /* â— */
+ [SPECIAL_GLYPH_BULLET] = "\342\200\242", /* • */
+ [SPECIAL_GLYPH_ARROW] = "\342\206\222", /* → */
+ [SPECIAL_GLYPH_MDASH] = "\342\200\223", /* – */
+ [SPECIAL_GLYPH_ELLIPSIS] = "\342\200\246", /* … */
+ [SPECIAL_GLYPH_MU] = "\316\274", /* μ */
+ [SPECIAL_GLYPH_CHECK_MARK] = "\342\234\223", /* ✓ */
+ [SPECIAL_GLYPH_CROSS_MARK] = "\342\234\227", /* ✗ */
+ [SPECIAL_GLYPH_ECSTATIC_SMILEY] = "\360\237\230\207", /* 😇 */
+ [SPECIAL_GLYPH_HAPPY_SMILEY] = "\360\237\230\200", /* 😀 */
+ [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = "\360\237\231\202", /* 🙂 */
+ [SPECIAL_GLYPH_NEUTRAL_SMILEY] = "\360\237\230\220", /* 😠*/
+ [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = "\360\237\231\201", /* 🙠*/
+ [SPECIAL_GLYPH_UNHAPPY_SMILEY] = "\360\237\230\250", /* 😨ï¸ï¸ */
+ [SPECIAL_GLYPH_DEPRESSED_SMILEY] = "\360\237\244\242", /* 🤢 */
},
};
- return draw_table[is_locale_utf8()][code];
+ assert(code < _SPECIAL_GLYPH_MAX);
+
+ return draw_table[code >= _SPECIAL_GLYPH_FIRST_SMILEY ? emoji_enabled() : is_locale_utf8()][code];
+}
+
+void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {
+ LocaleVariable i;
+
+ if (!l)
+ return;
+
+ for (i = 0; i < _VARIABLE_LC_MAX; i++)
+ l[i] = mfree(l[i]);
}
static const char * const locale_variable_table[_VARIABLE_LC_MAX] = {
diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h
index 775fe8bc72..e64f0ce15c 100644
--- a/src/basic/locale-util.h
+++ b/src/basic/locale-util.h
@@ -39,15 +39,27 @@ void init_gettext(void);
bool is_locale_utf8(void);
typedef enum {
- TREE_VERTICAL,
- TREE_BRANCH,
- TREE_RIGHT,
- TREE_SPACE,
- TRIANGULAR_BULLET,
- BLACK_CIRCLE,
- ARROW,
- MDASH,
- ELLIPSIS,
+ SPECIAL_GLYPH_TREE_VERTICAL,
+ SPECIAL_GLYPH_TREE_BRANCH,
+ SPECIAL_GLYPH_TREE_RIGHT,
+ SPECIAL_GLYPH_TREE_SPACE,
+ SPECIAL_GLYPH_TRIANGULAR_BULLET,
+ SPECIAL_GLYPH_BLACK_CIRCLE,
+ SPECIAL_GLYPH_BULLET,
+ SPECIAL_GLYPH_ARROW,
+ SPECIAL_GLYPH_MDASH,
+ SPECIAL_GLYPH_ELLIPSIS,
+ SPECIAL_GLYPH_MU,
+ SPECIAL_GLYPH_CHECK_MARK,
+ SPECIAL_GLYPH_CROSS_MARK,
+ _SPECIAL_GLYPH_FIRST_SMILEY,
+ SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_SMILEY,
+ SPECIAL_GLYPH_HAPPY_SMILEY,
+ SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY,
+ SPECIAL_GLYPH_NEUTRAL_SMILEY,
+ SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY,
+ SPECIAL_GLYPH_UNHAPPY_SMILEY,
+ SPECIAL_GLYPH_DEPRESSED_SMILEY,
_SPECIAL_GLYPH_MAX
} SpecialGlyph;
@@ -65,3 +77,8 @@ static inline void freelocalep(locale_t *p) {
freelocale(*p);
}
+
+void locale_variables_free(char* l[_VARIABLE_LC_MAX]);
+static inline void locale_variables_freep(char*(*l)[_VARIABLE_LC_MAX]) {
+ locale_variables_free(*l);
+}
diff --git a/src/basic/log.c b/src/basic/log.c
index 48c094b548..0486027296 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -401,7 +401,7 @@ static int write_to_syslog(
.msg_iovlen = ELEMENTSOF(iovec),
};
time_t t;
- struct tm *tm;
+ struct tm tm;
if (syslog_fd < 0)
return 0;
@@ -409,11 +409,10 @@ static int write_to_syslog(
xsprintf(header_priority, "<%i>", level);
t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
- tm = localtime(&t);
- if (!tm)
+ if (!localtime_r(&t, &tm))
return -EINVAL;
- if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
+ if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
return -EINVAL;
xsprintf(header_pid, "["PID_FMT"]: ", getpid_cached());
@@ -485,6 +484,8 @@ static int log_do_header(
const char *extra_field, const char *extra) {
int r;
+ error = IS_SYNTHETIC_ERRNO(error) ? 0 : ERRNO_VALUE(error);
+
r = snprintf(header, size,
"PRIORITY=%i\n"
"SYSLOG_FACILITY=%i\n"
@@ -570,15 +571,12 @@ int log_dispatch_internal(
assert_raw(buffer);
- if (error < 0)
- error = -error;
-
if (log_target == LOG_TARGET_NULL)
- return -error;
+ return -ERRNO_VALUE(error);
/* Patch in LOG_DAEMON facility if necessary */
if ((level & LOG_FACMASK) == 0)
- level = log_facility | LOG_PRI(level);
+ level |= log_facility;
if (open_when_needed)
log_open();
@@ -637,7 +635,7 @@ int log_dispatch_internal(
if (open_when_needed)
log_close();
- return -error;
+ return -ERRNO_VALUE(error);
}
int log_dump_internal(
@@ -653,11 +651,8 @@ int log_dump_internal(
/* This modifies the buffer... */
- if (error < 0)
- error = -error;
-
if (_likely_(LOG_PRI(level) > log_max_level[realm]))
- return -error;
+ return -ERRNO_VALUE(error);
return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
}
@@ -675,14 +670,11 @@ int log_internalv_realm(
char buffer[LINE_MAX];
PROTECT_ERRNO;
- if (error < 0)
- error = -error;
-
if (_likely_(LOG_PRI(level) > log_max_level[realm]))
- return -error;
+ return -ERRNO_VALUE(error);
/* Make sure that %m maps to the specified error (or "Success"). */
- errno = error;
+ errno = ERRNO_VALUE(error);
(void) vsnprintf(buffer, sizeof buffer, format, ap);
@@ -724,14 +716,11 @@ static int log_object_internalv(
PROTECT_ERRNO;
char *buffer, *b;
- if (error < 0)
- error = -error;
-
if (_likely_(LOG_PRI(level) > log_max_level[LOG_REALM_SYSTEMD]))
- return -error;
+ return -ERRNO_VALUE(error);
/* Make sure that %m maps to the specified error (or "Success"). */
- errno = error;
+ errno = ERRNO_VALUE(error);
/* Prepend the object name before the message */
if (object) {
@@ -854,7 +843,7 @@ int log_format_iovec(
* since vasprintf() leaves it afterwards at
* an undefined location */
- errno = error;
+ errno = ERRNO_VALUE(error);
va_copy(aq, ap);
r = vasprintf(&m, format, aq);
@@ -869,8 +858,7 @@ int log_format_iovec(
iovec[(*n)++] = IOVEC_MAKE_STRING(m);
if (newline_separator) {
- iovec[*n].iov_base = (char*) &nl;
- iovec[*n].iov_len = 1;
+ iovec[*n] = IOVEC_MAKE((char *)&nl, 1);
(*n)++;
}
@@ -893,17 +881,12 @@ int log_struct_internal(
PROTECT_ERRNO;
va_list ap;
- if (error < 0)
- error = -error;
-
- if (_likely_(LOG_PRI(level) > log_max_level[realm]))
- return -error;
-
- if (log_target == LOG_TARGET_NULL)
- return -error;
+ if (_likely_(LOG_PRI(level) > log_max_level[realm]) ||
+ log_target == LOG_TARGET_NULL)
+ return -ERRNO_VALUE(error);
if ((level & LOG_FACMASK) == 0)
- level = log_facility | LOG_PRI(level);
+ level |= log_facility;
if (IN_SET(log_target,
LOG_TARGET_AUTO,
@@ -923,7 +906,8 @@ int log_struct_internal(
};
bool fallback = false;
- /* If the journal is available do structured logging */
+ /* If the journal is available do structured logging.
+ * Do not report the errno if it is synthetic. */
log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
iovec[n++] = IOVEC_MAKE_STRING(header);
@@ -944,7 +928,7 @@ int log_struct_internal(
if (open_when_needed)
log_close();
- return -error;
+ return -ERRNO_VALUE(error);
}
}
}
@@ -955,7 +939,7 @@ int log_struct_internal(
while (format) {
va_list aq;
- errno = error;
+ errno = ERRNO_VALUE(error);
va_copy(aq, ap);
(void) vsnprintf(buf, sizeof buf, format, aq);
@@ -976,7 +960,7 @@ int log_struct_internal(
if (open_when_needed)
log_close();
- return -error;
+ return -ERRNO_VALUE(error);
}
return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buf + 8);
@@ -996,17 +980,12 @@ int log_struct_iovec_internal(
size_t i;
char *m;
- if (error < 0)
- error = -error;
-
- if (_likely_(LOG_PRI(level) > log_max_level[realm]))
- return -error;
-
- if (log_target == LOG_TARGET_NULL)
- return -error;
+ if (_likely_(LOG_PRI(level) > log_max_level[realm]) ||
+ log_target == LOG_TARGET_NULL)
+ return -ERRNO_VALUE(error);
if ((level & LOG_FACMASK) == 0)
- level = log_facility | LOG_PRI(level);
+ level |= log_facility;
if (IN_SET(log_target, LOG_TARGET_AUTO,
LOG_TARGET_JOURNAL_OR_KMSG,
@@ -1029,7 +1008,7 @@ int log_struct_iovec_internal(
}
if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) >= 0)
- return -error;
+ return -ERRNO_VALUE(error);
}
for (i = 0; i < n_input_iovec; i++)
@@ -1037,7 +1016,7 @@ int log_struct_iovec_internal(
break;
if (_unlikely_(i >= n_input_iovec)) /* Couldn't find MESSAGE=? */
- return -error;
+ return -ERRNO_VALUE(error);
m = strndupa(input_iovec[i].iov_base + STRLEN("MESSAGE="),
input_iovec[i].iov_len - STRLEN("MESSAGE="));
@@ -1240,14 +1219,9 @@ int log_syntax_internal(
va_list ap;
const char *unit_fmt = NULL;
- if (error < 0)
- error = -error;
-
- if (_likely_(LOG_PRI(level) > log_max_level[LOG_REALM_SYSTEMD]))
- return -error;
-
- if (log_target == LOG_TARGET_NULL)
- return -error;
+ if (_likely_(LOG_PRI(level) > log_max_level[LOG_REALM_SYSTEMD]) ||
+ log_target == LOG_TARGET_NULL)
+ return -ERRNO_VALUE(error);
errno = error;
@@ -1338,3 +1312,13 @@ int log_dup_console(void) {
console_fd = copy;
return 0;
}
+
+void log_setup_service(void) {
+ /* Sets up logging the way it is most appropriate for running a program as a service. Note that using this
+ * doesn't make the binary unsuitable for invocation on the command line, as log output will still go to the
+ * terminal if invoked interactively. */
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+}
diff --git a/src/basic/log.h b/src/basic/log.h
index e1f5fd30cd..17438d7ff7 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -36,10 +36,12 @@ typedef enum LogTarget{
_LOG_TARGET_INVALID = -1
} LogTarget;
-#define LOG_REALM_PLUS_LEVEL(realm, level) \
- ((realm) << 10 | (level))
-#define LOG_REALM_REMOVE_LEVEL(realm_level) \
- ((realm_level >> 10))
+/* Note to readers: << and >> have lower precedence than & and | */
+#define LOG_REALM_PLUS_LEVEL(realm, level) ((realm) << 10 | (level))
+#define LOG_REALM_REMOVE_LEVEL(realm_level) ((realm_level) >> 10)
+#define SYNTHETIC_ERRNO(num) (1 << 30 | (num))
+#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
+#define ERRNO_VALUE(val) (abs(val) & 255)
void log_set_target(LogTarget target);
void log_set_max_level_realm(LogRealm realm, int level);
@@ -201,10 +203,10 @@ void log_assert_failed_return_realm(
#define log_full_errno_realm(realm, level, error, ...) \
({ \
int _level = (level), _e = (error), _realm = (realm); \
- (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \
+ (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \
? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \
__FILE__, __LINE__, __func__, __VA_ARGS__) \
- : -abs(_e); \
+ : -ERRNO_VALUE(_e); \
})
#define log_full_errno(level, error, ...) \
@@ -318,3 +320,5 @@ int log_syntax_invalid_utf8_internal(
})
#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
+
+void log_setup_service(void);
diff --git a/src/basic/macro.h b/src/basic/macro.h
index d1365f7058..1971e912db 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -1,36 +1,42 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <assert.h>
+#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
-#define _printf_(a,b) __attribute__ ((format (printf, a, b)))
+#define _printf_(a, b) __attribute__((__format__(printf, a, b)))
#ifdef __clang__
# define _alloc_(...)
#else
-# define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
+# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__)))
#endif
-#define _sentinel_ __attribute__ ((sentinel))
-#define _unused_ __attribute__ ((unused))
-#define _destructor_ __attribute__ ((destructor))
-#define _pure_ __attribute__ ((pure))
-#define _const_ __attribute__ ((const))
-#define _deprecated_ __attribute__ ((deprecated))
-#define _packed_ __attribute__ ((packed))
-#define _malloc_ __attribute__ ((malloc))
-#define _weak_ __attribute__ ((weak))
-#define _likely_(x) (__builtin_expect(!!(x),1))
-#define _unlikely_(x) (__builtin_expect(!!(x),0))
-#define _public_ __attribute__ ((visibility("default")))
-#define _hidden_ __attribute__ ((visibility("hidden")))
-#define _weakref_(x) __attribute__((weakref(#x)))
-#define _alignas_(x) __attribute__((aligned(__alignof(x))))
-#define _cleanup_(x) __attribute__((cleanup(x)))
+#define _sentinel_ __attribute__((__sentinel__))
+#define _section_(x) __attribute__((__section__(x)))
+#define _used_ __attribute__((__used__))
+#define _unused_ __attribute__((__unused__))
+#define _destructor_ __attribute__((__destructor__))
+#define _pure_ __attribute__((__pure__))
+#define _const_ __attribute__((__const__))
+#define _deprecated_ __attribute__((__deprecated__))
+#define _packed_ __attribute__((__packed__))
+#define _malloc_ __attribute__((__malloc__))
+#define _weak_ __attribute__((__weak__))
+#define _likely_(x) (__builtin_expect(!!(x), 1))
+#define _unlikely_(x) (__builtin_expect(!!(x), 0))
+#define _public_ __attribute__((__visibility__("default")))
+#define _hidden_ __attribute__((__visibility__("hidden")))
+#define _weakref_(x) __attribute__((__weakref__(#x)))
+#define _align_(x) __attribute__((__aligned__(x)))
+#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
+#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
+#define _cleanup_(x) __attribute__((__cleanup__(x)))
#if __GNUC__ >= 7
-#define _fallthrough_ __attribute__((fallthrough))
+#define _fallthrough_ __attribute__((__fallthrough__))
#else
#define _fallthrough_
#endif
@@ -40,7 +46,7 @@
#if __STDC_VERSION__ >= 201112L
#define _noreturn_ _Noreturn
#else
-#define _noreturn_ __attribute__((noreturn))
+#define _noreturn_ __attribute__((__noreturn__))
#endif
#endif
@@ -55,11 +61,30 @@
# endif
#endif
-/* Temporarily disable some warnings */
-#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"")
+#if !defined(HAS_FEATURE_ADDRESS_SANITIZER)
+# ifdef __SANITIZE_ADDRESS__
+# define HAS_FEATURE_ADDRESS_SANITIZER 1
+# elif defined(__has_feature)
+# if __has_feature(address_sanitizer)
+# define HAS_FEATURE_ADDRESS_SANITIZER 1
+# endif
+# endif
+# if !defined(HAS_FEATURE_ADDRESS_SANITIZER)
+# define HAS_FEATURE_ADDRESS_SANITIZER 0
+# endif
+#endif
+/* Note: on GCC "no_sanitize_address" is a function attribute only, on llvm it may also be applied to global
+ * variables. We define a specific macro which knows this. Note that on GCC we don't need this decorator so much, since
+ * our primary usecase for this attribute is registration structures placed in named ELF sections which shall not be
+ * padded, but GCC doesn't pad those anyway if AddressSanitizer is enabled. */
+#if HAS_FEATURE_ADDRESS_SANITIZER && defined(__clang__)
+#define _variable_no_sanitize_address_ __attribute__((__no_sanitize_address__))
+#else
+#define _variable_no_sanitize_address_
+#endif
+
+/* Temporarily disable some warnings */
#define DISABLE_WARNING_FORMAT_NONLITERAL \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
@@ -146,10 +171,10 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
# define VOID_0 ((void*)0)
#endif
-#define ELEMENTSOF(x) \
- __extension__ (__builtin_choose_expr( \
+#define ELEMENTSOF(x) \
+ (__builtin_choose_expr( \
!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
- sizeof(x)/sizeof((x)[0]), \
+ sizeof(x)/sizeof((x)[0]), \
VOID_0))
/*
@@ -167,23 +192,23 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
*/
#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member)
#define __container_of(uniq, ptr, type, member) \
- __extension__ ({ \
+ ({ \
const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \
- (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type,member) ); \
+ (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \
})
#undef MAX
#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
#define __MAX(aq, a, bq, b) \
- __extension__ ({ \
+ ({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
- UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \
+ UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
})
/* evaluates to (void) if _A or _B are not constant or of different types */
#define CONST_MAX(_A, _B) \
- __extension__ (__builtin_choose_expr( \
+ (__builtin_choose_expr( \
__builtin_constant_p(_A) && \
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
@@ -193,66 +218,112 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
/* takes two types and returns the size of the larger one */
#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
-#define MAX3(x,y,z) \
- __extension__ ({ \
- const typeof(x) _c = MAX(x,y); \
- MAX(_c, z); \
- })
+#define MAX3(x, y, z) \
+ ({ \
+ const typeof(x) _c = MAX(x, y); \
+ MAX(_c, z); \
+ })
#undef MIN
#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b))
#define __MIN(aq, a, bq, b) \
- __extension__ ({ \
+ ({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
- UNIQ_T(A,aq) < UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \
+ UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
})
-#define MIN3(x,y,z) \
- __extension__ ({ \
- const typeof(x) _c = MIN(x,y); \
- MIN(_c, z); \
- })
+#define MIN3(x, y, z) \
+ ({ \
+ const typeof(x) _c = MIN(x, y); \
+ MIN(_c, z); \
+ })
#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
#define __LESS_BY(aq, a, bq, b) \
- __extension__ ({ \
+ ({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
- UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) - UNIQ_T(B,bq) : 0; \
+ UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \
+ })
+
+#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b))
+#define __CMP(aq, a, bq, b) \
+ ({ \
+ const typeof(a) UNIQ_T(A, aq) = (a); \
+ const typeof(b) UNIQ_T(B, bq) = (b); \
+ UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \
+ UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \
})
#undef CLAMP
#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high))
#define __CLAMP(xq, x, lowq, low, highq, high) \
- __extension__ ({ \
- const typeof(x) UNIQ_T(X,xq) = (x); \
- const typeof(low) UNIQ_T(LOW,lowq) = (low); \
- const typeof(high) UNIQ_T(HIGH,highq) = (high); \
- UNIQ_T(X,xq) > UNIQ_T(HIGH,highq) ? \
- UNIQ_T(HIGH,highq) : \
- UNIQ_T(X,xq) < UNIQ_T(LOW,lowq) ? \
- UNIQ_T(LOW,lowq) : \
- UNIQ_T(X,xq); \
+ ({ \
+ const typeof(x) UNIQ_T(X, xq) = (x); \
+ const typeof(low) UNIQ_T(LOW, lowq) = (low); \
+ const typeof(high) UNIQ_T(HIGH, highq) = (high); \
+ UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \
+ UNIQ_T(HIGH, highq) : \
+ UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \
+ UNIQ_T(LOW, lowq) : \
+ UNIQ_T(X, xq); \
})
/* [(x + y - 1) / y] suffers from an integer overflow, even though the
* computation should be possible in the given type. Therefore, we use
* [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
* quotient and the remainder, so both should be equally fast. */
-#define DIV_ROUND_UP(_x, _y) \
- __extension__ ({ \
- const typeof(_x) __x = (_x); \
- const typeof(_y) __y = (_y); \
- (__x / __y + !!(__x % __y)); \
+#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y))
+#define __DIV_ROUND_UP(xq, x, yq, y) \
+ ({ \
+ const typeof(x) UNIQ_T(X, xq) = (x); \
+ const typeof(y) UNIQ_T(Y, yq) = (y); \
+ (UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \
})
+#ifdef __COVERITY__
+
+/* Use special definitions of assertion macros in order to prevent
+ * false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer
+ * for uses of assert_se() and assert_return().
+ *
+ * These definitions make expression go through a (trivial) function
+ * call to ensure they are not discarded. Also use ! or !! to ensure
+ * the boolean expressions are seen as such.
+ *
+ * This technique has been described and recommended in:
+ * https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects
+ */
+
+extern void __coverity_panic__(void);
+
+static inline int __coverity_check__(int condition) {
+ return condition;
+}
+
+#define assert_message_se(expr, message) \
+ do { \
+ if (__coverity_check__(!(expr))) \
+ __coverity_panic__(); \
+ } while (false)
+
+#define assert_log(expr, message) __coverity_check__(!!(expr))
+
+#else /* ! __COVERITY__ */
+
#define assert_message_se(expr, message) \
do { \
if (_unlikely_(!(expr))) \
log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
} while (false)
+#define assert_log(expr, message) ((_likely_(expr)) \
+ ? (true) \
+ : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
+
+#endif /* __COVERITY__ */
+
#define assert_se(expr) assert_message_se(expr, #expr)
/* We override the glibc assert() here. */
@@ -269,26 +340,15 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
} while (false)
#if defined(static_assert)
-/* static_assert() is sometimes defined in a way that trips up
- * -Wdeclaration-after-statement, hence let's temporarily turn off
- * this warning around it. */
#define assert_cc(expr) \
- DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \
- static_assert(expr, #expr); \
- REENABLE_WARNING
+ static_assert(expr, #expr);
#else
#define assert_cc(expr) \
- DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \
struct CONCATENATE(_assert_struct_, __COUNTER__) { \
char x[(expr) ? 0 : -1]; \
- }; \
- REENABLE_WARNING
+ };
#endif
-#define assert_log(expr, message) ((_likely_(expr)) \
- ? (true) \
- : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
-
#define assert_return(expr, r) \
do { \
if (!assert_log(expr, #expr)) \
@@ -303,9 +363,15 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
} \
} while (false)
+#define return_with_errno(r, err) \
+ do { \
+ errno = abs(err); \
+ return r; \
+ } while (false)
+
#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
-#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define PTR_TO_UINT(p) ((unsigned) ((uintptr_t) (p)))
#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u)))
#define PTR_TO_LONG(p) ((long) ((intptr_t) (p)))
@@ -352,7 +418,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
#define SET_FLAG(v, flag, b) \
(v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
#define FLAGS_SET(v, flags) \
- (((v) & (flags)) == (flags))
+ ((~(v) & (flags)) == 0)
#define CASE_F(X) case X:
#define CASE_F_1(CASE, X) CASE_F(X)
@@ -385,8 +451,11 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
#define IN_SET(x, ...) \
({ \
bool _found = false; \
- /* If the build breaks in the line below, you need to extend the case macros */ \
- static _unused_ char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \
+ /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \
+ * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \
+ * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \
+ * doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \
+ assert_cc((sizeof((long double[]){__VA_ARGS__})/sizeof(long double)) <= 20); \
switch(x) { \
FOR_EACH_MAKE_CASE(__VA_ARGS__) \
_found = true; \
@@ -417,10 +486,64 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
#endif
#endif
+#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \
+ static inline void name(type *p) { \
+ func(p); \
+ }
+
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
static inline void func##p(type *p) { \
if (*p) \
func(*p); \
}
+#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
+ scope type *name##_ref(type *p) { \
+ if (!p) \
+ return NULL; \
+ \
+ assert(p->n_ref > 0); \
+ p->n_ref++; \
+ return p; \
+ }
+
+#define _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, scope) \
+ scope type *name##_unref(type *p) { \
+ if (!p) \
+ return NULL; \
+ \
+ assert(p->n_ref > 0); \
+ p->n_ref--; \
+ if (p->n_ref > 0) \
+ return NULL; \
+ \
+ return free_func(p); \
+ }
+
+#define DEFINE_TRIVIAL_REF_FUNC(type, name) \
+ _DEFINE_TRIVIAL_REF_FUNC(type, name,)
+#define DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name) \
+ _DEFINE_TRIVIAL_REF_FUNC(type, name, static)
+#define DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name) \
+ _DEFINE_TRIVIAL_REF_FUNC(type, name, _public_)
+
+#define DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
+ _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func,)
+#define DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
+ _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, static)
+#define DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func) \
+ _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, _public_)
+
+#define DEFINE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
+ DEFINE_TRIVIAL_REF_FUNC(type, name); \
+ DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func);
+
+#define DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
+ DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name); \
+ DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func);
+
+#define DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
+ DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \
+ DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
+
#include "log.h"
diff --git a/src/basic/mempool.c b/src/basic/mempool.c
index a5ec8a1020..159c963377 100644
--- a/src/basic/mempool.c
+++ b/src/basic/mempool.c
@@ -3,8 +3,10 @@
#include <stdint.h>
#include <stdlib.h>
+#include "env-util.h"
#include "macro.h"
#include "mempool.h"
+#include "process-util.h"
#include "util.h"
struct pool {
@@ -70,8 +72,21 @@ void mempool_free_tile(struct mempool *mp, void *p) {
mp->freelist = p;
}
-#if VALGRIND
+bool mempool_enabled(void) {
+ static int b = -1;
+
+ if (!is_main_thread())
+ return false;
+ if (!mempool_use_allowed)
+ b = false;
+ if (b < 0)
+ b = getenv_bool("SYSTEMD_MEMPOOL") != 0;
+
+ return b;
+}
+
+#if VALGRIND
void mempool_drop(struct mempool *mp) {
struct pool *p = mp->first_pool;
while (p) {
@@ -81,5 +96,4 @@ void mempool_drop(struct mempool *mp) {
p = n;
}
}
-
#endif
diff --git a/src/basic/mempool.h b/src/basic/mempool.h
index 2a41cb358c..0eecca0f92 100644
--- a/src/basic/mempool.h
+++ b/src/basic/mempool.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
+#include <stdbool.h>
#include <stddef.h>
struct pool;
@@ -23,6 +23,9 @@ static struct mempool pool_name = { \
.at_least = alloc_at_least, \
}
+extern const bool mempool_use_allowed;
+bool mempool_enabled(void);
+
#if VALGRIND
void mempool_drop(struct mempool *mp);
#endif
diff --git a/src/basic/meson.build b/src/basic/meson.build
index 31625b1785..23b5e75bd8 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -15,23 +15,13 @@ basic_sources = files('''
async.h
audit-util.c
audit-util.h
- barrier.c
- barrier.h
- bitmap.c
- bitmap.h
- blkid-util.h
blockdev-util.c
blockdev-util.h
- bpf-program.c
- bpf-program.h
- btrfs-ctree.h
btrfs-util.c
btrfs-util.h
build.h
bus-label.c
bus-label.h
- calendarspec.c
- calendarspec.h
cap-list.c
cap-list.h
capability-util.c
@@ -40,21 +30,17 @@ basic_sources = files('''
cgroup-util.h
chattr-util.c
chattr-util.h
- clock-util.c
- clock-util.h
conf-files.c
conf-files.h
copy.c
copy.h
- cpu-set-util.c
- cpu-set-util.h
- crypt-util.c
- crypt-util.h
def.h
device-nodes.c
device-nodes.h
dirent-util.c
dirent-util.h
+ env-file.c
+ env-file.h
env-util.c
env-util.h
errno-list.c
@@ -63,20 +49,12 @@ basic_sources = files('''
escape.h
ether-addr-util.c
ether-addr-util.h
- exec-util.c
- exec-util.h
- exit-status.c
- exit-status.h
extract-word.c
extract-word.h
fd-util.c
fd-util.h
- fileio-label.c
- fileio-label.h
fileio.c
fileio.h
- format-table.c
- format-table.h
format-util.h
fs-util.c
fs-util.h
@@ -97,8 +75,6 @@ basic_sources = files('''
io-util.c
io-util.h
ioprio.h
- journal-importer.c
- journal-importer.h
khash.c
khash.h
label.c
@@ -106,8 +82,6 @@ basic_sources = files('''
list.h
locale-util.c
locale-util.h
- lockfile-util.c
- lockfile-util.h
log.c
log.h
login-util.c
@@ -117,20 +91,45 @@ basic_sources = files('''
memfd-util.h
mempool.c
mempool.h
+ missing.h
+ missing_audit.h
+ missing_btrfs.h
+ missing_btrfs_tree.h
+ missing_capability.h
+ missing_drm.h
+ missing_ethtool.h
+ missing_fcntl.h
+ missing_fib_rules.h
+ missing_fou.h
+ missing_fs.h
+ missing_if_bridge.h
+ missing_if_link.h
+ missing_if_tunnel.h
+ missing_input.h
+ missing_keyctl.h
+ missing_magic.h
+ missing_mman.h
+ missing_network.h
+ missing_prctl.h
+ missing_random.h
+ missing_resource.h
+ missing_sched.h
+ missing_securebits.h
+ missing_socket.h
+ missing_stat.h
+ missing_stdlib.h
missing_syscall.h
+ missing_timerfd.h
+ missing_type.h
+ missing_vxcan.h
mkdir-label.c
mkdir.c
mkdir.h
- module-util.h
- mount-util.c
- mount-util.h
+ mountpoint-util.c
+ mountpoint-util.h
nss-util.h
ordered-set.c
ordered-set.h
- pager.c
- pager.h
- os-util.c
- os-util.h
parse-util.c
parse-util.h
path-util.c
@@ -149,8 +148,6 @@ basic_sources = files('''
ratelimit.h
raw-clone.h
raw-reboot.h
- reboot-util.c
- reboot-util.h
refcnt.h
replace-var.c
replace-var.h
@@ -158,9 +155,6 @@ basic_sources = files('''
rlimit-util.h
rm-rf.c
rm-rf.h
- securebits-util.c
- securebits-util.h
- securebits.h
selinux-util.c
selinux-util.h
set.h
@@ -173,14 +167,13 @@ basic_sources = files('''
smack-util.c
smack-util.h
socket-label.c
- socket-protocol-list.c
- socket-protocol-list.h
socket-util.c
socket-util.h
sparse-endian.h
special.h
stat-util.c
stat-util.h
+ static-destruct.h
stdio-util.h
strbuf.c
strbuf.h
@@ -198,6 +191,8 @@ basic_sources = files('''
terminal-util.h
time-util.c
time-util.h
+ tmpfile-util.c
+ tmpfile-util.h
umask-util.h
unaligned.h
unit-def.c
@@ -210,41 +205,36 @@ basic_sources = files('''
utf8.h
util.c
util.h
- verbs.c
- verbs.h
virt.c
virt.h
- web-util.c
- web-util.h
xattr-util.c
xattr-util.h
- xml.c
- xml.h
'''.split())
-missing_h = files('missing.h')
-
-generate_gperfs = find_program('generate-gperfs.py')
+missing_audit_h = files('missing_audit.h')
+missing_capability_h = files('missing_capability.h')
+missing_network_h = files('missing_network.h')
+missing_socket_h = files('missing_socket.h')
generate_af_list = find_program('generate-af-list.sh')
af_list_txt = custom_target(
'af-list.txt',
output : 'af-list.txt',
- command : [generate_af_list, cpp],
+ command : [generate_af_list, cpp, config_h, missing_socket_h],
capture : true)
generate_arphrd_list = find_program('generate-arphrd-list.sh')
arphrd_list_txt = custom_target(
'arphrd-list.txt',
output : 'arphrd-list.txt',
- command : [generate_arphrd_list, cpp],
+ command : [generate_arphrd_list, cpp, config_h, missing_network_h],
capture : true)
generate_cap_list = find_program('generate-cap-list.sh')
cap_list_txt = custom_target(
'cap-list.txt',
output : 'cap-list.txt',
- command : [generate_cap_list, cpp, config_h, missing_h],
+ command : [generate_cap_list, cpp, config_h, missing_capability_h],
capture : true)
generate_errno_list = find_program('generate-errno-list.sh')
@@ -254,19 +244,11 @@ errno_list_txt = custom_target(
command : [generate_errno_list, cpp],
capture : true)
-generate_socket_protocol_list = find_program('generate-socket-protocol-list.sh')
-socket_protocol_list_txt = custom_target(
- 'socket-protocol-list.txt',
- output : 'socket-protocol-list.txt',
- command : [generate_socket_protocol_list, cpp],
- capture : true)
-
generated_gperf_headers = []
foreach item : [['af', af_list_txt, 'af', ''],
['arphrd', arphrd_list_txt, 'arphrd', 'ARPHRD_'],
['cap', cap_list_txt, 'capability', ''],
- ['errno', errno_list_txt, 'errno', ''],
- ['socket-protocol', socket_protocol_list_txt, 'socket_protocol', 'IPPROTO_']]
+ ['errno', errno_list_txt, 'errno', '']]
fname = '@0@-from-name.gperf'.format(item[0])
gperf_file = custom_target(
@@ -301,7 +283,7 @@ foreach item : [['af', af_list_txt, 'af', ''],
generated_gperf_headers += [target1, target2]
endforeach
-basic_sources += [missing_h] + generated_gperf_headers
+basic_sources += generated_gperf_headers
basic_gcrypt_sources = files(
'gcrypt-util.c',
'gcrypt-util.h')
@@ -312,9 +294,8 @@ libbasic = static_library(
include_directories : includes,
dependencies : [threads,
libcap,
- libblkid,
- libmount,
- libselinux],
+ libselinux,
+ libm],
c_args : ['-fvisibility=default'],
install : false)
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 71a07d0574..5067c8fd00 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -3,1409 +3,23 @@
/* Missing glibc definitions to access certain kernel APIs */
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <linux/audit.h>
-#include <linux/capability.h>
-#include <linux/falloc.h>
-#include <linux/if_link.h>
-#include <linux/input.h>
-#include <linux/loop.h>
-#include <linux/neighbour.h>
-#include <linux/oom.h>
-#include <linux/rtnetlink.h>
-#include <linux/stat.h>
-#include <net/ethernet.h>
-#include <stdlib.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <uchar.h>
-#include <unistd.h>
-
-#if HAVE_AUDIT
-#include <libaudit.h>
-#endif
-
-#ifdef ARCH_MIPS
-#include <asm/sgidefs.h>
-#endif
-
-#if HAVE_LINUX_BTRFS_H
-#include <linux/btrfs.h>
-#endif
-
-#if HAVE_LINUX_VM_SOCKETS_H
-#include <linux/vm_sockets.h>
-#else
-#define VMADDR_CID_ANY -1U
-struct sockaddr_vm {
- unsigned short svm_family;
- unsigned short svm_reserved1;
- unsigned int svm_port;
- unsigned int svm_cid;
- unsigned char svm_zero[sizeof(struct sockaddr) -
- sizeof(unsigned short) -
- sizeof(unsigned short) -
- sizeof(unsigned int) -
- sizeof(unsigned int)];
-};
-#endif /* !HAVE_LINUX_VM_SOCKETS_H */
-
-#ifndef RLIMIT_RTTIME
-#define RLIMIT_RTTIME 15
-#endif
-
-/* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */
-#define _RLIMIT_MAX (RLIMIT_RTTIME+1 > RLIMIT_NLIMITS ? RLIMIT_RTTIME+1 : RLIMIT_NLIMITS)
-
-#ifndef F_LINUX_SPECIFIC_BASE
-#define F_LINUX_SPECIFIC_BASE 1024
-#endif
-
-#ifndef F_SETPIPE_SZ
-#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
-#endif
-
-#ifndef F_GETPIPE_SZ
-#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
-#endif
-
-#ifndef F_ADD_SEALS
-#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
-#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
-
-#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
-#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
-#define F_SEAL_GROW 0x0004 /* prevent file from growing */
-#define F_SEAL_WRITE 0x0008 /* prevent writes */
-#endif
-
-#ifndef F_OFD_GETLK
-#define F_OFD_GETLK 36
-#define F_OFD_SETLK 37
-#define F_OFD_SETLKW 38
-#endif
-
-#ifndef MFD_ALLOW_SEALING
-#define MFD_ALLOW_SEALING 0x0002U
-#endif
-
-#ifndef MFD_CLOEXEC
-#define MFD_CLOEXEC 0x0001U
-#endif
-
-#ifndef IP_FREEBIND
-#define IP_FREEBIND 15
-#endif
-
-#ifndef OOM_SCORE_ADJ_MIN
-#define OOM_SCORE_ADJ_MIN (-1000)
-#endif
-
-#ifndef OOM_SCORE_ADJ_MAX
-#define OOM_SCORE_ADJ_MAX 1000
-#endif
-
-#ifndef AUDIT_SERVICE_START
-#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */
-#endif
-
-#ifndef AUDIT_SERVICE_STOP
-#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */
-#endif
-
-#ifndef TIOCVHANGUP
-#define TIOCVHANGUP 0x5437
-#endif
-
-#ifndef IP_TRANSPARENT
-#define IP_TRANSPARENT 19
-#endif
-
-#ifndef SOL_NETLINK
-#define SOL_NETLINK 270
-#endif
-
-#ifndef NETLINK_LIST_MEMBERSHIPS
-#define NETLINK_LIST_MEMBERSHIPS 9
-#endif
-
-#ifndef SOL_SCTP
-#define SOL_SCTP 132
-#endif
-
-#ifndef GRND_NONBLOCK
-#define GRND_NONBLOCK 0x0001
-#endif
-
-#ifndef GRND_RANDOM
-#define GRND_RANDOM 0x0002
-#endif
-
-#ifndef FS_NOCOW_FL
-#define FS_NOCOW_FL 0x00800000
-#endif
-
-#ifndef BTRFS_IOCTL_MAGIC
-#define BTRFS_IOCTL_MAGIC 0x94
-#endif
-
-#ifndef BTRFS_PATH_NAME_MAX
-#define BTRFS_PATH_NAME_MAX 4087
-#endif
-
-#ifndef BTRFS_DEVICE_PATH_NAME_MAX
-#define BTRFS_DEVICE_PATH_NAME_MAX 1024
-#endif
-
-#ifndef BTRFS_FSID_SIZE
-#define BTRFS_FSID_SIZE 16
-#endif
-
-#ifndef BTRFS_UUID_SIZE
-#define BTRFS_UUID_SIZE 16
-#endif
-
-#ifndef BTRFS_SUBVOL_RDONLY
-#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
-#endif
-
-#ifndef BTRFS_SUBVOL_NAME_MAX
-#define BTRFS_SUBVOL_NAME_MAX 4039
-#endif
-
-#ifndef BTRFS_INO_LOOKUP_PATH_MAX
-#define BTRFS_INO_LOOKUP_PATH_MAX 4080
-#endif
-
-#ifndef BTRFS_SEARCH_ARGS_BUFSIZE
-#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
-#endif
-
-#ifndef BTRFS_QGROUP_LEVEL_SHIFT
-#define BTRFS_QGROUP_LEVEL_SHIFT 48
-#endif
-
-#if ! HAVE_LINUX_BTRFS_H
-#define BTRFS_IOC_QGROUP_ASSIGN _IOW(BTRFS_IOCTL_MAGIC, 41, \
- struct btrfs_ioctl_qgroup_assign_args)
-#define BTRFS_IOC_QGROUP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 42, \
- struct btrfs_ioctl_qgroup_create_args)
-#define BTRFS_IOC_QUOTA_RESCAN _IOW(BTRFS_IOCTL_MAGIC, 44, \
- struct btrfs_ioctl_quota_rescan_args)
-#define BTRFS_IOC_QUOTA_RESCAN_STATUS _IOR(BTRFS_IOCTL_MAGIC, 45, \
- struct btrfs_ioctl_quota_rescan_args)
-
-struct btrfs_ioctl_quota_rescan_args {
- __u64 flags;
- __u64 progress;
- __u64 reserved[6];
-};
-
-struct btrfs_ioctl_qgroup_assign_args {
- __u64 assign;
- __u64 src;
- __u64 dst;
-};
-
-struct btrfs_ioctl_qgroup_create_args {
- __u64 create;
- __u64 qgroupid;
-};
-
-struct btrfs_ioctl_vol_args {
- int64_t fd;
- char name[BTRFS_PATH_NAME_MAX + 1];
-};
-
-struct btrfs_qgroup_limit {
- __u64 flags;
- __u64 max_rfer;
- __u64 max_excl;
- __u64 rsv_rfer;
- __u64 rsv_excl;
-};
-
-struct btrfs_qgroup_inherit {
- __u64 flags;
- __u64 num_qgroups;
- __u64 num_ref_copies;
- __u64 num_excl_copies;
- struct btrfs_qgroup_limit lim;
- __u64 qgroups[0];
-};
-
-struct btrfs_ioctl_qgroup_limit_args {
- __u64 qgroupid;
- struct btrfs_qgroup_limit lim;
-};
-
-struct btrfs_ioctl_vol_args_v2 {
- __s64 fd;
- __u64 transid;
- __u64 flags;
- union {
- struct {
- __u64 size;
- struct btrfs_qgroup_inherit *qgroup_inherit;
- };
- __u64 unused[4];
- };
- char name[BTRFS_SUBVOL_NAME_MAX + 1];
-};
-
-struct btrfs_ioctl_dev_info_args {
- uint64_t devid; /* in/out */
- uint8_t uuid[BTRFS_UUID_SIZE]; /* in/out */
- uint64_t bytes_used; /* out */
- uint64_t total_bytes; /* out */
- uint64_t unused[379]; /* pad to 4k */
- char path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */
-};
-
-struct btrfs_ioctl_fs_info_args {
- uint64_t max_id; /* out */
- uint64_t num_devices; /* out */
- uint8_t fsid[BTRFS_FSID_SIZE]; /* out */
- uint64_t reserved[124]; /* pad to 1k */
-};
-
-struct btrfs_ioctl_ino_lookup_args {
- __u64 treeid;
- __u64 objectid;
- char name[BTRFS_INO_LOOKUP_PATH_MAX];
-};
-
-struct btrfs_ioctl_search_key {
- /* which root are we searching. 0 is the tree of tree roots */
- __u64 tree_id;
-
- /* keys returned will be >= min and <= max */
- __u64 min_objectid;
- __u64 max_objectid;
-
- /* keys returned will be >= min and <= max */
- __u64 min_offset;
- __u64 max_offset;
-
- /* max and min transids to search for */
- __u64 min_transid;
- __u64 max_transid;
-
- /* keys returned will be >= min and <= max */
- __u32 min_type;
- __u32 max_type;
-
- /*
- * how many items did userland ask for, and how many are we
- * returning
- */
- __u32 nr_items;
-
- /* align to 64 bits */
- __u32 unused;
-
- /* some extra for later */
- __u64 unused1;
- __u64 unused2;
- __u64 unused3;
- __u64 unused4;
-};
-
-struct btrfs_ioctl_search_header {
- __u64 transid;
- __u64 objectid;
- __u64 offset;
- __u32 type;
- __u32 len;
-};
-
-struct btrfs_ioctl_search_args {
- struct btrfs_ioctl_search_key key;
- char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
-};
-
-struct btrfs_ioctl_clone_range_args {
- __s64 src_fd;
- __u64 src_offset, src_length;
- __u64 dest_offset;
-};
-
-#define BTRFS_QUOTA_CTL_ENABLE 1
-#define BTRFS_QUOTA_CTL_DISABLE 2
-#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3
-struct btrfs_ioctl_quota_ctl_args {
- __u64 cmd;
- __u64 status;
-};
-#endif
-
-#ifndef BTRFS_IOC_DEFRAG
-#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
- struct btrfs_ioctl_vol_args)
-#endif
-
-#ifndef BTRFS_IOC_RESIZE
-#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \
- struct btrfs_ioctl_vol_args)
-#endif
-
-#ifndef BTRFS_IOC_CLONE
-#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
-#endif
-
-#ifndef BTRFS_IOC_CLONE_RANGE
-#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
- struct btrfs_ioctl_clone_range_args)
-#endif
-
-#ifndef BTRFS_IOC_SUBVOL_CREATE
-#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
- struct btrfs_ioctl_vol_args)
-#endif
-
-#ifndef BTRFS_IOC_SNAP_DESTROY
-#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
- struct btrfs_ioctl_vol_args)
-#endif
-
-#ifndef BTRFS_IOC_TREE_SEARCH
-#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
- struct btrfs_ioctl_search_args)
-#endif
-
-#ifndef BTRFS_IOC_INO_LOOKUP
-#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
- struct btrfs_ioctl_ino_lookup_args)
-#endif
-
-#ifndef BTRFS_IOC_SNAP_CREATE_V2
-#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
- struct btrfs_ioctl_vol_args_v2)
-#endif
-
-#ifndef BTRFS_IOC_SUBVOL_GETFLAGS
-#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, __u64)
-#endif
-
-#ifndef BTRFS_IOC_SUBVOL_SETFLAGS
-#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64)
-#endif
-
-#ifndef BTRFS_IOC_DEV_INFO
-#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \
- struct btrfs_ioctl_dev_info_args)
-#endif
-
-#ifndef BTRFS_IOC_FS_INFO
-#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
- struct btrfs_ioctl_fs_info_args)
-#endif
-
-#ifndef BTRFS_IOC_DEVICES_READY
-#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
- struct btrfs_ioctl_vol_args)
-#endif
-
-#ifndef BTRFS_IOC_QUOTA_CTL
-#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \
- struct btrfs_ioctl_quota_ctl_args)
-#endif
-
-#ifndef BTRFS_IOC_QGROUP_LIMIT
-#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
- struct btrfs_ioctl_qgroup_limit_args)
-#endif
-
-#ifndef BTRFS_IOC_QUOTA_RESCAN_WAIT
-#define BTRFS_IOC_QUOTA_RESCAN_WAIT _IO(BTRFS_IOCTL_MAGIC, 46)
-#endif
-
-#ifndef BTRFS_FIRST_FREE_OBJECTID
-#define BTRFS_FIRST_FREE_OBJECTID 256
-#endif
-
-#ifndef BTRFS_LAST_FREE_OBJECTID
-#define BTRFS_LAST_FREE_OBJECTID -256ULL
-#endif
-
-#ifndef BTRFS_ROOT_TREE_OBJECTID
-#define BTRFS_ROOT_TREE_OBJECTID 1
-#endif
-
-#ifndef BTRFS_QUOTA_TREE_OBJECTID
-#define BTRFS_QUOTA_TREE_OBJECTID 8ULL
-#endif
-
-#ifndef BTRFS_ROOT_ITEM_KEY
-#define BTRFS_ROOT_ITEM_KEY 132
-#endif
-
-#ifndef BTRFS_QGROUP_STATUS_KEY
-#define BTRFS_QGROUP_STATUS_KEY 240
-#endif
-
-#ifndef BTRFS_QGROUP_INFO_KEY
-#define BTRFS_QGROUP_INFO_KEY 242
-#endif
-
-#ifndef BTRFS_QGROUP_LIMIT_KEY
-#define BTRFS_QGROUP_LIMIT_KEY 244
-#endif
-
-#ifndef BTRFS_QGROUP_RELATION_KEY
-#define BTRFS_QGROUP_RELATION_KEY 246
-#endif
-
-#ifndef BTRFS_ROOT_BACKREF_KEY
-#define BTRFS_ROOT_BACKREF_KEY 144
-#endif
-
-#ifndef BTRFS_SUPER_MAGIC
-#define BTRFS_SUPER_MAGIC 0x9123683E
-#endif
-
-#ifndef CGROUP_SUPER_MAGIC
-#define CGROUP_SUPER_MAGIC 0x27e0eb
-#endif
-
-#ifndef CGROUP2_SUPER_MAGIC
-#define CGROUP2_SUPER_MAGIC 0x63677270
-#endif
-
-#ifndef CLONE_NEWCGROUP
-#define CLONE_NEWCGROUP 0x02000000
-#endif
-
-#ifndef TMPFS_MAGIC
-#define TMPFS_MAGIC 0x01021994
-#endif
-
-#ifndef MQUEUE_MAGIC
-#define MQUEUE_MAGIC 0x19800202
-#endif
-
-#ifndef SECURITYFS_MAGIC
-#define SECURITYFS_MAGIC 0x73636673
-#endif
-
-#ifndef TRACEFS_MAGIC
-#define TRACEFS_MAGIC 0x74726163
-#endif
-
-#ifndef BPF_FS_MAGIC
-#define BPF_FS_MAGIC 0xcafe4a11
-#endif
-
-#ifndef OCFS2_SUPER_MAGIC
-#define OCFS2_SUPER_MAGIC 0x7461636f
-#endif
-
-#ifndef MS_MOVE
-#define MS_MOVE 8192
-#endif
-
-#ifndef MS_REC
-#define MS_REC 16384
-#endif
-
-#ifndef MS_PRIVATE
-#define MS_PRIVATE (1<<18)
-#endif
-
-#ifndef MS_REC
-#define MS_REC (1<<19)
-#endif
-
-#ifndef MS_SHARED
-#define MS_SHARED (1<<20)
-#endif
-
-#ifndef MS_RELATIME
-#define MS_RELATIME (1<<21)
-#endif
-
-#ifndef MS_KERNMOUNT
-#define MS_KERNMOUNT (1<<22)
-#endif
-
-#ifndef MS_I_VERSION
-#define MS_I_VERSION (1<<23)
-#endif
-
-#ifndef MS_STRICTATIME
-#define MS_STRICTATIME (1<<24)
-#endif
-
-#ifndef MS_LAZYTIME
-#define MS_LAZYTIME (1<<25)
-#endif
-
-#ifndef SCM_SECURITY
-#define SCM_SECURITY 0x03
-#endif
-
-#ifndef PR_SET_NO_NEW_PRIVS
-#define PR_SET_NO_NEW_PRIVS 38
-#endif
-
-#ifndef PR_SET_CHILD_SUBREAPER
-#define PR_SET_CHILD_SUBREAPER 36
-#endif
-
-#ifndef PR_SET_MM_ARG_START
-#define PR_SET_MM_ARG_START 8
-#endif
-
-#ifndef PR_SET_MM_ARG_END
-#define PR_SET_MM_ARG_END 9
-#endif
-
-#ifndef PR_SET_MM_ENV_START
-#define PR_SET_MM_ENV_START 10
-#endif
-
-#ifndef PR_SET_MM_ENV_END
-#define PR_SET_MM_ENV_END 11
-#endif
-
-#ifndef EFIVARFS_MAGIC
-#define EFIVARFS_MAGIC 0xde5e81e4
-#endif
-
-#ifndef SMACK_MAGIC
-#define SMACK_MAGIC 0x43415d53
-#endif
-
-#ifndef DM_DEFERRED_REMOVE
-#define DM_DEFERRED_REMOVE (1 << 17)
-#endif
-
-#ifndef MAX_HANDLE_SZ
-#define MAX_HANDLE_SZ 128
-#endif
-
-#if ! HAVE_SECURE_GETENV
-# if HAVE___SECURE_GETENV
-# define secure_getenv __secure_getenv
-# else
-# error "neither secure_getenv nor __secure_getenv are available"
-# endif
-#endif
-
-#ifndef CIFS_MAGIC_NUMBER
-# define CIFS_MAGIC_NUMBER 0xFF534D42
-#endif
-
-#ifndef TFD_TIMER_CANCEL_ON_SET
-# define TFD_TIMER_CANCEL_ON_SET (1 << 1)
-#endif
-
-#ifndef SO_REUSEPORT
-# define SO_REUSEPORT 15
-#endif
-
-#ifndef SO_PEERGROUPS
-# define SO_PEERGROUPS 59
-#endif
-
-#ifndef EVIOCREVOKE
-# define EVIOCREVOKE _IOW('E', 0x91, int)
-#endif
-
-#ifndef EVIOCSMASK
-
-struct input_mask {
- uint32_t type;
- uint32_t codes_size;
- uint64_t codes_ptr;
-};
-
-#define EVIOCSMASK _IOW('E', 0x93, struct input_mask)
-#endif
-
-#ifndef DRM_IOCTL_SET_MASTER
-# define DRM_IOCTL_SET_MASTER _IO('d', 0x1e)
-#endif
-
-#ifndef DRM_IOCTL_DROP_MASTER
-# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
-#endif
-
-/* The precise definition of __O_TMPFILE is arch specific; use the
- * values defined by the kernel (note: some are hexa, some are octal,
- * duplicated as-is from the kernel definitions):
- * - alpha, parisc, sparc: each has a specific value;
- * - others: they use the "generic" value.
- */
-
-#ifndef __O_TMPFILE
-#if defined(__alpha__)
-#define __O_TMPFILE 0100000000
-#elif defined(__parisc__) || defined(__hppa__)
-#define __O_TMPFILE 0400000000
-#elif defined(__sparc__) || defined(__sparc64__)
-#define __O_TMPFILE 0x2000000
-#else
-#define __O_TMPFILE 020000000
-#endif
-#endif
-
-/* a horrid kludge trying to make sure that this will fail on old kernels */
-#ifndef O_TMPFILE
-#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
-#endif
-
-#if !HAVE_LO_FLAGS_PARTSCAN
-#define LO_FLAGS_PARTSCAN 8
-#endif
-
-#ifndef LOOP_CTL_REMOVE
-#define LOOP_CTL_REMOVE 0x4C81
-#endif
-
-#ifndef LOOP_CTL_GET_FREE
-#define LOOP_CTL_GET_FREE 0x4C82
-#endif
-
-#if !HAVE_IFLA_INET6_ADDR_GEN_MODE
-#define IFLA_INET6_UNSPEC 0
-#define IFLA_INET6_FLAGS 1
-#define IFLA_INET6_CONF 2
-#define IFLA_INET6_STATS 3
-#define IFLA_INET6_MCAST 4
-#define IFLA_INET6_CACHEINFO 5
-#define IFLA_INET6_ICMP6STATS 6
-#define IFLA_INET6_TOKEN 7
-#define IFLA_INET6_ADDR_GEN_MODE 8
-#define __IFLA_INET6_MAX 9
-
-#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
-
-#define IN6_ADDR_GEN_MODE_EUI64 0
-#define IN6_ADDR_GEN_MODE_NONE 1
-#endif
-
-#if !HAVE_IN6_ADDR_GEN_MODE_STABLE_PRIVACY
-#define IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2
-#endif
-
-#if !HAVE_IFLA_MACVLAN_FLAGS
-#define IFLA_MACVLAN_UNSPEC 0
-#define IFLA_MACVLAN_MODE 1
-#define IFLA_MACVLAN_FLAGS 2
-#define __IFLA_MACVLAN_MAX 3
-
-#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_IPVLAN_FLAGS
-#define IFLA_IPVLAN_UNSPEC 0
-#define IFLA_IPVLAN_MODE 1
-#define IFLA_IPVLAN_FLAGS 2
-#define __IFLA_IPVLAN_MAX 3
-
-#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
-
-#define IPVLAN_MODE_L2 0
-#define IPVLAN_MODE_L3 1
-#define IPVLAN_MODE_L3S 2
-#define IPVLAN_MAX 2
-#endif
-
-#if !HAVE_IPVLAN_F_PRIVATE
-#define IPVLAN_F_PRIVATE 0x01
-#define IPVLAN_F_VEPA 0x02
-#define __IPVLAN_F_PRIVATE_MAX 3
-
-#define HAVE_IPVLAN_F_PRIVATE_MAX (__HAVE_IPVLAN_F_PRIVATE_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_VTI_REMOTE
-#define IFLA_VTI_UNSPEC 0
-#define IFLA_VTI_LINK 1
-#define IFLA_VTI_IKEY 2
-#define IFLA_VTI_OKEY 3
-#define IFLA_VTI_LOCAL 4
-#define IFLA_VTI_REMOTE 5
-#define __IFLA_VTI_MAX 6
-
-#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_PHYS_PORT_ID
-#define IFLA_EXT_MASK 29
-#undef IFLA_PROMISCUITY
-#define IFLA_PROMISCUITY 30
-#define IFLA_NUM_TX_QUEUES 31
-#define IFLA_NUM_RX_QUEUES 32
-#define IFLA_CARRIER 33
-#define IFLA_PHYS_PORT_ID 34
-#define __IFLA_MAX 35
-
-#define IFLA_MAX (__IFLA_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_BOND_AD_INFO
-#define IFLA_BOND_UNSPEC 0
-#define IFLA_BOND_MODE 1
-#define IFLA_BOND_ACTIVE_SLAVE 2
-#define IFLA_BOND_MIIMON 3
-#define IFLA_BOND_UPDELAY 4
-#define IFLA_BOND_DOWNDELAY 5
-#define IFLA_BOND_USE_CARRIER 6
-#define IFLA_BOND_ARP_INTERVAL 7
-#define IFLA_BOND_ARP_IP_TARGET 8
-#define IFLA_BOND_ARP_VALIDATE 9
-#define IFLA_BOND_ARP_ALL_TARGETS 10
-#define IFLA_BOND_PRIMARY 11
-#define IFLA_BOND_PRIMARY_RESELECT 12
-#define IFLA_BOND_FAIL_OVER_MAC 13
-#define IFLA_BOND_XMIT_HASH_POLICY 14
-#define IFLA_BOND_RESEND_IGMP 15
-#define IFLA_BOND_NUM_PEER_NOTIF 16
-#define IFLA_BOND_ALL_SLAVES_ACTIVE 17
-#define IFLA_BOND_MIN_LINKS 18
-#define IFLA_BOND_LP_INTERVAL 19
-#define IFLA_BOND_PACKETS_PER_SLAVE 20
-#define IFLA_BOND_AD_LACP_RATE 21
-#define IFLA_BOND_AD_SELECT 22
-#define IFLA_BOND_AD_INFO 23
-#define __IFLA_BOND_MAX 24
-
-#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_VLAN_PROTOCOL
-#define IFLA_VLAN_UNSPEC 0
-#define IFLA_VLAN_ID 1
-#define IFLA_VLAN_FLAGS 2
-#define IFLA_VLAN_EGRESS_QOS 3
-#define IFLA_VLAN_INGRESS_QOS 4
-#define IFLA_VLAN_PROTOCOL 5
-#define __IFLA_VLAN_MAX 6
-
-#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_VXLAN_GPE
-#define IFLA_VXLAN_UNSPEC 0
-#define IFLA_VXLAN_ID 1
-#define IFLA_VXLAN_GROUP 2
-#define IFLA_VXLAN_LINK 3
-#define IFLA_VXLAN_LOCAL 4
-#define IFLA_VXLAN_TTL 5
-#define IFLA_VXLAN_TOS 6
-#define IFLA_VXLAN_LEARNING 7
-#define IFLA_VXLAN_AGEING 8
-#define IFLA_VXLAN_LIMIT 9
-#define IFLA_VXLAN_PORT_RANGE 10
-#define IFLA_VXLAN_PROXY 11
-#define IFLA_VXLAN_RSC 12
-#define IFLA_VXLAN_L2MISS 13
-#define IFLA_VXLAN_L3MISS 14
-#define IFLA_VXLAN_PORT 15
-#define IFLA_VXLAN_GROUP6 16
-#define IFLA_VXLAN_LOCAL6 17
-#define IFLA_VXLAN_UDP_CSUM 18
-#define IFLA_VXLAN_UDP_ZERO_CSUM6_TX 19
-#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20
-#define IFLA_VXLAN_REMCSUM_TX 21
-#define IFLA_VXLAN_REMCSUM_RX 22
-#define IFLA_VXLAN_GBP 23
-#define IFLA_VXLAN_REMCSUM_NOPARTIAL 24
-#define IFLA_VXLAN_COLLECT_METADATA 25
-#define IFLA_VXLAN_LABEL 26
-#define IFLA_VXLAN_GPE 27
-
-#define __IFLA_VXLAN_MAX 28
-
-#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_GENEVE_LABEL
-#define IFLA_GENEVE_UNSPEC 0
-#define IFLA_GENEVE_ID 1
-#define IFLA_GENEVE_REMOTE 2
-#define IFLA_GENEVE_TTL 3
-#define IFLA_GENEVE_TOS 4
-#define IFLA_GENEVE_PORT 5
-#define IFLA_GENEVE_COLLECT_METADATA 6
-#define IFLA_GENEVE_REMOTE6 7
-#define IFLA_GENEVE_UDP_CSUM 8
-#define IFLA_GENEVE_UDP_ZERO_CSUM6_TX 9
-#define IFLA_GENEVE_UDP_ZERO_CSUM6_RX 10
-#define IFLA_GENEVE_LABEL 11
-
-#define __IFLA_GENEVE_MAX 12
-
-#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_IPTUN_ENCAP_DPORT
-#define IFLA_IPTUN_UNSPEC 0
-#define IFLA_IPTUN_LINK 1
-#define IFLA_IPTUN_LOCAL 2
-#define IFLA_IPTUN_REMOTE 3
-#define IFLA_IPTUN_TTL 4
-#define IFLA_IPTUN_TOS 5
-#define IFLA_IPTUN_ENCAP_LIMIT 6
-#define IFLA_IPTUN_FLOWINFO 7
-#define IFLA_IPTUN_FLAGS 8
-#define IFLA_IPTUN_PROTO 9
-#define IFLA_IPTUN_PMTUDISC 10
-#define IFLA_IPTUN_6RD_PREFIX 11
-#define IFLA_IPTUN_6RD_RELAY_PREFIX 12
-#define IFLA_IPTUN_6RD_PREFIXLEN 13
-#define IFLA_IPTUN_6RD_RELAY_PREFIXLEN 14
-#define IFLA_IPTUN_ENCAP_TYPE 15
-#define IFLA_IPTUN_ENCAP_FLAGS 16
-#define IFLA_IPTUN_ENCAP_SPORT 17
-#define IFLA_IPTUN_ENCAP_DPORT 18
-
-#define __IFLA_IPTUN_MAX 19
-
-#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_GRE_ENCAP_DPORT
-#define IFLA_GRE_UNSPEC 0
-#define IFLA_GRE_LINK 1
-#define IFLA_GRE_IFLAGS 2
-#define IFLA_GRE_OFLAGS 3
-#define IFLA_GRE_IKEY 4
-#define IFLA_GRE_OKEY 5
-#define IFLA_GRE_LOCAL 6
-#define IFLA_GRE_REMOTE 7
-#define IFLA_GRE_TTL 8
-#define IFLA_GRE_TOS 9
-#define IFLA_GRE_PMTUDISC 10
-#define IFLA_GRE_ENCAP_LIMIT 11
-#define IFLA_GRE_FLOWINFO 12
-#define IFLA_GRE_FLAGS 13
-#define IFLA_GRE_ENCAP_TYPE 14
-#define IFLA_GRE_ENCAP_FLAGS 15
-#define IFLA_GRE_ENCAP_SPORT 16
-#define IFLA_GRE_ENCAP_DPORT 17
-
-#define __IFLA_GRE_MAX 18
-
-#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_BRIDGE_VLAN_INFO
-#define IFLA_BRIDGE_FLAGS 0
-#define IFLA_BRIDGE_MODE 1
-#define IFLA_BRIDGE_VLAN_INFO 2
-#define __IFLA_BRIDGE_MAX 3
-
-#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
-#endif
-
-#ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN
-#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */
-#endif
-
-#ifndef BRIDGE_VLAN_INFO_RANGE_END
-#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */
-#endif
-
-#if !HAVE_IFLA_BR_VLAN_DEFAULT_PVID
-#define IFLA_BR_UNSPEC 0
-#define IFLA_BR_FORWARD_DELAY 1
-#define IFLA_BR_HELLO_TIME 2
-#define IFLA_BR_MAX_AGE 3
-#define IFLA_BR_AGEING_TIME 4
-#define IFLA_BR_STP_STATE 5
-#define IFLA_BR_PRIORITY 6
-#define IFLA_BR_VLAN_FILTERING 7
-#define IFLA_BR_VLAN_PROTOCOL 8
-#define IFLA_BR_GROUP_FWD_MASK 9
-#define IFLA_BR_ROOT_ID 10
-#define IFLA_BR_BRIDGE_ID 11
-#define IFLA_BR_ROOT_PORT 12
-#define IFLA_BR_ROOT_PATH_COST 13
-#define IFLA_BR_TOPOLOGY_CHANGE 14
-#define IFLA_BR_TOPOLOGY_CHANGE_DETECTED 15
-#define IFLA_BR_HELLO_TIMER 16
-#define IFLA_BR_TCN_TIMER 17
-#define IFLA_BR_TOPOLOGY_CHANGE_TIMER 18
-#define IFLA_BR_GC_TIMER 19
-#define IFLA_BR_GROUP_ADDR 20
-#define IFLA_BR_FDB_FLUSH 21
-#define IFLA_BR_MCAST_ROUTER 22
-#define IFLA_BR_MCAST_SNOOPING 23
-#define IFLA_BR_MCAST_QUERY_USE_IFADDR 24
-#define IFLA_BR_MCAST_QUERIER 25
-#define IFLA_BR_MCAST_HASH_ELASTICITY 26
-#define IFLA_BR_MCAST_HASH_MAX 27
-#define IFLA_BR_MCAST_LAST_MEMBER_CNT 28
-#define IFLA_BR_MCAST_STARTUP_QUERY_CNT 29
-#define IFLA_BR_MCAST_LAST_MEMBER_INTVL 30
-#define IFLA_BR_MCAST_MEMBERSHIP_INTVL 31
-#define IFLA_BR_MCAST_QUERIER_INTVL 32
-#define IFLA_BR_MCAST_QUERY_INTVL 33
-#define IFLA_BR_MCAST_QUERY_RESPONSE_INTVL 34
-#define IFLA_BR_MCAST_STARTUP_QUERY_INTVL 35
-#define IFLA_BR_NF_CALL_IPTABLES 36
-#define IFLA_BR_NF_CALL_IP6TABLES 37
-#define IFLA_BR_NF_CALL_ARPTABLES 38
-#define IFLA_BR_VLAN_DEFAULT_PVID 39
-#define __IFLA_BR_MAX 40
-
-#define IFLA_BR_MAX (__IFLA_BR_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_BRPORT_LEARNING_SYNC
-#define IFLA_BRPORT_UNSPEC 0
-#define IFLA_BRPORT_STATE 1
-#define IFLA_BRPORT_PRIORITY 2
-#define IFLA_BRPORT_COST 3
-#define IFLA_BRPORT_MODE 4
-#define IFLA_BRPORT_GUARD 5
-#define IFLA_BRPORT_PROTECT 6
-#define IFLA_BRPORT_FAST_LEAVE 7
-#define IFLA_BRPORT_LEARNING 8
-#define IFLA_BRPORT_UNICAST_FLOOD 9
-#define IFLA_BRPORT_LEARNING_SYNC 11
-#define __IFLA_BRPORT_MAX 12
-
-#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
-#endif
-
-#if !HAVE_FRA_UID_RANGE
-#define FRA_UNSPEC 0
-#define FRA_DST 1
-#define FRA_SRC 2
-#define FRA_IIFNAME 3
-#define FRA_GOTO 4
-#define FRA_UNUSED2 5
-#define FRA_PRIORITY 6
-#define FRA_UNUSED3 7
-#define FRA_UNUSED4 8
-#define FRA_UNUSED5 9
-#define FRA_FWMARK 10
-#define FRA_FLOW 11
-#define FRA_TUN_ID 12
-#define FRA_SUPPRESS_IFGROUP 13
-#define FRA_SUPPRESS_PREFIXLEN 14
-#define FRA_TABLE 15
-#define FRA_FWMASK 16
-#define FRA_OIFNAME 17
-#define FRA_PAD 18
-#define FRA_L3MDEV 19
-#define FRA_UID_RANGE 20
-#define __FRA_MAX 12
-
-#define FRA_MAX (__FRA_MAX - 1)
-#endif
-
-#if !HAVE_IFLA_BRPORT_PROXYARP
-#define IFLA_BRPORT_PROXYARP 10
-#endif
-
-#if !HAVE_IFLA_VRF_TABLE
-#define IFLA_VRF_TABLE 1
-#endif
-
-#if !HAVE_VXCAN_INFO_PEER
-#define VXCAN_INFO_PEER 1
-#endif
-
-#if !HAVE_NDA_IFINDEX
-#define NDA_UNSPEC 0
-#define NDA_DST 1
-#define NDA_LLADDR 2
-#define NDA_CACHEINFO 3
-#define NDA_PROBES 4
-#define NDA_VLAN 5
-#define NDA_PORT 6
-#define NDA_VNI 7
-#define NDA_IFINDEX 8
-#define __NDA_MAX 9
-
-#define NDA_MAX (__NDA_MAX - 1)
-#endif
-
-#ifndef RTA_PREF
-#define RTA_PREF 20
-#endif
-
-#ifndef RTAX_QUICKACK
-#define RTAX_QUICKACK 15
-#endif
-
-#ifndef RTA_EXPIRES
-#define RTA_EXPIRES 23
-#endif
-
-#ifndef IPV6_UNICAST_IF
-#define IPV6_UNICAST_IF 76
-#endif
-
-#ifndef IPV6_MIN_MTU
-#define IPV6_MIN_MTU 1280
-#endif
-
-#ifndef IPV4_MIN_MTU
-#define IPV4_MIN_MTU 68
-#endif
-
-#ifndef IFF_MULTI_QUEUE
-#define IFF_MULTI_QUEUE 0x100
-#endif
-
-#ifndef IFF_LOWER_UP
-#define IFF_LOWER_UP 0x10000
-#endif
-
-#ifndef IFF_DORMANT
-#define IFF_DORMANT 0x20000
-#endif
-
-#ifndef BOND_XMIT_POLICY_ENCAP23
-#define BOND_XMIT_POLICY_ENCAP23 3
-#endif
-
-#ifndef BOND_XMIT_POLICY_ENCAP34
-#define BOND_XMIT_POLICY_ENCAP34 4
-#endif
-
-#ifndef NET_ADDR_RANDOM
-# define NET_ADDR_RANDOM 1
-#endif
-
-#ifndef NET_NAME_UNKNOWN
-# define NET_NAME_UNKNOWN 0
-#endif
-
-#ifndef NET_NAME_ENUM
-# define NET_NAME_ENUM 1
-#endif
-
-#ifndef NET_NAME_PREDICTABLE
-# define NET_NAME_PREDICTABLE 2
-#endif
-
-#ifndef NET_NAME_USER
-# define NET_NAME_USER 3
-#endif
-
-#ifndef NET_NAME_RENAMED
-# define NET_NAME_RENAMED 4
-#endif
-
-#ifndef BPF_XOR
-# define BPF_XOR 0xa0
-#endif
-
-/* Note that LOOPBACK_IFINDEX is currently not exported by the
- * kernel/glibc, but hardcoded internally by the kernel. However, as
- * it is exported to userspace indirectly via rtnetlink and the
- * ioctls, and made use of widely we define it here too, in a way that
- * is compatible with the kernel's internal definition. */
-#ifndef LOOPBACK_IFINDEX
-#define LOOPBACK_IFINDEX 1
-#endif
-
-#if !HAVE_IFA_FLAGS
-#define IFA_FLAGS 8
-#endif
-
-#ifndef IFA_F_MANAGETEMPADDR
-#define IFA_F_MANAGETEMPADDR 0x100
-#endif
-
-#ifndef IFA_F_NOPREFIXROUTE
-#define IFA_F_NOPREFIXROUTE 0x200
-#endif
-
-#ifndef MAX_AUDIT_MESSAGE_LENGTH
-#define MAX_AUDIT_MESSAGE_LENGTH 8970
-#endif
-
-#ifndef AUDIT_NLGRP_MAX
-#define AUDIT_NLGRP_READLOG 1
-#endif
-
-#ifndef CAP_MAC_OVERRIDE
-#define CAP_MAC_OVERRIDE 32
-#endif
-
-#ifndef CAP_MAC_ADMIN
-#define CAP_MAC_ADMIN 33
-#endif
-
-#ifndef CAP_SYSLOG
-#define CAP_SYSLOG 34
-#endif
-
-#ifndef CAP_WAKE_ALARM
-#define CAP_WAKE_ALARM 35
-#endif
-
-#ifndef CAP_BLOCK_SUSPEND
-#define CAP_BLOCK_SUSPEND 36
-#endif
-
-#ifndef CAP_AUDIT_READ
-#define CAP_AUDIT_READ 37
-#endif
-
-#ifndef RENAME_NOREPLACE
-#define RENAME_NOREPLACE (1 << 0)
-#endif
-
-#ifndef KCMP_FILE
-#define KCMP_FILE 0
-#endif
-
-#ifndef INPUT_PROP_POINTING_STICK
-#define INPUT_PROP_POINTING_STICK 0x05
-#endif
-
-#ifndef INPUT_PROP_ACCELEROMETER
-#define INPUT_PROP_ACCELEROMETER 0x06
-#endif
-
-#ifndef BTN_DPAD_UP
-#define BTN_DPAD_UP 0x220
-#define BTN_DPAD_RIGHT 0x223
-#endif
-
-#ifndef KEY_ALS_TOGGLE
-#define KEY_ALS_TOGGLE 0x230
-#endif
-
-#if ! HAVE_KEY_SERIAL_T
-typedef int32_t key_serial_t;
-#endif
-
-#ifndef KEYCTL_JOIN_SESSION_KEYRING
-#define KEYCTL_JOIN_SESSION_KEYRING 1
-#endif
-
-#ifndef KEYCTL_CHOWN
-#define KEYCTL_CHOWN 4
-#endif
-
-#ifndef KEYCTL_SETPERM
-#define KEYCTL_SETPERM 5
-#endif
-
-#ifndef KEYCTL_DESCRIBE
-#define KEYCTL_DESCRIBE 6
-#endif
-
-#ifndef KEYCTL_LINK
-#define KEYCTL_LINK 8
-#endif
-
-#ifndef KEYCTL_READ
-#define KEYCTL_READ 11
-#endif
-
-#ifndef KEYCTL_SET_TIMEOUT
-#define KEYCTL_SET_TIMEOUT 15
-#endif
-
-#ifndef KEY_POS_VIEW
-#define KEY_POS_VIEW 0x01000000
-#define KEY_POS_READ 0x02000000
-#define KEY_POS_WRITE 0x04000000
-#define KEY_POS_SEARCH 0x08000000
-#define KEY_POS_LINK 0x10000000
-#define KEY_POS_SETATTR 0x20000000
-
-#define KEY_USR_VIEW 0x00010000
-#define KEY_USR_READ 0x00020000
-#define KEY_USR_WRITE 0x00040000
-#define KEY_USR_SEARCH 0x00080000
-#define KEY_USR_LINK 0x00100000
-#define KEY_USR_SETATTR 0x00200000
-
-#define KEY_GRP_VIEW 0x00000100
-#define KEY_GRP_READ 0x00000200
-#define KEY_GRP_WRITE 0x00000400
-#define KEY_GRP_SEARCH 0x00000800
-#define KEY_GRP_LINK 0x00001000
-#define KEY_GRP_SETATTR 0x00002000
-
-#define KEY_OTH_VIEW 0x00000001
-#define KEY_OTH_READ 0x00000002
-#define KEY_OTH_WRITE 0x00000004
-#define KEY_OTH_SEARCH 0x00000008
-#define KEY_OTH_LINK 0x00000010
-#define KEY_OTH_SETATTR 0x00000020
-#endif
-
-#ifndef KEY_SPEC_USER_KEYRING
-#define KEY_SPEC_USER_KEYRING -4
-#endif
-
-#ifndef KEY_SPEC_SESSION_KEYRING
-#define KEY_SPEC_SESSION_KEYRING -3
-#endif
-
-#ifndef PR_CAP_AMBIENT
-#define PR_CAP_AMBIENT 47
-#endif
-
-#ifndef PR_CAP_AMBIENT_IS_SET
-#define PR_CAP_AMBIENT_IS_SET 1
-#endif
-
-#ifndef PR_CAP_AMBIENT_RAISE
-#define PR_CAP_AMBIENT_RAISE 2
-#endif
-
-#ifndef PR_CAP_AMBIENT_CLEAR_ALL
-#define PR_CAP_AMBIENT_CLEAR_ALL 4
-#endif
-
-/* The following two defines are actually available in the kernel headers for longer, but we define them here anyway,
- * since that makes it easier to use them in conjunction with the glibc net/if.h header which conflicts with
- * linux/if.h. */
-#ifndef IF_OPER_UNKNOWN
-#define IF_OPER_UNKNOWN 0
-#endif
-
-#ifndef IF_OPER_UP
-#define IF_OPER_UP 6
-
-#if ! HAVE_CHAR32_T
-#define char32_t uint32_t
-#endif
-
-#if ! HAVE_CHAR16_T
-#define char16_t uint16_t
-#endif
-
-#ifndef ETHERTYPE_LLDP
-#define ETHERTYPE_LLDP 0x88cc
-#endif
-
-#ifndef IFA_F_MCAUTOJOIN
-#define IFA_F_MCAUTOJOIN 0x400
-#endif
-
-#if ! HAVE_STRUCT_ETHTOOL_LINK_SETTINGS
-
-#define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */
-#define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */
-
-struct ethtool_link_settings {
- __u32 cmd;
- __u32 speed;
- __u8 duplex;
- __u8 port;
- __u8 phy_address;
- __u8 autoneg;
- __u8 mdio_support;
- __u8 eth_tp_mdix;
- __u8 eth_tp_mdix_ctrl;
- __s8 link_mode_masks_nwords;
- __u32 reserved[8];
- __u32 link_mode_masks[0];
- /* layout of link_mode_masks fields:
- * __u32 map_supported[link_mode_masks_nwords];
- * __u32 map_advertising[link_mode_masks_nwords];
- * __u32 map_lp_advertising[link_mode_masks_nwords];
- */
-};
-
-#endif
-
-#if ! HAVE_STRUCT_FIB_RULE_UID_RANGE
-
-struct fib_rule_uid_range {
- __u32 start;
- __u32 end;
-};
-
-#endif
-
-#endif
-
-#ifndef SOL_ALG
-#define SOL_ALG 279
-#endif
-
-#ifndef AF_VSOCK
-#define AF_VSOCK 40
-#endif
-
-#ifndef EXT4_IOC_RESIZE_FS
-# define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
-#endif
-
-#ifndef NSFS_MAGIC
-#define NSFS_MAGIC 0x6e736673
-#endif
-
-#ifndef NS_GET_NSTYPE
-#define NS_GET_NSTYPE _IO(0xb7, 0x3)
-#endif
-
-#ifndef FALLOC_FL_KEEP_SIZE
-#define FALLOC_FL_KEEP_SIZE 0x01
-#endif
-
-#ifndef FALLOC_FL_PUNCH_HOLE
-#define FALLOC_FL_PUNCH_HOLE 0x02
-#endif
-
-#ifndef PF_KTHREAD
-#define PF_KTHREAD 0x00200000
-#endif
-
-#if ! HAVE_STRUCT_STATX
-struct statx_timestamp {
- int64_t tv_sec;
- uint32_t tv_nsec;
- uint32_t __reserved;
-};
-struct statx {
- uint32_t stx_mask;
- uint32_t stx_blksize;
- uint64_t stx_attributes;
- uint32_t stx_nlink;
- uint32_t stx_uid;
- uint32_t stx_gid;
- uint16_t stx_mode;
- uint16_t __spare0[1];
- uint64_t stx_ino;
- uint64_t stx_size;
- uint64_t stx_blocks;
- uint64_t stx_attributes_mask;
- struct statx_timestamp stx_atime;
- struct statx_timestamp stx_btime;
- struct statx_timestamp stx_ctime;
- struct statx_timestamp stx_mtime;
- uint32_t stx_rdev_major;
- uint32_t stx_rdev_minor;
- uint32_t stx_dev_major;
- uint32_t stx_dev_minor;
- uint64_t __spare2[14];
-};
-#endif
-
-#ifndef STATX_BTIME
-#define STATX_BTIME 0x00000800U
-#endif
-
-#ifndef AT_STATX_DONT_SYNC
-#define AT_STATX_DONT_SYNC 0x4000
-#endif
-
-/* The maximum thread/process name length including trailing NUL byte. This mimics the kernel definition of the same
- * name, which we need in userspace at various places but is not defined in userspace currently, neither under this
- * name nor any other. */
-#ifndef TASK_COMM_LEN
-#define TASK_COMM_LEN 16
-#endif
+#include "missing_audit.h"
+#include "missing_btrfs_tree.h"
+#include "missing_capability.h"
+#include "missing_drm.h"
+#include "missing_fcntl.h"
+#include "missing_fs.h"
+#include "missing_input.h"
+#include "missing_magic.h"
+#include "missing_mman.h"
+#include "missing_network.h"
+#include "missing_prctl.h"
+#include "missing_random.h"
+#include "missing_resource.h"
+#include "missing_sched.h"
+#include "missing_socket.h"
+#include "missing_stdlib.h"
+#include "missing_timerfd.h"
+#include "missing_type.h"
#include "missing_syscall.h"
diff --git a/src/basic/missing_audit.h b/src/basic/missing_audit.h
new file mode 100644
index 0000000000..b00d537be2
--- /dev/null
+++ b/src/basic/missing_audit.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/audit.h>
+
+#if HAVE_AUDIT
+#include <libaudit.h>
+#endif
+
+#ifndef AUDIT_SERVICE_START
+#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */
+#endif
+
+#ifndef AUDIT_SERVICE_STOP
+#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */
+#endif
+
+#ifndef MAX_AUDIT_MESSAGE_LENGTH
+#define MAX_AUDIT_MESSAGE_LENGTH 8970
+#endif
+
+#ifndef AUDIT_NLGRP_MAX
+#define AUDIT_NLGRP_READLOG 1
+#endif
diff --git a/src/basic/missing_btrfs.h b/src/basic/missing_btrfs.h
new file mode 100644
index 0000000000..34c382ff0b
--- /dev/null
+++ b/src/basic/missing_btrfs.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+/* Old btrfs.h requires stddef.h to be included before btrfs.h */
+#include <stddef.h>
+
+#include <linux/btrfs.h>
+
+/* linux@57254b6ebce4ceca02d9c8b615f6059c56c19238 (3.11) */
+#ifndef BTRFS_IOC_QUOTA_RESCAN_WAIT
+#define BTRFS_IOC_QUOTA_RESCAN_WAIT _IO(BTRFS_IOCTL_MAGIC, 46)
+#endif
+
+/* linux@83288b60bf6668933689078973136e0c9d387b38 (4.7) */
+#ifndef BTRFS_QGROUP_LIMIT_MAX_RFER
+#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0)
+#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1)
+#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2)
+#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3)
+#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4)
+#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5)
+#endif
diff --git a/src/basic/missing_btrfs_tree.h b/src/basic/missing_btrfs_tree.h
new file mode 100644
index 0000000000..555f90fe1b
--- /dev/null
+++ b/src/basic/missing_btrfs_tree.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/types.h>
+
+#include "missing_btrfs.h"
+
+/* linux@db6711600e27c885aed89751f04e727f3af26715 (4.7) */
+#if HAVE_LINUX_BTRFS_TREE_H
+#include <linux/btrfs_tree.h>
+#else
+#define BTRFS_ROOT_TREE_OBJECTID 1
+#define BTRFS_QUOTA_TREE_OBJECTID 8
+#define BTRFS_FIRST_FREE_OBJECTID 256
+#define BTRFS_LAST_FREE_OBJECTID -256ULL
+
+#define BTRFS_ROOT_ITEM_KEY 132
+#define BTRFS_ROOT_BACKREF_KEY 144
+#define BTRFS_QGROUP_STATUS_KEY 240
+#define BTRFS_QGROUP_INFO_KEY 242
+#define BTRFS_QGROUP_LIMIT_KEY 244
+#define BTRFS_QGROUP_RELATION_KEY 246
+
+struct btrfs_disk_key {
+ __le64 objectid;
+ __u8 type;
+ __le64 offset;
+} __attribute__ ((__packed__));
+
+struct btrfs_timespec {
+ __le64 sec;
+ __le32 nsec;
+} __attribute__ ((__packed__));
+
+struct btrfs_inode_item {
+ __le64 generation;
+ __le64 transid;
+ __le64 size;
+ __le64 nbytes;
+ __le64 block_group;
+ __le32 nlink;
+ __le32 uid;
+ __le32 gid;
+ __le32 mode;
+ __le64 rdev;
+ __le64 flags;
+ __le64 sequence;
+ __le64 reserved[4];
+ struct btrfs_timespec atime;
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec mtime;
+ struct btrfs_timespec otime;
+} __attribute__ ((__packed__));
+
+#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
+
+struct btrfs_root_item {
+ struct btrfs_inode_item inode;
+ __le64 generation;
+ __le64 root_dirid;
+ __le64 bytenr;
+ __le64 byte_limit;
+ __le64 bytes_used;
+ __le64 last_snapshot;
+ __le64 flags;
+ __le32 refs;
+ struct btrfs_disk_key drop_progress;
+ __u8 drop_level;
+ __u8 level;
+
+ __le64 generation_v2;
+ __u8 uuid[BTRFS_UUID_SIZE];
+ __u8 parent_uuid[BTRFS_UUID_SIZE];
+ __u8 received_uuid[BTRFS_UUID_SIZE];
+ __le64 ctransid; /* updated when an inode changes */
+ __le64 otransid; /* trans when created */
+ __le64 stransid; /* trans when sent. non-zero for received subvol */
+ __le64 rtransid; /* trans when received. non-zero for received subvol */
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec otime;
+ struct btrfs_timespec stime;
+ struct btrfs_timespec rtime;
+ __le64 reserved[8]; /* for future */
+} __attribute__ ((__packed__));
+
+struct btrfs_root_ref {
+ __le64 dirid;
+ __le64 sequence;
+ __le16 name_len;
+} __attribute__ ((__packed__));
+
+#define BTRFS_QGROUP_LEVEL_SHIFT 48
+
+struct btrfs_qgroup_info_item {
+ __le64 generation;
+ __le64 rfer;
+ __le64 rfer_cmpr;
+ __le64 excl;
+ __le64 excl_cmpr;
+} __attribute__ ((__packed__));
+
+struct btrfs_qgroup_limit_item {
+ __le64 flags;
+ __le64 max_rfer;
+ __le64 max_excl;
+ __le64 rsv_rfer;
+ __le64 rsv_excl;
+} __attribute__ ((__packed__));
+#endif
diff --git a/src/basic/missing_capability.h b/src/basic/missing_capability.h
new file mode 100644
index 0000000000..1308a3d636
--- /dev/null
+++ b/src/basic/missing_capability.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/capability.h>
+
+/* 3a101b8de0d39403b2c7e5c23fd0b005668acf48 (3.16) */
+#ifndef CAP_AUDIT_READ
+#define CAP_AUDIT_READ 37
+
+#undef CAP_LAST_CAP
+#define CAP_LAST_CAP CAP_AUDIT_READ
+#endif
diff --git a/src/basic/missing_drm.h b/src/basic/missing_drm.h
new file mode 100644
index 0000000000..a64f74efda
--- /dev/null
+++ b/src/basic/missing_drm.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#ifndef DRM_IOCTL_SET_MASTER
+#define DRM_IOCTL_SET_MASTER _IO('d', 0x1e)
+#endif
+
+#ifndef DRM_IOCTL_DROP_MASTER
+#define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
+#endif
diff --git a/src/basic/missing_ethtool.h b/src/basic/missing_ethtool.h
new file mode 100644
index 0000000000..9ba929c632
--- /dev/null
+++ b/src/basic/missing_ethtool.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/types.h>
+
+/* Missing definitions in ethtool.h */
+
+#if !HAVE_ETHTOOL_LINK_MODE_10baseT_Half_BIT /* linux@3f1ac7a700d039c61d8d8b99f28d605d489a60cf (4.6) */
+
+#define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */
+#define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */
+
+struct ethtool_link_settings {
+ __u32 cmd;
+ __u32 speed;
+ __u8 duplex;
+ __u8 port;
+ __u8 phy_address;
+ __u8 autoneg;
+ __u8 mdio_support;
+ __u8 eth_tp_mdix;
+ __u8 eth_tp_mdix_ctrl;
+ __s8 link_mode_masks_nwords;
+ __u8 transceiver;
+ __u8 reserved1[3];
+ __u32 reserved[7];
+ __u32 link_mode_masks[0];
+ /* layout of link_mode_masks fields:
+ * __u32 map_supported[link_mode_masks_nwords];
+ * __u32 map_advertising[link_mode_masks_nwords];
+ * __u32 map_lp_advertising[link_mode_masks_nwords];
+ */
+};
+
+enum ethtool_link_mode_bit_indices {
+ ETHTOOL_LINK_MODE_10baseT_Half_BIT = 0,
+ ETHTOOL_LINK_MODE_10baseT_Full_BIT = 1,
+ ETHTOOL_LINK_MODE_100baseT_Half_BIT = 2,
+ ETHTOOL_LINK_MODE_100baseT_Full_BIT = 3,
+ ETHTOOL_LINK_MODE_1000baseT_Half_BIT = 4,
+ ETHTOOL_LINK_MODE_1000baseT_Full_BIT = 5,
+ ETHTOOL_LINK_MODE_Autoneg_BIT = 6,
+ ETHTOOL_LINK_MODE_TP_BIT = 7,
+ ETHTOOL_LINK_MODE_AUI_BIT = 8,
+ ETHTOOL_LINK_MODE_MII_BIT = 9,
+ ETHTOOL_LINK_MODE_FIBRE_BIT = 10,
+ ETHTOOL_LINK_MODE_BNC_BIT = 11,
+ ETHTOOL_LINK_MODE_10000baseT_Full_BIT = 12,
+ ETHTOOL_LINK_MODE_Pause_BIT = 13,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT = 14,
+ ETHTOOL_LINK_MODE_2500baseX_Full_BIT = 15,
+ ETHTOOL_LINK_MODE_Backplane_BIT = 16,
+ ETHTOOL_LINK_MODE_1000baseKX_Full_BIT = 17,
+ ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT = 18,
+ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT = 19,
+ ETHTOOL_LINK_MODE_10000baseR_FEC_BIT = 20,
+ ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT = 21,
+ ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT = 22,
+ ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT = 23,
+ ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT = 24,
+ ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT = 25,
+ ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT = 26,
+ ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT = 27,
+ ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT = 28,
+ ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT = 29,
+ ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT = 30,
+ ETHTOOL_LINK_MODE_25000baseCR_Full_BIT = 31,
+ ETHTOOL_LINK_MODE_25000baseKR_Full_BIT = 32,
+ ETHTOOL_LINK_MODE_25000baseSR_Full_BIT = 33,
+ ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT = 34,
+ ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT = 35,
+ ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT = 36,
+ ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT = 37,
+ ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT = 38,
+ ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT = 39,
+ ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT = 40,
+ ETHTOOL_LINK_MODE_1000baseX_Full_BIT = 41,
+ ETHTOOL_LINK_MODE_10000baseCR_Full_BIT = 42,
+ ETHTOOL_LINK_MODE_10000baseSR_Full_BIT = 43,
+ ETHTOOL_LINK_MODE_10000baseLR_Full_BIT = 44,
+ ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT = 45,
+ ETHTOOL_LINK_MODE_10000baseER_Full_BIT = 46,
+ ETHTOOL_LINK_MODE_2500baseT_Full_BIT = 47,
+ ETHTOOL_LINK_MODE_5000baseT_Full_BIT = 48,
+
+ ETHTOOL_LINK_MODE_FEC_NONE_BIT = 49,
+ ETHTOOL_LINK_MODE_FEC_RS_BIT = 50,
+ ETHTOOL_LINK_MODE_FEC_BASER_BIT = 51,
+
+ /* Last allowed bit for __ETHTOOL_LINK_MODE_LEGACY_MASK is bit
+ * 31. Please do NOT define any SUPPORTED_* or ADVERTISED_*
+ * macro for bits > 31. The only way to use indices > 31 is to
+ * use the new ETHTOOL_GLINKSETTINGS/ETHTOOL_SLINKSETTINGS API.
+ */
+
+ __ETHTOOL_LINK_MODE_LAST
+ = ETHTOOL_LINK_MODE_FEC_BASER_BIT,
+};
+#else
+#if !HAVE_ETHTOOL_LINK_MODE_25000baseCR_Full_BIT /* linux@3851112e4737cd52aaeda0ce8d084be9ee128106 (4.7) */
+#define ETHTOOL_LINK_MODE_25000baseCR_Full_BIT 31
+#define ETHTOOL_LINK_MODE_25000baseKR_Full_BIT 32
+#define ETHTOOL_LINK_MODE_25000baseSR_Full_BIT 33
+#define ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT 34
+#define ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT 35
+#define ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT 36
+#define ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT 37
+#define ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT 38
+#define ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT 39
+#endif
+#if !HAVE_ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT /* linux@89da45b8b5b2187734a11038b8593714f964ffd1 (4.8) */
+#define ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT 40
+#endif
+#if !HAVE_ETHTOOL_LINK_MODE_1000baseX_Full_BIT /* linux@5711a98221443aec54c4c81ee98c6ae46acccb65 (4.9) */
+#define ETHTOOL_LINK_MODE_1000baseX_Full_BIT 41
+#define ETHTOOL_LINK_MODE_10000baseCR_Full_BIT 42
+#define ETHTOOL_LINK_MODE_10000baseSR_Full_BIT 43
+#define ETHTOOL_LINK_MODE_10000baseLR_Full_BIT 44
+#define ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT 45
+#define ETHTOOL_LINK_MODE_10000baseER_Full_BIT 46
+#endif
+#if !HAVE_ETHTOOL_LINK_MODE_2500baseT_Full_BIT /* linux@94842b4fc4d6b1691cfc86c6f5251f299d27f4ba (4.10) */
+#define ETHTOOL_LINK_MODE_2500baseT_Full_BIT 47
+#define ETHTOOL_LINK_MODE_5000baseT_Full_BIT 48
+#endif
+#if !HAVE_ETHTOOL_LINK_MODE_FEC_NONE_BIT /* linux@1a5f3da20bd966220931239fbd31e6ac6ff42251 (4.14) */
+#define ETHTOOL_LINK_MODE_FEC_NONE_BIT 49
+#define ETHTOOL_LINK_MODE_FEC_RS_BIT 50
+#define ETHTOOL_LINK_MODE_FEC_BASER_BIT 51
+#endif
+#endif
diff --git a/src/basic/missing_fcntl.h b/src/basic/missing_fcntl.h
new file mode 100644
index 0000000000..5d1c6352f4
--- /dev/null
+++ b/src/basic/missing_fcntl.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <fcntl.h>
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_SETPIPE_SZ
+#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
+#endif
+
+#ifndef F_GETPIPE_SZ
+#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
+
+#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
+#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
+#define F_SEAL_GROW 0x0004 /* prevent file from growing */
+#define F_SEAL_WRITE 0x0008 /* prevent writes */
+#endif
+
+#ifndef F_OFD_GETLK
+#define F_OFD_GETLK 36
+#define F_OFD_SETLK 37
+#define F_OFD_SETLKW 38
+#endif
+
+#ifndef MAX_HANDLE_SZ
+#define MAX_HANDLE_SZ 128
+#endif
+
+/* The precise definition of __O_TMPFILE is arch specific; use the
+ * values defined by the kernel (note: some are hexa, some are octal,
+ * duplicated as-is from the kernel definitions):
+ * - alpha, parisc, sparc: each has a specific value;
+ * - others: they use the "generic" value.
+ */
+
+#ifndef __O_TMPFILE
+#if defined(__alpha__)
+#define __O_TMPFILE 0100000000
+#elif defined(__parisc__) || defined(__hppa__)
+#define __O_TMPFILE 0400000000
+#elif defined(__sparc__) || defined(__sparc64__)
+#define __O_TMPFILE 0x2000000
+#else
+#define __O_TMPFILE 020000000
+#endif
+#endif
+
+/* a horrid kludge trying to make sure that this will fail on old kernels */
+#ifndef O_TMPFILE
+#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
+#endif
diff --git a/src/basic/missing_fib_rules.h b/src/basic/missing_fib_rules.h
new file mode 100644
index 0000000000..df120d7bcd
--- /dev/null
+++ b/src/basic/missing_fib_rules.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/types.h>
+
+#if !HAVE_FRA_TUN_ID /* linux@e7030878fc8448492b6e5cecd574043f63271298 (4.3) */
+#define FRA_TUN_ID 12
+#endif
+
+#if !HAVE_FRA_SUPPRESS_PREFIXLEN /* linux@6ef94cfafba159d6b1a902ccb3349ac6a34ff6ad, 73f5698e77219bfc3ea1903759fe8e20ab5b285e (3.12) */
+#define FRA_SUPPRESS_IFGROUP 13
+#define FRA_SUPPRESS_PREFIXLEN 14
+#endif
+
+#if !HAVE_FRA_PAD /* linux@b46f6ded906ef0be52a4881ba50a084aeca64d7e (4.7) */
+#define FRA_PAD 18
+#endif
+
+#if !HAVE_FRA_L3MDEV /* linux@96c63fa7393d0a346acfe5a91e0c7d4c7782641b (4.8) */
+#define FRA_L3MDEV 19
+#endif
+
+#if !HAVE_FRA_UID_RANGE /* linux@622ec2c9d52405973c9f1ca5116eb1c393adfc7d (4.10) */
+#define FRA_UID_RANGE 20
+
+struct fib_rule_uid_range {
+ __u32 start;
+ __u32 end;
+};
+#endif
+
+#if !HAVE_FRA_DPORT_RANGE /* linux@1b71af6053af1bd2f849e9fda4f71c1e3f145dcf, bfff4862653bb96001ab57c1edd6d03f48e5f035 (4.17) */
+#define FRA_PROTOCOL 21
+#define FRA_IP_PROTO 22
+#define FRA_SPORT_RANGE 23
+#define FRA_DPORT_RANGE 24
+
+#undef FRA_MAX
+#define FRA_MAX 24
+
+struct fib_rule_port_range {
+ __u16 start;
+ __u16 end;
+};
+#endif
diff --git a/src/basic/missing_fou.h b/src/basic/missing_fou.h
new file mode 100644
index 0000000000..d8c743577c
--- /dev/null
+++ b/src/basic/missing_fou.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !HAVE_LINUX_FOU_H /* linux@23461551c00628c3f3fe9cf837bf53cf8f212b63 (3.18) */
+
+#define FOU_GENL_NAME "fou"
+#define FOU_GENL_VERSION 0x1
+
+enum {
+ FOU_ATTR_UNSPEC,
+ FOU_ATTR_PORT, /* u16 */
+ FOU_ATTR_AF, /* u8 */
+ FOU_ATTR_IPPROTO, /* u8 */
+ FOU_ATTR_TYPE, /* u8 */
+ FOU_ATTR_REMCSUM_NOPARTIAL, /* flag */
+
+ __FOU_ATTR_MAX,
+};
+
+#define FOU_ATTR_MAX (__FOU_ATTR_MAX - 1)
+
+enum {
+ FOU_CMD_UNSPEC,
+ FOU_CMD_ADD,
+ FOU_CMD_DEL,
+ FOU_CMD_GET,
+
+ __FOU_CMD_MAX,
+};
+
+enum {
+ FOU_ENCAP_UNSPEC,
+ FOU_ENCAP_DIRECT,
+ FOU_ENCAP_GUE,
+};
+
+#define FOU_CMD_MAX (__FOU_CMD_MAX - 1)
+
+#else
+
+#if !HAVE_FOU_ATTR_REMCSUM_NOPARTIAL /* linux@fe881ef11cf0220f118816181930494d484c4883 (4.0) */
+#define FOU_ATTR_REMCSUM_NOPARTIAL 5
+
+#undef FOU_ATTR_MAX
+#define FOU_ATTR_MAX 5
+#endif
+
+#if !HAVE_FOU_CMD_GET /* linux@7a6c8c34e5b71ac50e39588e20b39494a9e1d8e5 (4.1) */
+#define FOU_CMD_GET 3
+
+#undef FOU_CMD_MAX
+#define FOU_CMD_MAX 3
+#endif
+
+#endif
diff --git a/src/basic/missing_fs.h b/src/basic/missing_fs.h
new file mode 100644
index 0000000000..48c1af0458
--- /dev/null
+++ b/src/basic/missing_fs.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+/* linux/fs.h */
+#ifndef RENAME_NOREPLACE /* 0a7c3937a1f23f8cb5fc77ae01661e9968a51d0c (3.15) */
+#define RENAME_NOREPLACE (1 << 0)
+#endif
+
+/* linux/fs.h or sys/mount.h */
+#ifndef MS_MOVE
+#define MS_MOVE 8192
+#endif
+
+#ifndef MS_REC
+#define MS_REC 16384
+#endif
+
+#ifndef MS_PRIVATE
+#define MS_PRIVATE (1<<18)
+#endif
+
+#ifndef MS_SLAVE
+#define MS_SLAVE (1<<19)
+#endif
+
+#ifndef MS_SHARED
+#define MS_SHARED (1<<20)
+#endif
+
+#ifndef MS_RELATIME
+#define MS_RELATIME (1<<21)
+#endif
+
+#ifndef MS_KERNMOUNT
+#define MS_KERNMOUNT (1<<22)
+#endif
+
+#ifndef MS_I_VERSION
+#define MS_I_VERSION (1<<23)
+#endif
+
+#ifndef MS_STRICTATIME
+#define MS_STRICTATIME (1<<24)
+#endif
+
+#ifndef MS_LAZYTIME
+#define MS_LAZYTIME (1<<25)
+#endif
+
+/* Not exposed yet. Defined at fs/ext4/ext4.h */
+#ifndef EXT4_IOC_RESIZE_FS
+#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
+#endif
+
+/* Not exposed yet. Defined at fs/cifs/cifsglob.h */
+#ifndef CIFS_MAGIC_NUMBER
+#define CIFS_MAGIC_NUMBER 0xFF534D42
+#endif
+
+/* linux/nsfs.h */
+#ifndef NS_GET_NSTYPE /* d95fa3c76a66b6d76b1e109ea505c55e66360f3c (4.11) */
+#define NS_GET_NSTYPE _IO(0xb7, 0x3)
+#endif
diff --git a/src/basic/missing_if_bridge.h b/src/basic/missing_if_bridge.h
new file mode 100644
index 0000000000..9306062fc2
--- /dev/null
+++ b/src/basic/missing_if_bridge.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !HAVE_IFLA_BRIDGE_VLAN_TUNNEL_INFO /* linux@b3c7ef0adadc5768e0baa786213c6bd1ce521a77 (4.11) */
+#define IFLA_BRIDGE_VLAN_TUNNEL_INFO 3
+
+#undef IFLA_BRIDGE_MAX
+#define IFLA_BRIDGE_MAX 3
+#endif
+
+#ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN
+#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */
+#endif
+
+#ifndef BRIDGE_VLAN_INFO_RANGE_END
+#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */
+#endif
+
+#ifndef BRIDGE_VLAN_INFO_BRENTRY
+#define BRIDGE_VLAN_INFO_BRENTRY (1<<5) /* Global bridge VLAN entry */
+#endif
diff --git a/src/basic/missing_if_link.h b/src/basic/missing_if_link.h
new file mode 100644
index 0000000000..07675426bb
--- /dev/null
+++ b/src/basic/missing_if_link.h
@@ -0,0 +1,390 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !HAVE_IFLA_INET6_ADDR_GEN_MODE /* linux@bc91b0f07ada5535427373a4e2050877bcc12218 (3.17) */
+#define IFLA_INET6_ADDR_GEN_MODE 8
+
+#undef IFLA_INET6_MAX
+#define IFLA_INET6_MAX 8
+
+enum in6_addr_gen_mode {
+ IN6_ADDR_GEN_MODE_EUI64,
+ IN6_ADDR_GEN_MODE_NONE,
+ IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+ IN6_ADDR_GEN_MODE_RANDOM,
+};
+#else
+#if !HAVE_IN6_ADDR_GEN_MODE_STABLE_PRIVACY /* linux@622c81d57b392cc9be836670eb464a4dfaa9adfe (4.1) */
+#define IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2
+#endif
+#if !HAVE_IN6_ADDR_GEN_MODE_RANDOM /* linux@cc9da6cc4f56e05cc9e591459fe0192727ff58b3 (4.5) */
+#define IN6_ADDR_GEN_MODE_RANDOM 3
+#endif
+#endif /* !HAVE_IFLA_INET6_ADDR_GEN_MODE */
+
+#if !HAVE_IFLA_IPVLAN_MODE /* linux@2ad7bf3638411cb547f2823df08166c13ab04269 (3.19) */
+enum {
+ IFLA_IPVLAN_UNSPEC,
+ IFLA_IPVLAN_MODE,
+ IFLA_IPVLAN_FLAGS,
+ __IFLA_IPVLAN_MAX
+};
+#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
+enum ipvlan_mode {
+ IPVLAN_MODE_L2 = 0,
+ IPVLAN_MODE_L3,
+ IPVLAN_MODE_L3S,
+ IPVLAN_MODE_MAX
+};
+#else
+#if !HAVE_IPVLAN_MODE_L3S /* linux@4fbae7d83c98c30efcf0a2a2ac55fbb75ef5a1a5 (4.9) */
+#define IPVLAN_MODE_L3S 2
+#define IPVLAN_MODE_MAX 3
+#endif
+#if !HAVE_IFLA_IPVLAN_FLAGS /* linux@a190d04db93710ae166749055b6985397c6d13f5 (4.15) */
+#define IFLA_IPVLAN_FLAGS 2
+
+#undef IFLA_IPVLAN_MAX
+#define IFLA_IPVLAN_MAX 2
+#endif
+#endif /* !HAVE_IFLA_IPVLAN_MODE */
+
+/* linux@a190d04db93710ae166749055b6985397c6d13f5 (4.15) */
+#ifndef IPVLAN_F_PRIVATE
+#define IPVLAN_F_PRIVATE 0x01
+#endif
+
+/* linux@fe89aa6b250c1011ccf425fbb7998e96bd54263f (4.15) */
+#ifndef IPVLAN_F_VEPA
+#define IPVLAN_F_VEPA 0x02
+#endif
+
+#if !HAVE_IFLA_PHYS_PORT_ID /* linux@66cae9ed6bc46b8cc57a9693f99f69926f3cc7ef (3.12) */
+#define IFLA_PHYS_PORT_ID 34
+#endif
+#if !HAVE_IFLA_CARRIER_CHANGES /* linux@2d3b479df41a10e2f41f9259fcba775bd34de6e4 (3.15) */
+#define IFLA_CARRIER_CHANGES 35
+#endif
+#if !HAVE_IFLA_PHYS_SWITCH_ID /* linux@82f2841291cfaf4d225aa1766424280254d3e3b2 (3.19) */
+#define IFLA_PHYS_SWITCH_ID 36
+#endif
+#if !HAVE_IFLA_LINK_NETNSID /* linux@d37512a277dfb2cef8a578e25a3246f61399a55a (4.0) */
+#define IFLA_LINK_NETNSID 37
+#endif
+#if !HAVE_IFLA_PHYS_PORT_NAME /* linux@db24a9044ee191c397dcd1c6574f56d67d7c8df5 (4.1) */
+#define IFLA_PHYS_PORT_NAME 38
+#endif
+#if !HAVE_IFLA_PROTO_DOWN /* linux@88d6378bd6c096cb8440face3ae3f33d55a2e6e4 (4.3) */
+#define IFLA_PROTO_DOWN 39
+#endif
+#if !HAVE_IFLA_GSO_MAX_SIZE /* linux@c70ce028e834f8e51306217dbdbd441d851c64d3 (4.6) */
+#define IFLA_GSO_MAX_SEGS 40
+#define IFLA_GSO_MAX_SIZE 41
+#endif
+#if !HAVE_IFLA_PAD /* linux@18402843bf88c2e9674e1a3a05c73b7d9b09ee05 (4.7) */
+#define IFLA_PAD 42
+#endif
+#if !HAVE_IFLA_XDP /* linux@d1fdd9138682e0f272beee0cb08b6328c5478b26 (4.8) */
+#define IFLA_XDP 43
+#endif
+#if !HAVE_IFLA_EVENT /* linux@3d3ea5af5c0b382bc9d9aed378fd814fb5d4a011 (4.13) */
+#define IFLA_EVENT 44
+#endif
+#if !HAVE_IFLA_IF_NETNSID /* linux@6621dd29eb9b5e6774ec7a9a75161352fdea47fc, 79e1ad148c844f5c8b9d76b36b26e3886dca95ae (4.15) */
+#define IFLA_IF_NETNSID 45
+#define IFLA_NEW_NETNSID 46
+#endif
+#if !HAVE_IFLA_TARGET_NETNSID /* linux@19d8f1ad12fd746e60707a58d954980013c7a35a (4.20) */
+#define IFLA_TARGET_NETNSID IFLA_IF_NETNSID
+#endif
+#if !HAVE_IFLA_NEW_IFINDEX /* linux@b2d3bcfa26a7a8de41f358a6cae8b848673b3c6e, 38e01b30563a5b5ade7b54e5d739d16a2b02fe82 (4.16) */
+#define IFLA_CARRIER_UP_COUNT 47
+#define IFLA_CARRIER_DOWN_COUNT 48
+#define IFLA_NEW_IFINDEX 49
+#endif
+#if !HAVE_IFLA_MAX_MTU /* linux@3e7a50ceb11ea75c27e944f1a01e478fd62a2d8d (4.19) */
+#define IFLA_MIN_MTU 50
+#define IFLA_MAX_MTU 51
+
+#undef IFLA_MAX
+#define IFLA_MAX 51
+#endif
+
+#if !HAVE_IFLA_BOND_ACTIVE_SLAVE /* linux@ec76aa49855f6d6fea5e01de179fb57dd47c619d (3.13) */
+#define IFLA_BOND_ACTIVE_SLAVE 2
+#endif
+#if !HAVE_IFLA_BOND_AD_INFO /* linux@4ee7ac7526d4a9413cafa733d824edfe49fdcc46 (3.14) */
+#define IFLA_BOND_MIIMON 3
+#define IFLA_BOND_UPDELAY 4
+#define IFLA_BOND_DOWNDELAY 5
+#define IFLA_BOND_USE_CARRIER 6
+#define IFLA_BOND_ARP_INTERVAL 7
+#define IFLA_BOND_ARP_IP_TARGET 8
+#define IFLA_BOND_ARP_VALIDATE 9
+#define IFLA_BOND_ARP_ALL_TARGETS 10
+#define IFLA_BOND_PRIMARY 11
+#define IFLA_BOND_PRIMARY_RESELECT 12
+#define IFLA_BOND_FAIL_OVER_MAC 13
+#define IFLA_BOND_XMIT_HASH_POLICY 14
+#define IFLA_BOND_RESEND_IGMP 15
+#define IFLA_BOND_NUM_PEER_NOTIF 16
+#define IFLA_BOND_ALL_SLAVES_ACTIVE 17
+#define IFLA_BOND_MIN_LINKS 18
+#define IFLA_BOND_LP_INTERVAL 19
+#define IFLA_BOND_PACKETS_PER_SLAVE 20
+#define IFLA_BOND_AD_LACP_RATE 21
+#define IFLA_BOND_AD_SELECT 22
+#define IFLA_BOND_AD_INFO 23
+#endif
+#if !HAVE_IFLA_BOND_AD_ACTOR_SYSTEM /* linux@171a42c38c6e1a5a076d6276e94e55a0b5b7868c (4.2) */
+#define IFLA_BOND_AD_ACTOR_SYS_PRIO 24
+#define IFLA_BOND_AD_USER_PORT_KEY 25
+#define IFLA_BOND_AD_ACTOR_SYSTEM 26
+#endif
+#if !HAVE_IFLA_BOND_TLB_DYNAMIC_LB /* linux@0f7bffd9e512b77279bbce704fad3cb1d6887958 (4.3) */
+#define IFLA_BOND_TLB_DYNAMIC_LB 27
+
+#undef IFLA_BOND_MAX
+#define IFLA_BOND_MAX 27
+#endif
+
+#if !HAVE_IFLA_VXLAN_UDP_ZERO_CSUM6_RX /* linux@359a0ea9875ef4f32c8425bbe1ae348e1fd2ed2a (3.16) */
+#define IFLA_VXLAN_UDP_CSUM 18
+#define IFLA_VXLAN_UDP_ZERO_CSUM6_TX 19
+#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20
+#endif
+#if !HAVE_IFLA_VXLAN_REMCSUM_NOPARTIAL /* linux@dfd8645ea1bd91277f841e74c33e1f4dbbede808..0ace2ca89cbd6bcdf2b9d2df1fa0fa24ea9d1653 (4.0) */
+#define IFLA_VXLAN_REMCSUM_TX 21
+#define IFLA_VXLAN_REMCSUM_RX 22
+#define IFLA_VXLAN_GBP 23
+#define IFLA_VXLAN_REMCSUM_NOPARTIAL 24
+#endif
+#if !HAVE_IFLA_VXLAN_COLLECT_METADATA /* linux@f8a9b1bc1b238eed9987da747a0e52f5bb009980 (4.3) */
+#define IFLA_VXLAN_COLLECT_METADATA 25
+#endif
+#if !HAVE_IFLA_VXLAN_LABEL /* linux@e7f70af111f086a20800ad2e17f544b2e3e0f375 (4.6) */
+#define IFLA_VXLAN_LABEL 26
+#endif
+#if !HAVE_IFLA_VXLAN_GPE /* linux@e1e5314de08ba6003b358125eafc9ad9e75a950c (4.7) */
+#define IFLA_VXLAN_GPE 27
+#endif
+#if !HAVE_IFLA_VXLAN_TTL_INHERIT /* linux@72f6d71e491e6ce269b564865b21fab0a4402dd3 (4.18) */
+#define IFLA_VXLAN_TTL_INHERIT 28
+
+#undef IFLA_VXLAN_MAX
+#define IFLA_VXLAN_MAX 28
+#endif
+
+#if !HAVE_IFLA_GENEVE_TOS /* linux@2d07dc79fe04a43d82a346ced6bbf07bdb523f1b..d89511251f6519599b109dc6cda87a6ab314ed8c (4.2) */
+enum {
+ IFLA_GENEVE_UNSPEC,
+ IFLA_GENEVE_ID,
+ IFLA_GENEVE_REMOTE,
+ IFLA_GENEVE_TTL,
+ IFLA_GENEVE_TOS,
+ IFLA_GENEVE_PORT, /* destination port */
+ IFLA_GENEVE_COLLECT_METADATA,
+ IFLA_GENEVE_REMOTE6,
+ IFLA_GENEVE_UDP_CSUM,
+ IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+ IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+ IFLA_GENEVE_LABEL,
+ IFLA_GENEVE_TTL_INHERIT,
+ __IFLA_GENEVE_MAX
+};
+#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
+#else
+#if !HAVE_IFLA_GENEVE_COLLECT_METADATA /* linux@e305ac6cf5a1e1386aedce7ef9cb773635d5845c (4.3) */
+#define IFLA_GENEVE_PORT 5
+#define IFLA_GENEVE_COLLECT_METADATA 6
+#endif
+#if !HAVE_IFLA_GENEVE_REMOTE6 /* linux@8ed66f0e8235118a31720acdab3bbbe9debd0f6a (4.4) */
+#define IFLA_GENEVE_REMOTE6 7
+#endif
+#if !HAVE_IFLA_GENEVE_UDP_ZERO_CSUM6_RX /* linux@abe492b4f50c3ae2ebcfaa2f5c16176aebaa1c68 (4.5) */
+#define IFLA_GENEVE_UDP_CSUM 8
+#define IFLA_GENEVE_UDP_ZERO_CSUM6_TX 9
+#define IFLA_GENEVE_UDP_ZERO_CSUM6_RX 10
+#endif
+#if !HAVE_IFLA_GENEVE_LABEL /* linux@8eb3b99554b82da968d1fbc00df9f3156c5e2d63 (4.6) */
+#define IFLA_GENEVE_LABEL 11
+#endif
+#if !HAVE_IFLA_GENEVE_TTL_INHERIT /* linux@52d0d404d39dd9eac71a181615d6ca15e23d8e38 (4.20) */
+#define IFLA_GENEVE_TTL_INHERIT 12
+
+#undef IFLA_GENEVE_MAX
+#define IFLA_GENEVE_MAX 12
+#endif
+#endif
+
+#if !HAVE_IFLA_BR_MAX_AGE /* linux@e5c3ea5c668033b303e7ac835d7d91da32d97958 (3.18) */
+enum {
+ IFLA_BR_UNSPEC,
+ IFLA_BR_FORWARD_DELAY,
+ IFLA_BR_HELLO_TIME,
+ IFLA_BR_MAX_AGE,
+ IFLA_BR_AGEING_TIME,
+ IFLA_BR_STP_STATE,
+ IFLA_BR_PRIORITY,
+ IFLA_BR_VLAN_FILTERING,
+ IFLA_BR_VLAN_PROTOCOL,
+ IFLA_BR_GROUP_FWD_MASK,
+ IFLA_BR_ROOT_ID,
+ IFLA_BR_BRIDGE_ID,
+ IFLA_BR_ROOT_PORT,
+ IFLA_BR_ROOT_PATH_COST,
+ IFLA_BR_TOPOLOGY_CHANGE,
+ IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+ IFLA_BR_HELLO_TIMER,
+ IFLA_BR_TCN_TIMER,
+ IFLA_BR_TOPOLOGY_CHANGE_TIMER,
+ IFLA_BR_GC_TIMER,
+ IFLA_BR_GROUP_ADDR,
+ IFLA_BR_FDB_FLUSH,
+ IFLA_BR_MCAST_ROUTER,
+ IFLA_BR_MCAST_SNOOPING,
+ IFLA_BR_MCAST_QUERY_USE_IFADDR,
+ IFLA_BR_MCAST_QUERIER,
+ IFLA_BR_MCAST_HASH_ELASTICITY,
+ IFLA_BR_MCAST_HASH_MAX,
+ IFLA_BR_MCAST_LAST_MEMBER_CNT,
+ IFLA_BR_MCAST_STARTUP_QUERY_CNT,
+ IFLA_BR_MCAST_LAST_MEMBER_INTVL,
+ IFLA_BR_MCAST_MEMBERSHIP_INTVL,
+ IFLA_BR_MCAST_QUERIER_INTVL,
+ IFLA_BR_MCAST_QUERY_INTVL,
+ IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
+ IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
+ IFLA_BR_NF_CALL_IPTABLES,
+ IFLA_BR_NF_CALL_IP6TABLES,
+ IFLA_BR_NF_CALL_ARPTABLES,
+ IFLA_BR_VLAN_DEFAULT_PVID,
+ IFLA_BR_PAD,
+ IFLA_BR_VLAN_STATS_ENABLED,
+ IFLA_BR_MCAST_STATS_ENABLED,
+ IFLA_BR_MCAST_IGMP_VERSION,
+ IFLA_BR_MCAST_MLD_VERSION,
+ IFLA_BR_VLAN_STATS_PER_PORT,
+ __IFLA_BR_MAX,
+};
+
+#define IFLA_BR_MAX (__IFLA_BR_MAX - 1)
+#else
+#if !HAVE_IFLA_BR_PRIORITY /* linux@af615762e972be0c66cf1d156ca4fac13b93c0b0 (4.1) */
+#define IFLA_BR_AGEING_TIME 4
+#define IFLA_BR_STP_STATE 5
+#define IFLA_BR_PRIORITY 6
+#endif
+#if !HAVE_IFLA_BR_VLAN_PROTOCOL /* linux@a7854037da006a7472c48773e3190db55217ec9b, d2d427b3927bd7a0348fc7f323d0e291f79a2779 (4.3) */
+#define IFLA_BR_VLAN_FILTERING 7
+#define IFLA_BR_VLAN_PROTOCOL 8
+#endif
+#if !HAVE_IFLA_BR_VLAN_DEFAULT_PVID /* linux@7910228b6bb35f3c8e0bc72a8d84c29616cb1b90..0f963b7592ef9e054974b6672b86ec1edd84b4bc (4.4) */
+#define IFLA_BR_GROUP_FWD_MASK 9
+#define IFLA_BR_ROOT_ID 10
+#define IFLA_BR_BRIDGE_ID 11
+#define IFLA_BR_ROOT_PORT 12
+#define IFLA_BR_ROOT_PATH_COST 13
+#define IFLA_BR_TOPOLOGY_CHANGE 14
+#define IFLA_BR_TOPOLOGY_CHANGE_DETECTED 15
+#define IFLA_BR_HELLO_TIMER 16
+#define IFLA_BR_TCN_TIMER 17
+#define IFLA_BR_TOPOLOGY_CHANGE_TIMER 18
+#define IFLA_BR_GC_TIMER 19
+#define IFLA_BR_GROUP_ADDR 20
+#define IFLA_BR_FDB_FLUSH 21
+#define IFLA_BR_MCAST_ROUTER 22
+#define IFLA_BR_MCAST_SNOOPING 23
+#define IFLA_BR_MCAST_QUERY_USE_IFADDR 24
+#define IFLA_BR_MCAST_QUERIER 25
+#define IFLA_BR_MCAST_HASH_ELASTICITY 26
+#define IFLA_BR_MCAST_HASH_MAX 27
+#define IFLA_BR_MCAST_LAST_MEMBER_CNT 28
+#define IFLA_BR_MCAST_STARTUP_QUERY_CNT 29
+#define IFLA_BR_MCAST_LAST_MEMBER_INTVL 30
+#define IFLA_BR_MCAST_MEMBERSHIP_INTVL 31
+#define IFLA_BR_MCAST_QUERIER_INTVL 32
+#define IFLA_BR_MCAST_QUERY_INTVL 33
+#define IFLA_BR_MCAST_QUERY_RESPONSE_INTVL 34
+#define IFLA_BR_MCAST_STARTUP_QUERY_INTVL 35
+#define IFLA_BR_NF_CALL_IPTABLES 36
+#define IFLA_BR_NF_CALL_IP6TABLES 37
+#define IFLA_BR_NF_CALL_ARPTABLES 38
+#define IFLA_BR_VLAN_DEFAULT_PVID 39
+#endif
+#if !HAVE_IFLA_BR_VLAN_STATS_ENABLED /* linux@12a0faa3bd76157b9dc096758d6818ff535e4586, 6dada9b10a0818ba72c249526a742c8c41274a73 (4.7) */
+#define IFLA_BR_PAD 40
+#define IFLA_BR_VLAN_STATS_ENABLED 41
+#endif
+#if !HAVE_IFLA_BR_MCAST_STATS_ENABLED /* linux@1080ab95e3c7bdd77870e209aff83c763fdcf439 (4.8) */
+#define IFLA_BR_MCAST_STATS_ENABLED 42
+#endif
+#if !HAVE_IFLA_BR_MCAST_MLD_VERSION /* linux@5e9235853d652a295d5f56cb8652950b6b5bf56b, aa2ae3e71c74cc00ec22f133dc900b3817415785 (4.10) */
+#define IFLA_BR_MCAST_IGMP_VERSION 43
+#define IFLA_BR_MCAST_MLD_VERSION 44
+#endif
+#if !HAVE_IFLA_BR_VLAN_STATS_PER_PORT /* linux@9163a0fc1f0c0980f117cc25f4fa6ba9b0750a36 (4.20) */
+#define IFLA_BR_VLAN_STATS_PER_PORT 45
+
+#undef IFLA_BR_MAX
+#define IFLA_BR_MAX 45
+#endif
+#endif
+
+#if !HAVE_IFLA_BRPORT_LEARNING_SYNC /* linux@958501163ddd6ea22a98f94fa0e7ce6d4734e5c4, efacacdaf7cb5a0592ed772e3731636b2742e34a (3.19)*/
+#define IFLA_BRPORT_PROXYARP 10
+#define IFLA_BRPORT_LEARNING_SYNC 11
+#endif
+#if !HAVE_IFLA_BRPORT_PROXYARP_WIFI /* linux@842a9ae08a25671db3d4f689eed68b4d64be15b5 (4.1) */
+#define IFLA_BRPORT_PROXYARP_WIFI 12
+#endif
+#if !HAVE_IFLA_BRPORT_MULTICAST_ROUTER /* linux@4ebc7660ab4559cad10b6595e05f70562bb26dc5..5d6ae479ab7ddf77bb22bdf739268581453ff886 (4.4) */
+#define IFLA_BRPORT_ROOT_ID 13
+#define IFLA_BRPORT_BRIDGE_ID 14
+#define IFLA_BRPORT_DESIGNATED_PORT 15
+#define IFLA_BRPORT_DESIGNATED_COST 16
+#define IFLA_BRPORT_ID 17
+#define IFLA_BRPORT_NO 18
+#define IFLA_BRPORT_TOPOLOGY_CHANGE_ACK 19
+#define IFLA_BRPORT_CONFIG_PENDING 20
+#define IFLA_BRPORT_MESSAGE_AGE_TIMER 21
+#define IFLA_BRPORT_FORWARD_DELAY_TIMER 22
+#define IFLA_BRPORT_HOLD_TIMER 23
+#define IFLA_BRPORT_FLUSH 24
+#define IFLA_BRPORT_MULTICAST_ROUTER 25
+#endif
+#if !HAVE_IFLA_BRPORT_PAD /* linux@12a0faa3bd76157b9dc096758d6818ff535e4586 (4.7) */
+#define IFLA_BRPORT_PAD 26
+#endif
+#if !HAVE_IFLA_BRPORT_MCAST_FLOOD /* linux@b6cb5ac8331b6bcfe9ce38c7f7f58db6e1d6270a (4.9) */
+#define IFLA_BRPORT_MCAST_FLOOD 27
+#endif
+#if !HAVE_IFLA_BRPORT_VLAN_TUNNEL /* linux@6db6f0eae6052b70885562e1733896647ec1d807, b3c7ef0adadc5768e0baa786213c6bd1ce521a77 (4.11) */
+#define IFLA_BRPORT_MCAST_TO_UCAST 28
+#define IFLA_BRPORT_VLAN_TUNNEL 29
+#endif
+#if !HAVE_IFLA_BRPORT_BCAST_FLOOD /* linux@99f906e9ad7b6e79ffeda30f45906a8448b9d6a2 (4.12) */
+#define IFLA_BRPORT_BCAST_FLOOD 30
+#endif
+#if !HAVE_IFLA_BRPORT_NEIGH_SUPPRESS /* linux@5af48b59f35cf712793badabe1a574a0d0ce3bd3, 821f1b21cabb46827ce39ddf82e2789680b5042a (4.15) */
+#define IFLA_BRPORT_GROUP_FWD_MASK 31
+#define IFLA_BRPORT_NEIGH_SUPPRESS 32
+#endif
+#if !HAVE_IFLA_BRPORT_ISOLATED /* linux@7d850abd5f4edb1b1ca4b4141a4453305736f564 (4.18) */
+#define IFLA_BRPORT_ISOLATED 33
+#endif
+#if !HAVE_IFLA_BRPORT_BACKUP_PORT /* linux@2756f68c314917d03eb348084edb08bb929139d9 (4.19) */
+#define IFLA_BRPORT_BACKUP_PORT 34
+
+#undef IFLA_BRPORT_MAX
+#define IFLA_BRPORT_MAX 34
+#endif
+
+#if !HAVE_IFLA_VRF_TABLE /* linux@4e3c89920cd3a6cfce22c6f537690747c26128dd (4.3) */
+enum {
+ IFLA_VRF_UNSPEC,
+ IFLA_VRF_TABLE,
+ __IFLA_VRF_MAX
+};
+#define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1)
+#endif
diff --git a/src/basic/missing_if_tunnel.h b/src/basic/missing_if_tunnel.h
new file mode 100644
index 0000000000..f51fdd1ed7
--- /dev/null
+++ b/src/basic/missing_if_tunnel.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !HAVE_IFLA_VTI_FWMARK /* linux@0a473b82cb23e7a35c4be6e9765c8487a65e8f55 (4.12) */
+#define IFLA_VTI_FWMARK 6
+
+#undef IFLA_VTI_MAX
+#define IFLA_VTI_MAX 6
+#endif
+
+#if !HAVE_IFLA_IPTUN_ENCAP_DPORT /* linux@56328486539ddd07cbaafec7a542a2c8a3043623 (3.18)*/
+#define IFLA_IPTUN_ENCAP_TYPE 15
+#define IFLA_IPTUN_ENCAP_FLAGS 16
+#define IFLA_IPTUN_ENCAP_SPORT 17
+#define IFLA_IPTUN_ENCAP_DPORT 18
+#endif
+
+#if !HAVE_IFLA_IPTUN_COLLECT_METADATA /* linux@cfc7381b3002756b1dcada32979e942aa3126e31 (4.9) */
+#define IFLA_IPTUN_COLLECT_METADATA 19
+#endif
+
+#if !HAVE_IFLA_IPTUN_FWMARK /* linux@0a473b82cb23e7a35c4be6e9765c8487a65e8f55 (4.12) */
+#define IFLA_IPTUN_FWMARK 20
+
+#undef IFLA_IPTUN_MAX
+#define IFLA_IPTUN_MAX 20
+#endif
+
+#if !HAVE_IFLA_GRE_ENCAP_DPORT /* linux@4565e9919cda747815547e2e5d7b78f15efbffdf (3.18) */
+#define IFLA_GRE_ENCAP_TYPE 14
+#define IFLA_GRE_ENCAP_FLAGS 15
+#define IFLA_GRE_ENCAP_SPORT 16
+#define IFLA_GRE_ENCAP_DPORT 17
+#endif
+
+#if !HAVE_IFLA_GRE_COLLECT_METADATA /* linux@2e15ea390e6f4466655066d97e22ec66870a042c (4.3) */
+#define IFLA_GRE_COLLECT_METADATA 18
+#endif
+
+#if !HAVE_IFLA_GRE_IGNORE_DF /* linux@22a59be8b7693eb2d0897a9638f5991f2f8e4ddd (4.8) */
+#define IFLA_GRE_IGNORE_DF 19
+#endif
+
+#if !HAVE_IFLA_GRE_FWMARK /* linux@0a473b82cb23e7a35c4be6e9765c8487a65e8f55 (4.12) */
+#define IFLA_GRE_FWMARK 20
+#endif
+
+#if !HAVE_IFLA_GRE_ERSPAN_INDEX /* linux@84e54fe0a5eaed696dee4019c396f8396f5a908b (4.14) */
+#define IFLA_GRE_ERSPAN_INDEX 21
+#endif
+
+#if !HAVE_IFLA_GRE_ERSPAN_HWID /* linux@f551c91de262ba36b20c3ac19538afb4f4507441 (4.16) */
+#define IFLA_GRE_ERSPAN_VER 22
+#define IFLA_GRE_ERSPAN_DIR 23
+#define IFLA_GRE_ERSPAN_HWID 24
+
+#undef IFLA_GRE_MAX
+#define IFLA_GRE_MAX 24
+#endif
diff --git a/src/basic/missing_input.h b/src/basic/missing_input.h
new file mode 100644
index 0000000000..b91ccb6485
--- /dev/null
+++ b/src/basic/missing_input.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/input.h>
+#include <linux/types.h>
+
+/* linux@c7dc65737c9a607d3e6f8478659876074ad129b8 (3.12) */
+#ifndef EVIOCREVOKE
+#define EVIOCREVOKE _IOW('E', 0x91, int)
+#endif
+
+/* linux@06a16293f71927f756dcf37558a79c0b05a91641 (4.4) */
+#ifndef EVIOCSMASK
+struct input_mask {
+ __u32 type;
+ __u32 codes_size;
+ __u64 codes_ptr;
+};
+
+#define EVIOCGMASK _IOR('E', 0x92, struct input_mask)
+#define EVIOCSMASK _IOW('E', 0x93, struct input_mask)
+#endif
+
+/* linux@7611392fe8ff95ecae528b01a815ae3d72ca6b95 (3.17) */
+#ifndef INPUT_PROP_POINTING_STICK
+#define INPUT_PROP_POINTING_STICK 0x05
+#endif
+
+/* linux@500d4160abe9a2e88b12e319c13ae3ebd1e18108 (4.0) */
+#ifndef INPUT_PROP_ACCELEROMETER
+#define INPUT_PROP_ACCELEROMETER 0x06
+#endif
+
+/* linux@d09bbfd2a8408a995419dff0d2ba906013cf4cc9 (3.11) */
+#ifndef BTN_DPAD_UP
+#define BTN_DPAD_UP 0x220
+#define BTN_DPAD_DOWN 0x221
+#define BTN_DPAD_LEFT 0x222
+#define BTN_DPAD_RIGHT 0x223
+#endif
+
+/* linux@358f24704f2f016af7d504b357cdf32606091d07 (3.13) */
+#ifndef KEY_ALS_TOGGLE
+#define KEY_ALS_TOGGLE 0x230
+#endif
diff --git a/src/basic/missing_keyctl.h b/src/basic/missing_keyctl.h
new file mode 100644
index 0000000000..7eb709586c
--- /dev/null
+++ b/src/basic/missing_keyctl.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/keyctl.h>
+
+#ifndef KEYCTL_JOIN_SESSION_KEYRING
+#define KEYCTL_JOIN_SESSION_KEYRING 1
+#endif
+
+#ifndef KEYCTL_CHOWN
+#define KEYCTL_CHOWN 4
+#endif
+
+#ifndef KEYCTL_SETPERM
+#define KEYCTL_SETPERM 5
+#endif
+
+#ifndef KEYCTL_DESCRIBE
+#define KEYCTL_DESCRIBE 6
+#endif
+
+#ifndef KEYCTL_LINK
+#define KEYCTL_LINK 8
+#endif
+
+#ifndef KEYCTL_READ
+#define KEYCTL_READ 11
+#endif
+
+#ifndef KEYCTL_SET_TIMEOUT
+#define KEYCTL_SET_TIMEOUT 15
+#endif
+
+#ifndef KEY_SPEC_USER_KEYRING
+#define KEY_SPEC_USER_KEYRING -4
+#endif
+
+#ifndef KEY_SPEC_SESSION_KEYRING
+#define KEY_SPEC_SESSION_KEYRING -3
+#endif
+
+/* From linux/key.h */
+#ifndef KEY_POS_VIEW
+
+typedef int32_t key_serial_t;
+
+#define KEY_POS_VIEW 0x01000000
+#define KEY_POS_READ 0x02000000
+#define KEY_POS_WRITE 0x04000000
+#define KEY_POS_SEARCH 0x08000000
+#define KEY_POS_LINK 0x10000000
+#define KEY_POS_SETATTR 0x20000000
+#define KEY_POS_ALL 0x3f000000
+
+#define KEY_USR_VIEW 0x00010000
+#define KEY_USR_READ 0x00020000
+#define KEY_USR_WRITE 0x00040000
+#define KEY_USR_SEARCH 0x00080000
+#define KEY_USR_LINK 0x00100000
+#define KEY_USR_SETATTR 0x00200000
+#define KEY_USR_ALL 0x003f0000
+
+#define KEY_GRP_VIEW 0x00000100
+#define KEY_GRP_READ 0x00000200
+#define KEY_GRP_WRITE 0x00000400
+#define KEY_GRP_SEARCH 0x00000800
+#define KEY_GRP_LINK 0x00001000
+#define KEY_GRP_SETATTR 0x00002000
+#define KEY_GRP_ALL 0x00003f00
+
+#define KEY_OTH_VIEW 0x00000001
+#define KEY_OTH_READ 0x00000002
+#define KEY_OTH_WRITE 0x00000004
+#define KEY_OTH_SEARCH 0x00000008
+#define KEY_OTH_LINK 0x00000010
+#define KEY_OTH_SETATTR 0x00000020
+#define KEY_OTH_ALL 0x0000003f
+#endif
diff --git a/src/basic/missing_magic.h b/src/basic/missing_magic.h
new file mode 100644
index 0000000000..4910cd368f
--- /dev/null
+++ b/src/basic/missing_magic.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/magic.h>
+
+/* 62aa81d7c4c24b90fdb61da70ac0dbbc414f9939 (4.13) */
+#ifndef OCFS2_SUPER_MAGIC
+#define OCFS2_SUPER_MAGIC 0x7461636f
+#endif
+
+/* 67e9c74b8a873408c27ac9a8e4c1d1c8d72c93ff (4.5) */
+#ifndef CGROUP2_SUPER_MAGIC
+#define CGROUP2_SUPER_MAGIC 0x63677270
+#endif
+
+/* 4282d60689d4f21b40692029080440cc58e8a17d (4.1) */
+#ifndef TRACEFS_MAGIC
+#define TRACEFS_MAGIC 0x74726163
+#endif
+
+/* e149ed2b805fefdccf7ccdfc19eca22fdd4514ac (3.19) */
+#ifndef NSFS_MAGIC
+#define NSFS_MAGIC 0x6e736673
+#endif
+
+/* b2197755b2633e164a439682fb05a9b5ea48f706 (4.4) */
+#ifndef BPF_FS_MAGIC
+#define BPF_FS_MAGIC 0xcafe4a11
+#endif
+
+/* Not exposed yet (4.20). Defined at ipc/mqueue.c */
+#ifndef MQUEUE_MAGIC
+#define MQUEUE_MAGIC 0x19800202
+#endif
diff --git a/src/basic/missing_mman.h b/src/basic/missing_mman.h
new file mode 100644
index 0000000000..7ff12f770b
--- /dev/null
+++ b/src/basic/missing_mman.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sys/mman.h>
+
+#ifndef MFD_ALLOW_SEALING
+#define MFD_ALLOW_SEALING 0x0002U
+#endif
+
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC 0x0001U
+#endif
diff --git a/src/basic/missing_network.h b/src/basic/missing_network.h
new file mode 100644
index 0000000000..59a8cd2c60
--- /dev/null
+++ b/src/basic/missing_network.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/loop.h>
+#include <linux/rtnetlink.h>
+#include <net/ethernet.h>
+
+#include "missing_ethtool.h"
+#include "missing_fib_rules.h"
+#include "missing_fou.h"
+#include "missing_if_bridge.h"
+#include "missing_if_link.h"
+#include "missing_if_tunnel.h"
+#include "missing_vxcan.h"
+
+/* if.h */
+/* The following two defines are actually available in the kernel headers for longer, but we define them here anyway,
+ * since that makes it easier to use them in conjunction with the glibc net/if.h header which conflicts with
+ * linux/if.h. */
+#ifndef IF_OPER_UNKNOWN
+#define IF_OPER_UNKNOWN 0
+#endif
+
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP 0x10000
+#endif
+
+#ifndef IFF_DORMANT
+#define IFF_DORMANT 0x20000
+#endif
+
+/* if_addr.h */
+#if !HAVE_IFA_FLAGS
+#define IFA_FLAGS 8
+#endif
+
+#ifndef IFA_F_MANAGETEMPADDR
+#define IFA_F_MANAGETEMPADDR 0x100
+#endif
+
+#ifndef IFA_F_NOPREFIXROUTE
+#define IFA_F_NOPREFIXROUTE 0x200
+#endif
+
+#ifndef IFA_F_MCAUTOJOIN
+#define IFA_F_MCAUTOJOIN 0x400
+#endif
+
+/* if_arp.h */
+#ifndef ARPHRD_IP6GRE
+#define ARPHRD_IP6GRE 823
+#endif
+
+/* if_bonding.h */
+#ifndef BOND_XMIT_POLICY_ENCAP23
+#define BOND_XMIT_POLICY_ENCAP23 3
+#endif
+
+#ifndef BOND_XMIT_POLICY_ENCAP34
+#define BOND_XMIT_POLICY_ENCAP34 4
+#endif
+
+/* if_tun.h */
+#ifndef IFF_MULTI_QUEUE
+#define IFF_MULTI_QUEUE 0x100
+#endif
+
+/* in6.h */
+#ifndef IPV6_UNICAST_IF
+#define IPV6_UNICAST_IF 76
+#endif
+
+/* ip.h */
+#ifndef IPV4_MIN_MTU
+#define IPV4_MIN_MTU 68
+#endif
+
+/* ipv6.h */
+#ifndef IPV6_MIN_MTU
+#define IPV6_MIN_MTU 1280
+#endif
+
+/* loop.h */
+#if !HAVE_LO_FLAGS_PARTSCAN
+#define LO_FLAGS_PARTSCAN 8
+#endif
+
+#ifndef LOOP_CTL_REMOVE
+#define LOOP_CTL_REMOVE 0x4C81
+#endif
+
+#ifndef LOOP_CTL_GET_FREE
+#define LOOP_CTL_GET_FREE 0x4C82
+#endif
+
+/* netdevice.h */
+#ifndef NET_ADDR_RANDOM
+#define NET_ADDR_RANDOM 1
+#endif
+
+#ifndef NET_NAME_UNKNOWN
+#define NET_NAME_UNKNOWN 0
+#endif
+
+#ifndef NET_NAME_ENUM
+#define NET_NAME_ENUM 1
+#endif
+
+#ifndef NET_NAME_PREDICTABLE
+#define NET_NAME_PREDICTABLE 2
+#endif
+
+#ifndef NET_NAME_USER
+#define NET_NAME_USER 3
+#endif
+
+#ifndef NET_NAME_RENAMED
+#define NET_NAME_RENAMED 4
+#endif
+
+/* netlink.h */
+#ifndef NETLINK_LIST_MEMBERSHIPS /* b42be38b2778eda2237fc759e55e3b698b05b315 (4.2) */
+#define NETLINK_LIST_MEMBERSHIPS 9
+#endif
+
+/* rtnetlink.h */
+#ifndef RTA_PREF
+#define RTA_PREF 20
+#endif
+
+#ifndef RTAX_QUICKACK
+#define RTAX_QUICKACK 15
+#endif
+
+#ifndef RTA_EXPIRES
+#define RTA_EXPIRES 23
+#endif
+
+/* Note that LOOPBACK_IFINDEX is currently not exported by the
+ * kernel/glibc, but hardcoded internally by the kernel. However, as
+ * it is exported to userspace indirectly via rtnetlink and the
+ * ioctls, and made use of widely we define it here too, in a way that
+ * is compatible with the kernel's internal definition. */
+#ifndef LOOPBACK_IFINDEX
+#define LOOPBACK_IFINDEX 1
+#endif
+
+/* Not exposed yet. Similar values are defined in net/ethernet.h */
+#ifndef ETHERTYPE_LLDP
+#define ETHERTYPE_LLDP 0x88cc
+#endif
diff --git a/src/basic/missing_prctl.h b/src/basic/missing_prctl.h
new file mode 100644
index 0000000000..f80cd17f34
--- /dev/null
+++ b/src/basic/missing_prctl.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/prctl.h>
+
+/* 58319057b7847667f0c9585b9de0e8932b0fdb08 (4.3) */
+#ifndef PR_CAP_AMBIENT
+#define PR_CAP_AMBIENT 47
+
+#define PR_CAP_AMBIENT_IS_SET 1
+#define PR_CAP_AMBIENT_RAISE 2
+#define PR_CAP_AMBIENT_LOWER 3
+#define PR_CAP_AMBIENT_CLEAR_ALL 4
+#endif
diff --git a/src/basic/missing_random.h b/src/basic/missing_random.h
new file mode 100644
index 0000000000..2e76031b32
--- /dev/null
+++ b/src/basic/missing_random.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if USE_SYS_RANDOM_H
+# include <sys/random.h>
+#else
+# include <linux/random.h>
+#endif
+
+#ifndef GRND_NONBLOCK
+#define GRND_NONBLOCK 0x0001
+#endif
+
+#ifndef GRND_RANDOM
+#define GRND_RANDOM 0x0002
+#endif
diff --git a/src/basic/missing_resource.h b/src/basic/missing_resource.h
new file mode 100644
index 0000000000..22ba8abfc6
--- /dev/null
+++ b/src/basic/missing_resource.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sys/resource.h>
+
+#ifndef RLIMIT_RTTIME
+#define RLIMIT_RTTIME 15
+#endif
+
+/* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */
+#define _RLIMIT_MAX (RLIMIT_RTTIME+1 > RLIMIT_NLIMITS ? RLIMIT_RTTIME+1 : RLIMIT_NLIMITS)
diff --git a/src/basic/missing_sched.h b/src/basic/missing_sched.h
new file mode 100644
index 0000000000..baa3913283
--- /dev/null
+++ b/src/basic/missing_sched.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sched.h>
+
+#ifndef CLONE_NEWCGROUP
+#define CLONE_NEWCGROUP 0x02000000
+#endif
+
+/* Not exposed yet. Defined at include/linux/sched.h */
+#ifndef PF_KTHREAD
+#define PF_KTHREAD 0x00200000
+#endif
+
+/* The maximum thread/process name length including trailing NUL byte. This mimics the kernel definition of the same
+ * name, which we need in userspace at various places but is not defined in userspace currently, neither under this
+ * name nor any other. */
+/* Not exposed yet. Defined at include/linux/sched.h */
+#ifndef TASK_COMM_LEN
+#define TASK_COMM_LEN 16
+#endif
diff --git a/src/basic/missing_securebits.h b/src/basic/missing_securebits.h
new file mode 100644
index 0000000000..40d6ec9d71
--- /dev/null
+++ b/src/basic/missing_securebits.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <linux/securebits.h>
+
+/* 746bf6d64275be0c65b0631d8a72b16f1454cfa1 (4.3) */
+#ifndef SECURE_NO_CAP_AMBIENT_RAISE
+#define SECURE_NO_CAP_AMBIENT_RAISE 6
+#define SECURE_NO_CAP_AMBIENT_RAISE_LOCKED 7 /* make bit-6 immutable */
+#define SECBIT_NO_CAP_AMBIENT_RAISE (issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE))
+#define SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED (issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE_LOCKED))
+
+#undef SECURE_ALL_BITS
+#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \
+ issecure_mask(SECURE_NO_SETUID_FIXUP) | \
+ issecure_mask(SECURE_KEEP_CAPS) | \
+ issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE))
+#endif
diff --git a/src/basic/missing_socket.h b/src/basic/missing_socket.h
new file mode 100644
index 0000000000..a5fd457244
--- /dev/null
+++ b/src/basic/missing_socket.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sys/socket.h>
+
+#if HAVE_LINUX_VM_SOCKETS_H
+#include <linux/vm_sockets.h>
+#else
+#define VMADDR_CID_ANY -1U
+struct sockaddr_vm {
+ unsigned short svm_family;
+ unsigned short svm_reserved1;
+ unsigned int svm_port;
+ unsigned int svm_cid;
+ unsigned char svm_zero[sizeof(struct sockaddr) -
+ sizeof(unsigned short) -
+ sizeof(unsigned short) -
+ sizeof(unsigned int) -
+ sizeof(unsigned int)];
+};
+#endif /* !HAVE_LINUX_VM_SOCKETS_H */
+
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+
+#ifndef SO_REUSEPORT
+#define SO_REUSEPORT 15
+#endif
+
+#ifndef SO_PEERGROUPS
+#define SO_PEERGROUPS 59
+#endif
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
+/* Not exposed yet. Defined in include/linux/socket.h. */
+#ifndef SOL_SCTP
+#define SOL_SCTP 132
+#endif
+
+/* Not exposed yet. Defined in include/linux/socket.h */
+#ifndef SCM_SECURITY
+#define SCM_SECURITY 0x03
+#endif
+
+/* netinet/in.h */
+#ifndef IP_FREEBIND
+#define IP_FREEBIND 15
+#endif
+
+#ifndef IP_TRANSPARENT
+#define IP_TRANSPARENT 19
+#endif
diff --git a/src/basic/missing_stat.h b/src/basic/missing_stat.h
new file mode 100644
index 0000000000..5116206a2e
--- /dev/null
+++ b/src/basic/missing_stat.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/types.h>
+#include <sys/stat.h>
+
+#if WANT_LINUX_STAT_H
+#include <linux/stat.h>
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#if !HAVE_STRUCT_STATX
+struct statx_timestamp {
+ __s64 tv_sec;
+ __u32 tv_nsec;
+ __s32 __reserved;
+};
+struct statx {
+ __u32 stx_mask;
+ __u32 stx_blksize;
+ __u64 stx_attributes;
+ __u32 stx_nlink;
+ __u32 stx_uid;
+ __u32 stx_gid;
+ __u16 stx_mode;
+ __u16 __spare0[1];
+ __u64 stx_ino;
+ __u64 stx_size;
+ __u64 stx_blocks;
+ __u64 stx_attributes_mask;
+ struct statx_timestamp stx_atime;
+ struct statx_timestamp stx_btime;
+ struct statx_timestamp stx_ctime;
+ struct statx_timestamp stx_mtime;
+ __u32 stx_rdev_major;
+ __u32 stx_rdev_minor;
+ __u32 stx_dev_major;
+ __u32 stx_dev_minor;
+ __u64 __spare2[14];
+};
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_BTIME
+#define STATX_BTIME 0x00000800U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef AT_STATX_DONT_SYNC
+#define AT_STATX_DONT_SYNC 0x4000
+#endif
diff --git a/src/basic/missing_stdlib.h b/src/basic/missing_stdlib.h
new file mode 100644
index 0000000000..188a8d4406
--- /dev/null
+++ b/src/basic/missing_stdlib.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdlib.h>
+
+/* stdlib.h */
+#if !HAVE_SECURE_GETENV
+# if HAVE___SECURE_GETENV
+# define secure_getenv __secure_getenv
+# else
+# error "neither secure_getenv nor __secure_getenv are available"
+# endif
+#endif
diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h
index 93c60458bf..d5d4b26acb 100644
--- a/src/basic/missing_syscall.h
+++ b/src/basic/missing_syscall.h
@@ -3,7 +3,22 @@
/* Missing glibc definitions to access certain kernel APIs */
+#include <fcntl.h>
+#include <sys/syscall.h>
#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef ARCH_MIPS
+#include <asm/sgidefs.h>
+#endif
+
+#include "missing_keyctl.h"
+#include "missing_stat.h"
+
+/* linux/kcmp.h */
+#ifndef KCMP_FILE /* 3f4994cfc15f38a3159c6e3a4b3ab2e1481a6b02 (3.19) */
+#define KCMP_FILE 0
+#endif
#if !HAVE_PIVOT_ROOT
static inline int missing_pivot_root(const char *new_root, const char *put_old) {
@@ -252,7 +267,7 @@ static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long i
/* ======================================================================= */
#if !HAVE_KEYCTL
-static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) {
+static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) {
# ifdef __NR_keyctl
return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
# else
diff --git a/src/basic/missing_timerfd.h b/src/basic/missing_timerfd.h
new file mode 100644
index 0000000000..6b0404453a
--- /dev/null
+++ b/src/basic/missing_timerfd.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sys/timerfd.h>
+
+#ifndef TFD_TIMER_CANCEL_ON_SET
+#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+#endif
diff --git a/src/basic/missing_type.h b/src/basic/missing_type.h
new file mode 100644
index 0000000000..bf8a6caa1b
--- /dev/null
+++ b/src/basic/missing_type.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <uchar.h>
+
+#if !HAVE_CHAR32_T
+#define char32_t uint32_t
+#endif
+
+#if !HAVE_CHAR16_T
+#define char16_t uint16_t
+#endif
diff --git a/src/basic/missing_vxcan.h b/src/basic/missing_vxcan.h
new file mode 100644
index 0000000000..be430f708d
--- /dev/null
+++ b/src/basic/missing_vxcan.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !HAVE_LINUX_CAN_VXCAN_H /* linux@a8f820a380a2a06fc4fe1a54159067958f800929 (4.12) */
+enum {
+ VXCAN_INFO_UNSPEC,
+ VXCAN_INFO_PEER,
+
+ __VXCAN_INFO_MAX
+#define VXCAN_INFO_MAX (__VXCAN_INFO_MAX - 1)
+};
+#endif
diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c
index 1d51e92e9a..0eba7fc514 100644
--- a/src/basic/mkdir-label.c
+++ b/src/basic/mkdir-label.c
@@ -28,6 +28,23 @@ int mkdir_label(const char *path, mode_t mode) {
return mac_smack_fix(path, 0);
}
+int mkdirat_label(int dirfd, const char *path, mode_t mode) {
+ int r;
+
+ assert(path);
+
+ r = mac_selinux_create_file_prepare_at(dirfd, path, S_IFDIR);
+ if (r < 0)
+ return r;
+
+ r = mkdirat_errno_wrapper(dirfd, path, mode);
+ mac_selinux_create_file_clear();
+ if (r < 0)
+ return r;
+
+ return mac_smack_fix_at(dirfd, path, 0);
+}
+
int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
return mkdir_safe_internal(path, mode, uid, gid, flags, mkdir_label);
}
diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c
index 6ab1b4422b..4bb65d5838 100644
--- a/src/basic/mkdir.c
+++ b/src/basic/mkdir.c
@@ -80,6 +80,12 @@ int mkdir_errno_wrapper(const char *pathname, mode_t mode) {
return 0;
}
+int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode) {
+ if (mkdirat(dirfd, pathname, mode) < 0)
+ return -errno;
+ return 0;
+}
+
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
return mkdir_safe_internal(path, mode, uid, gid, flags, mkdir_errno_wrapper);
}
diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h
index 8e83fb102b..eb54853ea7 100644
--- a/src/basic/mkdir.h
+++ b/src/basic/mkdir.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <sys/types.h>
typedef enum MkdirFlags {
@@ -10,6 +9,7 @@ typedef enum MkdirFlags {
} MkdirFlags;
int mkdir_errno_wrapper(const char *pathname, mode_t mode);
+int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode);
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
int mkdir_parents(const char *path, mode_t mode);
int mkdir_p(const char *path, mode_t mode);
diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c
new file mode 100644
index 0000000000..1e946a0bb6
--- /dev/null
+++ b/src/basic/mountpoint-util.c
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio_ext.h>
+#include <sys/mount.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "missing.h"
+#include "mountpoint-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "stdio-util.h"
+#include "strv.h"
+
+/* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
+ * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
+ * is recompiled then it would cease working on old kernels, as those refuse any sizes larger than this value with
+ * EINVAL right-away. Hence, let's disconnect ourselves from any such API changes, and stick to the original definition
+ * from when it was introduced. We use it as a start value only anyway (see below), and hence should be able to deal
+ * with large file handles anyway. */
+#define ORIGINAL_MAX_HANDLE_SZ 128
+
+int name_to_handle_at_loop(
+ int fd,
+ const char *path,
+ struct file_handle **ret_handle,
+ int *ret_mnt_id,
+ int flags) {
+
+ _cleanup_free_ struct file_handle *h = NULL;
+ size_t n = ORIGINAL_MAX_HANDLE_SZ;
+
+ /* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
+ * buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
+ * start value, it is not an upper bound on the buffer size required.
+ *
+ * This improves on raw name_to_handle_at() also in one other regard: ret_handle and ret_mnt_id can be passed
+ * as NULL if there's no interest in either. */
+
+ for (;;) {
+ int mnt_id = -1;
+
+ h = malloc0(offsetof(struct file_handle, f_handle) + n);
+ if (!h)
+ return -ENOMEM;
+
+ h->handle_bytes = n;
+
+ if (name_to_handle_at(fd, path, h, &mnt_id, flags) >= 0) {
+
+ if (ret_handle)
+ *ret_handle = TAKE_PTR(h);
+
+ if (ret_mnt_id)
+ *ret_mnt_id = mnt_id;
+
+ return 0;
+ }
+ if (errno != EOVERFLOW)
+ return -errno;
+
+ if (!ret_handle && ret_mnt_id && mnt_id >= 0) {
+
+ /* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the
+ * buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to
+ * be filled in, and the caller was interested in only the mount ID an nothing else. */
+
+ *ret_mnt_id = mnt_id;
+ return 0;
+ }
+
+ /* If name_to_handle_at() didn't increase the byte size, then this EOVERFLOW is caused by something
+ * else (apparently EOVERFLOW is returned for untriggered nfs4 mounts sometimes), not by the too small
+ * buffer. In that case propagate EOVERFLOW */
+ if (h->handle_bytes <= n)
+ return -EOVERFLOW;
+
+ /* The buffer was too small. Size the new buffer by what name_to_handle_at() returned. */
+ n = h->handle_bytes;
+ if (offsetof(struct file_handle, f_handle) + n < n) /* check for addition overflow */
+ return -EOVERFLOW;
+
+ h = mfree(h);
+ }
+}
+
+static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
+ char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
+ _cleanup_free_ char *fdinfo = NULL;
+ _cleanup_close_ int subfd = -1;
+ char *p;
+ int r;
+
+ if ((flags & AT_EMPTY_PATH) && isempty(filename))
+ xsprintf(path, "/proc/self/fdinfo/%i", fd);
+ else {
+ subfd = openat(fd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_FOLLOW ? 0 : O_NOFOLLOW));
+ if (subfd < 0)
+ return -errno;
+
+ xsprintf(path, "/proc/self/fdinfo/%i", subfd);
+ }
+
+ r = read_full_file(path, &fdinfo, NULL);
+ if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
+ return -EOPNOTSUPP;
+ if (r < 0)
+ return r;
+
+ p = startswith(fdinfo, "mnt_id:");
+ if (!p) {
+ p = strstr(fdinfo, "\nmnt_id:");
+ if (!p) /* The mnt_id field is a relatively new addition */
+ return -EOPNOTSUPP;
+
+ p += 8;
+ }
+
+ p += strspn(p, WHITESPACE);
+ p[strcspn(p, WHITESPACE)] = 0;
+
+ return safe_atoi(p, mnt_id);
+}
+
+int fd_is_mount_point(int fd, const char *filename, int flags) {
+ _cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
+ int mount_id = -1, mount_id_parent = -1;
+ bool nosupp = false, check_st_dev = true;
+ struct stat a, b;
+ int r;
+
+ assert(fd >= 0);
+ assert(filename);
+
+ /* First we will try the name_to_handle_at() syscall, which
+ * tells us the mount id and an opaque file "handle". It is
+ * not supported everywhere though (kernel compile-time
+ * option, not all file systems are hooked up). If it works
+ * the mount id is usually good enough to tell us whether
+ * something is a mount point.
+ *
+ * If that didn't work we will try to read the mount id from
+ * /proc/self/fdinfo/<fd>. This is almost as good as
+ * name_to_handle_at(), however, does not return the
+ * opaque file handle. The opaque file handle is pretty useful
+ * to detect the root directory, which we should always
+ * consider a mount point. Hence we use this only as
+ * fallback. Exporting the mnt_id in fdinfo is a pretty recent
+ * kernel addition.
+ *
+ * As last fallback we do traditional fstat() based st_dev
+ * comparisons. This is how things were traditionally done,
+ * but unionfs breaks this since it exposes file
+ * systems with a variety of st_dev reported. Also, btrfs
+ * subvolumes have different st_dev, even though they aren't
+ * real mounts of their own. */
+
+ r = name_to_handle_at_loop(fd, filename, &h, &mount_id, flags);
+ if (IN_SET(r, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL))
+ /* This kernel does not support name_to_handle_at() at all (ENOSYS), or the syscall was blocked
+ * (EACCES/EPERM; maybe through seccomp, because we are running inside of a container?), or the mount
+ * point is not triggered yet (EOVERFLOW, think nfs4), or some general name_to_handle_at() flakiness
+ * (EINVAL): fall back to simpler logic. */
+ goto fallback_fdinfo;
+ else if (r == -EOPNOTSUPP)
+ /* This kernel or file system does not support name_to_handle_at(), hence let's see if the upper fs
+ * supports it (in which case it is a mount point), otherwise fallback to the traditional stat()
+ * logic */
+ nosupp = true;
+ else if (r < 0)
+ return r;
+
+ r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
+ if (r == -EOPNOTSUPP) {
+ if (nosupp)
+ /* Neither parent nor child do name_to_handle_at()? We have no choice but to fall back. */
+ goto fallback_fdinfo;
+ else
+ /* The parent can't do name_to_handle_at() but the directory we are interested in can? If so,
+ * it must be a mount point. */
+ return 1;
+ } else if (r < 0)
+ return r;
+
+ /* The parent can do name_to_handle_at() but the
+ * directory we are interested in can't? If so, it
+ * must be a mount point. */
+ if (nosupp)
+ return 1;
+
+ /* If the file handle for the directory we are
+ * interested in and its parent are identical, we
+ * assume this is the root directory, which is a mount
+ * point. */
+
+ if (h->handle_bytes == h_parent->handle_bytes &&
+ h->handle_type == h_parent->handle_type &&
+ memcmp(h->f_handle, h_parent->f_handle, h->handle_bytes) == 0)
+ return 1;
+
+ return mount_id != mount_id_parent;
+
+fallback_fdinfo:
+ r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
+ if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
+ goto fallback_fstat;
+ if (r < 0)
+ return r;
+
+ r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
+ if (r < 0)
+ return r;
+
+ if (mount_id != mount_id_parent)
+ return 1;
+
+ /* Hmm, so, the mount ids are the same. This leaves one
+ * special case though for the root file system. For that,
+ * let's see if the parent directory has the same inode as we
+ * are interested in. Hence, let's also do fstat() checks now,
+ * too, but avoid the st_dev comparisons, since they aren't
+ * that useful on unionfs mounts. */
+ check_st_dev = false;
+
+fallback_fstat:
+ /* yay for fstatat() taking a different set of flags than the other
+ * _at() above */
+ if (flags & AT_SYMLINK_FOLLOW)
+ flags &= ~AT_SYMLINK_FOLLOW;
+ else
+ flags |= AT_SYMLINK_NOFOLLOW;
+ if (fstatat(fd, filename, &a, flags) < 0)
+ return -errno;
+
+ if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
+ return -errno;
+
+ /* A directory with same device and inode as its parent? Must
+ * be the root directory */
+ if (a.st_dev == b.st_dev &&
+ a.st_ino == b.st_ino)
+ return 1;
+
+ return check_st_dev && (a.st_dev != b.st_dev);
+}
+
+/* flags can be AT_SYMLINK_FOLLOW or 0 */
+int path_is_mount_point(const char *t, const char *root, int flags) {
+ _cleanup_free_ char *canonical = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ assert(t);
+ assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
+
+ if (path_equal(t, "/"))
+ return 1;
+
+ /* we need to resolve symlinks manually, we can't just rely on
+ * fd_is_mount_point() to do that for us; if we have a structure like
+ * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
+ * look at needs to be /usr, not /. */
+ if (flags & AT_SYMLINK_FOLLOW) {
+ r = chase_symlinks(t, root, CHASE_TRAIL_SLASH, &canonical);
+ if (r < 0)
+ return r;
+
+ t = canonical;
+ }
+
+ fd = open_parent(t, O_PATH|O_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ return fd_is_mount_point(fd, last_path_component(t), flags);
+}
+
+int path_get_mnt_id(const char *path, int *ret) {
+ int r;
+
+ r = name_to_handle_at_loop(AT_FDCWD, path, NULL, ret, 0);
+ if (IN_SET(r, -EOPNOTSUPP, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL)) /* kernel/fs don't support this, or seccomp blocks access, or untriggered mount, or name_to_handle_at() is flaky */
+ return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret);
+
+ return r;
+}
+
+bool fstype_is_network(const char *fstype) {
+ const char *x;
+
+ x = startswith(fstype, "fuse.");
+ if (x)
+ fstype = x;
+
+ return STR_IN_SET(fstype,
+ "afs",
+ "cifs",
+ "smbfs",
+ "sshfs",
+ "ncpfs",
+ "ncp",
+ "nfs",
+ "nfs4",
+ "gfs",
+ "gfs2",
+ "glusterfs",
+ "pvfs2", /* OrangeFS */
+ "ocfs2",
+ "lustre");
+}
+
+bool fstype_is_api_vfs(const char *fstype) {
+ return STR_IN_SET(fstype,
+ "autofs",
+ "bpf",
+ "cgroup",
+ "cgroup2",
+ "configfs",
+ "cpuset",
+ "debugfs",
+ "devpts",
+ "devtmpfs",
+ "efivarfs",
+ "fusectl",
+ "hugetlbfs",
+ "mqueue",
+ "proc",
+ "pstore",
+ "ramfs",
+ "securityfs",
+ "sysfs",
+ "tmpfs",
+ "tracefs");
+}
+
+bool fstype_is_ro(const char *fstype) {
+ /* All Linux file systems that are necessarily read-only */
+ return STR_IN_SET(fstype,
+ "DM_verity_hash",
+ "iso9660",
+ "squashfs");
+}
+
+bool fstype_can_discard(const char *fstype) {
+ return STR_IN_SET(fstype,
+ "btrfs",
+ "ext4",
+ "vfat",
+ "xfs");
+}
+
+bool fstype_can_uid_gid(const char *fstype) {
+
+ /* All file systems that have a uid=/gid= mount option that fixates the owners of all files and directories,
+ * current and future. */
+
+ return STR_IN_SET(fstype,
+ "adfs",
+ "fat",
+ "hfs",
+ "hpfs",
+ "iso9660",
+ "msdos",
+ "ntfs",
+ "vfat");
+}
+
+int dev_is_devtmpfs(void) {
+ _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+ int mount_id, r;
+ char *e;
+
+ r = path_get_mnt_id("/dev", &mount_id);
+ if (r < 0)
+ return r;
+
+ proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+ if (!proc_self_mountinfo)
+ return -errno;
+
+ (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER);
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ int mid;
+
+ r = read_line(proc_self_mountinfo, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (sscanf(line, "%i", &mid) != 1)
+ continue;
+
+ if (mid != mount_id)
+ continue;
+
+ e = strstr(line, " - ");
+ if (!e)
+ continue;
+
+ /* accept any name that starts with the currently expected type */
+ if (startswith(e + 3, "devtmpfs"))
+ return true;
+ }
+
+ return false;
+}
+
+const char *mount_propagation_flags_to_string(unsigned long flags) {
+
+ switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
+ case 0:
+ return "";
+ case MS_SHARED:
+ return "shared";
+ case MS_SLAVE:
+ return "slave";
+ case MS_PRIVATE:
+ return "private";
+ }
+
+ return NULL;
+}
+
+int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
+
+ if (isempty(name))
+ *ret = 0;
+ else if (streq(name, "shared"))
+ *ret = MS_SHARED;
+ else if (streq(name, "slave"))
+ *ret = MS_SLAVE;
+ else if (streq(name, "private"))
+ *ret = MS_PRIVATE;
+ else
+ return -EINVAL;
+ return 0;
+}
diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h
new file mode 100644
index 0000000000..5398836fed
--- /dev/null
+++ b/src/basic/mountpoint-util.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
+
+int path_get_mnt_id(const char *path, int *ret);
+
+int fd_is_mount_point(int fd, const char *filename, int flags);
+int path_is_mount_point(const char *path, const char *root, int flags);
+
+bool fstype_is_network(const char *fstype);
+bool fstype_is_api_vfs(const char *fstype);
+bool fstype_is_ro(const char *fsype);
+bool fstype_can_discard(const char *fstype);
+bool fstype_can_uid_gid(const char *fstype);
+
+int dev_is_devtmpfs(void);
+
+const char *mount_propagation_flags_to_string(unsigned long flags);
+int mount_propagation_flags_from_string(const char *name, unsigned long *ret);
diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h
index e7c054d8e4..7cbb71819b 100644
--- a/src/basic/ordered-set.h
+++ b/src/basic/ordered-set.h
@@ -21,13 +21,11 @@ static inline int ordered_set_ensure_allocated(OrderedSet **s, const struct hash
}
static inline OrderedSet* ordered_set_free(OrderedSet *s) {
- ordered_hashmap_free((OrderedHashmap*) s);
- return NULL;
+ return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);
}
static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
- ordered_hashmap_free_free((OrderedHashmap*) s);
- return NULL;
+ return (OrderedSet*) ordered_hashmap_free_free((OrderedHashmap*) s);
}
static inline int ordered_set_put(OrderedSet *s, void *p) {
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index 6becf85878..87724af693 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -2,6 +2,7 @@
#include <errno.h>
#include <inttypes.h>
+#include <linux/oom.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
@@ -16,10 +17,12 @@
#include "missing.h"
#include "parse-util.h"
#include "process-util.h"
+#include "stat-util.h"
#include "string-util.h"
int parse_boolean(const char *v) {
- assert(v);
+ if (!v)
+ return -EINVAL;
if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
return 1;
@@ -570,7 +573,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
s = *p;
- /* accept any number of digits, strtoull is limted to 19 */
+ /* accept any number of digits, strtoull is limited to 19 */
for (i=0; i < digits; i++,s++) {
if (*s < '0' || *s > '9') {
if (i == 0)
@@ -637,6 +640,8 @@ int parse_permille_unbounded(const char *p) {
r = safe_atoi(n, &v);
if (r < 0)
return r;
+ if (v < 0)
+ return -ERANGE;
} else {
pc = endswith(p, "%");
if (!pc)
@@ -657,15 +662,14 @@ int parse_permille_unbounded(const char *p) {
r = safe_atoi(n, &v);
if (r < 0)
return r;
+ if (v < 0)
+ return -ERANGE;
if (v > (INT_MAX - q) / 10)
return -ERANGE;
v = v * 10 + q;
}
- if (v < 0)
- return -ERANGE;
-
return v;
}
@@ -709,18 +713,51 @@ int parse_ip_port(const char *s, uint16_t *ret) {
return 0;
}
+int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
+ unsigned l, h;
+ int r;
+
+ r = parse_range(s, &l, &h);
+ if (r < 0)
+ return r;
+
+ if (l <= 0 || l > 65535 || h <= 0 || h > 65535)
+ return -EINVAL;
+
+ if (h < l)
+ return -EINVAL;
+
+ *low = l;
+ *high = h;
+
+ return 0;
+}
+
int parse_dev(const char *s, dev_t *ret) {
+ const char *major;
unsigned x, y;
- dev_t d;
+ size_t n;
+ int r;
- if (sscanf(s, "%u:%u", &x, &y) != 2)
+ n = strspn(s, DIGITS);
+ if (n == 0)
return -EINVAL;
-
- d = makedev(x, y);
- if ((unsigned) major(d) != x || (unsigned) minor(d) != y)
+ if (s[n] != ':')
return -EINVAL;
- *ret = d;
+ major = strndupa(s, n);
+ r = safe_atou(major, &x);
+ if (r < 0)
+ return r;
+
+ r = safe_atou(s + n + 1, &y);
+ if (r < 0)
+ return r;
+
+ if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
+ return -ERANGE;
+
+ *ret = makedev(x, y);
return 0;
}
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index f3267f4cfe..e47641b429 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -115,5 +115,6 @@ int parse_permille(const char *p);
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_oom_score_adjust(const char *s, int *ret);
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index d214c72916..221517303e 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -110,10 +110,7 @@ int path_make_absolute_cwd(const char *p, char **ret) {
if (r < 0)
return r;
- if (endswith(cwd, "/"))
- c = strjoin(cwd, p);
- else
- c = strjoin(cwd, "/", p);
+ c = path_join(cwd, p);
}
if (!c)
return -ENOMEM;
@@ -336,12 +333,15 @@ char *path_simplify(char *path, bool kill_dots) {
/* Removes redundant inner and trailing slashes. Also removes unnecessary dots
* if kill_dots is true. Modifies the passed string in-place.
*
- * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false)
- * ///foo//./bar/. becomes /foo/bar (if kill_dots is true)
- * .//./foo//./bar/. becomes ./foo/bar (if kill_dots is false)
- * .//./foo//./bar/. becomes foo/bar (if kill_dots is true)
+ * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false)
+ * ///foo//./bar/. becomes /foo/bar (if kill_dots is true)
+ * .//./foo//./bar/. becomes ././foo/./bar/. (if kill_dots is false)
+ * .//./foo//./bar/. becomes foo/bar (if kill_dots is true)
*/
+ if (isempty(path))
+ return path;
+
absolute = path_is_absolute(path);
f = path;
@@ -371,9 +371,14 @@ char *path_simplify(char *path, bool kill_dots) {
*(t++) = *f;
}
- /* Special rule, if we are talking of the root directory, a trailing slash is good */
- if (absolute && t == path)
- *(t++) = '/';
+ /* Special rule, if we stripped everything, we either need a "/" (for the root directory)
+ * or "." for the current directory */
+ if (t == path) {
+ if (absolute)
+ *(t++) = '/';
+ else
+ *(t++) = '.';
+ }
*t = 0;
return path;
@@ -428,7 +433,7 @@ int path_compare(const char *a, const char *b) {
assert(a);
assert(b);
- /* A relative path and an abolute path must not compare as equal.
+ /* A relative path and an absolute path must not compare as equal.
* Which one is sorted before the other does not really matter.
* Here a relative path is ordered before an absolute path. */
d = (a[0] == '/') - (b[0] == '/');
@@ -476,18 +481,62 @@ bool path_equal_or_files_same(const char *a, const char *b, int flags) {
return path_equal(a, b) || files_same(a, b, flags) > 0;
}
-char* path_join(const char *root, const char *path, const char *rest) {
- assert(path);
+char* path_join_internal(const char *first, ...) {
+ char *joined, *q;
+ const char *p;
+ va_list ap;
+ bool slash;
+ size_t sz;
+
+ /* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin
+ * already with one so that it is unnecessary. Note that slashes which are already duplicate won't be
+ * removed. The string returned is hence always equal to or longer than the sum of the lengths of each
+ * individual string.
+ *
+ * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some
+ * are optional.
+ *
+ * Examples:
+ *
+ * path_join("foo", "bar") → "foo/bar"
+ * path_join("foo/", "bar") → "foo/bar"
+ * path_join("", "foo", "", "bar", "") → "foo/bar" */
+
+ sz = strlen_ptr(first);
+ va_start(ap, first);
+ while ((p = va_arg(ap, char*)) != (const char*) -1)
+ if (!isempty(p))
+ sz += 1 + strlen(p);
+ va_end(ap);
+
+ joined = new(char, sz + 1);
+ if (!joined)
+ return NULL;
- if (!isempty(root))
- return strjoin(root, endswith(root, "/") ? "" : "/",
- path[0] == '/' ? path+1 : path,
- rest ? (endswith(path, "/") ? "" : "/") : NULL,
- rest && rest[0] == '/' ? rest+1 : rest);
- else
- return strjoin(path,
- rest ? (endswith(path, "/") ? "" : "/") : NULL,
- rest && rest[0] == '/' ? rest+1 : rest);
+ if (!isempty(first)) {
+ q = stpcpy(joined, first);
+ slash = endswith(first, "/");
+ } else {
+ /* Skip empty items */
+ joined[0] = 0;
+ q = joined;
+ slash = true; /* no need to generate a slash anymore */
+ }
+
+ va_start(ap, first);
+ while ((p = va_arg(ap, char*)) != (const char*) -1) {
+ if (isempty(p))
+ continue;
+
+ if (!slash && p[0] != '/')
+ *(q++) = '/';
+
+ q = stpcpy(q, p);
+ slash = endswith(p, "/");
+ }
+ va_end(ap);
+
+ return joined;
}
int find_binary(const char *name, char **ret) {
@@ -745,6 +794,9 @@ const char *last_path_component(const char *path) {
unsigned l, k;
+ if (!path)
+ return NULL;
+
l = k = strlen(path);
if (l == 0) /* special case — an empty string */
return path;
@@ -761,6 +813,37 @@ const char *last_path_component(const char *path) {
return path + k;
}
+int path_extract_filename(const char *p, char **ret) {
+ _cleanup_free_ char *a = NULL;
+ const char *c, *e = NULL, *q;
+
+ /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
+ * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. */
+
+ if (!p)
+ return -EINVAL;
+
+ c = last_path_component(p);
+
+ for (q = c; *q != 0; q++)
+ if (*q != '/')
+ e = q + 1;
+
+ if (!e) /* no valid character? */
+ return -EINVAL;
+
+ a = strndup(c, e - c);
+ if (!a)
+ return -ENOMEM;
+
+ if (!filename_is_valid(a))
+ return -EINVAL;
+
+ *ret = TAKE_PTR(a);
+
+ return 0;
+}
+
bool filename_is_valid(const char *p) {
const char *e;
@@ -774,24 +857,32 @@ bool filename_is_valid(const char *p) {
if (*e != 0)
return false;
- if (e - p > FILENAME_MAX)
+ if (e - p > FILENAME_MAX) /* FILENAME_MAX is counted *without* the trailing NUL byte */
return false;
return true;
}
-bool path_is_normalized(const char *p) {
+bool path_is_valid(const char *p) {
if (isempty(p))
return false;
- if (dot_or_dot_dot(p))
+ if (strlen(p) >= PATH_MAX) /* PATH_MAX is counted *with* the trailing NUL byte */
return false;
- if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+ return true;
+}
+
+bool path_is_normalized(const char *p) {
+
+ if (!path_is_valid(p))
return false;
- if (strlen(p)+1 > PATH_MAX)
+ if (dot_or_dot_dot(p))
+ return false;
+
+ if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
return false;
if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
@@ -906,8 +997,7 @@ bool valid_device_allow_pattern(const char *path) {
/* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny=
* accept it */
- if (startswith(path, "block-") ||
- startswith(path, "char-"))
+ if (STARTSWITH_SET(path, "block-", "char-"))
return true;
return valid_device_node_path(path);
@@ -1049,5 +1139,12 @@ int path_simplify_and_warn(
return -EINVAL;
}
+ if (!path_is_valid(path)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "%s= path has invalid length (%zu bytes)%s.",
+ lvalue, strlen(path), fatal ? "" : ", ignoring");
+ return -EINVAL;
+ }
+
return 0;
}
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index 8277c6b916..094aa47c01 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -7,6 +7,7 @@
#include "macro.h"
#include "string-util.h"
+#include "strv.h"
#include "time-util.h"
#define PATH_SPLIT_SBIN_BIN(x) x "sbin:" x "bin"
@@ -48,7 +49,9 @@ char* path_startswith(const char *path, const char *prefix) _pure_;
int path_compare(const char *a, const char *b) _pure_;
bool path_equal(const char *a, const char *b) _pure_;
bool path_equal_or_files_same(const char *a, const char *b, int flags);
-char* path_join(const char *root, const char *path, const char *rest);
+char* path_join_internal(const char *first, ...);
+#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, (const char*) -1)
+
char* path_simplify(char *path, bool kill_dots);
static inline bool path_equal_ptr(const char *a, const char *b) {
@@ -58,10 +61,10 @@ static inline bool path_equal_ptr(const char *a, const char *b) {
/* Note: the search terminates on the first NULL item. */
#define PATH_IN_SET(p, ...) \
({ \
- char **s; \
+ char **_s; \
bool _found = false; \
- STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \
- if (path_equal(p, *s)) { \
+ STRV_FOREACH(_s, STRV_MAKE(__VA_ARGS__)) \
+ if (path_equal(p, *_s)) { \
_found = true; \
break; \
} \
@@ -70,13 +73,13 @@ static inline bool path_equal_ptr(const char *a, const char *b) {
#define PATH_STARTSWITH_SET(p, ...) \
({ \
- char **s; \
- bool _found = false; \
- STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \
- if (path_startswith(p, *s)) { \
- _found = true; \
- break; \
- } \
+ const char *_p = (p); \
+ char *_found = NULL, **_i; \
+ STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
+ _found = path_startswith(_p, *_i); \
+ if (_found) \
+ break; \
+ } \
_found; \
})
@@ -94,12 +97,24 @@ int mkfs_exists(const char *fstype);
/* Iterates through the path prefixes of the specified path, going up
* the tree, to root. Also returns "" (and not "/"!) for the root
* directory. Excludes the specified directory itself */
-#define PATH_FOREACH_PREFIX(prefix, path) \
- for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
+#define PATH_FOREACH_PREFIX(prefix, path) \
+ for (char *_slash = ({ \
+ path_simplify(strcpy(prefix, path), false); \
+ streq(prefix, "/") ? NULL : strrchr(prefix, '/'); \
+ }); \
+ _slash && ((*_slash = 0), true); \
+ _slash = strrchr((prefix), '/'))
/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
-#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
- for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
+#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
+ for (char *_slash = ({ \
+ path_simplify(strcpy(prefix, path), false); \
+ if (streq(prefix, "/")) \
+ prefix[0] = 0; \
+ strrchr(prefix, 0); \
+ }); \
+ _slash && ((*_slash = 0), true); \
+ _slash = strrchr((prefix), '/'))
char *prefix_root(const char *root, const char *path);
@@ -132,8 +147,10 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar
char* dirname_malloc(const char *path);
const char *last_path_component(const char *path);
+int path_extract_filename(const char *p, char **ret);
bool filename_is_valid(const char *p) _pure_;
+bool path_is_valid(const char *p) _pure_;
bool path_is_normalized(const char *p) _pure_;
char *file_in_same_dir(const char *path, const char *filename);
diff --git a/src/basic/prioq.c b/src/basic/prioq.c
index ef28a086d1..cfd08d5d23 100644
--- a/src/basic/prioq.c
+++ b/src/basic/prioq.c
@@ -32,11 +32,14 @@ struct Prioq {
Prioq *prioq_new(compare_func_t compare_func) {
Prioq *q;
- q = new0(Prioq, 1);
+ q = new(Prioq, 1);
if (!q)
return q;
- q->compare_func = compare_func;
+ *q = (Prioq) {
+ .compare_func = compare_func,
+ };
+
return q;
}
@@ -62,9 +65,6 @@ int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
}
static void swap(Prioq *q, unsigned j, unsigned k) {
- void *saved_data;
- unsigned *saved_idx;
-
assert(q);
assert(j < q->n_items);
assert(k < q->n_items);
@@ -72,12 +72,8 @@ static void swap(Prioq *q, unsigned j, unsigned k) {
assert(!q->items[j].idx || *(q->items[j].idx) == j);
assert(!q->items[k].idx || *(q->items[k].idx) == k);
- saved_data = q->items[j].data;
- saved_idx = q->items[j].idx;
- q->items[j].data = q->items[k].data;
- q->items[j].idx = q->items[k].idx;
- q->items[k].data = saved_data;
- q->items[k].idx = saved_idx;
+ SWAP_TWO(q->items[j].data, q->items[k].data);
+ SWAP_TWO(q->items[j].idx, q->items[k].idx);
if (q->items[j].idx)
*q->items[j].idx = j;
@@ -88,6 +84,7 @@ static void swap(Prioq *q, unsigned j, unsigned k) {
static unsigned shuffle_up(Prioq *q, unsigned idx) {
assert(q);
+ assert(idx < q->n_items);
while (idx > 0) {
unsigned k;
@@ -211,9 +208,12 @@ _pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx)
assert(q);
+ if (q->n_items <= 0)
+ return NULL;
+
if (idx) {
if (*idx == PRIOQ_IDX_NULL ||
- *idx > q->n_items)
+ *idx >= q->n_items)
return NULL;
i = q->items + *idx;
diff --git a/src/basic/prioq.h b/src/basic/prioq.h
index e036175260..bba5c7caa4 100644
--- a/src/basic/prioq.h
+++ b/src/basic/prioq.h
@@ -12,6 +12,7 @@ typedef struct Prioq Prioq;
Prioq *prioq_new(compare_func_t compare);
Prioq *prioq_free(Prioq *q);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Prioq*, prioq_free);
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
int prioq_put(Prioq *q, void *data, unsigned *idx);
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index add481c2ae..1670001364 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -39,46 +39,71 @@ int proc_cmdline(char **ret) {
return read_one_line_file("/proc/cmdline", ret);
}
-int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) {
+static int proc_cmdline_extract_first(const char **p, char **ret_word, ProcCmdlineFlags flags) {
+ const char *q = *p;
+ int r;
- _cleanup_free_ char *line = NULL;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ const char *c;
+
+ r = extract_first_word(&q, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ /* Filter out arguments that are intended only for the initrd */
+ c = startswith(word, "rd.");
+ if (c) {
+ if (!in_initrd())
+ continue;
+
+ if (FLAGS_SET(flags, PROC_CMDLINE_STRIP_RD_PREFIX)) {
+ r = free_and_strdup(&word, c);
+ if (r < 0)
+ return r;
+ }
+
+ } else if (FLAGS_SET(flags, PROC_CMDLINE_RD_STRICT) && in_initrd())
+ continue; /* And optionally filter out arguments that are intended only for the host */
+
+ *p = q;
+ *ret_word = TAKE_PTR(word);
+ return 1;
+ }
+
+ *p = q;
+ *ret_word = NULL;
+ return 0;
+}
+
+int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
const char *p;
int r;
assert(parse_item);
- r = proc_cmdline(&line);
- if (r < 0)
- return r;
+ /* The PROC_CMDLINE_VALUE_OPTIONAL flag doesn't really make sense for proc_cmdline_parse(), let's make this
+ * clear. */
+ assert(!FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL));
p = line;
for (;;) {
_cleanup_free_ char *word = NULL;
- char *value, *key, *q;
+ char *value;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
+ r = proc_cmdline_extract_first(&p, &word, flags);
if (r < 0)
return r;
if (r == 0)
break;
- key = word;
-
- /* Filter out arguments that are intended only for the initrd */
- q = startswith(word, "rd.");
- if (q) {
- if (!in_initrd())
- continue;
-
- if (flags & PROC_CMDLINE_STRIP_RD_PREFIX)
- key = q;
- }
-
- value = strchr(key, '=');
+ value = strchr(word, '=');
if (value)
*(value++) = 0;
- r = parse_item(key, value, data);
+ r = parse_item(word, value, data);
if (r < 0)
return r;
}
@@ -86,15 +111,26 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned fla
return 0;
}
-static bool relaxed_equal_char(char a, char b) {
+int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
+ _cleanup_free_ char *line = NULL;
+ int r;
+
+ assert(parse_item);
+ r = proc_cmdline(&line);
+ if (r < 0)
+ return r;
+
+ return proc_cmdline_parse_given(line, parse_item, data, flags);
+}
+
+static bool relaxed_equal_char(char a, char b) {
return a == b ||
(a == '_' && b == '-') ||
(a == '-' && b == '_');
}
char *proc_cmdline_key_startswith(const char *s, const char *prefix) {
-
assert(s);
assert(prefix);
@@ -120,29 +156,29 @@ bool proc_cmdline_key_streq(const char *x, const char *y) {
return true;
}
-int proc_cmdline_get_key(const char *key, unsigned flags, char **value) {
+int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
_cleanup_free_ char *line = NULL, *ret = NULL;
bool found = false;
const char *p;
int r;
- /* Looks for a specific key on the kernel command line. Supports two modes:
+ /* Looks for a specific key on the kernel command line. Supports three modes:
*
- * a) The "value" parameter is used. In this case a parameter beginning with the "key" string followed by "="
- * is searched, and the value following this is returned in "value".
+ * a) The "ret_value" parameter is used. In this case a parameter beginning with the "key" string followed by
+ * "=" is searched for, and the value following it is returned in "ret_value".
*
- * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a
- * separate word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then
- * this is also accepted, and "value" is returned as NULL.
+ * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a separate
+ * word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then this is
+ * also accepted, and "value" is returned as NULL.
*
- * c) The "value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
+ * c) The "ret_value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
*
* In all three cases, > 0 is returned if the key is found, 0 if not. */
if (isempty(key))
return -EINVAL;
- if ((flags & PROC_CMDLINE_VALUE_OPTIONAL) && !value)
+ if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
return -EINVAL;
r = proc_cmdline(&line);
@@ -152,20 +188,16 @@ int proc_cmdline_get_key(const char *key, unsigned flags, char **value) {
p = line;
for (;;) {
_cleanup_free_ char *word = NULL;
- const char *e;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
+ r = proc_cmdline_extract_first(&p, &word, flags);
if (r < 0)
return r;
if (r == 0)
break;
- /* Automatically filter out arguments that are intended only for the initrd, if we are not in the
- * initrd. */
- if (!in_initrd() && startswith(word, "rd."))
- continue;
+ if (ret_value) {
+ const char *e;
- if (value) {
e = proc_cmdline_key_startswith(word, key);
if (!e)
continue;
@@ -177,17 +209,19 @@ int proc_cmdline_get_key(const char *key, unsigned flags, char **value) {
found = true;
- } else if (*e == 0 && (flags & PROC_CMDLINE_VALUE_OPTIONAL))
+ } else if (*e == 0 && FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL))
found = true;
} else {
- if (streq(word, key))
+ if (streq(word, key)) {
found = true;
+ break; /* we found what we were looking for */
+ }
}
}
- if (value)
- *value = TAKE_PTR(ret);
+ if (ret_value)
+ *ret_value = TAKE_PTR(ret);
return found;
}
@@ -217,6 +251,62 @@ int proc_cmdline_get_bool(const char *key, bool *ret) {
return 1;
}
+int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
+ _cleanup_free_ char *line = NULL;
+ const char *p;
+ va_list ap;
+ int r, ret = 0;
+
+ /* The PROC_CMDLINE_VALUE_OPTIONAL flag doesn't really make sense for proc_cmdline_get_key_many(), let's make
+ * this clear. */
+ assert(!FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL));
+
+ /* This call may clobber arguments on failure! */
+
+ r = proc_cmdline(&line);
+ if (r < 0)
+ return r;
+
+ p = line;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = proc_cmdline_extract_first(&p, &word, flags);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ va_start(ap, flags);
+
+ for (;;) {
+ char **v;
+ const char *k, *e;
+
+ k = va_arg(ap, const char*);
+ if (!k)
+ break;
+
+ assert_se(v = va_arg(ap, char**));
+
+ e = proc_cmdline_key_startswith(word, k);
+ if (e && *e == '=') {
+ r = free_and_strdup(v, e + 1);
+ if (r < 0) {
+ va_end(ap);
+ return r;
+ }
+
+ ret++;
+ }
+ }
+
+ va_end(ap);
+ }
+
+ return ret;
+}
+
int shall_restore_state(void) {
bool ret;
int r;
diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h
index 4a9e6e0f62..ff04379fbd 100644
--- a/src/basic/proc-cmdline.h
+++ b/src/basic/proc-cmdline.h
@@ -5,20 +5,25 @@
#include "log.h"
-enum {
- PROC_CMDLINE_STRIP_RD_PREFIX = 1,
- PROC_CMDLINE_VALUE_OPTIONAL = 2,
-};
+typedef enum ProcCmdlineFlags {
+ PROC_CMDLINE_STRIP_RD_PREFIX = 1 << 0,
+ PROC_CMDLINE_VALUE_OPTIONAL = 1 << 1,
+ PROC_CMDLINE_RD_STRICT = 1 << 2,
+} ProcCmdlineFlags;
typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data);
int proc_cmdline(char **ret);
-int proc_cmdline_parse(const proc_cmdline_parse_t parse, void *userdata, unsigned flags);
+int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags);
+int proc_cmdline_parse(const proc_cmdline_parse_t parse, void *userdata, ProcCmdlineFlags flags);
-int proc_cmdline_get_key(const char *parameter, unsigned flags, char **value);
+int proc_cmdline_get_key(const char *parameter, ProcCmdlineFlags flags, char **value);
int proc_cmdline_get_bool(const char *key, bool *ret);
+int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...);
+#define proc_cmdline_get_key_many(flags, ...) proc_cmdline_get_key_many_internal(flags, __VA_ARGS__, NULL)
+
char *proc_cmdline_key_startswith(const char *s, const char *prefix);
bool proc_cmdline_key_streq(const char *x, const char *y);
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 0a4f917cbd..448503409b 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -35,6 +35,7 @@
#include "missing.h"
#include "process-util.h"
#include "raw-clone.h"
+#include "rlimit-util.h"
#include "signal-util.h"
#include "stat-util.h"
#include "string-table.h"
@@ -336,15 +337,33 @@ int rename_process(const char name[]) {
/* Now, let's tell the kernel about this new memory */
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
- log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m");
- (void) munmap(nn, nn_size);
- goto use_saved_argv;
- }
+ /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be
+ * below the desired start address, in which case the kernel may have kicked this back due
+ * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
+ * action). The proper solution would be to have a prctl() API that could set both start+end
+ * simultaneously, or at least let us query the existing address to anticipate this condition
+ * and respond accordingly. For now, we can only guess at the cause of this failure and try
+ * a workaround--which will briefly expand the arg space to something potentially huge before
+ * resizing it to what we want. */
+ log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
+
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
+ log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
+ (void) munmap(nn, nn_size);
+ goto use_saved_argv;
+ }
- /* And update the end pointer to the new end, too. If this fails, we don't really know what to do, it's
- * pretty unlikely that we can rollback, hence we'll just accept the failure, and continue. */
- if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
- log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
+ log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
+ goto use_saved_argv;
+ }
+ } else {
+ /* And update the end pointer to the new end, too. If this fails, we don't really know what
+ * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
+ * and continue. */
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
+ log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
+ }
if (mm)
(void) munmap(mm, mm_size);
@@ -425,7 +444,7 @@ int is_kernel_thread(pid_t pid) {
q += l;
}
- /* Skip preceeding whitespace */
+ /* Skip preceding whitespace */
l = strspn(q, WHITESPACE);
if (l < 1)
return -EINVAL;
@@ -496,8 +515,8 @@ int get_process_exe(pid_t pid, char **name) {
static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
_cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
const char *p;
+ int r;
assert(field);
assert(uid);
@@ -515,9 +534,16 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
- FOREACH_LINE(line, f, return -errno) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
char *l;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
l = strstrip(line);
if (startswith(l, field)) {
@@ -574,12 +600,14 @@ int get_process_root(pid_t pid, char **root) {
return get_process_link_contents(p, root);
}
+#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U)
+
int get_process_environ(pid_t pid, char **env) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *outcome = NULL;
- int c;
- const char *p;
size_t allocated = 0, sz = 0;
+ const char *p;
+ int r;
assert(pid >= 0);
assert(env);
@@ -595,23 +623,28 @@ int get_process_environ(pid_t pid, char **env) {
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
- while ((c = fgetc(f)) != EOF) {
+ for (;;) {
+ char c;
+
+ if (sz >= ENVIRONMENT_BLOCK_MAX)
+ return -ENOBUFS;
+
if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
return -ENOMEM;
+ r = safe_fgetc(f, &c);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
if (c == '\0')
outcome[sz++] = '\n';
else
sz += cescape_char(c, outcome + sz);
}
- if (!outcome) {
- outcome = strdup("");
- if (!outcome)
- return -ENOMEM;
- } else
- outcome[sz] = '\0';
-
+ outcome[sz] = '\0';
*env = TAKE_PTR(outcome);
return 0;
@@ -806,7 +839,7 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
void sigkill_wait(pid_t pid) {
assert(pid > 1);
- if (kill(pid, SIGKILL) > 0)
+ if (kill(pid, SIGKILL) >= 0)
(void) wait_for_terminate(pid, NULL);
}
@@ -824,7 +857,7 @@ void sigkill_waitp(pid_t *pid) {
void sigterm_wait(pid_t pid) {
assert(pid > 1);
- if (kill_and_sigcont(pid, SIGTERM) > 0)
+ if (kill_and_sigcont(pid, SIGTERM) >= 0)
(void) wait_for_terminate(pid, NULL);
}
@@ -844,9 +877,9 @@ int kill_and_sigcont(pid_t pid, int sig) {
int getenv_for_pid(pid_t pid, const char *field, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
char *value = NULL;
- bool done = false;
const char *path;
- size_t l;
+ size_t l, sum = 0;
+ int r;
assert(pid >= 0);
assert(field);
@@ -869,6 +902,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
return 1;
}
+ if (!pid_is_valid(pid))
+ return -EINVAL;
+
path = procfs_file_alloca(pid, "environ");
f = fopen(path, "re");
@@ -882,24 +918,19 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
l = strlen(field);
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
- do {
- char line[LINE_MAX];
- size_t i;
-
- for (i = 0; i < sizeof(line)-1; i++) {
- int c;
+ if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */
+ return -ENOBUFS;
- c = getc(f);
- if (_unlikely_(c == EOF)) {
- done = true;
- break;
- } else if (c == 0)
- break;
+ r = read_nul_string(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0) /* EOF */
+ break;
- line[i] = c;
- }
- line[i] = 0;
+ sum += r;
if (strneq(line, field, l) && line[l] == '=') {
value = strdup(line + l + 1);
@@ -909,8 +940,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
*ret = value;
return 1;
}
-
- } while (!done);
+ }
*ret = NULL;
return 0;
@@ -1104,16 +1134,9 @@ void valgrind_summary_hack(void) {
#endif
}
-int pid_compare_func(const void *a, const void *b) {
- const pid_t *p = a, *q = b;
-
+int pid_compare_func(const pid_t *a, const pid_t *b) {
/* Suitable for usage in qsort() */
-
- if (*p < *q)
- return -1;
- if (*p > *q)
- return 1;
- return 0;
+ return CMP(*a, *b);
}
int ioprio_parse_priority(const char *s, int *ret) {
@@ -1153,8 +1176,8 @@ void reset_cached_pid(void) {
/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc
* headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against
* libpthread, as it is part of glibc anyway. */
-extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void * __dso_handle);
-extern void* __dso_handle __attribute__ ((__weak__));
+extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle);
+extern void* __dso_handle _weak_;
pid_t getpid_cached(void) {
static bool installed = false;
@@ -1209,8 +1232,7 @@ int must_be_root(void) {
if (geteuid() == 0)
return 0;
- log_error("Need to be root.");
- return -EPERM;
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root.");
}
int safe_fork_full(
@@ -1233,25 +1255,17 @@ int safe_fork_full(
original_pid = getpid_cached();
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
-
/* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
* be sure that SIGTERMs are not lost we might send to the child. */
- if (sigfillset(&ss) < 0)
- return log_full_errno(prio, errno, "Failed to reset signal set: %m");
-
+ assert_se(sigfillset(&ss) >= 0);
block_signals = true;
} else if (flags & FORK_WAIT) {
-
/* Let's block SIGCHLD at least, so that we can safely watch for the child process */
- if (sigemptyset(&ss) < 0)
- return log_full_errno(prio, errno, "Failed to clear signal set: %m");
-
- if (sigaddset(&ss, SIGCHLD) < 0)
- return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m");
-
+ assert_se(sigemptyset(&ss) >= 0);
+ assert_se(sigaddset(&ss, SIGCHLD) >= 0);
block_signals = true;
}
@@ -1384,12 +1398,74 @@ int safe_fork_full(
}
}
+ if (flags & FORK_RLIMIT_NOFILE_SAFE) {
+ r = rlimit_nofile_safe();
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to lower RLIMIT_NOFILE's soft limit to 1K: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
if (ret_pid)
*ret_pid = getpid_cached();
return 0;
}
+int namespace_fork(
+ const char *outer_name,
+ const char *inner_name,
+ const int except_fds[],
+ size_t n_except_fds,
+ ForkFlags flags,
+ int pidns_fd,
+ int mntns_fd,
+ int netns_fd,
+ int userns_fd,
+ int root_fd,
+ pid_t *ret_pid) {
+
+ int r;
+
+ /* This is much like safe_fork(), but forks twice, and joins the specified namespaces in the middle
+ * process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that
+ * /proc/self/fd works correctly. */
+
+ r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ pid_t pid;
+
+ /* Child */
+
+ r = namespace_enter(pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd);
+ if (r < 0) {
+ log_full_errno(FLAGS_SET(flags, FORK_LOG) ? LOG_ERR : LOG_DEBUG, r, "Failed to join namespace: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ /* We mask a few flags here that either make no sense for the grandchild, or that we don't have to do again */
+ r = safe_fork_full(inner_name, except_fds, n_except_fds, flags & ~(FORK_WAIT|FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NULL_STDIO), &pid);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+ if (r == 0) {
+ /* Child */
+ if (ret_pid)
+ *ret_pid = pid;
+ return 0;
+ }
+
+ r = wait_for_terminate_and_check(inner_name, pid, FLAGS_SET(flags, FORK_LOG) ? WAIT_LOG : 0);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(r);
+ }
+
+ return 1;
+}
+
int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) {
bool stdout_is_tty, stderr_is_tty;
size_t n, i;
@@ -1441,6 +1517,8 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret
safe_close_above_stdio(fd);
}
+ (void) rlimit_nofile_safe();
+
/* Count arguments */
va_start(ap, path);
for (n = 0; va_arg(ap, char*); n++)
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index a5bb072b25..496e14d3de 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -108,7 +108,7 @@ static inline void* PID_TO_PTR(pid_t pid) {
void valgrind_summary_hack(void);
-int pid_compare_func(const void *a, const void *b);
+int pid_compare_func(const pid_t *a, const pid_t *b);
static inline bool nice_is_valid(int n) {
return n >= PRIO_MIN && n < PRIO_MAX;
@@ -134,13 +134,6 @@ static inline bool pid_is_valid(pid_t p) {
return p > 0;
}
-static inline int sched_policy_to_string_alloc_with_check(int n, char **s) {
- if (!sched_policy_is_valid(n))
- return -EINVAL;
-
- return sched_policy_to_string_alloc(n, s);
-}
-
int ioprio_parse_priority(const char *s, int *ret);
pid_t getpid_cached(void);
@@ -149,15 +142,16 @@ void reset_cached_pid(void);
int must_be_root(void);
typedef enum ForkFlags {
- FORK_RESET_SIGNALS = 1 << 0,
- FORK_CLOSE_ALL_FDS = 1 << 1,
- FORK_DEATHSIG = 1 << 2,
- FORK_NULL_STDIO = 1 << 3,
- FORK_REOPEN_LOG = 1 << 4,
- FORK_LOG = 1 << 5,
- FORK_WAIT = 1 << 6,
- FORK_NEW_MOUNTNS = 1 << 7,
- FORK_MOUNTNS_SLAVE = 1 << 8,
+ FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
+ FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
+ FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */
+ FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */
+ FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */
+ FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */
+ FORK_WAIT = 1 << 6, /* Wait until child exited */
+ FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */
+ FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */
+ FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
} ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
@@ -166,7 +160,9 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
return safe_fork_full(name, NULL, 0, flags, ret_pid);
}
-int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...);
+int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
+
+int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...) _sentinel_;
int set_oom_score_adjust(int value);
diff --git a/src/basic/random-util.c b/src/basic/random-util.c
index 91481559db..f7decf60b6 100644
--- a/src/basic/random-util.c
+++ b/src/basic/random-util.c
@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#if defined(__i386__) || defined(__x86_64__)
+#include <cpuid.h>
+#endif
+
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
-#include <linux/random.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@@ -26,67 +29,175 @@
#include "random-util.h"
#include "time-util.h"
-int acquire_random_bytes(void *p, size_t n, bool high_quality_required) {
- static int have_syscall = -1;
+#if HAS_FEATURE_MEMORY_SANITIZER
+#include <sanitizer/msan_interface.h>
+#endif
+
+int rdrand(unsigned long *ret) {
+
+#if defined(__i386__) || defined(__x86_64__)
+ static int have_rdrand = -1;
+ unsigned char err;
+
+ if (have_rdrand < 0) {
+ uint32_t eax, ebx, ecx, edx;
+
+ /* Check if RDRAND is supported by the CPU */
+ if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) {
+ have_rdrand = false;
+ return -EOPNOTSUPP;
+ }
+
+ have_rdrand = !!(ecx & (1U << 30));
+ }
+
+ if (have_rdrand == 0)
+ return -EOPNOTSUPP;
+ asm volatile("rdrand %0;"
+ "setc %1"
+ : "=r" (*ret),
+ "=qm" (err));
+
+#if HAS_FEATURE_MEMORY_SANITIZER
+ __msan_unpoison(&err, sizeof(err));
+#endif
+
+ if (!err)
+ return -EAGAIN;
+
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
+ static int have_syscall = -1;
_cleanup_close_ int fd = -1;
- size_t already_done = 0;
+ bool got_some = false;
int r;
- /* Gathers some randomness from the kernel. This call will never block. If
- * high_quality_required, it will always return some data from the kernel,
- * regardless of whether the random pool is fully initialized or not.
- * Otherwise, it will return success if at least some random bytes were
- * successfully acquired, and an error if the kernel has no entropy whatsover
- * for us. */
+ /* Gathers some randomness from the kernel (or the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't
+ * block, unless the RANDOM_BLOCK flag is set. If RANDOM_DONT_DRAIN is set, an error is returned if the random
+ * pool is not initialized. Otherwise it will always return some data from the kernel, regardless of whether
+ * the random pool is fully initialized or not. */
+
+ if (n == 0)
+ return 0;
+
+ if (FLAGS_SET(flags, RANDOM_ALLOW_RDRAND))
+ /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is not
+ * required, as we don't trust it (who does?). Note that we only do a single iteration of RDRAND here,
+ * even though the Intel docs suggest calling this in a tight loop of 10 invocations or so. That's
+ * because we don't really care about the quality here. We generally prefer using RDRAND if the caller
+ * allows us too, since this way we won't drain the kernel randomness pool if we don't need it, as the
+ * pool's entropy is scarce. */
+ for (;;) {
+ unsigned long u;
+ size_t m;
+
+ if (rdrand(&u) < 0) {
+ if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
+ /* Fill in the remaining bytes using pseudo-random values */
+ pseudo_random_bytes(p, n);
+ return 0;
+ }
+
+ /* OK, this didn't work, let's go to getrandom() + /dev/urandom instead */
+ break;
+ }
+
+ m = MIN(sizeof(u), n);
+ memcpy(p, &u, m);
+
+ p = (uint8_t*) p + m;
+ n -= m;
+
+ if (n == 0)
+ return 0; /* Yay, success! */
+
+ got_some = true;
+ }
/* Use the getrandom() syscall unless we know we don't have it. */
if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
- r = getrandom(p, n, GRND_NONBLOCK);
- if (r > 0) {
- have_syscall = true;
- if ((size_t) r == n)
- return 0;
- if (!high_quality_required) {
- /* Fill in the remaining bytes using pseudorandom values */
- pseudorandom_bytes((uint8_t*) p + r, n - r);
- return 0;
- }
- already_done = r;
- } else if (errno == ENOSYS)
- /* We lack the syscall, continue with reading from /dev/urandom. */
- have_syscall = false;
- else if (errno == EAGAIN) {
- /* The kernel has no entropy whatsoever. Let's remember to
- * use the syscall the next time again though.
- *
- * If high_quality_required is false, return an error so that
- * random_bytes() can produce some pseudorandom
- * bytes. Otherwise, fall back to /dev/urandom, which we know
- * is empty, but the kernel will produce some bytes for us on
- * a best-effort basis. */
- have_syscall = true;
-
- if (!high_quality_required)
- return -ENODATA;
- } else
- return -errno;
+ for (;;) {
+ r = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK);
+ if (r > 0) {
+ have_syscall = true;
+
+ if ((size_t) r == n)
+ return 0; /* Yay, success! */
+
+ assert((size_t) r < n);
+ p = (uint8_t*) p + r;
+ n -= r;
+
+ if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
+ /* Fill in the remaining bytes using pseudo-random values */
+ pseudo_random_bytes(p, n);
+ return 0;
+ }
+
+ got_some = true;
+
+ /* Hmm, we didn't get enough good data but the caller insists on good data? Then try again */
+ if (FLAGS_SET(flags, RANDOM_BLOCK))
+ continue;
+
+ /* Fill in the rest with /dev/urandom */
+ break;
+
+ } else if (r == 0) {
+ have_syscall = true;
+ return -EIO;
+
+ } else if (errno == ENOSYS) {
+ /* We lack the syscall, continue with reading from /dev/urandom. */
+ have_syscall = false;
+ break;
+
+ } else if (errno == EAGAIN) {
+ /* The kernel has no entropy whatsoever. Let's remember to use the syscall the next
+ * time again though.
+ *
+ * If RANDOM_DONT_DRAIN is set, return an error so that random_bytes() can produce some
+ * pseudo-random bytes instead. Otherwise, fall back to /dev/urandom, which we know is empty,
+ * but the kernel will produce some bytes for us on a best-effort basis. */
+ have_syscall = true;
+
+ if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
+ /* Fill in the remaining bytes using pseudorandom values */
+ pseudo_random_bytes(p, n);
+ return 0;
+ }
+
+ if (FLAGS_SET(flags, RANDOM_DONT_DRAIN))
+ return -ENODATA;
+
+ /* Use /dev/urandom instead */
+ break;
+ } else
+ return -errno;
+ }
}
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return errno == ENOENT ? -ENOSYS : -errno;
- return loop_read_exact(fd, (uint8_t*) p + already_done, n - already_done, true);
+ return loop_read_exact(fd, p, n, true);
}
void initialize_srand(void) {
static bool srand_called = false;
unsigned x;
#if HAVE_SYS_AUXV_H
- void *auxv;
+ const void *auxv;
#endif
+ unsigned long k;
if (srand_called)
return;
@@ -96,7 +207,7 @@ void initialize_srand(void) {
* try to make use of that to seed the pseudo-random generator. It's
* better than nothing... */
- auxv = (void*) getauxval(AT_RANDOM);
+ auxv = (const void*) getauxval(AT_RANDOM);
if (auxv) {
assert_cc(sizeof(x) <= 16);
memcpy(&x, auxv, sizeof(x));
@@ -107,6 +218,9 @@ void initialize_srand(void) {
x ^= (unsigned) now(CLOCK_REALTIME);
x ^= (unsigned) gettid();
+ if (rdrand(&k) >= 0)
+ x ^= (unsigned) k;
+
srand(x);
srand_called = true;
}
@@ -119,7 +233,7 @@ void initialize_srand(void) {
# define RAND_STEP 1
#endif
-void pseudorandom_bytes(void *p, size_t n) {
+void pseudo_random_bytes(void *p, size_t n) {
uint8_t *q;
initialize_srand();
@@ -142,13 +256,10 @@ void pseudorandom_bytes(void *p, size_t n) {
}
void random_bytes(void *p, size_t n) {
- int r;
- r = acquire_random_bytes(p, n, false);
- if (r >= 0)
+ if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_DONT_DRAIN|RANDOM_ALLOW_RDRAND) >= 0)
return;
- /* If some idiot made /dev/urandom unavailable to us, or the
- * kernel has no entropy, use a PRNG instead. */
- return pseudorandom_bytes(p, n);
+ /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */
+ pseudo_random_bytes(p, n);
}
diff --git a/src/basic/random-util.h b/src/basic/random-util.h
index 9a103f0e94..3e8c288d3d 100644
--- a/src/basic/random-util.h
+++ b/src/basic/random-util.h
@@ -5,9 +5,17 @@
#include <stddef.h>
#include <stdint.h>
-int acquire_random_bytes(void *p, size_t n, bool high_quality_required);
-void pseudorandom_bytes(void *p, size_t n);
-void random_bytes(void *p, size_t n);
+typedef enum RandomFlags {
+ RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */
+ RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */
+ RANDOM_DONT_DRAIN = 1 << 2, /* If we can't get any randomness at all, return early with -EAGAIN */
+ RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */
+} RandomFlags;
+
+int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled upwith pseudo random, if not enough is available */
+void pseudo_random_bytes(void *p, size_t n); /* returns only pseudo-randommess (but possibly seeded from something better) */
+void random_bytes(void *p, size_t n); /* returns genuine randomness if cheaply available, and pseudo randomness if not. */
+
void initialize_srand(void);
static inline uint64_t random_u64(void) {
@@ -21,3 +29,5 @@ static inline uint32_t random_u32(void) {
random_bytes(&u, sizeof(u));
return u;
}
+
+int rdrand(unsigned long *ret);
diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h
index 1f134ba7f2..b8857b0cdf 100644
--- a/src/basic/raw-clone.h
+++ b/src/basic/raw-clone.h
@@ -60,7 +60,7 @@ static inline pid_t raw_clone(unsigned long flags) {
"mov %%o0, %1" :
"=r"(in_child), "=r"(child_pid), "=r"(error) :
"i"(__NR_clone), "r"(flags) :
- "%o1", "%o0", "%g1" "cc" );
+ "%o1", "%o0", "%g1", "cc" );
if (error) {
errno = child_pid;
diff --git a/src/basic/refcnt.h b/src/basic/refcnt.h
index d2be6086d2..40f9a84a22 100644
--- a/src/basic/refcnt.h
+++ b/src/basic/refcnt.h
@@ -14,3 +14,41 @@ typedef struct {
#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1))
#define REFCNT_INIT ((RefCount) { ._value = 1 })
+
+#define _DEFINE_ATOMIC_REF_FUNC(type, name, scope) \
+ scope type *name##_ref(type *p) { \
+ if (!p) \
+ return NULL; \
+ \
+ assert_se(REFCNT_INC(p->n_ref) >= 2); \
+ return p; \
+ }
+
+#define _DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func, scope) \
+ scope type *name##_unref(type *p) { \
+ if (!p) \
+ return NULL; \
+ \
+ if (REFCNT_DEC(p->n_ref) > 0) \
+ return NULL; \
+ \
+ return free_func(p); \
+ }
+
+#define DEFINE_ATOMIC_REF_FUNC(type, name) \
+ _DEFINE_ATOMIC_REF_FUNC(type, name,)
+#define DEFINE_PUBLIC_ATOMIC_REF_FUNC(type, name) \
+ _DEFINE_ATOMIC_REF_FUNC(type, name, _public_)
+
+#define DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func) \
+ _DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func,)
+#define DEFINE_PUBLIC_ATOMIC_UNREF_FUNC(type, name, free_func) \
+ _DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func, _public_)
+
+#define DEFINE_ATOMIC_REF_UNREF_FUNC(type, name, free_func) \
+ DEFINE_ATOMIC_REF_FUNC(type, name); \
+ DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func);
+
+#define DEFINE_PUBLIC_ATOMIC_REF_UNREF_FUNC(type, name, free_func) \
+ DEFINE_PUBLIC_ATOMIC_REF_FUNC(type, name); \
+ DEFINE_PUBLIC_ATOMIC_UNREF_FUNC(type, name, free_func);
diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c
index be1ba615ec..74b3a023f1 100644
--- a/src/basic/rlimit-util.c
+++ b/src/basic/rlimit-util.c
@@ -5,6 +5,7 @@
#include "alloc-util.h"
#include "extract-word.h"
+#include "fd-util.h"
#include "format-util.h"
#include "macro.h"
#include "missing.h"
@@ -33,8 +34,15 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) {
if (highest.rlim_max == RLIM_INFINITY)
return -EPERM;
- fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max);
- fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max);
+ fixed = (struct rlimit) {
+ .rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
+ .rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
+ };
+
+ /* Shortcut things if we wouldn't change anything. */
+ if (fixed.rlim_cur == highest.rlim_cur &&
+ fixed.rlim_max == highest.rlim_max)
+ return 0;
if (setrlimit(resource, &fixed) < 0)
return -errno;
@@ -360,3 +368,43 @@ void rlimit_free_all(struct rlimit **rl) {
for (i = 0; i < _RLIMIT_MAX; i++)
rl[i] = mfree(rl[i]);
}
+
+int rlimit_nofile_bump(int limit) {
+ int r;
+
+ /* Bumps the (soft) RLIMIT_NOFILE resource limit as close as possible to the specified limit. If a negative
+ * limit is specified, bumps it to the maximum the kernel and the hard resource limit allows. This call should
+ * be used by all our programs that might need a lot of fds, and that know how to deal with high fd numbers
+ * (i.e. do not use select() — which chokes on fds >= 1024) */
+
+ if (limit < 0)
+ limit = read_nr_open();
+
+ if (limit < 3)
+ limit = 3;
+
+ r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
+ if (r < 0)
+ return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");
+
+ return 0;
+}
+
+int rlimit_nofile_safe(void) {
+ struct rlimit rl;
+
+ /* Resets RLIMIT_NOFILE's soft limit FD_SETSIZE (i.e. 1024), for compatibility with software still using
+ * select() */
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
+ return log_debug_errno(errno, "Failed to query RLIMIT_NOFILE: %m");
+
+ if (rl.rlim_cur <= FD_SETSIZE)
+ return 0;
+
+ rl.rlim_cur = FD_SETSIZE;
+ if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
+ return log_debug_errno(errno, "Failed to lower RLIMIT_NOFILE's soft limit to " RLIM_FMT ": %m", rl.rlim_cur);
+
+ return 1;
+}
diff --git a/src/basic/rlimit-util.h b/src/basic/rlimit-util.h
index c2ea6f846b..d4fca2b855 100644
--- a/src/basic/rlimit-util.h
+++ b/src/basic/rlimit-util.h
@@ -20,3 +20,6 @@ int rlimit_format(const struct rlimit *rl, char **ret);
void rlimit_free_all(struct rlimit **rl);
#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
+
+int rlimit_nofile_bump(int limit);
+int rlimit_nofile_safe(void);
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
index 54f6dc2059..0c957c9b3a 100644
--- a/src/basic/rm-rf.c
+++ b/src/basic/rm-rf.c
@@ -15,7 +15,7 @@
#include "fd-util.h"
#include "log.h"
#include "macro.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "stat-util.h"
@@ -168,10 +168,10 @@ int rm_rf(const char *path, RemoveFlags flags) {
/* We refuse to clean the root file system with this
* call. This is extra paranoia to never cause a really
* seriously broken system. */
- if (path_equal_or_files_same(path, "/", AT_SYMLINK_NOFOLLOW)) {
- log_error("Attempted to remove entire root file system (\"%s\"), and we can't allow that.", path);
- return -EPERM;
- }
+ if (path_equal_or_files_same(path, "/", AT_SYMLINK_NOFOLLOW))
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Attempted to remove entire root file system (\"%s\"), and we can't allow that.",
+ path);
if (FLAGS_SET(flags, REMOVE_SUBVOLUME | REMOVE_ROOT | REMOVE_PHYSICAL)) {
/* Try to remove as subvolume first */
@@ -194,10 +194,10 @@ int rm_rf(const char *path, RemoveFlags flags) {
if (statfs(path, &s) < 0)
return -errno;
- if (is_physical_fs(&s)) {
- log_error("Attempted to remove files from a disk file system under \"%s\", refusing.", path);
- return -EPERM;
- }
+ if (is_physical_fs(&s))
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Attempted to remove files from a disk file system under \"%s\", refusing.",
+ path);
}
if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h
index 0a2d7f0358..3ee2b97e37 100644
--- a/src/basic/rm-rf.h
+++ b/src/basic/rm-rf.h
@@ -22,3 +22,11 @@ static inline void rm_rf_physical_and_free(char *p) {
free(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_physical_and_free);
+
+/* Similar as above, but also has magic btrfs subvolume powers */
+static inline void rm_rf_subvolume_and_free(char *p) {
+ PROTECT_ERRNO;
+ (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ free(p);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_subvolume_and_free);
diff --git a/src/basic/securebits.h b/src/basic/securebits.h
deleted file mode 100644
index 98fbe0d433..0000000000
--- a/src/basic/securebits.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef _LINUX_SECUREBITS_H
-#define _LINUX_SECUREBITS_H 1
-
-/* This is minimal version of Linux' linux/securebits.h header file,
- * which is licensed GPL2 */
-
-#define SECUREBITS_DEFAULT 0x00000000
-
-/* When set UID 0 has no special privileges. When unset, we support
- inheritance of root-permissions and suid-root executable under
- compatibility mode. We raise the effective and inheritable bitmasks
- *of the executable file* if the effective uid of the new process is
- 0. If the real uid is 0, we raise the effective (legacy) bit of the
- executable file. */
-#define SECURE_NOROOT 0
-#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */
-
-/* When set, setuid to/from uid 0 does not trigger capability-"fixup".
- When unset, to provide compatibility with old programs relying on
- set*uid to gain/lose privilege, transitions to/from uid 0 cause
- capabilities to be gained/lost. */
-#define SECURE_NO_SETUID_FIXUP 2
-#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */
-
-/* When set, a process can retain its capabilities even after
- transitioning to a non-root user (the set-uid fixup suppressed by
- bit 2). Bit-4 is cleared when a process calls exec(); setting both
- bit 4 and 5 will create a barrier through exec that no exec()'d
- child can use this feature again. */
-#define SECURE_KEEP_CAPS 4
-#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */
-
-/* Each securesetting is implemented using two bits. One bit specifies
- whether the setting is on or off. The other bit specify whether the
- setting is locked or not. A setting which is locked cannot be
- changed from user-level. */
-#define issecure_mask(X) (1 << (X))
-#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits))
-
-#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \
- issecure_mask(SECURE_NO_SETUID_FIXUP) | \
- issecure_mask(SECURE_KEEP_CAPS))
-#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1)
-
-#endif /* !_LINUX_SECUREBITS_H */
diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c
index e15bd7e1fa..dc06f3d074 100644
--- a/src/basic/selinux-util.c
+++ b/src/basic/selinux-util.c
@@ -316,48 +316,85 @@ char* mac_selinux_free(char *label) {
return NULL;
}
-int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
-
#if HAVE_SELINUX
+static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
_cleanup_freecon_ char *filecon = NULL;
int r;
- assert(path);
-
- if (!label_hnd)
- return 0;
-
- if (path_is_absolute(path))
- r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
- else {
- _cleanup_free_ char *newpath = NULL;
-
- r = path_make_absolute_cwd(path, &newpath);
- if (r < 0)
- return r;
-
- r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
- }
+ assert(abspath);
+ assert(path_is_absolute(abspath));
+ r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
if (r < 0) {
/* No context specified by the policy? Proceed without setting it. */
if (errno == ENOENT)
return 0;
- log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
+ log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
} else {
if (setfscreatecon_raw(filecon) >= 0)
return 0; /* Success! */
- log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, path);
+ log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
}
if (security_getenforce() > 0)
return -errno;
-#endif
return 0;
}
+#endif
+
+int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) {
+ int r = 0;
+
+#if HAVE_SELINUX
+ _cleanup_free_ char *abspath = NULL;
+
+ assert(path);
+
+ if (!label_hnd)
+ return 0;
+
+ if (!path_is_absolute(path)) {
+ _cleanup_free_ char *p = NULL;
+
+ if (dirfd == AT_FDCWD)
+ r = safe_getcwd(&p);
+ else
+ r = fd_get_path(dirfd, &p);
+ if (r < 0)
+ return r;
+
+ path = abspath = path_join(p, path);
+ if (!path)
+ return -ENOMEM;
+ }
+
+ r = selinux_create_file_prepare_abspath(path, mode);
+#endif
+ return r;
+}
+
+int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
+ int r = 0;
+
+#if HAVE_SELINUX
+ _cleanup_free_ char *abspath = NULL;
+
+ assert(path);
+
+ if (!label_hnd)
+ return 0;
+
+ r = path_make_absolute_cwd(path, &abspath);
+ if (r < 0)
+ return r;
+
+ r = selinux_create_file_prepare_abspath(abspath, mode);
+#endif
+ return r;
+}
void mac_selinux_create_file_clear(void) {
diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h
index 08314057fb..bd5207c318 100644
--- a/src/basic/selinux-util.h
+++ b/src/basic/selinux-util.h
@@ -23,6 +23,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
char* mac_selinux_free(char *label);
int mac_selinux_create_file_prepare(const char *path, mode_t mode);
+int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode);
void mac_selinux_create_file_clear(void);
int mac_selinux_create_socket_prepare(const char *label);
diff --git a/src/basic/set.h b/src/basic/set.h
index 664713810d..2a80632b79 100644
--- a/src/basic/set.h
+++ b/src/basic/set.h
@@ -9,13 +9,11 @@ Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
static inline Set *set_free(Set *s) {
- internal_hashmap_free(HASHMAP_BASE(s));
- return NULL;
+ return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL);
}
static inline Set *set_free_free(Set *s) {
- internal_hashmap_free_free(HASHMAP_BASE(s));
- return NULL;
+ return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL);
}
/* no set_free_free_free */
@@ -76,17 +74,17 @@ static inline unsigned set_buckets(Set *s) {
bool set_iterate(Set *s, Iterator *i, void **value);
static inline void set_clear(Set *s) {
- internal_hashmap_clear(HASHMAP_BASE(s));
+ internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
}
static inline void set_clear_free(Set *s) {
- internal_hashmap_clear_free(HASHMAP_BASE(s));
+ internal_hashmap_clear(HASHMAP_BASE(s), free, NULL);
}
/* no set_clear_free_free */
static inline void *set_steal_first(Set *s) {
- return internal_hashmap_steal_first(HASHMAP_BASE(s));
+ return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
}
#define set_clear_with_destructor(_s, _f) \
@@ -105,7 +103,7 @@ static inline void *set_steal_first(Set *s) {
/* no set_first_key */
static inline void *set_first(Set *s) {
- return internal_hashmap_first(HASHMAP_BASE(s));
+ return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), false, NULL);
}
/* no set_next */
diff --git a/src/basic/sigbus.c b/src/basic/sigbus.c
index 70afba6bcf..d5254eab9d 100644
--- a/src/basic/sigbus.c
+++ b/src/basic/sigbus.c
@@ -113,6 +113,10 @@ void sigbus_install(void) {
.sa_flags = SA_SIGINFO,
};
+ /* make sure that sysconf() is not called from a signal handler because
+ * it is not guaranteed to be async-signal-safe since POSIX.1-2008 */
+ (void) page_size();
+
n_installed++;
if (n_installed == 1)
diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c
index d3a81b7cc1..30c228a78a 100644
--- a/src/basic/siphash24.c
+++ b/src/basic/siphash24.c
@@ -90,7 +90,7 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
/* We did not have enough input to fill out the padding completely */
return;
-#ifdef DEBUG
+#if ENABLE_DEBUG_SIPHASH
printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
@@ -110,7 +110,7 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
for ( ; in < end; in += 8) {
m = unaligned_read_le64(in);
-#ifdef DEBUG
+#if ENABLE_DEBUG_SIPHASH
printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
@@ -158,7 +158,7 @@ uint64_t siphash24_finalize(struct siphash *state) {
b = state->padding | (((uint64_t) state->inlen) << 56);
-#ifdef DEBUG
+#if ENABLE_DEBUG_SIPHASH
printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
@@ -171,7 +171,7 @@ uint64_t siphash24_finalize(struct siphash *state) {
sipround(state);
state->v0 ^= b;
-#ifdef DEBUG
+#if ENABLE_DEBUG_SIPHASH
printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h
index 54e2420cc6..70a4a03f6a 100644
--- a/src/basic/siphash24.h
+++ b/src/basic/siphash24.h
@@ -3,6 +3,7 @@
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
+#include <string.h>
#include <sys/types.h>
struct siphash {
@@ -21,3 +22,7 @@ void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
uint64_t siphash24_finalize(struct siphash *state);
uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]);
+
+static inline uint64_t siphash24_string(const char *s, const uint8_t k[16]) {
+ return siphash24(s, strlen(s) + 1, k);
+}
diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c
index 9d31b7717f..123d00e13e 100644
--- a/src/basic/smack-util.c
+++ b/src/basic/smack-util.c
@@ -115,50 +115,27 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
return 0;
p = procfs_file_alloca(pid, "attr/current");
- r = write_string_file(p, label, 0);
+ r = write_string_file(p, label, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return r;
return r;
}
-int mac_smack_fix(const char *path, LabelFixFlags flags) {
+static int smack_fix_fd(int fd , const char *abspath, LabelFixFlags flags) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
- _cleanup_close_ int fd = -1;
const char *label;
struct stat st;
int r;
- assert(path);
+ /* The caller should have done the sanity checks. */
+ assert(abspath);
+ assert(path_is_absolute(abspath));
- if (!mac_smack_use())
+ /* Path must be in /dev. */
+ if (!path_startswith(abspath, "/dev"))
return 0;
- /* Path must be in /dev. Note that this check is pretty sloppy, as we might be called with non-normalized paths
- * and hence not detect all cases of /dev. */
-
- if (path_is_absolute(path)) {
- if (!path_startswith(path, "/dev"))
- return 0;
- } else {
- _cleanup_free_ char *cwd = NULL;
-
- r = safe_getcwd(&cwd);
- if (r < 0)
- return r;
-
- if (!path_startswith(cwd, "/dev"))
- return 0;
- }
-
- fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
- if (fd < 0) {
- if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
- return 0;
-
- return -errno;
- }
-
if (fstat(fd, &st) < 0)
return -errno;
@@ -196,12 +173,65 @@ int mac_smack_fix(const char *path, LabelFixFlags flags) {
streq(old_label, label))
return 0;
- return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", path);
+ return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", abspath);
}
return 0;
}
+int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags) {
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ assert(path);
+
+ if (!mac_smack_use())
+ return 0;
+
+ fd = openat(dirfd, path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
+ if (fd < 0) {
+ if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
+ return 0;
+
+ return -errno;
+ }
+
+ if (!path_is_absolute(path)) {
+ r = fd_get_path(fd, &p);
+ if (r < 0)
+ return r;
+ path = p;
+ }
+
+ return smack_fix_fd(fd, path, flags);
+}
+
+int mac_smack_fix(const char *path, LabelFixFlags flags) {
+ _cleanup_free_ char *abspath = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ assert(path);
+
+ if (!mac_smack_use())
+ return 0;
+
+ r = path_make_absolute_cwd(path, &abspath);
+ if (r < 0)
+ return r;
+
+ fd = open(abspath, O_NOFOLLOW|O_CLOEXEC|O_PATH);
+ if (fd < 0) {
+ if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
+ return 0;
+
+ return -errno;
+ }
+
+ return smack_fix_fd(fd, abspath, flags);
+}
+
int mac_smack_copy(const char *dest, const char *src) {
int r = 0;
_cleanup_free_ char *label = NULL;
@@ -249,6 +279,10 @@ int mac_smack_fix(const char *path, LabelFixFlags flags) {
return 0;
}
+int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags) {
+ return 0;
+}
+
int mac_smack_copy(const char *dest, const char *src) {
return 0;
}
diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h
index fd59787ecb..395ec07b57 100644
--- a/src/basic/smack-util.h
+++ b/src/basic/smack-util.h
@@ -30,6 +30,7 @@ typedef enum SmackAttr {
bool mac_smack_use(void);
int mac_smack_fix(const char *path, LabelFixFlags flags);
+int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags);
const char* smack_attr_to_string(SmackAttr i) _const_;
SmackAttr smack_attr_from_string(const char *s) _pure_;
diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c
index 35e14e567c..4ed19cd937 100644
--- a/src/basic/socket-label.c
+++ b/src/basic/socket-label.c
@@ -35,11 +35,11 @@ int socket_address_listen(
_cleanup_close_ int fd = -1;
const char *p;
- int r, one;
+ int r;
assert(a);
- r = socket_address_verify(a);
+ r = socket_address_verify(a, true);
if (r < 0)
return r;
@@ -62,10 +62,9 @@ int socket_address_listen(
return r;
if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
- int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
-
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_V6ONLY, only == SOCKET_ADDRESS_IPV6_ONLY);
+ if (r < 0)
+ return r;
}
if (IN_SET(socket_address_family(a), AF_INET, AF_INET6)) {
@@ -74,27 +73,27 @@ int socket_address_listen(
return -errno;
if (reuse_port) {
- one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
- log_warning_errno(errno, "SO_REUSEPORT failed: %m");
+ r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEPORT, true);
+ if (r < 0)
+ log_warning_errno(r, "SO_REUSEPORT failed: %m");
}
if (free_bind) {
- one = 1;
- if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
- log_warning_errno(errno, "IP_FREEBIND failed: %m");
+ r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
+ if (r < 0)
+ log_warning_errno(r, "IP_FREEBIND failed: %m");
}
if (transparent) {
- one = 1;
- if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
- log_warning_errno(errno, "IP_TRANSPARENT failed: %m");
+ r = setsockopt_int(fd, IPPROTO_IP, IP_TRANSPARENT, true);
+ if (r < 0)
+ log_warning_errno(r, "IP_TRANSPARENT failed: %m");
}
}
- one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
- return -errno;
+ r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return r;
p = socket_address_get_path(a);
if (p) {
diff --git a/src/basic/socket-protocol-list.c b/src/basic/socket-protocol-list.c
deleted file mode 100644
index 8041b84958..0000000000
--- a/src/basic/socket-protocol-list.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#include <netinet/in.h>
-#include <string.h>
-
-#include "socket-protocol-list.h"
-#include "macro.h"
-
-static const struct socket_protocol_name* lookup_socket_protocol(register const char *str, register GPERF_LEN_TYPE len);
-
-#include "socket-protocol-from-name.h"
-#include "socket-protocol-to-name.h"
-
-const char *socket_protocol_to_name(int id) {
-
- if (id < 0)
- return NULL;
-
- if (id >= (int) ELEMENTSOF(socket_protocol_names))
- return NULL;
-
- return socket_protocol_names[id];
-}
-
-int socket_protocol_from_name(const char *name) {
- const struct socket_protocol_name *sc;
-
- assert(name);
-
- sc = lookup_socket_protocol(name, strlen(name));
- if (!sc)
- return 0;
-
- return sc->id;
-}
-
-int socket_protocol_max(void) {
- return ELEMENTSOF(socket_protocol_names);
-}
diff --git a/src/basic/socket-protocol-list.h b/src/basic/socket-protocol-list.h
deleted file mode 100644
index 458904dbb3..0000000000
--- a/src/basic/socket-protocol-list.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-#pragma once
-
-const char *socket_protocol_to_name(int id);
-int socket_protocol_from_name(const char *name);
-
-int socket_protocol_max(void);
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index a913102e13..91bf801cdf 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -15,6 +15,7 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@@ -50,14 +51,16 @@ static const char* const socket_address_type_table[] = {
DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int);
int socket_address_parse(SocketAddress *a, const char *s) {
- char *e, *n;
+ _cleanup_free_ char *n = NULL;
+ char *e;
int r;
assert(a);
assert(s);
- zero(*a);
- a->type = SOCK_STREAM;
+ *a = (SocketAddress) {
+ .type = SOCK_STREAM,
+ };
if (*s == '[') {
uint16_t port;
@@ -68,7 +71,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (!e)
return -EINVAL;
- n = strndupa(s+1, e-s-1);
+ n = strndup(s+1, e-s-1);
+ if (!n)
+ return -ENOMEM;
errno = 0;
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
@@ -93,7 +98,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
size_t l;
l = strlen(s);
- if (l >= sizeof(a->sockaddr.un.sun_path))
+ if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when
+ * parsing (the kernel itself is less strict here in what it
+ * accepts) */
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
@@ -105,7 +112,11 @@ int socket_address_parse(SocketAddress *a, const char *s) {
size_t l;
l = strlen(s+1);
- if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
+ if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminated sockets here
+ * when parsing, even though abstract namespace sockets
+ * explicitly allow embedded NUL bytes and don't consider
+ * them special. But it's simply annoying to debug such
+ * sockets. */
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
@@ -125,7 +136,10 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (r < 0)
return r;
- n = strndupa(cid_start, e - cid_start);
+ n = strndup(cid_start, e - cid_start);
+ if (!n)
+ return -ENOMEM;
+
if (!isempty(n)) {
r = safe_atou(n, &a->sockaddr.vm.svm_cid);
if (r < 0)
@@ -146,7 +160,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (r < 0)
return r;
- n = strndupa(s, e-s);
+ n = strndup(s, e-s);
+ if (!n)
+ return -ENOMEM;
/* IPv4 in w.x.y.z:p notation? */
r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
@@ -246,9 +262,12 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s) {
return 0;
}
-int socket_address_verify(const SocketAddress *a) {
+int socket_address_verify(const SocketAddress *a, bool strict) {
assert(a);
+ /* With 'strict' we enforce additional sanity constraints which are not set by the standard,
+ * but should only apply to sockets we create ourselves. */
+
switch (socket_address_family(a)) {
case AF_INET:
@@ -278,19 +297,29 @@ int socket_address_verify(const SocketAddress *a) {
case AF_UNIX:
if (a->size < offsetof(struct sockaddr_un, sun_path))
return -EINVAL;
+ if (a->size > sizeof(struct sockaddr_un) + !strict)
+ /* If !strict, allow one extra byte, since getsockname() on Linux will append
+ * a NUL byte if we have path sockets that are above sun_path's full size. */
+ return -EINVAL;
- if (a->size > offsetof(struct sockaddr_un, sun_path)) {
-
- if (a->sockaddr.un.sun_path[0] != 0) {
- char *e;
-
- /* path */
- e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
- if (!e)
- return -EINVAL;
+ if (a->size > offsetof(struct sockaddr_un, sun_path) &&
+ a->sockaddr.un.sun_path[0] != 0 &&
+ strict) {
+ /* Only validate file system sockets here, and only in strict mode */
+ const char *e;
+ e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
+ if (e) {
+ /* If there's an embedded NUL byte, make sure the size of the socket address matches it */
if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
return -EINVAL;
+ } else {
+ /* If there's no embedded NUL byte, then then the size needs to match the whole
+ * structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful,
+ * and considers both equivalent: getsockname() even extends sockaddr_un beyond its
+ * size if the path is non NUL terminated.)*/
+ if (!IN_SET(a->size, sizeof(a->sockaddr.un.sun_path), sizeof(a->sockaddr.un.sun_path)+1))
+ return -EINVAL;
}
}
@@ -329,7 +358,10 @@ int socket_address_print(const SocketAddress *a, char **ret) {
assert(a);
assert(ret);
- r = socket_address_verify(a);
+ r = socket_address_verify(a, false); /* We do non-strict validation, because we want to be
+ * able to pretty-print any socket the kernel considers
+ * valid. We still need to do validation to know if we
+ * can meaningfully print the address. */
if (r < 0)
return r;
@@ -362,8 +394,8 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
assert(b);
/* Invalid addresses are unequal to all */
- if (socket_address_verify(a) < 0 ||
- socket_address_verify(b) < 0)
+ if (socket_address_verify(a, false) < 0 ||
+ socket_address_verify(b, false) < 0)
return false;
if (a->type != b->type)
@@ -474,6 +506,10 @@ const char* socket_address_get_path(const SocketAddress *a) {
if (a->sockaddr.un.sun_path[0] == 0)
return NULL;
+ /* Note that this is only safe because we know that there's an extra NUL byte after the sockaddr_un
+ * structure. On Linux AF_UNIX file system socket addresses don't have to be NUL terminated if they take up the
+ * full sun_path space. */
+ assert_cc(sizeof(union sockaddr_union) >= sizeof(struct sockaddr_un)+1);
return a->sockaddr.un.sun_path;
}
@@ -543,7 +579,13 @@ int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) {
}
}
-int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
+int sockaddr_pretty(
+ const struct sockaddr *_sa,
+ socklen_t salen,
+ bool translate_ipv6,
+ bool include_port,
+ char **ret) {
+
union sockaddr_union *sa = (union sockaddr_union*) _sa;
char *p;
int r;
@@ -614,42 +656,51 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
}
case AF_UNIX:
- if (salen <= offsetof(struct sockaddr_un, sun_path)) {
+ if (salen <= offsetof(struct sockaddr_un, sun_path) ||
+ (sa->un.sun_path[0] == 0 && salen == offsetof(struct sockaddr_un, sun_path) + 1))
+ /* The name must have at least one character (and the leading NUL does not count) */
p = strdup("<unnamed>");
- if (!p)
- return -ENOMEM;
-
- } else if (sa->un.sun_path[0] == 0) {
- /* abstract */
-
- /* FIXME: We assume we can print the
- * socket path here and that it hasn't
- * more than one NUL byte. That is
- * actually an invalid assumption */
-
- p = new(char, sizeof(sa->un.sun_path)+1);
- if (!p)
- return -ENOMEM;
+ else {
+ /* Note that we calculate the path pointer here through the .un_buffer[] field, in order to
+ * outtrick bounds checking tools such as ubsan, which are too smart for their own good: on
+ * Linux the kernel may return sun_path[] data one byte longer than the declared size of the
+ * field. */
+ char *path = (char*) sa->un_buffer + offsetof(struct sockaddr_un, sun_path);
+ size_t path_len = salen - offsetof(struct sockaddr_un, sun_path);
+
+ if (path[0] == 0) {
+ /* Abstract socket. When parsing address information from, we
+ * explicitly reject overly long paths and paths with embedded NULs.
+ * But we might get such a socket from the outside. Let's return
+ * something meaningful and printable in this case. */
+
+ _cleanup_free_ char *e = NULL;
+
+ e = cescape_length(path + 1, path_len - 1);
+ if (!e)
+ return -ENOMEM;
- p[0] = '@';
- memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
- p[sizeof(sa->un.sun_path)] = 0;
+ p = strjoin("@", e);
+ } else {
+ if (path[path_len - 1] == '\0')
+ /* We expect a terminating NUL and don't print it */
+ path_len --;
- } else {
- p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
- if (!p)
- return -ENOMEM;
+ p = cescape_length(path, path_len);
+ }
}
+ if (!p)
+ return -ENOMEM;
break;
case AF_VSOCK:
- if (include_port)
- r = asprintf(&p,
- "vsock:%u:%u",
- sa->vm.svm_cid,
- sa->vm.svm_port);
- else
+ if (include_port) {
+ if (sa->vm.svm_cid == VMADDR_CID_ANY)
+ r = asprintf(&p, "vsock::%u", sa->vm.svm_port);
+ else
+ r = asprintf(&p, "vsock:%u:%u", sa->vm.svm_cid, sa->vm.svm_port);
+ } else
r = asprintf(&p, "vsock:%u", sa->vm.svm_cid);
if (r < 0)
return -ENOMEM;
@@ -739,21 +790,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
return 0;
}
-int socket_address_unlink(SocketAddress *a) {
- assert(a);
-
- if (socket_address_family(a) != AF_UNIX)
- return 0;
-
- if (a->sockaddr.un.sun_path[0] == 0)
- return 0;
-
- if (unlink(a->sockaddr.un.sun_path) < 0)
- return -errno;
-
- return 1;
-}
-
static const char* const netlink_family_table[] = {
[NETLINK_ROUTE] = "route",
[NETLINK_FIREWALL] = "firewall",
@@ -826,10 +862,11 @@ int fd_inc_sndbuf(int fd, size_t n) {
/* If we have the privileges we will ignore the kernel limit. */
- value = (int) n;
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
- return -errno;
+ if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n) < 0) {
+ r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n);
+ if (r < 0)
+ return r;
+ }
return 1;
}
@@ -844,10 +881,12 @@ int fd_inc_rcvbuf(int fd, size_t n) {
/* If we have the privileges we will ignore the kernel limit. */
- value = (int) n;
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
- return -errno;
+ if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n) < 0) {
+ r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n);
+ if (r < 0)
+ return r;
+ }
+
return 1;
}
@@ -1003,9 +1042,10 @@ int getpeergroups(int fd, gid_t **ret) {
return (int) n;
}
-int send_one_fd_sa(
+ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
+ struct iovec *iov, size_t iovlen,
const struct sockaddr *sa, socklen_t len,
int flags) {
@@ -1016,28 +1056,58 @@ int send_one_fd_sa(
struct msghdr mh = {
.msg_name = (struct sockaddr*) sa,
.msg_namelen = len,
- .msg_control = &control,
- .msg_controllen = sizeof(control),
+ .msg_iov = iov,
+ .msg_iovlen = iovlen,
};
- struct cmsghdr *cmsg;
+ ssize_t k;
assert(transport_fd >= 0);
- assert(fd >= 0);
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+ /*
+ * We need either an FD or data to send.
+ * If there's nothing, return an error.
+ */
+ if (fd < 0 && !iov)
+ return -EINVAL;
+
+ if (fd >= 0) {
+ struct cmsghdr *cmsg;
- mh.msg_controllen = CMSG_SPACE(sizeof(int));
- if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0)
- return -errno;
+ mh.msg_control = &control;
+ mh.msg_controllen = sizeof(control);
- return 0;
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+ mh.msg_controllen = CMSG_SPACE(sizeof(int));
+ }
+ k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags);
+ if (k < 0)
+ return (ssize_t) -errno;
+
+ return k;
}
-int receive_one_fd(int transport_fd, int flags) {
+int send_one_fd_sa(
+ int transport_fd,
+ int fd,
+ const struct sockaddr *sa, socklen_t len,
+ int flags) {
+
+ assert(fd >= 0);
+
+ return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags);
+}
+
+ssize_t receive_one_fd_iov(
+ int transport_fd,
+ struct iovec *iov, size_t iovlen,
+ int flags,
+ int *ret_fd) {
+
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
@@ -1045,10 +1115,14 @@ int receive_one_fd(int transport_fd, int flags) {
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
+ .msg_iov = iov,
+ .msg_iovlen = iovlen,
};
struct cmsghdr *cmsg, *found = NULL;
+ ssize_t k;
assert(transport_fd >= 0);
+ assert(ret_fd);
/*
* Receive a single FD via @transport_fd. We don't care for
@@ -1058,8 +1132,9 @@ int receive_one_fd(int transport_fd, int flags) {
* combination with send_one_fd().
*/
- if (recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags) < 0)
- return -errno;
+ k = recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
+ if (k < 0)
+ return (ssize_t) -errno;
CMSG_FOREACH(cmsg, &mh) {
if (cmsg->cmsg_level == SOL_SOCKET &&
@@ -1071,12 +1146,33 @@ int receive_one_fd(int transport_fd, int flags) {
}
}
- if (!found) {
+ if (!found)
cmsg_close_all(&mh);
+
+ /* If didn't receive an FD or any data, return an error. */
+ if (k == 0 && !found)
return -EIO;
- }
- return *(int*) CMSG_DATA(found);
+ if (found)
+ *ret_fd = *(int*) CMSG_DATA(found);
+ else
+ *ret_fd = -1;
+
+ return k;
+}
+
+int receive_one_fd(int transport_fd, int flags) {
+ int fd;
+ ssize_t k;
+
+ k = receive_one_fd_iov(transport_fd, NULL, 0, flags, &fd);
+ if (k == 0)
+ return fd;
+
+ /* k must be negative, since receive_one_fd_iov() only returns
+ * a positive value if data was received through the iov. */
+ assert(k < 0);
+ return (int) k;
}
ssize_t next_datagram_size_fd(int fd) {
@@ -1181,3 +1277,71 @@ int socket_ioctl_fd(void) {
return fd;
}
+
+int sockaddr_un_unlink(const struct sockaddr_un *sa) {
+ const char *p, * nul;
+
+ assert(sa);
+
+ if (sa->sun_family != AF_UNIX)
+ return -EPROTOTYPE;
+
+ if (sa->sun_path[0] == 0) /* Nothing to do for abstract sockets */
+ return 0;
+
+ /* The path in .sun_path is not necessarily NUL terminated. Let's fix that. */
+ nul = memchr(sa->sun_path, 0, sizeof(sa->sun_path));
+ if (nul)
+ p = sa->sun_path;
+ else
+ p = memdupa_suffix0(sa->sun_path, sizeof(sa->sun_path));
+
+ if (unlink(p) < 0)
+ return -errno;
+
+ return 1;
+}
+
+int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
+ size_t l;
+
+ assert(ret);
+ assert(path);
+
+ /* Initialize ret->sun_path from the specified argument. This will interpret paths starting with '@' as
+ * abstract namespace sockets, and those starting with '/' as regular filesystem sockets. It won't accept
+ * anything else (i.e. no relative paths), to avoid ambiguities. Note that this function cannot be used to
+ * reference paths in the abstract namespace that include NUL bytes in the name. */
+
+ l = strlen(path);
+ if (l == 0)
+ return -EINVAL;
+ if (!IN_SET(path[0], '/', '@'))
+ return -EINVAL;
+ if (path[1] == 0)
+ return -EINVAL;
+
+ /* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than
+ * the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket
+ * addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that
+ * do not expect non-NUL terminated file system path*/
+ if (l+1 > sizeof(ret->sun_path))
+ return -EINVAL;
+
+ *ret = (struct sockaddr_un) {
+ .sun_family = AF_UNIX,
+ };
+
+ if (path[0] == '@') {
+ /* Abstract namespace socket */
+ memcpy(ret->sun_path + 1, path + 1, l); /* copy *with* trailing NUL byte */
+ return (int) (offsetof(struct sockaddr_un, sun_path) + l); /* 🔥 *don't* 🔥 include trailing NUL in size */
+
+ } else {
+ assert(path[0] == '/');
+
+ /* File system socket */
+ memcpy(ret->sun_path, path, l + 1); /* copy *with* trailing NUL byte */
+ return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */
+ }
+}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 8e23cf2dbd..574d2b73f5 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -1,6 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <inttypes.h>
+#include <linux/netlink.h>
+#include <linux/if_infiniband.h>
+#include <linux/if_packet.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <stdbool.h>
@@ -8,13 +12,10 @@
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
-#include <linux/netlink.h>
-#include <linux/if_infiniband.h>
-#include <linux/if_packet.h>
#include "macro.h"
-#include "missing.h"
-#include "util.h"
+#include "missing_socket.h"
+#include "sparse-endian.h"
union sockaddr_union {
/* The minimal, abstract version */
@@ -70,8 +71,13 @@ int socket_address_parse(SocketAddress *a, const char *s);
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
int socket_address_print(const SocketAddress *a, char **p);
-int socket_address_verify(const SocketAddress *a) _pure_;
-int socket_address_unlink(SocketAddress *a);
+int socket_address_verify(const SocketAddress *a, bool strict) _pure_;
+
+int sockaddr_un_unlink(const struct sockaddr_un *sa);
+
+static inline int socket_address_unlink(const SocketAddress *a) {
+ return socket_address_family(a) == AF_UNIX ? sockaddr_un_unlink(&a->sockaddr.un) : 0;
+}
bool socket_address_can_accept(const SocketAddress *a) _pure_;
@@ -130,11 +136,19 @@ int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
int getpeergroups(int fd, gid_t **ret);
+ssize_t send_one_fd_iov_sa(
+ int transport_fd,
+ int fd,
+ struct iovec *iov, size_t iovlen,
+ const struct sockaddr *sa, socklen_t len,
+ int flags);
int send_one_fd_sa(int transport_fd,
int fd,
const struct sockaddr *sa, socklen_t len,
int flags);
-#define send_one_fd(transport_fd, fd, flags) send_one_fd_sa(transport_fd, fd, NULL, 0, flags)
+#define send_one_fd_iov(transport_fd, fd, iov, iovlen, flags) send_one_fd_iov_sa(transport_fd, fd, iov, iovlen, NULL, 0, flags)
+#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags)
+ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd);
int receive_one_fd(int transport_fd, int flags);
ssize_t next_datagram_size_fd(int fd);
@@ -171,7 +185,16 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
offsetof(struct sockaddr_un, sun_path) + \
(_sa->sun_path[0] == 0 ? \
1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
- strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \
+ strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \
})
int socket_ioctl_fd(void);
+
+int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path);
+
+static inline int setsockopt_int(int fd, int level, int optname, int value) {
+ if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/basic/sparse-endian.h b/src/basic/sparse-endian.h
index 5e59de5437..9583dda9e5 100644
--- a/src/basic/sparse-endian.h
+++ b/src/basic/sparse-endian.h
@@ -20,16 +20,15 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
-#ifndef SPARSE_ENDIAN_H
-#define SPARSE_ENDIAN_H
+#pragma once
#include <byteswap.h>
#include <endian.h>
#include <stdint.h>
#ifdef __CHECKER__
-#define __sd_bitwise __attribute__((bitwise))
-#define __sd_force __attribute__((force))
+#define __sd_bitwise __attribute__((__bitwise__))
+#define __sd_force __attribute__((__force__))
#else
#define __sd_bitwise
#define __sd_force
@@ -89,5 +88,3 @@ static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t _
#undef __sd_bitwise
#undef __sd_force
-
-#endif /* SPARSE_ENDIAN_H */
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index 07154e25bb..57700e2388 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -10,11 +10,13 @@
#include <sys/types.h>
#include <unistd.h>
+#include "alloc-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
#include "missing.h"
+#include "parse-util.h"
#include "stat-util.h"
#include "string-util.h"
@@ -45,6 +47,15 @@ int is_dir(const char* path, bool follow) {
return !!S_ISDIR(st.st_mode);
}
+int is_dir_fd(int fd) {
+ struct stat st;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ return !!S_ISDIR(st.st_mode);
+}
+
int is_device_node(const char *path) {
struct stat info;
@@ -204,15 +215,47 @@ int fd_is_network_fs(int fd) {
}
int fd_is_network_ns(int fd) {
+ struct statfs s;
int r;
- r = fd_is_fs_type(fd, NSFS_MAGIC);
- if (r <= 0)
- return r;
+ /* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice
+ * way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle
+ * this somewhat nicely.
+ *
+ * This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not
+ * refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */
+
+ if (fstatfs(fd, &s) < 0)
+ return -errno;
+
+ if (!is_fs_type(&s, NSFS_MAGIC)) {
+ /* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs
+ * instead. Handle that in a somewhat smart way. */
+
+ if (is_fs_type(&s, PROC_SUPER_MAGIC)) {
+ struct statfs t;
+
+ /* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the
+ * passed fd might refer to a network namespace, but we can't know for sure. In that case,
+ * return a recognizable error. */
+
+ if (statfs("/proc/self/ns/net", &t) < 0)
+ return -errno;
+
+ if (s.f_type == t.f_type)
+ return -EUCLEAN; /* It's possible, we simply don't know */
+ }
+
+ return 0; /* No! */
+ }
r = ioctl(fd, NS_GET_NSTYPE);
- if (r < 0)
+ if (r < 0) {
+ if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */
+ return -EUCLEAN;
+
return -errno;
+ }
return r == CLONE_NEWNET;
}
@@ -255,3 +298,122 @@ int fd_verify_regular(int fd) {
return stat_verify_regular(&st);
}
+
+int stat_verify_directory(const struct stat *st) {
+ assert(st);
+
+ if (S_ISLNK(st->st_mode))
+ return -ELOOP;
+
+ if (!S_ISDIR(st->st_mode))
+ return -ENOTDIR;
+
+ return 0;
+}
+
+int fd_verify_directory(int fd) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ return stat_verify_directory(&st);
+}
+
+int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) {
+ const char *t;
+
+ /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
+
+ if (S_ISCHR(mode))
+ t = "char";
+ else if (S_ISBLK(mode))
+ t = "block";
+ else
+ return -ENODEV;
+
+ if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0)
+ return -ENOMEM;
+
+ return 0;
+
+}
+
+int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
+
+ assert(ret);
+
+ if (major(devno) == 0 && minor(devno) == 0) {
+ char *s;
+
+ /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
+ * /dev/block/ and /dev/char/, hence we handle them specially here. */
+
+ if (S_ISCHR(mode))
+ s = strdup("/run/systemd/inaccessible/chr");
+ else if (S_ISBLK(mode))
+ s = strdup("/run/systemd/inaccessible/blk");
+ else
+ return -ENODEV;
+
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+ }
+
+ r = device_path_make_major_minor(mode, devno, &p);
+ if (r < 0)
+ return r;
+
+ return chase_symlinks(p, NULL, 0, ret);
+}
+
+int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) {
+ mode_t mode;
+ dev_t devno;
+ int r;
+
+ /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
+ * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
+ * path cannot be parsed like this. */
+
+ if (path_equal(path, "/run/systemd/inaccessible/chr")) {
+ mode = S_IFCHR;
+ devno = makedev(0, 0);
+ } else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
+ mode = S_IFBLK;
+ devno = makedev(0, 0);
+ } else {
+ const char *w;
+
+ w = path_startswith(path, "/dev/block/");
+ if (w)
+ mode = S_IFBLK;
+ else {
+ w = path_startswith(path, "/dev/char/");
+ if (!w)
+ return -ENODEV;
+
+ mode = S_IFCHR;
+ }
+
+ r = parse_dev(w, &devno);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_mode)
+ *ret_mode = mode;
+ if (ret_devno)
+ *ret_devno = devno;
+
+ return 0;
+}
diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
index f8014ed30b..0a08e642b5 100644
--- a/src/basic/stat-util.h
+++ b/src/basic/stat-util.h
@@ -12,6 +12,7 @@
int is_symlink(const char *path);
int is_dir(const char *path, bool follow);
+int is_dir_fd(int fd);
int is_device_node(const char *path);
int dir_is_empty(const char *path);
@@ -58,3 +59,29 @@ int path_is_temporary_fs(const char *path);
int stat_verify_regular(const struct stat *st);
int fd_verify_regular(int fd);
+
+int stat_verify_directory(const struct stat *st);
+int fd_verify_directory(int fd);
+
+/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
+ * specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
+ * major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
+ * comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
+ * such a test would be pointless in such a case.) */
+
+#define DEVICE_MAJOR_VALID(x) \
+ ({ \
+ typeof(x) _x = (x), _y = 0; \
+ _x >= _y && _x < (UINT32_C(1) << 12); \
+ \
+ })
+
+#define DEVICE_MINOR_VALID(x) \
+ ({ \
+ typeof(x) _x = (x), _y = 0; \
+ _x >= _y && _x < (UINT32_C(1) << 20); \
+ })
+
+int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret);
+int device_path_make_canonical(mode_t mode, dev_t devno, char **ret);
+int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno);
diff --git a/src/basic/static-destruct.h b/src/basic/static-destruct.h
new file mode 100644
index 0000000000..443c0e8ebb
--- /dev/null
+++ b/src/basic/static-destruct.h
@@ -0,0 +1,56 @@
+#pragma once
+
+#include "alloc-util.h"
+#include "macro.h"
+
+/* A framework for registering static variables that shall be freed on shutdown of a process. It's a bit like gcc's
+ * destructor attribute, but allows us to precisely schedule when we want to free the variables. This is supposed to
+ * feel a bit like the gcc cleanup attribute, but for static variables. Note that this does not work for static
+ * variables declared in .so's, as the list is private to the same linking unit. But maybe that's a good thing. */
+
+typedef struct StaticDestructor {
+ void *data;
+ free_func_t destroy;
+} StaticDestructor;
+
+#define STATIC_DESTRUCTOR_REGISTER(variable, func) \
+ _STATIC_DESTRUCTOR_REGISTER(UNIQ, variable, func)
+
+#define _STATIC_DESTRUCTOR_REGISTER(uq, variable, func) \
+ /* Type-safe destructor */ \
+ static void UNIQ_T(static_destructor_wrapper, uq)(void *p) { \
+ typeof(variable) *q = p; \
+ func(q); \
+ } \
+ /* The actual destructor structure we place in a special section to find it */ \
+ _section_("SYSTEMD_STATIC_DESTRUCT") \
+ /* We pick pointer alignment, since that is apparently what gcc does for static variables */ \
+ _alignptr_ \
+ /* Make sure this is not dropped from the image because not explicitly referenced */ \
+ _used_ \
+ /* Make sure that AddressSanitizer doesn't pad this variable: we want everything in this section packed next to each other so that we can enumerate it. */ \
+ _variable_no_sanitize_address_ \
+ static const StaticDestructor UNIQ_T(static_destructor_entry, uq) = { \
+ .data = &(variable), \
+ .destroy = UNIQ_T(static_destructor_wrapper, uq), \
+ }
+
+/* Beginning and end of our section listing the destructors. We define these as weak as we want this to work even if
+ * there's not a single destructor is defined in which case the section will be missing. */
+extern const struct StaticDestructor _weak_ __start_SYSTEMD_STATIC_DESTRUCT[];
+extern const struct StaticDestructor _weak_ __stop_SYSTEMD_STATIC_DESTRUCT[];
+
+/* The function to destroy everything. (Note that this must be static inline, as it's key that it remains in the same
+ * linking unit as the variables we want to destroy. */
+static inline void static_destruct(void) {
+ const StaticDestructor *d;
+
+ if (!__start_SYSTEMD_STATIC_DESTRUCT)
+ return;
+
+ d = ALIGN_TO_PTR(__start_SYSTEMD_STATIC_DESTRUCT, sizeof(void*));
+ while (d < __stop_SYSTEMD_STATIC_DESTRUCT) {
+ d->destroy(d->data);
+ d = ALIGN_TO_PTR(d + 1, sizeof(void*));
+ }
+}
diff --git a/src/basic/stdio-util.h b/src/basic/stdio-util.h
index 73c03274c7..dc67b6e761 100644
--- a/src/basic/stdio-util.h
+++ b/src/basic/stdio-util.h
@@ -7,6 +7,7 @@
#include <sys/types.h>
#include "macro.h"
+#include "util.h"
#define snprintf_ok(buf, len, fmt, ...) \
((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len))
@@ -18,6 +19,9 @@
do { \
int _argtypes[128]; \
size_t _i, _k; \
+ /* See https://github.com/google/sanitizers/issues/992 */ \
+ if (HAS_FEATURE_MEMORY_SANITIZER) \
+ zero(_argtypes); \
_k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
assert(_k < ELEMENTSOF(_argtypes)); \
for (_i = 0; _i < _k; _i++) { \
diff --git a/src/basic/strbuf.c b/src/basic/strbuf.c
index e2ed776a09..81f4f21ade 100644
--- a/src/basic/strbuf.c
+++ b/src/basic/strbuf.c
@@ -66,6 +66,9 @@ void strbuf_complete(struct strbuf *str) {
/* clean up everything */
void strbuf_cleanup(struct strbuf *str) {
+ if (!str)
+ return;
+
strbuf_complete(str);
free(str->buf);
free(str);
@@ -139,9 +142,7 @@ ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
/* lookup child node */
search.c = c;
- child = bsearch_safe(&search, node->children, node->children_count,
- sizeof(struct strbuf_child_entry),
- (__compar_fn_t) strbuf_children_cmp);
+ child = typesafe_bsearch(&search, node->children, node->children_count, strbuf_children_cmp);
if (!child)
break;
node = child->child;
diff --git a/src/basic/strbuf.h b/src/basic/strbuf.h
index 75ed30b05b..a36944ad39 100644
--- a/src/basic/strbuf.h
+++ b/src/basic/strbuf.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
diff --git a/src/basic/string-table.h b/src/basic/string-table.h
index 9bd7879355..228c12ad00 100644
--- a/src/basic/string-table.h
+++ b/src/basic/string-table.h
@@ -58,7 +58,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
}
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \
- type name##_from_string(const char *s) { \
+ scope type name##_from_string(const char *s) { \
type i; \
unsigned u = 0; \
if (!s) \
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 0a40683493..05469ac01f 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -128,7 +128,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) {
}
/* Split a string into words. */
-const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
+const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) {
const char *current;
current = *state;
@@ -144,20 +144,24 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
return NULL;
}
- if (quoted && strchr("\'\"", *current)) {
+ if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) {
char quotechars[2] = {*current, '\0'};
*l = strcspn_escaped(current + 1, quotechars);
if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
(current[*l + 2] && !strchr(separator, current[*l + 2]))) {
/* right quote missing or garbage at the end */
+ if (flags & SPLIT_RELAX) {
+ *state = current + *l + 1 + (current[*l + 1] != '\0');
+ return current + 1;
+ }
*state = current;
return NULL;
}
*state = current++ + *l + 2;
- } else if (quoted) {
+ } else if (flags & SPLIT_QUOTES) {
*l = strcspn_escaped(current, separator);
- if (current[*l] && !strchr(separator, current[*l])) {
+ if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) {
/* unfinished escape */
*state = current;
return NULL;
@@ -394,12 +398,7 @@ int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
if (r != 0)
return r;
- if (n < m)
- return -1;
- else if (n > m)
- return 1;
- else
- return 0;
+ return CMP(n, m);
}
bool chars_intersect(const char *a, const char *b) {
@@ -1004,7 +1003,7 @@ int free_and_strdup(char **p, const char *s) {
assert(p);
- /* Replaces a string pointer with an strdup()ed new string,
+ /* Replaces a string pointer with a strdup()ed new string,
* possibly freeing the old one. */
if (streq_ptr(*p, s))
@@ -1023,6 +1022,32 @@ int free_and_strdup(char **p, const char *s) {
return 1;
}
+int free_and_strndup(char **p, const char *s, size_t l) {
+ char *t;
+
+ assert(p);
+ assert(s || l == 0);
+
+ /* Replaces a string pointer with a strndup()ed new string,
+ * freeing the old one. */
+
+ if (!*p && !s)
+ return 0;
+
+ if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0'))
+ return 0;
+
+ if (s) {
+ t = strndup(s, l);
+ if (!t)
+ return -ENOMEM;
+ } else
+ t = NULL;
+
+ free_and_replace(*p, t);
+ return 1;
+}
+
#if !HAVE_EXPLICIT_BZERO
/*
* Pointer to memset is volatile so that compiler must de-reference
@@ -1034,8 +1059,11 @@ typedef void *(*memset_t)(void *,int,size_t);
static volatile memset_t memset_func = memset;
-void explicit_bzero(void *p, size_t l) {
- memset_func(p, '\0', l);
+void* explicit_bzero_safe(void *p, size_t l) {
+ if (l > 0)
+ memset_func(p, '\0', l);
+
+ return p;
}
#endif
@@ -1045,7 +1073,7 @@ char* string_erase(char *x) {
/* A delicious drop of snake-oil! To be called on memory where
* we stored passphrases or so, after we used them. */
- explicit_bzero(x, strlen(x));
+ explicit_bzero_safe(x, strlen(x));
return x;
}
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
index c0cc4e78d7..a5b5a16a5d 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -81,16 +81,21 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_;
char *first_word(const char *s, const char *word) _pure_;
-const char* split(const char **state, size_t *l, const char *separator, bool quoted);
+typedef enum SplitFlags {
+ SPLIT_QUOTES = 0x01 << 0,
+ SPLIT_RELAX = 0x01 << 1,
+} SplitFlags;
+
+const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags);
#define FOREACH_WORD(word, length, s, state) \
- _FOREACH_WORD(word, length, s, WHITESPACE, false, state)
+ _FOREACH_WORD(word, length, s, WHITESPACE, 0, state)
#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
- _FOREACH_WORD(word, length, s, separator, false, state)
+ _FOREACH_WORD(word, length, s, separator, 0, state)
-#define _FOREACH_WORD(word, length, s, separator, quoted, state) \
- for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
+#define _FOREACH_WORD(word, length, s, separator, flags, state) \
+ for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags)))
char *strappend(const char *s, const char *suffix);
char *strnappend(const char *s, const char *suffix, size_t length);
@@ -176,6 +181,7 @@ char *strrep(const char *s, unsigned n);
int split_pair(const char *s, const char *sep, char **l, char **r);
int free_and_strdup(char **p, const char *s);
+int free_and_strndup(char **p, const char *s, size_t l);
/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
@@ -192,8 +198,15 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
return memmem(haystack, haystacklen, needle, needlelen);
}
-#if !HAVE_EXPLICIT_BZERO
-void explicit_bzero(void *p, size_t l);
+#if HAVE_EXPLICIT_BZERO
+static inline void* explicit_bzero_safe(void *p, size_t l) {
+ if (l > 0)
+ explicit_bzero(p, l);
+
+ return p;
+}
+#else
+void *explicit_bzero_safe(void *p, size_t l);
#endif
char *string_erase(char *x);
@@ -228,3 +241,25 @@ static inline void *memory_startswith(const void *p, size_t sz, const char *toke
return (uint8_t*) p + n;
}
+
+/* Like startswith_no_case(), but operates on arbitrary memory blocks.
+ * It works only for ASCII strings.
+ */
+static inline void *memory_startswith_no_case(const void *p, size_t sz, const char *token) {
+ size_t n, i;
+
+ assert(token);
+
+ n = strlen(token);
+ if (sz < n)
+ return NULL;
+
+ assert(p);
+
+ for (i = 0; i < n; i++) {
+ if (ascii_tolower(((char *)p)[i]) != ascii_tolower(token[i]))
+ return NULL;
+ }
+
+ return (uint8_t*) p + n;
+}
diff --git a/src/basic/strv.c b/src/basic/strv.c
index b3716233b5..3a62f25ded 100644
--- a/src/basic/strv.c
+++ b/src/basic/strv.c
@@ -169,7 +169,7 @@ char **strv_new_ap(const char *x, va_list ap) {
return TAKE_PTR(a);
}
-char **strv_new(const char *x, ...) {
+char **strv_new_internal(const char *x, ...) {
char **r;
va_list ap;
@@ -245,7 +245,7 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
return 0;
}
-char **strv_split(const char *s, const char *separator) {
+char **strv_split_full(const char *s, const char *separator, SplitFlags flags) {
const char *word, *state;
size_t l;
size_t n, i;
@@ -253,8 +253,15 @@ char **strv_split(const char *s, const char *separator) {
assert(s);
+ if (!separator)
+ separator = WHITESPACE;
+
+ s += strspn(s, separator);
+ if (isempty(s))
+ return new0(char*, 1);
+
n = 0;
- FOREACH_WORD_SEPARATOR(word, l, s, separator, state)
+ _FOREACH_WORD(word, l, s, separator, flags, state)
n++;
r = new(char*, n+1);
@@ -262,7 +269,7 @@ char **strv_split(const char *s, const char *separator) {
return NULL;
i = 0;
- FOREACH_WORD_SEPARATOR(word, l, s, separator, state) {
+ _FOREACH_WORD(word, l, s, separator, flags, state) {
r[i] = strndup(word, l);
if (!r[i]) {
strv_free(r);
@@ -335,21 +342,22 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
return (int) n;
}
-char *strv_join(char **l, const char *separator) {
+char *strv_join_prefix(char **l, const char *separator, const char *prefix) {
char *r, *e;
char **s;
- size_t n, k;
+ size_t n, k, m;
if (!separator)
separator = " ";
k = strlen(separator);
+ m = strlen_ptr(prefix);
n = 0;
STRV_FOREACH(s, l) {
if (s != l)
n += k;
- n += strlen(*s);
+ n += m + strlen(*s);
}
r = new(char, n+1);
@@ -361,6 +369,9 @@ char *strv_join(char **l, const char *separator) {
if (s != l)
e = stpcpy(e, separator);
+ if (prefix)
+ e = stpcpy(e, prefix);
+
e = stpcpy(e, *s);
}
@@ -647,7 +658,7 @@ char **strv_split_nulstr(const char *s) {
}
if (!r)
- return strv_new(NULL, NULL);
+ return strv_new(NULL);
return r;
}
@@ -707,14 +718,12 @@ bool strv_overlap(char **a, char **b) {
return false;
}
-static int str_compare(const void *_a, const void *_b) {
- const char **a = (const char**) _a, **b = (const char**) _b;
-
+static int str_compare(char * const *a, char * const *b) {
return strcmp(*a, *b);
}
char **strv_sort(char **l) {
- qsort_safe(l, strv_length(l), sizeof(char*), str_compare);
+ typesafe_qsort(l, strv_length(l), str_compare);
return l;
}
diff --git a/src/basic/strv.h b/src/basic/strv.h
index 51d03db940..aa4cd4aaca 100644
--- a/src/basic/strv.h
+++ b/src/basic/strv.h
@@ -9,6 +9,7 @@
#include "alloc-util.h"
#include "extract-word.h"
#include "macro.h"
+#include "string-util.h"
#include "util.h"
char *strv_find(char **l, const char *name) _pure_;
@@ -53,8 +54,9 @@ bool strv_equal(char **a, char **b);
#define strv_contains(l, s) (!!strv_find((l), (s)))
-char **strv_new(const char *x, ...) _sentinel_;
+char **strv_new_internal(const char *x, ...) _sentinel_;
char **strv_new_ap(const char *x, va_list ap);
+#define strv_new(...) strv_new_internal(__VA_ARGS__, NULL)
#define STRV_IGNORE ((const char *) -1)
@@ -66,12 +68,18 @@ static inline bool strv_isempty(char * const *l) {
return !l || !*l;
}
-char **strv_split(const char *s, const char *separator);
+char **strv_split_full(const char *s, const char *separator, SplitFlags flags);
+static inline char **strv_split(const char *s, const char *separator) {
+ return strv_split_full(s, separator, 0);
+}
char **strv_split_newlines(const char *s);
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
-char *strv_join(char **l, const char *separator);
+char *strv_join_prefix(char **l, const char *separator, const char *prefix);
+static inline char *strv_join(char **l, const char *separator) {
+ return strv_join_prefix(l, separator, NULL);
+}
char **strv_parse_nulstr(const char *s, size_t l);
char **strv_split_nulstr(const char *s);
@@ -136,6 +144,18 @@ void strv_print(char **l);
_x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \
})
+#define STARTSWITH_SET(p, ...) \
+ ({ \
+ const char *_p = (p); \
+ char *_found = NULL, **_i; \
+ STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
+ _found = startswith(_p, *_i); \
+ if (_found) \
+ break; \
+ } \
+ _found; \
+ })
+
#define FOREACH_STRING(x, ...) \
for (char **_l = ({ \
char **_ll = STRV_MAKE(__VA_ARGS__); \
diff --git a/src/basic/strxcpyx.c b/src/basic/strxcpyx.c
index 6277f56b80..9210277cde 100644
--- a/src/basic/strxcpyx.c
+++ b/src/basic/strxcpyx.c
@@ -31,12 +31,11 @@ size_t strpcpy(char **dest, size_t size, const char *src) {
if (size > 1)
*dest = mempcpy(*dest, src, size-1);
size = 0;
- } else {
- if (len > 0) {
- *dest = mempcpy(*dest, src, len);
- size -= len;
- }
+ } else if (len > 0) {
+ *dest = mempcpy(*dest, src, len);
+ size -= len;
}
+
*dest[0] = '\0';
return size;
}
@@ -56,9 +55,8 @@ size_t strpcpyf(char **dest, size_t size, const char *src, ...) {
if (i < (int)size) {
*dest += i;
size -= i;
- } else {
+ } else
size = 0;
- }
va_end(va);
return size;
}
@@ -73,7 +71,7 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
do {
size = strpcpy(dest, size, src);
src = va_arg(va, char *);
- } while (src != NULL);
+ } while (src);
va_end(va);
return size;
}
@@ -100,7 +98,7 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) {
do {
size = strpcpy(&s, size, src);
src = va_arg(va, char *);
- } while (src != NULL);
+ } while (src);
va_end(va);
return size;
diff --git a/src/basic/strxcpyx.h b/src/basic/strxcpyx.h
index 458f9f2568..0f2b7499ba 100644
--- a/src/basic/strxcpyx.h
+++ b/src/basic/strxcpyx.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <stddef.h>
#include "macro.h"
diff --git a/src/basic/syslog-util.c b/src/basic/syslog-util.c
index 21461fa581..fe129482f3 100644
--- a/src/basic/syslog-util.c
+++ b/src/basic/syslog-util.c
@@ -10,7 +10,8 @@
int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
int a = 0, b = 0, c = 0;
- int k;
+ const char *end;
+ size_t k;
assert(p);
assert(*p);
@@ -19,21 +20,22 @@ int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
if ((*p)[0] != '<')
return 0;
- if (!strchr(*p, '>'))
+ end = strchr(*p, '>');
+ if (!end)
return 0;
- if ((*p)[2] == '>') {
+ k = end - *p;
+ assert(k > 0);
+
+ if (k == 2)
c = undecchar((*p)[1]);
- k = 3;
- } else if ((*p)[3] == '>') {
+ else if (k == 3) {
b = undecchar((*p)[1]);
c = undecchar((*p)[2]);
- k = 4;
- } else if ((*p)[4] == '>') {
+ } else if (k == 4) {
a = undecchar((*p)[1]);
b = undecchar((*p)[2]);
c = undecchar((*p)[3]);
- k = 5;
} else
return 0;
@@ -46,7 +48,7 @@ int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
else
*priority = (*priority & LOG_FACMASK) | c;
- *p += k;
+ *p += k + 1;
return 1;
}
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index f4af0e6522..0f38120729 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -32,7 +32,6 @@
#include "io-util.h"
#include "log.h"
#include "macro.h"
-#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
@@ -81,34 +80,36 @@ int chvt(int vt) {
}
int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
- struct termios old_termios, new_termios;
- char c, line[LINE_MAX];
+ _cleanup_free_ char *line = NULL;
+ struct termios old_termios;
+ int r;
assert(f);
assert(ret);
+ /* If this is a terminal, then switch canonical mode off, so that we can read a single character */
if (tcgetattr(fileno(f), &old_termios) >= 0) {
- new_termios = old_termios;
+ struct termios new_termios = old_termios;
new_termios.c_lflag &= ~ICANON;
new_termios.c_cc[VMIN] = 1;
new_termios.c_cc[VTIME] = 0;
if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
- size_t k;
+ char c;
if (t != USEC_INFINITY) {
if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ (void) tcsetattr(fileno(f), TCSADRAIN, &old_termios);
return -ETIMEDOUT;
}
}
- k = fread(&c, 1, 1, f);
-
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
-
- if (k <= 0)
+ r = safe_fgetc(f, &c);
+ (void) tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ if (r < 0)
+ return r;
+ if (r == 0)
return -EIO;
if (need_nl)
@@ -124,11 +125,13 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
return -ETIMEDOUT;
}
- errno = 0;
- if (!fgets(line, sizeof(line), f))
- return errno > 0 ? -errno : -EIO;
+ /* If this is not a terminal, then read a full line instead */
- truncate_nl(line);
+ r = read_line(f, 16, &line); /* longer than necessary, to eat up UTF-8 chars/vt100 key sequences */
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EIO;
if (strlen(line) != 1)
return -EBADMSG;
@@ -196,11 +199,13 @@ int ask_char(char *ret, const char *replies, const char *fmt, ...) {
}
int ask_string(char **ret, const char *text, ...) {
+ int r;
+
assert(ret);
assert(text);
for (;;) {
- char line[LINE_MAX];
+ _cleanup_free_ char *line = NULL;
va_list ap;
if (colors_enabled())
@@ -215,24 +220,14 @@ int ask_string(char **ret, const char *text, ...) {
fflush(stdout);
- errno = 0;
- if (!fgets(line, sizeof(line), stdin))
- return errno > 0 ? -errno : -EIO;
-
- if (!endswith(line, "\n"))
- putchar('\n');
- else {
- char *s;
-
- if (isempty(line))
- continue;
-
- truncate_nl(line);
- s = strdup(line);
- if (!s)
- return -ENOMEM;
+ r = read_line(stdin, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EIO;
- *ret = s;
+ if (!isempty(line)) {
+ *ret = TAKE_PTR(line);
return 0;
}
}
@@ -819,11 +814,11 @@ unsigned columns(void) {
if (e)
(void) safe_atoi(e, &c);
- if (c <= 0)
+ if (c <= 0 || c > USHRT_MAX) {
c = fd_columns(STDOUT_FILENO);
-
- if (c <= 0)
- c = 80;
+ if (c <= 0)
+ c = 80;
+ }
cached_columns = c;
return cached_columns;
@@ -853,11 +848,11 @@ unsigned lines(void) {
if (e)
(void) safe_atoi(e, &l);
- if (l <= 0)
+ if (l <= 0 || l > USHRT_MAX) {
l = fd_lines(STDOUT_FILENO);
-
- if (l <= 0)
- l = 24;
+ if (l <= 0)
+ l = 24;
+ }
cached_lines = l;
return cached_lines;
@@ -979,56 +974,56 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
return 0;
}
-int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
- char fn[STRLEN("/dev/char/") + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
- _cleanup_free_ char *s = NULL;
- const char *p;
+int get_ctty(pid_t pid, dev_t *ret_devnr, char **ret) {
+ _cleanup_free_ char *fn = NULL, *b = NULL;
dev_t devnr;
- int k;
-
- assert(r);
-
- k = get_ctty_devnr(pid, &devnr);
- if (k < 0)
- return k;
-
- sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
+ int r;
- k = readlink_malloc(fn, &s);
- if (k < 0) {
+ r = get_ctty_devnr(pid, &devnr);
+ if (r < 0)
+ return r;
- if (k != -ENOENT)
- return k;
+ r = device_path_make_canonical(S_IFCHR, devnr, &fn);
+ if (r < 0) {
+ if (r != -ENOENT) /* No symlink for this in /dev/char/? */
+ return r;
- /* This is an ugly hack */
if (major(devnr) == 136) {
+ /* This is an ugly hack: PTY devices are not listed in /dev/char/, as they don't follow the
+ * Linux device model. This means we have no nice way to match them up against their actual
+ * device node. Let's hence do the check by the fixed, assigned major number. Normally we try
+ * to avoid such fixed major/minor matches, but there appears to nother nice way to handle
+ * this. */
+
if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
return -ENOMEM;
} else {
- /* Probably something like the ptys which have no
- * symlink in /dev/char. Let's return something
- * vaguely useful. */
+ /* Probably something similar to the ptys which have no symlink in /dev/char/. Let's return
+ * something vaguely useful. */
- b = strdup(fn + 5);
- if (!b)
- return -ENOMEM;
+ r = device_path_make_major_minor(S_IFCHR, devnr, &fn);
+ if (r < 0)
+ return r;
}
- } else {
- if (startswith(s, "/dev/"))
- p = s + 5;
- else if (startswith(s, "../"))
- p = s + 3;
- else
- p = s;
+ }
- b = strdup(p);
- if (!b)
- return -ENOMEM;
+ if (!b) {
+ const char *w;
+
+ w = path_startswith(fn, "/dev/");
+ if (w) {
+ b = strdup(w);
+ if (!b)
+ return -ENOMEM;
+ } else
+ b = TAKE_PTR(fn);
}
- *r = b;
- if (_devnr)
- *_devnr = devnr;
+ if (ret)
+ *ret = TAKE_PTR(b);
+
+ if (ret_devnr)
+ *ret_devnr = devnr;
return 0;
}
@@ -1094,7 +1089,8 @@ int openpt_in_namespace(pid_t pid, int flags) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- r = safe_fork("(sd-openpt)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ r = namespace_fork("(sd-openptns)", "(sd-openpt)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child);
if (r < 0)
return r;
if (r == 0) {
@@ -1102,10 +1098,6 @@ int openpt_in_namespace(pid_t pid, int flags) {
pair[0] = safe_close(pair[0]);
- r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
master = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
if (master < 0)
_exit(EXIT_FAILURE);
@@ -1121,7 +1113,7 @@ int openpt_in_namespace(pid_t pid, int flags) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-openpt)", child, 0);
+ r = wait_for_terminate_and_check("(sd-openptns)", child, 0);
if (r < 0)
return r;
if (r != EXIT_SUCCESS)
@@ -1143,7 +1135,8 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- r = safe_fork("(sd-terminal)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ r = namespace_fork("(sd-terminalns)", "(sd-terminal)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child);
if (r < 0)
return r;
if (r == 0) {
@@ -1151,10 +1144,6 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
pair[0] = safe_close(pair[0]);
- r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
master = open_terminal(name, mode|O_NOCTTY|O_CLOEXEC);
if (master < 0)
_exit(EXIT_FAILURE);
@@ -1167,7 +1156,7 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-terminal)", child, 0);
+ r = wait_for_terminate_and_check("(sd-terminalns)", child, 0);
if (r < 0)
return r;
if (r != EXIT_SUCCESS)
@@ -1278,174 +1267,52 @@ int vt_reset_keyboard(int fd) {
return 0;
}
-static bool urlify_enabled(void) {
- static int cached_urlify_enabled = -1;
-
- /* Unfortunately 'less' doesn't support links like this yet 😭, hence let's disable this as long as there's a
- * pager in effect. Let's drop this check as soon as less got fixed a and enough time passed so that it's safe
- * to assume that a link-enabled 'less' version has hit most installations. */
-
- if (cached_urlify_enabled < 0) {
- int val;
-
- val = getenv_bool("SYSTEMD_URLIFY");
- if (val >= 0)
- cached_urlify_enabled = val;
- else
- cached_urlify_enabled = colors_enabled() && !pager_have();
- }
-
- return cached_urlify_enabled;
-}
-
-int terminal_urlify(const char *url, const char *text, char **ret) {
- char *n;
-
- assert(url);
-
- /* Takes an URL and a pretty string and formats it as clickable link for the terminal. See
- * https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda for details. */
-
- if (isempty(text))
- text = url;
-
- if (urlify_enabled())
- n = strjoin("\x1B]8;;", url, "\a", text, "\x1B]8;;\a");
- else
- n = strdup(text);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
-}
-
-int terminal_urlify_path(const char *path, const char *text, char **ret) {
- _cleanup_free_ char *absolute = NULL;
- struct utsname u;
- const char *url;
- int r;
-
- assert(path);
-
- /* Much like terminal_urlify() above, but takes a file system path as input
- * and turns it into a proper file:// URL first. */
-
- if (isempty(path))
- return -EINVAL;
-
- if (isempty(text))
- text = path;
-
- if (!urlify_enabled()) {
- char *n;
-
- n = strdup(text);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
- }
-
- if (uname(&u) < 0)
- return -errno;
-
- if (!path_is_absolute(path)) {
- r = path_make_absolute_cwd(path, &absolute);
- if (r < 0)
- return r;
-
- path = absolute;
- }
-
- /* As suggested by https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, let's include the local
- * hostname here. Note that we don't use gethostname_malloc() or gethostname_strict() since we are interested
- * in the raw string the kernel has set, whatever it may be, under the assumption that terminals are not overly
- * careful with validating the strings either. */
-
- url = strjoina("file://", u.nodename, path);
-
- return terminal_urlify(url, text, ret);
-}
-
-static int cat_file(const char *filename, bool newline) {
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *urlified = NULL;
- int r;
-
- f = fopen(filename, "re");
- if (!f)
- return -errno;
+int vt_restore(int fd) {
+ static const struct vt_mode mode = {
+ .mode = VT_AUTO,
+ };
+ int r, q = 0;
- r = terminal_urlify_path(filename, NULL, &urlified);
+ r = ioctl(fd, KDSETMODE, KD_TEXT);
if (r < 0)
- return r;
-
- printf("%s%s# %s%s\n",
- newline ? "\n" : "",
- ansi_highlight_blue(),
- urlified,
- ansi_normal());
- fflush(stdout);
+ q = log_debug_errno(errno, "Failed to set VT in text mode, ignoring: %m");
- for (;;) {
- _cleanup_free_ char *line = NULL;
-
- r = read_line(f, LONG_LINE_MAX, &line);
- if (r < 0)
- return log_error_errno(r, "Failed to read \"%s\": %m", filename);
- if (r == 0)
- break;
-
- puts(line);
+ r = vt_reset_keyboard(fd);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to reset keyboard mode, ignoring: %m");
+ if (q >= 0)
+ q = r;
}
- return 0;
-}
-
-int cat_files(const char *file, char **dropins, CatFlags flags) {
- char **path;
- int r;
-
- if (file) {
- r = cat_file(file, false);
- if (r == -ENOENT && (flags & CAT_FLAGS_MAIN_FILE_OPTIONAL))
- printf("%s# config file %s not found%s\n",
- ansi_highlight_magenta(),
- file,
- ansi_normal());
- else if (r < 0)
- return log_warning_errno(r, "Failed to cat %s: %m", file);
+ r = ioctl(fd, VT_SETMODE, &mode);
+ if (r < 0) {
+ log_debug_errno(errno, "Failed to set VT_AUTO mode, ignoring: %m");
+ if (q >= 0)
+ q = -errno;
}
- STRV_FOREACH(path, dropins) {
- r = cat_file(*path, file || path != dropins);
- if (r < 0)
- return log_warning_errno(r, "Failed to cat %s: %m", *path);
+ r = fchown(fd, 0, (gid_t) -1);
+ if (r < 0) {
+ log_debug_errno(errno, "Failed to chown VT, ignoring: %m");
+ if (q >= 0)
+ q = -errno;
}
- return 0;
+ return q;
}
-void print_separator(void) {
-
- /* Outputs a separator line that resolves to whitespace when copied from the terminal. We do that by outputting
- * one line filled with spaces with ANSI underline set, followed by a second (empty) line. */
-
- if (underline_enabled()) {
- size_t i, c;
+int vt_release(int fd, bool restore) {
+ assert(fd >= 0);
- c = columns();
+ /* This function releases the VT by acknowledging the VT-switch signal
+ * sent by the kernel and optionally reset the VT in text and auto
+ * VT-switching modes. */
- flockfile(stdout);
- fputs_unlocked(ANSI_UNDERLINE, stdout);
+ if (ioctl(fd, VT_RELDISP, 1) < 0)
+ return -errno;
- for (i = 0; i < c; i++)
- fputc_unlocked(' ', stdout);
+ if (restore)
+ return vt_restore(fd);
- fputs_unlocked(ANSI_NORMAL "\n\n", stdout);
- funlockfile(stdout);
- } else
- fputs("\n\n", stdout);
+ return 0;
}
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index c0bd0e67a6..c885e0a2d1 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -18,6 +18,7 @@
#define ANSI_MAGENTA "\x1B[0;35m"
#define ANSI_CYAN "\x1B[0;36m"
#define ANSI_WHITE "\x1B[0;37m"
+#define ANSI_GREY "\x1B[0;2;37m"
/* Bold/highlighted */
#define ANSI_HIGHLIGHT_BLACK "\x1B[0;1;30m"
@@ -50,6 +51,9 @@
/* Erase characters until the end of the line */
#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
+/* Move cursor up one line */
+#define ANSI_REVERSE_LINEFEED "\x1BM"
+
/* Set cursor to top left corner and clear screen */
#define ANSI_HOME_CLEAR "\x1B[H\x1B[2J"
@@ -129,6 +133,7 @@ DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW);
DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
DEFINE_ANSI_FUNC(normal, NORMAL);
+DEFINE_ANSI_FUNC(grey, GREY);
DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL);
DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT_UNDERLINE, HIGHLIGHT);
@@ -151,14 +156,5 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode);
int vt_default_utf8(void);
int vt_reset_keyboard(int fd);
-
-int terminal_urlify(const char *url, const char *text, char **ret);
-int terminal_urlify_path(const char *path, const char *text, char **ret);
-
-typedef enum CatFlags {
- CAT_FLAGS_MAIN_FILE_OPTIONAL = 1 << 0,
-} CatFlags;
-
-int cat_files(const char *file, char **dropins, CatFlags flags);
-
-void print_separator(void);
+int vt_restore(int fd);
+int vt_release(int fd, bool restore_vt);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index fe201c398d..557c75debc 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
@@ -19,9 +20,11 @@
#include "io-util.h"
#include "log.h"
#include "macro.h"
+#include "missing_timerfd.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "serialize.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
@@ -278,7 +281,7 @@ static char *format_timestamp_internal(
/* Let's not format times with years > 9999 */
if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) {
- assert(l >= strlen("--- XXXX-XX-XX XX:XX:XX") + 1);
+ assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1);
strcpy(buf, "--- XXXX-XX-XX XX:XX:XX");
return buf;
}
@@ -528,64 +531,6 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
return buf;
}
-void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
-
- assert(f);
- assert(name);
- assert(t);
-
- if (!dual_timestamp_is_set(t))
- return;
-
- fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
- name,
- t->realtime,
- t->monotonic);
-}
-
-int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
- uint64_t a, b;
- int r, pos;
-
- assert(value);
- assert(t);
-
- pos = strspn(value, WHITESPACE);
- if (value[pos] == '-')
- return -EINVAL;
- pos += strspn(value + pos, DIGITS);
- pos += strspn(value + pos, WHITESPACE);
- if (value[pos] == '-')
- return -EINVAL;
-
- r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
- if (r != 2) {
- log_debug("Failed to parse dual timestamp value \"%s\".", value);
- return -EINVAL;
- }
-
- if (value[pos] != '\0')
- /* trailing garbage */
- return -EINVAL;
-
- t->realtime = a;
- t->monotonic = b;
-
- return 0;
-}
-
-int timestamp_deserialize(const char *value, usec_t *timestamp) {
- int r;
-
- assert(value);
-
- r = safe_atou64(value, timestamp);
- if (r < 0)
- return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value);
-
- return r;
-}
-
static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
static const struct {
const char *name;
@@ -904,7 +849,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
* Otherwise just cut it off. */
with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]);
- /* Cut off the timezone if we dont need it. */
+ /* Cut off the timezone if we don't need it. */
if (with_tz)
t = strndupa(t, last_space - t);
@@ -923,7 +868,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
return tmp.return_value;
}
-static char* extract_multiplier(char *p, usec_t *multiplier) {
+static const char* extract_multiplier(const char *p, usec_t *multiplier) {
static const struct {
const char *suffix;
usec_t usec;
@@ -996,10 +941,9 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
}
for (;;) {
- long long l, z = 0;
- char *e;
- unsigned n = 0;
usec_t multiplier = default_unit, k;
+ long long l;
+ char *e;
p += strspn(p, WHITESPACE);
@@ -1010,6 +954,9 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
break;
}
+ if (*p == '-') /* Don't allow "-0" */
+ return -ERANGE;
+
errno = 0;
l = strtoll(p, &e, 10);
if (errno > 0)
@@ -1018,35 +965,47 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
return -ERANGE;
if (*e == '.') {
- char *b = e + 1;
-
- errno = 0;
- z = strtoll(b, &e, 10);
- if (errno > 0)
- return -errno;
+ p = e + 1;
+ p += strspn(p, DIGITS);
+ } else if (e == p)
+ return -EINVAL;
+ else
+ p = e;
- if (z < 0)
- return -ERANGE;
+ s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier);
+ if (s == p && *s != '\0')
+ /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
+ return -EINVAL;
- if (e == b)
- return -EINVAL;
+ p = s;
- n = e - b;
+ if ((usec_t) l >= USEC_INFINITY / multiplier)
+ return -ERANGE;
- } else if (e == p)
- return -EINVAL;
+ k = (usec_t) l * multiplier;
+ if (k >= USEC_INFINITY - r)
+ return -ERANGE;
- e += strspn(e, WHITESPACE);
- p = extract_multiplier(e, &multiplier);
+ r += k;
something = true;
- k = (usec_t) z * multiplier;
+ if (*e == '.') {
+ usec_t m = multiplier / 10;
+ const char *b;
+
+ for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
+ k = (usec_t) (*b - '0') * m;
+ if (k >= USEC_INFINITY - r)
+ return -ERANGE;
- for (; n > 0; n--)
- k /= 10;
+ r += k;
+ }
- r += (usec_t) l * multiplier + k;
+ /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
+ if (b == e + 1)
+ return -EINVAL;
+ }
}
*usec = r;
@@ -1058,58 +1017,75 @@ int parse_sec(const char *t, usec_t *usec) {
return parse_time(t, usec, USEC_PER_SEC);
}
-int parse_sec_fix_0(const char *t, usec_t *usec) {
- assert(t);
- assert(usec);
+int parse_sec_fix_0(const char *t, usec_t *ret) {
+ usec_t k;
+ int r;
- t += strspn(t, WHITESPACE);
+ assert(t);
+ assert(ret);
- if (streq(t, "0")) {
- *usec = USEC_INFINITY;
- return 0;
- }
+ r = parse_sec(t, &k);
+ if (r < 0)
+ return r;
- return parse_sec(t, usec);
+ *ret = k == 0 ? USEC_INFINITY : k;
+ return r;
}
-int parse_nsec(const char *t, nsec_t *nsec) {
+static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) {
static const struct {
const char *suffix;
nsec_t nsec;
} table[] = {
- { "seconds", NSEC_PER_SEC },
- { "second", NSEC_PER_SEC },
- { "sec", NSEC_PER_SEC },
- { "s", NSEC_PER_SEC },
+ { "seconds", NSEC_PER_SEC },
+ { "second", NSEC_PER_SEC },
+ { "sec", NSEC_PER_SEC },
+ { "s", NSEC_PER_SEC },
{ "minutes", NSEC_PER_MINUTE },
- { "minute", NSEC_PER_MINUTE },
- { "min", NSEC_PER_MINUTE },
- { "months", NSEC_PER_MONTH },
- { "month", NSEC_PER_MONTH },
- { "msec", NSEC_PER_MSEC },
- { "ms", NSEC_PER_MSEC },
- { "m", NSEC_PER_MINUTE },
- { "hours", NSEC_PER_HOUR },
- { "hour", NSEC_PER_HOUR },
- { "hr", NSEC_PER_HOUR },
- { "h", NSEC_PER_HOUR },
- { "days", NSEC_PER_DAY },
- { "day", NSEC_PER_DAY },
- { "d", NSEC_PER_DAY },
- { "weeks", NSEC_PER_WEEK },
- { "week", NSEC_PER_WEEK },
- { "w", NSEC_PER_WEEK },
- { "years", NSEC_PER_YEAR },
- { "year", NSEC_PER_YEAR },
- { "y", NSEC_PER_YEAR },
- { "usec", NSEC_PER_USEC },
- { "us", NSEC_PER_USEC },
- { "µs", NSEC_PER_USEC },
- { "nsec", 1ULL },
- { "ns", 1ULL },
- { "", 1ULL }, /* default is nsec */
+ { "minute", NSEC_PER_MINUTE },
+ { "min", NSEC_PER_MINUTE },
+ { "months", NSEC_PER_MONTH },
+ { "month", NSEC_PER_MONTH },
+ { "M", NSEC_PER_MONTH },
+ { "msec", NSEC_PER_MSEC },
+ { "ms", NSEC_PER_MSEC },
+ { "m", NSEC_PER_MINUTE },
+ { "hours", NSEC_PER_HOUR },
+ { "hour", NSEC_PER_HOUR },
+ { "hr", NSEC_PER_HOUR },
+ { "h", NSEC_PER_HOUR },
+ { "days", NSEC_PER_DAY },
+ { "day", NSEC_PER_DAY },
+ { "d", NSEC_PER_DAY },
+ { "weeks", NSEC_PER_WEEK },
+ { "week", NSEC_PER_WEEK },
+ { "w", NSEC_PER_WEEK },
+ { "years", NSEC_PER_YEAR },
+ { "year", NSEC_PER_YEAR },
+ { "y", NSEC_PER_YEAR },
+ { "usec", NSEC_PER_USEC },
+ { "us", NSEC_PER_USEC },
+ { "µs", NSEC_PER_USEC },
+ { "nsec", 1ULL },
+ { "ns", 1ULL },
+ { "", 1ULL }, /* default is nsec */
};
+ size_t i;
+
+ for (i = 0; i < ELEMENTSOF(table); i++) {
+ char *e;
+ e = startswith(p, table[i].suffix);
+ if (e) {
+ *multiplier = table[i].nsec;
+ return e;
+ }
+ }
+
+ return p;
+}
+
+int parse_nsec(const char *t, nsec_t *nsec) {
const char *p, *s;
nsec_t r = 0;
bool something = false;
@@ -1131,8 +1107,8 @@ int parse_nsec(const char *t, nsec_t *nsec) {
}
for (;;) {
- long long l, z = 0;
- size_t n = 0, i;
+ nsec_t multiplier = 1, k;
+ long long l;
char *e;
p += strspn(p, WHITESPACE);
@@ -1144,53 +1120,58 @@ int parse_nsec(const char *t, nsec_t *nsec) {
break;
}
+ if (*p == '-') /* Don't allow "-0" */
+ return -ERANGE;
+
errno = 0;
l = strtoll(p, &e, 10);
-
if (errno > 0)
return -errno;
-
if (l < 0)
return -ERANGE;
if (*e == '.') {
- char *b = e + 1;
-
- errno = 0;
- z = strtoll(b, &e, 10);
- if (errno > 0)
- return -errno;
+ p = e + 1;
+ p += strspn(p, DIGITS);
+ } else if (e == p)
+ return -EINVAL;
+ else
+ p = e;
- if (z < 0)
- return -ERANGE;
+ s = extract_nsec_multiplier(p + strspn(p, WHITESPACE), &multiplier);
+ if (s == p && *s != '\0')
+ /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
+ return -EINVAL;
- if (e == b)
- return -EINVAL;
+ p = s;
- n = e - b;
+ if ((nsec_t) l >= NSEC_INFINITY / multiplier)
+ return -ERANGE;
- } else if (e == p)
- return -EINVAL;
+ k = (nsec_t) l * multiplier;
+ if (k >= NSEC_INFINITY - r)
+ return -ERANGE;
- e += strspn(e, WHITESPACE);
+ r += k;
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (startswith(e, table[i].suffix)) {
- nsec_t k = (nsec_t) z * table[i].nsec;
+ something = true;
- for (; n > 0; n--)
- k /= 10;
+ if (*e == '.') {
+ nsec_t m = multiplier / 10;
+ const char *b;
- r += (nsec_t) l * table[i].nsec + k;
- p = e + strlen(table[i].suffix);
+ for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
+ k = (nsec_t) (*b - '0') * m;
+ if (k >= NSEC_INFINITY - r)
+ return -ERANGE;
- something = true;
- break;
+ r += k;
}
- if (i >= ELEMENTSOF(table))
- return -EINVAL;
-
+ /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
+ if (b == e + 1)
+ return -EINVAL;
+ }
}
*nsec = r;
@@ -1214,10 +1195,11 @@ int get_timezones(char ***ret) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **zones = NULL;
size_t n_zones = 0, n_allocated = 0;
+ int r;
assert(ret);
- zones = strv_new("UTC", NULL);
+ zones = strv_new("UTC");
if (!zones)
return -ENOMEM;
@@ -1226,13 +1208,18 @@ int get_timezones(char ***ret) {
f = fopen("/usr/share/zoneinfo/zone.tab", "re");
if (f) {
- char l[LINE_MAX];
-
- FOREACH_LINE(l, f, return -errno) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
char *p, *w;
size_t k;
- p = strstrip(l);
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ p = strstrip(line);
if (isempty(p) || *p == '#')
continue;
@@ -1398,9 +1385,7 @@ int get_timezone(char **tz) {
if (r < 0)
return r; /* returns EINVAL if not a symlink */
- e = path_startswith(t, "/usr/share/zoneinfo/");
- if (!e)
- e = path_startswith(t, "../usr/share/zoneinfo/");
+ e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
if (!e)
return -EINVAL;
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 344f2dc52e..5316305062 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -108,10 +108,6 @@ char *format_timestamp_us_utc(char *buf, size_t l, usec_t t);
char *format_timestamp_relative(char *buf, size_t l, usec_t t);
char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
-void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
-int dual_timestamp_deserialize(const char *value, dual_timestamp *t);
-int timestamp_deserialize(const char *value, usec_t *timestamp);
-
int parse_timestamp(const char *t, usec_t *usec);
int parse_sec(const char *t, usec_t *usec);
diff --git a/src/basic/tmpfile-util.c b/src/basic/tmpfile-util.c
new file mode 100644
index 0000000000..669eb2666c
--- /dev/null
+++ b/src/basic/tmpfile-util.c
@@ -0,0 +1,329 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/mman.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "hexdecoct.h"
+#include "macro.h"
+#include "memfd-util.h"
+#include "missing_syscall.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "tmpfile-util.h"
+#include "umask-util.h"
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
+ FILE *f;
+ char *t;
+ int r, fd;
+
+ assert(path);
+ assert(_f);
+ assert(_temp_path);
+
+ r = tempfn_xxxxxx(path, NULL, &t);
+ if (r < 0)
+ return r;
+
+ fd = mkostemp_safe(t);
+ if (fd < 0) {
+ free(t);
+ return -errno;
+ }
+
+ f = fdopen(fd, "w");
+ if (!f) {
+ unlink_noerrno(t);
+ free(t);
+ safe_close(fd);
+ return -errno;
+ }
+
+ *_f = f;
+ *_temp_path = t;
+
+ return 0;
+}
+
+/* This is much like mkostemp() but is subject to umask(). */
+int mkostemp_safe(char *pattern) {
+ _cleanup_umask_ mode_t u = 0;
+ int fd;
+
+ assert(pattern);
+
+ u = umask(077);
+
+ fd = mkostemp(pattern, O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
+ int fd;
+ FILE *f;
+
+ fd = mkostemp_safe(pattern);
+ if (fd < 0)
+ return fd;
+
+ f = fdopen(fd, mode);
+ if (!f) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ *ret_f = f;
+ return 0;
+}
+
+int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
+ const char *fn;
+ char *t;
+
+ assert(ret);
+
+ if (isempty(p))
+ return -EINVAL;
+ if (path_equal(p, "/"))
+ return -EINVAL;
+
+ /*
+ * Turns this:
+ * /foo/bar/waldo
+ *
+ * Into this:
+ * /foo/bar/.#<extra>waldoXXXXXX
+ */
+
+ fn = basename(p);
+ if (!filename_is_valid(fn))
+ return -EINVAL;
+
+ extra = strempty(extra);
+
+ t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
+
+ *ret = path_simplify(t, false);
+ return 0;
+}
+
+int tempfn_random(const char *p, const char *extra, char **ret) {
+ const char *fn;
+ char *t, *x;
+ uint64_t u;
+ unsigned i;
+
+ assert(ret);
+
+ if (isempty(p))
+ return -EINVAL;
+ if (path_equal(p, "/"))
+ return -EINVAL;
+
+ /*
+ * Turns this:
+ * /foo/bar/waldo
+ *
+ * Into this:
+ * /foo/bar/.#<extra>waldobaa2a261115984a9
+ */
+
+ fn = basename(p);
+ if (!filename_is_valid(fn))
+ return -EINVAL;
+
+ extra = strempty(extra);
+
+ t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
+
+ u = random_u64();
+ for (i = 0; i < 16; i++) {
+ *(x++) = hexchar(u & 0xF);
+ u >>= 4;
+ }
+
+ *x = 0;
+
+ *ret = path_simplify(t, false);
+ return 0;
+}
+
+int tempfn_random_child(const char *p, const char *extra, char **ret) {
+ char *t, *x;
+ uint64_t u;
+ unsigned i;
+ int r;
+
+ assert(ret);
+
+ /* Turns this:
+ * /foo/bar/waldo
+ * Into this:
+ * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
+ */
+
+ if (!p) {
+ r = tmp_dir(&p);
+ if (r < 0)
+ return r;
+ }
+
+ extra = strempty(extra);
+
+ t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ if (isempty(p))
+ x = stpcpy(stpcpy(t, ".#"), extra);
+ else
+ x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
+
+ u = random_u64();
+ for (i = 0; i < 16; i++) {
+ *(x++) = hexchar(u & 0xF);
+ u >>= 4;
+ }
+
+ *x = 0;
+
+ *ret = path_simplify(t, false);
+ return 0;
+}
+
+int open_tmpfile_unlinkable(const char *directory, int flags) {
+ char *p;
+ int fd, r;
+
+ if (!directory) {
+ r = tmp_dir(&directory);
+ if (r < 0)
+ return r;
+ } else if (isempty(directory))
+ return -EINVAL;
+
+ /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
+
+ /* Try O_TMPFILE first, if it is supported */
+ fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
+ if (fd >= 0)
+ return fd;
+
+ /* Fall back to unguessable name + unlinking */
+ p = strjoina(directory, "/systemd-tmp-XXXXXX");
+
+ fd = mkostemp_safe(p);
+ if (fd < 0)
+ return fd;
+
+ (void) unlink(p);
+
+ return fd;
+}
+
+int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
+ _cleanup_free_ char *tmp = NULL;
+ int r, fd;
+
+ assert(target);
+ assert(ret_path);
+
+ /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
+ assert((flags & O_EXCL) == 0);
+
+ /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
+ * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
+ * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
+
+ fd = open_parent(target, O_TMPFILE|flags, 0640);
+ if (fd >= 0) {
+ *ret_path = NULL;
+ return fd;
+ }
+
+ log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target);
+
+ r = tempfn_random(target, NULL, &tmp);
+ if (r < 0)
+ return r;
+
+ fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
+ if (fd < 0)
+ return -errno;
+
+ *ret_path = TAKE_PTR(tmp);
+
+ return fd;
+}
+
+int link_tmpfile(int fd, const char *path, const char *target) {
+ int r;
+
+ assert(fd >= 0);
+ assert(target);
+
+ /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
+ * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
+ * on the directory, and renameat2() is used instead.
+ *
+ * Note that in both cases we will not replace existing files. This is because linkat() does not support this
+ * operation currently (renameat2() does), and there is no nice way to emulate this. */
+
+ if (path) {
+ r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target);
+ if (r < 0)
+ return r;
+ } else {
+ char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
+
+ xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
+
+ if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
+ return -errno;
+ }
+
+ return 0;
+}
+
+int mkdtemp_malloc(const char *template, char **ret) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(ret);
+
+ if (template)
+ p = strdup(template);
+ else {
+ const char *tmp;
+
+ r = tmp_dir(&tmp);
+ if (r < 0)
+ return r;
+
+ p = strjoin(tmp, "/XXXXXX");
+ }
+ if (!p)
+ return -ENOMEM;
+
+ if (!mkdtemp(p))
+ return -errno;
+
+ *ret = TAKE_PTR(p);
+ return 0;
+}
diff --git a/src/basic/tmpfile-util.h b/src/basic/tmpfile-util.h
new file mode 100644
index 0000000000..802c85d6d9
--- /dev/null
+++ b/src/basic/tmpfile-util.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdio.h>
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
+int mkostemp_safe(char *pattern);
+int fmkostemp_safe(char *pattern, const char *mode, FILE**_f);
+
+int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
+int tempfn_random(const char *p, const char *extra, char **ret);
+int tempfn_random_child(const char *p, const char *extra, char **ret);
+
+int open_tmpfile_unlinkable(const char *directory, int flags);
+int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
+
+int link_tmpfile(int fd, const char *path, const char *target);
+
+int mkdtemp_malloc(const char *template, char **ret);
diff --git a/src/basic/unaligned.h b/src/basic/unaligned.h
index 8a1d06445e..00c17f8769 100644
--- a/src/basic/unaligned.h
+++ b/src/basic/unaligned.h
@@ -1,44 +1,43 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <endian.h>
#include <stdint.h>
/* BE */
static inline uint16_t unaligned_read_be16(const void *_u) {
- const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
+ const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
return be16toh(u->x);
}
static inline uint32_t unaligned_read_be32(const void *_u) {
- const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
+ const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
return be32toh(u->x);
}
static inline uint64_t unaligned_read_be64(const void *_u) {
- const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
+ const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
return be64toh(u->x);
}
static inline void unaligned_write_be16(void *_u, uint16_t a) {
- struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
+ struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
u->x = be16toh(a);
}
static inline void unaligned_write_be32(void *_u, uint32_t a) {
- struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
+ struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
u->x = be32toh(a);
}
static inline void unaligned_write_be64(void *_u, uint64_t a) {
- struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
+ struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
u->x = be64toh(a);
}
@@ -46,37 +45,37 @@ static inline void unaligned_write_be64(void *_u, uint64_t a) {
/* LE */
static inline uint16_t unaligned_read_le16(const void *_u) {
- const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
+ const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
return le16toh(u->x);
}
static inline uint32_t unaligned_read_le32(const void *_u) {
- const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
+ const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
return le32toh(u->x);
}
static inline uint64_t unaligned_read_le64(const void *_u) {
- const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
+ const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
return le64toh(u->x);
}
static inline void unaligned_write_le16(void *_u, uint16_t a) {
- struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
+ struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
u->x = le16toh(a);
}
static inline void unaligned_write_le32(void *_u, uint32_t a) {
- struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
+ struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
u->x = le32toh(a);
}
static inline void unaligned_write_le64(void *_u, uint64_t a) {
- struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
+ struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
u->x = le64toh(a);
}
diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c
index ac6a9b37e8..245daabcf8 100644
--- a/src/basic/unit-def.c
+++ b/src/basic/unit-def.c
@@ -169,7 +169,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
[SERVICE_EXITED] = "exited",
[SERVICE_RELOAD] = "reload",
[SERVICE_STOP] = "stop",
- [SERVICE_STOP_SIGABRT] = "stop-sigabrt",
+ [SERVICE_STOP_WATCHDOG] = "stop-watchdog",
[SERVICE_STOP_SIGTERM] = "stop-sigterm",
[SERVICE_STOP_SIGKILL] = "stop-sigkill",
[SERVICE_STOP_POST] = "stop-post",
diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h
index d7e2d74669..85f3e42d30 100644
--- a/src/basic/unit-def.h
+++ b/src/basic/unit-def.h
@@ -108,7 +108,7 @@ typedef enum ServiceState {
SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
SERVICE_RELOAD,
SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */
- SERVICE_STOP_SIGABRT, /* Watchdog timeout */
+ SERVICE_STOP_WATCHDOG,
SERVICE_STOP_SIGTERM,
SERVICE_STOP_SIGKILL,
SERVICE_STOP_POST,
diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h
index 61abcd585b..d373f03aca 100644
--- a/src/basic/unit-name.h
+++ b/src/basic/unit-name.h
@@ -61,5 +61,5 @@ static inline int unit_name_mangle(const char *name, UnitNameMangle flags, char
}
int slice_build_parent_slice(const char *slice, char **ret);
-int slice_build_subslice(const char *slice, const char*name, char **subslice);
+int slice_build_subslice(const char *slice, const char *name, char **subslice);
bool slice_name_is_valid(const char *name);
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index a562a397c7..d410c9068b 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -87,17 +87,31 @@ char *getusername_malloc(void) {
return uid_to_name(getuid());
}
-int get_user_creds(
+static inline bool is_nologin_shell(const char *shell) {
+
+ return PATH_IN_SET(shell,
+ /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
+ * message and exits. Different distributions place the binary at different places though,
+ * hence let's list them all. */
+ "/bin/nologin",
+ "/sbin/nologin",
+ "/usr/bin/nologin",
+ "/usr/sbin/nologin",
+ /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
+ * any message printing. Different distributions place the binary at various places but at
+ * least not in the 'sbin' directory. */
+ "/bin/false",
+ "/usr/bin/false",
+ "/bin/true",
+ "/usr/bin/true");
+}
+
+static int synthesize_user_creds(
const char **username,
uid_t *uid, gid_t *gid,
const char **home,
- const char **shell) {
-
- struct passwd *p;
- uid_t u;
-
- assert(username);
- assert(*username);
+ const char **shell,
+ UserCredsFlags flags) {
/* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
* their user record data. */
@@ -129,32 +143,85 @@ int get_user_creds(
*gid = GID_NOBODY;
if (home)
- *home = "/";
+ *home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
if (shell)
- *shell = "/sbin/nologin";
+ *shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/sbin/nologin";
return 0;
}
+ return -ENOMEDIUM;
+}
+
+int get_user_creds(
+ const char **username,
+ uid_t *uid, gid_t *gid,
+ const char **home,
+ const char **shell,
+ UserCredsFlags flags) {
+
+ uid_t u = UID_INVALID;
+ struct passwd *p;
+ int r;
+
+ assert(username);
+ assert(*username);
+
+ if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
+ (!home && !shell)) {
+
+ /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
+ * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the
+ * user database will override the synthetic records instead — except if the user is only interested in
+ * the UID and/or GID (but not the home directory, or the shell), in which case we'll always override
+ * the user database (i.e. the USER_CREDS_PREFER_NSS flag has no effect in this case). Why?
+ * Simply because there are valid usecase where the user might change the home directory or the shell
+ * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
+ * support. */
+
+ r = synthesize_user_creds(username, uid, gid, home, shell, flags);
+ if (r >= 0)
+ return 0;
+ if (r != -ENOMEDIUM) /* not a username we can synthesize */
+ return r;
+ }
+
if (parse_uid(*username, &u) >= 0) {
errno = 0;
p = getpwuid(u);
- /* If there are multiple users with the same id, make
- * sure to leave $USER to the configured value instead
- * of the first occurrence in the database. However if
- * the uid was configured by a numeric uid, then let's
- * pick the real username from /etc/passwd. */
+ /* If there are multiple users with the same id, make sure to leave $USER to the configured value
+ * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
+ * then let's pick the real username from /etc/passwd. */
if (p)
*username = p->pw_name;
+ else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) {
+
+ /* If the specified user is a numeric UID and it isn't in the user database, and the caller
+ * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then juts return that
+ * and don't complain. */
+
+ if (uid)
+ *uid = u;
+
+ return 0;
+ }
} else {
errno = 0;
p = getpwnam(*username);
}
+ if (!p) {
+ r = errno > 0 ? -errno : -ESRCH;
- if (!p)
- return errno > 0 ? -errno : -ESRCH;
+ /* If the user requested that we only synthesize as fallback, do so now */
+ if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
+ if (synthesize_user_creds(username, uid, gid, home, shell, flags) >= 0)
+ return 0;
+ }
+
+ return r;
+ }
if (uid) {
if (!uid_is_valid(p->pw_uid))
@@ -170,66 +237,30 @@ int get_user_creds(
*gid = p->pw_gid;
}
- if (home)
- *home = p->pw_dir;
-
- if (shell)
- *shell = p->pw_shell;
-
- return 0;
-}
-
-static inline bool is_nologin_shell(const char *shell) {
-
- return PATH_IN_SET(shell,
- /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
- * message and exits. Different distributions place the binary at different places though,
- * hence let's list them all. */
- "/bin/nologin",
- "/sbin/nologin",
- "/usr/bin/nologin",
- "/usr/sbin/nologin",
- /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
- * any message printing. Different distributions place the binary at various places but at
- * least not in the 'sbin' directory. */
- "/bin/false",
- "/usr/bin/false",
- "/bin/true",
- "/usr/bin/true");
-}
-
-int get_user_creds_clean(
- const char **username,
- uid_t *uid, gid_t *gid,
- const char **home,
- const char **shell) {
-
- int r;
-
- /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */
-
- r = get_user_creds(username, uid, gid, home, shell);
- if (r < 0)
- return r;
-
- if (shell &&
- (isempty(*shell) || is_nologin_shell(*shell)))
- *shell = NULL;
+ if (home) {
+ if (FLAGS_SET(flags, USER_CREDS_CLEAN) && empty_or_root(p->pw_dir))
+ *home = NULL;
+ else
+ *home = p->pw_dir;
+ }
- if (home && empty_or_root(*home))
- *home = NULL;
+ if (shell) {
+ if (FLAGS_SET(flags, USER_CREDS_CLEAN) && (isempty(p->pw_shell) || is_nologin_shell(p->pw_shell)))
+ *shell = NULL;
+ else
+ *shell = p->pw_shell;
+ }
return 0;
}
-int get_group_creds(const char **groupname, gid_t *gid) {
+int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
struct group *g;
gid_t id;
assert(groupname);
- /* We enforce some special rules for gid=0: in order to avoid
- * NSS lookups for root we hardcode its data. */
+ /* We enforce some special rules for gid=0: in order to avoid NSS lookups for root we hardcode its data. */
if (STR_IN_SET(*groupname, "root", "0")) {
*groupname = "root";
@@ -256,6 +287,12 @@ int get_group_creds(const char **groupname, gid_t *gid) {
if (g)
*groupname = g->gr_name;
+ else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
+ if (gid)
+ *gid = id;
+
+ return 0;
+ }
} else {
errno = 0;
g = getgrnam(*groupname);
@@ -391,7 +428,7 @@ int in_group(const char *name) {
int r;
gid_t gid;
- r = get_group_creds(&name, &gid);
+ r = get_group_creds(&name, &gid, 0);
if (r < 0)
return r;
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index b74f168859..cc899ee76f 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -2,7 +2,9 @@
#pragma once
#include <grp.h>
+#if ENABLE_GSHADOW
#include <gshadow.h>
+#endif
#include <pwd.h>
#include <shadow.h>
#include <stdbool.h>
@@ -25,9 +27,14 @@ static inline int parse_gid(const char *s, gid_t *ret_gid) {
char* getlogname_malloc(void);
char* getusername_malloc(void);
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
-int get_user_creds_clean(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
-int get_group_creds(const char **groupname, gid_t *gid);
+typedef enum UserCredsFlags {
+ USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */
+ USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */
+ USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
+} UserCredsFlags;
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell, UserCredsFlags flags);
+int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags);
char* uid_to_name(uid_t uid);
char* gid_to_name(gid_t gid);
@@ -102,7 +109,7 @@ int fgetgrent_sane(FILE *stream, struct group **gr);
int putpwent_sane(const struct passwd *pw, FILE *stream);
int putspent_sane(const struct spwd *sp, FILE *stream);
int putgrent_sane(const struct group *gr, FILE *stream);
-#ifdef ENABLE_GSHADOW
+#if ENABLE_GSHADOW
int fgetsgent_sane(FILE *stream, struct sgrp **sg);
int putsgent_sane(const struct sgrp *sg, FILE *stream);
#endif
diff --git a/src/basic/utf8.c b/src/basic/utf8.c
index a5ce1a2944..e0d1949dc7 100644
--- a/src/basic/utf8.c
+++ b/src/basic/utf8.c
@@ -61,12 +61,12 @@ static bool unichar_is_control(char32_t ch) {
}
/* count of characters used to encode one unicode char */
-static int utf8_encoded_expected_len(const char *str) {
- unsigned char c;
+static size_t utf8_encoded_expected_len(const char *str) {
+ uint8_t c;
assert(str);
- c = (unsigned char) str[0];
+ c = (uint8_t) str[0];
if (c < 0x80)
return 1;
if ((c & 0xe0) == 0xc0)
@@ -86,7 +86,7 @@ static int utf8_encoded_expected_len(const char *str) {
/* decode one unicode char */
int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
char32_t unichar;
- int len, i;
+ size_t len, i;
assert(str);
@@ -118,6 +118,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
for (i = 1; i < len; i++) {
if (((char32_t)str[i] & 0xc0) != 0x80)
return -EINVAL;
+
unichar <<= 6;
unichar |= (char32_t)str[i] & 0x3f;
}
@@ -154,22 +155,23 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
return true;
}
-const char *utf8_is_valid(const char *str) {
- const uint8_t *p;
+char *utf8_is_valid(const char *str) {
+ const char *p;
assert(str);
- for (p = (const uint8_t*) str; *p; ) {
+ p = str;
+ while (*p) {
int len;
- len = utf8_encoded_valid_unichar((const char *)p);
+ len = utf8_encoded_valid_unichar(p);
if (len < 0)
return NULL;
p += len;
}
- return str;
+ return (char*) str;
}
char *utf8_escape_invalid(const char *str) {
@@ -312,18 +314,25 @@ size_t utf8_encode_unichar(char *out_utf8, char32_t g) {
return 0;
}
-char *utf16_to_utf8(const void *s, size_t length) {
+char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) {
const uint8_t *f;
char *r, *t;
- r = new(char, (length * 4 + 1) / 2 + 1);
+ assert(s);
+
+ /* Input length is in bytes, i.e. the shortest possible character takes 2 bytes. Each unicode character may
+ * take up to 4 bytes in UTF-8. Let's also account for a trailing NUL byte. */
+ if (length * 2 < length)
+ return NULL; /* overflow */
+
+ r = new(char, length * 2 + 1);
if (!r)
return NULL;
- f = s;
+ f = (const uint8_t*) s;
t = r;
- while (f < (const uint8_t*) s + length) {
+ while (f + 1 < (const uint8_t*) s + length) {
char16_t w1, w2;
/* see RFC 2781 section 2.2 */
@@ -333,13 +342,13 @@ char *utf16_to_utf8(const void *s, size_t length) {
if (!utf16_is_surrogate(w1)) {
t += utf8_encode_unichar(t, w1);
-
continue;
}
if (utf16_is_trailing_surrogate(w1))
- continue;
- else if (f >= (const uint8_t*) s + length)
+ continue; /* spurious trailing surrogate, ignore */
+
+ if (f + 1 >= (const uint8_t*) s + length)
break;
w2 = f[1] << 8 | f[0];
@@ -347,7 +356,7 @@ char *utf16_to_utf8(const void *s, size_t length) {
if (!utf16_is_trailing_surrogate(w2)) {
f -= 2;
- continue;
+ continue; /* surrogate missing its trailing surrogate, ignore */
}
t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
@@ -357,6 +366,79 @@ char *utf16_to_utf8(const void *s, size_t length) {
return r;
}
+size_t utf16_encode_unichar(char16_t *out, char32_t c) {
+
+ /* Note that this encodes as little-endian. */
+
+ switch (c) {
+
+ case 0 ... 0xd7ffU:
+ case 0xe000U ... 0xffffU:
+ out[0] = htole16(c);
+ return 1;
+
+ case 0x10000U ... 0x10ffffU:
+ c -= 0x10000U;
+ out[0] = htole16((c >> 10) + 0xd800U);
+ out[1] = htole16((c & 0x3ffU) + 0xdc00U);
+ return 2;
+
+ default: /* A surrogate (invalid) */
+ return 0;
+ }
+}
+
+char16_t *utf8_to_utf16(const char *s, size_t length) {
+ char16_t *n, *p;
+ size_t i;
+ int r;
+
+ assert(s);
+
+ n = new(char16_t, length + 1);
+ if (!n)
+ return NULL;
+
+ p = n;
+
+ for (i = 0; i < length;) {
+ char32_t unichar;
+ size_t e;
+
+ e = utf8_encoded_expected_len(s + i);
+ if (e <= 1) /* Invalid and single byte characters are copied as they are */
+ goto copy;
+
+ if (i + e > length) /* sequence longer than input buffer, then copy as-is */
+ goto copy;
+
+ r = utf8_encoded_to_unichar(s + i, &unichar);
+ if (r < 0) /* sequence invalid, then copy as-is */
+ goto copy;
+
+ p += utf16_encode_unichar(p, unichar);
+ i += e;
+ continue;
+
+ copy:
+ *(p++) = htole16(s[i++]);
+ }
+
+ *p = 0;
+ return n;
+}
+
+size_t char16_strlen(const char16_t *s) {
+ size_t n = 0;
+
+ assert(s);
+
+ while (*s != 0)
+ n++, s++;
+
+ return n;
+}
+
/* expected size used to encode one unicode char */
static int utf8_unichar_to_encoded_len(char32_t unichar) {
@@ -376,8 +458,9 @@ static int utf8_unichar_to_encoded_len(char32_t unichar) {
/* validate one encoded unicode char and return its length */
int utf8_encoded_valid_unichar(const char *str) {
- int len, i, r;
char32_t unichar;
+ size_t len, i;
+ int r;
assert(str);
@@ -399,14 +482,14 @@ int utf8_encoded_valid_unichar(const char *str) {
return r;
/* check if encoded length matches encoded value */
- if (utf8_unichar_to_encoded_len(unichar) != len)
+ if (utf8_unichar_to_encoded_len(unichar) != (int) len)
return -EINVAL;
/* check if value has valid range */
if (!unichar_is_valid(unichar))
return -EINVAL;
- return len;
+ return (int) len;
}
size_t utf8_n_codepoints(const char *str) {
diff --git a/src/basic/utf8.h b/src/basic/utf8.h
index e8af7a576b..628456936e 100644
--- a/src/basic/utf8.h
+++ b/src/basic/utf8.h
@@ -7,14 +7,14 @@
#include <uchar.h>
#include "macro.h"
-#include "missing.h"
+#include "missing_type.h"
#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf"
bool unichar_is_valid(char32_t c);
-const char *utf8_is_valid(const char *s) _pure_;
+char *utf8_is_valid(const char *s) _pure_;
char *ascii_is_valid(const char *s) _pure_;
char *ascii_is_valid_n(const char *str, size_t len);
@@ -25,21 +25,26 @@ char *utf8_escape_invalid(const char *s);
char *utf8_escape_non_printable(const char *str);
size_t utf8_encode_unichar(char *out_utf8, char32_t g);
-char *utf16_to_utf8(const void *s, size_t length);
+size_t utf16_encode_unichar(char16_t *out, char32_t c);
+
+char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */);
+char16_t *utf8_to_utf16(const char *s, size_t length);
+
+size_t char16_strlen(const char16_t *s); /* returns the number of 16bit words in the string (not bytes!) */
int utf8_encoded_valid_unichar(const char *str);
int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar);
static inline bool utf16_is_surrogate(char16_t c) {
- return (0xd800 <= c && c <= 0xdfff);
+ return c >= 0xd800U && c <= 0xdfffU;
}
static inline bool utf16_is_trailing_surrogate(char16_t c) {
- return (0xdc00 <= c && c <= 0xdfff);
+ return c >= 0xdc00U && c <= 0xdfffU;
}
static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) {
- return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000;
+ return ((((char32_t) lead - 0xd800U) << 10) + ((char32_t) trail - 0xdc00U) + 0x10000U);
}
size_t utf8_n_codepoints(const char *str);
diff --git a/src/basic/util.c b/src/basic/util.c
index 8f2d6061da..c4f12a6daa 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -23,6 +23,8 @@
#include "def.h"
#include "device-nodes.h"
#include "dirent-util.h"
+#include "env-file.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@@ -77,31 +79,6 @@ bool display_is_local(const char *display) {
display[1] <= '9';
}
-int socket_from_display(const char *display, char **path) {
- size_t k;
- char *f, *c;
-
- assert(display);
- assert(path);
-
- if (!display_is_local(display))
- return -EINVAL;
-
- k = strspn(display+1, "0123456789");
-
- f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
- if (!f)
- return -ENOMEM;
-
- c = stpcpy(f, "/tmp/.X11-unix/X");
- memcpy(c, display+1, k);
- c[k] = 0;
-
- *path = f;
-
- return 0;
-}
-
bool kexec_loaded(void) {
_cleanup_free_ char *s = NULL;
@@ -131,6 +108,7 @@ int prot_from_flags(int flags) {
bool in_initrd(void) {
struct statfs s;
+ int r;
if (saved_in_initrd >= 0)
return saved_in_initrd;
@@ -145,9 +123,16 @@ bool in_initrd(void) {
* emptying when transititioning to the main systemd.
*/
- saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
- statfs("/", &s) >= 0 &&
- is_temporary_fs(&s);
+ r = getenv_bool_secure("SYSTEMD_IN_INITRD");
+ if (r < 0 && r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m");
+
+ if (r >= 0)
+ saved_in_initrd = r > 0;
+ else
+ saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
+ statfs("/", &s) >= 0 &&
+ is_temporary_fs(&s);
return saved_in_initrd;
}
@@ -158,7 +143,7 @@ void in_initrd_force(bool value) {
/* hey glibc, APIs with callbacks without a user pointer are so useless */
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
- int (*compar) (const void *, const void *, void *), void *arg) {
+ __compar_d_fn_t compar, void *arg) {
size_t l, u, idx;
const void *p;
int comparison;
@@ -181,6 +166,28 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
return NULL;
}
+bool memeqzero(const void *data, size_t length) {
+ /* Does the buffer consist entirely of NULs?
+ * Copied from https://github.com/systemd/casync/, copied in turn from
+ * https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92,
+ * which is licensed CC-0.
+ */
+
+ const uint8_t *p = data;
+ size_t i;
+
+ /* Check first 16 bytes manually */
+ for (i = 0; i < 16; i++, length--) {
+ if (length == 0)
+ return true;
+ if (p[i])
+ return false;
+ }
+
+ /* Now we know first 16 bytes are NUL, memcmp with self. */
+ return memcmp(data, p + i, length) == 0;
+}
+
int on_ac_power(void) {
bool found_offline = false, found_online = false;
_cleanup_closedir_ DIR *d = NULL;
@@ -255,11 +262,18 @@ int container_get_leader(const char *machine, pid_t *pid) {
assert(machine);
assert(pid);
+ if (streq(machine, ".host")) {
+ *pid = 1;
+ return 0;
+ }
+
if (!machine_name_is_valid(machine))
return -EINVAL;
p = strjoina("/run/systemd/machines/", machine);
- r = parse_env_file(NULL, p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
+ r = parse_env_file(NULL, p,
+ "LEADER", &s,
+ "CLASS", &class);
if (r == -ENOENT)
return -EHOSTDOWN;
if (r < 0)
@@ -617,7 +631,7 @@ void disable_coredumps(void) {
if (detect_container() > 0)
return;
- r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
+ r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
}
diff --git a/src/basic/util.h b/src/basic/util.h
index 9699d228f9..f009d37d4c 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -25,7 +25,6 @@
#include "format-util.h"
#include "macro.h"
-#include "missing.h"
#include "time-util.h"
size_t page_size(void) _pure_;
@@ -50,7 +49,6 @@ static inline const char* enable_disable(bool b) {
bool plymouth_running(void);
bool display_is_local(const char *display) _pure_;
-int socket_from_display(const char *display, char **path);
#define NULSTR_FOREACH(i, l) \
for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
@@ -69,15 +67,21 @@ bool in_initrd(void);
void in_initrd_force(bool value);
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
- int (*compar) (const void *, const void *, void *),
- void *arg);
+ __compar_d_fn_t compar, void *arg);
+
+#define typesafe_bsearch_r(k, b, n, func, userdata) \
+ ({ \
+ const typeof(b[0]) *_k = k; \
+ int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \
+ xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \
+ })
/**
* Normal bsearch requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
static inline void* bsearch_safe(const void *key, const void *base,
- size_t nmemb, size_t size, comparison_fn_t compar) {
+ size_t nmemb, size_t size, __compar_fn_t compar) {
if (nmemb <= 0)
return NULL;
@@ -85,11 +89,18 @@ static inline void* bsearch_safe(const void *key, const void *base,
return bsearch(key, base, nmemb, size, compar);
}
+#define typesafe_bsearch(k, b, n, func) \
+ ({ \
+ const typeof(b[0]) *_k = k; \
+ int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \
+ bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \
+ })
+
/**
* Normal qsort requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
-static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
+static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
if (nmemb <= 1)
return;
@@ -105,7 +116,7 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_
qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
})
-static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void *userdata) {
+static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) {
if (nmemb <= 1)
return;
@@ -113,9 +124,13 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, int (*com
qsort_r(base, nmemb, size, compar, userdata);
}
-/**
- * Normal memcpy requires src to be nonnull. We do nothing if n is 0.
- */
+#define typesafe_qsort_r(p, n, func, userdata) \
+ ({ \
+ int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \
+ qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \
+ })
+
+/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
static inline void memcpy_safe(void *dst, const void *src, size_t n) {
if (n == 0)
return;
@@ -123,11 +138,36 @@ static inline void memcpy_safe(void *dst, const void *src, size_t n) {
memcpy(dst, src, n);
}
+/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */
+static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
+ if (n == 0)
+ return 0;
+ assert(s1);
+ assert(s2);
+ return memcmp(s1, s2, n);
+}
+
+/* Compare s1 (length n1) with s2 (length n2) in lexicographic order. */
+static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2) {
+ return memcmp_safe(s1, s2, MIN(n1, n2))
+ ?: CMP(n1, n2);
+}
+
int on_ac_power(void);
-#define memzero(x,l) (memset((x), 0, (l)))
+#define memzero(x,l) \
+ ({ \
+ size_t _l_ = (l); \
+ void *_x_ = (x); \
+ _l_ == 0 ? _x_ : memset(_x_, 0, _l_); \
+ })
+
#define zero(x) (memzero(&(x), sizeof(x)))
+bool memeqzero(const void *data, size_t length);
+
+#define eqzero(x) memeqzero(x, sizeof(x))
+
static inline void *mempset(void *s, int c, size_t n) {
memset(s, c, n);
return (uint8_t*)s + n;
@@ -137,7 +177,8 @@ static inline void _reset_errno_(int *saved_errno) {
errno = *saved_errno;
}
-#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno
+#define PROTECT_ERRNO \
+ _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno
static inline int negative_errno(void) {
/* This helper should be used to shut up gcc if you know 'errno' is
@@ -158,7 +199,7 @@ static inline unsigned u64log2(uint64_t n) {
static inline unsigned u32ctz(uint32_t n) {
#if __SIZEOF_INT__ == 4
- return __builtin_ctz(n);
+ return n != 0 ? __builtin_ctz(n) : 32;
#else
#error "Wut?"
#endif
diff --git a/src/basic/virt.c b/src/basic/virt.c
index d347732bb3..f63f15f6c1 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -259,21 +259,38 @@ static int detect_vm_hypervisor(void) {
}
static int detect_vm_uml(void) {
- _cleanup_free_ char *cpuinfo_contents = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
int r;
/* Detect User-Mode Linux by reading /proc/cpuinfo */
- r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
- if (r == -ENOENT) {
- log_debug("/proc/cpuinfo not found, assuming no UML virtualization.");
- return VIRTUALIZATION_NONE;
+ f = fopen("/proc/cpuinfo", "re");
+ if (!f) {
+ if (errno == ENOENT) {
+ log_debug("/proc/cpuinfo not found, assuming no UML virtualization.");
+ return VIRTUALIZATION_NONE;
+ }
+ return -errno;
}
- if (r < 0)
- return r;
- if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
- log_debug("UML virtualization found in /proc/cpuinfo");
- return VIRTUALIZATION_UML;
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ const char *t;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ t = startswith(line, "vendor_id\t: ");
+ if (t) {
+ if (startswith(t, "User Mode Linux")) {
+ log_debug("UML virtualization found in /proc/cpuinfo");
+ return VIRTUALIZATION_UML;
+ }
+
+ break;
+ }
}
log_debug("UML virtualization not found in /proc/cpuinfo.");
diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c
index c5c55ea846..0ee0979837 100644
--- a/src/basic/xattr-util.c
+++ b/src/basic/xattr-util.c
@@ -2,7 +2,6 @@
#include <errno.h>
#include <fcntl.h>
-#include <linux/stat.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c
index 697e16c53a..af31f0967e 100644
--- a/src/binfmt/binfmt.c
+++ b/src/binfmt/binfmt.c
@@ -14,19 +14,22 @@
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
+#include "main-func.h"
#include "pager.h"
+#include "path-util.h"
+#include "pretty-print.h"
#include "string-util.h"
#include "strv.h"
-#include "terminal-util.h"
#include "util.h"
static bool arg_cat_config = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static int delete_rule(const char *rule) {
_cleanup_free_ char *x = NULL, *fn = NULL;
char *e;
+ assert(rule);
assert(rule[0]);
x = strdup(rule);
@@ -36,19 +39,23 @@ static int delete_rule(const char *rule) {
e = strchrnul(x+1, x[0]);
*e = 0;
+ if (!filename_is_valid(x + 1))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Rule file name '%s' is not valid, refusing.", x + 1);
+
fn = strappend("/proc/sys/fs/binfmt_misc/", x+1);
if (!fn)
return log_oom();
- return write_string_file(fn, "-1", 0);
+ return write_string_file(fn, "-1", WRITE_STRING_FILE_DISABLE_BUFFER);
}
static int apply_rule(const char *rule) {
int r;
- delete_rule(rule);
+ (void) delete_rule(rule);
- r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule, 0);
+ r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return log_error_errno(r, "Failed to add binary format: %m");
@@ -66,25 +73,25 @@ static int apply_file(const char *path, bool ignore_enoent) {
if (ignore_enoent && r == -ENOENT)
return 0;
- return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
+ return log_error_errno(r, "Failed to open file '%s': %m", path);
}
log_debug("apply: %s", path);
for (;;) {
- char l[LINE_MAX], *p;
+ _cleanup_free_ char *line = NULL;
+ char *p;
int k;
- if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
-
- return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
- }
+ k = read_line(f, LONG_LINE_MAX, &line);
+ if (k < 0)
+ return log_error_errno(k, "Failed to read file '%s': %m", path);
+ if (k == 0)
+ break;
- p = strstrip(l);
- if (!*p)
+ p = strstrip(line);
+ if (isempty(p))
continue;
- if (strchr(COMMENTS "\n", *p))
+ if (strchr(COMMENTS, p[0]))
continue;
k = apply_rule(p);
@@ -95,18 +102,29 @@ static int apply_file(const char *path, bool ignore_enoent) {
return r;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-binfmt.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
- "Registers binary formats.\n\n"
+ "Registers binary formats with the kernel.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --cat-config Show configuration files\n"
" --no-pager Do not pipe output into a pager\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
-
enum {
ARG_VERSION = 0x100,
ARG_CAT_CONFIG,
@@ -131,8 +149,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -142,7 +159,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case '?':
@@ -152,24 +169,21 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (arg_cat_config && argc > optind) {
- log_error("Positional arguments are not allowed with --cat-config");
- return -EINVAL;
- }
+ if (arg_cat_config && argc > optind)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Positional arguments are not allowed with --cat-config");
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r, k;
r = parse_argv(argc, argv);
if (r <= 0)
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
@@ -188,20 +202,17 @@ int main(int argc, char *argv[]) {
char **f;
r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("binfmt.d"));
- if (r < 0) {
- log_error_errno(r, "Failed to enumerate binfmt.d files: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate binfmt.d files: %m");
if (arg_cat_config) {
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
- r = cat_files(NULL, files, 0);
- goto finish;
+ return cat_files(NULL, files, 0);
}
/* Flush out all rules */
- write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", 0);
+ write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", WRITE_STRING_FILE_DISABLE_BUFFER);
STRV_FOREACH(f, files) {
k = apply_file(*f, true);
@@ -210,8 +221,7 @@ int main(int argc, char *argv[]) {
}
}
-finish:
- pager_close();
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/boot/bless-boot-generator.c b/src/boot/bless-boot-generator.c
new file mode 100644
index 0000000000..e28cccd761
--- /dev/null
+++ b/src/boot/bless-boot-generator.c
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "efivars.h"
+#include "generator.h"
+#include "log.h"
+#include "mkdir.h"
+#include "special.h"
+#include "string-util.h"
+#include "util.h"
+#include "virt.h"
+
+/* This generator pulls systemd-bless-boot.service into the initial transaction if the "LoaderBootCountPath" EFI
+ * variable is set, i.e. the system boots up with boot counting in effect, which means we should mark the boot as
+ * "good" if we manage to boot up far enough. */
+
+static const char *arg_dest = "/tmp";
+
+int main(int argc, char *argv[]) {
+ const char *p;
+
+ log_setup_generator();
+
+ if (argc > 1 && argc != 4) {
+ log_error("This program takes three or no arguments.");
+ return EXIT_FAILURE;
+ }
+
+ if (argc > 1)
+ arg_dest = argv[2];
+
+ if (in_initrd() > 0) {
+ log_debug("Skipping generator, running in the initrd.");
+ return EXIT_SUCCESS;
+ }
+
+ if (detect_container() > 0) {
+ log_debug("Skipping generator, running in a container.");
+ return EXIT_SUCCESS;
+ }
+
+ if (!is_efi_boot()) {
+ log_debug("Skipping generator, not an EFI boot.");
+ return EXIT_SUCCESS;
+ }
+
+ if (access("/sys/firmware/efi/efivars/LoaderBootCountPath-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f", F_OK) < 0) {
+
+ if (errno == ENOENT) {
+ log_debug_errno(errno, "Skipping generator, not booted with boot counting in effect.");
+ return EXIT_SUCCESS;
+ }
+
+ log_error_errno(errno, "Failed to check if LoaderBootCountPath EFI variable exists: %m");
+ return EXIT_FAILURE;
+ }
+
+ /* We pull this in from basic.target so that it ends up in all "regular" boot ups, but not in rescue.target or
+ * even emergency.target. */
+ p = strjoina(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/systemd-bless-boot.service");
+ (void) mkdir_parents(p, 0755);
+ if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-bless-boot.service", p) < 0) {
+ log_error_errno(errno, "Failed to create symlink '%s': %m", p);
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c
new file mode 100644
index 0000000000..42b9618aa9
--- /dev/null
+++ b/src/boot/bless-boot.c
@@ -0,0 +1,472 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "bootspec.h"
+#include "efivars.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "util.h"
+#include "verbs.h"
+#include "virt.h"
+
+static char *arg_path = NULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_path, freep);
+
+static int help(int argc, char *argv[], void *userdata) {
+
+ printf("%s [COMMAND] [OPTIONS...]\n"
+ "\n"
+ "Mark the boot process as good or bad.\n\n"
+ " -h --help Show this help\n"
+ " --version Print version\n"
+ " --path=PATH Path to the EFI System Partition (ESP)\n"
+ "\n"
+ "Commands:\n"
+ " good Mark this boot as good\n"
+ " bad Mark this boot as bad\n"
+ " indeterminate Undo any marking as good or bad\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_PATH = 0x100,
+ ARG_VERSION,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "path", required_argument, NULL, ARG_PATH },
+ {}
+ };
+
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ switch (c) {
+
+ case 'h':
+ help(0, NULL, NULL);
+ return 0;
+
+ case ARG_VERSION:
+ return version();
+
+ case ARG_PATH:
+ r = free_and_strdup(&arg_path, optarg);
+ if (r < 0)
+ return log_oom();
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unknown option");
+ }
+
+ return 1;
+}
+
+static int acquire_esp(void) {
+ _cleanup_free_ char *np = NULL;
+ int r;
+
+ r = find_esp_and_warn(arg_path, false, &np, NULL, NULL, NULL, NULL);
+ if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn in this one error case, but in all others */
+ return log_error_errno(r,
+ "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"
+ "Alternatively, use --path= to specify path to mount point.");
+ if (r < 0)
+ return r;
+
+ free_and_replace(arg_path, np);
+ log_debug("Using EFI System Partition at %s.", arg_path);
+
+ return 0;
+}
+
+static int parse_counter(
+ const char *path,
+ const char **p,
+ uint64_t *ret_left,
+ uint64_t *ret_done) {
+
+ uint64_t left, done;
+ const char *z, *e;
+ size_t k;
+ int r;
+
+ assert(path);
+ assert(p);
+
+ e = *p;
+ assert(e);
+ assert(*e == '+');
+
+ e++;
+
+ k = strspn(e, DIGITS);
+ if (k == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Can't parse empty 'tries left' counter from LoaderBootCountPath: %s",
+ path);
+
+ z = strndupa(e, k);
+ r = safe_atou64(z, &left);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse 'tries left' counter from LoaderBootCountPath: %s", path);
+
+ e += k;
+
+ if (*e == '-') {
+ e++;
+
+ k = strspn(e, DIGITS);
+ if (k == 0) /* If there's a "-" there also needs to be at least one digit */
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Can't parse empty 'tries done' counter from LoaderBootCountPath: %s",
+ path);
+
+ z = strndupa(e, k);
+ r = safe_atou64(z, &done);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse 'tries done' counter from LoaderBootCountPath: %s", path);
+
+ e += k;
+ } else
+ done = 0;
+
+ if (done == 0)
+ log_warning("The 'tries done' counter is currently at zero. This can't really be, after all we are running, and this boot must hence count as one. Proceeding anyway.");
+
+ *p = e;
+
+ if (ret_left)
+ *ret_left = left;
+
+ if (ret_done)
+ *ret_done = done;
+
+ return 0;
+}
+
+static int acquire_boot_count_path(
+ char **ret_path,
+ char **ret_prefix,
+ uint64_t *ret_left,
+ uint64_t *ret_done,
+ char **ret_suffix) {
+
+ _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL;
+ const char *last, *e;
+ uint64_t left, done;
+ int r;
+
+ r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderBootCountPath", &path);
+ if (r == -ENOENT)
+ return -EUNATCH; /* in this case, let the caller print a message */
+ if (r < 0)
+ return log_error_errno(r, "Failed to read LoaderBootCountPath EFI variable: %m");
+
+ efi_tilt_backslashes(path);
+
+ if (!path_is_normalized(path))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Path read from LoaderBootCountPath is not normalized, refusing: %s",
+ path);
+
+ if (!path_is_absolute(path))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Path read from LoaderBootCountPath is not absolute, refusing: %s",
+ path);
+
+ last = last_path_component(path);
+ e = strrchr(last, '+');
+ if (!e)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Path read from LoaderBootCountPath does not contain a counter, refusing: %s",
+ path);
+
+ if (ret_prefix) {
+ prefix = strndup(path, e - path);
+ if (!prefix)
+ return log_oom();
+ }
+
+ r = parse_counter(path, &e, &left, &done);
+ if (r < 0)
+ return r;
+
+ if (ret_suffix) {
+ suffix = strdup(e);
+ if (!suffix)
+ return log_oom();
+
+ *ret_suffix = TAKE_PTR(suffix);
+ }
+
+ if (ret_path)
+ *ret_path = TAKE_PTR(path);
+ if (ret_prefix)
+ *ret_prefix = TAKE_PTR(prefix);
+ if (ret_left)
+ *ret_left = left;
+ if (ret_done)
+ *ret_done = done;
+
+ return 0;
+}
+
+static int make_good(const char *prefix, const char *suffix, char **ret) {
+ _cleanup_free_ char *good = NULL;
+
+ assert(prefix);
+ assert(suffix);
+ assert(ret);
+
+ /* Generate the path we'd use on good boots. This one is easy. If we are successful, we simple drop the counter
+ * pair entirely from the name. After all, we know all is good, and the logs will contain information about the
+ * tries we needed to come here, hence it's safe to drop the counters from the name. */
+
+ good = strjoin(prefix, suffix);
+ if (!good)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(good);
+ return 0;
+}
+
+static int make_bad(const char *prefix, uint64_t done, const char *suffix, char **ret) {
+ _cleanup_free_ char *bad = NULL;
+
+ assert(prefix);
+ assert(suffix);
+ assert(ret);
+
+ /* Generate the path we'd use on bad boots. Let's simply set the 'left' counter to zero, and keep the 'done'
+ * counter. The information might be interesting to boot loaders, after all. */
+
+ if (done == 0) {
+ bad = strjoin(prefix, "+0", suffix);
+ if (!bad)
+ return -ENOMEM;
+ } else {
+ if (asprintf(&bad, "%s+0-%" PRIu64 "%s", prefix, done, suffix) < 0)
+ return -ENOMEM;
+ }
+
+ *ret = TAKE_PTR(bad);
+ return 0;
+}
+
+static const char *skip_slash(const char *path) {
+ assert(path);
+ assert(path[0] == '/');
+
+ return path + 1;
+}
+
+static int verb_status(int argc, char *argv[], void *userdata) {
+
+ _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
+ _cleanup_close_ int fd = -1;
+ uint64_t left, done;
+ int r;
+
+ r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix);
+ if (r == -EUNATCH) { /* No boot count in place, then let's consider this a "clean" boot, as "good", "bad" or "indeterminate" don't apply. */
+ puts("clean");
+ return 0;
+ }
+ if (r < 0)
+ return r;
+
+ r = acquire_esp();
+ if (r < 0)
+ return r;
+
+ r = make_good(prefix, suffix, &good);
+ if (r < 0)
+ return log_oom();
+
+ r = make_bad(prefix, done, suffix, &bad);
+ if (r < 0)
+ return log_oom();
+
+ log_debug("Booted file: %s%s\n"
+ "The same modified for 'good': %s%s\n"
+ "The same modified for 'bad': %s%s\n",
+ arg_path, path,
+ arg_path, good,
+ arg_path, bad);
+
+ log_debug("Tries left: %" PRIu64"\n"
+ "Tries done: %" PRIu64"\n",
+ left, done);
+
+ fd = open(arg_path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open ESP '%s': %m", arg_path);
+
+ if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) {
+ puts("indeterminate");
+ return 0;
+ }
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to check if '%s' exists: %m", path);
+
+ if (faccessat(fd, skip_slash(good), F_OK, 0) >= 0) {
+ puts("good");
+ return 0;
+ }
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to check if '%s' exists: %m", good);
+
+ if (faccessat(fd, skip_slash(bad), F_OK, 0) >= 0) {
+ puts("bad");
+ return 0;
+ }
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to check if '%s' exists: %m", bad);
+
+ return log_error_errno(errno, "Couldn't determine boot state: %m");
+}
+
+static int verb_set(int argc, char *argv[], void *userdata) {
+ _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL, *parent = NULL;
+ const char *target, *source1, *source2;
+ _cleanup_close_ int fd = -1;
+ uint64_t done;
+ int r;
+
+ r = acquire_boot_count_path(&path, &prefix, NULL, &done, &suffix);
+ if (r == -EUNATCH) /* acquire_boot_count_path() won't log on its own for this specific error */
+ return log_error_errno(r, "Not booted with boot counting in effect.");
+ if (r < 0)
+ return r;
+
+ r = acquire_esp();
+ if (r < 0)
+ return r;
+
+ r = make_good(prefix, suffix, &good);
+ if (r < 0)
+ return log_oom();
+
+ r = make_bad(prefix, done, suffix, &bad);
+ if (r < 0)
+ return log_oom();
+
+ fd = open(arg_path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open ESP '%s': %m", arg_path);
+
+ /* Figure out what rename to what */
+ if (streq(argv[0], "good")) {
+ target = good;
+ source1 = path;
+ source2 = bad; /* Maybe this boot was previously marked as 'bad'? */
+ } else if (streq(argv[0], "bad")) {
+ target = bad;
+ source1 = path;
+ source2 = good; /* Maybe this boot was previously marked as 'good'? */
+ } else {
+ assert(streq(argv[0], "indeterminate"));
+ target = path;
+ source1 = good;
+ source2 = bad;
+ }
+
+ r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target));
+ if (r == -EEXIST)
+ goto exists;
+ else if (r == -ENOENT) {
+
+ r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target));
+ if (r == -EEXIST)
+ goto exists;
+ else if (r == -ENOENT) {
+
+ if (access(target, F_OK) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
+ goto exists;
+
+ return log_error_errno(r, "Can't find boot counter source file for '%s': %m", target);
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target);
+ else
+ log_debug("Successfully renamed '%s' to '%s'.", source2, target);
+
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target);
+ else
+ log_debug("Successfully renamed '%s' to '%s'.", source1, target);
+
+ /* First, fsync() the directory these files are located in */
+ parent = dirname_malloc(path);
+ if (!parent)
+ return log_oom();
+
+ r = fsync_path_at(fd, skip_slash(parent));
+ if (r < 0)
+ log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m");
+
+ /* Secondly, syncfs() the whole file system these files are located in */
+ if (syncfs(fd) < 0)
+ log_debug_errno(errno, "Failed to synchronize ESP, ignoring: %m");
+
+ log_info("Marked boot as '%s'. (Boot attempt counter is at %" PRIu64".)", argv[0], done);
+
+ return 1;
+
+exists:
+ log_debug("Operation already executed before, not doing anything.");
+ return 0;
+}
+
+static int run(int argc, char *argv[]) {
+
+ static const Verb verbs[] = {
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
+ { "good", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_set },
+ { "bad", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_set },
+ { "indeterminate", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_set },
+ {}
+ };
+
+ int r;
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ if (detect_container() > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Marking a boot is not supported in containers.");
+
+ if (!is_efi_boot())
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Marking a boot is only supported on EFI systems.");
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/boot/boot-check-no-failures.c b/src/boot/boot-check-no-failures.c
new file mode 100644
index 0000000000..3284a04793
--- /dev/null
+++ b/src/boot/boot-check-no-failures.c
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "log.h"
+#include "main-func.h"
+#include "util.h"
+
+static int help(void) {
+
+ printf("%s [COMMAND] [OPTIONS...]\n"
+ "\n"
+ "Verify system operational state.\n\n"
+ " -h --help Show this help\n"
+ " --version Print version\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_PATH = 0x100,
+ ARG_VERSION,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ {}
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_VERSION:
+ return version();
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unknown option");
+ }
+
+ return 1;
+}
+
+static int run(int argc, 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;
+ uint32_t n;
+ int r;
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to system bus: %m");
+
+ r = sd_bus_get_property_trivial(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "NFailedUnits",
+ &error,
+ 'u',
+ &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get failed units counter: %s", bus_error_message(&error, r));
+
+ if (n > 0)
+ log_notice("Health check: %" PRIu32 " units have failed.", n);
+ else
+ log_info("Health check: no failed units.");
+
+ return n > 0;
+}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 2832a39dd7..dc2fd96628 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -29,13 +29,18 @@
#include "fileio.h"
#include "fs-util.h"
#include "locale-util.h"
+#include "main-func.h"
+#include "pager.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "rm-rf.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "umask-util.h"
+#include "utf8.h"
#include "util.h"
#include "verbs.h"
#include "virt.h"
@@ -43,6 +48,9 @@
static char *arg_path = NULL;
static bool arg_print_path = false;
static bool arg_touch_variables = true;
+static PagerFlags arg_pager_flags = 0;
+
+STATIC_DESTRUCTOR_REGISTER(arg_path, freep);
static int acquire_esp(
bool unprivileged_mode,
@@ -155,9 +163,9 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
if (r < 0)
return r;
if (r > 0)
- printf(" File: %s/%s/%s (%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, v);
+ printf(" File: %s/%s/%s (%s%s%s)\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name, ansi_highlight(), v, ansi_normal());
else
- printf(" File: %s/%s/%s\n", special_glyph(TREE_RIGHT), path, de->d_name);
+ printf(" File: %s/%s/%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name);
c++;
}
@@ -167,7 +175,7 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
static int status_binaries(const char *esp_path, sd_id128_t partition) {
int r;
- printf("Boot Loader Binaries:\n");
+ printf("Available Boot Loaders on ESP:\n");
if (!esp_path) {
printf(" ESP: Cannot find or access mount point of ESP.\n\n");
@@ -181,13 +189,13 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) {
r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
if (r == 0)
- log_error("systemd-boot not installed in ESP.");
+ log_info("systemd-boot not installed in ESP.");
else if (r < 0)
return r;
r = enumerate_binaries(esp_path, "EFI/BOOT", "boot");
if (r == 0)
- log_error("No default/fallback boot loader installed in ESP.");
+ log_info("No default/fallback boot loader installed in ESP.");
else if (r < 0)
return r;
@@ -213,20 +221,19 @@ static int print_efi_option(uint16_t id, bool in_order) {
efi_tilt_backslashes(path);
- printf(" Title: %s\n", strna(title));
+ printf(" Title: %s%s%s\n", ansi_highlight(), strna(title), ansi_normal());
printf(" ID: 0x%04X\n", id);
printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition));
- printf(" File: %s%s\n", special_glyph(TREE_RIGHT), path);
+ printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path);
printf("\n");
return 0;
}
static int status_variables(void) {
- int n_options, n_order;
_cleanup_free_ uint16_t *options = NULL, *order = NULL;
- int i;
+ int n_options, n_order, i;
n_options = efi_get_boot_options(&options);
if (n_options == -ENOENT)
@@ -240,10 +247,10 @@ static int status_variables(void) {
if (n_order == -ENOENT)
n_order = 0;
else if (n_order < 0)
- return log_error_errno(n_order, "Failed to read EFI boot order.");
+ return log_error_errno(n_order, "Failed to read EFI boot order: %m");
/* print entries in BootOrder first */
- printf("Boot Loader Entries in EFI Variables:\n");
+ printf("Boot Loaders Listed in EFI Variables:\n");
for (i = 0; i < n_order; i++)
print_efi_option(order[i], true);
@@ -264,49 +271,65 @@ static int status_variables(void) {
return 0;
}
-static int status_entries(const char *esp_path, sd_id128_t partition) {
- int r;
+static int boot_entry_show(const BootEntry *e, bool show_as_default) {
+ assert(e);
+
+ printf(" title: %s%s%s%s%s%s\n",
+ ansi_highlight(),
+ boot_entry_title(e),
+ ansi_normal(),
+ ansi_highlight_green(),
+ show_as_default ? " (default)" : "",
+ ansi_normal());
+
+ if (e->id)
+ printf(" id: %s\n", e->id);
+ if (e->version)
+ printf(" version: %s\n", e->version);
+ if (e->machine_id)
+ printf(" machine-id: %s\n", e->machine_id);
+ if (e->architecture)
+ printf(" architecture: %s\n", e->architecture);
+ if (e->kernel)
+ printf(" linux: %s\n", e->kernel);
+ if (!strv_isempty(e->initrd)) {
+ _cleanup_free_ char *t;
+
+ t = strv_join(e->initrd, " ");
+ if (!t)
+ return log_oom();
+
+ printf(" initrd: %s\n", t);
+ }
+ if (!strv_isempty(e->options)) {
+ _cleanup_free_ char *t;
- _cleanup_(boot_config_free) BootConfig config = {};
+ t = strv_join(e->options, " ");
+ if (!t)
+ return log_oom();
+
+ printf(" options: %s\n", t);
+ }
+ if (e->device_tree)
+ printf(" devicetree: %s\n", e->device_tree);
- printf("Default Boot Entry:\n");
+ return 0;
+}
+
+static int status_entries(const char *esp_path, sd_id128_t partition) {
+ _cleanup_(boot_config_free) BootConfig config = {};
+ int r;
r = boot_entries_load_config(esp_path, &config);
if (r < 0)
- return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m",
- esp_path);
+ return r;
if (config.default_entry < 0)
- printf("%zu entries, no entry suitable as default\n", config.n_entries);
+ printf("%zu entries, no entry could be determined as default.\n", config.n_entries);
else {
- const BootEntry *e = &config.entries[config.default_entry];
-
- printf(" title: %s\n", boot_entry_title(e));
- if (e->version)
- printf(" version: %s\n", e->version);
- if (e->kernel)
- printf(" linux: %s\n", e->kernel);
- if (!strv_isempty(e->initrd)) {
- _cleanup_free_ char *t;
-
- t = strv_join(e->initrd, " ");
- if (!t)
- return log_oom();
+ printf("Default Boot Loader Entry:\n");
- printf(" initrd: %s\n", t);
- }
- if (!strv_isempty(e->options)) {
- _cleanup_free_ char *t;
-
- t = strv_join(e->options, " ");
- if (!t)
- return log_oom();
-
- printf(" options: %s\n", t);
- }
- if (e->device_tree)
- printf(" devicetree: %s\n", e->device_tree);
- puts("");
+ boot_entry_show(config.entries + config.default_entry, false);
}
return 0;
@@ -350,18 +373,18 @@ static int version_check(int fd_from, const char *from, int fd_to, const char *t
r = get_file_version(fd_from, &a);
if (r < 0)
return r;
- if (r == 0) {
- log_error("Source file \"%s\" does not carry version information!", from);
- return -EINVAL;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Source file \"%s\" does not carry version information!",
+ from);
r = get_file_version(fd_to, &b);
if (r < 0)
return r;
- if (r == 0 || compare_product(a, b) != 0) {
- log_notice("Skipping \"%s\", since it's owned by another boot loader.", to);
- return -EEXIST;
- }
+ if (r == 0 || compare_product(a, b) != 0)
+ return log_notice_errno(SYNTHETIC_ERRNO(EEXIST),
+ "Skipping \"%s\", since it's owned by another boot loader.",
+ to);
if (compare_version(a, b) < 0) {
log_warning("Skipping \"%s\", since a newer boot loader version exists already.", to);
@@ -800,15 +823,15 @@ static int install_loader_config(const char *esp_path) {
if (fd < 0)
return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p);
- f = fdopen(fd, "we");
+ f = fdopen(fd, "w");
if (!f) {
safe_close(fd);
return log_oom();
}
- fprintf(f, "#timeout 3\n");
- fprintf(f, "#console-mode keep\n");
- fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
+ fprintf(f, "#timeout 3\n"
+ "#console-mode keep\n"
+ "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
r = fflush_sync_and_check(f);
if (r < 0)
@@ -826,23 +849,33 @@ static int install_loader_config(const char *esp_path) {
}
static int help(int argc, char *argv[], void *userdata) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("bootctl", "1", &link);
+ if (r < 0)
+ return log_oom();
- printf("%s [COMMAND] [OPTIONS...]\n"
- "\n"
+ printf("%s [COMMAND] [OPTIONS...]\n\n"
"Install, update or remove the systemd-boot EFI boot manager.\n\n"
" -h --help Show this help\n"
" --version Print version\n"
" --path=PATH Path to the EFI System Partition (ESP)\n"
" -p --print-path Print path to the EFI partition\n"
" --no-variables Don't touch EFI variables\n"
- "\n"
- "Commands:\n"
+ " --no-pager Do not pipe output into a pager\n"
+ "\nBoot Loader Commands:\n"
" status Show status of installed systemd-boot and EFI variables\n"
- " list List boot entries\n"
" install Install systemd-boot to the ESP and EFI variables\n"
" update Update systemd-boot in the ESP and EFI variables\n"
- " remove Remove systemd-boot from the ESP and EFI variables\n",
- program_invocation_short_name);
+ " remove Remove systemd-boot from the ESP and EFI variables\n"
+ "\nBoot Loader Entries Commands:\n"
+ " list List boot loader entries\n"
+ " set-default ID Set default boot loader entry\n"
+ " set-oneshot ID Set default boot loader entry, for next boot only\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link);
return 0;
}
@@ -852,6 +885,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PATH = 0x100,
ARG_VERSION,
ARG_NO_VARIABLES,
+ ARG_NO_PAGER,
};
static const struct option options[] = {
@@ -860,7 +894,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "path", required_argument, NULL, ARG_PATH },
{ "print-path", no_argument, NULL, 'p' },
{ "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
- { NULL, 0, NULL, 0 }
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ {}
};
int c, r;
@@ -892,6 +927,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_touch_variables = false;
break;
+ case ARG_NO_PAGER:
+ arg_pager_flags |= PAGER_DISABLE;
+ break;
+
case '?':
return -EINVAL;
@@ -931,15 +970,31 @@ static int verb_status(int argc, char *argv[], void *userdata) {
r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just show what we
* can show */
+ (void) pager_open(arg_pager_flags);
+
if (is_efi_boot()) {
+ static const struct {
+ uint64_t flag;
+ const char *name;
+ } flags[] = {
+ { EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
+ { EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
+ { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
+ { EFI_LOADER_FEATURE_ENTRY_DEFAULT, "Default entry control" },
+ { EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" },
+ };
+
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
sd_id128_t loader_part_uuid = SD_ID128_NULL;
+ uint64_t loader_features = 0;
+ size_t i;
read_loader_efi_var("LoaderFirmwareType", &fw_type);
read_loader_efi_var("LoaderFirmwareInfo", &fw_info);
read_loader_efi_var("LoaderInfo", &loader);
read_loader_efi_var("StubInfo", &stub);
read_loader_efi_var("LoaderImageIdentifier", &loader_path);
+ (void) efi_loader_get_features(&loader_features);
if (loader_path)
efi_tilt_backslashes(loader_path);
@@ -949,23 +1004,27 @@ static int verb_status(int argc, char *argv[], void *userdata) {
r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
printf("System:\n");
- printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info));
+ printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
+ printf(" Secure Boot: %sd\n", enable_disable(is_efi_secure_boot()));
+ printf(" Setup Mode: %s\n", is_efi_secure_boot_setup_mode() ? "setup" : "user");
+ printf("\n");
- k = is_efi_secure_boot();
- if (k < 0)
- r = log_warning_errno(k, "Failed to query secure boot status: %m");
- else
- printf(" Secure Boot: %sd\n", enable_disable(k));
+ printf("Current Boot Loader:\n");
+ printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
- k = is_efi_secure_boot_setup_mode();
- if (k < 0)
- r = log_warning_errno(k, "Failed to query secure boot mode: %m");
- else
- printf(" Setup Mode: %s\n", k ? "setup" : "user");
- printf("\n");
+ for (i = 0; i < ELEMENTSOF(flags); i++) {
+
+ if (i == 0)
+ printf(" Features: ");
+ else
+ printf(" ");
+
+ if (FLAGS_SET(loader_features, flags[i].flag))
+ printf("%s%s%s %s\n", ansi_highlight_green(), special_glyph(SPECIAL_GLYPH_CHECK_MARK), ansi_normal(), flags[i].name);
+ else
+ printf("%s%s%s %s\n", ansi_highlight_red(), special_glyph(SPECIAL_GLYPH_CROSS_MARK), ansi_normal(), flags[i].name);
+ }
- printf("Current Loader:\n");
- printf(" Product: %s\n", strna(loader));
if (stub)
printf(" Stub: %s\n", stub);
if (!sd_id128_is_null(loader_part_uuid))
@@ -973,7 +1032,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
SD_ID128_FORMAT_VAL(loader_part_uuid));
else
printf(" ESP: n/a\n");
- printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path));
+ printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
printf("\n");
} else
printf("System:\n Not booted with EFI\n\n");
@@ -1001,8 +1060,8 @@ static int verb_status(int argc, char *argv[], void *userdata) {
static int verb_list(int argc, char *argv[], void *userdata) {
_cleanup_(boot_config_free) BootConfig config = {};
+ _cleanup_free_ char **found_by_loader = NULL;
sd_id128_t uuid = SD_ID128_NULL;
- unsigned n;
int r;
/* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn
@@ -1017,56 +1076,60 @@ static int verb_list(int argc, char *argv[], void *userdata) {
r = boot_entries_load_config(arg_path, &config);
if (r < 0)
- return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m",
- arg_path);
-
- printf("Available boot entries:\n");
-
- for (n = 0; n < config.n_entries; n++) {
- const BootEntry *e = &config.entries[n];
-
- printf(" title: %s%s%s%s%s%s\n",
- ansi_highlight(),
- boot_entry_title(e),
- ansi_normal(),
- ansi_highlight_green(),
- n == (unsigned) config.default_entry ? " (default)" : "",
- ansi_normal());
- if (e->version)
- printf(" version: %s\n", e->version);
- if (e->machine_id)
- printf(" machine-id: %s\n", e->machine_id);
- if (e->architecture)
- printf(" architecture: %s\n", e->architecture);
- if (e->kernel)
- printf(" linux: %s\n", e->kernel);
- if (!strv_isempty(e->initrd)) {
- _cleanup_free_ char *t;
-
- t = strv_join(e->initrd, " ");
- if (!t)
- return log_oom();
+ return r;
- printf(" initrd: %s\n", t);
- }
- if (!strv_isempty(e->options)) {
- _cleanup_free_ char *t;
+ r = efi_loader_get_entries(&found_by_loader);
+ if (r < 0 && !IN_SET(r, -ENOENT, -EOPNOTSUPP))
+ log_debug_errno(r, "Failed to acquire boot loader discovered entries: %m");
- t = strv_join(e->options, " ");
- if (!t)
- return log_oom();
+ if (config.n_entries == 0)
+ log_info("No boot loader entries found.");
+ else {
+ size_t n;
+
+ (void) pager_open(arg_pager_flags);
+
+ printf("Boot Loader Entries:\n");
+
+ for (n = 0; n < config.n_entries; n++) {
+ r = boot_entry_show(config.entries + n, n == (size_t) config.default_entry);
+ if (r < 0)
+ return r;
- printf(" options: %s\n", t);
+ puts("");
+
+ strv_remove(found_by_loader, config.entries[n].id);
}
- if (e->device_tree)
- printf(" devicetree: %s\n", e->device_tree);
+ }
+
+ if (!strv_isempty(found_by_loader)) {
+ char **i;
- puts("");
+ printf("Automatic/Other Entries Found by Boot Loader:\n\n");
+
+ STRV_FOREACH(i, found_by_loader)
+ puts(*i);
}
return 0;
}
+static int sync_esp(void) {
+ _cleanup_close_ int fd = -1;
+
+ if (!arg_path)
+ return 0;
+
+ fd = open(arg_path, O_CLOEXEC|O_DIRECTORY|O_RDONLY);
+ if (fd < 0)
+ return log_error_errno(errno, "Couldn't open ESP '%s' for synchronization: %m", arg_path);
+
+ if (syncfs(fd) < 0)
+ return log_error_errno(errno, "Failed to synchronize the ESP '%s': %m", arg_path);
+
+ return 1;
+}
+
static int verb_install(int argc, char *argv[], void *userdata) {
sd_id128_t uuid = SD_ID128_NULL;
@@ -1093,6 +1156,8 @@ static int verb_install(int argc, char *argv[], void *userdata) {
}
}
+ (void) sync_esp();
+
if (arg_touch_variables)
r = install_variables(arg_path,
part, pstart, psize, uuid,
@@ -1112,6 +1177,8 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
r = remove_binaries(arg_path);
+ (void) sync_esp();
+
if (arg_touch_variables) {
int q;
@@ -1123,22 +1190,72 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
return r;
}
+static int verb_set_default(int argc, char *argv[], void *userdata) {
+ const char *name;
+ int r;
+
+ if (!is_efi_boot())
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Not booted with UEFI.");
+
+ if (access("/sys/firmware/efi/efivars/LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f", F_OK) < 0) {
+ if (errno == ENOENT) {
+ log_error_errno(errno, "Not booted with a supported boot loader.");
+ return -EOPNOTSUPP;
+ }
+
+ return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]);
+ }
+
+ if (detect_container() > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "'%s' operation not supported in a container.",
+ argv[0]);
+
+ if (!arg_touch_variables)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "'%s' operation cannot be combined with --touch-variables=no.",
+ argv[0]);
+
+ name = streq(argv[0], "set-default") ? "LoaderEntryDefault" : "LoaderEntryOneShot";
+
+ if (isempty(argv[1])) {
+ r = efi_set_variable(EFI_VENDOR_LOADER, name, NULL, 0);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to remove EFI variale: %m");
+ } else {
+ _cleanup_free_ char16_t *encoded = NULL;
+
+ encoded = utf8_to_utf16(argv[1], strlen(argv[1]));
+ if (!encoded)
+ return log_oom();
+
+ r = efi_set_variable(EFI_VENDOR_LOADER, name, encoded, char16_strlen(encoded) * 2 + 2);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update EFI variable: %m");
+ }
+
+ return 0;
+}
+
static int bootctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
- { "help", VERB_ANY, VERB_ANY, 0, help },
- { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
- { "list", VERB_ANY, 1, 0, verb_list },
- { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
- { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
- { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove },
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
+ { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
+ { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
+ { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove },
+ { "list", VERB_ANY, 1, 0, verb_list },
+ { "set-default", 2, 2, VERB_MUST_BE_ROOT, verb_set_default },
+ { "set-oneshot", 2, 2, VERB_MUST_BE_ROOT, verb_set_default },
{}
};
return dispatch_verb(argc, argv, verbs, NULL);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
log_parse_environment();
@@ -1150,11 +1267,9 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
-
- r = bootctl_main(argc, argv);
+ return r;
- finish:
- free(arg_path);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return bootctl_main(argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 2f014446be..4719eeaf4b 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/*
- * Copyright © 2012-2015 Harald Hoyer <harald@redhat.com>
- */
#include <efi.h>
#include <efilib.h>
@@ -27,11 +24,11 @@ static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
enum loader_type {
LOADER_UNDEFINED,
LOADER_EFI,
- LOADER_LINUX
+ LOADER_LINUX,
};
typedef struct {
- CHAR16 *file;
+ CHAR16 *id; /* The identifier for this entry (note that this id is not necessarily unique though!) */
CHAR16 *title_show;
CHAR16 *title;
CHAR16 *version;
@@ -44,6 +41,11 @@ typedef struct {
EFI_STATUS (*call)(VOID);
BOOLEAN no_autoselect;
BOOLEAN non_unique;
+ UINTN tries_done;
+ UINTN tries_left;
+ CHAR16 *path;
+ CHAR16 *current_name;
+ CHAR16 *next_name;
} ConfigEntry;
typedef struct {
@@ -60,6 +62,7 @@ typedef struct {
BOOLEAN editor;
BOOLEAN auto_entries;
BOOLEAN auto_firmware;
+ BOOLEAN force_menu;
UINTN console_mode;
enum console_mode_change_type console_mode_change;
} Config;
@@ -71,14 +74,24 @@ static VOID cursor_left(UINTN *cursor, UINTN *first) {
(*first)--;
}
-static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len) {
+static VOID cursor_right(
+ UINTN *cursor,
+ UINTN *first,
+ UINTN x_max,
+ UINTN len) {
+
if ((*cursor)+1 < x_max)
(*cursor)++;
else if ((*first) + (*cursor) < len)
(*first)++;
}
-static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
+static BOOLEAN line_edit(
+ CHAR16 *line_in,
+ CHAR16 **line_out,
+ UINTN x_max,
+ UINTN y_pos) {
+
_cleanup_freepool_ CHAR16 *line = NULL, *print = NULL;
UINTN size, len, first, cursor, clear;
BOOLEAN exit, enter;
@@ -373,10 +386,10 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
Print(L"\n--- press key ---\n\n");
console_key_read(&key, TRUE);
- Print(L"timeout: %d\n", config->timeout_sec);
+ Print(L"timeout: %u\n", config->timeout_sec);
if (config->timeout_sec_efivar >= 0)
Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar);
- Print(L"timeout (config): %d\n", config->timeout_sec_config);
+ Print(L"timeout (config): %u\n", config->timeout_sec_config);
if (config->entry_default_pattern)
Print(L"default pattern: '%s'\n", config->entry_default_pattern);
Print(L"editor: %s\n", yes_no(config->editor));
@@ -391,7 +404,8 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
Print(L"\n");
if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS)
- Print(L"LoaderConfigTimeout: %d\n", i);
+ Print(L"LoaderConfigTimeout: %u\n", i);
+
if (config->entry_oneshot)
Print(L"LoaderEntryOneShot: %s\n", config->entry_oneshot);
if (efivar_get(L"LoaderDevicePartUUID", &partstr) == EFI_SUCCESS)
@@ -410,8 +424,8 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
entry = config->entries[i];
Print(L"config entry: %d/%d\n", i+1, config->entry_count);
- if (entry->file)
- Print(L"file '%s'\n", entry->file);
+ if (entry->id)
+ Print(L"id '%s'\n", entry->id);
Print(L"title show '%s'\n", entry->title_show);
if (entry->title)
Print(L"title '%s'\n", entry->title);
@@ -438,6 +452,17 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
if (entry->call)
Print(L"internal call yes\n");
+ if (entry->tries_left != (UINTN) -1)
+ Print(L"counting boots yes\n"
+ "tries done %u\n"
+ "tries left %u\n"
+ "current path %s\\%s\n"
+ "next path %s\\%s\n",
+ entry->tries_done,
+ entry->tries_left,
+ entry->path, entry->current_name,
+ entry->path, entry->next_name);
+
Print(L"\n--- press key ---\n\n");
console_key_read(&key, TRUE);
}
@@ -445,7 +470,11 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
}
-static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *loaded_image_path) {
+static BOOLEAN menu_run(
+ Config *config,
+ ConfigEntry **chosen_entry,
+ CHAR16 *loaded_image_path) {
+
EFI_STATUS err;
UINTN visible_max;
UINTN idx_highlight;
@@ -716,7 +745,7 @@ static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *load
case KEYPRESS(0, 0, 'd'):
if (config->idx_default_efivar != (INTN)idx_highlight) {
/* store the selected entry in a persistent EFI variable */
- efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
+ efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->id, TRUE);
config->idx_default_efivar = idx_highlight;
status = StrDuplicate(L"Default boot entry selected.");
} else {
@@ -840,11 +869,20 @@ static VOID config_add_entry(Config *config, ConfigEntry *entry) {
}
static VOID config_entry_free(ConfigEntry *entry) {
+ if (!entry)
+ return;
+
+ FreePool(entry->id);
FreePool(entry->title_show);
FreePool(entry->title);
+ FreePool(entry->version);
FreePool(entry->machine_id);
FreePool(entry->loader);
FreePool(entry->options);
+ FreePool(entry->path);
+ FreePool(entry->current_name);
+ FreePool(entry->next_name);
+ FreePool(entry);
}
static BOOLEAN is_digit(CHAR16 c) {
@@ -873,7 +911,7 @@ static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2) {
INTN order;
order = c_order(*s1) - c_order(*s2);
- if (order)
+ if (order != 0)
return order;
s1++;
s2++;
@@ -897,14 +935,20 @@ static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2) {
if (is_digit(*s2))
return -1;
- if (first)
+ if (first != 0)
return first;
}
return StrCmp(os1, os2);
}
-static CHAR8 *line_get_key_value(CHAR8 *content, CHAR8 *sep, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
+static CHAR8 *line_get_key_value(
+ CHAR8 *content,
+ CHAR8 *sep,
+ UINTN *pos,
+ CHAR8 **key_ret,
+ CHAR8 **value_ret) {
+
CHAR8 *line;
UINTN linelen;
CHAR8 *value;
@@ -937,7 +981,7 @@ skip:
}
/* remove trailing whitespace */
- while (linelen > 0 && strchra(sep, line[linelen-1]))
+ while (linelen > 0 && strchra((CHAR8 *)" \t", line[linelen-1]))
linelen--;
line[linelen] = '\0';
@@ -971,7 +1015,6 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
UINTN pos = 0;
CHAR8 *key, *value;
- line = content;
while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
if (strcmpa((CHAR8 *)"timeout", key) == 0) {
_cleanup_freepool_ CHAR16 *s = NULL;
@@ -1033,7 +1076,199 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
}
}
-static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
+static VOID config_entry_parse_tries(
+ ConfigEntry *entry,
+ CHAR16 *path,
+ CHAR16 *file,
+ CHAR16 *suffix) {
+
+ UINTN left = (UINTN) -1, done = (UINTN) -1, factor = 1, i, next_left, next_done;
+ _cleanup_freepool_ CHAR16 *prefix = NULL;
+
+ /*
+ * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
+ * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
+ * which case that counter as assumed to be zero, i.e. the missing part is synonymous to "-0").
+ *
+ * Names we grok, and the series they result in:
+ *
+ * foobar+3.efi → foobar+2-1.efi → foobar+1-2.efi → foobar+0-3.efi → STOP!
+ * foobar+4-0.efi → foobar+3-1.efi → foobar+2-2.efi → foobar+1-3.efi → foobar+0-4.efi → STOP!
+ */
+
+ i = StrLen(file);
+
+ /* Chop off any suffix such as ".conf" or ".efi" */
+ if (suffix) {
+ UINTN suffix_length;
+
+ suffix_length = StrLen(suffix);
+ if (i < suffix_length)
+ return;
+
+ i -= suffix_length;
+ }
+
+ /* Go backwards through the string and parse everything we encounter */
+ for (;;) {
+ if (i == 0)
+ return;
+
+ i--;
+
+ switch (file[i]) {
+
+ case '+':
+ if (left == (UINTN) -1) /* didn't read at least one digit for 'left'? */
+ return;
+
+ if (done == (UINTN) -1) /* no 'done' counter? If so, it's equivalent to 0 */
+ done = 0;
+
+ goto good;
+
+ case '-':
+ if (left == (UINTN) -1) /* didn't parse any digit yet? */
+ return;
+
+ if (done != (UINTN) -1) /* already encountered a dash earlier? */
+ return;
+
+ /* So we encountered a dash. This means this counter is of the form +LEFT-DONE. Let's assign
+ * what we already parsed to 'done', and start fresh for the 'left' part. */
+
+ done = left;
+ left = (UINTN) -1;
+ factor = 1;
+ break;
+
+ case '0'...'9': {
+ UINTN new_factor;
+
+ if (left == (UINTN) -1)
+ left = file[i] - '0';
+ else {
+ UINTN new_left, digit;
+
+ digit = file[i] - '0';
+ if (digit > (UINTN) -1 / factor) /* overflow check */
+ return;
+
+ new_left = left + digit * factor;
+ if (new_left < left) /* overflow check */
+ return;
+
+ if (new_left == (UINTN) -1) /* don't allow us to be confused */
+ return;
+ }
+
+ new_factor = factor * 10;
+ if (new_factor < factor) /* overflow chck */
+ return;
+
+ factor = new_factor;
+ break;
+ }
+
+ default:
+ return;
+ }
+ }
+
+good:
+ entry->tries_left = left;
+ entry->tries_done = done;
+
+ entry->path = StrDuplicate(path);
+ entry->current_name = StrDuplicate(file);
+
+ next_left = left <= 0 ? 0 : left - 1;
+ next_done = done >= (UINTN) -2 ? (UINTN) -2 : done + 1;
+
+ prefix = StrDuplicate(file);
+ prefix[i] = 0;
+
+ entry->next_name = PoolPrint(L"%s+%u-%u%s", prefix, next_left, next_done, suffix ?: L"");
+}
+
+static VOID config_entry_bump_counters(
+ ConfigEntry *entry,
+ EFI_FILE_HANDLE root_dir) {
+
+ _cleanup_freepool_ CHAR16* old_path = NULL, *new_path = NULL;
+ _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
+ static EFI_GUID EfiFileInfoGuid = EFI_FILE_INFO_ID;
+ _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL;
+ UINTN file_info_size, a, b;
+ EFI_STATUS r;
+
+ if (entry->tries_left == (UINTN) -1)
+ return;
+
+ if (!entry->path || !entry->current_name || !entry->next_name)
+ return;
+
+ old_path = PoolPrint(L"%s\\%s", entry->path, entry->current_name);
+
+ r = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, old_path, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL);
+ if (EFI_ERROR(r))
+ return;
+
+ a = StrLen(entry->current_name);
+ b = StrLen(entry->next_name);
+
+ file_info_size = OFFSETOF(EFI_FILE_INFO, FileName) + (a > b ? a : b) + 1;
+
+ for (;;) {
+ file_info = AllocatePool(file_info_size);
+
+ r = uefi_call_wrapper(handle->GetInfo, 4, handle, &EfiFileInfoGuid, &file_info_size, file_info);
+ if (!EFI_ERROR(r))
+ break;
+
+ if (r != EFI_BUFFER_TOO_SMALL || file_info_size * 2 < file_info_size) {
+ Print(L"\nFailed to get file info for '%s': %r\n", old_path, r);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return;
+ }
+
+ file_info_size *= 2;
+ FreePool(file_info);
+ }
+
+ /* And rename the file */
+ StrCpy(file_info->FileName, entry->next_name);
+ r = uefi_call_wrapper(handle->SetInfo, 4, handle, &EfiFileInfoGuid, file_info_size, file_info);
+ if (EFI_ERROR(r)) {
+ Print(L"\nFailed to rename '%s' to '%s', ignoring: %r\n", old_path, entry->next_name, r);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return;
+ }
+
+ /* Flush everything to disk, just in case… */
+ (void) uefi_call_wrapper(handle->Flush, 1, handle);
+
+ /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
+ * success */
+ new_path = PoolPrint(L"%s\\%s", entry->path, entry->next_name);
+ efivar_set(L"LoaderBootCountPath", new_path, FALSE);
+
+ /* If the file we just renamed is the loader path, then let's update that. */
+ if (StrCmp(entry->loader, old_path) == 0) {
+ FreePool(entry->loader);
+ entry->loader = new_path;
+ new_path = NULL;
+ }
+}
+
+static VOID config_entry_add_from_file(
+ Config *config,
+ EFI_HANDLE *device,
+ CHAR16 *path,
+ CHAR16 *file,
+ CHAR8 *content,
+ CHAR16 *loaded_image_path) {
+
ConfigEntry *entry;
CHAR8 *line;
UINTN pos = 0;
@@ -1041,9 +1276,13 @@ static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR1
UINTN len;
_cleanup_freepool_ CHAR16 *initrd = NULL;
- entry = AllocateZeroPool(sizeof(ConfigEntry));
+ entry = AllocatePool(sizeof(ConfigEntry));
+
+ *entry = (ConfigEntry) {
+ .tries_done = (UINTN) -1,
+ .tries_left = (UINTN) -1,
+ };
- line = content;
while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
if (strcmpa((CHAR8 *)"title", key) == 0) {
FreePool(entry->title);
@@ -1130,7 +1369,6 @@ static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR1
if (entry->type == LOADER_UNDEFINED) {
config_entry_free(entry);
- FreePool(entry);
return;
}
@@ -1149,14 +1387,16 @@ static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR1
}
entry->device = device;
- entry->file = StrDuplicate(file);
- len = StrLen(entry->file);
+ entry->id = StrDuplicate(file);
+ len = StrLen(entry->id);
/* remove ".conf" */
if (len > 5)
- entry->file[len - 5] = '\0';
- StrLwr(entry->file);
+ entry->id[len - 5] = '\0';
+ StrLwr(entry->id);
config_add_entry(config, entry);
+
+ config_entry_parse_tries(entry, path, file, L".conf");
}
static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
@@ -1164,9 +1404,11 @@ static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
UINTN sec;
EFI_STATUS err;
- config->editor = TRUE;
- config->auto_entries = TRUE;
- config->auto_firmware = TRUE;
+ *config = (Config) {
+ .editor = TRUE,
+ .auto_entries = TRUE,
+ .auto_firmware = TRUE,
+ };
err = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content, NULL);
if (!EFI_ERROR(err))
@@ -1174,13 +1416,27 @@ static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
err = efivar_get_int(L"LoaderConfigTimeout", &sec);
if (!EFI_ERROR(err)) {
- config->timeout_sec_efivar = sec;
+ config->timeout_sec_efivar = sec > INTN_MAX ? INTN_MAX : sec;
config->timeout_sec = sec;
} else
config->timeout_sec_efivar = -1;
+
+ err = efivar_get_int(L"LoaderConfigTimeoutOneShot", &sec);
+ if (!EFI_ERROR(err)) {
+ /* Unset variable now, after all it's "one shot". */
+ (void) efivar_set(L"LoaderConfigTimeoutOneShot", NULL, TRUE);
+
+ config->timeout_sec = sec;
+ config->force_menu = TRUE; /* force the menu when this is set */
+ }
}
-static VOID config_load_entries(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
+static VOID config_load_entries(
+ Config *config,
+ EFI_HANDLE *device,
+ EFI_FILE *root_dir,
+ CHAR16 *loaded_image_path) {
+
EFI_FILE_HANDLE entries_dir;
EFI_STATUS err;
@@ -1214,12 +1470,44 @@ static VOID config_load_entries(Config *config, EFI_HANDLE *device, EFI_FILE *ro
err = file_read(entries_dir, f->FileName, 0, 0, &content, NULL);
if (!EFI_ERROR(err))
- config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path);
+ config_entry_add_from_file(config, device, L"\\loader\\entries", f->FileName, content, loaded_image_path);
}
uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
}
}
+static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
+ INTN r;
+
+ /* Order entries that have no tries left to the end of the list */
+ if (a->tries_left != 0 && b->tries_left == 0)
+ return -1;
+ if (a->tries_left == 0 && b->tries_left != 0)
+ return 1;
+
+ r = str_verscmp(a->id, b->id);
+ if (r != 0)
+ return r;
+
+ if (a->tries_left == (UINTN) -1 ||
+ b->tries_left == (UINTN) -1)
+ return 0;
+
+ /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
+ if (a->tries_left > b->tries_left)
+ return -1;
+ if (a->tries_left < b->tries_left)
+ return 1;
+
+ /* If they have the same number of tries left, then let the one win which was tried fewer times so far */
+ if (a->tries_done < b->tries_done)
+ return -1;
+ if (a->tries_done > b->tries_done)
+ return 1;
+
+ return 0;
+}
+
static VOID config_sort_entries(Config *config) {
UINTN i;
@@ -1231,8 +1519,9 @@ static VOID config_sort_entries(Config *config) {
for (k = 0; k < config->entry_count - i; k++) {
ConfigEntry *entry;
- if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0)
+ if (config_entry_compare(config->entries[k], config->entries[k+1]) <= 0)
continue;
+
entry = config->entries[k];
config->entries[k] = config->entries[k+1];
config->entries[k+1] = entry;
@@ -1243,10 +1532,20 @@ static VOID config_sort_entries(Config *config) {
}
}
+static INTN config_entry_find(Config *config, CHAR16 *id) {
+ UINTN i;
+
+ for (i = 0; i < config->entry_count; i++)
+ if (StrCmp(config->entries[i]->id, id) == 0)
+ return (INTN) i;
+
+ return -1;
+}
+
static VOID config_default_entry_select(Config *config) {
_cleanup_freepool_ CHAR16 *entry_oneshot = NULL, *entry_default = NULL;
EFI_STATUS err;
- UINTN i;
+ INTN i;
/*
* The EFI variable to specify a boot entry for the next, and only the
@@ -1254,19 +1553,15 @@ static VOID config_default_entry_select(Config *config) {
*/
err = efivar_get(L"LoaderEntryOneShot", &entry_oneshot);
if (!EFI_ERROR(err)) {
- BOOLEAN found = FALSE;
-
- for (i = 0; i < config->entry_count; i++)
- if (StrCmp(config->entries[i]->file, entry_oneshot) == 0) {
- config->idx_default = i;
- found = TRUE;
- break;
- }
config->entry_oneshot = StrDuplicate(entry_oneshot);
efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
- if (found)
+
+ i = config_entry_find(config, entry_oneshot);
+ if (i >= 0) {
+ config->idx_default = i;
return;
+ }
}
/*
@@ -1277,12 +1572,13 @@ static VOID config_default_entry_select(Config *config) {
*/
err = efivar_get(L"LoaderEntryDefault", &entry_default);
if (!EFI_ERROR(err)) {
- for (i = 0; i < config->entry_count; i++)
- if (StrCmp(config->entries[i]->file, entry_default) == 0) {
- config->idx_default = i;
- config->idx_default_efivar = i;
- return;
- }
+
+ i = config_entry_find(config, entry_default);
+ if (i >= 0) {
+ config->idx_default = i;
+ config->idx_default_efivar = i;
+ return;
+ }
}
config->idx_default_efivar = -1;
@@ -1298,7 +1594,7 @@ static VOID config_default_entry_select(Config *config) {
while (i--) {
if (config->entries[i]->no_autoselect)
continue;
- if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
+ if (MetaiMatch(config->entries[i]->id, config->entry_default_pattern)) {
config->idx_default = i;
return;
}
@@ -1349,7 +1645,7 @@ static VOID config_title_generate(Config *config) {
FreePool(config->entries[i]->title_show);
title = config->entries[i]->title;
if (!title)
- title = config->entries[i]->file;
+ title = config->entries[i]->id;
config->entries[i]->title_show = StrDuplicate(title);
}
@@ -1399,43 +1695,74 @@ static VOID config_title_generate(Config *config) {
if (!config->entries[i]->non_unique)
continue;
- s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file);
+ s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->id);
FreePool(config->entries[i]->title_show);
config->entries[i]->title_show = s;
config->entries[i]->non_unique = FALSE;
}
}
-static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(VOID)) {
+static BOOLEAN config_entry_add_call(
+ Config *config,
+ CHAR16 *id,
+ CHAR16 *title,
+ EFI_STATUS (*call)(VOID)) {
+
ConfigEntry *entry;
- entry = AllocateZeroPool(sizeof(ConfigEntry));
- entry->title = StrDuplicate(title);
- entry->call = call;
- entry->no_autoselect = TRUE;
+ entry = AllocatePool(sizeof(ConfigEntry));
+ *entry = (ConfigEntry) {
+ .id = StrDuplicate(id),
+ .title = StrDuplicate(title),
+ .call = call,
+ .no_autoselect = TRUE,
+ .tries_done = (UINTN) -1,
+ .tries_left = (UINTN) -1,
+ };
+
config_add_entry(config, entry);
return TRUE;
}
-static ConfigEntry *config_entry_add_loader(Config *config, EFI_HANDLE *device,
- enum loader_type type,CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
+static ConfigEntry *config_entry_add_loader(
+ Config *config,
+ EFI_HANDLE *device,
+ enum loader_type type,
+ CHAR16 *id,
+ CHAR16 key,
+ CHAR16 *title,
+ CHAR16 *loader) {
+
ConfigEntry *entry;
- entry = AllocateZeroPool(sizeof(ConfigEntry));
- entry->type = type;
- entry->title = StrDuplicate(title);
- entry->device = device;
- entry->loader = StrDuplicate(loader);
- entry->file = StrDuplicate(file);
- StrLwr(entry->file);
- entry->key = key;
- config_add_entry(config, entry);
+ entry = AllocatePool(sizeof(ConfigEntry));
+ *entry = (ConfigEntry) {
+ .type = type,
+ .title = StrDuplicate(title),
+ .device = device,
+ .loader = StrDuplicate(loader),
+ .id = StrDuplicate(id),
+ .key = key,
+ .tries_done = (UINTN) -1,
+ .tries_left = (UINTN) -1,
+ };
+ StrLwr(entry->id);
+
+ config_add_entry(config, entry);
return entry;
}
-static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
- CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
+static BOOLEAN config_entry_add_loader_auto(
+ Config *config,
+ EFI_HANDLE *device,
+ EFI_FILE *root_dir,
+ CHAR16 *loaded_image_path,
+ CHAR16 *id,
+ CHAR16 key,
+ CHAR16 *title,
+ CHAR16 *loader) {
+
EFI_FILE_HANDLE handle;
ConfigEntry *entry;
EFI_STATUS err;
@@ -1469,7 +1796,7 @@ static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device,
return FALSE;
uefi_call_wrapper(handle->Close, 1, handle);
- entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, file, key, title, loader);
+ entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, id, key, title, loader);
if (!entry)
return FALSE;
@@ -1507,7 +1834,11 @@ static VOID config_entry_add_osx(Config *config) {
}
}
-static VOID config_entry_add_linux(Config *config, EFI_LOADED_IMAGE *loaded_image, EFI_FILE *root_dir) {
+static VOID config_entry_add_linux(
+ Config *config,
+ EFI_LOADED_IMAGE *loaded_image,
+ EFI_FILE *root_dir) {
+
EFI_FILE_HANDLE linux_dir;
EFI_STATUS err;
ConfigEntry *entry;
@@ -1563,7 +1894,6 @@ static VOID config_entry_add_linux(Config *config, EFI_LOADED_IMAGE *loaded_imag
continue;
/* read properties from the embedded os-release file */
- line = content;
while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
FreePool(os_name);
@@ -1591,26 +1921,28 @@ static VOID config_entry_add_linux(Config *config, EFI_LOADED_IMAGE *loaded_imag
}
if (os_name && os_id && (os_version || os_build)) {
- CHAR16 *conf;
- CHAR16 *path;
- CHAR16 *cmdline;
+ _cleanup_freepool_ CHAR16 *conf = NULL, *path = NULL;
conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build);
path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
+
entry = config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path);
FreePool(content);
content = NULL;
+
/* read the embedded cmdline file */
- err = file_read(linux_dir, f->FileName, offs[1], szs[1] - 1, &content, NULL);
+ err = file_read(linux_dir, f->FileName, offs[1], szs[1], &content, NULL);
if (!EFI_ERROR(err)) {
- cmdline = stra_to_str(content);
- entry->options = cmdline;
- cmdline = NULL;
+
+ /* chomp the newline */
+ if (content[szs[1]-1] == '\n')
+ content[szs[1]-1] = '\0';
+
+ entry->options = stra_to_str(content);
}
- FreePool(cmdline);
- FreePool(conf);
- FreePool(path);
+
+ config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
}
FreePool(os_name);
@@ -1623,7 +1955,11 @@ static VOID config_entry_add_linux(Config *config, EFI_LOADED_IMAGE *loaded_imag
uefi_call_wrapper(linux_dir->Close, 1, linux_dir);
}
-static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
+static EFI_STATUS image_start(
+ EFI_HANDLE parent_image,
+ const Config *config,
+ const ConfigEntry *entry) {
+
EFI_HANDLE image;
_cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
CHAR16 *options;
@@ -1693,7 +2029,7 @@ static EFI_STATUS reboot_into_firmware(VOID) {
if (!EFI_ERROR(err))
osind |= (UINT64)*b;
- err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
+ err = efivar_set_raw(&global_guid, L"OsIndications", &osind, sizeof(UINT64), TRUE);
if (EFI_ERROR(err))
return err;
@@ -1714,7 +2050,38 @@ static VOID config_free(Config *config) {
FreePool(config->entry_oneshot);
}
+static VOID config_write_entries_to_variable(Config *config) {
+ _cleanup_freepool_ CHAR16 *buffer = NULL;
+ UINTN i, sz = 0;
+ CHAR16 *p;
+
+ for (i = 0; i < config->entry_count; i++)
+ sz += StrLen(config->entries[i]->id) + 1;
+
+ p = buffer = AllocatePool(sz * sizeof(CHAR16));
+
+ for (i = 0; i < config->entry_count; i++) {
+ UINTN l;
+
+ l = StrLen(config->entries[i]->id) + 1;
+ CopyMem(p, config->entries[i]->id, l * sizeof(CHAR16));
+
+ p += l;
+ }
+
+ /* Store the full list of discovered entries. */
+ (void) efivar_set_raw(&loader_guid, L"LoaderEntries", buffer, (UINT8*) p - (UINT8*) buffer, FALSE);
+}
+
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+ static const UINT64 loader_features =
+ (1ULL << 0) | /* I honour the LoaderConfigTimeout variable */
+ (1ULL << 1) | /* I honour the LoaderConfigTimeoutOneShot variable */
+ (1ULL << 2) | /* I honour the LoaderEntryDefault variable */
+ (1ULL << 3) | /* I honour the LoaderEntryOneShot variable */
+ (1ULL << 4) | /* I support boot counting */
+ 0;
+
_cleanup_freepool_ CHAR16 *infostr = NULL, *typestr = NULL;
CHAR8 *b;
UINTN size;
@@ -1738,6 +2105,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
typestr = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
efivar_set(L"LoaderFirmwareType", typestr, FALSE);
+ (void) efivar_set_raw(&loader_guid, L"LoaderFeatures", &loader_features, sizeof(loader_features), FALSE);
+
err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err)) {
@@ -1770,7 +2139,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
loaded_image_path = DevicePathToStr(loaded_image->FilePath);
efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
- ZeroMem(&config, sizeof(Config));
config_load_defaults(&config, root_dir);
/* scan /EFI/Linux/ directory */
@@ -1795,7 +2163,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
UINT64 osind = (UINT64)*b;
if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
- config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware);
+ config_entry_add_call(&config, L"auto-reboot-to-firmware-setup", L"Reboot Into Firmware Interface", reboot_into_firmware);
FreePool(b);
}
@@ -1805,6 +2173,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
goto out;
}
+ config_write_entries_to_variable(&config);
+
config_title_generate(&config);
/* select entry by configured pattern or EFI LoaderDefaultEntry= variable */
@@ -1818,7 +2188,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
}
/* select entry or show menu when key is pressed or timeout is set */
- if (config.timeout_sec == 0) {
+ if (config.force_menu || config.timeout_sec > 0)
+ menu = TRUE;
+ else {
UINT64 key;
err = console_key_read(&key, FALSE);
@@ -1832,8 +2204,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
else
menu = TRUE;
}
- } else
- menu = TRUE;
+ }
for (;;) {
ConfigEntry *entry;
@@ -1844,16 +2215,18 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL);
if (!menu_run(&config, &entry, loaded_image_path))
break;
+ }
- /* run special entry like "reboot" */
- if (entry->call) {
- entry->call();
- continue;
- }
+ /* run special entry like "reboot" */
+ if (entry->call) {
+ entry->call();
+ continue;
}
+ config_entry_bump_counters(entry, root_dir);
+
/* export the selected boot entry to the system */
- efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
+ efivar_set(L"LoaderEntrySelected", entry->id, FALSE);
uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
err = image_start(image, &config, entry);
diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c
index a365d08951..4d72bada62 100644
--- a/src/boot/efi/console.c
+++ b/src/boot/efi/console.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/*
- * Copyright © 2012 Harald Hoyer <harald@redhat.com>
- */
#include <efi.h>
#include <efilib.h>
diff --git a/src/boot/efi/console.h b/src/boot/efi/console.h
index bc2ee4959e..b9ed6c70b3 100644
--- a/src/boot/efi/console.h
+++ b/src/boot/efi/console.h
@@ -1,10 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/*
- * Copyright © 2012 Harald Hoyer <harald@redhat.com>
- */
-
-#ifndef __SDBOOT_CONSOLE_H
-#define __SDBOOT_CONSOLE_H
+#pragma once
#define EFI_SHIFT_STATE_VALID 0x80000000
#define EFI_RIGHT_CONTROL_PRESSED 0x00000004
@@ -14,7 +9,7 @@
#define EFI_CONTROL_PRESSED (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED)
#define EFI_ALT_PRESSED (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED)
-#define KEYPRESS(keys, scan, uni) ((((UINT64)keys) << 32) | ((scan) << 16) | (uni))
+#define KEYPRESS(keys, scan, uni) ((((UINT64)keys) << 32) | (((UINT64)scan) << 16) | (uni))
#define KEYCHAR(k) ((k) & 0xffff)
#define CHAR_CTRL(c) ((c) - 'a' + 1)
@@ -27,4 +22,3 @@ enum console_mode_change_type {
EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait);
EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how);
-#endif
diff --git a/src/boot/efi/disk.h b/src/boot/efi/disk.h
index dfd9200ae1..2a0b8ff2a7 100644
--- a/src/boot/efi/disk.h
+++ b/src/boot/efi/disk.h
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#ifndef __SDBOOT_DISK_H
-#define __SDBOOT_DISK_H
+#pragma once
EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[37]);
-#endif
diff --git a/src/boot/efi/graphics.c b/src/boot/efi/graphics.c
index 6dbe9e2078..9b5003a85d 100644
--- a/src/boot/efi/graphics.c
+++ b/src/boot/efi/graphics.c
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
- * Copyright © 2012 Harald Hoyer <harald@redhat.com>
* Copyright © 2013 Intel Corporation
* Authored by Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
*/
diff --git a/src/boot/efi/graphics.h b/src/boot/efi/graphics.h
index f42f934dd7..809e878d16 100644
--- a/src/boot/efi/graphics.h
+++ b/src/boot/efi/graphics.h
@@ -1,12 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
- * Copyright © 2012 Harald Hoyer <harald@redhat.com>
* Copyright © 2013 Intel Corporation
* Authored by Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
*/
-
-#ifndef __SDBOOT_GRAPHICS_H
-#define __SDBOOT_GRAPHICS_H
+#pragma once
EFI_STATUS graphics_mode(BOOLEAN on);
-#endif
diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h
index 54140a2207..2458a2fbd0 100644
--- a/src/boot/efi/linux.h
+++ b/src/boot/efi/linux.h
@@ -1,10 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#ifndef __SDBOOT_kernel_H
-#define __SDBOOT_kernel_H
+#pragma once
EFI_STATUS linux_exec(EFI_HANDLE *image,
CHAR8 *cmdline, UINTN cmdline_size,
UINTN linux_addr,
UINTN initrd_addr, UINTN initrd_size, BOOLEAN secure);
-#endif
diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c
index b6e3c44283..243c80a835 100644
--- a/src/boot/efi/measure.c
+++ b/src/boot/efi/measure.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/*
- */
#if ENABLE_TPM
@@ -134,13 +132,13 @@ typedef struct {
UINT16 HeaderVersion;
UINT32 PCRIndex;
UINT32 EventType;
-} __attribute__ ((packed)) EFI_TCG2_EVENT_HEADER;
+} __attribute__((packed)) EFI_TCG2_EVENT_HEADER;
typedef struct tdEFI_TCG2_EVENT {
UINT32 Size;
EFI_TCG2_EVENT_HEADER Header;
UINT8 Event[1];
-} __attribute__ ((packed)) EFI_TCG2_EVENT;
+} __attribute__((packed)) EFI_TCG2_EVENT;
typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_CAPABILITY) (IN EFI_TCG2_PROTOCOL * This,
IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY * ProtocolCapability);
@@ -348,7 +346,6 @@ EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UIN
else
log_fmt = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;
- uefi_call_wrapper(BS->Stall, 1, 2000 * 1000);
return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description, log_fmt);
}
diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h
index 99cf3b3fbd..ebb6406eca 100644
--- a/src/boot/efi/measure.h
+++ b/src/boot/efi/measure.h
@@ -1,9 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/*
- */
-#ifndef __SDBOOT_MEASURE_H
-#define __SDBOOT_MEASURE_H
+#pragma once
EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description);
-
-#endif
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index 8ec1fa7be4..aa897c62a1 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -34,7 +34,13 @@ stub_sources = '''
if conf.get('ENABLE_EFI') == 1 and get_option('gnu-efi') != 'false'
efi_cc = get_option('efi-cc')
+ if efi_cc.length() == 0
+ efi_cc = cc.cmd_array()
+ endif
efi_ld = get_option('efi-ld')
+ if efi_ld == ''
+ efi_ld = find_program('ld', required: true)
+ endif
efi_incdir = get_option('efi-includedir')
gnu_efi_path_arch = ''
@@ -51,10 +57,13 @@ if conf.get('ENABLE_EFI') == 1 and get_option('gnu-efi') != 'false'
efi_libdir = get_option('efi-libdir')
if efi_libdir == ''
- cmd = 'cd /usr/lib/$(@0@ -print-multi-os-directory) && pwd'.format(efi_cc)
- ret = run_command('sh', '-c', cmd)
+ ret = run_command(efi_cc + ['-print-multi-os-directory'])
if ret.returncode() == 0
- efi_libdir = ret.stdout().strip()
+ path = join_paths('/usr/lib', ret.stdout().strip())
+ ret = run_command('realpath', '-e', path)
+ if ret.returncode() == 0
+ efi_libdir = ret.stdout().strip()
+ endif
endif
endif
@@ -94,10 +103,6 @@ if have_gnu_efi
endif
endif
- message('efi-libdir: "@0@"'.format(efi_libdir))
- message('efi-ldsdir: "@0@"'.format(efi_ldsdir))
- message('efi-includedir: "@0@"'.format(efi_incdir))
-
compile_args = ['-Wall',
'-Wextra',
'-std=gnu90',
@@ -147,18 +152,18 @@ if have_gnu_efi
o_file = custom_target(file + '.o',
input : file,
output : file + '.o',
- command : [efi_cc, '-c', '@INPUT@', '-o', '@OUTPUT@']
+ command : efi_cc + ['-c', '@INPUT@', '-o', '@OUTPUT@']
+ compile_args,
depend_files : efi_headers)
if (common_sources + systemd_boot_sources).contains(file)
- systemd_boot_objects += [o_file]
+ systemd_boot_objects += o_file
endif
if (common_sources + stub_sources).contains(file)
- stub_objects += [o_file]
+ stub_objects += o_file
endif
endforeach
- libgcc_file_name = run_command(efi_cc, '-print-libgcc-file-name').stdout().strip()
+ libgcc_file_name = run_command(efi_cc + ['-print-libgcc-file-name']).stdout().strip()
systemd_boot_efi_name = 'systemd-boot@0@.efi'.format(EFI_MACHINE_TYPE_NAME)
stub_efi_name = 'linux@0@.efi.stub'.format(EFI_MACHINE_TYPE_NAME)
no_undefined_symbols = find_program('no-undefined-symbols.sh')
@@ -173,9 +178,11 @@ if have_gnu_efi
efi_ldflags + tuple[2] +
['-lefi', '-lgnuefi', libgcc_file_name])
- test('no-undefined-symbols-' + tuple[0],
- no_undefined_symbols,
- args : [so])
+ if want_tests != 'false'
+ test('no-undefined-symbols-' + tuple[0],
+ no_undefined_symbols,
+ args : [so])
+ endif
stub = custom_target(
tuple[1],
diff --git a/src/boot/efi/pe.h b/src/boot/efi/pe.h
index d3fc8ecf6b..bfbb8d9baf 100644
--- a/src/boot/efi/pe.h
+++ b/src/boot/efi/pe.h
@@ -1,10 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#ifndef __SDBOOT_PEFILE_H
-#define __SDBOOT_PEFILE_H
+#pragma once
EFI_STATUS pe_memory_locate_sections(CHAR8 *base,
CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path,
CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
-#endif
diff --git a/src/boot/efi/shim.c b/src/boot/efi/shim.c
index 73ff861cb6..f6ffed143c 100644
--- a/src/boot/efi/shim.c
+++ b/src/boot/efi/shim.c
@@ -205,35 +205,3 @@ EFI_STATUS security_policy_install(void) {
return EFI_SUCCESS;
}
-
-EFI_STATUS security_policy_uninstall(void) {
- EFI_STATUS status;
-
- if (esfas) {
- EFI_SECURITY_PROTOCOL *security_protocol;
-
- status = uefi_call_wrapper(BS->LocateProtocol, 3, (EFI_GUID*) &security_protocol_guid, NULL, (VOID**) &security_protocol);
-
- if (status != EFI_SUCCESS)
- return status;
-
- security_protocol->FileAuthenticationState = esfas;
- esfas = NULL;
- } else
- /* nothing installed */
- return EFI_NOT_STARTED;
-
- if (es2fa) {
- EFI_SECURITY2_PROTOCOL *security2_protocol;
-
- status = uefi_call_wrapper(BS->LocateProtocol, 3, (EFI_GUID*) &security2_protocol_guid, NULL, (VOID**) &security2_protocol);
-
- if (status != EFI_SUCCESS)
- return status;
-
- security2_protocol->FileAuthentication = es2fa;
- es2fa = NULL;
- }
-
- return EFI_SUCCESS;
-}
diff --git a/src/boot/efi/shim.h b/src/boot/efi/shim.h
index c847ae97a4..209c9d4cf0 100644
--- a/src/boot/efi/shim.h
+++ b/src/boot/efi/shim.h
@@ -7,16 +7,10 @@
* Copyright © 2012 <James.Bottomley@HansenPartnership.com>
* https://github.com/mjg59/efitools
*/
-
-#ifndef __SDBOOT_SHIM_H
-#define __SDBOOT_SHIM_H
+#pragma once
BOOLEAN shim_loaded(void);
BOOLEAN secure_boot_enabled(void);
EFI_STATUS security_policy_install(void);
-
-EFI_STATUS security_policy_uninstall(void);
-
-#endif
diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c
index dd9cc29e1b..ba4a2c5da0 100644
--- a/src/boot/efi/splash.c
+++ b/src/boot/efi/splash.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/*
- * Copyright © 2012 Harald Hoyer <harald@redhat.com>
- */
#include <efi.h>
#include <efilib.h>
@@ -283,7 +280,7 @@ EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_
GraphicsOutput->Mode->Info->VerticalResolution, 0);
/* EFI buffer */
- blt_size = dib->x * dib->y * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
+ blt_size = sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * dib->x * dib->y;
blt = AllocatePool(blt_size);
if (!blt)
return EFI_OUT_OF_RESOURCES;
diff --git a/src/boot/efi/splash.h b/src/boot/efi/splash.h
index c9aa705b9a..8928b069a2 100644
--- a/src/boot/efi/splash.h
+++ b/src/boot/efi/splash.h
@@ -1,10 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/*
- * Copyright © 2012 Harald Hoyer <harald@redhat.com>
- */
-
-#ifndef __SDBOOT_SPLASH_H
-#define __SDBOOT_SPLASH_H
+#pragma once
EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background);
-#endif
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 4318a054a4..d11e555748 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -1,10 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- */
#include <efi.h>
#include <efilib.h>
@@ -69,7 +63,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
cmdline_len = szs[0];
/* if we are not in secure boot mode, accept a custom command line and replace the built-in one */
- if (!secure && loaded_image->LoadOptionsSize > 0 && *(CHAR16 *)loaded_image->LoadOptions != 0) {
+ if (!secure && loaded_image->LoadOptionsSize > 0 && *(CHAR16 *)loaded_image->LoadOptions > 0x1F) {
CHAR16 *options;
CHAR8 *line;
UINTN i;
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index f513e2fd13..f1f1674c59 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/*
- * Copyright © 2012 Harald Hoyer <harald@redhat.com>
- */
#include <efi.h>
#include <efilib.h>
@@ -13,7 +10,7 @@
* the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
* associated EFI variables.
*/
-static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
+const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
#ifdef __x86_64__
UINT64 ticks_read(VOID) {
@@ -42,7 +39,7 @@ UINT64 ticks_freq(VOID) {
uefi_call_wrapper(BS->Stall, 1, 1000);
ticks_end = ticks_read();
- return (ticks_end - ticks_start) * 1000;
+ return (ticks_end - ticks_start) * 1000UL;
}
UINT64 time_usec(VOID) {
@@ -59,10 +56,13 @@ UINT64 time_usec(VOID) {
return 0;
}
- return 1000 * 1000 * ticks / freq;
+ return 1000UL * 1000UL * ticks / freq;
}
-EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b) {
+EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
+ if (!v)
+ return EFI_INVALID_PARAMETER;
+
if (strcmpa(v, (CHAR8 *)"1") == 0 ||
strcmpa(v, (CHAR8 *)"yes") == 0 ||
strcmpa(v, (CHAR8 *)"y") == 0 ||
@@ -82,46 +82,61 @@ EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b) {
return EFI_INVALID_PARAMETER;
}
-EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
+EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, BOOLEAN persistent) {
UINT32 flags;
flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
if (persistent)
flags |= EFI_VARIABLE_NON_VOLATILE;
- return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf);
+ return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf);
}
-EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
- return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
+EFI_STATUS efivar_set(const CHAR16 *name, const CHAR16 *value, BOOLEAN persistent) {
+ return efivar_set_raw(&loader_guid, name, value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
}
EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
CHAR16 str[32];
- SPrint(str, 32, L"%d", i);
+ SPrint(str, 32, L"%u", i);
return efivar_set(name, str, persistent);
}
-EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
+EFI_STATUS efivar_get(const CHAR16 *name, CHAR16 **value) {
_cleanup_freepool_ CHAR8 *buf = NULL;
+ EFI_STATUS err;
CHAR16 *val;
UINTN size;
- EFI_STATUS err;
err = efivar_get_raw(&loader_guid, name, &buf, &size);
if (EFI_ERROR(err))
return err;
- val = StrDuplicate((CHAR16 *)buf);
+ /* Make sure there are no incomplete characters in the buffer */
+ if ((size % 2) != 0)
+ return EFI_INVALID_PARAMETER;
+
+ /* Return buffer directly if it happens to be NUL terminated already */
+ if (size >= 2 && buf[size-2] == 0 && buf[size-1] == 0) {
+ *value = (CHAR16*) buf;
+ buf = NULL;
+ return EFI_SUCCESS;
+ }
+
+ /* Make sure a terminating NUL is available at the end */
+ val = AllocatePool(size + 2);
if (!val)
return EFI_OUT_OF_RESOURCES;
+ CopyMem(val, buf, size);
+ val[size/2] = 0; /* NUL terminate */
+
*value = val;
return EFI_SUCCESS;
}
-EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
+EFI_STATUS efivar_get_int(const CHAR16 *name, UINTN *i) {
_cleanup_freepool_ CHAR16 *val = NULL;
EFI_STATUS err;
@@ -132,7 +147,7 @@ EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
return err;
}
-EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
+EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **buffer, UINTN *size) {
_cleanup_freepool_ CHAR8 *buf = NULL;
UINTN l;
EFI_STATUS err;
@@ -142,7 +157,7 @@ EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer,
if (!buf)
return EFI_OUT_OF_RESOURCES;
- err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf);
+ err = uefi_call_wrapper(RT->GetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, NULL, &l, buf);
if (!EFI_ERROR(err)) {
*buffer = buf;
buf = NULL;
@@ -290,12 +305,12 @@ CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
return NULL;
}
-EFI_STATUS file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size) {
+EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size) {
EFI_FILE_HANDLE handle;
_cleanup_freepool_ CHAR8 *buf = NULL;
EFI_STATUS err;
- err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
+ err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return err;
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index 1f64b026dc..4ba7050a91 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -1,45 +1,57 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/*
- * Copyright © 2012 Harald Hoyer <harald@redhat.com>
- */
-
-#ifndef __SDBOOT_UTIL_H
-#define __SDBOOT_UTIL_H
+#pragma once
#include <efi.h>
#include <efilib.h>
#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+#define OFFSETOF(x,y) __builtin_offsetof(x,y)
static inline const CHAR16 *yes_no(BOOLEAN b) {
return b ? L"yes" : L"no";
}
-EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b);
+EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b);
UINT64 ticks_read(void);
UINT64 ticks_freq(void);
UINT64 time_usec(void);
-EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent);
-EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent);
+EFI_STATUS efivar_set(const CHAR16 *name, const CHAR16 *value, BOOLEAN persistent);
+EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, BOOLEAN persistent);
EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent);
VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec);
-EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value);
-EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size);
-EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i);
+EFI_STATUS efivar_get(const CHAR16 *name, CHAR16 **value);
+EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **buffer, UINTN *size);
+EFI_STATUS efivar_get_int(const CHAR16 *name, UINTN *i);
CHAR8 *strchra(CHAR8 *s, CHAR8 c);
CHAR16 *stra_to_path(CHAR8 *stra);
CHAR16 *stra_to_str(CHAR8 *stra);
-EFI_STATUS file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size);
-#endif
+EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size);
static inline void FreePoolp(void *p) {
- FreePool(*(void**) p);
+ void *q = *(void**) p;
+
+ if (!q)
+ return;
+
+ FreePool(q);
}
-#define _cleanup_(x) __attribute__((cleanup(x)))
+#define _cleanup_(x) __attribute__((__cleanup__(x)))
#define _cleanup_freepool_ _cleanup_(FreePoolp)
+
+static inline void FileHandleClosep(EFI_FILE_HANDLE *handle) {
+ if (!*handle)
+ return;
+
+ uefi_call_wrapper((*handle)->Close, 1, *handle);
+}
+
+const EFI_GUID loader_guid;
+
+#define UINTN_MAX (~(UINTN)0)
+#define INTN_MAX ((INTN)(UINTN_MAX>>1))
diff --git a/src/busctl/busctl-introspect.c b/src/busctl/busctl-introspect.c
index 4af3481a5f..18254efd2d 100644
--- a/src/busctl/busctl-introspect.c
+++ b/src/busctl/busctl-introspect.c
@@ -67,10 +67,9 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) {
return t;
}
- if (t == XML_END) {
- log_error("Premature end of XML data.");
- return -EBADMSG;
- }
+ if (t == XML_END)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Premature end of XML data.");
switch (state) {
@@ -84,10 +83,10 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) {
else if (streq_ptr(name, "value"))
state = STATE_VALUE;
- else {
- log_error("Unexpected <annotation> attribute %s.", name);
- return -EBADMSG;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Unexpected <annotation> attribute %s.",
+ name);
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "annotation"))) {
@@ -116,10 +115,9 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) {
return 0;
- } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
- log_error("Unexpected token in <annotation>. (1)");
- return -EINVAL;
- }
+ } else if (t != XML_TEXT || !in_charset(name, WHITESPACE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <annotation>. (1)");
break;
@@ -129,10 +127,9 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) {
free_and_replace(field, name);
state = STATE_ANNOTATION;
- } else {
- log_error("Unexpected token in <annotation>. (2)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <annotation>. (2)");
break;
@@ -142,10 +139,9 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) {
free_and_replace(value, name);
state = STATE_ANNOTATION;
- } else {
- log_error("Unexpected token in <annotation>. (3)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <annotation>. (3)");
break;
@@ -187,10 +183,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
assert(context);
assert(prefix);
- if (n_depth > NODE_DEPTH_MAX) {
- log_error("<node> depth too high.");
- return -EINVAL;
- }
+ if (n_depth > NODE_DEPTH_MAX)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "<node> depth too high.");
for (;;) {
_cleanup_free_ char *name = NULL;
@@ -202,10 +196,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
return t;
}
- if (t == XML_END) {
- log_error("Premature end of XML data.");
- return -EBADMSG;
- }
+ if (t == XML_END)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Premature end of XML data.");
switch (state) {
@@ -214,10 +206,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
if (streq_ptr(name, "name"))
state = STATE_NODE_NAME;
- else {
- log_error("Unexpected <node> attribute %s.", name);
- return -EBADMSG;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Unexpected <node> attribute %s.", name);
} else if (t == XML_TAG_OPEN) {
@@ -228,10 +219,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
r = parse_xml_node(context, np, n_depth+1);
if (r < 0)
return r;
- } else {
- log_error("Unexpected <node> tag %s.", name);
- return -EBADMSG;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Unexpected <node> tag %s.", name);
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "node"))) {
@@ -244,10 +234,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
return 0;
- } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
- log_error("Unexpected token in <node>. (1)");
- return -EINVAL;
- }
+ } else if (t != XML_TEXT || !in_charset(name, WHITESPACE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <node>. (1)");
break;
@@ -271,10 +260,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
np = node_path;
state = STATE_NODE;
- } else {
- log_error("Unexpected token in <node>. (2)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <node>. (2)");
break;
@@ -283,10 +271,10 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_INTERFACE_NAME;
- else {
- log_error("Unexpected <interface> attribute %s.", name);
- return -EBADMSG;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Unexpected <interface> attribute %s.",
+ name);
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "method"))
@@ -300,10 +288,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
r = parse_xml_annotation(context, &context->interface_flags);
if (r < 0)
return r;
- } else {
- log_error("Unexpected <interface> tag %s.", name);
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected <interface> tag %s.", name);
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "interface"))) {
@@ -319,10 +306,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
state = STATE_NODE;
- } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
- log_error("Unexpected token in <interface>. (1)");
- return -EINVAL;
- }
+ } else if (t != XML_TEXT || !in_charset(name, WHITESPACE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <interface>. (1)");
break;
@@ -333,10 +319,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
free_and_replace(context->interface_name, name);
state = STATE_INTERFACE;
- } else {
- log_error("Unexpected token in <interface>. (2)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <interface>. (2)");
break;
@@ -345,10 +330,10 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_METHOD_NAME;
- else {
- log_error("Unexpected <method> attribute %s", name);
- return -EBADMSG;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Unexpected <method> attribute %s",
+ name);
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "arg"))
state = STATE_METHOD_ARG;
@@ -356,10 +341,10 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
r = parse_xml_annotation(context, &context->member_flags);
if (r < 0)
return r;
- } else {
- log_error("Unexpected <method> tag %s.", name);
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected <method> tag %s.",
+ name);
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "method"))) {
@@ -375,10 +360,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
state = STATE_INTERFACE;
- } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
- log_error("Unexpected token in <method> (1).");
- return -EINVAL;
- }
+ } else if (t != XML_TEXT || !in_charset(name, WHITESPACE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <method> (1).");
break;
@@ -389,10 +373,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
free_and_replace(context->member_name, name);
state = STATE_METHOD;
- } else {
- log_error("Unexpected token in <method> (2).");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <method> (2).");
break;
@@ -405,19 +388,19 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
state = STATE_METHOD_ARG_TYPE;
else if (streq_ptr(name, "direction"))
state = STATE_METHOD_ARG_DIRECTION;
- else {
- log_error("Unexpected method <arg> attribute %s.", name);
- return -EBADMSG;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Unexpected method <arg> attribute %s.",
+ name);
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "annotation")) {
r = parse_xml_annotation(context, NULL);
if (r < 0)
return r;
- } else {
- log_error("Unexpected method <arg> tag %s.", name);
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected method <arg> tag %s.",
+ name);
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) {
@@ -439,10 +422,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
}
state = STATE_METHOD;
- } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
- log_error("Unexpected token in method <arg>. (1)");
- return -EINVAL;
- }
+ } else if (t != XML_TEXT || !in_charset(name, WHITESPACE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in method <arg>. (1)");
break;
@@ -450,10 +432,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
if (t == XML_ATTRIBUTE_VALUE)
state = STATE_METHOD_ARG;
- else {
- log_error("Unexpected token in method <arg>. (2)");
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in method <arg>. (2)");
break;
@@ -463,10 +444,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
free_and_replace(argument_type, name);
state = STATE_METHOD_ARG;
- } else {
- log_error("Unexpected token in method <arg>. (3)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in method <arg>. (3)");
break;
@@ -476,10 +456,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
free_and_replace(argument_direction, name);
state = STATE_METHOD_ARG;
- } else {
- log_error("Unexpected token in method <arg>. (4)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in method <arg>. (4)");
break;
@@ -488,10 +467,10 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
if (t == XML_ATTRIBUTE_NAME) {
if (streq_ptr(name, "name"))
state = STATE_SIGNAL_NAME;
- else {
- log_error("Unexpected <signal> attribute %s.", name);
- return -EBADMSG;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Unexpected <signal> attribute %s.",
+ name);
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "arg"))
state = STATE_SIGNAL_ARG;
@@ -499,10 +478,10 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
r = parse_xml_annotation(context, &context->member_flags);
if (r < 0)
return r;
- } else {
- log_error("Unexpected <signal> tag %s.", name);
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected <signal> tag %s.",
+ name);
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "signal"))) {
@@ -518,10 +497,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
state = STATE_INTERFACE;
- } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
- log_error("Unexpected token in <signal>. (1)");
- return -EINVAL;
- }
+ } else if (t != XML_TEXT || !in_charset(name, WHITESPACE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <signal>. (1)");
break;
@@ -532,10 +510,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
free_and_replace(context->member_name, name);
state = STATE_SIGNAL;
- } else {
- log_error("Unexpected token in <signal>. (2)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <signal>. (2)");
break;
@@ -548,19 +525,19 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
state = STATE_SIGNAL_ARG_TYPE;
else if (streq_ptr(name, "direction"))
state = STATE_SIGNAL_ARG_DIRECTION;
- else {
- log_error("Unexpected signal <arg> attribute %s.", name);
- return -EBADMSG;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Unexpected signal <arg> attribute %s.",
+ name);
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "annotation")) {
r = parse_xml_annotation(context, NULL);
if (r < 0)
return r;
- } else {
- log_error("Unexpected signal <arg> tag %s.", name);
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected signal <arg> tag %s.",
+ name);
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) {
@@ -575,10 +552,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
}
state = STATE_SIGNAL;
- } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
- log_error("Unexpected token in signal <arg> (1).");
- return -EINVAL;
- }
+ } else if (t != XML_TEXT || !in_charset(name, WHITESPACE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in signal <arg> (1).");
break;
@@ -586,10 +562,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
if (t == XML_ATTRIBUTE_VALUE)
state = STATE_SIGNAL_ARG;
- else {
- log_error("Unexpected token in signal <arg> (2).");
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in signal <arg> (2).");
break;
@@ -599,10 +574,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
free_and_replace(argument_type, name);
state = STATE_SIGNAL_ARG;
- } else {
- log_error("Unexpected token in signal <arg> (3).");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in signal <arg> (3).");
break;
@@ -612,10 +586,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
free_and_replace(argument_direction, name);
state = STATE_SIGNAL_ARG;
- } else {
- log_error("Unexpected token in signal <arg>. (4)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in signal <arg>. (4)");
break;
@@ -628,20 +601,20 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
state = STATE_PROPERTY_TYPE;
else if (streq_ptr(name, "access"))
state = STATE_PROPERTY_ACCESS;
- else {
- log_error("Unexpected <property> attribute %s.", name);
- return -EBADMSG;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Unexpected <property> attribute %s.",
+ name);
} else if (t == XML_TAG_OPEN) {
if (streq_ptr(name, "annotation")) {
r = parse_xml_annotation(context, &context->member_flags);
if (r < 0)
return r;
- } else {
- log_error("Unexpected <property> tag %s.", name);
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected <property> tag %s.",
+ name);
} else if (t == XML_TAG_CLOSE_EMPTY ||
(t == XML_TAG_CLOSE && streq_ptr(name, "property"))) {
@@ -658,10 +631,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
state = STATE_INTERFACE;
- } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
- log_error("Unexpected token in <property>. (1)");
- return -EINVAL;
- }
+ } else if (t != XML_TEXT || !in_charset(name, WHITESPACE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <property>. (1)");
break;
@@ -672,10 +644,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
free_and_replace(context->member_name, name);
state = STATE_PROPERTY;
- } else {
- log_error("Unexpected token in <property>. (2)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <property>. (2)");
break;
@@ -686,10 +657,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
free_and_replace(context->member_signature, name);
state = STATE_PROPERTY;
- } else {
- log_error("Unexpected token in <property>. (3)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <property>. (3)");
break;
@@ -701,10 +671,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
context->member_writable = true;
state = STATE_PROPERTY;
- } else {
- log_error("Unexpected token in <property>. (4)");
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected token in <property>. (4)");
break;
}
diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c
index f9b23514c1..96b4177495 100644
--- a/src/busctl/busctl.c
+++ b/src/busctl/busctl.c
@@ -8,6 +8,7 @@
#include "alloc-util.h"
#include "bus-dump.h"
#include "bus-internal.h"
+#include "bus-message.h"
#include "bus-signature.h"
#include "bus-type.h"
#include "bus-util.h"
@@ -15,11 +16,14 @@
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
+#include "json.h"
#include "locale-util.h"
#include "log.h"
+#include "main-func.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "set.h"
#include "strv.h"
#include "terminal-util.h"
@@ -27,9 +31,14 @@
#include "util.h"
#include "verbs.h"
-static bool arg_no_pager = false;
+static enum {
+ JSON_OFF,
+ JSON_SHORT,
+ JSON_PRETTY,
+} arg_json = JSON_OFF;
+static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
-static char *arg_address = NULL;
+static const char *arg_address = NULL;
static bool arg_unique = false;
static bool arg_acquired = false;
static bool arg_activatable = false;
@@ -49,9 +58,14 @@ static bool arg_augment_creds = true;
static bool arg_watch_bind = false;
static usec_t arg_timeout = 0;
+STATIC_DESTRUCTOR_REGISTER(arg_matches, strv_freep);
+
#define NAME_IS_ACQUIRED INT_TO_PTR(1)
#define NAME_IS_ACTIVATABLE INT_TO_PTR(2)
+static int json_transform_message(sd_bus_message *m, JsonVariant **ret);
+static void json_dump_with_flags(JsonVariant *v, FILE *f);
+
static int acquire_bus(bool set_monitor, sd_bus **ret) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
@@ -149,7 +163,7 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to list names: %m");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
names = hashmap_new(&string_hash_ops);
if (!names)
@@ -315,8 +329,8 @@ static void print_subtree(const char *prefix, const char *path, char **l) {
l++;
}
- vertical = strjoina(prefix, special_glyph(TREE_VERTICAL));
- space = strjoina(prefix, special_glyph(TREE_SPACE));
+ vertical = strjoina(prefix, special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
+ space = strjoina(prefix, special_glyph(SPECIAL_GLYPH_TREE_SPACE));
for (;;) {
bool has_more = false;
@@ -337,7 +351,7 @@ static void print_subtree(const char *prefix, const char *path, char **l) {
n++;
}
- printf("%s%s%s\n", prefix, special_glyph(has_more ? TREE_BRANCH : TREE_RIGHT), *l);
+ printf("%s%s%s\n", prefix, special_glyph(has_more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT), *l);
print_subtree(has_more ? vertical : space, *l, l);
l = n;
@@ -464,7 +478,7 @@ static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool m
p = NULL;
}
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
l = set_get_strv(done);
if (!l)
@@ -498,7 +512,7 @@ static int tree(int argc, char **argv, void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to get name list: %m");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
STRV_FOREACH(i, names) {
int q;
@@ -528,7 +542,7 @@ static int tree(int argc, char **argv, void *userdata) {
printf("\n");
if (argv[2]) {
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
}
@@ -699,8 +713,7 @@ typedef struct Member {
uint64_t flags;
} Member;
-static void member_hash_func(const void *p, struct siphash *state) {
- const Member *m = p;
+static void member_hash_func(const Member *m, struct siphash *state) {
uint64_t arity = 1;
assert(m);
@@ -719,8 +732,7 @@ static void member_hash_func(const void *p, struct siphash *state) {
string_hash_func(m->interface, state);
}
-static int member_compare_func(const void *a, const void *b) {
- const Member *x = a, *y = b;
+static int member_compare_func(const Member *x, const Member *y) {
int d;
assert(x);
@@ -739,10 +751,8 @@ static int member_compare_func(const void *a, const void *b) {
return strcmp_ptr(x->name, y->name);
}
-static int member_compare_funcp(const void *a, const void *b) {
- const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b;
-
- return member_compare_func(*x, *y);
+static int member_compare_funcp(Member * const *a, Member * const *b) {
+ return member_compare_func(*a, *b);
}
static void member_free(Member *m) {
@@ -910,12 +920,9 @@ static int on_property(const char *interface, const char *name, const char *sign
return 0;
}
-static int introspect(int argc, char **argv, void *userdata) {
- static const struct hash_ops member_hash_ops = {
- .hash = member_hash_func,
- .compare = member_compare_func,
- };
+DEFINE_PRIVATE_HASH_OPS(member_hash_ops, Member, member_hash_func, member_compare_func);
+static int introspect(int argc, char **argv, void *userdata) {
static const XMLIntrospectOps ops = {
.on_interface = on_interface,
.on_method = on_method,
@@ -1030,7 +1037,7 @@ static int introspect(int argc, char **argv, void *userdata) {
return bus_log_parse_error(r);
}
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
name_width = STRLEN("NAME");
type_width = STRLEN("TYPE");
@@ -1063,7 +1070,7 @@ static int introspect(int argc, char **argv, void *userdata) {
if (result_width > 40)
result_width = 40;
- qsort(sorted, k, sizeof(Member*), member_compare_funcp);
+ typesafe_qsort(sorted, k, member_compare_funcp);
if (arg_legend) {
printf("%-*s %-*s %-*s %-*s %s\n",
@@ -1125,6 +1132,43 @@ static int message_pcap(sd_bus_message *m, FILE *f) {
return bus_message_pcap_frame(m, arg_snaplen, f);
}
+static int message_json(sd_bus_message *m, FILE *f) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
+ char e[2];
+ int r;
+
+ r = json_transform_message(m, &v);
+ if (r < 0)
+ return r;
+
+ e[0] = m->header->endian;
+ e[1] = 0;
+
+ r = json_build(&w, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("type", JSON_BUILD_STRING(bus_message_type_to_string(m->header->type))),
+ JSON_BUILD_PAIR("endian", JSON_BUILD_STRING(e)),
+ JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(m->header->flags)),
+ JSON_BUILD_PAIR("version", JSON_BUILD_INTEGER(m->header->version)),
+ JSON_BUILD_PAIR_CONDITION(m->priority != 0, "priority", JSON_BUILD_INTEGER(m->priority)),
+ JSON_BUILD_PAIR("cookie", JSON_BUILD_INTEGER(BUS_MESSAGE_COOKIE(m))),
+ JSON_BUILD_PAIR_CONDITION(m->reply_cookie != 0, "reply_cookie", JSON_BUILD_INTEGER(m->reply_cookie)),
+ JSON_BUILD_PAIR_CONDITION(m->sender, "sender", JSON_BUILD_STRING(m->sender)),
+ JSON_BUILD_PAIR_CONDITION(m->destination, "destination", JSON_BUILD_STRING(m->destination)),
+ JSON_BUILD_PAIR_CONDITION(m->path, "path", JSON_BUILD_STRING(m->path)),
+ JSON_BUILD_PAIR_CONDITION(m->interface, "interface", JSON_BUILD_STRING(m->interface)),
+ JSON_BUILD_PAIR_CONDITION(m->member, "member", JSON_BUILD_STRING(m->member)),
+ JSON_BUILD_PAIR_CONDITION(m->monotonic != 0, "monotonic", JSON_BUILD_INTEGER(m->monotonic)),
+ JSON_BUILD_PAIR_CONDITION(m->realtime != 0, "realtime", JSON_BUILD_INTEGER(m->realtime)),
+ JSON_BUILD_PAIR_CONDITION(m->seqnum != 0, "seqnum", JSON_BUILD_INTEGER(m->seqnum)),
+ JSON_BUILD_PAIR_CONDITION(m->error.name, "error_name", JSON_BUILD_STRING(m->error.name)),
+ JSON_BUILD_PAIR("payload", JSON_BUILD_VARIANT(v))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to build JSON object: %m");
+
+ json_dump_with_flags(w, f);
+ return 0;
+}
+
static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f)) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
@@ -1244,16 +1288,15 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f
}
static int verb_monitor(int argc, char **argv, void *userdata) {
- return monitor(argc, argv, message_dump);
+ return monitor(argc, argv, arg_json != JSON_OFF ? message_json : message_dump);
}
static int verb_capture(int argc, char **argv, void *userdata) {
int r;
- if (isatty(fileno(stdout)) > 0) {
- log_error("Refusing to write message data to console, please redirect output to a file.");
- return -EINVAL;
- }
+ if (isatty(fileno(stdout)) > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to write message data to console, please redirect output to a file.");
bus_pcap_header(arg_snaplen, stdout);
@@ -1339,10 +1382,9 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
if (t == 0)
break;
- if (!v) {
- log_error("Too few parameters for signature.");
- return -EINVAL;
- }
+ if (!v)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too few parameters for signature.");
signature++;
p++;
@@ -1353,7 +1395,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = parse_boolean(v);
if (r < 0)
- return log_error_errno(r, "Failed to parse as boolean: %s", v);
+ return log_error_errno(r, "Failed to parse '%s' as boolean: %m", v);
r = sd_bus_message_append_basic(m, t, &r);
break;
@@ -1363,7 +1405,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = safe_atou8(v, &z);
if (r < 0)
- return log_error_errno(r, "Failed to parse as byte (unsigned 8bit integer): %s", v);
+ return log_error_errno(r, "Failed to parse '%s' as byte (unsigned 8bit integer): %m", v);
r = sd_bus_message_append_basic(m, t, &z);
break;
@@ -1374,7 +1416,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = safe_atoi16(v, &z);
if (r < 0)
- return log_error_errno(r, "Failed to parse as signed 16bit integer: %s", v);
+ return log_error_errno(r, "Failed to parse '%s' as signed 16bit integer: %m", v);
r = sd_bus_message_append_basic(m, t, &z);
break;
@@ -1385,7 +1427,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = safe_atou16(v, &z);
if (r < 0)
- return log_error_errno(r, "Failed to parse as unsigned 16bit integer: %s", v);
+ return log_error_errno(r, "Failed to parse '%s' as unsigned 16bit integer: %m", v);
r = sd_bus_message_append_basic(m, t, &z);
break;
@@ -1396,7 +1438,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = safe_atoi32(v, &z);
if (r < 0)
- return log_error_errno(r, "Failed to parse as signed 32bit integer: %s", v);
+ return log_error_errno(r, "Failed to parse '%s' as signed 32bit integer: %m", v);
r = sd_bus_message_append_basic(m, t, &z);
break;
@@ -1407,7 +1449,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = safe_atou32(v, &z);
if (r < 0)
- return log_error_errno(r, "Failed to parse as unsigned 32bit integer: %s", v);
+ return log_error_errno(r, "Failed to parse '%s' as unsigned 32bit integer: %m", v);
r = sd_bus_message_append_basic(m, t, &z);
break;
@@ -1418,7 +1460,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = safe_atoi64(v, &z);
if (r < 0)
- return log_error_errno(r, "Failed to parse as signed 64bit integer: %s", v);
+ return log_error_errno(r, "Failed to parse '%s' as signed 64bit integer: %m", v);
r = sd_bus_message_append_basic(m, t, &z);
break;
@@ -1429,7 +1471,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = safe_atou64(v, &z);
if (r < 0)
- return log_error_errno(r, "Failed to parse as unsigned 64bit integer: %s", v);
+ return log_error_errno(r, "Failed to parse '%s' as unsigned 64bit integer: %m", v);
r = sd_bus_message_append_basic(m, t, &z);
break;
@@ -1440,7 +1482,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = safe_atod(v, &z);
if (r < 0)
- return log_error_errno(r, "Failed to parse as double precision floating point: %s", v);
+ return log_error_errno(r, "Failed to parse '%s' as double precision floating point: %m", v);
r = sd_bus_message_append_basic(m, t, &z);
break;
@@ -1459,11 +1501,11 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = safe_atou32(v, &n);
if (r < 0)
- return log_error_errno(r, "Failed to parse number of array entries: %s", v);
+ return log_error_errno(r, "Failed to parse '%s' number of array entries: %m", v);
r = signature_element_length(signature, &k);
if (r < 0)
- return log_error_errno(r, "Invalid array signature.");
+ return log_error_errno(r, "Invalid array signature: %m");
{
unsigned i;
@@ -1509,7 +1551,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
r = signature_element_length(signature, &k);
if (r < 0)
- return log_error_errno(r, "Invalid struct/dict entry signature.");
+ return log_error_errno(r, "Invalid struct/dict entry signature: %m");
{
char s[k-1];
@@ -1532,12 +1574,12 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
}
case SD_BUS_TYPE_UNIX_FD:
- log_error("UNIX file descriptor not supported as type.");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "UNIX file descriptor not supported as type.");
default:
- log_error("Unknown signature type %c.", t);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown signature type %c.", t);
}
if (r < 0)
@@ -1548,6 +1590,359 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
return 0;
}
+static int json_transform_one(sd_bus_message *m, JsonVariant **ret);
+
+static int json_transform_array_or_struct(sd_bus_message *m, JsonVariant **ret) {
+ size_t n_elements = 0, n_allocated = 0;
+ JsonVariant **elements = NULL;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ for (;;) {
+ r = sd_bus_message_at_end(m, false);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ goto finish;
+ }
+ if (r > 0)
+ break;
+
+ if (!GREEDY_REALLOC(elements, n_allocated, n_elements + 1)) {
+ r = log_oom();
+ goto finish;
+ }
+
+ r = json_transform_one(m, elements + n_elements);
+ if (r < 0)
+ goto finish;
+
+ n_elements++;
+ }
+
+ r = json_variant_new_array(ret, elements, n_elements);
+
+finish:
+ json_variant_unref_many(elements, n_elements);
+ free(elements);
+
+ return r;
+}
+
+static int json_transform_variant(sd_bus_message *m, const char *contents, JsonVariant **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *value = NULL;
+ int r;
+
+ assert(m);
+ assert(contents);
+ assert(ret);
+
+ r = json_transform_one(m, &value);
+ if (r < 0)
+ return r;
+
+ r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("type", JSON_BUILD_STRING(contents)),
+ JSON_BUILD_PAIR("data", JSON_BUILD_VARIANT(value))));
+ if (r < 0)
+ return log_oom();
+
+ return r;
+}
+
+static int json_transform_dict_array(sd_bus_message *m, JsonVariant **ret) {
+ size_t n_elements = 0, n_allocated = 0;
+ JsonVariant **elements = NULL;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ for (;;) {
+ const char *contents;
+ char type;
+
+ r = sd_bus_message_at_end(m, false);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ goto finish;
+ }
+ if (r > 0)
+ break;
+
+ r = sd_bus_message_peek_type(m, &type, &contents);
+ if (r < 0)
+ return r;
+
+ assert(type == 'e');
+
+ if (!GREEDY_REALLOC(elements, n_allocated, n_elements + 2)) {
+ r = log_oom();
+ goto finish;
+ }
+
+ r = sd_bus_message_enter_container(m, type, contents);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ goto finish;
+ }
+
+ r = json_transform_one(m, elements + n_elements);
+ if (r < 0)
+ goto finish;
+
+ n_elements++;
+
+ r = json_transform_one(m, elements + n_elements);
+ if (r < 0)
+ goto finish;
+
+ n_elements++;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ goto finish;
+ }
+ }
+
+ r = json_variant_new_object(ret, elements, n_elements);
+
+finish:
+ json_variant_unref_many(elements, n_elements);
+ free(elements);
+
+ return r;
+}
+
+static int json_transform_one(sd_bus_message *m, JsonVariant **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ const char *contents;
+ char type;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ r = sd_bus_message_peek_type(m, &type, &contents);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ switch (type) {
+
+ case SD_BUS_TYPE_BYTE: {
+ uint8_t b;
+
+ r = sd_bus_message_read_basic(m, type, &b);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_unsigned(&v, b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform byte: %m");
+
+ break;
+ }
+
+ case SD_BUS_TYPE_BOOLEAN: {
+ int b;
+
+ r = sd_bus_message_read_basic(m, type, &b);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_boolean(&v, b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform boolean: %m");
+
+ break;
+ }
+
+ case SD_BUS_TYPE_INT16: {
+ int16_t b;
+
+ r = sd_bus_message_read_basic(m, type, &b);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_integer(&v, b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform int16: %m");
+
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT16: {
+ uint16_t b;
+
+ r = sd_bus_message_read_basic(m, type, &b);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_unsigned(&v, b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform uint16: %m");
+
+ break;
+ }
+
+ case SD_BUS_TYPE_INT32: {
+ int32_t b;
+
+ r = sd_bus_message_read_basic(m, type, &b);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_integer(&v, b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform int32: %m");
+
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT32: {
+ uint32_t b;
+
+ r = sd_bus_message_read_basic(m, type, &b);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_unsigned(&v, b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform uint32: %m");
+
+ break;
+ }
+
+ case SD_BUS_TYPE_INT64: {
+ int64_t b;
+
+ r = sd_bus_message_read_basic(m, type, &b);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_integer(&v, b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform int64: %m");
+
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT64: {
+ uint64_t b;
+
+ r = sd_bus_message_read_basic(m, type, &b);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_unsigned(&v, b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform uint64: %m");
+
+ break;
+ }
+
+ case SD_BUS_TYPE_DOUBLE: {
+ double d;
+
+ r = sd_bus_message_read_basic(m, type, &d);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_real(&v, d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform double: %m");
+
+ break;
+ }
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE: {
+ const char *s;
+
+ r = sd_bus_message_read_basic(m, type, &s);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_string(&v, s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform double: %m");
+
+ break;
+ }
+
+ case SD_BUS_TYPE_UNIX_FD:
+ r = sd_bus_message_read_basic(m, type, NULL);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = json_variant_new_null(&v);
+ if (r < 0)
+ return log_error_errno(r, "Failed to transform fd: %m");
+
+ break;
+
+ case SD_BUS_TYPE_ARRAY:
+ case SD_BUS_TYPE_VARIANT:
+ case SD_BUS_TYPE_STRUCT:
+ r = sd_bus_message_enter_container(m, type, contents);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (type == SD_BUS_TYPE_VARIANT)
+ r = json_transform_variant(m, contents, &v);
+ else if (type == SD_BUS_TYPE_ARRAY && contents[0] == '{')
+ r = json_transform_dict_array(m, &v);
+ else
+ r = json_transform_array_or_struct(m, &v);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ break;
+
+ default:
+ assert_not_reached("Unexpected element type");
+ }
+
+ *ret = TAKE_PTR(v);
+ return 0;
+}
+
+static int json_transform_message(sd_bus_message *m, JsonVariant **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ const char *type;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ assert_se(type = sd_bus_message_get_signature(m, false));
+
+ r = json_transform_array_or_struct(m, &v);
+ if (r < 0)
+ return r;
+
+ r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("type", JSON_BUILD_STRING(type)),
+ JSON_BUILD_PAIR("data", JSON_BUILD_VARIANT(v))));
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+}
+
+static void json_dump_with_flags(JsonVariant *v, FILE *f) {
+
+ json_variant_dump(v,
+ (arg_json == JSON_PRETTY ? JSON_FORMAT_PRETTY : JSON_FORMAT_NEWLINE) |
+ JSON_FORMAT_COLOR_AUTO,
+ f, NULL);
+}
+
static int call(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -1607,8 +2002,20 @@ static int call(int argc, char **argv, void *userdata) {
if (r == 0 && !arg_quiet) {
- if (arg_verbose) {
- (void) pager_open(arg_no_pager, false);
+ if (arg_json != JSON_OFF) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ if (arg_json != JSON_SHORT)
+ (void) pager_open(arg_pager_flags);
+
+ r = json_transform_message(reply, &v);
+ if (r < 0)
+ return r;
+
+ json_dump_with_flags(v, stdout);
+
+ } else if (arg_verbose) {
+ (void) pager_open(arg_pager_flags);
r = bus_message_dump(reply, stdout, 0);
if (r < 0)
@@ -1656,8 +2063,20 @@ static int get_property(int argc, char **argv, void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- if (arg_verbose) {
- (void) pager_open(arg_no_pager, false);
+ if (arg_json != JSON_OFF) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ if (arg_json != JSON_SHORT)
+ (void) pager_open(arg_pager_flags);
+
+ r = json_transform_variant(reply, contents, &v);
+ if (r < 0)
+ return r;
+
+ json_dump_with_flags(v, stdout);
+
+ } else if (arg_verbose) {
+ (void) pager_open(arg_pager_flags);
r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
if (r < 0)
@@ -1726,6 +2145,13 @@ static int set_property(int argc, char **argv, void *userdata) {
}
static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("busctl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Introspect the bus.\n\n"
" -h --help Show this help\n"
@@ -1746,6 +2172,8 @@ static int help(void) {
" --list Don't show tree, but simple object path list\n"
" -q --quiet Don't show method call reply\n"
" --verbose Show result values in long format\n"
+ " --json=MODE Output as JSON\n"
+ " -j Same as --json=pretty on tty, --json=short otherwise\n"
" --expect-reply=BOOL Expect a method call reply\n"
" --auto-start=BOOL Auto-start destination service\n"
" --allow-interactive-authorization=BOOL\n"
@@ -1768,7 +2196,10 @@ static int help(void) {
" set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
" Set property value\n"
" help Show this help\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
@@ -1800,33 +2231,35 @@ static int parse_argv(int argc, char *argv[]) {
ARG_TIMEOUT,
ARG_AUGMENT_CREDS,
ARG_WATCH_BIND,
+ ARG_JSON,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
- { "system", no_argument, NULL, ARG_SYSTEM },
- { "user", no_argument, NULL, ARG_USER },
- { "address", required_argument, NULL, ARG_ADDRESS },
- { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
- { "unique", no_argument, NULL, ARG_UNIQUE },
- { "acquired", no_argument, NULL, ARG_ACQUIRED },
- { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
- { "match", required_argument, NULL, ARG_MATCH },
- { "host", required_argument, NULL, 'H' },
- { "machine", required_argument, NULL, 'M' },
- { "size", required_argument, NULL, ARG_SIZE },
- { "list", no_argument, NULL, ARG_LIST },
- { "quiet", no_argument, NULL, 'q' },
- { "verbose", no_argument, NULL, ARG_VERBOSE },
- { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
- { "auto-start", required_argument, NULL, ARG_AUTO_START },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
+ { "system", no_argument, NULL, ARG_SYSTEM },
+ { "user", no_argument, NULL, ARG_USER },
+ { "address", required_argument, NULL, ARG_ADDRESS },
+ { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
+ { "unique", no_argument, NULL, ARG_UNIQUE },
+ { "acquired", no_argument, NULL, ARG_ACQUIRED },
+ { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
+ { "match", required_argument, NULL, ARG_MATCH },
+ { "host", required_argument, NULL, 'H' },
+ { "machine", required_argument, NULL, 'M' },
+ { "size", required_argument, NULL, ARG_SIZE },
+ { "list", no_argument, NULL, ARG_LIST },
+ { "quiet", no_argument, NULL, 'q' },
+ { "verbose", no_argument, NULL, ARG_VERBOSE },
+ { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
+ { "auto-start", required_argument, NULL, ARG_AUTO_START },
{ "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
- { "timeout", required_argument, NULL, ARG_TIMEOUT },
- { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS},
- { "watch-bind", required_argument, NULL, ARG_WATCH_BIND },
+ { "timeout", required_argument, NULL, ARG_TIMEOUT },
+ { "augment-creds", required_argument, NULL, ARG_AUGMENT_CREDS },
+ { "watch-bind", required_argument, NULL, ARG_WATCH_BIND },
+ { "json", required_argument, NULL, ARG_JSON },
{},
};
@@ -1835,7 +2268,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hH:M:qj", options, NULL)) >= 0)
switch (c) {
@@ -1846,7 +2279,7 @@ static int parse_argv(int argc, char *argv[]) {
return version();
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
@@ -1891,12 +2324,11 @@ static int parse_argv(int argc, char *argv[]) {
r = parse_size(optarg, 1024, &sz);
if (r < 0)
- return log_error_errno(r, "Failed to parse size: %s", optarg);
+ return log_error_errno(r, "Failed to parse size '%s': %m", optarg);
- if ((uint64_t) (size_t) sz != sz) {
- log_error("Size out of range.");
- return -E2BIG;
- }
+ if ((uint64_t) (size_t) sz != sz)
+ return log_error_errno(SYNTHETIC_ERRNO(E2BIG),
+ "Size out of range.");
arg_snaplen = (size_t) sz;
break;
@@ -1927,7 +2359,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_EXPECT_REPLY:
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse --expect-reply= parameter: %s", optarg);
+ return log_error_errno(r, "Failed to parse --expect-reply= parameter '%s': %m", optarg);
arg_expect_reply = r;
break;
@@ -1935,7 +2367,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_AUTO_START:
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse --auto-start= parameter: %s", optarg);
+ return log_error_errno(r, "Failed to parse --auto-start= parameter '%s': %m", optarg);
arg_auto_start = r;
break;
@@ -1943,7 +2375,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse --allow-interactive-authorization= parameter: %s", optarg);
+ return log_error_errno(r, "Failed to parse --allow-interactive-authorization= parameter '%s': %m", optarg);
arg_allow_interactive_authorization = r;
break;
@@ -1951,14 +2383,14 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_TIMEOUT:
r = parse_sec(optarg, &arg_timeout);
if (r < 0)
- return log_error_errno(r, "Failed to parse --timeout= parameter: %s", optarg);
+ return log_error_errno(r, "Failed to parse --timeout= parameter '%s': %m", optarg);
break;
case ARG_AUGMENT_CREDS:
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse --augment-creds= parameter: %s", optarg);
+ return log_error_errno(r, "Failed to parse --augment-creds= parameter '%s': %m", optarg);
arg_augment_creds = r;
break;
@@ -1966,11 +2398,34 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_WATCH_BIND:
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse --watch-bind= parameter: %s", optarg);
+ return log_error_errno(r, "Failed to parse --watch-bind= parameter '%s': %m", optarg);
arg_watch_bind = r;
break;
+ case 'j':
+ if (on_tty())
+ arg_json = JSON_PRETTY;
+ else
+ arg_json = JSON_SHORT;
+ break;
+
+ case ARG_JSON:
+ if (streq(optarg, "short"))
+ arg_json = JSON_SHORT;
+ else if (streq(optarg, "pretty"))
+ arg_json = JSON_PRETTY;
+ else if (streq(optarg, "help")) {
+ fputs("short\n"
+ "pretty\n", stdout);
+ return 0;
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown JSON out mode: %s",
+ optarg);
+
+ break;
+
case '?':
return -EINVAL;
@@ -2000,7 +2455,7 @@ static int busctl_main(int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, NULL);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
log_parse_environment();
@@ -2008,16 +2463,9 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
-
- r = busctl_main(argc, argv);
-
-finish:
- /* make sure we terminate the bus connection first, and then close the
- * pager, see issue #3543 for the details. */
- pager_close();
-
- arg_matches = strv_free(arg_matches);
+ return r;
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return busctl_main(argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index 9c2f591dc6..b6b15cf114 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -14,14 +14,16 @@
#include "cgroup-util.h"
#include "fileio.h"
#include "log.h"
+#include "main-func.h"
#include "output-mode.h"
#include "pager.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "strv.h"
#include "unit-name.h"
#include "util.h"
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static bool arg_kernel_threads = false;
static bool arg_all = false;
@@ -33,9 +35,18 @@ static enum {
static char **arg_names = NULL;
static int arg_full = -1;
-static char* arg_machine = NULL;
+static const char* arg_machine = NULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_names, freep); /* don't free the strings */
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-cgls", "1", &link);
+ if (r < 0)
+ return log_oom();
-static void help(void) {
printf("%s [OPTIONS...] [CGROUP...]\n\n"
"Recursively show control group contents.\n\n"
" -h --help Show this help\n"
@@ -47,7 +58,12 @@ static void help(void) {
" -l --full Do not ellipsize output\n"
" -k Include kernel threads in output\n"
" -M --machine= Show container\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -80,14 +96,13 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case 'a':
@@ -131,10 +146,9 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (arg_machine && arg_show_unit != SHOW_UNIT_NONE) {
- log_error("Cannot combine --unit or --user-unit with --machine=.");
- return -EINVAL;
- }
+ if (arg_machine && arg_show_unit != SHOW_UNIT_NONE)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot combine --unit or --user-unit with --machine=.");
return 1;
}
@@ -148,7 +162,7 @@ static void show_cg_info(const char *controller, const char *path) {
fflush(stdout);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r, output_flags;
log_parse_environment();
@@ -156,9 +170,9 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
- r = pager_open(arg_no_pager, false);
+ r = pager_open(arg_pager_flags);
if (r > 0 && arg_full < 0)
arg_full = true;
@@ -184,10 +198,8 @@ int main(int argc, char *argv[]) {
r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL,
arg_show_unit == SHOW_UNIT_USER,
&bus);
- if (r < 0) {
- log_error_errno(r, "Failed to create bus connection: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
}
q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup);
@@ -219,7 +231,7 @@ int main(int argc, char *argv[]) {
/* Query root only if needed, treat error as fatal */
r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to list cgroup tree: %m");
}
q = cg_split_spec(*name, &c, &p);
@@ -231,10 +243,8 @@ int main(int argc, char *argv[]) {
controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
if (p) {
j = strjoin(root, "/", p);
- if (!j) {
- r = log_oom();
- goto finish;
- }
+ if (!j)
+ return log_oom();
path_simplify(j, false);
path = j;
@@ -258,10 +268,8 @@ int main(int argc, char *argv[]) {
_cleanup_free_ char *cwd = NULL;
r = safe_getcwd(&cwd);
- if (r < 0) {
- log_error_errno(r, "Cannot determine current working directory: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Cannot determine current working directory: %m");
if (path_startswith(cwd, "/sys/fs/cgroup")) {
printf("Working directory %s:\n", cwd);
@@ -277,7 +285,7 @@ int main(int argc, char *argv[]) {
r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to list cgroup tree: %m");
show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root);
@@ -285,13 +293,10 @@ int main(int argc, char *argv[]) {
r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags);
}
}
-
if (r < 0)
- log_error_errno(r, "Failed to list cgroup tree: %m");
-
-finish:
- pager_close();
- free(arg_names); /* don't free the strings */
+ return log_error_errno(r, "Failed to list cgroup tree: %m");
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/cgroups-agent/cgroups-agent.c b/src/cgroups-agent/cgroups-agent.c
index 27a28d1414..9721a32ed6 100644
--- a/src/cgroups-agent/cgroups-agent.c
+++ b/src/cgroups-agent/cgroups-agent.c
@@ -23,9 +23,7 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
if (fd < 0) {
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 8dda08ab4c..5abfab03de 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -19,8 +19,10 @@
#include "fd-util.h"
#include "fileio.h"
#include "hashmap.h"
+#include "main-func.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "process-util.h"
#include "procfs-util.h"
#include "stdio-util.h"
@@ -82,20 +84,12 @@ static enum {
CPU_TIME,
} arg_cpu_type = CPU_PERCENT;
-static void group_free(Group *g) {
- assert(g);
+static Group *group_free(Group *g) {
+ if (!g)
+ return NULL;
free(g->path);
- free(g);
-}
-
-static void group_hashmap_clear(Hashmap *h) {
- hashmap_clear_with_destructor(h, group_free);
-}
-
-static void group_hashmap_free(Hashmap *h) {
- group_hashmap_clear(h);
- hashmap_free(h);
+ return mfree(g);
}
static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, uint64_t t) {
@@ -229,7 +223,7 @@ static int process(
if (g->n_tasks > 0)
g->n_tasks_valid = true;
- } else if (STR_IN_SET(controller, "cpu", "cpuacct")) {
+ } else if (STR_IN_SET(controller, "cpu", "cpuacct") || cpu_accounting_is_cheap()) {
_cleanup_free_ char *p = NULL, *v = NULL;
uint64_t new_usage;
nsec_t timestamp;
@@ -343,10 +337,14 @@ static int process(
}
for (;;) {
- char line[LINE_MAX], *l;
+ _cleanup_free_ char *line = NULL;
uint64_t k, *q;
+ char *l;
- if (!fgets(line, sizeof(line), f))
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
break;
/* Trim and skip the device */
@@ -495,37 +493,21 @@ static int refresh_one(
}
static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) {
+ const char *c;
int r;
- assert(a);
-
- r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, root, a, b, iteration, 0, NULL);
- if (r < 0)
- return r;
- r = refresh_one("cpu", root, a, b, iteration, 0, NULL);
- if (r < 0)
- return r;
- r = refresh_one("cpuacct", root, a, b, iteration, 0, NULL);
- if (r < 0)
- return r;
- r = refresh_one("memory", root, a, b, iteration, 0, NULL);
- if (r < 0)
- return r;
- r = refresh_one("io", root, a, b, iteration, 0, NULL);
- if (r < 0)
- return r;
- r = refresh_one("blkio", root, a, b, iteration, 0, NULL);
- if (r < 0)
- return r;
- r = refresh_one("pids", root, a, b, iteration, 0, NULL);
- if (r < 0)
- return r;
+ FOREACH_STRING(c, SYSTEMD_CGROUP_CONTROLLER, "cpu", "cpuacct", "memory", "io", "blkio", "pids") {
+ r = refresh_one(c, root, a, b, iteration, 0, NULL);
+ if (r < 0)
+ return r;
+ }
return 0;
}
-static int group_compare(const void*a, const void *b) {
- const Group *x = *(Group**)a, *y = *(Group**)b;
+static int group_compare(Group * const *a, Group * const *b) {
+ const Group *x = *a, *y = *b;
+ int r;
if (arg_order != ORDER_TASKS || arg_recursive) {
/* Let's make sure that the parent is always before
@@ -547,29 +529,26 @@ static int group_compare(const void*a, const void *b) {
case ORDER_CPU:
if (arg_cpu_type == CPU_PERCENT) {
if (x->cpu_valid && y->cpu_valid) {
- if (x->cpu_fraction > y->cpu_fraction)
- return -1;
- else if (x->cpu_fraction < y->cpu_fraction)
- return 1;
+ r = CMP(y->cpu_fraction, x->cpu_fraction);
+ if (r != 0)
+ return r;
} else if (x->cpu_valid)
return -1;
else if (y->cpu_valid)
return 1;
} else {
- if (x->cpu_usage > y->cpu_usage)
- return -1;
- else if (x->cpu_usage < y->cpu_usage)
- return 1;
+ r = CMP(y->cpu_usage, x->cpu_usage);
+ if (r != 0)
+ return r;
}
break;
case ORDER_TASKS:
if (x->n_tasks_valid && y->n_tasks_valid) {
- if (x->n_tasks > y->n_tasks)
- return -1;
- else if (x->n_tasks < y->n_tasks)
- return 1;
+ r = CMP(y->n_tasks, x->n_tasks);
+ if (r != 0)
+ return r;
} else if (x->n_tasks_valid)
return -1;
else if (y->n_tasks_valid)
@@ -579,10 +558,9 @@ static int group_compare(const void*a, const void *b) {
case ORDER_MEMORY:
if (x->memory_valid && y->memory_valid) {
- if (x->memory > y->memory)
- return -1;
- else if (x->memory < y->memory)
- return 1;
+ r = CMP(y->memory, x->memory);
+ if (r != 0)
+ return r;
} else if (x->memory_valid)
return -1;
else if (y->memory_valid)
@@ -592,10 +570,9 @@ static int group_compare(const void*a, const void *b) {
case ORDER_IO:
if (x->io_valid && y->io_valid) {
- if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps)
- return -1;
- else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps)
- return 1;
+ r = CMP(y->io_input_bps + y->io_output_bps, x->io_input_bps + x->io_output_bps);
+ if (r != 0)
+ return r;
} else if (x->io_valid)
return -1;
else if (y->io_valid)
@@ -624,7 +601,7 @@ static void display(Hashmap *a) {
if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
array[n++] = g;
- qsort_safe(array, n, sizeof(Group*), group_compare);
+ typesafe_qsort(array, n, group_compare);
/* Find the longest names in one run */
for (j = 0; j < n; j++) {
@@ -709,7 +686,14 @@ static void display(Hashmap *a) {
}
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-cgtop", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] [CGROUP]\n\n"
"Show top control groups by their resource usage.\n\n"
" -h --help Show this help\n"
@@ -731,11 +715,16 @@ static void help(void) {
" -b --batch Run in batch mode, accepting no input\n"
" --depth=DEPTH Maximum traversal depth (default: %u)\n"
" -M --machine= Show container\n"
- , program_invocation_short_name, arg_depth);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , arg_depth
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
-
enum {
ARG_VERSION = 0x100,
ARG_DEPTH,
@@ -769,8 +758,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -781,10 +769,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_cpu_type = CPU_TIME;
else if (streq(optarg, "percentage"))
arg_cpu_type = CPU_PERCENT;
- else {
- log_error("Unknown argument to --cpu=: %s", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown argument to --cpu=: %s",
+ optarg);
} else
arg_cpu_type = CPU_TIME;
@@ -793,23 +781,25 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_DEPTH:
r = safe_atou(optarg, &arg_depth);
if (r < 0)
- return log_error_errno(r, "Failed to parse depth parameter: %s", optarg);
+ return log_error_errno(r, "Failed to parse depth parameter '%s': %m", optarg);
break;
case 'd':
r = parse_sec(optarg, &arg_delay);
- if (r < 0 || arg_delay <= 0) {
- log_error("Failed to parse delay parameter: %s", optarg);
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse delay parameter '%s': %m", optarg);
+ if (arg_delay <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid delay parameter '%s'",
+ optarg);
break;
case 'n':
r = safe_atou(optarg, &arg_iterations);
if (r < 0)
- return log_error_errno(r, "Failed to parse iterations parameter: %s", optarg);
+ return log_error_errno(r, "Failed to parse iterations parameter '%s': %m", optarg);
break;
@@ -856,10 +846,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_order = ORDER_MEMORY;
else if (streq(optarg, "io"))
arg_order = ORDER_IO;
- else {
- log_error("Invalid argument to --order=: %s", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid argument to --order=: %s",
+ optarg);
break;
case 'k':
@@ -873,7 +863,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_RECURSIVE:
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse --recursive= argument: %s", optarg);
+ return log_error_errno(r, "Failed to parse --recursive= argument '%s': %m", optarg);
arg_recursive = r;
arg_recursive_unset = r == 0;
@@ -892,10 +882,9 @@ static int parse_argv(int argc, char *argv[]) {
if (optind == argc - 1)
arg_root = argv[optind];
- else if (optind < argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
+ else if (optind < argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many arguments.");
return 1;
}
@@ -909,27 +898,27 @@ static const char* counting_what(void) {
return "userspace processes (excl. kernel)";
}
-int main(int argc, char *argv[]) {
- int r;
- Hashmap *a = NULL, *b = NULL;
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(group_hash_ops, char, path_hash_func, path_compare_func, Group, group_free);
+
+static int run(int argc, char *argv[]) {
+ _cleanup_hashmap_free_ Hashmap *a = NULL, *b = NULL;
unsigned iteration = 0;
usec_t last_refresh = 0;
bool quit = false, immediate_refresh = false;
_cleanup_free_ char *root = NULL;
CGroupMask mask;
+ int r;
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = cg_mask_supported(&mask);
- if (r < 0) {
- log_error_errno(r, "Failed to determine supported controllers: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine supported controllers: %m");
arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES;
@@ -939,18 +928,14 @@ int main(int argc, char *argv[]) {
}
r = show_cgroup_get_path_and_warn(arg_machine, arg_root, &root);
- if (r < 0) {
- log_error_errno(r, "Failed to get root control group path: %m");
- goto finish;
- } else
- log_debug("Cgroup path: %s", root);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get root control group path: %m");
+ log_debug("Cgroup path: %s", root);
- a = hashmap_new(&path_hash_ops);
- b = hashmap_new(&path_hash_ops);
- if (!a || !b) {
- r = log_oom();
- goto finish;
- }
+ a = hashmap_new(&group_hash_ops);
+ b = hashmap_new(&group_hash_ops);
+ if (!a || !b)
+ return log_oom();
signal(SIGWINCH, columns_lines_cache_reset);
@@ -958,7 +943,6 @@ int main(int argc, char *argv[]) {
arg_iterations = on_tty() ? 0 : 1;
while (!quit) {
- Hashmap *c;
usec_t t;
char key;
char h[FORMAT_TIMESPAN_MAX];
@@ -968,16 +952,11 @@ int main(int argc, char *argv[]) {
if (t >= last_refresh + arg_delay || immediate_refresh) {
r = refresh(root, a, b, iteration++);
- if (r < 0) {
- log_error_errno(r, "Failed to refresh: %m");
- goto finish;
- }
-
- group_hashmap_clear(b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to refresh: %m");
- c = a;
- a = b;
- b = c;
+ hashmap_clear(b);
+ SWAP_TWO(a, b);
last_refresh = t;
immediate_refresh = false;
@@ -998,10 +977,8 @@ int main(int argc, char *argv[]) {
r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL);
if (r == -ETIMEDOUT)
continue;
- if (r < 0) {
- log_error_errno(r, "Couldn't read key: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Couldn't read key: %m");
}
if (on_tty()) { /* TTY: Clear any user keystroke */
@@ -1121,11 +1098,7 @@ int main(int argc, char *argv[]) {
}
}
- r = 0;
-
-finish:
- group_hashmap_free(a);
- group_hashmap_free(b);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/core/automount.c b/src/core/automount.c
index 1b96a52c00..de8010bf2e 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -16,6 +16,7 @@
#include "bus-error.h"
#include "bus-util.h"
#include "dbus-automount.h"
+#include "dbus-unit.h"
#include "fd-util.h"
#include "format-util.h"
#include "io-util.h"
@@ -23,9 +24,11 @@
#include "mkdir.h"
#include "mount-util.h"
#include "mount.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "serialize.h"
#include "special.h"
#include "stdio-util.h"
#include "string-table.h"
@@ -85,7 +88,7 @@ static void unmount_autofs(Automount *a) {
a->pipe_fd = safe_close(a->pipe_fd);
/* If we reload/reexecute things we keep the mount point around */
- if (!IN_SET(UNIT(a)->manager->exit_code, MANAGER_RELOAD, MANAGER_REEXECUTE)) {
+ if (!IN_SET(UNIT(a)->manager->objective, MANAGER_RELOAD, MANAGER_REEXECUTE)) {
automount_send_ready(a, a->tokens, -EHOSTDOWN);
automount_send_ready(a, a->expire_tokens, -EHOSTDOWN);
@@ -149,7 +152,7 @@ static int automount_add_default_dependencies(Automount *a) {
if (!MANAGER_IS_SYSTEM(UNIT(a)->manager))
return 0;
- r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
@@ -235,6 +238,9 @@ static void automount_set_state(Automount *a, AutomountState state) {
AutomountState old_state;
assert(a);
+ if (a->state != state)
+ bus_unit_send_pending_change_signal(UNIT(a), false);
+
old_state = a->state;
a->state = state;
@@ -314,9 +320,7 @@ static void automount_enter_dead(Automount *a, AutomountResult f) {
if (a->result == AUTOMOUNT_SUCCESS)
a->result = f;
- if (a->result != AUTOMOUNT_SUCCESS)
- log_unit_warning(UNIT(a), "Failed with result '%s'.", automount_result_to_string(a->result));
-
+ unit_log_result(UNIT(a), a->result == AUTOMOUNT_SUCCESS, automount_result_to_string(a->result));
automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD);
}
@@ -841,16 +845,16 @@ static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", automount_state_to_string(a->state));
- unit_serialize_item(u, f, "result", automount_result_to_string(a->result));
- unit_serialize_item_format(u, f, "dev-id", "%u", (unsigned) a->dev_id);
+ (void) serialize_item(f, "state", automount_state_to_string(a->state));
+ (void) serialize_item(f, "result", automount_result_to_string(a->result));
+ (void) serialize_item_format(f, "dev-id", "%lu", (unsigned long) a->dev_id);
SET_FOREACH(p, a->tokens, i)
- unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p));
+ (void) serialize_item_format(f, "token", "%u", PTR_TO_UINT(p));
SET_FOREACH(p, a->expire_tokens, i)
- unit_serialize_item_format(u, f, "expire-token", "%u", PTR_TO_UINT(p));
+ (void) serialize_item_format(f, "expire-token", "%u", PTR_TO_UINT(p));
- r = unit_serialize_item_fd(u, f, fds, "pipe-fd", a->pipe_fd);
+ r = serialize_fd(f, fds, "pipe-fd", a->pipe_fd);
if (r < 0)
return r;
@@ -882,12 +886,13 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
a->result = f;
} else if (streq(key, "dev-id")) {
- unsigned d;
+ unsigned long d;
- if (safe_atou(value, &d) < 0)
+ if (safe_atolu(value, &d) < 0)
log_unit_debug(u, "Failed to parse dev-id value: %s", value);
else
- a->dev_id = (unsigned) d;
+ a->dev_id = (dev_t) d;
+
} else if (streq(key, "token")) {
unsigned token;
diff --git a/src/core/bpf-devices.c b/src/core/bpf-devices.c
new file mode 100644
index 0000000000..dade7f0490
--- /dev/null
+++ b/src/core/bpf-devices.c
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <linux/libbpf.h>
+
+#include "bpf-devices.h"
+#include "bpf-program.h"
+
+#define PASS_JUMP_OFF 4096
+
+static int bpf_access_type(const char *acc) {
+ int r = 0;
+
+ assert(acc);
+
+ for (; *acc; acc++)
+ switch(*acc) {
+ case 'r':
+ r |= BPF_DEVCG_ACC_READ;
+ break;
+ case 'w':
+ r |= BPF_DEVCG_ACC_WRITE;
+ break;
+ case 'm':
+ r |= BPF_DEVCG_ACC_MKNOD;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+int cgroup_bpf_whitelist_device(BPFProgram *prog, int type, int major, int minor, const char *acc) {
+ struct bpf_insn insn[] = {
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 6), /* compare device type */
+ BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
+ BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
+ BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 3), /* compare access type */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_4, major, 2), /* compare major */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_5, minor, 1), /* compare minor */
+ BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
+ };
+ int r, access;
+
+ assert(prog);
+ assert(acc);
+
+ access = bpf_access_type(acc);
+ if (access <= 0)
+ return -EINVAL;
+
+ insn[2].imm = access;
+
+ r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
+ if (r < 0)
+ log_error_errno(r, "Extending device control BPF program failed: %m");
+
+ return r;
+}
+
+int cgroup_bpf_whitelist_major(BPFProgram *prog, int type, int major, const char *acc) {
+ struct bpf_insn insn[] = {
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 5), /* compare device type */
+ BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
+ BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
+ BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 2), /* compare access type */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_4, major, 1), /* compare major */
+ BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
+ };
+ int r, access;
+
+ assert(prog);
+ assert(acc);
+
+ access = bpf_access_type(acc);
+ if (access <= 0)
+ return -EINVAL;
+
+ insn[2].imm = access;
+
+ r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
+ if (r < 0)
+ log_error_errno(r, "Extending device control BPF program failed: %m");
+
+ return r;
+}
+
+int cgroup_bpf_whitelist_class(BPFProgram *prog, int type, const char *acc) {
+ struct bpf_insn insn[] = {
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 5), /* compare device type */
+ BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
+ BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
+ BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 1), /* compare access type */
+ BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
+ };
+ int r, access;
+
+ assert(prog);
+ assert(acc);
+
+ access = bpf_access_type(acc);
+ if (access <= 0)
+ return -EINVAL;
+
+ insn[2].imm = access;
+
+ r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
+ if (r < 0)
+ log_error_errno(r, "Extending device control BPF program failed: %m");
+
+ return r;
+}
+
+int cgroup_init_device_bpf(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist) {
+ struct bpf_insn pre_insn[] = {
+ /* load device type to r2 */
+ BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
+ offsetof(struct bpf_cgroup_dev_ctx, access_type)),
+
+ /* load access type to r3 */
+ BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
+ offsetof(struct bpf_cgroup_dev_ctx, access_type)),
+ BPF_ALU32_IMM(BPF_RSH, BPF_REG_3, 16),
+
+ /* load major number to r4 */
+ BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1,
+ offsetof(struct bpf_cgroup_dev_ctx, major)),
+
+ /* load minor number to r5 */
+ BPF_LDX_MEM(BPF_W, BPF_REG_5, BPF_REG_1,
+ offsetof(struct bpf_cgroup_dev_ctx, minor)),
+ };
+
+ _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ int r;
+
+ assert(ret);
+
+ if (policy == CGROUP_AUTO && !whitelist)
+ return 0;
+
+ r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &prog);
+ if (r < 0)
+ return log_error_errno(r, "Loading device control BPF program failed: %m");
+
+ if (policy == CGROUP_CLOSED || whitelist) {
+ r = bpf_program_add_instructions(prog, pre_insn, ELEMENTSOF(pre_insn));
+ if (r < 0)
+ return log_error_errno(r, "Extending device control BPF program failed: %m");
+ }
+
+ *ret = TAKE_PTR(prog);
+
+ return 0;
+}
+
+int cgroup_apply_device_bpf(Unit *u, BPFProgram *prog, CGroupDevicePolicy policy, bool whitelist) {
+ struct bpf_insn post_insn[] = {
+ /* return DENY */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_JMP_A(1),
+
+ };
+
+ struct bpf_insn exit_insn[] = {
+ /* else return ALLOW */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN()
+ };
+
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ if (!prog) {
+ /* Remove existing program. */
+ u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
+ return 0;
+ }
+
+ if (policy != CGROUP_STRICT || whitelist) {
+ size_t off;
+
+ r = bpf_program_add_instructions(prog, post_insn, ELEMENTSOF(post_insn));
+ if (r < 0)
+ return log_error_errno(r, "Extending device control BPF program failed: %m");
+
+ /* Fixup PASS_JUMP_OFF jump offsets. */
+ for (off = 0; off < prog->n_instructions; off++) {
+ struct bpf_insn *ins = &prog->instructions[off];
+
+ if (ins->code == (BPF_JMP | BPF_JA) && ins->off == PASS_JUMP_OFF)
+ ins->off = prog->n_instructions - off - 1;
+ }
+ } else
+ /* Explicitly forbid everything. */
+ exit_insn[0].imm = 0;
+
+ r = bpf_program_add_instructions(prog, exit_insn, ELEMENTSOF(exit_insn));
+ if (r < 0)
+ return log_error_errno(r, "Extending device control BPF program failed: %m");
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine cgroup path: %m");
+
+
+ r = bpf_program_cgroup_attach(prog, BPF_CGROUP_DEVICE, path, BPF_F_ALLOW_MULTI);
+ if (r < 0)
+ return log_error_errno(r, "Attaching device control BPF program to cgroup %s failed: %m", path);
+
+ /* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */
+ u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
+
+ /* Remember that this BPF program is installed now. */
+ u->bpf_device_control_installed = bpf_program_ref(prog);
+
+ return 0;
+}
+
+int bpf_devices_supported(void) {
+ struct bpf_insn trivial[] = {
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN()
+ };
+
+ _cleanup_(bpf_program_unrefp) BPFProgram *program = NULL;
+ static int supported = -1;
+ int r;
+
+ /* Checks whether BPF device controller is supported. For this, we check five things:
+ *
+ * a) whether we are privileged
+ * b) whether the unified hierarchy is being used
+ * c) the BPF implementation in the kernel supports BPF_PROG_TYPE_CGROUP_DEVICE programs, which we require
+ */
+
+ if (supported >= 0)
+ return supported;
+
+ if (geteuid() != 0) {
+ log_debug("Not enough privileges, BPF device control is not supported.");
+ return supported = 0;
+ }
+
+ r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
+ if (r < 0)
+ return log_error_errno(r, "Can't determine whether the unified hierarchy is used: %m");
+ if (r == 0) {
+ log_debug("Not running with unified cgroups, BPF device control is not supported.");
+ return supported = 0;
+ }
+
+ r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &program);
+ if (r < 0) {
+ log_debug_errno(r, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
+ return supported = 0;
+ }
+
+ r = bpf_program_add_instructions(program, trivial, ELEMENTSOF(trivial));
+ if (r < 0) {
+ log_debug_errno(r, "Can't add trivial instructions to CGROUP DEVICE BPF program, BPF device control is not supported: %m");
+ return supported = 0;
+ }
+
+ r = bpf_program_load_kernel(program, NULL, 0);
+ if (r < 0) {
+ log_debug_errno(r, "Can't load kernel CGROUP DEVICE BPF program, BPF device control is not supported: %m");
+ return supported = 0;
+ }
+
+ return supported = 1;
+}
diff --git a/src/core/bpf-devices.h b/src/core/bpf-devices.h
new file mode 100644
index 0000000000..8d3de3bd94
--- /dev/null
+++ b/src/core/bpf-devices.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <inttypes.h>
+
+#include "unit.h"
+
+struct BPFProgram;
+
+int bpf_devices_supported(void);
+
+int cgroup_bpf_whitelist_device(BPFProgram *p, int type, int major, int minor, const char *acc);
+int cgroup_bpf_whitelist_major(BPFProgram *p, int type, int major, const char *acc);
+int cgroup_bpf_whitelist_class(BPFProgram *prog, int type, const char *acc);
+
+int cgroup_init_device_bpf(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist);
+int cgroup_apply_device_bpf(Unit *u, BPFProgram *p, CGroupDevicePolicy policy, bool whitelist);
diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c
index 8b66ef73dc..b9a611fd9e 100644
--- a/src/core/bpf-firewall.c
+++ b/src/core/bpf-firewall.c
@@ -20,6 +20,7 @@
#include "bpf-program.h"
#include "fd-util.h"
#include "ip-address-access.h"
+#include "missing_syscall.h"
#include "unit.h"
enum {
@@ -483,7 +484,7 @@ int bpf_firewall_compile(Unit *u) {
if (supported < 0)
return supported;
if (supported == BPF_FIREWALL_UNSUPPORTED) {
- log_debug("BPF firewalling not supported on this manager, proceeding without.");
+ log_unit_debug(u, "BPF firewalling not supported on this manager, proceeding without.");
return -EOPNOTSUPP;
}
if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI && u->type == UNIT_SLICE) {
@@ -492,7 +493,7 @@ int bpf_firewall_compile(Unit *u) {
* that BPF is more interesting on leaf nodes we hence avoid it on inner nodes in that case. This is
* consistent with old systemd behaviour from before v238, where BPF wasn't supported in inner nodes at
* all, either. */
- log_debug("BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
+ log_unit_debug(u, "BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
return -EOPNOTSUPP;
}
@@ -518,24 +519,24 @@ int bpf_firewall_compile(Unit *u) {
r = bpf_firewall_prepare_access_maps(u, ACCESS_ALLOWED, &u->ipv4_allow_map_fd, &u->ipv6_allow_map_fd);
if (r < 0)
- return log_error_errno(r, "Preparation of eBPF allow maps failed: %m");
+ return log_unit_error_errno(u, r, "Preparation of eBPF allow maps failed: %m");
r = bpf_firewall_prepare_access_maps(u, ACCESS_DENIED, &u->ipv4_deny_map_fd, &u->ipv6_deny_map_fd);
if (r < 0)
- return log_error_errno(r, "Preparation of eBPF deny maps failed: %m");
+ return log_unit_error_errno(u, r, "Preparation of eBPF deny maps failed: %m");
}
r = bpf_firewall_prepare_accounting_maps(u, cc->ip_accounting, &u->ip_accounting_ingress_map_fd, &u->ip_accounting_egress_map_fd);
if (r < 0)
- return log_error_errno(r, "Preparation of eBPF accounting maps failed: %m");
+ return log_unit_error_errno(u, r, "Preparation of eBPF accounting maps failed: %m");
r = bpf_firewall_compile_bpf(u, true, &u->ip_bpf_ingress);
if (r < 0)
- return log_error_errno(r, "Compilation for ingress BPF program failed: %m");
+ return log_unit_error_errno(u, r, "Compilation for ingress BPF program failed: %m");
r = bpf_firewall_compile_bpf(u, false, &u->ip_bpf_egress);
if (r < 0)
- return log_error_errno(r, "Compilation for egress BPF program failed: %m");
+ return log_unit_error_errno(u, r, "Compilation for egress BPF program failed: %m");
return 0;
}
@@ -560,17 +561,17 @@ int bpf_firewall_install(Unit *u) {
if (supported < 0)
return supported;
if (supported == BPF_FIREWALL_UNSUPPORTED) {
- log_debug("BPF firewalling not supported on this manager, proceeding without.");
+ log_unit_debug(u, "BPF firewalling not supported on this manager, proceeding without.");
return -EOPNOTSUPP;
}
if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI && u->type == UNIT_SLICE) {
- log_debug("BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
+ log_unit_debug(u, "BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
return -EOPNOTSUPP;
}
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &path);
if (r < 0)
- return log_error_errno(r, "Failed to determine cgroup path: %m");
+ return log_unit_error_errno(u, r, "Failed to determine cgroup path: %m");
flags = (supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI &&
(u->type == UNIT_SLICE || unit_cgroup_delegate(u))) ? BPF_F_ALLOW_MULTI : 0;
@@ -583,7 +584,7 @@ int bpf_firewall_install(Unit *u) {
if (u->ip_bpf_egress) {
r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, flags);
if (r < 0)
- return log_error_errno(r, "Attaching egress BPF program to cgroup %s failed: %m", path);
+ return log_unit_error_errno(u, r, "Attaching egress BPF program to cgroup %s failed: %m", path);
/* Remember that this BPF program is installed now. */
u->ip_bpf_egress_installed = bpf_program_ref(u->ip_bpf_egress);
@@ -592,7 +593,7 @@ int bpf_firewall_install(Unit *u) {
if (u->ip_bpf_ingress) {
r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, flags);
if (r < 0)
- return log_error_errno(r, "Attaching ingress BPF program to cgroup %s failed: %m", path);
+ return log_unit_error_errno(u, r, "Attaching ingress BPF program to cgroup %s failed: %m", path);
u->ip_bpf_ingress_installed = bpf_program_ref(u->ip_bpf_ingress);
}
@@ -660,8 +661,7 @@ int bpf_firewall_supported(void) {
* b) whether the unified hierarchy is being used
* c) the BPF implementation in the kernel supports BPF LPM TRIE maps, which we require
* d) the BPF implementation in the kernel supports BPF_PROG_TYPE_CGROUP_SKB programs, which we require
- * e) the BPF implementation in the kernel supports the BPF_PROG_ATTACH call, which we require
- *
+ * e) the BPF implementation in the kernel supports the BPF_PROG_DETACH call, which we require
*/
if (supported >= 0)
@@ -714,7 +714,7 @@ int bpf_firewall_supported(void) {
* is turned off at kernel compilation time. This sucks of course: why does it allow us to create a cgroup BPF
* program if we can't do a thing with it later?
*
- * We detect this case by issuing the BPF_PROG_ATTACH bpf() call with invalid file descriptors: if
+ * We detect this case by issuing the BPF_PROG_DETACH bpf() call with invalid file descriptors: if
* CONFIG_CGROUP_BPF is turned off, then the call will fail early with EINVAL. If it is turned on the
* parameters are validated however, and that'll fail with EBADF then. */
@@ -724,22 +724,22 @@ int bpf_firewall_supported(void) {
.attach_bpf_fd = -1,
};
- if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0) {
+ if (bpf(BPF_PROG_DETACH, &attr, sizeof(attr)) < 0) {
if (errno != EBADF) {
- log_debug_errno(errno, "Didn't get EBADF from BPF_PROG_ATTACH, BPF firewalling is not supported: %m");
+ log_debug_errno(errno, "Didn't get EBADF from BPF_PROG_DETACH, BPF firewalling is not supported: %m");
return supported = BPF_FIREWALL_UNSUPPORTED;
}
/* YAY! */
} else {
- log_debug("Wut? Kernel accepted our invalid BPF_PROG_ATTACH call? Something is weird, assuming BPF firewalling is broken and hence not supported.");
+ log_debug("Wut? Kernel accepted our invalid BPF_PROG_DETACH call? Something is weird, assuming BPF firewalling is broken and hence not supported.");
return supported = BPF_FIREWALL_UNSUPPORTED;
}
/* So now we know that the BPF program is generally available, let's see if BPF_F_ALLOW_MULTI is also supported
- * (which was added in kernel 4.15). We use a similar logic as before, but this time we use
- * BPF_F_ALLOW_MULTI. Since the flags are checked early in the system call we'll get EINVAL if it's not
- * supported, and EBADF as before if it is available. */
+ * (which was added in kernel 4.15). We use a similar logic as before, but this time we use the BPF_PROG_ATTACH
+ * bpf() call and the BPF_F_ALLOW_MULTI flags value. Since the flags are checked early in the system call we'll
+ * get EINVAL if it's not supported, and EBADF as before if it is available. */
attr = (union bpf_attr) {
.attach_type = BPF_CGROUP_INET_EGRESS,
diff --git a/src/core/bpf-firewall.h b/src/core/bpf-firewall.h
index e2d08a0fc8..7d38483dbd 100644
--- a/src/core/bpf-firewall.h
+++ b/src/core/bpf-firewall.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <inttypes.h>
#include "unit.h"
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index bb02436203..a7ce3fceaa 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -7,6 +7,7 @@
#include "blockdev-util.h"
#include "bpf-firewall.h"
#include "btrfs-util.h"
+#include "bpf-devices.h"
#include "bus-error.h"
#include "cgroup-util.h"
#include "cgroup.h"
@@ -18,6 +19,7 @@
#include "process-util.h"
#include "procfs-util.h"
#include "special.h"
+#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
@@ -25,7 +27,12 @@
#define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC)
-bool manager_owns_root_cgroup(Manager *m) {
+/* Returns the log level to use when cgroup attribute writes fail. When an attribute is missing or we have access
+ * problems we downgrade to LOG_DEBUG. This is supposed to be nice to container managers and kernels which want to mask
+ * out specific attributes from us. */
+#define LOG_LEVEL_CGROUP_WRITE(r) (IN_SET(abs(r), ENOENT, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING)
+
+bool manager_owns_host_root_cgroup(Manager *m) {
assert(m);
/* Returns true if we are managing the root cgroup. Note that it isn't sufficient to just check whether the
@@ -33,24 +40,38 @@ bool manager_owns_root_cgroup(Manager *m) {
* appears to be no nice way to detect whether we are in a CLONE_NEWCGROUP namespace we instead just check if
* we run in any kind of container virtualization. */
+ if (MANAGER_IS_USER(m))
+ return false;
+
if (detect_container() > 0)
return false;
return empty_or_root(m->cgroup_root);
}
-bool unit_has_root_cgroup(Unit *u) {
+bool unit_has_host_root_cgroup(Unit *u) {
assert(u);
/* Returns whether this unit manages the root cgroup. This will return true if this unit is the root slice and
* the manager manages the root cgroup. */
- if (!manager_owns_root_cgroup(u->manager))
+ if (!manager_owns_host_root_cgroup(u->manager))
return false;
return unit_has_name(u, SPECIAL_ROOT_SLICE);
}
+static int set_attribute_and_warn(Unit *u, const char *controller, const char *attribute, const char *value) {
+ int r;
+
+ r = cg_set_attribute(controller, u->cgroup_path, attribute, value);
+ if (r < 0)
+ log_unit_full(u, LOG_LEVEL_CGROUP_WRITE(r), r, "Failed to set '%s' attribute on '%s' to '%.*s': %m",
+ strna(attribute), isempty(u->cgroup_path) ? "/" : u->cgroup_path, (int) strcspn(value, NEWLINE), value);
+
+ return r;
+}
+
static void cgroup_compat_warn(void) {
static bool cgroup_compat_warned = false;
@@ -71,29 +92,30 @@ static void cgroup_compat_warn(void) {
void cgroup_context_init(CGroupContext *c) {
assert(c);
- /* Initialize everything to the kernel defaults, assuming the
- * structure is preinitialized to 0 */
+ /* Initialize everything to the kernel defaults. */
- c->cpu_weight = CGROUP_WEIGHT_INVALID;
- c->startup_cpu_weight = CGROUP_WEIGHT_INVALID;
- c->cpu_quota_per_sec_usec = USEC_INFINITY;
+ *c = (CGroupContext) {
+ .cpu_weight = CGROUP_WEIGHT_INVALID,
+ .startup_cpu_weight = CGROUP_WEIGHT_INVALID,
+ .cpu_quota_per_sec_usec = USEC_INFINITY,
- c->cpu_shares = CGROUP_CPU_SHARES_INVALID;
- c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID;
+ .cpu_shares = CGROUP_CPU_SHARES_INVALID,
+ .startup_cpu_shares = CGROUP_CPU_SHARES_INVALID,
- c->memory_high = CGROUP_LIMIT_MAX;
- c->memory_max = CGROUP_LIMIT_MAX;
- c->memory_swap_max = CGROUP_LIMIT_MAX;
+ .memory_high = CGROUP_LIMIT_MAX,
+ .memory_max = CGROUP_LIMIT_MAX,
+ .memory_swap_max = CGROUP_LIMIT_MAX,
- c->memory_limit = CGROUP_LIMIT_MAX;
+ .memory_limit = CGROUP_LIMIT_MAX,
- c->io_weight = CGROUP_WEIGHT_INVALID;
- c->startup_io_weight = CGROUP_WEIGHT_INVALID;
+ .io_weight = CGROUP_WEIGHT_INVALID,
+ .startup_io_weight = CGROUP_WEIGHT_INVALID,
- c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID;
- c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID;
+ .blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID,
+ .startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID,
- c->tasks_max = (uint64_t) -1;
+ .tasks_max = CGROUP_LIMIT_MAX,
+ };
}
void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
@@ -114,6 +136,15 @@ void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight
free(w);
}
+void cgroup_context_free_io_device_latency(CGroupContext *c, CGroupIODeviceLatency *l) {
+ assert(c);
+ assert(l);
+
+ LIST_REMOVE(device_latencies, c->io_device_latencies, l);
+ free(l->path);
+ free(l);
+}
+
void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l) {
assert(c);
assert(l);
@@ -147,6 +178,9 @@ void cgroup_context_done(CGroupContext *c) {
while (c->io_device_weights)
cgroup_context_free_io_device_weight(c, c->io_device_weights);
+ while (c->io_device_latencies)
+ cgroup_context_free_io_device_latency(c, c->io_device_latencies);
+
while (c->io_device_limits)
cgroup_context_free_io_device_limit(c, c->io_device_limits);
@@ -166,6 +200,7 @@ void cgroup_context_done(CGroupContext *c) {
void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
CGroupIODeviceLimit *il;
CGroupIODeviceWeight *iw;
+ CGroupIODeviceLatency *l;
CGroupBlockIODeviceBandwidth *b;
CGroupBlockIODeviceWeight *w;
CGroupDeviceAllow *a;
@@ -193,6 +228,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
"%sStartupIOWeight=%" PRIu64 "\n"
"%sBlockIOWeight=%" PRIu64 "\n"
"%sStartupBlockIOWeight=%" PRIu64 "\n"
+ "%sMemoryMin=%" PRIu64 "\n"
"%sMemoryLow=%" PRIu64 "\n"
"%sMemoryHigh=%" PRIu64 "\n"
"%sMemoryMax=%" PRIu64 "\n"
@@ -216,6 +252,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
prefix, c->startup_io_weight,
prefix, c->blockio_weight,
prefix, c->startup_blockio_weight,
+ prefix, c->memory_min,
prefix, c->memory_low,
prefix, c->memory_high,
prefix, c->memory_max,
@@ -244,11 +281,18 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
LIST_FOREACH(device_weights, iw, c->io_device_weights)
fprintf(f,
- "%sIODeviceWeight=%s %" PRIu64,
+ "%sIODeviceWeight=%s %" PRIu64 "\n",
prefix,
iw->path,
iw->weight);
+ LIST_FOREACH(device_latencies, l, c->io_device_latencies)
+ fprintf(f,
+ "%sIODeviceLatencyTargetSec=%s %s\n",
+ prefix,
+ l->path,
+ format_timespan(u, sizeof(u), l->target_usec, 1));
+
LIST_FOREACH(device_limits, il, c->io_device_limits) {
char buf[FORMAT_BYTES_MAX];
CGroupIOLimitType type;
@@ -302,17 +346,73 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
}
}
+int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) {
+ _cleanup_free_ CGroupDeviceAllow *a = NULL;
+ _cleanup_free_ char *d = NULL;
+
+ assert(c);
+ assert(dev);
+ assert(isempty(mode) || in_charset(mode, "rwm"));
+
+ a = new(CGroupDeviceAllow, 1);
+ if (!a)
+ return -ENOMEM;
+
+ d = strdup(dev);
+ if (!d)
+ return -ENOMEM;
+
+ *a = (CGroupDeviceAllow) {
+ .path = TAKE_PTR(d),
+ .r = isempty(mode) || strchr(mode, 'r'),
+ .w = isempty(mode) || strchr(mode, 'w'),
+ .m = isempty(mode) || strchr(mode, 'm'),
+ };
+
+ LIST_PREPEND(device_allow, c->device_allow, a);
+ TAKE_PTR(a);
+
+ return 0;
+}
+
+static void cgroup_xattr_apply(Unit *u) {
+ char ids[SD_ID128_STRING_MAX];
+ int r;
+
+ assert(u);
+
+ if (!MANAGER_IS_SYSTEM(u->manager))
+ return;
+
+ if (sd_id128_is_null(u->invocation_id))
+ return;
+
+ r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
+ "trusted.invocation_id",
+ sd_id128_to_string(u->invocation_id, ids), 32,
+ 0);
+ if (r < 0)
+ log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", u->cgroup_path);
+}
+
static int lookup_block_device(const char *p, dev_t *ret) {
- struct stat st;
+ struct stat st = {};
int r;
assert(p);
assert(ret);
- if (stat(p, &st) < 0)
- return log_warning_errno(errno, "Couldn't stat device '%s': %m", p);
-
- if (S_ISBLK(st.st_mode))
+ r = device_path_parse_major_minor(p, &st.st_mode, &st.st_rdev);
+ if (r == -ENODEV) { /* not a parsable device node, need to go to disk */
+ if (stat(p, &st) < 0)
+ return log_warning_errno(errno, "Couldn't stat device '%s': %m", p);
+ } else if (r < 0)
+ return log_warning_errno(r, "Failed to parse major/minor from path '%s': %m", p);
+
+ if (S_ISCHR(st.st_mode)) {
+ log_warning("Device node '%s' is a character device, but block device needed.", p);
+ return -ENOTBLK;
+ } else if (S_ISBLK(st.st_mode))
*ret = st.st_rdev;
else if (major(st.st_dev) != 0)
*ret = st.st_dev; /* If this is not a device node then use the block device this file is stored on */
@@ -335,67 +435,123 @@ static int lookup_block_device(const char *p, dev_t *ret) {
return 0;
}
-static int whitelist_device(const char *path, const char *node, const char *acc) {
- char buf[2+DECIMAL_STR_MAX(dev_t)*2+2+4];
- struct stat st;
- bool ignore_notfound;
+static int whitelist_device(BPFProgram *prog, const char *path, const char *node, const char *acc) {
+ struct stat st = {};
int r;
assert(path);
assert(acc);
- if (node[0] == '-') {
- /* Non-existent paths starting with "-" must be silently ignored */
- node++;
- ignore_notfound = true;
- } else
- ignore_notfound = false;
+ /* Some special handling for /dev/block/%u:%u, /dev/char/%u:%u, /run/systemd/inaccessible/chr and
+ * /run/systemd/inaccessible/blk paths. Instead of stat()ing these we parse out the major/minor directly. This
+ * means clients can use these path without the device node actually around */
+ r = device_path_parse_major_minor(node, &st.st_mode, &st.st_rdev);
+ if (r < 0) {
+ if (r != -ENODEV)
+ return log_warning_errno(r, "Couldn't parse major/minor from device path '%s': %m", node);
- if (stat(node, &st) < 0) {
- if (errno == ENOENT && ignore_notfound)
- return 0;
+ if (stat(node, &st) < 0)
+ return log_warning_errno(errno, "Couldn't stat device %s: %m", node);
- return log_warning_errno(errno, "Couldn't stat device %s: %m", node);
+ if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+ log_warning("%s is not a device.", node);
+ return -ENODEV;
+ }
}
- if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
- log_warning("%s is not a device.", node);
- return -ENODEV;
- }
+ if (cg_all_unified() > 0) {
+ if (!prog)
+ return 0;
- sprintf(buf,
- "%c %u:%u %s",
- S_ISCHR(st.st_mode) ? 'c' : 'b',
- major(st.st_rdev), minor(st.st_rdev),
- acc);
+ return cgroup_bpf_whitelist_device(prog, S_ISCHR(st.st_mode) ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK,
+ major(st.st_rdev), minor(st.st_rdev), acc);
- r = cg_set_attribute("devices", path, "devices.allow", buf);
- if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set devices.allow on %s: %m", path);
+ } else {
+ char buf[2+DECIMAL_STR_MAX(dev_t)*2+2+4];
- return r;
+ sprintf(buf,
+ "%c %u:%u %s",
+ S_ISCHR(st.st_mode) ? 'c' : 'b',
+ major(st.st_rdev), minor(st.st_rdev),
+ acc);
+
+ /* Changing the devices list of a populated cgroup might result in EINVAL, hence ignore EINVAL here. */
+
+ r = cg_set_attribute("devices", path, "devices.allow", buf);
+ if (r < 0)
+ return log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING,
+ r, "Failed to set devices.allow on %s: %m", path);
+
+ return 0;
+ }
}
-static int whitelist_major(const char *path, const char *name, char type, const char *acc) {
+static int whitelist_major(BPFProgram *prog, const char *path, const char *name, char type, const char *acc) {
_cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
+ char buf[2+DECIMAL_STR_MAX(unsigned)+3+4];
bool good = false;
+ unsigned maj;
int r;
assert(path);
assert(acc);
assert(IN_SET(type, 'b', 'c'));
+ if (streq(name, "*")) {
+ /* If the name is a wildcard, then apply this list to all devices of this type */
+
+ if (cg_all_unified() > 0) {
+ if (!prog)
+ return 0;
+
+ (void) cgroup_bpf_whitelist_class(prog, type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK, acc);
+ } else {
+ xsprintf(buf, "%c *:* %s", type, acc);
+
+ r = cg_set_attribute("devices", path, "devices.allow", buf);
+ if (r < 0)
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set devices.allow on %s: %m", path);
+ return 0;
+ }
+ }
+
+ if (safe_atou(name, &maj) >= 0 && DEVICE_MAJOR_VALID(maj)) {
+ /* The name is numeric and suitable as major. In that case, let's take is major, and create the entry
+ * directly */
+
+ if (cg_all_unified() > 0) {
+ if (!prog)
+ return 0;
+
+ (void) cgroup_bpf_whitelist_major(prog,
+ type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK,
+ maj, acc);
+ } else {
+ xsprintf(buf, "%c %u:* %s", type, maj, acc);
+
+ r = cg_set_attribute("devices", path, "devices.allow", buf);
+ if (r < 0)
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set devices.allow on %s: %m", path);
+ }
+
+ return 0;
+ }
+
f = fopen("/proc/devices", "re");
if (!f)
return log_warning_errno(errno, "Cannot open /proc/devices to resolve %s (%c): %m", name, type);
- FOREACH_LINE(line, f, goto fail) {
- char buf[2+DECIMAL_STR_MAX(unsigned)+3+4], *p, *w;
- unsigned maj;
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ char *w, *p;
- truncate_nl(line);
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to read /proc/devices: %m");
+ if (r == 0)
+ break;
if (type == 'c' && streq(line, "Character devices:")) {
good = true;
@@ -434,22 +590,31 @@ static int whitelist_major(const char *path, const char *name, char type, const
if (fnmatch(name, w, 0) != 0)
continue;
- sprintf(buf,
- "%c %u:* %s",
- type,
- maj,
- acc);
+ if (cg_all_unified() > 0) {
+ if (!prog)
+ continue;
- r = cg_set_attribute("devices", path, "devices.allow", buf);
- if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set devices.allow on %s: %m", path);
+ (void) cgroup_bpf_whitelist_major(prog,
+ type == 'c' ? BPF_DEVCG_DEV_CHAR : BPF_DEVCG_DEV_BLOCK,
+ maj, acc);
+ } else {
+ sprintf(buf,
+ "%c %u:* %s",
+ type,
+ maj,
+ acc);
+
+ /* Changing the devices list of a populated cgroup might result in EINVAL, hence ignore EINVAL
+ * here. */
+
+ r = cg_set_attribute("devices", path, "devices.allow", buf);
+ if (r < 0)
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING,
+ r, "Failed to set devices.allow on %s: %m", path);
+ }
}
return 0;
-
-fail:
- return log_warning_errno(errno, "Failed to read /proc/devices: %m");
}
static bool cgroup_context_has_cpu_weight(CGroupContext *c) {
@@ -482,53 +647,42 @@ static uint64_t cgroup_context_cpu_shares(CGroupContext *c, ManagerState state)
return CGROUP_CPU_SHARES_DEFAULT;
}
-static void cgroup_apply_unified_cpu_config(Unit *u, uint64_t weight, uint64_t quota) {
- char buf[MAX(DECIMAL_STR_MAX(uint64_t) + 1, (DECIMAL_STR_MAX(usec_t) + 1) * 2)];
- int r;
+static void cgroup_apply_unified_cpu_weight(Unit *u, uint64_t weight) {
+ char buf[DECIMAL_STR_MAX(uint64_t) + 2];
xsprintf(buf, "%" PRIu64 "\n", weight);
- r = cg_set_attribute("cpu", u->cgroup_path, "cpu.weight", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.weight: %m");
+ (void) set_attribute_and_warn(u, "cpu", "cpu.weight", buf);
+}
+
+static void cgroup_apply_unified_cpu_quota(Unit *u, usec_t quota) {
+ char buf[(DECIMAL_STR_MAX(usec_t) + 1) * 2 + 1];
if (quota != USEC_INFINITY)
xsprintf(buf, USEC_FMT " " USEC_FMT "\n",
quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC, CGROUP_CPU_QUOTA_PERIOD_USEC);
else
xsprintf(buf, "max " USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
-
- r = cg_set_attribute("cpu", u->cgroup_path, "cpu.max", buf);
-
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.max: %m");
+ (void) set_attribute_and_warn(u, "cpu", "cpu.max", buf);
}
-static void cgroup_apply_legacy_cpu_config(Unit *u, uint64_t shares, uint64_t quota) {
- char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1];
- int r;
+static void cgroup_apply_legacy_cpu_shares(Unit *u, uint64_t shares) {
+ char buf[DECIMAL_STR_MAX(uint64_t) + 2];
xsprintf(buf, "%" PRIu64 "\n", shares);
- r = cg_set_attribute("cpu", u->cgroup_path, "cpu.shares", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.shares: %m");
+ (void) set_attribute_and_warn(u, "cpu", "cpu.shares", buf);
+}
+
+static void cgroup_apply_legacy_cpu_quota(Unit *u, usec_t quota) {
+ char buf[DECIMAL_STR_MAX(usec_t) + 2];
xsprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
- r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_period_us", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.cfs_period_us: %m");
+ (void) set_attribute_and_warn(u, "cpu", "cpu.cfs_period_us", buf);
if (quota != USEC_INFINITY) {
xsprintf(buf, USEC_FMT "\n", quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC);
- r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", buf);
+ (void) set_attribute_and_warn(u, "cpu", "cpu.cfs_quota_us", buf);
} else
- r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", "-1");
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.cfs_quota_us: %m");
+ (void) set_attribute_and_warn(u, "cpu", "cpu.cfs_quota_us", "-1\n");
}
static uint64_t cgroup_cpu_shares_to_weight(uint64_t shares) {
@@ -546,6 +700,7 @@ static bool cgroup_context_has_io_config(CGroupContext *c) {
c->io_weight != CGROUP_WEIGHT_INVALID ||
c->startup_io_weight != CGROUP_WEIGHT_INVALID ||
c->io_device_weights ||
+ c->io_device_latencies ||
c->io_device_limits;
}
@@ -597,10 +752,7 @@ static void cgroup_apply_io_device_weight(Unit *u, const char *dev_path, uint64_
return;
xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), io_weight);
- r = cg_set_attribute("io", u->cgroup_path, "io.weight", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set io.weight: %m");
+ (void) set_attribute_and_warn(u, "io", "io.weight", buf);
}
static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint64_t blkio_weight) {
@@ -613,10 +765,24 @@ static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint
return;
xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), blkio_weight);
- r = cg_set_attribute("blkio", u->cgroup_path, "blkio.weight_device", buf);
+ (void) set_attribute_and_warn(u, "blkio", "blkio.weight_device", buf);
+}
+
+static void cgroup_apply_io_device_latency(Unit *u, const char *dev_path, usec_t target) {
+ char buf[DECIMAL_STR_MAX(dev_t)*2+2+7+DECIMAL_STR_MAX(uint64_t)+1];
+ dev_t dev;
+ int r;
+
+ r = lookup_block_device(dev_path, &dev);
if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set blkio.weight_device: %m");
+ return;
+
+ if (target != USEC_INFINITY)
+ xsprintf(buf, "%u:%u target=%" PRIu64 "\n", major(dev), minor(dev), target);
+ else
+ xsprintf(buf, "%u:%u target=max\n", major(dev), minor(dev));
+
+ (void) set_attribute_and_warn(u, "io", "io.latency", buf);
}
static void cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t *limits) {
@@ -639,10 +805,7 @@ static void cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t
xsprintf(buf, "%u:%u rbps=%s wbps=%s riops=%s wiops=%s\n", major(dev), minor(dev),
limit_bufs[CGROUP_IO_RBPS_MAX], limit_bufs[CGROUP_IO_WBPS_MAX],
limit_bufs[CGROUP_IO_RIOPS_MAX], limit_bufs[CGROUP_IO_WIOPS_MAX]);
- r = cg_set_attribute("io", u->cgroup_path, "io.max", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set io.max: %m");
+ (void) set_attribute_and_warn(u, "io", "io.max", buf);
}
static void cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint64_t rbps, uint64_t wbps) {
@@ -655,33 +818,23 @@ static void cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint6
return;
sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), rbps);
- r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.read_bps_device", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set blkio.throttle.read_bps_device: %m");
+ (void) set_attribute_and_warn(u, "blkio", "blkio.throttle.read_bps_device", buf);
sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), wbps);
- r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.write_bps_device", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set blkio.throttle.write_bps_device: %m");
+ (void) set_attribute_and_warn(u, "blkio", "blkio.throttle.write_bps_device", buf);
}
static bool cgroup_context_has_unified_memory_config(CGroupContext *c) {
- return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX;
+ return c->memory_min > 0 || c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX;
}
static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) {
- char buf[DECIMAL_STR_MAX(uint64_t) + 1] = "max";
- int r;
+ char buf[DECIMAL_STR_MAX(uint64_t) + 1] = "max\n";
if (v != CGROUP_LIMIT_MAX)
xsprintf(buf, "%" PRIu64 "\n", v);
- r = cg_set_attribute("memory", u->cgroup_path, file, buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set %s: %m", file);
+ (void) set_attribute_and_warn(u, "memory", file, buf);
}
static void cgroup_apply_firewall(Unit *u) {
@@ -698,130 +851,133 @@ static void cgroup_apply_firewall(Unit *u) {
static void cgroup_context_apply(
Unit *u,
CGroupMask apply_mask,
- bool apply_bpf,
ManagerState state) {
const char *path;
CGroupContext *c;
- bool is_root;
+ bool is_host_root, is_local_root;
int r;
assert(u);
/* Nothing to do? Exit early! */
- if (apply_mask == 0 && !apply_bpf)
+ if (apply_mask == 0)
return;
- /* Some cgroup attributes are not supported on the root cgroup, hence silently ignore */
- is_root = unit_has_root_cgroup(u);
+ /* Some cgroup attributes are not supported on the host root cgroup, hence silently ignore them here. And other
+ * attributes should only be managed for cgroups further down the tree. */
+ is_local_root = unit_has_name(u, SPECIAL_ROOT_SLICE);
+ is_host_root = unit_has_host_root_cgroup(u);
assert_se(c = unit_get_cgroup_context(u));
assert_se(path = u->cgroup_path);
- if (is_root) /* Make sure we don't try to display messages with an empty path. */
+ if (is_local_root) /* Make sure we don't try to display messages with an empty path. */
path = "/";
- /* We generally ignore errors caused by read-only mounted
- * cgroup trees (assuming we are running in a container then),
- * and missing cgroups, i.e. EROFS and ENOENT. */
-
- if ((apply_mask & CGROUP_MASK_CPU) && !is_root) {
- bool has_weight, has_shares;
+ /* We generally ignore errors caused by read-only mounted cgroup trees (assuming we are running in a container
+ * then), and missing cgroups, i.e. EROFS and ENOENT. */
- has_weight = cgroup_context_has_cpu_weight(c);
- has_shares = cgroup_context_has_cpu_shares(c);
+ /* In fully unified mode these attributes don't exist on the host cgroup root. On legacy the weights exist, but
+ * setting the weight makes very little sense on the host root cgroup, as there are no other cgroups at this
+ * level. The quota exists there too, but any attempt to write to it is refused with EINVAL. Inside of
+ * containers we want to leave control of these to the container manager (and if cgroupsv2 delegation is used
+ * we couldn't even write to them if we wanted to). */
+ if ((apply_mask & CGROUP_MASK_CPU) && !is_local_root) {
if (cg_all_unified() > 0) {
uint64_t weight;
- if (has_weight)
+ if (cgroup_context_has_cpu_weight(c))
weight = cgroup_context_cpu_weight(c, state);
- else if (has_shares) {
- uint64_t shares = cgroup_context_cpu_shares(c, state);
+ else if (cgroup_context_has_cpu_shares(c)) {
+ uint64_t shares;
+ shares = cgroup_context_cpu_shares(c, state);
weight = cgroup_cpu_shares_to_weight(shares);
- log_cgroup_compat(u, "Applying [Startup]CpuShares %" PRIu64 " as [Startup]CpuWeight %" PRIu64 " on %s",
+ log_cgroup_compat(u, "Applying [Startup]CPUShares=%" PRIu64 " as [Startup]CPUWeight=%" PRIu64 " on %s",
shares, weight, path);
} else
weight = CGROUP_WEIGHT_DEFAULT;
- cgroup_apply_unified_cpu_config(u, weight, c->cpu_quota_per_sec_usec);
+ cgroup_apply_unified_cpu_weight(u, weight);
+ cgroup_apply_unified_cpu_quota(u, c->cpu_quota_per_sec_usec);
+
} else {
uint64_t shares;
- if (has_weight) {
- uint64_t weight = cgroup_context_cpu_weight(c, state);
+ if (cgroup_context_has_cpu_weight(c)) {
+ uint64_t weight;
+ weight = cgroup_context_cpu_weight(c, state);
shares = cgroup_cpu_weight_to_shares(weight);
- log_cgroup_compat(u, "Applying [Startup]CpuWeight %" PRIu64 " as [Startup]CpuShares %" PRIu64 " on %s",
+ log_cgroup_compat(u, "Applying [Startup]CPUWeight=%" PRIu64 " as [Startup]CPUShares=%" PRIu64 " on %s",
weight, shares, path);
- } else if (has_shares)
+ } else if (cgroup_context_has_cpu_shares(c))
shares = cgroup_context_cpu_shares(c, state);
else
shares = CGROUP_CPU_SHARES_DEFAULT;
- cgroup_apply_legacy_cpu_config(u, shares, c->cpu_quota_per_sec_usec);
+ cgroup_apply_legacy_cpu_shares(u, shares);
+ cgroup_apply_legacy_cpu_quota(u, c->cpu_quota_per_sec_usec);
}
}
- if (apply_mask & CGROUP_MASK_IO) {
- bool has_io = cgroup_context_has_io_config(c);
- bool has_blockio = cgroup_context_has_blockio_config(c);
+ /* The 'io' controller attributes are not exported on the host's root cgroup (being a pure cgroupsv2
+ * controller), and in case of containers we want to leave control of these attributes to the container manager
+ * (and we couldn't access that stuff anyway, even if we tried if proper delegation is used). */
+ if ((apply_mask & CGROUP_MASK_IO) && !is_local_root) {
+ char buf[8+DECIMAL_STR_MAX(uint64_t)+1];
+ bool has_io, has_blockio;
+ uint64_t weight;
- if (!is_root) {
- char buf[8+DECIMAL_STR_MAX(uint64_t)+1];
- uint64_t weight;
+ has_io = cgroup_context_has_io_config(c);
+ has_blockio = cgroup_context_has_blockio_config(c);
- if (has_io)
- weight = cgroup_context_io_weight(c, state);
- else if (has_blockio) {
- uint64_t blkio_weight = cgroup_context_blkio_weight(c, state);
+ if (has_io)
+ weight = cgroup_context_io_weight(c, state);
+ else if (has_blockio) {
+ uint64_t blkio_weight;
- weight = cgroup_weight_blkio_to_io(blkio_weight);
+ blkio_weight = cgroup_context_blkio_weight(c, state);
+ weight = cgroup_weight_blkio_to_io(blkio_weight);
- log_cgroup_compat(u, "Applying [Startup]BlockIOWeight %" PRIu64 " as [Startup]IOWeight %" PRIu64,
- blkio_weight, weight);
- } else
- weight = CGROUP_WEIGHT_DEFAULT;
+ log_cgroup_compat(u, "Applying [Startup]BlockIOWeight=%" PRIu64 " as [Startup]IOWeight=%" PRIu64,
+ blkio_weight, weight);
+ } else
+ weight = CGROUP_WEIGHT_DEFAULT;
- xsprintf(buf, "default %" PRIu64 "\n", weight);
- r = cg_set_attribute("io", path, "io.weight", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set io.weight: %m");
+ xsprintf(buf, "default %" PRIu64 "\n", weight);
+ (void) set_attribute_and_warn(u, "io", "io.weight", buf);
- if (has_io) {
- CGroupIODeviceWeight *w;
+ if (has_io) {
+ CGroupIODeviceLatency *latency;
+ CGroupIODeviceLimit *limit;
+ CGroupIODeviceWeight *w;
- /* FIXME: no way to reset this list */
- LIST_FOREACH(device_weights, w, c->io_device_weights)
- cgroup_apply_io_device_weight(u, w->path, w->weight);
- } else if (has_blockio) {
- CGroupBlockIODeviceWeight *w;
+ LIST_FOREACH(device_weights, w, c->io_device_weights)
+ cgroup_apply_io_device_weight(u, w->path, w->weight);
- /* FIXME: no way to reset this list */
- LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
- weight = cgroup_weight_blkio_to_io(w->weight);
+ LIST_FOREACH(device_limits, limit, c->io_device_limits)
+ cgroup_apply_io_device_limit(u, limit->path, limit->limits);
- log_cgroup_compat(u, "Applying BlockIODeviceWeight %" PRIu64 " as IODeviceWeight %" PRIu64 " for %s",
- w->weight, weight, w->path);
+ LIST_FOREACH(device_latencies, latency, c->io_device_latencies)
+ cgroup_apply_io_device_latency(u, latency->path, latency->target_usec);
- cgroup_apply_io_device_weight(u, w->path, weight);
- }
- }
- }
+ } else if (has_blockio) {
+ CGroupBlockIODeviceWeight *w;
+ CGroupBlockIODeviceBandwidth *b;
- /* Apply limits and free ones without config. */
- if (has_io) {
- CGroupIODeviceLimit *l;
+ LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
+ weight = cgroup_weight_blkio_to_io(w->weight);
- LIST_FOREACH(device_limits, l, c->io_device_limits)
- cgroup_apply_io_device_limit(u, l->path, l->limits);
+ log_cgroup_compat(u, "Applying BlockIODeviceWeight=%" PRIu64 " as IODeviceWeight=%" PRIu64 " for %s",
+ w->weight, weight, w->path);
- } else if (has_blockio) {
- CGroupBlockIODeviceBandwidth *b;
+ cgroup_apply_io_device_weight(u, w->path, weight);
+ }
LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX];
@@ -833,7 +989,7 @@ static void cgroup_context_apply(
limits[CGROUP_IO_RBPS_MAX] = b->rbps;
limits[CGROUP_IO_WBPS_MAX] = b->wbps;
- log_cgroup_compat(u, "Applying BlockIO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as IO{Read|Write}BandwidthMax for %s",
+ log_cgroup_compat(u, "Applying BlockIO{Read|Write}Bandwidth=%" PRIu64 " %" PRIu64 " as IO{Read|Write}BandwidthMax= for %s",
b->rbps, b->wbps, b->path);
cgroup_apply_io_device_limit(u, b->path, limits);
@@ -842,19 +998,24 @@ static void cgroup_context_apply(
}
if (apply_mask & CGROUP_MASK_BLKIO) {
- bool has_io = cgroup_context_has_io_config(c);
- bool has_blockio = cgroup_context_has_blockio_config(c);
+ bool has_io, has_blockio;
- if (!is_root) {
+ has_io = cgroup_context_has_io_config(c);
+ has_blockio = cgroup_context_has_blockio_config(c);
+
+ /* Applying a 'weight' never makes sense for the host root cgroup, and for containers this should be
+ * left to our container manager, too. */
+ if (!is_local_root) {
char buf[DECIMAL_STR_MAX(uint64_t)+1];
uint64_t weight;
if (has_io) {
- uint64_t io_weight = cgroup_context_io_weight(c, state);
+ uint64_t io_weight;
+ io_weight = cgroup_context_io_weight(c, state);
weight = cgroup_weight_io_to_blkio(cgroup_context_io_weight(c, state));
- log_cgroup_compat(u, "Applying [Startup]IOWeight %" PRIu64 " as [Startup]BlockIOWeight %" PRIu64,
+ log_cgroup_compat(u, "Applying [Startup]IOWeight=%" PRIu64 " as [Startup]BlockIOWeight=%" PRIu64,
io_weight, weight);
} else if (has_blockio)
weight = cgroup_context_blkio_weight(c, state);
@@ -862,19 +1023,15 @@ static void cgroup_context_apply(
weight = CGROUP_BLKIO_WEIGHT_DEFAULT;
xsprintf(buf, "%" PRIu64 "\n", weight);
- r = cg_set_attribute("blkio", path, "blkio.weight", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set blkio.weight: %m");
+ (void) set_attribute_and_warn(u, "blkio", "blkio.weight", buf);
if (has_io) {
CGroupIODeviceWeight *w;
- /* FIXME: no way to reset this list */
LIST_FOREACH(device_weights, w, c->io_device_weights) {
weight = cgroup_weight_io_to_blkio(w->weight);
- log_cgroup_compat(u, "Applying IODeviceWeight %" PRIu64 " as BlockIODeviceWeight %" PRIu64 " for %s",
+ log_cgroup_compat(u, "Applying IODeviceWeight=%" PRIu64 " as BlockIODeviceWeight=%" PRIu64 " for %s",
w->weight, weight, w->path);
cgroup_apply_blkio_device_weight(u, w->path, weight);
@@ -882,31 +1039,38 @@ static void cgroup_context_apply(
} else if (has_blockio) {
CGroupBlockIODeviceWeight *w;
- /* FIXME: no way to reset this list */
LIST_FOREACH(device_weights, w, c->blockio_device_weights)
cgroup_apply_blkio_device_weight(u, w->path, w->weight);
}
}
- /* Apply limits and free ones without config. */
- if (has_io) {
- CGroupIODeviceLimit *l;
+ /* The bandwith limits are something that make sense to be applied to the host's root but not container
+ * roots, as there we want the container manager to handle it */
+ if (is_host_root || !is_local_root) {
+ if (has_io) {
+ CGroupIODeviceLimit *l;
- LIST_FOREACH(device_limits, l, c->io_device_limits) {
- log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax for %s",
- l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path);
+ LIST_FOREACH(device_limits, l, c->io_device_limits) {
+ log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth=%" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax= for %s",
+ l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path);
- cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX]);
- }
- } else if (has_blockio) {
- CGroupBlockIODeviceBandwidth *b;
+ cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX]);
+ }
+ } else if (has_blockio) {
+ CGroupBlockIODeviceBandwidth *b;
- LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths)
- cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps);
+ LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths)
+ cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps);
+ }
}
}
- if ((apply_mask & CGROUP_MASK_MEMORY) && !is_root) {
+ /* In unified mode 'memory' attributes do not exist on the root cgroup. In legacy mode 'memory.limit_in_bytes'
+ * exists on the root cgroup, but any writes to it are refused with EINVAL. And if we run in a container we
+ * want to leave control to the container manager (and if proper cgroupsv2 delegation is used we couldn't even
+ * write to this if we wanted to.) */
+ if ((apply_mask & CGROUP_MASK_MEMORY) && !is_local_root) {
+
if (cg_all_unified() > 0) {
uint64_t max, swap_max = CGROUP_LIMIT_MAX;
@@ -917,20 +1081,22 @@ static void cgroup_context_apply(
max = c->memory_limit;
if (max != CGROUP_LIMIT_MAX)
- log_cgroup_compat(u, "Applying MemoryLimit %" PRIu64 " as MemoryMax", max);
+ log_cgroup_compat(u, "Applying MemoryLimit=%" PRIu64 " as MemoryMax=", max);
}
+ cgroup_apply_unified_memory_limit(u, "memory.min", c->memory_min);
cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low);
cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
cgroup_apply_unified_memory_limit(u, "memory.max", max);
cgroup_apply_unified_memory_limit(u, "memory.swap.max", swap_max);
+
} else {
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
uint64_t val;
if (cgroup_context_has_unified_memory_config(c)) {
val = c->memory_max;
- log_cgroup_compat(u, "Applying MemoryMax %" PRIi64 " as MemoryLimit", val);
+ log_cgroup_compat(u, "Applying MemoryMax=%" PRIi64 " as MemoryLimit=", val);
} else
val = c->memory_limit;
@@ -939,27 +1105,33 @@ static void cgroup_context_apply(
else
xsprintf(buf, "%" PRIu64 "\n", val);
- r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set memory.limit_in_bytes: %m");
+ (void) set_attribute_and_warn(u, "memory", "memory.limit_in_bytes", buf);
}
}
- if ((apply_mask & CGROUP_MASK_DEVICES) && !is_root) {
+ /* On cgroupsv2 we can apply BPF everywhere. On cgroupsv1 we apply it everywhere except for the root of
+ * containers, where we leave this to the manager */
+ if ((apply_mask & (CGROUP_MASK_DEVICES | CGROUP_MASK_BPF_DEVICES)) &&
+ (is_host_root || cg_all_unified() > 0 || !is_local_root)) {
+ _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
CGroupDeviceAllow *a;
- /* Changing the devices list of a populated cgroup
- * might result in EINVAL, hence ignore EINVAL
- * here. */
+ if (cg_all_unified() > 0) {
+ r = cgroup_init_device_bpf(&prog, c->device_policy, c->device_allow);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to initialize device control bpf program: %m");
+ } else {
+ /* Changing the devices list of a populated cgroup might result in EINVAL, hence ignore EINVAL
+ * here. */
- if (c->device_allow || c->device_policy != CGROUP_AUTO)
- r = cg_set_attribute("devices", path, "devices.deny", "a");
- else
- r = cg_set_attribute("devices", path, "devices.allow", "a");
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to reset devices.list: %m");
+ if (c->device_allow || c->device_policy != CGROUP_AUTO)
+ r = cg_set_attribute("devices", path, "devices.deny", "a");
+ else
+ r = cg_set_attribute("devices", path, "devices.allow", "a");
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to reset devices.allow/devices.deny: %m");
+ }
if (c->device_policy == CGROUP_CLOSED ||
(c->device_policy == CGROUP_AUTO && c->device_allow)) {
@@ -972,16 +1144,16 @@ static void cgroup_context_apply(
"/dev/tty\0" "rwm\0"
"/dev/ptmx\0" "rwm\0"
/* Allow /run/systemd/inaccessible/{chr,blk} devices for mapping InaccessiblePaths */
- "-/run/systemd/inaccessible/chr\0" "rwm\0"
- "-/run/systemd/inaccessible/blk\0" "rwm\0";
+ "/run/systemd/inaccessible/chr\0" "rwm\0"
+ "/run/systemd/inaccessible/blk\0" "rwm\0";
const char *x, *y;
NULSTR_FOREACH_PAIR(x, y, auto_devices)
- whitelist_device(path, x, y);
+ (void) whitelist_device(prog, path, x, y);
/* PTS (/dev/pts) devices may not be duplicated, but accessed */
- whitelist_major(path, "pts", 'c', "rw");
+ (void) whitelist_major(prog, path, "pts", 'c', "rw");
}
LIST_FOREACH(device_allow, a, c->device_allow) {
@@ -1001,19 +1173,31 @@ static void cgroup_context_apply(
acc[k++] = 0;
if (path_startswith(a->path, "/dev/"))
- whitelist_device(path, a->path, acc);
+ (void) whitelist_device(prog, path, a->path, acc);
else if ((val = startswith(a->path, "block-")))
- whitelist_major(path, val, 'b', acc);
+ (void) whitelist_major(prog, path, val, 'b', acc);
else if ((val = startswith(a->path, "char-")))
- whitelist_major(path, val, 'c', acc);
+ (void) whitelist_major(prog, path, val, 'c', acc);
else
- log_unit_debug(u, "Ignoring device %s while writing cgroup attribute.", a->path);
+ log_unit_debug(u, "Ignoring device '%s' while writing cgroup attribute.", a->path);
+ }
+
+ r = cgroup_apply_device_bpf(u, prog, c->device_policy, c->device_allow);
+ if (r < 0) {
+ static bool warned = false;
+
+ log_full_errno(warned ? LOG_DEBUG : LOG_WARNING, r,
+ "Unit %s configures device ACL, but the local system doesn't seem to support the BPF-based device controller.\n"
+ "Proceeding WITHOUT applying ACL (all devices will be accessible)!\n"
+ "(This warning is only shown for the first loaded unit using device ACL.)", u->id);
+
+ warned = true;
}
}
if (apply_mask & CGROUP_MASK_PIDS) {
- if (is_root) {
+ if (is_host_root) {
/* So, the "pids" controller does not expose anything on the root cgroup, in order not to
* replicate knobs exposed elsewhere needlessly. We abstract this away here however, and when
* the knobs of the root cgroup are modified propagate this to the relevant sysctls. There's a
@@ -1034,39 +1218,68 @@ static void cgroup_context_apply(
r = procfs_tasks_set_limit(TASKS_MAX);
else
r = 0;
-
if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ log_unit_full(u, LOG_LEVEL_CGROUP_WRITE(r), r,
"Failed to write to tasks limit sysctls: %m");
+ }
- } else {
+ /* The attribute itself is not available on the host root cgroup, and in the container case we want to
+ * leave it for the container manager. */
+ if (!is_local_root) {
if (c->tasks_max != CGROUP_LIMIT_MAX) {
char buf[DECIMAL_STR_MAX(uint64_t) + 2];
sprintf(buf, "%" PRIu64 "\n", c->tasks_max);
- r = cg_set_attribute("pids", path, "pids.max", buf);
+ (void) set_attribute_and_warn(u, "pids", "pids.max", buf);
} else
- r = cg_set_attribute("pids", path, "pids.max", "max");
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set pids.max: %m");
+ (void) set_attribute_and_warn(u, "pids", "pids.max", "max\n");
}
}
- if (apply_bpf)
+ if (apply_mask & CGROUP_MASK_BPF_FIREWALL)
cgroup_apply_firewall(u);
}
-CGroupMask cgroup_context_get_mask(CGroupContext *c) {
+static bool unit_get_needs_bpf_firewall(Unit *u) {
+ CGroupContext *c;
+ Unit *p;
+ assert(u);
+
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return false;
+
+ if (c->ip_accounting ||
+ c->ip_address_allow ||
+ c->ip_address_deny)
+ return true;
+
+ /* If any parent slice has an IP access list defined, it applies too */
+ for (p = UNIT_DEREF(u->slice); p; p = UNIT_DEREF(p->slice)) {
+ c = unit_get_cgroup_context(p);
+ if (!c)
+ return false;
+
+ if (c->ip_address_allow ||
+ c->ip_address_deny)
+ return true;
+ }
+
+ return false;
+}
+
+static CGroupMask cgroup_context_get_mask(CGroupContext *c) {
CGroupMask mask = 0;
- /* Figure out which controllers we need */
+ /* Figure out which controllers we need, based on the cgroup context object */
+
+ if (c->cpu_accounting)
+ mask |= get_cpu_accounting_mask();
- if (c->cpu_accounting ||
- cgroup_context_has_cpu_weight(c) ||
+ if (cgroup_context_has_cpu_weight(c) ||
cgroup_context_has_cpu_shares(c) ||
c->cpu_quota_per_sec_usec != USEC_INFINITY)
- mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU;
+ mask |= CGROUP_MASK_CPU;
if (cgroup_context_has_io_config(c) || cgroup_context_has_blockio_config(c))
mask |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO;
@@ -1078,25 +1291,41 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) {
if (c->device_allow ||
c->device_policy != CGROUP_AUTO)
- mask |= CGROUP_MASK_DEVICES;
+ mask |= CGROUP_MASK_DEVICES | CGROUP_MASK_BPF_DEVICES;
if (c->tasks_accounting ||
c->tasks_max != CGROUP_LIMIT_MAX)
mask |= CGROUP_MASK_PIDS;
+ return CGROUP_MASK_EXTEND_JOINED(mask);
+}
+
+static CGroupMask unit_get_bpf_mask(Unit *u) {
+ CGroupMask mask = 0;
+
+ /* Figure out which controllers we need, based on the cgroup context, possibly taking into account children
+ * too. */
+
+ if (unit_get_needs_bpf_firewall(u))
+ mask |= CGROUP_MASK_BPF_FIREWALL;
+
return mask;
}
CGroupMask unit_get_own_mask(Unit *u) {
CGroupContext *c;
- /* Returns the mask of controllers the unit needs for itself */
+ /* Returns the mask of controllers the unit needs for itself. If a unit is not properly loaded, return an empty
+ * mask, as we shouldn't reflect it in the cgroup hierarchy then. */
+
+ if (u->load_state != UNIT_LOADED)
+ return 0;
c = unit_get_cgroup_context(u);
if (!c)
return 0;
- return cgroup_context_get_mask(c) | unit_get_delegate_mask(u);
+ return (cgroup_context_get_mask(c) | unit_get_bpf_mask(u) | unit_get_delegate_mask(u)) & ~unit_get_ancestor_disable_mask(u);
}
CGroupMask unit_get_delegate_mask(Unit *u) {
@@ -1119,7 +1348,7 @@ CGroupMask unit_get_delegate_mask(Unit *u) {
}
assert_se(c = unit_get_cgroup_context(u));
- return c->delegate_controllers;
+ return CGROUP_MASK_EXTEND_JOINED(c->delegate_controllers);
}
CGroupMask unit_get_members_mask(Unit *u) {
@@ -1128,7 +1357,7 @@ CGroupMask unit_get_members_mask(Unit *u) {
/* Returns the mask of controllers all of the unit's children require, merged */
if (u->cgroup_members_mask_valid)
- return u->cgroup_members_mask;
+ return u->cgroup_members_mask; /* Use cached value if possible */
u->cgroup_members_mask = 0;
@@ -1138,14 +1367,8 @@ CGroupMask unit_get_members_mask(Unit *u) {
Iterator i;
HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i) {
-
- if (member == u)
- continue;
-
- if (UNIT_DEREF(member->slice) != u)
- continue;
-
- u->cgroup_members_mask |= unit_get_subtree_mask(member); /* note that this calls ourselves again, for the children */
+ if (UNIT_DEREF(member->slice) == u)
+ u->cgroup_members_mask |= unit_get_subtree_mask(member); /* note that this calls ourselves again, for the children */
}
}
@@ -1166,6 +1389,31 @@ CGroupMask unit_get_siblings_mask(Unit *u) {
return unit_get_subtree_mask(u); /* we are the top-level slice */
}
+CGroupMask unit_get_disable_mask(Unit *u) {
+ CGroupContext *c;
+
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return 0;
+
+ return c->disable_controllers;
+}
+
+CGroupMask unit_get_ancestor_disable_mask(Unit *u) {
+ CGroupMask mask;
+
+ assert(u);
+ mask = unit_get_disable_mask(u);
+
+ /* Returns the mask of controllers which are marked as forcibly
+ * disabled in any ancestor unit or the unit in question. */
+
+ if (UNIT_ISSET(u->slice))
+ mask |= unit_get_ancestor_disable_mask(UNIT_DEREF(u->slice));
+
+ return mask;
+}
+
CGroupMask unit_get_subtree_mask(Unit *u) {
/* Returns the mask of this subtree, meaning of the group
@@ -1186,6 +1434,7 @@ CGroupMask unit_get_target_mask(Unit *u) {
mask = unit_get_own_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
mask &= u->manager->cgroup_supported;
+ mask &= ~unit_get_ancestor_disable_mask(u);
return mask;
}
@@ -1200,85 +1449,19 @@ CGroupMask unit_get_enable_mask(Unit *u) {
mask = unit_get_members_mask(u);
mask &= u->manager->cgroup_supported;
+ mask &= ~unit_get_ancestor_disable_mask(u);
return mask;
}
-bool unit_get_needs_bpf(Unit *u) {
- CGroupContext *c;
- Unit *p;
+void unit_invalidate_cgroup_members_masks(Unit *u) {
assert(u);
- c = unit_get_cgroup_context(u);
- if (!c)
- return false;
+ /* Recurse invalidate the member masks cache all the way up the tree */
+ u->cgroup_members_mask_valid = false;
- if (c->ip_accounting ||
- c->ip_address_allow ||
- c->ip_address_deny)
- return true;
-
- /* If any parent slice has an IP access list defined, it applies too */
- for (p = UNIT_DEREF(u->slice); p; p = UNIT_DEREF(p->slice)) {
- c = unit_get_cgroup_context(p);
- if (!c)
- return false;
-
- if (c->ip_address_allow ||
- c->ip_address_deny)
- return true;
- }
-
- return false;
-}
-
-/* Recurse from a unit up through its containing slices, propagating
- * mask bits upward. A unit is also member of itself. */
-void unit_update_cgroup_members_masks(Unit *u) {
- CGroupMask m;
- bool more;
-
- assert(u);
-
- /* Calculate subtree mask */
- m = unit_get_subtree_mask(u);
-
- /* See if anything changed from the previous invocation. If
- * not, we're done. */
- if (u->cgroup_subtree_mask_valid && m == u->cgroup_subtree_mask)
- return;
-
- more =
- u->cgroup_subtree_mask_valid &&
- ((m & ~u->cgroup_subtree_mask) != 0) &&
- ((~m & u->cgroup_subtree_mask) == 0);
-
- u->cgroup_subtree_mask = m;
- u->cgroup_subtree_mask_valid = true;
-
- if (UNIT_ISSET(u->slice)) {
- Unit *s = UNIT_DEREF(u->slice);
-
- if (more)
- /* There's more set now than before. We
- * propagate the new mask to the parent's mask
- * (not caring if it actually was valid or
- * not). */
-
- s->cgroup_members_mask |= m;
-
- else
- /* There's less set now than before (or we
- * don't know), we need to recalculate
- * everything, so let's invalidate the
- * parent's members mask */
-
- s->cgroup_members_mask_valid = false;
-
- /* And now make sure that this change also hits our
- * grandparents */
- unit_update_cgroup_members_masks(s);
- }
+ if (UNIT_ISSET(u->slice))
+ unit_invalidate_cgroup_members_masks(UNIT_DEREF(u->slice));
}
const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask) {
@@ -1302,7 +1485,7 @@ static const char *migrate_callback(CGroupMask mask, void *userdata) {
return unit_get_realized_cgroup_path(userdata, mask);
}
-char *unit_default_cgroup_path(Unit *u) {
+char *unit_default_cgroup_path(const Unit *u) {
_cleanup_free_ char *escaped = NULL, *slice = NULL;
int r;
@@ -1435,16 +1618,14 @@ static int unit_create_cgroup(
Unit *u,
CGroupMask target_mask,
CGroupMask enable_mask,
- bool needs_bpf) {
+ ManagerState state) {
- CGroupContext *c;
- int r;
bool created;
+ int r;
assert(u);
- c = unit_get_cgroup_context(u);
- if (!c)
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
return 0;
/* Figure out our cgroup path */
@@ -1456,26 +1637,44 @@ static int unit_create_cgroup(
r = cg_create_everywhere(u->manager->cgroup_supported, target_mask, u->cgroup_path);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", u->cgroup_path);
- created = !!r;
+ created = r;
/* Start watching it */
(void) unit_watch_cgroup(u);
/* Preserve enabled controllers in delegated units, adjust others. */
- if (created || !unit_cgroup_delegate(u)) {
+ if (created || !u->cgroup_realized || !unit_cgroup_delegate(u)) {
+ CGroupMask result_mask = 0;
/* Enable all controllers we need */
- r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path);
+ r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path, &result_mask);
if (r < 0)
- log_unit_warning_errno(u, r, "Failed to enable controllers on cgroup %s, ignoring: %m",
- u->cgroup_path);
+ log_unit_warning_errno(u, r, "Failed to enable/disable controllers on cgroup %s, ignoring: %m", u->cgroup_path);
+
+ /* If we just turned off a controller, this might release the controller for our parent too, let's
+ * enqueue the parent for re-realization in that case again. */
+ if (UNIT_ISSET(u->slice)) {
+ CGroupMask turned_off;
+
+ turned_off = (u->cgroup_realized ? u->cgroup_enabled_mask & ~result_mask : 0);
+ if (turned_off != 0) {
+ Unit *parent;
+
+ /* Force the parent to propagate the enable mask to the kernel again, by invalidating
+ * the controller we just turned off. */
+
+ for (parent = UNIT_DEREF(u->slice); parent; parent = UNIT_DEREF(parent->slice))
+ unit_invalidate_cgroup(parent, turned_off);
+ }
+ }
+
+ /* Remember what's actually enabled now */
+ u->cgroup_enabled_mask = result_mask;
}
/* Keep track that this is now realized */
u->cgroup_realized = true;
u->cgroup_realized_mask = target_mask;
- u->cgroup_enabled_mask = enable_mask;
- u->cgroup_bpf_state = needs_bpf ? UNIT_CGROUP_BPF_ON : UNIT_CGROUP_BPF_OFF;
if (u->type != UNIT_SLICE && !unit_cgroup_delegate(u)) {
@@ -1487,6 +1686,10 @@ static int unit_create_cgroup(
log_unit_warning_errno(u, r, "Failed to migrate cgroup from to %s, ignoring: %m", u->cgroup_path);
}
+ /* Set attributes */
+ cgroup_context_apply(u, target_mask, state);
+ cgroup_xattr_apply(u);
+
return 0;
}
@@ -1628,42 +1831,69 @@ int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) {
return r;
}
-static void cgroup_xattr_apply(Unit *u) {
- char ids[SD_ID128_STRING_MAX];
- int r;
+static bool unit_has_mask_realized(
+ Unit *u,
+ CGroupMask target_mask,
+ CGroupMask enable_mask) {
assert(u);
- if (!MANAGER_IS_SYSTEM(u->manager))
- return;
+ /* Returns true if this unit is fully realized. We check four things:
+ *
+ * 1. Whether the cgroup was created at all
+ * 2. Whether the cgroup was created in all the hierarchies we need it to be created in (in case of cgroupsv1)
+ * 3. Whether the cgroup has all the right controllers enabled (in case of cgroupsv2)
+ * 4. Whether the invalidation mask is currently zero
+ *
+ * If you wonder why we mask the target realization and enable mask with CGROUP_MASK_V1/CGROUP_MASK_V2: note
+ * that there are three sets of bitmasks: CGROUP_MASK_V1 (for real cgroupv1 controllers), CGROUP_MASK_V2 (for
+ * real cgroupv2 controllers) and CGROUP_MASK_BPF (for BPF-based pseudo-controllers). Now, cgroup_realized_mask
+ * is only matters for cgroupsv1 controllers, and cgroup_enabled_mask only used for cgroupsv2, and if they
+ * differ in the others, we don't really care. (After all, the cgroup_enabled_mask tracks with controllers are
+ * enabled through cgroup.subtree_control, and since the BPF pseudo-controllers don't show up there, they
+ * simply don't matter. */
- if (sd_id128_is_null(u->invocation_id))
- return;
+ return u->cgroup_realized &&
+ ((u->cgroup_realized_mask ^ target_mask) & CGROUP_MASK_V1) == 0 &&
+ ((u->cgroup_enabled_mask ^ enable_mask) & CGROUP_MASK_V2) == 0 &&
+ u->cgroup_invalidated_mask == 0;
+}
- r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
- "trusted.invocation_id",
- sd_id128_to_string(u->invocation_id, ids), 32,
- 0);
- if (r < 0)
- log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", u->cgroup_path);
+static bool unit_has_mask_disables_realized(
+ Unit *u,
+ CGroupMask target_mask,
+ CGroupMask enable_mask) {
+
+ assert(u);
+
+ /* Returns true if all controllers which should be disabled are indeed disabled.
+ *
+ * Unlike unit_has_mask_realized, we don't care what was enabled, only that anything we want to remove is
+ * already removed. */
+
+ return !u->cgroup_realized ||
+ (FLAGS_SET(u->cgroup_realized_mask, target_mask & CGROUP_MASK_V1) &&
+ FLAGS_SET(u->cgroup_enabled_mask, enable_mask & CGROUP_MASK_V2));
}
-static bool unit_has_mask_realized(
+static bool unit_has_mask_enables_realized(
Unit *u,
CGroupMask target_mask,
- CGroupMask enable_mask,
- bool needs_bpf) {
+ CGroupMask enable_mask) {
assert(u);
+ /* Returns true if all controllers which should be enabled are indeed enabled.
+ *
+ * Unlike unit_has_mask_realized, we don't care about the controllers that are not present, only that anything
+ * we want to add is already added. */
+
return u->cgroup_realized &&
- u->cgroup_realized_mask == target_mask &&
- u->cgroup_enabled_mask == enable_mask &&
- ((needs_bpf && u->cgroup_bpf_state == UNIT_CGROUP_BPF_ON) ||
- (!needs_bpf && u->cgroup_bpf_state == UNIT_CGROUP_BPF_OFF));
+ ((u->cgroup_realized_mask | target_mask) & CGROUP_MASK_V1) == (u->cgroup_realized_mask & CGROUP_MASK_V1) &&
+ ((u->cgroup_enabled_mask | enable_mask) & CGROUP_MASK_V2) == (u->cgroup_enabled_mask & CGROUP_MASK_V2);
}
-static void unit_add_to_cgroup_realize_queue(Unit *u) {
+void unit_add_to_cgroup_realize_queue(Unit *u) {
assert(u);
if (u->in_cgroup_realize_queue)
@@ -1683,15 +1913,131 @@ static void unit_remove_from_cgroup_realize_queue(Unit *u) {
u->in_cgroup_realize_queue = false;
}
+/* Controllers can only be enabled breadth-first, from the root of the
+ * hierarchy downwards to the unit in question. */
+static int unit_realize_cgroup_now_enable(Unit *u, ManagerState state) {
+ CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask;
+ int r;
+
+ assert(u);
+
+ /* First go deal with this unit's parent, or we won't be able to enable
+ * any new controllers at this layer. */
+ if (UNIT_ISSET(u->slice)) {
+ r = unit_realize_cgroup_now_enable(UNIT_DEREF(u->slice), state);
+ if (r < 0)
+ return r;
+ }
+
+ target_mask = unit_get_target_mask(u);
+ enable_mask = unit_get_enable_mask(u);
+
+ /* We can only enable in this direction, don't try to disable anything.
+ */
+ if (unit_has_mask_enables_realized(u, target_mask, enable_mask))
+ return 0;
+
+ new_target_mask = u->cgroup_realized_mask | target_mask;
+ new_enable_mask = u->cgroup_enabled_mask | enable_mask;
+
+ return unit_create_cgroup(u, new_target_mask, new_enable_mask, state);
+}
+
+/* Controllers can only be disabled depth-first, from the leaves of the
+ * hierarchy upwards to the unit in question. */
+static int unit_realize_cgroup_now_disable(Unit *u, ManagerState state) {
+ Iterator i;
+ Unit *m;
+ void *v;
+
+ assert(u);
+
+ if (u->type != UNIT_SLICE)
+ return 0;
+
+ HASHMAP_FOREACH_KEY(v, m, u->dependencies[UNIT_BEFORE], i) {
+ CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask;
+ int r;
+
+ if (UNIT_DEREF(m->slice) != u)
+ continue;
+
+ /* The cgroup for this unit might not actually be fully
+ * realised yet, in which case it isn't holding any controllers
+ * open anyway. */
+ if (!m->cgroup_path)
+ continue;
+
+ /* We must disable those below us first in order to release the
+ * controller. */
+ if (m->type == UNIT_SLICE)
+ (void) unit_realize_cgroup_now_disable(m, state);
+
+ target_mask = unit_get_target_mask(m);
+ enable_mask = unit_get_enable_mask(m);
+
+ /* We can only disable in this direction, don't try to enable
+ * anything. */
+ if (unit_has_mask_disables_realized(m, target_mask, enable_mask))
+ continue;
+
+ new_target_mask = m->cgroup_realized_mask & target_mask;
+ new_enable_mask = m->cgroup_enabled_mask & enable_mask;
+
+ r = unit_create_cgroup(m, new_target_mask, new_enable_mask, state);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
/* Check if necessary controllers and attributes for a unit are in place.
*
- * If so, do nothing.
- * If not, create paths, move processes over, and set attributes.
+ * - If so, do nothing.
+ * - If not, create paths, move processes over, and set attributes.
+ *
+ * Controllers can only be *enabled* in a breadth-first way, and *disabled* in
+ * a depth-first way. As such the process looks like this:
+ *
+ * Suppose we have a cgroup hierarchy which looks like this:
+ *
+ * root
+ * / \
+ * / \
+ * / \
+ * a b
+ * / \ / \
+ * / \ / \
+ * c d e f
+ * / \ / \ / \ / \
+ * h i j k l m n o
+ *
+ * 1. We want to realise cgroup "d" now.
+ * 2. cgroup "a" has DisableControllers=cpu in the associated unit.
+ * 3. cgroup "k" just started requesting the memory controller.
+ *
+ * To make this work we must do the following in order:
+ *
+ * 1. Disable CPU controller in k, j
+ * 2. Disable CPU controller in d
+ * 3. Enable memory controller in root
+ * 4. Enable memory controller in a
+ * 5. Enable memory controller in d
+ * 6. Enable memory controller in k
+ *
+ * Notice that we need to touch j in one direction, but not the other. We also
+ * don't go beyond d when disabling -- it's up to "a" to get realized if it
+ * wants to disable further. The basic rules are therefore:
+ *
+ * - If you're disabling something, you need to realise all of the cgroups from
+ * your recursive descendants to the root. This starts from the leaves.
+ * - If you're enabling something, you need to realise from the root cgroup
+ * downwards, but you don't need to iterate your recursive descendants.
*
* Returns 0 on success and < 0 on failure. */
static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
CGroupMask target_mask, enable_mask;
- bool needs_bpf, apply_bpf;
int r;
assert(u);
@@ -1700,32 +2046,29 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
target_mask = unit_get_target_mask(u);
enable_mask = unit_get_enable_mask(u);
- needs_bpf = unit_get_needs_bpf(u);
- if (unit_has_mask_realized(u, target_mask, enable_mask, needs_bpf))
+ if (unit_has_mask_realized(u, target_mask, enable_mask))
return 0;
- /* Make sure we apply the BPF filters either when one is configured, or if none is configured but previously
- * the state was anything but off. This way, if a unit with a BPF filter applied is reconfigured to lose it
- * this will trickle down properly to cgroupfs. */
- apply_bpf = needs_bpf || u->cgroup_bpf_state != UNIT_CGROUP_BPF_OFF;
+ /* Disable controllers below us, if there are any */
+ r = unit_realize_cgroup_now_disable(u, state);
+ if (r < 0)
+ return r;
- /* First, realize parents */
+ /* Enable controllers above us, if there are any */
if (UNIT_ISSET(u->slice)) {
- r = unit_realize_cgroup_now(UNIT_DEREF(u->slice), state);
+ r = unit_realize_cgroup_now_enable(UNIT_DEREF(u->slice), state);
if (r < 0)
return r;
}
- /* And then do the real work */
- r = unit_create_cgroup(u, target_mask, enable_mask, needs_bpf);
+ /* Now actually deal with the cgroup we were trying to realise and set attributes */
+ r = unit_create_cgroup(u, target_mask, enable_mask, state);
if (r < 0)
return r;
- /* Finally, apply the necessary attributes. */
- cgroup_context_apply(u, target_mask, apply_bpf, state);
- cgroup_xattr_apply(u);
-
+ /* Now, reset the invalidation mask */
+ u->cgroup_invalidated_mask = 0;
return 0;
}
@@ -1771,9 +2114,6 @@ static void unit_add_siblings_to_cgroup_realize_queue(Unit *u) {
void *v;
HASHMAP_FOREACH_KEY(v, m, u->dependencies[UNIT_BEFORE], i) {
- if (m == u)
- continue;
-
/* Skip units that have a dependency on the slice
* but aren't actually in it. */
if (UNIT_DEREF(m->slice) != slice)
@@ -1789,8 +2129,7 @@ static void unit_add_siblings_to_cgroup_realize_queue(Unit *u) {
* any changes. */
if (unit_has_mask_realized(m,
unit_get_target_mask(m),
- unit_get_enable_mask(m),
- unit_get_needs_bpf(m)))
+ unit_get_enable_mask(m)))
continue;
unit_add_to_cgroup_realize_queue(m);
@@ -1827,7 +2166,8 @@ int unit_realize_cgroup(Unit *u) {
void unit_release_cgroup(Unit *u) {
assert(u);
- /* Forgets all cgroup details for this cgroup */
+ /* Forgets all cgroup details for this cgroup — but does *not* destroy the cgroup. This is hence OK to call
+ * when we close down everything for reexecution, where we really want to leave the cgroup in place. */
if (u->cgroup_path) {
(void) hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
@@ -1836,7 +2176,7 @@ void unit_release_cgroup(Unit *u) {
if (u->cgroup_inotify_wd >= 0) {
if (inotify_rm_watch(u->manager->cgroup_inotify_fd, u->cgroup_inotify_wd) < 0)
- log_unit_debug_errno(u, errno, "Failed to remove cgroup inotify watch %i for %s, ignoring", u->cgroup_inotify_wd, u->id);
+ log_unit_debug_errno(u, errno, "Failed to remove cgroup inotify watch %i for %s, ignoring: %m", u->cgroup_inotify_wd, u->id);
(void) hashmap_remove(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd));
u->cgroup_inotify_wd = -1;
@@ -1872,6 +2212,8 @@ void unit_prune_cgroup(Unit *u) {
u->cgroup_realized = false;
u->cgroup_realized_mask = 0;
u->cgroup_enabled_mask = 0;
+
+ u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
}
int unit_search_main_pid(Unit *u, pid_t *ret) {
@@ -2133,11 +2475,30 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents,
}
}
+static int cg_bpf_mask_supported(CGroupMask *ret) {
+ CGroupMask mask = 0;
+ int r;
+
+ /* BPF-based firewall */
+ r = bpf_firewall_supported();
+ if (r > 0)
+ mask |= CGROUP_MASK_BPF_FIREWALL;
+
+ /* BPF-based device access control */
+ r = bpf_devices_supported();
+ if (r > 0)
+ mask |= CGROUP_MASK_BPF_DEVICES;
+
+ *ret = mask;
+ return 0;
+}
+
int manager_setup_cgroup(Manager *m) {
_cleanup_free_ char *path = NULL;
const char *scope_path;
CGroupController c;
int r, all_unified;
+ CGroupMask mask;
char *e;
assert(m);
@@ -2231,7 +2592,7 @@ int manager_setup_cgroup(Manager *m) {
(void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify");
- } else if (MANAGER_IS_SYSTEM(m) && m->test_run_flags == 0) {
+ } else if (MANAGER_IS_SYSTEM(m) && manager_owns_host_root_cgroup(m) && !MANAGER_IS_TEST_RUN(m)) {
/* On the legacy hierarchy we only get notifications via cgroup agents. (Which isn't really reliable,
* since it does not generate events when control groups with children run empty. */
@@ -2260,17 +2621,25 @@ int manager_setup_cgroup(Manager *m) {
if (m->pin_cgroupfs_fd < 0)
return log_error_errno(errno, "Failed to open pin file: %m");
- } else if (r < 0 && !m->test_run_flags)
+ } else if (!MANAGER_IS_TEST_RUN(m))
return log_error_errno(r, "Failed to create %s control group: %m", scope_path);
/* 7. Always enable hierarchical support if it exists... */
- if (!all_unified && m->test_run_flags == 0)
+ if (!all_unified && !MANAGER_IS_TEST_RUN(m))
(void) cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
- /* 8. Figure out which controllers are supported, and log about it */
+ /* 8. Figure out which controllers are supported */
r = cg_mask_supported(&m->cgroup_supported);
if (r < 0)
return log_error_errno(r, "Failed to determine supported controllers: %m");
+
+ /* 9. Figure out which bpf-based pseudo-controllers are supported */
+ r = cg_bpf_mask_supported(&mask);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine supported bpf-based pseudo-controllers: %m");
+ m->cgroup_supported |= mask;
+
+ /* 10. Log which controllers are supported */
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++)
log_debug("Controller '%s' supported: %s", cgroup_controller_to_string(c), yes_no(m->cgroup_supported & CGROUP_CONTROLLER_TO_MASK(c)));
@@ -2401,7 +2770,7 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
return -ENODATA;
/* The root cgroup doesn't expose this information, let's get it from /proc instead */
- if (unit_has_root_cgroup(u))
+ if (unit_has_host_root_cgroup(u))
return procfs_memory_get_current(ret);
if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
@@ -2436,7 +2805,7 @@ int unit_get_tasks_current(Unit *u, uint64_t *ret) {
return -ENODATA;
/* The root cgroup doesn't expose this information, let's get it from /proc instead */
- if (unit_has_root_cgroup(u))
+ if (unit_has_host_root_cgroup(u))
return procfs_tasks_get_current(ret);
if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0)
@@ -2463,9 +2832,13 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
return -ENODATA;
/* The root cgroup doesn't expose this information, let's get it from /proc instead */
- if (unit_has_root_cgroup(u))
+ if (unit_has_host_root_cgroup(u))
return procfs_cpu_get_usage(ret);
+ /* Requisite controllers for CPU accounting are not enabled */
+ if ((get_cpu_accounting_mask() & ~u->cgroup_realized_mask) != 0)
+ return -ENODATA;
+
r = cg_all_unified();
if (r < 0)
return r;
@@ -2473,14 +2846,11 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
_cleanup_free_ char *val = NULL;
uint64_t us;
- if ((u->cgroup_realized_mask & CGROUP_MASK_CPU) == 0)
- return -ENODATA;
-
r = cg_get_keyed_attribute("cpu", u->cgroup_path, "cpu.stat", STRV_MAKE("usage_usec"), &val);
- if (r < 0)
- return r;
if (IN_SET(r, -ENOENT, -ENXIO))
return -ENODATA;
+ if (r < 0)
+ return r;
r = safe_atou64(val, &us);
if (r < 0)
@@ -2488,9 +2858,6 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
ns = us * NSEC_PER_USEC;
} else {
- if ((u->cgroup_realized_mask & CGROUP_MASK_CPUACCT) == 0)
- return -ENODATA;
-
r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v);
if (r == -ENOENT)
return -ENODATA;
@@ -2631,10 +2998,10 @@ void unit_invalidate_cgroup(Unit *u, CGroupMask m) {
if (m & (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT))
m |= CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT;
- if ((u->cgroup_realized_mask & m) == 0) /* NOP? */
+ if (FLAGS_SET(u->cgroup_invalidated_mask, m)) /* NOP? */
return;
- u->cgroup_realized_mask &= ~m;
+ u->cgroup_invalidated_mask |= m;
unit_add_to_cgroup_realize_queue(u);
}
@@ -2644,10 +3011,10 @@ void unit_invalidate_cgroup_bpf(Unit *u) {
if (!UNIT_HAS_CGROUP_CONTEXT(u))
return;
- if (u->cgroup_bpf_state == UNIT_CGROUP_BPF_INVALIDATED) /* NOP? */
+ if (u->cgroup_invalidated_mask & CGROUP_MASK_BPF_FIREWALL) /* NOP? */
return;
- u->cgroup_bpf_state = UNIT_CGROUP_BPF_INVALIDATED;
+ u->cgroup_invalidated_mask |= CGROUP_MASK_BPF_FIREWALL;
unit_add_to_cgroup_realize_queue(u);
/* If we are a slice unit, we also need to put compile a new BPF program for all our children, as the IP access
@@ -2658,13 +3025,8 @@ void unit_invalidate_cgroup_bpf(Unit *u) {
void *v;
HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i) {
- if (member == u)
- continue;
-
- if (UNIT_DEREF(member->slice) != u)
- continue;
-
- unit_invalidate_cgroup_bpf(member);
+ if (UNIT_DEREF(member->slice) == u)
+ unit_invalidate_cgroup_bpf(member);
}
}
}
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 2d2ff6fc3c..266daa20a5 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -12,6 +12,7 @@ typedef struct CGroupContext CGroupContext;
typedef struct CGroupDeviceAllow CGroupDeviceAllow;
typedef struct CGroupIODeviceWeight CGroupIODeviceWeight;
typedef struct CGroupIODeviceLimit CGroupIODeviceLimit;
+typedef struct CGroupIODeviceLatency CGroupIODeviceLatency;
typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight;
typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth;
@@ -51,6 +52,12 @@ struct CGroupIODeviceLimit {
uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX];
};
+struct CGroupIODeviceLatency {
+ LIST_FIELDS(CGroupIODeviceLatency, device_latencies);
+ char *path;
+ usec_t target_usec;
+};
+
struct CGroupBlockIODeviceWeight {
LIST_FIELDS(CGroupBlockIODeviceWeight, device_weights);
char *path;
@@ -81,7 +88,9 @@ struct CGroupContext {
uint64_t startup_io_weight;
LIST_HEAD(CGroupIODeviceWeight, io_device_weights);
LIST_HEAD(CGroupIODeviceLimit, io_device_limits);
+ LIST_HEAD(CGroupIODeviceLatency, io_device_latencies);
+ uint64_t memory_min;
uint64_t memory_low;
uint64_t memory_high;
uint64_t memory_max;
@@ -109,6 +118,8 @@ struct CGroupContext {
bool delegate;
CGroupMask delegate_controllers;
+
+ CGroupMask disable_controllers;
};
/* Used when querying IP accounting data */
@@ -128,29 +139,32 @@ void cgroup_context_init(CGroupContext *c);
void cgroup_context_done(CGroupContext *c);
void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix);
-CGroupMask cgroup_context_get_mask(CGroupContext *c);
-
void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a);
void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w);
void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l);
+void cgroup_context_free_io_device_latency(CGroupContext *c, CGroupIODeviceLatency *l);
void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w);
void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
+int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode);
+
CGroupMask unit_get_own_mask(Unit *u);
CGroupMask unit_get_delegate_mask(Unit *u);
CGroupMask unit_get_members_mask(Unit *u);
CGroupMask unit_get_siblings_mask(Unit *u);
CGroupMask unit_get_subtree_mask(Unit *u);
+CGroupMask unit_get_disable_mask(Unit *u);
+CGroupMask unit_get_ancestor_disable_mask(Unit *u);
CGroupMask unit_get_target_mask(Unit *u);
CGroupMask unit_get_enable_mask(Unit *u);
-bool unit_get_needs_bpf(Unit *u);
+void unit_invalidate_cgroup_members_masks(Unit *u);
-void unit_update_cgroup_members_masks(Unit *u);
+void unit_add_to_cgroup_realize_queue(Unit *u);
const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask);
-char *unit_default_cgroup_path(Unit *u);
+char *unit_default_cgroup_path(const Unit *u);
int unit_set_cgroup_path(Unit *u, const char *path);
int unit_pick_cgroup_path(Unit *u);
@@ -191,8 +205,8 @@ int unit_reset_ip_accounting(Unit *u);
cc ? cc->name : false; \
})
-bool manager_owns_root_cgroup(Manager *m);
-bool unit_has_root_cgroup(Unit *u);
+bool manager_owns_host_root_cgroup(Manager *m);
+bool unit_has_host_root_cgroup(Unit *u);
int manager_notify_cgroup_empty(Manager *m, const char *group);
diff --git a/src/core/chown-recursive.c b/src/core/chown-recursive.c
index c4794501c2..7767301f7d 100644
--- a/src/core/chown-recursive.c
+++ b/src/core/chown-recursive.c
@@ -1,17 +1,21 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
-#include "user-util.h"
-#include "macro.h"
-#include "fd-util.h"
-#include "dirent-util.h"
#include "chown-recursive.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "macro.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "user-util.h"
-static int chown_one(int fd, const char *name, const struct stat *st, uid_t uid, gid_t gid) {
- int r;
+static int chown_one(int fd, const struct stat *st, uid_t uid, gid_t gid) {
+ char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+ const char *n;
assert(fd >= 0);
assert(st);
@@ -20,98 +24,95 @@ static int chown_one(int fd, const char *name, const struct stat *st, uid_t uid,
(!gid_is_valid(gid) || st->st_gid == gid))
return 0;
- if (name)
- r = fchownat(fd, name, uid, gid, AT_SYMLINK_NOFOLLOW);
- else
- r = fchown(fd, uid, gid);
- if (r < 0)
- return -errno;
+ /* We change ownership through the /proc/self/fd/%i path, so that we have a stable reference that works with
+ * O_PATH. (Note: fchown() and fchmod() do not work with O_PATH, the kernel refuses that. */
+ xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- /* The linux kernel alters the mode in some cases of chown(). Let's undo this. */
- if (name) {
- if (!S_ISLNK(st->st_mode))
- r = fchmodat(fd, name, st->st_mode, 0);
- else /* There's currently no AT_SYMLINK_NOFOLLOW for fchmodat() */
- r = 0;
- } else
- r = fchmod(fd, st->st_mode);
- if (r < 0)
+ /* Drop any ACL if there is one */
+ FOREACH_STRING(n, "system.posix_acl_access", "system.posix_acl_default")
+ if (removexattr(procfs_path, n) < 0)
+ if (!IN_SET(errno, ENODATA, EOPNOTSUPP, ENOSYS, ENOTTY))
+ return -errno;
+
+ if (chown(procfs_path, uid, gid) < 0)
return -errno;
+ /* The linux kernel alters the mode in some cases of chown(), as well when we change ACLs. Let's undo this. We
+ * do this only for non-symlinks however. That's because for symlinks the access mode is ignored anyway and
+ * because on some kernels/file systems trying to change the access mode will succeed but has no effect while
+ * on others it actively fails. */
+ if (!S_ISLNK(st->st_mode))
+ if (chmod(procfs_path, st->st_mode & 07777) < 0)
+ return -errno;
+
return 1;
}
static int chown_recursive_internal(int fd, const struct stat *st, uid_t uid, gid_t gid) {
+ _cleanup_closedir_ DIR *d = NULL;
bool changed = false;
+ struct dirent *de;
int r;
assert(fd >= 0);
assert(st);
- if (S_ISDIR(st->st_mode)) {
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
-
- d = fdopendir(fd);
- if (!d) {
- r = -errno;
- goto finish;
- }
- fd = -1;
-
- FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) {
- struct stat fst;
-
- if (dot_or_dot_dot(de->d_name))
- continue;
-
- if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (S_ISDIR(fst.st_mode)) {
- int subdir_fd;
-
- subdir_fd = openat(dirfd(d), de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (subdir_fd < 0) {
- r = -errno;
- goto finish;
- }
-
- r = chown_recursive_internal(subdir_fd, &fst, uid, gid);
- if (r < 0)
- goto finish;
- if (r > 0)
- changed = true;
- } else {
- r = chown_one(dirfd(d), de->d_name, &fst, uid, gid);
- if (r < 0)
- goto finish;
- if (r > 0)
- changed = true;
- }
+ d = fdopendir(fd);
+ if (!d) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
+ _cleanup_close_ int path_fd = -1;
+ struct stat fst;
+
+ if (dot_or_dot_dot(de->d_name))
+ continue;
+
+ /* Let's pin the child inode we want to fix now with an O_PATH fd, so that it cannot be swapped out
+ * while we manipulate it. */
+ path_fd = openat(dirfd(d), de->d_name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (path_fd < 0)
+ return -errno;
+
+ if (fstat(path_fd, &fst) < 0)
+ return -errno;
+
+ if (S_ISDIR(fst.st_mode)) {
+ int subdir_fd;
+
+ /* Convert it to a "real" (i.e. non-O_PATH) fd now */
+ subdir_fd = fd_reopen(path_fd, O_RDONLY|O_CLOEXEC|O_NOATIME);
+ if (subdir_fd < 0)
+ return subdir_fd;
+
+ r = chown_recursive_internal(subdir_fd, &fst, uid, gid); /* takes possession of subdir_fd even on failure */
+ if (r < 0)
+ return r;
+ if (r > 0)
+ changed = true;
+ } else {
+ r = chown_one(path_fd, &fst, uid, gid);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ changed = true;
}
+ }
- r = chown_one(dirfd(d), NULL, st, uid, gid);
- } else
- r = chown_one(fd, NULL, st, uid, gid);
+ r = chown_one(dirfd(d), st, uid, gid);
if (r < 0)
- goto finish;
+ return r;
- r = r > 0 || changed;
-
-finish:
- safe_close(fd);
- return r;
+ return r > 0 || changed;
}
int path_chown_recursive(const char *path, uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
struct stat st;
- int r;
- fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (fd < 0)
return -errno;
@@ -128,8 +129,5 @@ int path_chown_recursive(const char *path, uid_t uid, gid_t gid) {
(!gid_is_valid(gid) || st.st_gid == gid))
return 0;
- r = chown_recursive_internal(fd, &st, uid, gid);
- fd = -1; /* we donated the fd to the call, regardless if it succeeded or failed */
-
- return r;
+ return chown_recursive_internal(TAKE_FD(fd), &st, uid, gid); /* we donate the fd to the call, regardless if it succeeded or failed */
}
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index 540bc77aed..53890bcafb 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -17,7 +17,7 @@
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
-static int property_get_delegate_controllers(
+static int property_get_cgroup_mask(
sd_bus *bus,
const char *path,
const char *interface,
@@ -26,26 +26,22 @@ static int property_get_delegate_controllers(
void *userdata,
sd_bus_error *error) {
- CGroupContext *c = userdata;
- CGroupController cc;
+ CGroupMask *mask = userdata;
+ CGroupController ctrl;
int r;
assert(bus);
assert(reply);
- assert(c);
-
- if (!c->delegate)
- return sd_bus_message_append(reply, "as", 0);
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)
return r;
- for (cc = 0; cc < _CGROUP_CONTROLLER_MAX; cc++) {
- if ((c->delegate_controllers & CGROUP_CONTROLLER_TO_MASK(cc)) == 0)
+ for (ctrl = 0; ctrl < _CGROUP_CONTROLLER_MAX; ctrl++) {
+ if ((*mask & CGROUP_CONTROLLER_TO_MASK(ctrl)) == 0)
continue;
- r = sd_bus_message_append(reply, "s", cgroup_controller_to_string(cc));
+ r = sd_bus_message_append(reply, "s", cgroup_controller_to_string(ctrl));
if (r < 0)
return r;
}
@@ -53,6 +49,27 @@ static int property_get_delegate_controllers(
return sd_bus_message_close_container(reply);
}
+static int property_get_delegate_controllers(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ CGroupContext *c = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ if (!c->delegate)
+ return sd_bus_message_append(reply, "as", 0);
+
+ return property_get_cgroup_mask(bus, path, interface, property, reply, &c->delegate_controllers, error);
+}
+
static int property_get_io_device_weight(
sd_bus *bus,
const char *path,
@@ -119,6 +136,36 @@ static int property_get_io_device_limits(
return sd_bus_message_close_container(reply);
}
+static int property_get_io_device_latency(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ CGroupContext *c = userdata;
+ CGroupIODeviceLatency *l;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ r = sd_bus_message_open_container(reply, 'a', "(st)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(device_latencies, l, c->io_device_latencies) {
+ r = sd_bus_message_append(reply, "(st)", l->path, l->target_usec);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
static int property_get_blockio_device_weight(
sd_bus *bus,
const char *path,
@@ -291,6 +338,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("IOWriteBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0),
SD_BUS_PROPERTY("IOReadIOPSMax", "a(st)", property_get_io_device_limits, 0, 0),
SD_BUS_PROPERTY("IOWriteIOPSMax", "a(st)", property_get_io_device_limits, 0, 0),
+ SD_BUS_PROPERTY("IODeviceLatencyTargetUSec", "a(st)", property_get_io_device_latency, 0, 0),
SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
SD_BUS_PROPERTY("BlockIOWeight", "t", NULL, offsetof(CGroupContext, blockio_weight), 0),
SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL, offsetof(CGroupContext, startup_blockio_weight), 0),
@@ -298,6 +346,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
+ SD_BUS_PROPERTY("MemoryMin", "t", NULL, offsetof(CGroupContext, memory_min), 0),
SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0),
SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0),
SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0),
@@ -310,6 +359,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("IPAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, ip_accounting), 0),
SD_BUS_PROPERTY("IPAddressAllow", "a(iayu)", property_get_ip_address_access, offsetof(CGroupContext, ip_address_allow), 0),
SD_BUS_PROPERTY("IPAddressDeny", "a(iayu)", property_get_ip_address_access, offsetof(CGroupContext, ip_address_deny), 0),
+ SD_BUS_PROPERTY("DisableControllers", "as", property_get_cgroup_mask, offsetof(CGroupContext, disable_controllers), 0),
SD_BUS_VTABLE_END
};
@@ -571,7 +621,7 @@ int bus_cgroup_set_property(
flags |= UNIT_PRIVATE;
if (streq(name, "CPUAccounting"))
- return bus_cgroup_set_boolean(u, name, &c->cpu_accounting, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU, message, flags, error);
+ return bus_cgroup_set_boolean(u, name, &c->cpu_accounting, get_cpu_accounting_mask(), message, flags, error);
if (streq(name, "CPUWeight"))
return bus_cgroup_set_cpu_weight(u, name, &c->cpu_weight, message, flags, error);
@@ -606,6 +656,9 @@ int bus_cgroup_set_property(
if (streq(name, "MemoryAccounting"))
return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, error);
+ if (streq(name, "MemoryMin"))
+ return bus_cgroup_set_memory(u, name, &c->memory_min, message, flags, error);
+
if (streq(name, "MemoryLow"))
return bus_cgroup_set_memory(u, name, &c->memory_low, message, flags, error);
@@ -621,6 +674,9 @@ int bus_cgroup_set_property(
if (streq(name, "MemoryLimit"))
return bus_cgroup_set_memory(u, name, &c->memory_limit, message, flags, error);
+ if (streq(name, "MemoryMinScale"))
+ return bus_cgroup_set_memory_scale(u, name, &c->memory_min, message, flags, error);
+
if (streq(name, "MemoryLowScale"))
return bus_cgroup_set_memory_scale(u, name, &c->memory_low, message, flags, error);
@@ -839,6 +895,86 @@ int bus_cgroup_set_property(
return 1;
+ } else if (streq(name, "IODeviceLatencyTargetUSec")) {
+ const char *path;
+ uint64_t target;
+ unsigned n = 0;
+
+ r = sd_bus_message_enter_container(message, 'a', "(st)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(st)", &path, &target)) > 0) {
+
+ if (!path_is_normalized(path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ CGroupIODeviceLatency *a = NULL, *b;
+
+ LIST_FOREACH(device_latencies, b, c->io_device_latencies) {
+ if (path_equal(b->path, path)) {
+ a = b;
+ break;
+ }
+ }
+
+ if (!a) {
+ a = new0(CGroupIODeviceLatency, 1);
+ if (!a)
+ return -ENOMEM;
+
+ a->path = strdup(path);
+ if (!a->path) {
+ free(a);
+ return -ENOMEM;
+ }
+ LIST_PREPEND(device_latencies, c->io_device_latencies, a);
+ }
+
+ a->target_usec = target;
+ }
+
+ n++;
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ char ts[FORMAT_TIMESPAN_MAX];
+ CGroupIODeviceLatency *a;
+ size_t size = 0;
+
+ if (n == 0) {
+ while (c->io_device_latencies)
+ cgroup_context_free_io_device_latency(c, c->io_device_latencies);
+ }
+
+ unit_invalidate_cgroup(u, CGROUP_MASK_IO);
+
+ f = open_memstream(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+
+ fputs("IODeviceLatencyTargetSec=\n", f);
+ LIST_FOREACH(device_latencies, a, c->io_device_latencies)
+ fprintf(f, "IODeviceLatencyTargetSec=%s %s\n",
+ a->path, format_timespan(ts, sizeof(ts), a->target_usec, 1));
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
+ unit_write_setting(u, flags, name, buf);
+ }
+
+ return 1;
+
} else if (STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
const char *path;
bool read = true;
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index c44970c10c..11301e4b69 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -27,7 +27,7 @@
#include "ioprio.h"
#include "journal-util.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "namespace.h"
#include "parse-util.h"
#include "path-util.h"
@@ -718,6 +718,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogLevelMax", "i", bus_property_get_int, offsetof(ExecContext, log_level_max), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LogRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(ExecContext, log_rate_limit_interval_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LogRateLimitBurst", "u", bus_property_get_unsigned, offsetof(ExecContext, log_rate_limit_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("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),
@@ -1015,8 +1017,6 @@ static BUS_DEFINE_SET_TRANSIENT_IS_VALID(log_level, "i", int32_t, int, "%" PRIi3
#if HAVE_SECCOMP
static BUS_DEFINE_SET_TRANSIENT_IS_VALID(errno, "i", int32_t, int, "%" PRIi32, errno_is_valid);
#endif
-static BUS_DEFINE_SET_TRANSIENT_IS_VALID(sched_priority, "i", int32_t, int, "%" PRIi32, sched_priority_is_valid);
-static BUS_DEFINE_SET_TRANSIENT_IS_VALID(nice, "i", int32_t, int, "%" PRIi32, nice_is_valid);
static BUS_DEFINE_SET_TRANSIENT_PARSE(std_input, ExecInput, exec_input_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(std_output, ExecOutput, exec_output_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(utmp_mode, ExecUtmpMode, exec_utmp_mode_from_string);
@@ -1027,7 +1027,6 @@ static BUS_DEFINE_SET_TRANSIENT_PARSE(preserve_mode, ExecPreserveMode, exec_pres
static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(personality, unsigned long, parse_personality);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(secure_bits, "i", int32_t, int, "%" PRIi32, secure_bits_to_string_alloc_with_check);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(capability, "t", uint64_t, uint64_t, "%" PRIu64, capability_set_to_string_alloc);
-static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(sched_policy, "i", int32_t, int, "%" PRIi32, sched_policy_to_string_alloc_with_check);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(namespace_flag, "t", uint64_t, unsigned long, "%" PRIu64, namespace_flags_to_string);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING(mount_flags, "t", uint64_t, unsigned long, "%" PRIu64, mount_propagation_flags_to_string_with_check);
@@ -1070,15 +1069,15 @@ int bus_exec_context_set_transient_property(
if (streq(name, "LogLevelMax"))
return bus_set_transient_log_level(u, name, &c->log_level_max, message, flags, error);
- if (streq(name, "CPUSchedulingPriority"))
- return bus_set_transient_sched_priority(u, name, &c->cpu_sched_priority, message, flags, error);
+ if (streq(name, "LogRateLimitIntervalUSec"))
+ return bus_set_transient_usec(u, name, &c->log_rate_limit_interval_usec, message, flags, error);
+
+ if (streq(name, "LogRateLimitBurst"))
+ return bus_set_transient_unsigned(u, name, &c->log_rate_limit_burst, message, flags, error);
if (streq(name, "Personality"))
return bus_set_transient_personality(u, name, &c->personality, message, flags, error);
- if (streq(name, "Nice"))
- return bus_set_transient_nice(u, name, &c->nice, message, flags, error);
-
if (streq(name, "StandardInput"))
return bus_set_transient_std_input(u, name, &c->std_input, message, flags, error);
@@ -1208,9 +1207,6 @@ int bus_exec_context_set_transient_property(
if (streq(name, "AmbientCapabilities"))
return bus_set_transient_capability(u, name, &c->capability_ambient_set, message, flags, error);
- if (streq(name, "CPUSchedulingPolicy"))
- return bus_set_transient_sched_policy(u, name, &c->cpu_sched_policy, message, flags, error);
-
if (streq(name, "RestrictNamespaces"))
return bus_set_transient_namespace_flag(u, name, &c->restrict_namespaces, message, flags, error);
@@ -1521,8 +1517,8 @@ int bus_exec_context_set_transient_property(
int af;
af = af_from_name(*s);
- if (af <= 0)
- return -EINVAL;
+ if (af < 0)
+ return af;
if (!invert == c->address_families_whitelist) {
r = set_put(c->address_families, INT_TO_PTR(af));
@@ -1609,6 +1605,72 @@ int bus_exec_context_set_transient_property(
return 1;
+ } else if (streq(name, "Nice")) {
+ int32_t q;
+
+ r = sd_bus_message_read(message, "i", &q);
+ if (r < 0)
+ return r;
+
+ if (!nice_is_valid(q))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Nice value: %i", q);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ c->nice = q;
+ c->nice_set = true;
+
+ unit_write_settingf(u, flags, name, "Nice=%i", q);
+ }
+
+ return 1;
+
+ } else if (streq(name, "CPUSchedulingPolicy")) {
+ int32_t q;
+
+ r = sd_bus_message_read(message, "i", &q);
+ if (r < 0)
+ return r;
+
+ if (!sched_policy_is_valid(q))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid CPU scheduling policy: %i", q);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ _cleanup_free_ char *s = NULL;
+
+ r = sched_policy_to_string_alloc(q, &s);
+ if (r < 0)
+ return r;
+
+ c->cpu_sched_policy = q;
+ c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(q), sched_get_priority_max(q));
+ c->cpu_sched_set = true;
+
+ unit_write_settingf(u, flags, name, "CPUSchedulingPolicy=%s", s);
+ }
+
+ return 1;
+
+ } else if (streq(name, "CPUSchedulingPriority")) {
+ int32_t p, min, max;
+
+ r = sd_bus_message_read(message, "i", &p);
+ if (r < 0)
+ return r;
+
+ min = sched_get_priority_min(c->cpu_sched_policy);
+ max = sched_get_priority_max(c->cpu_sched_policy);
+ if (p < min || p > max)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid CPU scheduling priority: %i", p);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ c->cpu_sched_priority = p;
+ c->cpu_sched_set = true;
+
+ unit_write_settingf(u, flags, name, "CPUSchedulingPriority=%i", p);
+ }
+
+ return 1;
+
} else if (streq(name, "IOSchedulingClass")) {
int32_t q;
@@ -1731,7 +1793,10 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (STR_IN_SET(name, "StandardInputFile", "StandardOutputFile", "StandardErrorFile")) {
+ } else if (STR_IN_SET(name,
+ "StandardInputFile",
+ "StandardOutputFile", "StandardOutputFileToAppend",
+ "StandardErrorFile", "StandardErrorFileToAppend")) {
const char *s;
r = sd_bus_message_read(message, "s", &s);
@@ -1755,23 +1820,34 @@ int bus_exec_context_set_transient_property(
c->std_input = EXEC_INPUT_FILE;
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=file:%s", s);
- } else if (streq(name, "StandardOutputFile")) {
+ } else if (STR_IN_SET(name, "StandardOutputFile", "StandardOutputFileToAppend")) {
r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], empty_to_null(s));
if (r < 0)
return r;
- c->std_output = EXEC_OUTPUT_FILE;
- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=file:%s", s);
-
+ if (streq(name, "StandardOutputFile")) {
+ c->std_output = EXEC_OUTPUT_FILE;
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=file:%s", s);
+ } else {
+ assert(streq(name, "StandardOutputFileToAppend"));
+ c->std_output = EXEC_OUTPUT_FILE_APPEND;
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=append:%s", s);
+ }
} else {
- assert(streq(name, "StandardErrorFile"));
+ assert(STR_IN_SET(name, "StandardErrorFile", "StandardErrorFileToAppend"));
r = free_and_strdup(&c->stdio_file[STDERR_FILENO], empty_to_null(s));
if (r < 0)
return r;
- c->std_error = EXEC_OUTPUT_FILE;
- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardError=file:%s", s);
+ if (streq(name, "StandardErrorFile")) {
+ c->std_error = EXEC_OUTPUT_FILE;
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardError=file:%s", s);
+ } else {
+ assert(streq(name, "StandardErrorFileToAppend"));
+ c->std_error = EXEC_OUTPUT_FILE_APPEND;
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardError=append:%s", s);
+ }
}
}
diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c
index 5551c56d0e..d11e58b51d 100644
--- a/src/core/dbus-job.c
+++ b/src/core/dbus-job.c
@@ -4,6 +4,7 @@
#include "alloc-util.h"
#include "dbus-job.h"
+#include "dbus-unit.h"
#include "dbus.h"
#include "job.h"
#include "log.h"
@@ -50,7 +51,7 @@ int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error
/* Access is granted to the job owner */
if (!sd_bus_track_contains(j->bus_track, sd_bus_message_get_sender(message))) {
- /* And for everybody else consult PolicyKit */
+ /* And for everybody else consult polkit */
r = bus_verify_manage_units_async(j->unit->manager, message, error);
if (r < 0)
return r;
@@ -173,6 +174,9 @@ void bus_job_send_change_signal(Job *j) {
assert(j);
+ /* Make sure that any change signal on the unit is reflected before we send out the change signal on the job */
+ bus_unit_send_pending_change_signal(j->unit, true);
+
if (j->in_dbus_queue) {
LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j);
j->in_dbus_queue = false;
@@ -185,6 +189,21 @@ void bus_job_send_change_signal(Job *j) {
j->sent_dbus_new_signal = true;
}
+void bus_job_send_pending_change_signal(Job *j, bool including_new) {
+ assert(j);
+
+ if (!j->in_dbus_queue)
+ return;
+
+ if (!j->sent_dbus_new_signal && !including_new)
+ return;
+
+ if (MANAGER_IS_RELOADING(j->unit->manager))
+ return;
+
+ bus_job_send_change_signal(j);
+}
+
static int send_removed_signal(sd_bus *bus, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
@@ -222,6 +241,9 @@ void bus_job_send_removed_signal(Job *j) {
if (!j->sent_dbus_new_signal)
bus_job_send_change_signal(j);
+ /* Make sure that any change signal on the unit is reflected before we send out the change signal on the job */
+ bus_unit_send_pending_change_signal(j->unit, true);
+
r = bus_foreach_bus(j->manager, j->bus_track, send_removed_signal, j);
if (r < 0)
log_debug_errno(r, "Failed to send job remove signal for %u: %m", j->id);
diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h
index 3cc60f22ee..c9f6fc7187 100644
--- a/src/core/dbus-job.h
+++ b/src/core/dbus-job.h
@@ -12,6 +12,7 @@ int bus_job_method_cancel(sd_bus_message *message, void *job, sd_bus_error *erro
int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error);
void bus_job_send_change_signal(Job *j);
+void bus_job_send_pending_change_signal(Job *j, bool including_new);
void bus_job_send_removed_signal(Job *j);
int bus_job_coldplug_bus_track(Job *j);
diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c
index 028e7ec1c1..e2b3a0d517 100644
--- a/src/core/dbus-kill.c
+++ b/src/core/dbus-kill.c
@@ -12,13 +12,17 @@ const sd_bus_vtable bus_kill_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("KillMode", "s", property_get_kill_mode, offsetof(KillContext, kill_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KillSignal", "i", bus_property_get_int, offsetof(KillContext, kill_signal), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("FinalKillSignal", "i", bus_property_get_int, offsetof(KillContext, final_kill_signal), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SendSIGKILL", "b", bus_property_get_bool, offsetof(KillContext, send_sigkill), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SendSIGHUP", "b", bus_property_get_bool, offsetof(KillContext, send_sighup), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("WatchdogSignal", "i", bus_property_get_int, offsetof(KillContext, watchdog_signal), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
static BUS_DEFINE_SET_TRANSIENT_PARSE(kill_mode, KillMode, kill_mode_from_string);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING(kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING(final_kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING(watchdog_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
int bus_kill_context_set_transient_property(
Unit *u,
@@ -47,5 +51,11 @@ int bus_kill_context_set_transient_property(
if (streq(name, "KillSignal"))
return bus_set_transient_kill_signal(u, name, &c->kill_signal, message, flags, error);
+ if (streq(name, "FinalKillSignal"))
+ return bus_set_transient_final_kill_signal(u, name, &c->final_kill_signal, message, flags, error);
+
+ if (streq(name, "WatchdogSignal"))
+ return bus_set_transient_watchdog_signal(u, name, &c->watchdog_signal, message, flags, error);
+
return 0;
}
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 4ed68af1e0..8da07adfe7 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -12,6 +12,7 @@
#include "dbus-execute.h"
#include "dbus-job.h"
#include "dbus-manager.h"
+#include "dbus-scope.h"
#include "dbus-unit.h"
#include "dbus.h"
#include "env-util.h"
@@ -216,6 +217,30 @@ static int property_get_progress(
return sd_bus_message_append(reply, "d", d);
}
+static int property_get_environment(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_strv_free_ char **l = NULL;
+ Manager *m = userdata;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(m);
+
+ r = manager_get_effective_environment(m, &l);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_append_strv(reply, l);
+}
+
static int property_get_show_status(
sd_bus *bus,
const char *path,
@@ -232,7 +257,7 @@ static int property_get_show_status(
assert(reply);
assert(m);
- b = m->show_status > 0;
+ b = IN_SET(m->show_status, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
return sd_bus_message_append_basic(reply, 'b', &b);
}
@@ -1298,9 +1323,9 @@ int verify_run_space_and_log(const char *message) {
r = verify_run_space(message, &error);
if (r < 0)
- log_error_errno(r, "%s", bus_error_message(&error, r));
+ return log_error_errno(r, "%s", bus_error_message(&error, r));
- return r;
+ return 0;
}
static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1329,12 +1354,12 @@ static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *
* is finished. That way the caller knows when the reload
* finished. */
- assert(!m->queued_message);
- r = sd_bus_message_new_method_return(message, &m->queued_message);
+ assert(!m->pending_reload_message);
+ r = sd_bus_message_new_method_return(message, &m->pending_reload_message);
if (r < 0)
return r;
- m->exit_code = MANAGER_RELOAD;
+ m->objective = MANAGER_RELOAD;
return 1;
}
@@ -1363,7 +1388,7 @@ static int method_reexecute(sd_bus_message *message, void *userdata, sd_bus_erro
/* We don't send a reply back here, the client should
* just wait for us disconnecting. */
- m->exit_code = MANAGER_REEXECUTE;
+ m->objective = MANAGER_REEXECUTE;
return 1;
}
@@ -1383,7 +1408,7 @@ static int method_exit(sd_bus_message *message, void *userdata, sd_bus_error *er
* systemd-shutdown if it cannot do the exit() because it isn't a
* container. */
- m->exit_code = MANAGER_EXIT;
+ m->objective = MANAGER_EXIT;
return sd_bus_reply_method_return(message, NULL);
}
@@ -1402,7 +1427,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
- m->exit_code = MANAGER_REBOOT;
+ m->objective = MANAGER_REBOOT;
return sd_bus_reply_method_return(message, NULL);
}
@@ -1421,7 +1446,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
- m->exit_code = MANAGER_POWEROFF;
+ m->objective = MANAGER_POWEROFF;
return sd_bus_reply_method_return(message, NULL);
}
@@ -1440,7 +1465,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Halt is only supported for system managers.");
- m->exit_code = MANAGER_HALT;
+ m->objective = MANAGER_HALT;
return sd_bus_reply_method_return(message, NULL);
}
@@ -1459,7 +1484,7 @@ static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *e
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "KExec is only supported for system managers.");
- m->exit_code = MANAGER_KEXEC;
+ m->objective = MANAGER_KEXEC;
return sd_bus_reply_method_return(message, NULL);
}
@@ -1549,7 +1574,7 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
free(m->switch_root_init);
m->switch_root_init = ri;
- m->exit_code = MANAGER_SWITCH_ROOT;
+ m->objective = MANAGER_SWITCH_ROOT;
return sd_bus_reply_method_return(message, NULL);
}
@@ -1578,7 +1603,7 @@ static int method_set_environment(sd_bus_message *message, void *userdata, sd_bu
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- r = manager_environment_add(m, NULL, plus);
+ r = manager_client_environment_modify(m, NULL, plus);
if (r < 0)
return r;
@@ -1610,7 +1635,7 @@ static int method_unset_environment(sd_bus_message *message, void *userdata, sd_
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- r = manager_environment_add(m, minus, NULL);
+ r = manager_client_environment_modify(m, minus, NULL);
if (r < 0)
return r;
@@ -1648,7 +1673,7 @@ static int method_unset_and_set_environment(sd_bus_message *message, void *userd
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- r = manager_environment_add(m, minus, plus);
+ r = manager_client_environment_modify(m, minus, plus);
if (r < 0)
return r;
@@ -2398,6 +2423,29 @@ static int method_get_job_waiting(sd_bus_message *message, void *userdata, sd_bu
return bus_job_method_get_waiting_jobs(message, j, error);
}
+static int method_abandon_scope(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = bus_get_unit_by_name(m, message, name, &u, error);
+ if (r < 0)
+ return r;
+
+ if (u->type != UNIT_SCOPE)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit '%s' is not a scope unit, refusing.", name);
+
+ return bus_scope_method_abandon(message, u, error);
+}
+
const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -2418,6 +2466,12 @@ const sd_bus_vtable bus_manager_vtable[] = {
BUS_PROPERTY_DUAL_TIMESTAMP("GeneratorsFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_GENERATORS_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("UnitsLoadStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_UNITS_LOAD_START]), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("UnitsLoadFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_UNITS_LOAD_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
+ BUS_PROPERTY_DUAL_TIMESTAMP("InitRDSecurityStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_SECURITY_START]), SD_BUS_VTABLE_PROPERTY_CONST),
+ BUS_PROPERTY_DUAL_TIMESTAMP("InitRDSecurityFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_SECURITY_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
+ BUS_PROPERTY_DUAL_TIMESTAMP("InitRDGeneratorsStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_GENERATORS_START]), SD_BUS_VTABLE_PROPERTY_CONST),
+ BUS_PROPERTY_DUAL_TIMESTAMP("InitRDGeneratorsFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
+ BUS_PROPERTY_DUAL_TIMESTAMP("InitRDUnitsLoadStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START]), SD_BUS_VTABLE_PROPERTY_CONST),
+ BUS_PROPERTY_DUAL_TIMESTAMP("InitRDUnitsLoadFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", property_get_log_level, property_set_log_level, 0, 0),
SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", property_get_log_target, property_set_log_target, 0, 0),
SD_BUS_PROPERTY("NNames", "u", property_get_hashmap_size, offsetof(Manager, units), 0),
@@ -2426,7 +2480,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("NInstalledJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_installed_jobs), 0),
SD_BUS_PROPERTY("NFailedJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_failed_jobs), 0),
SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
- SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(Manager, environment), 0),
+ SD_BUS_PROPERTY("Environment", "as", property_get_environment, 0, 0),
SD_BUS_PROPERTY("ConfirmSpawn", "b", bus_property_get_bool, offsetof(Manager, confirm_spawn), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ShowStatus", "b", property_get_show_status, 0, 0),
SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -2507,6 +2561,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("AttachProcessesToUnit", "ssau", NULL, method_attach_processes_to_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("AbandonScope", "s", NULL, method_abandon_scope, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJobAfter", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJobBefore", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 3f98d3ecf0..b6d61627eb 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -145,7 +145,7 @@ int bus_mount_set_property(
int bus_mount_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c
index 6725f62794..bb807df2e9 100644
--- a/src/core/dbus-scope.c
+++ b/src/core/dbus-scope.c
@@ -14,7 +14,7 @@
#include "selinux-access.h"
#include "unit.h"
-static int bus_scope_abandon(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+int bus_scope_method_abandon(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Scope *s = userdata;
int r;
@@ -48,7 +48,7 @@ const sd_bus_vtable bus_scope_vtable[] = {
SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Scope, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Scope, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_SIGNAL("RequestStop", NULL, 0),
- SD_BUS_METHOD("Abandon", NULL, NULL, bus_scope_abandon, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Abandon", NULL, NULL, bus_scope_method_abandon, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
@@ -186,7 +186,7 @@ int bus_scope_set_property(
int bus_scope_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-scope.h b/src/core/dbus-scope.h
index 7c080dbcf7..702f55898d 100644
--- a/src/core/dbus-scope.h
+++ b/src/core/dbus-scope.h
@@ -14,4 +14,6 @@ int bus_scope_commit_properties(Unit *u);
int bus_scope_send_request_stop(Scope *s);
+int bus_scope_method_abandon(sd_bus_message *message, void *userdata, sd_bus_error *error);
+
int bus_scope_track_controller(Scope *s);
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 1b4c98c7d2..ec61ea2772 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -105,7 +105,7 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
- SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* 😷 deprecated */
SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemainAfterExit", "b", bus_property_get_bool, offsetof(Service, remain_after_exit), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("GuessMainPID", "b", bus_property_get_bool, offsetof(Service, guess_main_pid), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -120,8 +120,8 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("StatusText", "s", NULL, offsetof(Service, status_text), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("StatusErrno", "i", bus_property_get_int, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
- SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
- SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("NRestarts", "u", bus_property_get_unsigned, offsetof(Service, n_restarts), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -312,8 +312,40 @@ static int bus_service_set_transient_property(
if (streq(name, "NotifyAccess"))
return bus_set_transient_notify_access(u, name, &s->notify_access, message, flags, error);
- if (streq(name, "PIDFile"))
- return bus_set_transient_path(u, name, &s->pid_file, message, flags, error);
+ if (streq(name, "PIDFile")) {
+ _cleanup_free_ char *n = NULL;
+ const char *v, *e;
+
+ r = sd_bus_message_read(message, "s", &v);
+ if (r < 0)
+ return r;
+
+ n = path_make_absolute(v, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
+ if (!n)
+ return -ENOMEM;
+
+ path_simplify(n, true);
+
+ if (!path_is_normalized(n))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PIDFile= path '%s' is not valid", n);
+
+ e = path_startswith(n, "/var/run/");
+ if (e) {
+ char *z;
+
+ z = strjoin("/run/", e);
+ if (!z)
+ return log_oom();
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags))
+ log_unit_notice(u, "Transient unit's PIDFile= property references path below legacy directory /var/run, updating %s → %s; please update client accordingly.", n, z);
+
+ free_and_replace(s->pid_file, z);
+ } else
+ free_and_replace(s->pid_file, n);
+
+ return 1;
+ }
if (streq(name, "USBFunctionDescriptors"))
return bus_set_transient_path(u, name, &s->usb_function_descriptors, message, flags, error);
@@ -392,7 +424,7 @@ int bus_service_set_property(
int bus_service_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c
index 722a5688a5..effd5fa5d7 100644
--- a/src/core/dbus-slice.c
+++ b/src/core/dbus-slice.c
@@ -28,7 +28,7 @@ int bus_slice_set_property(
int bus_slice_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index 913cc74918..37cf9d204c 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -8,10 +8,10 @@
#include "dbus-socket.h"
#include "dbus-util.h"
#include "fd-util.h"
+#include "ip-protocol-list.h"
#include "parse-util.h"
#include "path-util.h"
#include "socket.h"
-#include "socket-protocol-list.h"
#include "socket-util.h"
#include "string-util.h"
#include "unit.h"
@@ -138,14 +138,14 @@ static inline bool check_size_t_truncation(uint64_t t) {
return (size_t) t == t;
}
-static inline const char* supported_socket_protocol_to_string(int32_t i) {
+static inline const char* socket_protocol_to_string(int32_t i) {
if (i == IPPROTO_IP)
return "";
if (!IN_SET(i, IPPROTO_UDPLITE, IPPROTO_SCTP))
return NULL;
- return socket_protocol_to_name(i);
+ return ip_protocol_to_name(i);
}
static BUS_DEFINE_SET_TRANSIENT(int, "i", int32_t, int, "%" PRIi32);
@@ -155,7 +155,7 @@ static BUS_DEFINE_SET_TRANSIENT_PARSE(bind_ipv6_only, SocketAddressBindIPv6Only,
static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(fdname, fdname_is_valid);
static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(ifname, ifname_valid);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(ip_tos, "i", int32_t, int, "%" PRIi32, ip_tos_to_string_alloc);
-static BUS_DEFINE_SET_TRANSIENT_TO_STRING(socket_protocol, "i", int32_t, int, "%" PRIi32, supported_socket_protocol_to_string);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING(socket_protocol, "i", int32_t, int, "%" PRIi32, socket_protocol_to_string);
static int bus_socket_set_transient_property(
Socket *s,
@@ -351,16 +351,27 @@ static int bus_socket_set_transient_property(
while ((r = sd_bus_message_read(message, "(ss)", &t, &a)) > 0) {
_cleanup_free_ SocketPort *p = NULL;
- p = new0(SocketPort, 1);
+ p = new(SocketPort, 1);
if (!p)
return log_oom();
+ *p = (SocketPort) {
+ .fd = -1,
+ .socket = s,
+ };
+
p->type = socket_port_type_from_string(t);
if (p->type < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown Socket type: %s", t);
if (p->type != SOCKET_SOCKET) {
+ if (!path_is_valid(p->path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid socket path: %s", t);
+
p->path = strdup(a);
+ if (!p->path)
+ return log_oom();
+
path_simplify(p->path, false);
} else if (streq(t, "Netlink")) {
@@ -381,21 +392,10 @@ static int bus_socket_set_transient_property(
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Address family not supported: %s", a);
}
- p->fd = -1;
- p->auxiliary_fds = NULL;
- p->n_auxiliary_fds = 0;
- p->socket = s;
-
empty = false;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- SocketPort *tail;
-
- LIST_FIND_TAIL(port, s->ports, tail);
- LIST_INSERT_AFTER(port, s->ports, tail, p);
-
- p = NULL;
-
+ LIST_APPEND(port, s->ports, TAKE_PTR(p));
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "Listen%s=%s", t, a);
}
}
@@ -461,7 +461,7 @@ int bus_socket_set_property(
int bus_socket_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
index b272d10113..353fa20132 100644
--- a/src/core/dbus-swap.c
+++ b/src/core/dbus-swap.c
@@ -63,7 +63,7 @@ int bus_swap_set_property(
int bus_swap_commit_properties(Unit *u) {
assert(u);
- unit_update_cgroup_members_masks(u);
+ unit_invalidate_cgroup_members_masks(u);
unit_realize_cgroup(u);
return 0;
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index ae0410414e..968166ee60 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -434,7 +434,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
u,
"kill",
CAP_KILL,
- N_("Authentication is required to kill '$(unit)'."),
+ N_("Authentication is required to send a UNIX signal to the processes of '$(unit)'."),
true,
message,
error);
@@ -561,6 +561,44 @@ int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error
return sd_bus_reply_method_return(message, NULL);
}
+static int property_get_refs(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Unit *u = userdata;
+ const char *i;
+ int r;
+
+ assert(bus);
+ assert(reply);
+
+ r = sd_bus_message_open_container(reply, 'a', "s");
+ if (r < 0)
+ return r;
+
+ for (i = sd_bus_track_first(u->bus_track); i; i = sd_bus_track_next(u->bus_track)) {
+ int c, k;
+
+ c = sd_bus_track_count_name(u->bus_track, i);
+ if (c < 0)
+ return c;
+
+ /* Add the item multiple times if the ref count for each is above 1 */
+ for (k = 0; k < c; k++) {
+ r = sd_bus_message_append(reply, "s", i);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -624,8 +662,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("AssertResult", "b", bus_property_get_bool, offsetof(Unit, assert_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_PROPERTY_DUAL_TIMESTAMP("ConditionTimestamp", offsetof(Unit, condition_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_PROPERTY_DUAL_TIMESTAMP("AssertTimestamp", offsetof(Unit, assert_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
- SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, offsetof(Unit, conditions), 0),
- SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0),
+ SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, offsetof(Unit, conditions), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+ SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Perpetual", "b", bus_property_get_bool, offsetof(Unit, perpetual), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -633,10 +671,13 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("FailureAction", "s", property_get_emergency_action, offsetof(Unit, failure_action), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("FailureActionExitStatus", "i", bus_property_get_int, offsetof(Unit, failure_action_exit_status), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SuccessAction", "s", property_get_emergency_action, offsetof(Unit, success_action), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SuccessActionExitStatus", "i", bus_property_get_int, offsetof(Unit, success_action_exit_status), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), 0),
- SD_BUS_PROPERTY("CollectMode", "s", property_get_collect_mode, offsetof(Unit, collect_mode), 0),
+ SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("CollectMode", "s", property_get_collect_mode, offsetof(Unit, collect_mode), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("Refs", "as", property_get_refs, 0, 0),
SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1033,7 +1074,7 @@ int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
- /* Let's validate security: if the sender is root, then all is OK. If the sender is is any other unit,
+ /* Let's validate security: if the sender is root, then all is OK. If the sender is any other unit,
* then the process' UID and the target unit's UID have to match the sender's UID */
if (sender_uid != 0 && sender_uid != getuid()) {
r = get_process_uid(pid, &process_uid);
@@ -1161,6 +1202,27 @@ void bus_unit_send_change_signal(Unit *u) {
u->sent_dbus_new_signal = true;
}
+void bus_unit_send_pending_change_signal(Unit *u, bool including_new) {
+
+ /* Sends out any pending change signals, but only if they really are pending. This call is used when we are
+ * about to change state in order to force out a PropertiesChanged signal beforehand if there was one pending
+ * so that clients can follow the full state transition */
+
+ if (!u->in_dbus_queue) /* If not enqueued, don't bother */
+ return;
+
+ if (!u->sent_dbus_new_signal && !including_new) /* If the unit was never announced, don't bother, it's fine if
+ * the unit appears in the new state right-away (except if the
+ * caller explicitly asked us to send it anyway) */
+ return;
+
+ if (MANAGER_IS_RELOADING(u->manager)) /* Don't generate unnecessary PropertiesChanged signals for the same unit
+ * when we are reloading. */
+ return;
+
+ bus_unit_send_change_signal(u);
+}
+
static int send_removed_signal(sd_bus *bus, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
@@ -1259,6 +1321,9 @@ int bus_unit_queue_job(
if (!path)
return -ENOMEM;
+ /* Before we send the method reply, force out the announcement JobNew for this job */
+ bus_job_send_pending_change_signal(j, true);
+
return sd_bus_reply_method_return(message, "o", path);
}
@@ -1299,8 +1364,75 @@ static int bus_unit_set_live_property(
return 0;
}
+static int bus_set_transient_emergency_action(
+ Unit *u,
+ const char *name,
+ EmergencyAction *p,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+
+ const char *s;
+ EmergencyAction v;
+ int r;
+ bool system;
+
+ assert(p);
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ system = MANAGER_IS_SYSTEM(u->manager);
+ r = parse_emergency_action(s, system, &v);
+ if (v < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ v == -EOPNOTSUPP ? "EmergencyAction setting invalid for manager type: %s"
+ : "Invalid %s setting: %s",
+ name, s);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ *p = v;
+ unit_write_settingf(u, flags, name,
+ "%s=%s", name, s);
+ }
+
+ return 1;
+}
+
+static int bus_set_transient_exit_status(
+ Unit *u,
+ const char *name,
+ int *p,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+
+ int32_t k;
+ int r;
+
+ assert(p);
+
+ r = sd_bus_message_read(message, "i", &k);
+ if (r < 0)
+ return r;
+
+ if (k > 255)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Exit status must be in range 0…255 or negative.");
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ *p = k < 0 ? -1 : k;
+
+ if (k < 0)
+ unit_write_settingf(u, flags, name, "%s=", name);
+ else
+ unit_write_settingf(u, flags, name, "%s=%i", name, k);
+ }
+
+ return 1;
+}
+
static BUS_DEFINE_SET_TRANSIENT_PARSE(collect_mode, CollectMode, collect_mode_from_string);
-static BUS_DEFINE_SET_TRANSIENT_PARSE(emergency_action, EmergencyAction, emergency_action_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(job_mode, JobMode, job_mode_from_string);
static int bus_set_transient_conditions(
@@ -1450,6 +1582,12 @@ static int bus_unit_set_transient_property(
if (streq(name, "SuccessAction"))
return bus_set_transient_emergency_action(u, name, &u->success_action, message, flags, error);
+ if (streq(name, "FailureActionExitStatus"))
+ return bus_set_transient_exit_status(u, name, &u->failure_action_exit_status, message, flags, error);
+
+ if (streq(name, "SuccessActionExitStatus"))
+ return bus_set_transient_exit_status(u, name, &u->success_action_exit_status, message, flags, error);
+
if (streq(name, "RebootArgument"))
return bus_set_transient_string(u, name, &u->reboot_arg, message, flags, error);
@@ -1572,7 +1710,7 @@ static int bus_unit_set_transient_property(
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
_cleanup_free_ char *label = NULL;
- r = unit_add_dependency_by_name(u, d, other, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_dependency_by_name(u, d, other, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
@@ -1726,7 +1864,7 @@ int bus_unit_validate_load_state(Unit *u, sd_bus_error *error) {
return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, "Unit %s has a bad unit file setting.", u->id);
case UNIT_ERROR: /* Only show .load_error in UNIT_ERROR state */
- return sd_bus_error_set_errnof(error, u->load_error, "Unit %s failed to loaded properly: %m.", u->id);
+ return sd_bus_error_set_errnof(error, u->load_error, "Unit %s failed to load properly: %m.", u->id);
case UNIT_MASKED:
return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit %s is masked.", u->id);
@@ -1746,7 +1884,13 @@ static int bus_unit_track_handler(sd_bus_track *t, void *userdata) {
u->bus_track = sd_bus_track_unref(u->bus_track); /* make sure we aren't called again */
+ /* If the client that tracks us disappeared, then there's reason to believe that the cgroup is empty now too,
+ * let's see */
+ unit_add_to_cgroup_empty_queue(u);
+
+ /* Also add the unit to the GC queue, after all if the client left it might be time to GC this unit */
unit_add_to_gc_queue(u);
+
return 0;
}
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index 68eb621836..345345e3eb 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -11,6 +11,7 @@ extern const sd_bus_vtable bus_unit_vtable[];
extern const sd_bus_vtable bus_unit_cgroup_vtable[];
void bus_unit_send_change_signal(Unit *u);
+void bus_unit_send_pending_change_signal(Unit *u, bool including_new);
void bus_unit_send_removed_signal(Unit *u);
int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error);
diff --git a/src/core/dbus.c b/src/core/dbus.c
index bf5917696e..5908ad792a 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -36,6 +36,7 @@
#include "mkdir.h"
#include "process-util.h"
#include "selinux-access.h"
+#include "serialize.h"
#include "service.h"
#include "special.h"
#include "string-util.h"
@@ -47,23 +48,22 @@
static void destroy_bus(Manager *m, sd_bus **bus);
-int bus_send_queued_message(Manager *m) {
+int bus_send_pending_reload_message(Manager *m) {
int r;
assert(m);
- if (!m->queued_message)
+ if (!m->pending_reload_message)
return 0;
- /* If we cannot get rid of this message we won't dispatch any
- * D-Bus messages, so that we won't end up wanting to queue
- * another message. */
+ /* If we cannot get rid of this message we won't dispatch any D-Bus messages, so that we won't end up wanting
+ * to queue another message. */
- r = sd_bus_send(NULL, m->queued_message, NULL);
+ r = sd_bus_send(NULL, m->pending_reload_message, NULL);
if (r < 0)
- log_warning_errno(r, "Failed to send queued message: %m");
+ log_warning_errno(r, "Failed to send queued message, ignoring: %m");
- m->queued_message = sd_bus_message_unref(m->queued_message);
+ m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message);
return 0;
}
@@ -974,12 +974,9 @@ int bus_init_system(Manager *m) {
int bus_init_private(Manager *m) {
_cleanup_close_ int fd = -1;
- union sockaddr_union sa = {
- .un.sun_family = AF_UNIX
- };
+ union sockaddr_union sa = {};
sd_event_source *s;
- socklen_t salen;
- int r;
+ int r, salen;
assert(m);
@@ -992,27 +989,23 @@ int bus_init_private(Manager *m) {
if (getpid_cached() != 1)
return 0;
- strcpy(sa.un.sun_path, "/run/systemd/private");
- salen = SOCKADDR_UN_LEN(sa.un);
+ salen = sockaddr_un_set_path(&sa.un, "/run/systemd/private");
} else {
- size_t left = sizeof(sa.un.sun_path);
- char *p = sa.un.sun_path;
- const char *e;
+ const char *e, *joined;
e = secure_getenv("XDG_RUNTIME_DIR");
- if (!e) {
- log_error("Failed to determine XDG_RUNTIME_DIR");
- return -EHOSTDOWN;
- }
-
- left = strpcpy(&p, left, e);
- left = strpcpy(&p, left, "/systemd/private");
+ if (!e)
+ return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
+ "XDG_RUNTIME_DIR is not set, refusing.");
- salen = sizeof(sa.un) - left;
+ joined = strjoina(e, "/systemd/private");
+ salen = 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");
(void) mkdir_parents_label(sa.un.sun_path, 0755);
- (void) unlink(sa.un.sun_path);
+ (void) sockaddr_un_unlink(&sa.un);
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
@@ -1035,9 +1028,8 @@ int bus_init_private(Manager *m) {
(void) sd_event_source_set_description(s, "bus-connection");
- m->private_listen_fd = fd;
+ m->private_listen_fd = TAKE_FD(fd);
m->private_listen_event_source = s;
- fd = -1;
log_debug("Successfully created private D-Bus server.");
@@ -1079,8 +1071,8 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
u->bus_track = sd_bus_track_unref(u->bus_track);
/* Get rid of queued message on this bus */
- if (m->queued_message && sd_bus_message_get_bus(m->queued_message) == *bus)
- m->queued_message = sd_bus_message_unref(m->queued_message);
+ if (m->pending_reload_message && sd_bus_message_get_bus(m->pending_reload_message) == *bus)
+ m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message);
/* Possibly flush unwritten data, but only if we are
* unprivileged, since we don't want to sync here */
@@ -1211,13 +1203,8 @@ void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) {
int c, j;
c = sd_bus_track_count_name(t, n);
-
- for (j = 0; j < c; j++) {
- fputs(prefix, f);
- fputc('=', f);
- fputs(n, f);
- fputc('\n', f);
- }
+ for (j = 0; j < c; j++)
+ (void) serialize_item(f, prefix, n);
}
}
diff --git a/src/core/dbus.h b/src/core/dbus.h
index 382a96da7d..f1c0fa86c0 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -5,7 +5,7 @@
#include "manager.h"
-int bus_send_queued_message(Manager *m);
+int bus_send_pending_reload_message(Manager *m);
int bus_init_private(Manager *m);
int bus_init_api(Manager *m);
diff --git a/src/core/device.c b/src/core/device.c
index a2d00a0fbe..960f403718 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -3,19 +3,20 @@
#include <errno.h>
#include <sys/epoll.h>
-#include "libudev.h"
-
#include "alloc-util.h"
#include "bus-error.h"
#include "dbus-device.h"
+#include "dbus-unit.h"
+#include "device-private.h"
+#include "device-util.h"
#include "device.h"
#include "log.h"
#include "parse-util.h"
#include "path-util.h"
+#include "serialize.h"
#include "stat-util.h"
#include "string-util.h"
#include "swap.h"
-#include "udev-util.h"
#include "unit-name.h"
#include "unit.h"
@@ -25,7 +26,7 @@ static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
[DEVICE_PLUGGED] = UNIT_ACTIVE,
};
-static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
+static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata);
static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask);
static void device_unset_sysfs(Device *d) {
@@ -111,10 +112,31 @@ static void device_done(Unit *u) {
d->wants_property = strv_free(d->wants_property);
}
+static int device_load(Unit *u) {
+ int r;
+
+ r = unit_load_fragment_and_dropin_optional(u);
+ if (r < 0)
+ return r;
+
+ if (!u->description) {
+ /* Generate a description based on the path, to be used until the
+ device is initialized properly */
+ r = unit_name_to_path(u->id, &u->description);
+ if (r < 0)
+ log_unit_debug_errno(u, r, "Failed to unescape name: %m");
+ }
+
+ return 0;
+}
+
static void device_set_state(Device *d, DeviceState state) {
DeviceState old_state;
assert(d);
+ if (d->state != state)
+ bus_unit_send_pending_change_signal(UNIT(d), false);
+
old_state = d->state;
d->state = state;
@@ -226,10 +248,10 @@ static int device_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", device_state_to_string(d->state));
+ (void) serialize_item(f, "state", device_state_to_string(d->state));
if (device_found_to_string_many(d->found, &s) >= 0)
- unit_serialize_item(u, f, "found", s);
+ (void) serialize_item(f, "found", s);
return 0;
}
@@ -255,7 +277,7 @@ static int device_deserialize_item(Unit *u, const char *key, const char *value,
} else if (streq(key, "found")) {
r = device_found_from_string_many(value, &d->deserialized_found);
if (r < 0)
- log_unit_debug_errno(u, r, "Failed to parse found value, ignoring: %s", value);
+ log_unit_debug_errno(u, r, "Failed to parse found value '%s', ignoring: %m", value);
} else
log_unit_debug(u, "Unknown serialization key: %s", key);
@@ -300,47 +322,40 @@ _pure_ static const char *device_sub_state_to_string(Unit *u) {
return device_state_to_string(DEVICE(u)->state);
}
-static int device_update_description(Unit *u, struct udev_device *dev, const char *path) {
- const char *model;
+static int device_update_description(Unit *u, sd_device *dev, const char *path) {
+ _cleanup_free_ char *j = NULL;
+ const char *model, *label, *desc;
int r;
assert(u);
- assert(dev);
assert(path);
- model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE");
- if (!model)
- model = udev_device_get_property_value(dev, "ID_MODEL");
+ desc = path;
- if (model) {
- const char *label;
+ if (dev &&
+ (sd_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE", &model) >= 0 ||
+ sd_device_get_property_value(dev, "ID_MODEL", &model) >= 0)) {
+ desc = model;
/* Try to concatenate the device model string with a label, if there is one */
- label = udev_device_get_property_value(dev, "ID_FS_LABEL");
- if (!label)
- label = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME");
- if (!label)
- label = udev_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER");
+ if (sd_device_get_property_value(dev, "ID_FS_LABEL", &label) >= 0 ||
+ sd_device_get_property_value(dev, "ID_PART_ENTRY_NAME", &label) >= 0 ||
+ sd_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER", &label) >= 0) {
- if (label) {
- _cleanup_free_ char *j;
-
- j = strjoin(model, " ", label);
+ desc = j = strjoin(model, " ", label);
if (!j)
return log_oom();
+ }
+ }
- r = unit_set_description(u, j);
- } else
- r = unit_set_description(u, model);
- } else
- r = unit_set_description(u, path);
+ r = unit_set_description(u, desc);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to set device description: %m");
return 0;
}
-static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
+static int device_add_udev_wants(Unit *u, sd_device *dev) {
_cleanup_strv_free_ char **added = NULL;
const char *wants, *property;
Device *d = DEVICE(u);
@@ -351,8 +366,8 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
- wants = udev_device_get_property_value(dev, property);
- if (!wants)
+ r = sd_device_get_property_value(dev, property, &wants);
+ if (r < 0)
return 0;
for (;;) {
@@ -387,7 +402,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
return log_unit_error_errno(u, r, "Failed to mangle unit name \"%s\": %m", word);
}
- r = unit_add_dependency_by_name(u, UNIT_WANTS, k, NULL, true, UNIT_DEPENDENCY_UDEV);
+ r = unit_add_dependency_by_name(u, UNIT_WANTS, k, true, UNIT_DEPENDENCY_UDEV);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to add Wants= dependency: %m");
@@ -429,18 +444,17 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
return 0;
}
-static bool device_is_bound_by_mounts(Device *d, struct udev_device *dev) {
+static bool device_is_bound_by_mounts(Device *d, sd_device *dev) {
const char *bound_by;
int r;
assert(d);
assert(dev);
- bound_by = udev_device_get_property_value(dev, "SYSTEMD_MOUNT_DEVICE_BOUND");
- if (bound_by) {
+ if (sd_device_get_property_value(dev, "SYSTEMD_MOUNT_DEVICE_BOUND", &bound_by) >= 0) {
r = parse_boolean(bound_by);
if (r < 0)
- log_warning_errno(r, "Failed to parse SYSTEMD_MOUNT_DEVICE_BOUND='%s' udev property of %s, ignoring: %m", bound_by, strna(d->sysfs));
+ log_device_warning_errno(dev, r, "Failed to parse SYSTEMD_MOUNT_DEVICE_BOUND='%s' udev property, ignoring: %m", bound_by);
d->bind_mounts = r > 0;
} else
@@ -467,7 +481,7 @@ static void device_upgrade_mount_deps(Unit *u) {
}
}
-static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
+static int device_setup_unit(Manager *m, sd_device *dev, const char *path, bool main) {
_cleanup_free_ char *e = NULL;
const char *sysfs = NULL;
Unit *u = NULL;
@@ -478,16 +492,16 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
assert(path);
if (dev) {
- sysfs = udev_device_get_syspath(dev);
- if (!sysfs) {
- log_debug("Couldn't get syspath from udev device, ignoring.");
+ r = sd_device_get_syspath(dev, &sysfs);
+ if (r < 0) {
+ log_device_debug_errno(dev, r, "Couldn't get syspath from device, ignoring: %m");
return 0;
}
}
r = unit_name_from_path(path, ".device", &e);
if (r < 0)
- return log_error_errno(r, "Failed to generate unit name from device path: %m");
+ return log_device_error_errno(dev, r, "Failed to generate unit name from device path: %m");
u = manager_get_unit(m, e);
if (u) {
@@ -518,7 +532,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
r = unit_new_for_name(m, sizeof(Device), e, &u);
if (r < 0) {
- log_error_errno(r, "Failed to allocate device unit %s: %m", e);
+ log_device_error_errno(dev, r, "Failed to allocate device unit %s: %m", e);
goto fail;
}
@@ -530,17 +544,17 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
if (sysfs) {
r = device_set_sysfs(DEVICE(u), sysfs);
if (r < 0) {
- log_error_errno(r, "Failed to set sysfs path %s for device unit %s: %m", sysfs, e);
+ log_unit_error_errno(u, r, "Failed to set sysfs path %s: %m", sysfs);
goto fail;
}
- (void) device_update_description(u, dev, path);
-
/* The additional systemd udev properties we only interpret for the main object */
if (main)
(void) device_add_udev_wants(u, dev);
}
+ (void) device_update_description(u, dev, path);
+
/* So the user wants the mount units to be bound to the device but a mount unit might has been seen by systemd
* before the device appears on its radar. In this case the device unit is partially initialized and includes
* the deps on the mount unit but at that time the "bind mounts" flag wasn't not present. Fix this up now. */
@@ -559,15 +573,14 @@ fail:
return r;
}
-static int device_process_new(Manager *m, struct udev_device *dev) {
+static int device_process_new(Manager *m, sd_device *dev) {
const char *sysfs, *dn, *alias;
- struct udev_list_entry *item = NULL, *first = NULL;
+ dev_t devnum;
int r;
assert(m);
- sysfs = udev_device_get_syspath(dev);
- if (!sysfs)
+ if (sd_device_get_syspath(dev, &sysfs) < 0)
return 0;
/* Add the main unit named after the sysfs path */
@@ -576,40 +589,39 @@ static int device_process_new(Manager *m, struct udev_device *dev) {
return r;
/* Add an additional unit for the device node */
- dn = udev_device_get_devnode(dev);
- if (dn)
+ if (sd_device_get_devname(dev, &dn) >= 0)
(void) device_setup_unit(m, dev, dn, false);
/* Add additional units for all symlinks */
- first = udev_device_get_devlinks_list_entry(dev);
- udev_list_entry_foreach(item, first) {
+ if (sd_device_get_devnum(dev, &devnum) >= 0) {
const char *p;
- struct stat st;
- /* Don't bother with the /dev/block links */
- p = udev_list_entry_get_name(item);
+ FOREACH_DEVICE_DEVLINK(dev, p) {
+ struct stat st;
- if (PATH_STARTSWITH_SET(p, "/dev/block/", "/dev/char/"))
- continue;
+ if (PATH_STARTSWITH_SET(p, "/dev/block/", "/dev/char/"))
+ continue;
- /* Verify that the symlink in the FS actually belongs
- * to this device. This is useful to deal with
- * conflicting devices, e.g. when two disks want the
- * same /dev/disk/by-label/xxx link because they have
- * the same label. We want to make sure that the same
- * device that won the symlink wins in systemd, so we
- * check the device node major/minor */
- if (stat(p, &st) >= 0)
- if ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) ||
- st.st_rdev != udev_device_get_devnum(dev))
+ /* Verify that the symlink in the FS actually belongs
+ * to this device. This is useful to deal with
+ * conflicting devices, e.g. when two disks want the
+ * same /dev/disk/by-label/xxx link because they have
+ * the same label. We want to make sure that the same
+ * device that won the symlink wins in systemd, so we
+ * check the device node major/minor */
+ if (stat(p, &st) >= 0 &&
+ ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) ||
+ st.st_rdev != devnum))
continue;
- (void) device_setup_unit(m, dev, p, false);
+ (void) device_setup_unit(m, dev, p, false);
+ }
}
- /* Add additional units for all explicitly configured
- * aliases */
- alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS");
+ /* Add additional units for all explicitly configured aliases */
+ if (sd_device_get_property_value(dev, "SYSTEMD_ALIAS", &alias) < 0)
+ return 0;
+
for (;;) {
_cleanup_free_ char *word = NULL;
@@ -619,12 +631,12 @@ static int device_process_new(Manager *m, struct udev_device *dev) {
if (r == -ENOMEM)
return log_oom();
if (r < 0)
- return log_warning_errno(r, "Failed to add parse SYSTEMD_ALIAS for %s: %m", sysfs);
+ return log_device_warning_errno(dev, r, "Failed to add parse SYSTEMD_ALIAS property: %m");
if (!path_is_absolute(word))
- log_warning("SYSTEMD_ALIAS for %s is not an absolute path, ignoring: %s", sysfs, word);
+ log_device_warning(dev, "SYSTEMD_ALIAS is not an absolute path, ignoring: %s", word);
else if (!path_is_normalized(word))
- log_warning("SYSTEMD_ALIAS for %s is not a normalized path, ignoring: %s", sysfs, word);
+ log_device_warning(dev, "SYSTEMD_ALIAS is not a normalized path, ignoring: %s", word);
else
(void) device_setup_unit(m, dev, word, false);
}
@@ -712,13 +724,12 @@ static int device_update_found_by_name(Manager *m, const char *path, DeviceFound
return 0;
}
-static bool device_is_ready(struct udev_device *dev) {
+static bool device_is_ready(sd_device *dev) {
const char *ready;
assert(dev);
- ready = udev_device_get_property_value(dev, "SYSTEMD_READY");
- if (!ready)
+ if (sd_device_get_property_value(dev, "SYSTEMD_READY", &ready) < 0)
return true;
return parse_boolean(ready) != 0;
@@ -734,11 +745,11 @@ static Unit *device_following(Unit *u) {
return NULL;
/* Make everybody follow the unit that's named after the sysfs path */
- for (other = d->same_sysfs_next; other; other = other->same_sysfs_next)
+ LIST_FOREACH_AFTER(same_sysfs, other, d)
if (startswith(UNIT(other)->id, "sys-"))
return UNIT(other);
- for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev) {
+ LIST_FOREACH_BEFORE(same_sysfs, other, d) {
if (startswith(UNIT(other)->id, "sys-"))
return UNIT(other);
@@ -784,98 +795,71 @@ static int device_following_set(Unit *u, Set **_set) {
static void device_shutdown(Manager *m) {
assert(m);
- m->udev_event_source = sd_event_source_unref(m->udev_event_source);
- m->udev_monitor = udev_monitor_unref(m->udev_monitor);
+ m->device_monitor = sd_device_monitor_unref(m->device_monitor);
m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs);
}
static void device_enumerate(Manager *m) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *dev;
int r;
assert(m);
- if (!m->udev_monitor) {
- m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
- if (!m->udev_monitor) {
- log_error_errno(errno, "Failed to allocate udev monitor: %m");
+ if (!m->device_monitor) {
+ r = sd_device_monitor_new(&m->device_monitor);
+ if (r < 0) {
+ log_error_errno(r, "Failed to allocate device monitor: %m");
goto fail;
}
/* This will fail if we are unprivileged, but that
* should not matter much, as user instances won't run
* during boot. */
- (void) udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024);
+ (void) sd_device_monitor_set_receive_buffer_size(m->device_monitor, 128*1024*1024);
- r = udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd");
+ r = sd_device_monitor_filter_add_match_tag(m->device_monitor, "systemd");
if (r < 0) {
log_error_errno(r, "Failed to add udev tag match: %m");
goto fail;
}
- r = udev_monitor_enable_receiving(m->udev_monitor);
+ r = sd_device_monitor_attach_event(m->device_monitor, m->event);
if (r < 0) {
- log_error_errno(r, "Failed to enable udev event reception: %m");
+ log_error_errno(r, "Failed to attach event to device monitor: %m");
goto fail;
}
- r = sd_event_add_io(m->event, &m->udev_event_source, udev_monitor_get_fd(m->udev_monitor), EPOLLIN, device_dispatch_io, m);
+ r = sd_device_monitor_start(m->device_monitor, device_dispatch_io, m);
if (r < 0) {
- log_error_errno(r, "Failed to watch udev file descriptor: %m");
+ log_error_errno(r, "Failed to start device monitor: %m");
goto fail;
}
-
- (void) sd_event_source_set_description(m->udev_event_source, "device");
- }
-
- e = udev_enumerate_new(m->udev);
- if (!e) {
- log_error_errno(errno, "Failed to alloacte udev enumerator: %m");
- goto fail;
- }
-
- r = udev_enumerate_add_match_tag(e, "systemd");
- if (r < 0) {
- log_error_errno(r, "Failed to create udev tag enumeration: %m");
- goto fail;
}
- r = udev_enumerate_add_match_is_initialized(e);
+ r = sd_device_enumerator_new(&e);
if (r < 0) {
- log_error_errno(r, "Failed to install initialization match into enumeration: %m");
+ log_error_errno(r, "Failed to allocate device enumerator: %m");
goto fail;
}
- r = udev_enumerate_scan_devices(e);
+ r = sd_device_enumerator_add_match_tag(e, "systemd");
if (r < 0) {
- log_error_errno(r, "Failed to enumerate devices: %m");
+ log_error_errno(r, "Failed to set tag for device enumeration: %m");
goto fail;
}
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
+ FOREACH_DEVICE(e, dev) {
const char *sysfs;
- sysfs = udev_list_entry_get_name(item);
-
- dev = udev_device_new_from_syspath(m->udev, sysfs);
- if (!dev) {
- if (errno == ENOMEM) {
- log_oom();
- goto fail;
- }
-
- /* If we can't create a device, don't bother, it probably just disappeared. */
- log_debug_errno(errno, "Failed to create udev device object for %s: %m", sysfs);
- continue;
- }
-
if (!device_is_ready(dev))
continue;
(void) device_process_new(m, dev);
+
+ if (sd_device_get_syspath(dev, &sysfs) < 0)
+ continue;
+
device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
}
@@ -903,40 +887,23 @@ static void device_propagate_reload_by_sysfs(Manager *m, const char *sysfs) {
}
}
-static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
- _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
+static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
Manager *m = userdata;
const char *action, *sysfs;
int r;
assert(m);
+ assert(dev);
- if (revents != EPOLLIN) {
- static RATELIMIT_DEFINE(limit, 10*USEC_PER_SEC, 5);
-
- if (ratelimit_below(&limit))
- log_warning("Failed to get udev event");
- if (!(revents & EPOLLIN))
- return 0;
- }
-
- /*
- * libudev might filter-out devices which pass the bloom
- * filter, so getting NULL here is not necessarily an error.
- */
- dev = udev_monitor_receive_device(m->udev_monitor);
- if (!dev)
- return 0;
-
- sysfs = udev_device_get_syspath(dev);
- if (!sysfs) {
- log_error("Failed to get udev sys path.");
+ r = sd_device_get_syspath(dev, &sysfs);
+ if (r < 0) {
+ log_device_error_errno(dev, r, "Failed to get device sys path: %m");
return 0;
}
- action = udev_device_get_action(dev);
- if (!action) {
- log_error("Failed to get udev action string.");
+ r = sd_device_get_property_value(dev, "ACTION", &action);
+ if (r < 0) {
+ log_device_error_errno(dev, r, "Failed to get udev action string: %m");
return 0;
}
@@ -949,7 +916,7 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
if (streq(action, "remove")) {
r = swap_process_device_remove(m, dev);
if (r < 0)
- log_warning_errno(r, "Failed to process swap device remove event, ignoring: %m");
+ log_device_warning_errno(dev, r, "Failed to process swap device remove event, ignoring: %m");
/* If we get notified that a device was removed by
* udev, then it's completely gone, hence unset all
@@ -962,7 +929,7 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
r = swap_process_device_new(m, dev);
if (r < 0)
- log_warning_errno(r, "Failed to process swap device new event, ignoring: %m");
+ log_device_warning_errno(dev, r, "Failed to process swap device new event, ignoring: %m");
manager_dispatch_load_queue(m);
@@ -992,7 +959,7 @@ static bool device_supported(void) {
return read_only <= 0;
}
-static int validate_node(Manager *m, const char *node, struct udev_device **ret) {
+static int validate_node(Manager *m, const char *node, sd_device **ret) {
struct stat st;
int r;
@@ -1016,9 +983,9 @@ static int validate_node(Manager *m, const char *node, struct udev_device **ret)
return 1; /* good! (though missing) */
} else {
- _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- r = udev_device_new_from_stat_rdev(m->udev, &st, &dev);
+ r = device_new_from_stat_rdev(&dev, &st);
if (r == -ENOENT) {
*ret = NULL;
return 1; /* good! (though missing) */
@@ -1054,7 +1021,7 @@ void device_found_node(Manager *m, const char *node, DeviceFound found, DeviceFo
* and unset individual bits in a single call, while merging partially with previous state. */
if ((found & mask) != 0) {
- _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
/* If the device is known in the kernel and newly appeared, then we'll create a device unit for it,
* under the name referenced in /proc/swaps or /proc/self/mountinfo. But first, let's validate if
@@ -1092,7 +1059,7 @@ const UnitVTable device_vtable = {
.init = device_init,
.done = device_done,
- .load = unit_load_fragment_and_dropin_optional,
+ .load = device_load,
.coldplug = device_coldplug,
.catchup = device_catchup,
diff --git a/src/core/device.h b/src/core/device.h
index a119b33e57..3062be782d 100644
--- a/src/core/device.h
+++ b/src/core/device.h
@@ -11,9 +11,9 @@ typedef struct Device Device;
* in quick succession). Hence we need to track precisely where it is already visible and where not. */
typedef enum DeviceFound {
DEVICE_NOT_FOUND = 0,
- DEVICE_FOUND_UDEV = 1U << 1, /* The device has shown up in the udev database */
- DEVICE_FOUND_MOUNT = 1U << 2, /* The device has shown up in /proc/self/mountinfo */
- DEVICE_FOUND_SWAP = 1U << 3, /* The device has shown up in /proc/swaps */
+ DEVICE_FOUND_UDEV = 1 << 0, /* The device has shown up in the udev database */
+ DEVICE_FOUND_MOUNT = 1 << 1, /* The device has shown up in /proc/self/mountinfo */
+ DEVICE_FOUND_SWAP = 1 << 2, /* The device has shown up in /proc/swaps */
DEVICE_FOUND_MASK = DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP,
} DeviceFound;
diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c
index 7c5111ddf6..089461a18a 100644
--- a/src/core/dynamic-user.c
+++ b/src/core/dynamic-user.c
@@ -10,8 +10,10 @@
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
+#include "nscd-flush.h"
#include "parse-util.h"
#include "random-util.h"
+#include "serialize.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "string-util.h"
@@ -20,6 +22,8 @@
/* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
#define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (DYNAMIC_UID_MAX - DYNAMIC_UID_MIN + 1)) + DYNAMIC_UID_MIN)
+DEFINE_PRIVATE_TRIVIAL_REF_FUNC(DynamicUser, dynamic_user);
+
static DynamicUser* dynamic_user_free(DynamicUser *d) {
if (!d)
return NULL;
@@ -32,7 +36,7 @@ static DynamicUser* dynamic_user_free(DynamicUser *d) {
}
static int dynamic_user_add(Manager *m, const char *name, int storage_socket[2], DynamicUser **ret) {
- DynamicUser *d = NULL;
+ DynamicUser *d;
int r;
assert(m);
@@ -102,9 +106,11 @@ static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret)
d = hashmap_get(m->dynamic_users, name);
if (d) {
- /* We already have a structure for the dynamic user, let's increase the ref count and reuse it */
- d->n_ref++;
- *ret = d;
+ if (ret) {
+ /* We already have a structure for the dynamic user, let's increase the ref count and reuse it */
+ d->n_ref++;
+ *ret = d;
+ }
return 0;
}
@@ -173,7 +179,7 @@ static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {
*
* 1. Initially, we try to read the UID of a number of specified paths. If any of these UIDs works, we use
* them. We use in order to increase the chance of UID reuse, if StateDirectory=, CacheDirectory= or
- * LogDirectory= are used, as reusing the UID these directories are owned by saves us from having to
+ * LogsDirectory= are used, as reusing the UID these directories are owned by saves us from having to
* recursively chown() them to new users.
*
* 2. If that didn't yield a currently unused UID, we hash the user name, and try to use that. This should be
@@ -312,20 +318,8 @@ static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {
static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
uid_t uid = UID_INVALID;
struct iovec iov = IOVEC_INIT(&uid, sizeof(uid));
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- .msg_iov = &iov,
- .msg_iovlen = 1,
- };
- struct cmsghdr *cmsg;
-
+ int lock_fd;
ssize_t k;
- int lock_fd = -1;
assert(d);
assert(ret_uid);
@@ -334,15 +328,9 @@ static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
/* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with the lock
* on the socket taken. */
- k = recvmsg(d->storage_socket[0], &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
+ k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
if (k < 0)
- return -errno;
-
- cmsg = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
- if (cmsg)
- lock_fd = *(int*) CMSG_DATA(cmsg);
- else
- cmsg_close_all(&mh); /* just in case... */
+ return (int) k;
*ret_uid = uid;
*ret_lock_fd = lock_fd;
@@ -352,42 +340,11 @@ static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
struct iovec iov = IOVEC_INIT(&uid, sizeof(uid));
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- .msg_iov = &iov,
- .msg_iovlen = 1,
- };
- ssize_t k;
assert(d);
/* Store the UID and lock_fd in the storage socket. This should be called with the socket pair lock taken. */
-
- if (lock_fd >= 0) {
- struct cmsghdr *cmsg;
-
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &lock_fd, sizeof(int));
-
- mh.msg_controllen = CMSG_SPACE(sizeof(int));
- } else {
- mh.msg_control = NULL;
- mh.msg_controllen = 0;
- }
-
- k = sendmsg(d->storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
- if (k < 0)
- return -errno;
-
- return 0;
+ return send_one_fd_iov(d->storage_socket[1], lock_fd, &iov, 1, MSG_DONTWAIT);
}
static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {
@@ -427,6 +384,7 @@ static int dynamic_user_realize(
_cleanup_close_ int etc_passwd_lock_fd = -1;
uid_t num = UID_INVALID; /* a uid if is_user, and a gid otherwise */
gid_t gid = GID_INVALID; /* a gid if is_user, ignored otherwise */
+ bool flush_cache = false;
int r;
assert(d);
@@ -515,6 +473,7 @@ static int dynamic_user_realize(
}
/* Great! Nothing is stored here, still. Store our newly acquired data. */
+ flush_cache = true;
} else {
/* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
* acquired, and use what's stored now. */
@@ -525,6 +484,16 @@ static int dynamic_user_realize(
num = new_uid;
uid_lock_fd = new_uid_lock_fd;
}
+ } else if (is_user && !uid_is_dynamic(num)) {
+ struct passwd *p;
+
+ /* Statically allocated user may have different uid and gid. So, let's obtain the gid. */
+ errno = 0;
+ p = getpwuid(num);
+ if (!p)
+ return errno > 0 ? -errno : -ESRCH;
+
+ gid = p->pw_gid;
}
/* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already
@@ -534,6 +503,14 @@ static int dynamic_user_realize(
if (r < 0)
return r;
+ if (flush_cache) {
+ /* If we allocated a new dynamic UID, refresh nscd, so that it forgets about potentially cached
+ * negative entries. But let's do so after we release the /etc/passwd lock, so that there's no
+ * potential for nscd wanting to lock that for completing the invalidation. */
+ etc_passwd_lock_fd = safe_close(etc_passwd_lock_fd);
+ (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
+ }
+
if (is_user) {
*ret_uid = num;
*ret_gid = gid != GID_INVALID ? gid : num;
@@ -570,16 +547,6 @@ int dynamic_user_current(DynamicUser *d, uid_t *ret) {
return 0;
}
-static DynamicUser* dynamic_user_ref(DynamicUser *d) {
- if (!d)
- return NULL;
-
- assert(d->n_ref > 0);
- d->n_ref++;
-
- return d;
-}
-
static DynamicUser* dynamic_user_unref(DynamicUser *d) {
if (!d)
return NULL;
@@ -616,6 +583,8 @@ static int dynamic_user_close(DynamicUser *d) {
/* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
unlink_uid_lock(lock_fd, uid, d->name);
+
+ (void) nscd_flush_cache(STRV_MAKE("passwd", "group"));
return 1;
}
@@ -652,13 +621,13 @@ int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds) {
copy0 = fdset_put_dup(fds, d->storage_socket[0]);
if (copy0 < 0)
- return copy0;
+ return log_error_errno(copy0, "Failed to add dynamic user storage fd to serialization: %m");
copy1 = fdset_put_dup(fds, d->storage_socket[1]);
if (copy1 < 0)
- return copy1;
+ return log_error_errno(copy1, "Failed to add dynamic user storage fd to serialization: %m");
- fprintf(f, "dynamic-user=%s %i %i\n", d->name, copy0, copy1);
+ (void) serialize_item_format(f, "dynamic-user", "%s %i %i", d->name, copy0, copy1);
}
return 0;
diff --git a/src/core/dynamic-user.h b/src/core/dynamic-user.h
index 791a8ba0ef..112f91e63a 100644
--- a/src/core/dynamic-user.h
+++ b/src/core/dynamic-user.h
@@ -15,7 +15,7 @@ typedef struct DynamicCreds {
* used. This means, if you want to allocate a group and user pair, and they might have two different names, then you
* need to allocated two of these objects. DynamicCreds below makes that easy. */
struct DynamicUser {
- int n_ref;
+ unsigned n_ref;
Manager *manager;
/* An AF_UNIX socket pair that contains a datagram containing both the numeric ID assigned, as well as a lock
diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c
index 76e1124cff..f98b0de792 100644
--- a/src/core/emergency-action.c
+++ b/src/core/emergency-action.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2012 Michael Olbrich
-***/
#include <sys/reboot.h>
@@ -13,18 +10,22 @@
#include "special.h"
#include "string-table.h"
#include "terminal-util.h"
-
-static void log_and_status(Manager *m, const char *message, const char *reason) {
- log_warning("%s: %s", message, reason);
- manager_status_printf(m, STATUS_TYPE_EMERGENCY,
- ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL,
- "%s: %s", message, reason);
+#include "virt.h"
+
+static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
+ log_full(warn ? LOG_WARNING : LOG_DEBUG, "%s: %s", message, reason);
+ if (warn)
+ manager_status_printf(m, STATUS_TYPE_EMERGENCY,
+ ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL,
+ "%s: %s", message, reason);
}
int emergency_action(
Manager *m,
EmergencyAction action,
+ EmergencyActionFlags options,
const char *reboot_arg,
+ int exit_status,
const char *reason) {
assert(m);
@@ -34,24 +35,17 @@ int emergency_action(
if (action == EMERGENCY_ACTION_NONE)
return -ECANCELED;
- if (!m->service_watchdogs) {
+ if (FLAGS_SET(options, EMERGENCY_ACTION_IS_WATCHDOG) && !m->service_watchdogs) {
log_warning("Watchdog disabled! Not acting on: %s", reason);
return -ECANCELED;
}
- if (!MANAGER_IS_SYSTEM(m)) {
- /* Downgrade all options to simply exiting if we run
- * in user mode */
-
- log_warning("Exiting: %s", reason);
- m->exit_code = MANAGER_EXIT;
- return -ECANCELED;
- }
+ bool warn = FLAGS_SET(options, EMERGENCY_ACTION_WARN);
switch (action) {
case EMERGENCY_ACTION_REBOOT:
- log_and_status(m, "Rebooting", reason);
+ log_and_status(m, warn, "Rebooting", reason);
(void) update_reboot_parameter_and_warn(reboot_arg);
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
@@ -59,15 +53,15 @@ int emergency_action(
break;
case EMERGENCY_ACTION_REBOOT_FORCE:
- log_and_status(m, "Forcibly rebooting", reason);
+ log_and_status(m, warn, "Forcibly rebooting", reason);
(void) update_reboot_parameter_and_warn(reboot_arg);
- m->exit_code = MANAGER_REBOOT;
+ m->objective = MANAGER_REBOOT;
break;
case EMERGENCY_ACTION_REBOOT_IMMEDIATE:
- log_and_status(m, "Rebooting immediately", reason);
+ log_and_status(m, warn, "Rebooting immediately", reason);
sync();
@@ -81,18 +75,46 @@ int emergency_action(
(void) reboot(RB_AUTOBOOT);
break;
+ case EMERGENCY_ACTION_EXIT:
+
+ if (exit_status >= 0)
+ m->return_value = exit_status;
+
+ if (MANAGER_IS_USER(m) || detect_container() > 0) {
+ log_and_status(m, warn, "Exiting", reason);
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_EXIT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
+ break;
+ }
+
+ log_notice("Doing \"poweroff\" action instead of an \"exit\" emergency action.");
+ _fallthrough_;
+
case EMERGENCY_ACTION_POWEROFF:
- log_and_status(m, "Powering off", reason);
+ log_and_status(m, warn, "Powering off", reason);
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
break;
+ case EMERGENCY_ACTION_EXIT_FORCE:
+
+ if (exit_status >= 0)
+ m->return_value = exit_status;
+
+ if (MANAGER_IS_USER(m) || detect_container() > 0) {
+ log_and_status(m, warn, "Exiting immediately", reason);
+ m->objective = MANAGER_EXIT;
+ break;
+ }
+
+ log_notice("Doing \"poweroff-force\" action instead of an \"exit-force\" emergency action.");
+ _fallthrough_;
+
case EMERGENCY_ACTION_POWEROFF_FORCE:
- log_and_status(m, "Forcibly powering off", reason);
- m->exit_code = MANAGER_POWEROFF;
+ log_and_status(m, warn, "Forcibly powering off", reason);
+ m->objective = MANAGER_POWEROFF;
break;
case EMERGENCY_ACTION_POWEROFF_IMMEDIATE:
- log_and_status(m, "Powering off immediately", reason);
+ log_and_status(m, warn, "Powering off immediately", reason);
sync();
@@ -114,6 +136,26 @@ static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
[EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
[EMERGENCY_ACTION_POWEROFF] = "poweroff",
[EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force",
- [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate"
+ [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
+ [EMERGENCY_ACTION_EXIT] = "exit",
+ [EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
};
DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction);
+
+int parse_emergency_action(
+ const char *value,
+ bool system,
+ EmergencyAction *ret) {
+
+ EmergencyAction x;
+
+ x = emergency_action_from_string(value);
+ if (x < 0)
+ return -EINVAL;
+
+ if (!system && x != EMERGENCY_ACTION_NONE && x < _EMERGENCY_ACTION_FIRST_USER_ACTION)
+ return -EOPNOTSUPP;
+
+ *ret = x;
+ return 0;
+}
diff --git a/src/core/emergency-action.h b/src/core/emergency-action.h
index 61791f176f..6e6c69ddfc 100644
--- a/src/core/emergency-action.h
+++ b/src/core/emergency-action.h
@@ -1,10 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- Copyright © 2012 Michael Olbrich
-***/
-
typedef enum EmergencyAction {
EMERGENCY_ACTION_NONE,
EMERGENCY_ACTION_REBOOT,
@@ -13,14 +9,26 @@ typedef enum EmergencyAction {
EMERGENCY_ACTION_POWEROFF,
EMERGENCY_ACTION_POWEROFF_FORCE,
EMERGENCY_ACTION_POWEROFF_IMMEDIATE,
+ EMERGENCY_ACTION_EXIT,
+ _EMERGENCY_ACTION_FIRST_USER_ACTION = EMERGENCY_ACTION_EXIT,
+ EMERGENCY_ACTION_EXIT_FORCE,
_EMERGENCY_ACTION_MAX,
_EMERGENCY_ACTION_INVALID = -1
} EmergencyAction;
+typedef enum EmergencyActionFlags {
+ EMERGENCY_ACTION_IS_WATCHDOG = 1 << 0,
+ EMERGENCY_ACTION_WARN = 1 << 1,
+} EmergencyActionFlags;
+
#include "macro.h"
#include "manager.h"
-int emergency_action(Manager *m, EmergencyAction action, const char *reboot_arg, const char *reason);
+int emergency_action(Manager *m,
+ EmergencyAction action, EmergencyActionFlags options,
+ const char *reboot_arg, int exit_status, const char *reason);
const char* emergency_action_to_string(EmergencyAction i) _const_;
EmergencyAction emergency_action_from_string(const char *s) _pure_;
+
+int parse_emergency_action(const char *value, bool system, EmergencyAction *ret);
diff --git a/src/core/execute.c b/src/core/execute.c
index 8ac69d1a0f..595a3c6eca 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -50,12 +50,12 @@
#include "chown-recursive.h"
#include "cpu-set-util.h"
#include "def.h"
+#include "env-file.h"
#include "env-util.h"
#include "errno-list.h"
#include "execute.h"
#include "exit-status.h"
#include "fd-util.h"
-#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "glob-util.h"
@@ -76,7 +76,6 @@
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
-#include "securebits.h"
#include "securebits-util.h"
#include "selinux-util.h"
#include "signal-util.h"
@@ -89,6 +88,7 @@
#include "strv.h"
#include "syslog-util.h"
#include "terminal-util.h"
+#include "umask-util.h"
#include "unit.h"
#include "user-util.h"
#include "util.h"
@@ -147,11 +147,11 @@ static int shift_fds(int fds[], size_t n_fds) {
return 0;
}
-static int flags_fds(const int fds[], size_t n_storage_fds, size_t n_socket_fds, bool nonblock) {
+static int flags_fds(const int fds[], size_t n_socket_fds, size_t n_storage_fds, bool nonblock) {
size_t i, n_fds;
int r;
- n_fds = n_storage_fds + n_socket_fds;
+ n_fds = n_socket_fds + n_storage_fds;
if (n_fds <= 0)
return 0;
@@ -323,7 +323,8 @@ static int connect_logger_as(
uid_t uid,
gid_t gid) {
- int fd, r;
+ _cleanup_close_ int fd = -1;
+ int r;
assert(context);
assert(params);
@@ -339,14 +340,12 @@ static int connect_logger_as(
if (r < 0)
return r;
- if (shutdown(fd, SHUT_RD) < 0) {
- safe_close(fd);
+ if (shutdown(fd, SHUT_RD) < 0)
return -errno;
- }
(void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
- dprintf(fd,
+ if (dprintf(fd,
"%s\n"
"%s\n"
"%i\n"
@@ -360,10 +359,12 @@ static int connect_logger_as(
!!context->syslog_level_prefix,
is_syslog_output(output),
is_kmsg_output(output),
- is_terminal_output(output));
+ is_terminal_output(output)) < 0)
+ return -errno;
- return move_fd(fd, nfd, false);
+ return move_fd(TAKE_FD(fd), nfd, false);
}
+
static int open_terminal_as(const char *path, int flags, int nfd) {
int fd;
@@ -378,10 +379,9 @@ 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 = {
- .sa.sa_family = AF_UNIX,
- };
- int fd, r;
+ union sockaddr_union sa = {};
+ _cleanup_close_ int fd = -1;
+ int r, salen;
assert(path);
@@ -390,11 +390,11 @@ static int acquire_path(const char *path, int flags, mode_t mode) {
fd = open(path, flags|O_NOCTTY, mode);
if (fd >= 0)
- return fd;
+ return TAKE_FD(fd);
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 */
+ 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. */
@@ -403,25 +403,24 @@ static int acquire_path(const char *path, int flags, mode_t mode) {
if (fd < 0)
return -errno;
- strncpy(sa.un.sun_path, path, sizeof(sa.un.sun_path));
- if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
- safe_close(fd);
+ salen = sockaddr_un_set_path(&sa.un, path);
+ if (salen < 0)
+ return salen;
+
+ if (connect(fd, &sa.sa, salen) < 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 */
- }
if ((flags & O_ACCMODE) == O_RDONLY)
r = shutdown(fd, SHUT_WR);
else if ((flags & O_ACCMODE) == O_WRONLY)
r = shutdown(fd, SHUT_RD);
else
- return fd;
- if (r < 0) {
- safe_close(fd);
+ return TAKE_FD(fd);
+ if (r < 0)
return -errno;
- }
- return fd;
+ return TAKE_FD(fd);
}
static int fixup_input(
@@ -544,6 +543,30 @@ static int setup_input(
}
}
+static bool can_inherit_stderr_from_stdout(
+ const ExecContext *context,
+ ExecOutput o,
+ ExecOutput e) {
+
+ assert(context);
+
+ /* Returns true, if given the specified STDERR and STDOUT output we can directly dup() the stdout fd to the
+ * stderr fd */
+
+ if (e == EXEC_OUTPUT_INHERIT)
+ return true;
+ if (e != o)
+ return false;
+
+ if (e == EXEC_OUTPUT_NAMED_FD)
+ return streq_ptr(context->stdio_fdname[STDOUT_FILENO], context->stdio_fdname[STDERR_FILENO]);
+
+ if (IN_SET(e, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND))
+ return streq_ptr(context->stdio_file[STDOUT_FILENO], context->stdio_file[STDERR_FILENO]);
+
+ return true;
+}
+
static int setup_output(
const Unit *unit,
const ExecContext *context,
@@ -602,7 +625,7 @@ static int setup_output(
return fileno;
/* Duplicate from stdout if possible */
- if ((e == o && e != EXEC_OUTPUT_NAMED_FD) || e == EXEC_OUTPUT_INHERIT)
+ if (can_inherit_stderr_from_stdout(context, o, e))
return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
o = e;
@@ -675,9 +698,10 @@ static int setup_output(
(void) fd_nonblock(named_iofds[fileno], false);
return dup2(named_iofds[fileno], fileno) < 0 ? -errno : fileno;
- case EXEC_OUTPUT_FILE: {
+ case EXEC_OUTPUT_FILE:
+ case EXEC_OUTPUT_FILE_APPEND: {
bool rw;
- int fd;
+ int fd, flags;
assert(context->stdio_file[fileno]);
@@ -687,11 +711,15 @@ static int setup_output(
if (rw)
return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
- fd = acquire_path(context->stdio_file[fileno], O_WRONLY, 0666 & ~context->umask);
+ flags = O_WRONLY;
+ if (o == EXEC_OUTPUT_FILE_APPEND)
+ flags |= O_APPEND;
+
+ fd = acquire_path(context->stdio_file[fileno], flags, 0666 & ~context->umask);
if (fd < 0)
return fd;
- return move_fd(fd, fileno, false);
+ return move_fd(fd, fileno, 0);
}
default:
@@ -914,7 +942,7 @@ static int get_fixed_user(const ExecContext *c, const char **user,
* (i.e. are "/" or "/bin/nologin"). */
name = c->user;
- r = get_user_creds_clean(&name, uid, gid, home, shell);
+ r = get_user_creds(&name, uid, gid, home, shell, USER_CREDS_CLEAN);
if (r < 0)
return r;
@@ -932,7 +960,7 @@ static int get_fixed_group(const ExecContext *c, const char **group, gid_t *gid)
return 0;
name = c->group;
- r = get_group_creds(&name, gid);
+ r = get_group_creds(&name, gid, 0);
if (r < 0)
return r;
@@ -1004,7 +1032,7 @@ static int get_supplementary_groups(const ExecContext *c, const char *user,
return -E2BIG;
g = *i;
- r = get_group_creds(&g, l_gids+k);
+ r = get_group_creds(&g, l_gids+k, 0);
if (r < 0)
return r;
@@ -1151,6 +1179,16 @@ static int setup_pam(
goto fail;
}
+ if (!tty) {
+ _cleanup_free_ char *q = NULL;
+
+ /* Hmm, so no TTY was explicitly passed, but an fd passed to us directly might be a TTY. Let's figure
+ * out if that's the case, and read the TTY off it. */
+
+ if (getttyname_malloc(STDIN_FILENO, &q) >= 0)
+ tty = strjoina("/dev/", q);
+ }
+
if (tty) {
pam_code = pam_set_item(handle, PAM_TTY, tty);
if (pam_code != PAM_SUCCESS)
@@ -1415,7 +1453,7 @@ static int apply_syscall_filter(const Unit* u, const ExecContext *c, bool needs_
return r;
}
- return seccomp_load_syscall_filter_set_raw(default_action, c->syscall_filter, action);
+ return seccomp_load_syscall_filter_set_raw(default_action, c->syscall_filter, action, false);
}
static int apply_syscall_archs(const Unit *u, const ExecContext *c) {
@@ -1498,7 +1536,7 @@ static int apply_protect_kernel_modules(const Unit *u, const ExecContext *c) {
if (skip_seccomp_unavailable(u, "ProtectKernelModules="))
return 0;
- return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_MODULE, SCMP_ACT_ERRNO(EPERM));
+ return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_MODULE, SCMP_ACT_ERRNO(EPERM), false);
}
static int apply_private_devices(const Unit *u, const ExecContext *c) {
@@ -1513,7 +1551,7 @@ static int apply_private_devices(const Unit *u, const ExecContext *c) {
if (skip_seccomp_unavailable(u, "PrivateDevices="))
return 0;
- return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO, SCMP_ACT_ERRNO(EPERM));
+ return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO, SCMP_ACT_ERRNO(EPERM), false);
}
static int apply_restrict_namespaces(const Unit *u, const ExecContext *c) {
@@ -1585,6 +1623,8 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
idle_pipe[3] = safe_close(idle_pipe[3]);
}
+static const char *exec_directory_env_name_to_string(ExecDirectoryType t);
+
static int build_environment(
const Unit *u,
const ExecContext *c,
@@ -1598,14 +1638,16 @@ static int build_environment(
char ***ret) {
_cleanup_strv_free_ char **our_env = NULL;
+ ExecDirectoryType t;
size_t n_env = 0;
char *x;
assert(u);
assert(c);
+ assert(p);
assert(ret);
- our_env = new0(char*, 14);
+ our_env = new0(char*, 14 + _EXEC_DIRECTORY_TYPE_MAX);
if (!our_env)
return -ENOMEM;
@@ -1710,8 +1752,37 @@ static int build_environment(
our_env[n_env++] = x;
}
+ for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
+ _cleanup_free_ char *pre = NULL, *joined = NULL;
+ const char *n;
+
+ if (!p->prefix[t])
+ continue;
+
+ if (strv_isempty(c->directories[t].paths))
+ continue;
+
+ n = exec_directory_env_name_to_string(t);
+ if (!n)
+ continue;
+
+ pre = strjoin(p->prefix[t], "/");
+ if (!pre)
+ return -ENOMEM;
+
+ joined = strv_join_prefix(c->directories[t].paths, ":", pre);
+ if (!joined)
+ return -ENOMEM;
+
+ x = strjoin(n, "=", joined);
+ if (!x)
+ return -ENOMEM;
+
+ our_env[n_env++] = x;
+ }
+
our_env[n_env++] = NULL;
- assert(n_env <= 12);
+ assert(n_env <= 14 + _EXEC_DIRECTORY_TYPE_MAX);
*ret = TAKE_PTR(our_env);
@@ -2010,7 +2081,7 @@ static int setup_exec_directory(
if (context->dynamic_user &&
!IN_SET(type, EXEC_DIRECTORY_RUNTIME, EXEC_DIRECTORY_CONFIGURATION)) {
- _cleanup_free_ char *private_root = NULL, *relative = NULL, *parent = NULL;
+ _cleanup_free_ char *private_root = NULL;
/* So, here's one extra complication when dealing with DynamicUser=1 units. In that case we
* want to avoid leaving a directory around fully accessible that is owned by a dynamic user
@@ -2075,18 +2146,8 @@ static int setup_exec_directory(
goto fail;
}
- parent = dirname_malloc(p);
- if (!parent) {
- r = -ENOMEM;
- goto fail;
- }
-
- r = path_make_relative(parent, pp, &relative);
- if (r < 0)
- goto fail;
-
/* And link it up from the original place */
- r = symlink_idempotent(relative, p);
+ r = symlink_idempotent(pp, p, true);
if (r < 0)
goto fail;
@@ -2379,11 +2440,24 @@ static int apply_mount_namespace(
bind_mount_free_many(bind_mounts, n_bind_mounts);
- /* If we couldn't set up the namespace this is probably due to a
- * missing capability. In this case, silently proceeed. */
- if (IN_SET(r, -EPERM, -EACCES)) {
- log_unit_debug_errno(u, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m");
- return 0;
+ /* If we couldn't set up the namespace this is probably due to a missing capability. setup_namespace() reports
+ * that with a special, recognizable error ENOANO. In this case, silently proceeed, but only if exclusively
+ * sandboxing options were used, i.e. nothing such as RootDirectory= or BindMount= that would result in a
+ * completely different execution environment. */
+ if (r == -ENOANO) {
+ if (n_bind_mounts == 0 &&
+ context->n_temporary_filesystems == 0 &&
+ !root_dir && !root_image &&
+ !context->dynamic_user) {
+ log_unit_debug(u, "Failed to set up namespace, assuming containerized execution and ignoring.");
+ return 0;
+ }
+
+ log_unit_debug(u, "Failed to set up namespace, and refusing to continue since the selected namespacing options alter mount environment non-trivially.\n"
+ "Bind mounts: %zu, temporary filesystems: %zu, root directory: %s, root image: %s, dynamic user: %s",
+ n_bind_mounts, context->n_temporary_filesystems, yes_no(root_dir), yes_no(root_image), yes_no(context->dynamic_user));
+
+ return -EOPNOTSUPP;
}
return r;
@@ -2456,9 +2530,6 @@ static int setup_keyring(
* on-demand behaviour is very appropriate for login users, but probably not so much for system services, where
* UIDs are not necessarily specific to a service but reused (at least in the case of UID 0). */
- if (!(p->flags & EXEC_NEW_KEYRING))
- return 0;
-
if (context->keyring_mode == EXEC_KEYRING_INHERIT)
return 0;
@@ -2566,6 +2637,7 @@ static int close_remaining_fds(
const DynamicCreds *dcreds,
int user_lookup_fd,
int socket_fd,
+ int exec_fd,
int *fds, size_t n_fds) {
size_t n_dont_close = 0;
@@ -2582,6 +2654,8 @@ static int close_remaining_fds(
if (socket_fd >= 0)
dont_close[n_dont_close++] = socket_fd;
+ if (exec_fd >= 0)
+ dont_close[n_dont_close++] = exec_fd;
if (n_fds > 0) {
memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
n_dont_close += n_fds;
@@ -2707,6 +2781,37 @@ static int compile_suggested_paths(const ExecContext *c, const ExecParameters *p
static char *exec_command_line(char **argv);
+static int exec_parameters_get_cgroup_path(const ExecParameters *params, char **ret) {
+ bool using_subcgroup;
+ char *p;
+
+ assert(params);
+ assert(ret);
+
+ if (!params->cgroup_path)
+ return -EINVAL;
+
+ /* If we are called for a unit where cgroup delegation is on, and the payload created its own populated
+ * subcgroup (which we expect it to do, after all it asked for delegation), then we cannot place the control
+ * processes started after the main unit's process in the unit's main cgroup because it is now an inner one,
+ * and inner cgroups may not contain processes. Hence, if delegation is on, and this is a control process,
+ * let's use ".control" as subcgroup instead. Note that we do so only for ExecStartPost=, ExecReload=,
+ * ExecStop=, ExecStopPost=, i.e. for the commands where the main process is already forked. For ExecStartPre=
+ * this is not necessary, the cgroup is still empty. We distinguish these cases with the EXEC_CONTROL_CGROUP
+ * flag, which is only passed for the former statements, not for the latter. */
+
+ using_subcgroup = FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP|EXEC_CGROUP_DELEGATE|EXEC_IS_CONTROL);
+ if (using_subcgroup)
+ p = strjoin(params->cgroup_path, "/.control");
+ else
+ p = strdup(params->cgroup_path);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = p;
+ return using_subcgroup;
+}
+
static int exec_child(
Unit *unit,
const ExecCommand *command,
@@ -2714,20 +2819,20 @@ static int exec_child(
const ExecParameters *params,
ExecRuntime *runtime,
DynamicCreds *dcreds,
- char **argv,
int socket_fd,
int named_iofds[3],
int *fds,
- size_t n_storage_fds,
size_t n_socket_fds,
+ size_t n_storage_fds,
char **files_env,
int user_lookup_fd,
int *exit_status) {
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
- _cleanup_free_ char *home_buffer = NULL;
+ int *fds_with_exec_fd, n_fds_with_exec_fd, r, ngids = 0, exec_fd = -1;
_cleanup_free_ gid_t *supplementary_gids = NULL;
const char *username = NULL, *groupname = NULL;
+ _cleanup_free_ char *home_buffer = NULL;
const char *home = NULL, *shell = NULL;
dev_t journal_stream_dev = 0;
ino_t journal_stream_ino = 0;
@@ -2747,7 +2852,6 @@ static int exec_child(
#endif
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
- int r, ngids = 0;
size_t n_fds;
ExecDirectoryType dt;
int secure_bits;
@@ -2791,8 +2895,8 @@ static int exec_child(
/* In case anything used libc syslog(), close this here, too */
closelog();
- n_fds = n_storage_fds + n_socket_fds;
- r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds);
+ n_fds = n_socket_fds + n_storage_fds;
+ r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, params->exec_fd, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_FDS;
return log_unit_error_errno(unit, r, "Failed to close unwanted file descriptors: %m");
@@ -2810,7 +2914,7 @@ static int exec_child(
const char *vc = params->confirm_spawn;
_cleanup_free_ char *cmdline = NULL;
- cmdline = exec_command_line(argv);
+ cmdline = exec_command_line(command->argv);
if (!cmdline) {
*exit_status = EXIT_MEMORY;
return log_oom();
@@ -2828,10 +2932,22 @@ static int exec_child(
}
}
+ /* We are about to invoke NSS and PAM modules. Let's tell them what we are doing here, maybe they care. This is
+ * used by nss-resolve to disable itself when we are about to start systemd-resolved, to avoid deadlocks. Note
+ * that these env vars do not survive the execve(), which means they really only apply to the PAM and NSS
+ * invocations themselves. Also note that while we'll only invoke NSS modules involved in user management they
+ * might internally call into other NSS modules that are involved in hostname resolution, we never know. */
+ if (setenv("SYSTEMD_ACTIVATION_UNIT", unit->id, true) != 0 ||
+ setenv("SYSTEMD_ACTIVATION_SCOPE", MANAGER_IS_SYSTEM(unit->manager) ? "system" : "user", true) != 0) {
+ *exit_status = EXIT_MEMORY;
+ return log_unit_error_errno(unit, errno, "Failed to update environment: %m");
+ }
+
if (context->dynamic_user && dcreds) {
_cleanup_strv_free_ char **suggested_paths = NULL;
- /* Make sure we bypass our own NSS module for any NSS checks */
+ /* On top of that, make sure we bypass our own NSS module nss-systemd comprehensively for any NSS
+ * checks, if DynamicUser=1 is used, as we shouldn't create a feedback loop with ourselves here.*/
if (putenv((char*) "SYSTEMD_NSS_DYNAMIC_BYPASS=1") != 0) {
*exit_status = EXIT_USER;
return log_unit_error_errno(unit, errno, "Failed to update environment: %m");
@@ -2909,6 +3025,24 @@ static int exec_child(
if (socket_fd >= 0)
(void) fd_nonblock(socket_fd, false);
+ /* Journald will try to look-up our cgroup in order to populate _SYSTEMD_CGROUP and _SYSTEMD_UNIT fields.
+ * Hence we need to migrate to the target cgroup from init.scope before connecting to journald */
+ if (params->cgroup_path) {
+ _cleanup_free_ char *p = NULL;
+
+ r = exec_parameters_get_cgroup_path(params, &p);
+ if (r < 0) {
+ *exit_status = EXIT_CGROUP;
+ return log_unit_error_errno(unit, r, "Failed to acquire cgroup path: %m");
+ }
+
+ r = cg_attach_everywhere(params->cgroup_supported, p, 0, NULL, NULL);
+ if (r < 0) {
+ *exit_status = EXIT_CGROUP;
+ return log_unit_error_errno(unit, r, "Failed to attach to cgroup %s: %m", p);
+ }
+ }
+
r = setup_input(context, params, socket_fd, named_iofds);
if (r < 0) {
*exit_status = EXIT_STDIN;
@@ -2927,14 +3061,6 @@ static int exec_child(
return log_unit_error_errno(unit, r, "Failed to set up standard error output: %m");
}
- if (params->cgroup_path) {
- r = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0, NULL, NULL);
- if (r < 0) {
- *exit_status = EXIT_CGROUP;
- return log_unit_error_errno(unit, r, "Failed to attach to cgroup %s: %m", params->cgroup_path);
- }
- }
-
if (context->oom_score_adjust_set) {
/* When we can't make this change due to EPERM, then let's silently skip over it. User namespaces
* prohibit write access to this file, and we shouldn't trip up over that. */
@@ -3130,11 +3256,6 @@ static int exec_child(
}
}
- /* Apply just after mount namespace setup */
- r = apply_working_directory(context, params, home, needs_mount_namespace, exit_status);
- if (r < 0)
- return log_unit_error_errno(unit, r, "Changing to the requested working directory failed: %m");
-
/* Drop groups as early as possbile */
if (needs_setuid) {
r = enforce_groups(gid, supplementary_gids, ngids);
@@ -3165,18 +3286,59 @@ static int exec_child(
}
/* We repeat the fd closing here, to make sure that nothing is leaked from the PAM modules. Note that we are
- * more aggressive this time since socket_fd and the netns fds we don't need anymore. The custom endpoint fd
- * was needed to upload the policy and can now be closed as well. */
- r = close_all_fds(fds, n_fds);
+ * more aggressive this time since socket_fd and the netns fds we don't need anymore. We do keep the exec_fd
+ * however if we have it as we want to keep it open until the final execve(). */
+
+ if (params->exec_fd >= 0) {
+ exec_fd = params->exec_fd;
+
+ if (exec_fd < 3 + (int) n_fds) {
+ int moved_fd;
+
+ /* Let's move the exec fd far up, so that it's outside of the fd range we want to pass to the
+ * process we are about to execute. */
+
+ moved_fd = fcntl(exec_fd, F_DUPFD_CLOEXEC, 3 + (int) n_fds);
+ if (moved_fd < 0) {
+ *exit_status = EXIT_FDS;
+ return log_unit_error_errno(unit, errno, "Couldn't move exec fd up: %m");
+ }
+
+ safe_close(exec_fd);
+ exec_fd = moved_fd;
+ } else {
+ /* This fd should be FD_CLOEXEC already, but let's make sure. */
+ r = fd_cloexec(exec_fd, true);
+ if (r < 0) {
+ *exit_status = EXIT_FDS;
+ return log_unit_error_errno(unit, r, "Failed to make exec fd FD_CLOEXEC: %m");
+ }
+ }
+
+ fds_with_exec_fd = newa(int, n_fds + 1);
+ memcpy_safe(fds_with_exec_fd, fds, n_fds * sizeof(int));
+ fds_with_exec_fd[n_fds] = exec_fd;
+ n_fds_with_exec_fd = n_fds + 1;
+ } else {
+ fds_with_exec_fd = fds;
+ n_fds_with_exec_fd = n_fds;
+ }
+
+ r = close_all_fds(fds_with_exec_fd, n_fds_with_exec_fd);
if (r >= 0)
r = shift_fds(fds, n_fds);
if (r >= 0)
- r = flags_fds(fds, n_storage_fds, n_socket_fds, context->non_blocking);
+ r = flags_fds(fds, n_socket_fds, n_storage_fds, context->non_blocking);
if (r < 0) {
*exit_status = EXIT_FDS;
return log_unit_error_errno(unit, r, "Failed to adjust passed file descriptors: %m");
}
+ /* At this point, the fds we want to pass to the program are all ready and set up, with O_CLOEXEC turned off
+ * and at the right fd numbers. The are no other fds open, with one exception: the exec_fd if it is defined,
+ * and it has O_CLOEXEC set, after all we want it to be closed by the execve(), so that our parent knows we
+ * came this far. */
+
secure_bits = context->secure_bits;
if (needs_sandboxing) {
@@ -3268,6 +3430,12 @@ static int exec_child(
}
}
+ /* Apply working directory here, because the working directory might be on NFS and only the user running
+ * this service might have the correct privilege to change to the working directory */
+ r = apply_working_directory(context, params, home, needs_mount_namespace, exit_status);
+ if (r < 0)
+ return log_unit_error_errno(unit, r, "Changing to the requested working directory failed: %m");
+
if (needs_sandboxing) {
/* Apply other MAC contexts late, but before seccomp syscall filtering, as those should really be last to
* influence our own codepaths as little as possible. Moreover, applying MAC contexts usually requires
@@ -3389,7 +3557,7 @@ static int exec_child(
strv_free_and_replace(accum_env, ee);
}
- final_argv = replace_env_argv(argv, accum_env);
+ final_argv = replace_env_argv(command->argv, accum_env);
if (!final_argv) {
*exit_status = EXIT_MEMORY;
return log_oom();
@@ -3407,10 +3575,35 @@ static int exec_child(
LOG_UNIT_INVOCATION_ID(unit));
}
+ if (exec_fd >= 0) {
+ uint8_t hot = 1;
+
+ /* We have finished with all our initializations. Let's now let the manager know that. From this point
+ * on, if the manager sees POLLHUP on the exec_fd, then execve() was successful. */
+
+ if (write(exec_fd, &hot, sizeof(hot)) < 0) {
+ *exit_status = EXIT_EXEC;
+ return log_unit_error_errno(unit, errno, "Failed to enable exec_fd: %m");
+ }
+ }
+
execve(command->path, final_argv, accum_env);
+ r = -errno;
+
+ if (exec_fd >= 0) {
+ uint8_t hot = 0;
- if (errno == ENOENT && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
- log_struct_errno(LOG_INFO, errno,
+ /* The execve() failed. This means the exec_fd is still open. Which means we need to tell the manager
+ * that POLLHUP on it no longer means execve() succeeded. */
+
+ if (write(exec_fd, &hot, sizeof(hot)) < 0) {
+ *exit_status = EXIT_EXEC;
+ return log_unit_error_errno(unit, errno, "Failed to disable exec_fd: %m");
+ }
+ }
+
+ if (r == -ENOENT && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
+ log_struct_errno(LOG_INFO, r,
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
LOG_UNIT_ID(unit),
LOG_UNIT_INVOCATION_ID(unit),
@@ -3421,7 +3614,7 @@ static int exec_child(
}
*exit_status = EXIT_EXEC;
- return log_unit_error_errno(unit, errno, "Failed to execute command: %m");
+ return log_unit_error_errno(unit, r, "Failed to execute command: %m");
}
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
@@ -3435,13 +3628,11 @@ int exec_spawn(Unit *unit,
DynamicCreds *dcreds,
pid_t *ret) {
+ int socket_fd, r, named_iofds[3] = { -1, -1, -1 }, *fds = NULL;
+ _cleanup_free_ char *subcgroup_path = NULL;
_cleanup_strv_free_ char **files_env = NULL;
- int *fds = NULL;
size_t n_storage_fds = 0, n_socket_fds = 0;
_cleanup_free_ char *line = NULL;
- int socket_fd, r;
- int named_iofds[3] = { -1, -1, -1 };
- char **argv;
pid_t pid;
assert(unit);
@@ -3449,7 +3640,7 @@ int exec_spawn(Unit *unit,
assert(context);
assert(ret);
assert(params);
- assert(params->fds || (params->n_storage_fds + params->n_socket_fds <= 0));
+ assert(params->fds || (params->n_socket_fds + params->n_storage_fds <= 0));
if (context->std_input == EXEC_INPUT_SOCKET ||
context->std_output == EXEC_OUTPUT_SOCKET ||
@@ -3469,8 +3660,8 @@ int exec_spawn(Unit *unit,
} else {
socket_fd = -1;
fds = params->fds;
- n_storage_fds = params->n_storage_fds;
n_socket_fds = params->n_socket_fds;
+ n_storage_fds = params->n_storage_fds;
}
r = exec_context_named_iofds(context, params, named_iofds);
@@ -3481,8 +3672,7 @@ int exec_spawn(Unit *unit,
if (r < 0)
return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
- argv = params->argv ?: command->argv;
- line = exec_command_line(argv);
+ line = exec_command_line(command->argv);
if (!line)
return log_oom();
@@ -3492,6 +3682,17 @@ int exec_spawn(Unit *unit,
LOG_UNIT_ID(unit),
LOG_UNIT_INVOCATION_ID(unit));
+ if (params->cgroup_path) {
+ r = exec_parameters_get_cgroup_path(params, &subcgroup_path);
+ if (r < 0)
+ return log_unit_error_errno(unit, r, "Failed to acquire subcgroup path: %m");
+ if (r > 0) { /* We are using a child cgroup */
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path);
+ if (r < 0)
+ return log_unit_error_errno(unit, r, "Failed to create control group '%s': %m", subcgroup_path);
+ }
+ }
+
pid = fork();
if (pid < 0)
return log_unit_error_errno(unit, errno, "Failed to fork: %m");
@@ -3505,12 +3706,11 @@ int exec_spawn(Unit *unit,
params,
runtime,
dcreds,
- argv,
socket_fd,
named_iofds,
fds,
- n_storage_fds,
n_socket_fds,
+ n_storage_fds,
files_env,
unit->manager->user_lookup_fds[1],
&exit_status);
@@ -3530,13 +3730,11 @@ int exec_spawn(Unit *unit,
log_unit_debug(unit, "Forked %s as "PID_FMT, command->path, pid);
- /* We add the new process to the cgroup both in the child (so
- * that we can be sure that no user code is ever executed
- * outside of the cgroup) and in the parent (so that we can be
- * sure that when we kill the cgroup the process will be
- * killed too). */
- if (params->cgroup_path)
- (void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, pid);
+ /* We add the new process to the cgroup both in the child (so that we can be sure that no user code is ever
+ * executed outside of the cgroup) and in the parent (so that we can be sure that when we kill the cgroup the
+ * process will be killed too). */
+ if (subcgroup_path)
+ (void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path, pid);
exec_status_start(&command->exec_status, pid);
@@ -3624,6 +3822,9 @@ void exec_context_done(ExecContext *c) {
exec_context_free_log_extra_fields(c);
+ c->log_rate_limit_interval_usec = 0;
+ c->log_rate_limit_burst = 0;
+
c->stdin_data = mfree(c->stdin_data);
c->stdin_data_size = 0;
}
@@ -3655,7 +3856,6 @@ static void exec_command_done(ExecCommand *c) {
assert(c);
c->path = mfree(c->path);
-
c->argv = strv_free(c->argv);
}
@@ -3685,6 +3885,24 @@ void exec_command_free_array(ExecCommand **c, size_t n) {
c[i] = exec_command_free_list(c[i]);
}
+void exec_command_reset_status_array(ExecCommand *c, size_t n) {
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ exec_status_reset(&c[i].exec_status);
+}
+
+void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ ExecCommand *z;
+
+ LIST_FOREACH(command, z, c[i])
+ exec_status_reset(&z->exec_status);
+ }
+}
+
typedef struct InvalidEnvInfo {
const Unit *unit;
const char *path;
@@ -3813,7 +4031,7 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c,
assert(pglob.gl_pathc > 0);
for (n = 0; n < pglob.gl_pathc; n++) {
- k = load_env_file(NULL, pglob.gl_pathv[n], NULL, &p);
+ k = load_env_file(NULL, pglob.gl_pathv[n], &p);
if (k < 0) {
if (ignore)
continue;
@@ -3976,9 +4194,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
for (i = 0; i < RLIM_NLIMITS; i++)
if (c->rlimit[i]) {
- fprintf(f, "Limit%s%s: " RLIM_FMT "\n",
+ fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
- fprintf(f, "Limit%s%sSoft: " RLIM_FMT "\n",
+ fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
}
@@ -4036,8 +4254,12 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
if (c->std_output == EXEC_OUTPUT_FILE)
fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
+ if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
+ fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
if (c->std_error == EXEC_OUTPUT_FILE)
fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
+ if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
+ fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
if (c->tty_path)
fprintf(f,
@@ -4084,6 +4306,17 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
}
+ if (c->log_rate_limit_interval_usec > 0) {
+ char buf_timespan[FORMAT_TIMESPAN_MAX];
+
+ fprintf(f,
+ "%sLogRateLimitIntervalSec: %s\n",
+ prefix, format_timespan(buf_timespan, sizeof(buf_timespan), c->log_rate_limit_interval_usec, USEC_PER_SEC));
+ }
+
+ if (c->log_rate_limit_burst > 0)
+ fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_rate_limit_burst);
+
if (c->n_log_extra_fields > 0) {
size_t j;
@@ -4331,18 +4564,22 @@ void exec_context_free_log_extra_fields(ExecContext *c) {
void exec_status_start(ExecStatus *s, pid_t pid) {
assert(s);
- zero(*s);
- s->pid = pid;
+ *s = (ExecStatus) {
+ .pid = pid,
+ };
+
dual_timestamp_get(&s->start_timestamp);
}
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
assert(s);
- if (s->pid && s->pid != pid)
- zero(*s);
+ if (s->pid != pid) {
+ *s = (ExecStatus) {
+ .pid = pid,
+ };
+ }
- s->pid = pid;
dual_timestamp_get(&s->exit_timestamp);
s->code = code;
@@ -4350,12 +4587,18 @@ void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int
if (context) {
if (context->utmp_id)
- utmp_put_dead_process(context->utmp_id, pid, code, status);
+ (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
exec_context_tty_reset(context, NULL);
}
}
+void exec_status_reset(ExecStatus *s) {
+ assert(s);
+
+ *s = (ExecStatus) {};
+}
+
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
char buf[FORMAT_TIMESTAMP_MAX];
@@ -4487,8 +4730,7 @@ int exec_command_set(ExecCommand *c, const char *path, ...) {
return -ENOMEM;
}
- free(c->path);
- c->path = p;
+ free_and_replace(c->path, p);
return strv_free_and_replace(c->argv, l);
}
@@ -4920,10 +5162,8 @@ void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
finalize:
r = exec_runtime_add(m, id, tmp_dir, var_tmp_dir, (int[]) { fd0, fd1 }, NULL);
- if (r < 0) {
+ if (r < 0)
log_debug_errno(r, "Failed to add exec-runtime: %m");
- return;
- }
}
void exec_runtime_vacuum(Manager *m) {
@@ -4942,6 +5182,13 @@ void exec_runtime_vacuum(Manager *m) {
}
}
+void exec_params_clear(ExecParameters *p) {
+ if (!p)
+ return;
+
+ strv_free(p->environment);
+}
+
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
[EXEC_INPUT_NULL] = "null",
[EXEC_INPUT_TTY] = "tty",
@@ -4968,6 +5215,7 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
[EXEC_OUTPUT_SOCKET] = "socket",
[EXEC_OUTPUT_NAMED_FD] = "fd",
[EXEC_OUTPUT_FILE] = "file",
+ [EXEC_OUTPUT_FILE_APPEND] = "append",
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
@@ -4998,6 +5246,16 @@ static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
+static const char* const exec_directory_env_name_table[_EXEC_DIRECTORY_TYPE_MAX] = {
+ [EXEC_DIRECTORY_RUNTIME] = "RUNTIME_DIRECTORY",
+ [EXEC_DIRECTORY_STATE] = "STATE_DIRECTORY",
+ [EXEC_DIRECTORY_CACHE] = "CACHE_DIRECTORY",
+ [EXEC_DIRECTORY_LOGS] = "LOGS_DIRECTORY",
+ [EXEC_DIRECTORY_CONFIGURATION] = "CONFIGURATION_DIRECTORY",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(exec_directory_env_name, ExecDirectoryType);
+
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
[EXEC_KEYRING_INHERIT] = "inherit",
[EXEC_KEYRING_PRIVATE] = "private",
diff --git a/src/core/execute.h b/src/core/execute.h
index 77ffe82323..0f1bf56744 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -16,7 +16,7 @@ typedef struct Manager Manager;
#include "cgroup-util.h"
#include "fdset.h"
#include "list.h"
-#include "missing.h"
+#include "missing_resource.h"
#include "namespace.h"
#include "nsflags.h"
@@ -56,6 +56,7 @@ typedef enum ExecOutput {
EXEC_OUTPUT_SOCKET,
EXEC_OUTPUT_NAMED_FD,
EXEC_OUTPUT_FILE,
+ EXEC_OUTPUT_FILE_APPEND,
_EXEC_OUTPUT_MAX,
_EXEC_OUTPUT_INVALID = -1
} ExecOutput;
@@ -76,21 +77,23 @@ typedef enum ExecKeyringMode {
_EXEC_KEYRING_MODE_INVALID = -1,
} ExecKeyringMode;
+/* Contains start and exit information about an executed command. */
struct ExecStatus {
+ pid_t pid;
dual_timestamp start_timestamp;
dual_timestamp exit_timestamp;
- pid_t pid;
int code; /* as in siginfo_t::si_code */
int status; /* as in sigingo_t::si_status */
};
typedef enum ExecCommandFlags {
- EXEC_COMMAND_IGNORE_FAILURE = 1,
- EXEC_COMMAND_FULLY_PRIVILEGED = 2,
- EXEC_COMMAND_NO_SETUID = 4,
- EXEC_COMMAND_AMBIENT_MAGIC = 8,
+ EXEC_COMMAND_IGNORE_FAILURE = 1 << 0,
+ EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
+ EXEC_COMMAND_NO_SETUID = 1 << 2,
+ EXEC_COMMAND_AMBIENT_MAGIC = 1 << 3,
} ExecCommandFlags;
+/* Stores information about commands we execute. Covers both configuration settings as well as runtime data. */
struct ExecCommand {
char *path;
char **argv;
@@ -99,13 +102,16 @@ struct ExecCommand {
LIST_FIELDS(ExecCommand, command); /* useful for chaining commands */
};
+/* Encapsulates certain aspects of the runtime environment that is to be shared between multiple otherwise separate
+ * invocations of commands. Specifically, this allows sharing of /tmp and /var/tmp data as well as network namespaces
+ * between invocations of commands. This is a reference counted object, with one reference taken by each currently
+ * active command invocation that wants to share this runtime. */
struct ExecRuntime {
- int n_ref;
+ unsigned n_ref;
Manager *manager;
- /* unit id of the owner */
- char *id;
+ char *id; /* Unit id of the owner */
char *tmp_dir;
char *var_tmp_dir;
@@ -130,6 +136,9 @@ typedef struct ExecDirectory {
mode_t mode;
} ExecDirectory;
+/* Encodes configuration parameters applied to invoked commands. Does not carry runtime data, but only configuration
+ * changes sourced from unit files and suchlike. ExecContext objects are usually embedded into Unit objects, and do not
+ * change after being loaded. */
struct ExecContext {
char **environment;
char **environment_files;
@@ -216,6 +225,9 @@ struct ExecContext {
struct iovec* log_extra_fields;
size_t n_log_extra_fields;
+ usec_t log_rate_limit_interval_usec;
+ unsigned log_rate_limit_burst;
+
bool cpu_sched_reset_on_fork;
bool non_blocking;
bool private_tmp;
@@ -277,27 +289,28 @@ typedef enum ExecFlags {
EXEC_APPLY_SANDBOXING = 1 << 0,
EXEC_APPLY_CHROOT = 1 << 1,
EXEC_APPLY_TTY_STDIN = 1 << 2,
- EXEC_NEW_KEYRING = 1 << 3,
- EXEC_PASS_LOG_UNIT = 1 << 4, /* Whether to pass the unit name to the service's journal stream connection */
- EXEC_CHOWN_DIRECTORIES = 1 << 5, /* chown() the runtime/state/cache/log directories to the user we run as, under all conditions */
- EXEC_NSS_BYPASS_BUS = 1 << 6, /* Set the SYSTEMD_NSS_BYPASS_BUS environment variable, to disable nss-systemd for dbus */
- EXEC_CGROUP_DELEGATE = 1 << 7,
+ EXEC_PASS_LOG_UNIT = 1 << 3, /* Whether to pass the unit name to the service's journal stream connection */
+ EXEC_CHOWN_DIRECTORIES = 1 << 4, /* chown() the runtime/state/cache/log directories to the user we run as, under all conditions */
+ EXEC_NSS_BYPASS_BUS = 1 << 5, /* Set the SYSTEMD_NSS_BYPASS_BUS environment variable, to disable nss-systemd for dbus */
+ EXEC_CGROUP_DELEGATE = 1 << 6,
+ EXEC_IS_CONTROL = 1 << 7,
+ EXEC_CONTROL_CGROUP = 1 << 8, /* Place the process not in the indicated cgroup but in a subcgroup '/.control', but only EXEC_CGROUP_DELEGATE and EXEC_IS_CONTROL is set, too */
/* The following are not used by execute.c, but by consumers internally */
- EXEC_PASS_FDS = 1 << 8,
- EXEC_IS_CONTROL = 1 << 9,
+ EXEC_PASS_FDS = 1 << 9,
EXEC_SETENV_RESULT = 1 << 10,
EXEC_SET_WATCHDOG = 1 << 11,
} ExecFlags;
+/* Parameters for a specific invocation of a command. This structure is put together right before a command is
+ * executed. */
struct ExecParameters {
- char **argv;
char **environment;
int *fds;
char **fd_names;
- size_t n_storage_fds;
size_t n_socket_fds;
+ size_t n_storage_fds;
ExecFlags flags;
bool selinux_context_net:1;
@@ -316,6 +329,9 @@ struct ExecParameters {
int stdin_fd;
int stdout_fd;
int stderr_fd;
+
+ /* An fd that is closed by the execve(), and thus will result in EOF when the execve() is done */
+ int exec_fd;
};
#include "unit.h"
@@ -330,14 +346,14 @@ int exec_spawn(Unit *unit,
pid_t *ret);
void exec_command_done_array(ExecCommand *c, size_t n);
-
ExecCommand* exec_command_free_list(ExecCommand *c);
void exec_command_free_array(ExecCommand **c, size_t n);
-
+void exec_command_reset_status_array(ExecCommand *c, size_t n);
+void exec_command_reset_status_list_array(ExecCommand **c, size_t n);
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix);
void exec_command_append_list(ExecCommand **l, ExecCommand *e);
-int exec_command_set(ExecCommand *c, const char *path, ...);
-int exec_command_append(ExecCommand *c, const char *path, ...);
+int exec_command_set(ExecCommand *c, const char *path, ...) _sentinel_;
+int exec_command_append(ExecCommand *c, const char *path, ...) _sentinel_;
void exec_context_init(ExecContext *c);
void exec_context_done(ExecContext *c);
@@ -357,6 +373,7 @@ void exec_context_free_log_extra_fields(ExecContext *c);
void exec_status_start(ExecStatus *s, pid_t pid);
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status);
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix);
+void exec_status_reset(ExecStatus *s);
int exec_runtime_acquire(Manager *m, const ExecContext *c, const char *name, bool create, ExecRuntime **ret);
ExecRuntime *exec_runtime_unref(ExecRuntime *r, bool destroy);
@@ -366,6 +383,8 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value,
void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds);
void exec_runtime_vacuum(Manager *m);
+void exec_params_clear(ExecParameters *p);
+
const char* exec_output_to_string(ExecOutput i) _const_;
ExecOutput exec_output_from_string(const char *s) _pure_;
diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c
index 013d6c5de3..fd7c5f64af 100644
--- a/src/core/ima-setup.c
+++ b/src/core/ima-setup.c
@@ -7,6 +7,7 @@
#include <errno.h>
#include <unistd.h>
+#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "ima-setup.h"
@@ -22,20 +23,20 @@ int ima_setup(void) {
_cleanup_fclose_ FILE *input = NULL;
_cleanup_close_ int imafd = -1;
unsigned lineno = 0;
- char line[page_size()];
+ int r;
if (access(IMA_SECFS_DIR, F_OK) < 0) {
- log_debug("IMA support is disabled in the kernel, ignoring.");
+ log_debug_errno(errno, "IMA support is disabled in the kernel, ignoring: %m");
return 0;
}
if (access(IMA_SECFS_POLICY, W_OK) < 0) {
- log_warning("Another IMA custom policy has already been loaded, ignoring.");
+ log_warning_errno(errno, "Another IMA custom policy has already been loaded, ignoring: %m");
return 0;
}
if (access(IMA_POLICY_PATH, F_OK) < 0) {
- log_debug("No IMA custom policy file "IMA_POLICY_PATH", ignoring.");
+ log_debug_errno(errno, "No IMA custom policy file "IMA_POLICY_PATH", ignoring: %m");
return 0;
}
@@ -56,7 +57,7 @@ int ima_setup(void) {
return 0;
}
- close(imafd);
+ safe_close(imafd);
imafd = open(IMA_SECFS_POLICY, O_WRONLY|O_CLOEXEC);
if (imafd < 0) {
@@ -64,10 +65,16 @@ int ima_setup(void) {
return 0;
}
- FOREACH_LINE(line, input,
- return log_error_errno(errno, "Failed to read the IMA custom policy file "IMA_POLICY_PATH": %m")) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
size_t len;
+ r = read_line(input, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read the IMA custom policy file "IMA_POLICY_PATH": %m");
+ if (r == 0)
+ break;
+
len = strlen(line);
lineno++;
diff --git a/src/core/ip-address-access.h b/src/core/ip-address-access.h
index 7babf19562..77078e1f14 100644
--- a/src/core/ip-address-access.h
+++ b/src/core/ip-address-access.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "conf-parser.h"
#include "in-addr-util.h"
#include "list.h"
diff --git a/src/core/job.c b/src/core/job.c
index 734756b666..f635b7e933 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -10,10 +10,12 @@
#include "dbus-job.h"
#include "dbus.h"
#include "escape.h"
+#include "fileio.h"
#include "job.h"
#include "log.h"
#include "macro.h"
#include "parse-util.h"
+#include "serialize.h"
#include "set.h"
#include "special.h"
#include "stdio-util.h"
@@ -31,14 +33,15 @@ Job* job_new_raw(Unit *unit) {
assert(unit);
- j = new0(Job, 1);
+ j = new(Job, 1);
if (!j)
return NULL;
- j->manager = unit->manager;
- j->unit = unit;
- j->type = _JOB_TYPE_INVALID;
- j->reloaded = false;
+ *j = (Job) {
+ .manager = unit->manager,
+ .unit = unit,
+ .type = _JOB_TYPE_INVALID,
+ };
return j;
}
@@ -86,7 +89,7 @@ void job_unlink(Job *j) {
j->timer_event_source = sd_event_source_unref(j->timer_event_source);
}
-void job_free(Job *j) {
+Job* job_free(Job *j) {
assert(j);
assert(!j->installed);
assert(!j->transaction_prev);
@@ -99,7 +102,7 @@ void job_free(Job *j) {
sd_bus_track_unref(j->bus_track);
strv_free(j->deserialized_clients);
- free(j);
+ return mfree(j);
}
static void job_set_state(Job *j, JobState state) {
@@ -148,7 +151,7 @@ void job_uninstall(Job *j) {
unit_add_to_gc_queue(j->unit);
- hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
+ hashmap_remove_value(j->manager->jobs, UINT32_TO_PTR(j->id), j);
j->installed = false;
}
@@ -174,7 +177,7 @@ static void job_merge_into_installed(Job *j, Job *other) {
assert(j->unit == other->unit);
if (j->type != JOB_NOP)
- job_type_merge_and_collapse(&j->type, other->type, j->unit);
+ assert_se(job_type_merge_and_collapse(&j->type, other->type, j->unit) == 0);
else
assert(other->type == JOB_NOP);
@@ -233,28 +236,36 @@ Job* job_install(Job *j) {
job_add_to_gc_queue(j);
+ job_add_to_dbus_queue(j); /* announce this job to clients */
+ unit_add_to_dbus_queue(j->unit); /* The Job property of the unit has changed now */
+
return j;
}
int job_install_deserialized(Job *j) {
Job **pj;
+ int r;
assert(!j->installed);
- if (j->type < 0 || j->type >= _JOB_TYPE_MAX_IN_TRANSACTION) {
- log_debug("Invalid job type %s in deserialization.", strna(job_type_to_string(j->type)));
- return -EINVAL;
- }
+ if (j->type < 0 || j->type >= _JOB_TYPE_MAX_IN_TRANSACTION)
+ return log_unit_debug_errno(j->unit, SYNTHETIC_ERRNO(EINVAL),
+ "Invalid job type %s in deserialization.",
+ strna(job_type_to_string(j->type)));
pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
- if (*pj) {
- log_unit_debug(j->unit, "Unit already has a job installed. Not installing deserialized job.");
- return -EEXIST;
- }
+ if (*pj)
+ return log_unit_debug_errno(j->unit, SYNTHETIC_ERRNO(EEXIST),
+ "Unit already has a job installed. Not installing deserialized job.");
+
+ r = hashmap_put(j->manager->jobs, UINT32_TO_PTR(j->id), j);
+ if (r == -EEXIST)
+ return log_unit_debug_errno(j->unit, r, "Job ID %" PRIu32 " already used, cannot deserialize job.", j->id);
+ if (r < 0)
+ return log_unit_debug_errno(j->unit, r, "Failed to insert job into jobs hash table: %m");
*pj = j;
j->installed = true;
- j->reloaded = true;
if (j->state == JOB_RUNNING)
j->unit->manager->n_running_jobs++;
@@ -303,7 +314,7 @@ void job_dependency_free(JobDependency *l) {
free(l);
}
-void job_dump(Job *j, FILE*f, const char *prefix) {
+void job_dump(Job *j, FILE *f, const char *prefix) {
assert(j);
assert(f);
@@ -506,6 +517,95 @@ static void job_change_type(Job *j, JobType newtype) {
j->type = newtype;
}
+_pure_ static const char* job_get_begin_status_message_format(Unit *u, JobType t) {
+ const char *format;
+
+ assert(u);
+
+ if (t == JOB_RELOAD)
+ return "Reloading %s.";
+
+ assert(IN_SET(t, JOB_START, JOB_STOP));
+
+ format = UNIT_VTABLE(u)->status_message_formats.starting_stopping[t == JOB_STOP];
+ if (format)
+ return format;
+
+ /* Return generic strings */
+ if (t == JOB_START)
+ return "Starting %s.";
+ else {
+ assert(t == JOB_STOP);
+ return "Stopping %s.";
+ }
+}
+
+static void job_print_begin_status_message(Unit *u, JobType t) {
+ const char *format;
+
+ assert(u);
+
+ /* Reload status messages have traditionally not been printed to console. */
+ if (!IN_SET(t, JOB_START, JOB_STOP))
+ return;
+
+ format = job_get_begin_status_message_format(u, t);
+
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ unit_status_printf(u, "", format);
+ REENABLE_WARNING;
+}
+
+static void job_log_begin_status_message(Unit *u, uint32_t job_id, JobType t) {
+ const char *format, *mid;
+ char buf[LINE_MAX];
+
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
+
+ if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD))
+ return;
+
+ if (log_on_console()) /* Skip this if it would only go on the console anyway */
+ return;
+
+ /* We log status messages for all units and all operations. */
+
+ format = job_get_begin_status_message_format(u, t);
+
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ (void) snprintf(buf, sizeof buf, format, unit_description(u));
+ REENABLE_WARNING;
+
+ mid = t == JOB_START ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTING_STR :
+ t == JOB_STOP ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPING_STR :
+ "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADING_STR;
+
+ /* Note that we deliberately use LOG_MESSAGE() instead of
+ * LOG_UNIT_MESSAGE() here, since this is supposed to mimic
+ * closely what is written to screen using the status output,
+ * which is supposed the highest level, friendliest output
+ * possible, which means we should avoid the low-level unit
+ * name. */
+ log_struct(LOG_INFO,
+ LOG_MESSAGE("%s", buf),
+ "JOB_ID=%" PRIu32, job_id,
+ "JOB_TYPE=%s", job_type_to_string(t),
+ LOG_UNIT_ID(u),
+ LOG_UNIT_INVOCATION_ID(u),
+ mid);
+}
+
+static void job_emit_begin_status_message(Unit *u, uint32_t job_id, JobType t) {
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
+
+ job_log_begin_status_message(u, job_id, t);
+ job_print_begin_status_message(u, t);
+}
+
static int job_perform_on_unit(Job **j) {
uint32_t id;
Manager *m;
@@ -547,11 +647,12 @@ static int job_perform_on_unit(Job **j) {
assert_not_reached("Invalid job type");
}
- /* Log if the job still exists and the start/stop/reload function
- * actually did something. */
+ /* Log if the job still exists and the start/stop/reload function actually did something. Note that this means
+ * for units for which there's no 'activating' phase (i.e. because we transition directly from 'inactive' to
+ * 'active') we'll possibly skip the "Starting..." message. */
*j = manager_get_job(m, id);
if (*j && r > 0)
- unit_status_emit_starting_stopping_reloading(u, t);
+ job_emit_begin_status_message(u, id, t);
return r;
}
@@ -580,7 +681,9 @@ int job_run_and_invalidate(Job *j) {
switch (j->type) {
case JOB_VERIFY_ACTIVE: {
- UnitActiveState t = unit_active_state(j->unit);
+ UnitActiveState t;
+
+ t = unit_active_state(j->unit);
if (UNIT_IS_ACTIVE_OR_RELOADING(t))
r = -EALREADY;
else if (t == UNIT_ACTIVATING)
@@ -595,8 +698,7 @@ int job_run_and_invalidate(Job *j) {
case JOB_RESTART:
r = job_perform_on_unit(&j);
- /* If the unit type does not support starting/stopping,
- * then simply wait. */
+ /* If the unit type does not support starting/stopping, then simply wait. */
if (r == -EBADR)
r = 0;
break;
@@ -614,8 +716,12 @@ int job_run_and_invalidate(Job *j) {
}
if (j) {
- if (r == -EALREADY)
+ if (r == -EAGAIN)
+ job_set_state(j, JOB_WAITING); /* Hmm, not ready after all, let's return to JOB_WAITING state */
+ else if (r == -EALREADY) /* already being executed */
r = job_finish_and_invalidate(j, JOB_DONE, true, true);
+ else if (r == -ECOMM) /* condition failed, but all is good */
+ r = job_finish_and_invalidate(j, JOB_DONE, true, false);
else if (r == -EBADR)
r = job_finish_and_invalidate(j, JOB_SKIPPED, true, false);
else if (r == -ENOEXEC)
@@ -628,8 +734,6 @@ int job_run_and_invalidate(Job *j) {
r = job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false);
else if (r == -ESTALE)
r = job_finish_and_invalidate(j, JOB_ONCE, true, false);
- else if (r == -EAGAIN)
- job_set_state(j, JOB_WAITING);
else if (r < 0)
r = job_finish_and_invalidate(j, JOB_FAILED, true, false);
}
@@ -637,7 +741,7 @@ int job_run_and_invalidate(Job *j) {
return r;
}
-_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
+_pure_ static const char *job_get_done_status_message_format(Unit *u, JobType t, JobResult result) {
static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = {
[JOB_DONE] = "Started %s.",
@@ -666,7 +770,6 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
[JOB_SKIPPED] = "%s is not active.",
};
- const UnitStatusMessageFormats *format_table;
const char *format;
assert(u);
@@ -674,13 +777,11 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
assert(t < _JOB_TYPE_MAX);
if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) {
- format_table = &UNIT_VTABLE(u)->status_message_formats;
- if (format_table) {
- format = t == JOB_START ? format_table->finished_start_job[result] :
- format_table->finished_stop_job[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];
+ if (format)
+ return format;
}
/* Return generic strings */
@@ -698,7 +799,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
static const struct {
const char *color, *word;
-} job_print_status_messages [_JOB_RESULT_MAX] = {
+} job_print_done_status_messages[_JOB_RESULT_MAX] = {
[JOB_DONE] = { ANSI_OK_COLOR, " OK " },
[JOB_TIMEOUT] = { ANSI_HIGHLIGHT_RED, " TIME " },
[JOB_FAILED] = { ANSI_HIGHLIGHT_RED, "FAILED" },
@@ -710,7 +811,7 @@ static const struct {
[JOB_ONCE] = { ANSI_HIGHLIGHT_RED, " ONCE " },
};
-static void job_print_status_message(Unit *u, JobType t, JobResult result) {
+static void job_print_done_status_message(Unit *u, JobType t, JobResult result) {
const char *format;
const char *status;
@@ -722,19 +823,23 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
if (t == JOB_RELOAD)
return;
- if (!job_print_status_messages[result].word)
+ /* No message if the job did not actually do anything due to failed condition. */
+ if (t == JOB_START && result == JOB_DONE && !u->condition_result)
return;
- format = job_get_status_message_format(u, t, result);
+ if (!job_print_done_status_messages[result].word)
+ return;
+
+ format = job_get_done_status_message_format(u, t, result);
if (!format)
return;
if (log_get_show_color())
- status = strjoina(job_print_status_messages[result].color,
- job_print_status_messages[result].word,
+ status = strjoina(job_print_done_status_messages[result].color,
+ job_print_done_status_messages[result].word,
ANSI_NORMAL);
else
- status = job_print_status_messages[result].word;
+ status = job_print_done_status_messages[result].word;
if (result != JOB_DONE)
manager_flip_auto_status(u->manager, true);
@@ -751,7 +856,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
}
}
-static void job_log_status_message(Unit *u, JobType t, JobResult result) {
+static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, JobResult result) {
const char *format, *mid;
char buf[LINE_MAX];
static const int job_result_log_level[_JOB_RESULT_MAX] = {
@@ -774,10 +879,24 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
/* Skip printing if output goes to the console, and job_print_status_message()
will actually print something to the console. */
- if (log_on_console() && job_print_status_messages[result].word)
+ if (log_on_console() && job_print_done_status_messages[result].word)
+ return;
+
+ /* Show condition check message if the job did not actually do anything due to failed condition. */
+ if (t == JOB_START && result == JOB_DONE && !u->condition_result) {
+ log_struct(LOG_INFO,
+ "MESSAGE=Condition check resulted in %s being skipped.", unit_description(u),
+ "JOB_ID=%" PRIu32, job_id,
+ "JOB_TYPE=%s", job_type_to_string(t),
+ "JOB_RESULT=%s", job_result_to_string(result),
+ LOG_UNIT_ID(u),
+ LOG_UNIT_INVOCATION_ID(u),
+ "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTED_STR);
+
return;
+ }
- format = job_get_status_message_format(u, t, result);
+ format = job_get_done_status_message_format(u, t, result);
if (!format)
return;
@@ -810,6 +929,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
default:
log_struct(job_result_log_level[result],
LOG_MESSAGE("%s", buf),
+ "JOB_ID=%" PRIu32, job_id,
"JOB_TYPE=%s", job_type_to_string(t),
"JOB_RESULT=%s", job_result_to_string(result),
LOG_UNIT_ID(u),
@@ -819,6 +939,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
log_struct(job_result_log_level[result],
LOG_MESSAGE("%s", buf),
+ "JOB_ID=%" PRIu32, job_id,
"JOB_TYPE=%s", job_type_to_string(t),
"JOB_RESULT=%s", job_result_to_string(result),
LOG_UNIT_ID(u),
@@ -826,15 +947,11 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
mid);
}
-static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
+static void job_emit_done_status_message(Unit *u, uint32_t job_id, JobType t, JobResult result) {
assert(u);
- /* No message if the job did not actually do anything due to failed condition. */
- if (t == JOB_START && result == JOB_DONE && !u->condition_result)
- return;
-
- job_log_status_message(u, t, result);
- job_print_status_message(u, t, result);
+ job_log_done_status_message(u, job_id, t, result);
+ job_print_done_status_message(u, t, result);
}
static void job_fail_dependencies(Unit *u, UnitDependency d) {
@@ -856,19 +973,6 @@ static void job_fail_dependencies(Unit *u, UnitDependency d) {
}
}
-static int job_save_pending_finished_job(Job *j) {
- int r;
-
- assert(j);
-
- r = set_ensure_allocated(&j->manager->pending_finished_jobs, NULL);
- if (r < 0)
- return r;
-
- job_unlink(j);
- return set_put(j->manager->pending_finished_jobs, j);
-}
-
int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) {
Unit *u;
Unit *other;
@@ -885,11 +989,11 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
j->result = result;
- log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
+ log_unit_debug(u, "Job %" PRIu32 " %s/%s finished, result=%s", j->id, u->id, job_type_to_string(t), job_result_to_string(result));
/* If this job did nothing to respective unit we don't log the status message */
if (!already)
- job_emit_status_message(u, t, result);
+ job_emit_done_status_message(u, j->id, t, result);
/* Patch restart jobs so that they become normal start jobs */
if (result == JOB_DONE && t == JOB_RESTART) {
@@ -908,11 +1012,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
j->manager->n_failed_jobs++;
job_uninstall(j);
- /* Keep jobs started before the reload to send singal later, free all others */
- if (!MANAGER_IS_RELOADING(j->manager) ||
- !j->reloaded ||
- job_save_pending_finished_job(j) < 0)
- job_free(j);
+ job_free(j);
/* Fail depending jobs on failure */
if (result != JOB_DONE && recursive) {
@@ -973,7 +1073,9 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user
u = j->unit;
job_finish_and_invalidate(j, JOB_TIMEOUT, true, false);
- emergency_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg, "job timed out");
+ emergency_action(u->manager, u->job_timeout_action,
+ EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN,
+ u->job_timeout_reboot_arg, -1, "job timed out");
return 0;
}
@@ -1028,14 +1130,19 @@ int job_start_timer(Job *j, bool job_running) {
}
void job_add_to_run_queue(Job *j) {
+ int r;
+
assert(j);
assert(j->installed);
if (j->in_run_queue)
return;
- if (!j->manager->run_queue)
- sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT);
+ if (!j->manager->run_queue) {
+ r = sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enable job run queue event source, ignoring: %m");
+ }
LIST_PREPEND(run_queue, j->manager->run_queue, j);
j->in_run_queue = true;
@@ -1071,17 +1178,17 @@ int job_serialize(Job *j, FILE *f) {
assert(j);
assert(f);
- fprintf(f, "job-id=%u\n", j->id);
- fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
- fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
- fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
- fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
- fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
+ (void) serialize_item_format(f, "job-id", "%u", j->id);
+ (void) serialize_item(f, "job-type", job_type_to_string(j->type));
+ (void) serialize_item(f, "job-state", job_state_to_string(j->state));
+ (void) serialize_bool(f, "job-irreversible", j->irreversible);
+ (void) serialize_bool(f, "job-sent-dbus-new-signal", j->sent_dbus_new_signal);
+ (void) serialize_bool(f, "job-ignore-order", j->ignore_order);
if (j->begin_usec > 0)
- fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
+ (void) serialize_usec(f, "job-begin", j->begin_usec);
if (j->begin_running_usec > 0)
- fprintf(f, "job-begin-running="USEC_FMT"\n", j->begin_running_usec);
+ (void) serialize_usec(f, "job-begin-running", j->begin_running_usec);
bus_track_serialize(j->bus_track, f, "subscribed");
@@ -1091,24 +1198,26 @@ int job_serialize(Job *j, FILE *f) {
}
int job_deserialize(Job *j, FILE *f) {
+ int r;
+
assert(j);
assert(f);
for (;;) {
- char line[LINE_MAX], *l, *v;
+ _cleanup_free_ char *line = NULL;
+ char *l, *v;
size_t k;
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- return 0;
- return -errno;
- }
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read serialization line: %m");
+ if (r == 0)
+ return 0;
- char_array_0(line);
l = strstrip(line);
/* End marker */
- if (l[0] == 0)
+ if (isempty(l))
return 0;
k = strcspn(l, "=");
@@ -1122,16 +1231,16 @@ int job_deserialize(Job *j, FILE *f) {
if (streq(l, "job-id")) {
if (safe_atou32(v, &j->id) < 0)
- log_debug("Failed to parse job id value %s", v);
+ log_debug("Failed to parse job id value: %s", v);
} else if (streq(l, "job-type")) {
JobType t;
t = job_type_from_string(v);
if (t < 0)
- log_debug("Failed to parse job type %s", v);
+ log_debug("Failed to parse job type: %s", v);
else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION)
- log_debug("Cannot deserialize job of type %s", v);
+ log_debug("Cannot deserialize job of type: %s", v);
else
j->type = t;
@@ -1140,7 +1249,7 @@ int job_deserialize(Job *j, FILE *f) {
s = job_state_from_string(v);
if (s < 0)
- log_debug("Failed to parse job state %s", v);
+ log_debug("Failed to parse job state: %s", v);
else
job_set_state(j, s);
@@ -1149,7 +1258,7 @@ int job_deserialize(Job *j, FILE *f) {
b = parse_boolean(v);
if (b < 0)
- log_debug("Failed to parse job irreversible flag %s", v);
+ log_debug("Failed to parse job irreversible flag: %s", v);
else
j->irreversible = j->irreversible || b;
@@ -1158,7 +1267,7 @@ int job_deserialize(Job *j, FILE *f) {
b = parse_boolean(v);
if (b < 0)
- log_debug("Failed to parse job sent_dbus_new_signal flag %s", v);
+ log_debug("Failed to parse job sent_dbus_new_signal flag: %s", v);
else
j->sent_dbus_new_signal = j->sent_dbus_new_signal || b;
@@ -1167,31 +1276,21 @@ int job_deserialize(Job *j, FILE *f) {
b = parse_boolean(v);
if (b < 0)
- log_debug("Failed to parse job ignore_order flag %s", v);
+ log_debug("Failed to parse job ignore_order flag: %s", v);
else
j->ignore_order = j->ignore_order || b;
- } else if (streq(l, "job-begin")) {
- unsigned long long ull;
-
- if (sscanf(v, "%llu", &ull) != 1)
- log_debug("Failed to parse job-begin value %s", v);
- else
- j->begin_usec = ull;
-
- } else if (streq(l, "job-begin-running")) {
- unsigned long long ull;
-
- if (sscanf(v, "%llu", &ull) != 1)
- log_debug("Failed to parse job-begin-running value %s", v);
- else
- j->begin_running_usec = ull;
+ } else if (streq(l, "job-begin"))
+ (void) deserialize_usec(v, &j->begin_usec);
- } else if (streq(l, "subscribed")) {
+ else if (streq(l, "job-begin-running"))
+ (void) deserialize_usec(v, &j->begin_running_usec);
+ else if (streq(l, "subscribed")) {
if (strv_extend(&j->deserialized_clients, v) < 0)
- log_oom();
- }
+ return log_oom();
+ } else
+ log_debug("Unknown job serialization key: %s", l);
}
}
@@ -1366,7 +1465,6 @@ bool job_may_gc(Job *j) {
* we start + other stop → gc
* we stop + other start → stay
* we stop + other stop → stay
- *
*/
return true;
@@ -1385,15 +1483,8 @@ void job_add_to_gc_queue(Job *j) {
j->in_gc_queue = true;
}
-static int job_compare(const void *a, const void *b) {
- Job *x = *(Job**) a, *y = *(Job**) b;
-
- if (x->id < y->id)
- return -1;
- if (x->id > y->id)
- return 1;
-
- return 0;
+static int job_compare(Job * const *a, Job * const *b) {
+ return CMP((*a)->id, (*b)->id);
}
static size_t sort_job_list(Job **list, size_t n) {
@@ -1401,7 +1492,7 @@ static size_t sort_job_list(Job **list, size_t n) {
size_t a, b;
/* Order by numeric IDs */
- qsort_safe(list, n, sizeof(Job*), job_compare);
+ typesafe_qsort(list, n, job_compare);
/* Filter out duplicates */
for (a = 0, b = 0; a < n; a++) {
diff --git a/src/core/job.h b/src/core/job.h
index 2f5f3f3989..1b9bcdd895 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -80,7 +80,7 @@ enum JobMode {
};
enum JobResult {
- JOB_DONE, /* Job completed successfully */
+ JOB_DONE, /* Job completed successfully (or skipped due to a failed ConditionXYZ=) */
JOB_CANCELED, /* Job canceled by a conflicting job installation or by explicit cancel request */
JOB_TIMEOUT, /* Job timeout elapsed */
JOB_FAILED, /* Job failed */
@@ -156,17 +156,16 @@ struct Job {
bool irreversible:1;
bool in_gc_queue:1;
bool ref_by_private_bus:1;
- bool reloaded:1;
};
Job* job_new(Unit *unit, JobType type);
Job* job_new_raw(Unit *unit);
void job_unlink(Job *job);
-void job_free(Job *job);
+Job* job_free(Job *job);
Job* job_install(Job *j);
int job_install_deserialized(Job *j);
void job_uninstall(Job *j);
-void job_dump(Job *j, FILE*f, const char *prefix);
+void job_dump(Job *j, FILE *f, const char *prefix);
int job_serialize(Job *j, FILE *f);
int job_deserialize(Job *j, FILE *f);
int job_coldplug(Job *j);
@@ -223,6 +222,8 @@ void job_add_to_gc_queue(Job *j);
int job_get_before(Job *j, Job*** ret);
int job_get_after(Job *j, Job*** ret);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Job*, job_free);
+
const char* job_type_to_string(JobType t) _const_;
JobType job_type_from_string(const char *s) _pure_;
diff --git a/src/core/kill.c b/src/core/kill.c
index 929eebfe37..6fe96cfc07 100644
--- a/src/core/kill.c
+++ b/src/core/kill.c
@@ -9,8 +9,10 @@ void kill_context_init(KillContext *c) {
assert(c);
c->kill_signal = SIGTERM;
+ c->final_kill_signal = SIGKILL;
c->send_sigkill = true;
c->send_sighup = false;
+ c->watchdog_signal = SIGABRT;
}
void kill_context_dump(KillContext *c, FILE *f, const char *prefix) {
@@ -21,10 +23,12 @@ void kill_context_dump(KillContext *c, FILE *f, const char *prefix) {
fprintf(f,
"%sKillMode: %s\n"
"%sKillSignal: SIG%s\n"
+ "%sFinalKillSignal: SIG%s\n"
"%sSendSIGKILL: %s\n"
"%sSendSIGHUP: %s\n",
prefix, kill_mode_to_string(c->kill_mode),
prefix, signal_to_string(c->kill_signal),
+ prefix, signal_to_string(c->final_kill_signal),
prefix, yes_no(c->send_sigkill),
prefix, yes_no(c->send_sighup));
}
diff --git a/src/core/kill.h b/src/core/kill.h
index 2d6aa943a6..f3915be1dc 100644
--- a/src/core/kill.h
+++ b/src/core/kill.h
@@ -21,8 +21,10 @@ typedef enum KillMode {
struct KillContext {
KillMode kill_mode;
int kill_signal;
+ int final_kill_signal;
bool send_sigkill;
bool send_sighup;
+ int watchdog_signal;
};
typedef enum KillWho {
diff --git a/src/core/killall.c b/src/core/killall.c
index 87d207fd3d..f0ce996556 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -23,16 +23,20 @@
static bool ignore_proc(pid_t pid, bool warn_rootfs) {
_cleanup_fclose_ FILE *f = NULL;
- char c;
const char *p;
- size_t count;
+ char c = 0;
uid_t uid;
int r;
/* We are PID 1, let's not commit suicide */
- if (pid == 1)
+ if (pid <= 1)
return true;
+ /* Ignore kernel threads */
+ r = is_kernel_thread(pid);
+ if (r != 0)
+ return true; /* also ignore processes where we can't determine this */
+
r = get_process_uid(pid, &uid);
if (r < 0)
return true; /* not really, but better safe than sorry */
@@ -46,11 +50,10 @@ static bool ignore_proc(pid_t pid, bool warn_rootfs) {
if (!f)
return true; /* not really, but has the desired effect */
- count = fread(&c, 1, 1, f);
-
- /* Kernel threads have an empty cmdline */
- if (count <= 0)
- return true;
+ /* Try to read the first character of the command line. If the cmdline is empty (which might be the case for
+ * kernel threads but potentially also other stuff), this line won't do anything, but we don't care much, as
+ * actual kernel threads are already filtered out above. */
+ (void) fread(&c, 1, 1, f);
/* Processes with argv[0][0] = '@' we ignore from the killing spree.
*
@@ -63,7 +66,7 @@ static bool ignore_proc(pid_t pid, bool warn_rootfs) {
_cleanup_free_ char *comm = NULL;
- get_process_comm(pid, &comm);
+ (void) get_process_comm(pid, &comm);
log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is "
"running from the root file system, and thus likely to block re-mounting of the "
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
index 9251929558..a91cfebc67 100644
--- a/src/core/kmod-setup.c
+++ b/src/core/kmod-setup.c
@@ -76,13 +76,15 @@ int kmod_setup(void) {
bool warn_if_module:1;
bool (*condition_fn)(void);
} kmod_table[] = {
- /* auto-loading on use doesn't work before udev is up */
+ /* This one we need to load explicitly, since auto-loading on use doesn't work
+ * before udev created the ghost device nodes, and we need it earlier than that. */
{ "autofs4", "/sys/class/misc/autofs", true, false, NULL },
- /* early configure of ::1 on the loopback device */
+ /* This one we need to load explicitly, since auto-loading of IPv6 is not done when
+ * we try to configure ::1 on the loopback device. */
{ "ipv6", "/sys/module/ipv6", false, true, NULL },
- /* this should never be a module */
+ /* This should never be a module */
{ "unix", "/proc/net/unix", true, true, NULL },
#if HAVE_LIBIPTC
@@ -93,15 +95,12 @@ int kmod_setup(void) {
{ "virtio_rng", NULL, false, false, has_virtio_rng },
};
_cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
- unsigned int i;
- int r;
+ unsigned i;
if (have_effective_cap(CAP_SYS_MODULE) == 0)
return 0;
for (i = 0; i < ELEMENTSOF(kmod_table); i++) {
- _cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
-
if (kmod_table[i].path && access(kmod_table[i].path, F_OK) >= 0)
continue;
@@ -122,23 +121,7 @@ int kmod_setup(void) {
kmod_load_resources(ctx);
}
- r = kmod_module_new_from_name(ctx, kmod_table[i].module, &mod);
- if (r < 0) {
- log_error("Failed to lookup module '%s'", kmod_table[i].module);
- continue;
- }
-
- r = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL);
- if (r == 0)
- log_debug("Inserted module '%s'", kmod_module_get_name(mod));
- else if (r == KMOD_PROBE_APPLY_BLACKLIST)
- log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
- else {
- bool print_warning = kmod_table[i].warn_if_unavailable || (r < 0 && r != -ENOENT);
-
- log_full_errno(print_warning ? LOG_WARNING : LOG_DEBUG, r,
- "Failed to insert module '%s': %m", kmod_module_get_name(mod));
- }
+ (void) module_load_and_warn(ctx, kmod_table[i].module, kmod_table[i].warn_if_unavailable);
}
#endif
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
index 4b422cc54e..a50b200f5b 100644
--- a/src/core/load-dropin.c
+++ b/src/core/load-dropin.c
@@ -92,7 +92,7 @@ static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suff
log_unit_warning(u, "%s dependency dropin %s target %s has different name",
unit_dependency_to_string(dependency), *p, target);
- r = unit_add_dependency_by_name(u, dependency, entry, *p, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_dependency_by_name(u, dependency, entry, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
log_unit_warning_errno(u, r, "Cannot add %s dependency on %s, ignoring: %m",
unit_dependency_to_string(dependency), entry);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 15fb47838c..cdbc67f885 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -57,6 +57,8 @@ $1.SyslogFacility, config_parse_log_facility, 0,
$1.SyslogLevel, config_parse_log_level, 0, offsetof($1, exec_context.syslog_priority)
$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix)
$1.LogLevelMax, config_parse_log_level, 0, offsetof($1, exec_context.log_level_max)
+$1.LogRateLimitIntervalSec, config_parse_sec, 0, offsetof($1, exec_context.log_rate_limit_interval_usec)
+$1.LogRateLimitBurst, config_parse_unsigned, 0, offsetof($1, exec_context.log_rate_limit_burst)
$1.LogExtraFields, config_parse_log_extra_fields, 0, offsetof($1, exec_context)
$1.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof($1, exec_context)
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context.secure_bits)
@@ -151,7 +153,9 @@ m4_define(`KILL_CONTEXT_CONFIG_ITEMS',
`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill)
$1.SendSIGHUP, config_parse_bool, 0, offsetof($1, kill_context.send_sighup)
$1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode)
-$1.KillSignal, config_parse_signal, 0, offsetof($1, kill_context.kill_signal)'
+$1.KillSignal, config_parse_signal, 0, offsetof($1, kill_context.kill_signal)
+$1.FinalKillSignal, config_parse_signal, 0, offsetof($1, kill_context.final_kill_signal)
+$1.WatchdogSignal, config_parse_signal, 0, offsetof($1, kill_context.watchdog_signal)'
)m4_dnl
m4_define(`CGROUP_CONTEXT_CONFIG_ITEMS',
`$1.Slice, config_parse_unit_slice, 0, 0
@@ -162,6 +166,7 @@ $1.CPUShares, config_parse_cpu_shares, 0,
$1.StartupCPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context.startup_cpu_shares)
$1.CPUQuota, config_parse_cpu_quota, 0, offsetof($1, cgroup_context)
$1.MemoryAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.memory_accounting)
+$1.MemoryMin, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.MemoryMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
@@ -177,6 +182,7 @@ $1.IOReadBandwidthMax, config_parse_io_limit, 0,
$1.IOWriteBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context)
$1.IOReadIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context)
$1.IOWriteIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context)
+$1.IODeviceLatencyTargetSec, config_parse_io_device_latency, 0, offsetof($1, cgroup_context)
$1.BlockIOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.blockio_accounting)
$1.BlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.blockio_weight)
$1.StartupBlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.startup_blockio_weight)
@@ -186,6 +192,7 @@ $1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0,
$1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting)
$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context.tasks_max)
$1.Delegate, config_parse_delegate, 0, offsetof($1, cgroup_context)
+$1.DisableControllers, config_parse_disable_controllers, 0, offsetof($1, cgroup_context)
$1.IPAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.ip_accounting)
$1.IPAddressAllow, config_parse_ip_address_access, 0, offsetof($1, cgroup_context.ip_address_allow)
$1.IPAddressDeny, config_parse_ip_address_access, 0, offsetof($1, cgroup_context.ip_address_deny)
@@ -233,6 +240,8 @@ Unit.StartLimitBurst, config_parse_unsigned, 0,
Unit.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action)
Unit.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_action)
Unit.SuccessAction, config_parse_emergency_action, 0, offsetof(Unit, success_action)
+Unit.FailureActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, failure_action_exit_status)
+Unit.SuccessActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, success_action_exit_status)
Unit.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions)
Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions)
@@ -282,7 +291,7 @@ Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDI
Unit.AssertNull, config_parse_unit_condition_null, 0, offsetof(Unit, asserts)
Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode)
m4_dnl
-Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file)
+Service.PIDFile, config_parse_pid_file, 0, offsetof(Service, pid_file)
Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command)
Service.ExecStart, config_parse_exec, SERVICE_EXEC_START, offsetof(Service, exec_command)
Service.ExecStartPost, config_parse_exec, SERVICE_EXEC_START_POST, offsetof(Service, exec_command)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index d9a5094aa0..4ebe92fd45 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -34,21 +34,20 @@
#include "hexdecoct.h"
#include "io-util.h"
#include "ioprio.h"
+#include "ip-protocol-list.h"
#include "journal-util.h"
#include "load-fragment.h"
#include "log.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
-#include "securebits.h"
#include "securebits-util.h"
#include "signal-util.h"
-#include "socket-protocol-list.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
@@ -57,26 +56,22 @@
#include "user-util.h"
#include "web-util.h"
-static int supported_socket_protocol_from_string(const char *s) {
+static int parse_socket_protocol(const char *s) {
int r;
- if (isempty(s))
- return IPPROTO_IP;
-
- r = socket_protocol_from_name(s);
+ r = parse_ip_protocol(s);
if (r < 0)
- return -EINVAL;
+ return r;
if (!IN_SET(r, IPPROTO_UDPLITE, IPPROTO_SCTP))
return -EPROTONOSUPPORT;
return r;
}
-DEFINE_CONFIG_PARSE(config_parse_socket_protocol, supported_socket_protocol_from_string, "Failed to parse socket protocol");
+DEFINE_CONFIG_PARSE(config_parse_socket_protocol, parse_socket_protocol, "Failed to parse socket protocol");
DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits");
DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
@@ -135,7 +130,7 @@ int config_parse_unit_deps(
continue;
}
- r = unit_add_dependency_by_name(u, d, k, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_dependency_by_name(u, d, k, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
}
@@ -1015,6 +1010,17 @@ int config_parse_exec_output(
eo = EXEC_OUTPUT_FILE;
+ } else if ((n = startswith(rvalue, "append:"))) {
+
+ r = unit_full_printf(u, n, &resolved);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n);
+
+ r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
+ if (r < 0)
+ return -ENOEXEC;
+
+ eo = EXEC_OUTPUT_FILE_APPEND;
} else {
eo = exec_output_from_string(rvalue);
if (eo < 0) {
@@ -1557,7 +1563,7 @@ int config_parse_trigger_unit(
return 0;
}
- r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, true, UNIT_DEPENDENCY_FILE);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
return 0;
@@ -1755,11 +1761,11 @@ int config_parse_service_sockets(
continue;
}
- r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
- r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
}
@@ -2861,8 +2867,8 @@ int config_parse_address_families(
}
af = af_from_name(word);
- if (af <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0,
+ if (af < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, af,
"Failed to parse address family, ignoring: %s", word);
continue;
}
@@ -3002,13 +3008,13 @@ int config_parse_cpu_quota(
return 0;
}
- r = parse_percent_unbounded(rvalue);
+ r = parse_permille_unbounded(rvalue);
if (r <= 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Invalid CPU quota '%s', ignoring.", rvalue);
return 0;
}
- c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
+ c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 1000U;
return 0;
}
@@ -3030,7 +3036,7 @@ int config_parse_memory_limit(
if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
- r = parse_percent(rvalue);
+ r = parse_permille(rvalue);
if (r < 0) {
r = parse_size(rvalue, 1024, &bytes);
if (r < 0) {
@@ -3038,7 +3044,7 @@ int config_parse_memory_limit(
return 0;
}
} else
- bytes = physical_memory_scale(r, 100U);
+ bytes = physical_memory_scale(r, 1000U);
if (bytes >= UINT64_MAX ||
(bytes <= 0 && !streq(lvalue, "MemorySwapMax"))) {
@@ -3047,7 +3053,9 @@ int config_parse_memory_limit(
}
}
- if (streq(lvalue, "MemoryLow"))
+ if (streq(lvalue, "MemoryMin"))
+ c->memory_min = bytes;
+ else if (streq(lvalue, "MemoryLow"))
c->memory_low = bytes;
else if (streq(lvalue, "MemoryHigh"))
c->memory_high = bytes;
@@ -3080,7 +3088,7 @@ int config_parse_tasks_max(
int r;
if (isempty(rvalue)) {
- *tasks_max = u->manager->default_tasks_max;
+ *tasks_max = u ? u->manager->default_tasks_max : UINT64_MAX;
return 0;
}
@@ -3089,7 +3097,7 @@ int config_parse_tasks_max(
return 0;
}
- r = parse_percent(rvalue);
+ r = parse_permille(rvalue);
if (r < 0) {
r = safe_atou64(rvalue, &v);
if (r < 0) {
@@ -3097,7 +3105,7 @@ int config_parse_tasks_max(
return 0;
}
} else
- v = system_tasks_max_scale(r, 100U);
+ v = system_tasks_max_scale(r, 1000U);
if (v <= 0 || v >= UINT64_MAX) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
@@ -3199,7 +3207,6 @@ int config_parse_device_allow(
_cleanup_free_ char *path = NULL, *resolved = NULL;
CGroupContext *c = data;
- CGroupDeviceAllow *a;
const char *p = rvalue;
int r;
@@ -3231,7 +3238,7 @@ int config_parse_device_allow(
return 0;
}
- if (!startswith(resolved, "block-") && !startswith(resolved, "char-")) {
+ if (!STARTSWITH_SET(resolved, "block-", "char-")) {
r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
if (r < 0)
@@ -3248,17 +3255,7 @@ int config_parse_device_allow(
return 0;
}
- a = new0(CGroupDeviceAllow, 1);
- if (!a)
- return log_oom();
-
- a->path = TAKE_PTR(resolved);
- a->r = isempty(p) || !!strchr(p, 'r');
- a->w = isempty(p) || !!strchr(p, 'w');
- a->m = isempty(p) || !!strchr(p, 'm');
-
- LIST_PREPEND(device_allow, c->device_allow, a);
- return 0;
+ return cgroup_add_device_allow(c, resolved, p);
}
int config_parse_io_device_weight(
@@ -3335,6 +3332,77 @@ int config_parse_io_device_weight(
return 0;
}
+int config_parse_io_device_latency(
+ 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 *path = NULL, *resolved = NULL;
+ CGroupIODeviceLatency *l;
+ CGroupContext *c = data;
+ const char *p = rvalue;
+ usec_t usec;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ while (c->io_device_latencies)
+ cgroup_context_free_io_device_latency(c, c->io_device_latencies);
+
+ return 0;
+ }
+
+ r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r == 0 || isempty(p)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Failed to extract device path and latency from '%s', ignoring.", rvalue);
+ return 0;
+ }
+
+ r = unit_full_printf(userdata, path, &resolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
+ return 0;
+ }
+
+ r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
+ if (r < 0)
+ return 0;
+
+ if (parse_sec(p, &usec) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", p);
+ return 0;
+ }
+
+ l = new0(CGroupIODeviceLatency, 1);
+ if (!l)
+ return log_oom();
+
+ l->path = TAKE_PTR(resolved);
+ l->target_usec = usec;
+
+ LIST_PREPEND(device_latencies, c->io_device_latencies, l);
+ return 0;
+}
+
int config_parse_io_limit(
const char *unit,
const char *filename,
@@ -3904,13 +3972,9 @@ int config_parse_temporary_filesystems(
if (r < 0)
continue;
- r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, path, w);
- if (r == -ENOMEM)
+ r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, resolved, w);
+ if (r < 0)
return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse mount options, ignoring: %s", word);
- continue;
- }
}
}
@@ -4116,6 +4180,183 @@ int config_parse_job_running_timeout_sec(
return 0;
}
+int config_parse_emergency_action(
+ 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) {
+
+ Manager *m = NULL;
+ EmergencyAction *x = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (unit)
+ m = ((Unit*) userdata)->manager;
+ else
+ m = data;
+
+ r = parse_emergency_action(rvalue, MANAGER_IS_SYSTEM(m), x);
+ if (r < 0) {
+ if (r == -EOPNOTSUPP && MANAGER_IS_USER(m)) {
+ /* Compat mode: remove for systemd 241. */
+
+ log_syntax(unit, LOG_INFO, filename, line, r,
+ "%s= in user mode specified as \"%s\", using \"exit-force\" instead.",
+ lvalue, rvalue);
+ *x = EMERGENCY_ACTION_EXIT_FORCE;
+ return 0;
+ }
+
+ if (r == -EOPNOTSUPP)
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "%s= specified as %s mode action, ignoring: %s",
+ lvalue, MANAGER_IS_SYSTEM(m) ? "user" : "system", rvalue);
+ else
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_pid_file(
+ 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, *n = NULL;
+ Unit *u = userdata;
+ char **s = data;
+ const char *e;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(u);
+
+ 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 this is a relative path make it absolute by prefixing the /run */
+ n = path_make_absolute(k, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
+ if (!n)
+ return log_oom();
+
+ /* Check that the result is a sensible path */
+ r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ return r;
+
+ e = path_startswith(n, "/var/run/");
+ if (e) {
+ char *z;
+
+ z = strjoin("/run/", e);
+ if (!z)
+ return log_oom();
+
+ log_syntax(unit, LOG_NOTICE, filename, line, 0, "PIDFile= references path below legacy directory /var/run/, updating %s → %s; please update the unit file accordingly.", n, z);
+
+ free_and_replace(*s, z);
+ } else
+ free_and_replace(*s, n);
+
+ return 0;
+}
+
+int config_parse_exit_status(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int *exit_status = data, r;
+ uint8_t u;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(exit_status);
+
+ if (isempty(rvalue)) {
+ *exit_status = -1;
+ return 0;
+ }
+
+ r = safe_atou8(rvalue, &u);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse exit status '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ *exit_status = u;
+ return 0;
+}
+
+int config_parse_disable_controllers(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int r;
+ CGroupContext *c = data;
+ CGroupMask disabled_mask;
+
+ /* 1. If empty, make all controllers eligible for use again.
+ * 2. If non-empty, merge all listed controllers, space separated. */
+
+ if (isempty(rvalue)) {
+ c->disable_controllers = 0;
+ return 0;
+ }
+
+ r = cg_mask_from_string(rvalue, &disabled_mask);
+ if (r < 0 || disabled_mask <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid cgroup string: %s, ignoring", rvalue);
+ return 0;
+ }
+
+ c->disable_controllers |= disabled_mask;
+
+ return 0;
+}
+
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
@@ -4174,7 +4415,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
free_and_replace(*filename, target);
}
- f = fdopen(fd, "re");
+ f = fdopen(fd, "r");
if (!f) {
safe_close(fd);
return -errno;
@@ -4290,7 +4531,6 @@ static int load_from_path(Unit *u, const char *path) {
r = open_follow(&filename, &f, symlink_names, &id);
if (r >= 0)
break;
- filename = mfree(filename);
/* ENOENT means that the file is missing or is a dangling symlink.
* ENOTDIR means that one of paths we expect to be is a directory
@@ -4302,6 +4542,7 @@ static int load_from_path(Unit *u, const char *path) {
else if (!IN_SET(r, -ENOENT, -ENOTDIR))
return r;
+ filename = mfree(filename);
/* Empty the symlink names for the next run */
set_clear_free(symlink_names);
}
@@ -4524,6 +4765,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_device_policy, "POLICY" },
{ config_parse_io_limit, "LIMIT" },
{ config_parse_io_device_weight, "DEVICEWEIGHT" },
+ { config_parse_io_device_latency, "DEVICELATENCY" },
{ config_parse_blockio_bandwidth, "BANDWIDTH" },
{ config_parse_blockio_weight, "WEIGHT" },
{ config_parse_blockio_device_weight, "DEVICEWEIGHT" },
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index dad281ef72..e0d3b4ec3b 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -39,6 +39,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
CONFIG_PARSER_PROTOTYPE(config_parse_kill_signal);
+CONFIG_PARSER_PROTOTYPE(config_parse_final_kill_signal);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_timer);
CONFIG_PARSER_PROTOTYPE(config_parse_trigger_unit);
@@ -68,12 +69,12 @@ CONFIG_PARSER_PROTOTYPE(config_parse_tasks_max);
CONFIG_PARSER_PROTOTYPE(config_parse_delegate);
CONFIG_PARSER_PROTOTYPE(config_parse_device_policy);
CONFIG_PARSER_PROTOTYPE(config_parse_device_allow);
+CONFIG_PARSER_PROTOTYPE(config_parse_io_device_latency);
CONFIG_PARSER_PROTOTYPE(config_parse_io_device_weight);
CONFIG_PARSER_PROTOTYPE(config_parse_io_limit);
CONFIG_PARSER_PROTOTYPE(config_parse_blockio_weight);
CONFIG_PARSER_PROTOTYPE(config_parse_blockio_device_weight);
CONFIG_PARSER_PROTOTYPE(config_parse_blockio_bandwidth);
-CONFIG_PARSER_PROTOTYPE(config_parse_netclass);
CONFIG_PARSER_PROTOTYPE(config_parse_job_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_job_mode_isolate);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_selinux_context);
@@ -102,6 +103,9 @@ 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_collect_mode);
+CONFIG_PARSER_PROTOTYPE(config_parse_pid_file);
+CONFIG_PARSER_PROTOTYPE(config_parse_exit_status);
+CONFIG_PARSER_PROTOTYPE(config_parse_disable_controllers);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
index c14523fee9..584fb220a1 100644
--- a/src/core/locale-setup.c
+++ b/src/core/locale-setup.c
@@ -4,46 +4,43 @@
#include <stdlib.h>
#include <string.h>
+#include "env-file.h"
#include "env-util.h"
-#include "fileio.h"
#include "locale-setup.h"
#include "locale-util.h"
+#include "proc-cmdline.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
#include "virt.h"
int locale_setup(char ***environment) {
- char **add;
- char *variables[_VARIABLE_LC_MAX] = {};
- int r = 0, i;
-
- if (detect_container() <= 0) {
- r = parse_env_file(NULL, "/proc/cmdline", WHITESPACE,
- "locale.LANG", &variables[VARIABLE_LANG],
- "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
- "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "locale.LC_TIME", &variables[VARIABLE_LC_TIME],
- "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "locale.LC_NAME", &variables[VARIABLE_LC_NAME],
- "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL);
-
- if (r < 0 && r != -ENOENT)
- log_warning_errno(r, "Failed to read /proc/cmdline: %m");
- }
-
- /* Hmm, nothing set on the kernel cmd line? Then let's
- * try /etc/locale.conf */
+ _cleanup_(locale_variables_freep) char *variables[_VARIABLE_LC_MAX] = {};
+ _cleanup_strv_free_ char **add = NULL;
+ LocaleVariable i;
+ int r;
+
+ r = proc_cmdline_get_key_many(PROC_CMDLINE_STRIP_RD_PREFIX,
+ "locale.LANG", &variables[VARIABLE_LANG],
+ "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
+ "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+ "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
+ "locale.LC_TIME", &variables[VARIABLE_LC_TIME],
+ "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
+ "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
+ "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
+ "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER],
+ "locale.LC_NAME", &variables[VARIABLE_LC_NAME],
+ "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
+ "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
+ "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
+ "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION]);
+ if (r < 0 && r != -ENOENT)
+ log_warning_errno(r, "Failed to read /proc/cmdline: %m");
+
+ /* Hmm, nothing set on the kernel cmd line? Then let's try /etc/locale.conf */
if (r <= 0) {
- r = parse_env_file(NULL, "/etc/locale.conf", NEWLINE,
+ r = parse_env_file(NULL, "/etc/locale.conf",
"LANG", &variables[VARIABLE_LANG],
"LANGUAGE", &variables[VARIABLE_LANGUAGE],
"LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
@@ -57,14 +54,11 @@ int locale_setup(char ***environment) {
"LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
"LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
"LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL);
-
+ "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION]);
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read /etc/locale.conf: %m");
}
- add = NULL;
for (i = 0; i < _VARIABLE_LC_MAX; i++) {
char *s;
@@ -72,36 +66,32 @@ int locale_setup(char ***environment) {
continue;
s = strjoin(locale_variable_to_string(i), "=", variables[i]);
- if (!s) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!s)
+ return -ENOMEM;
- if (strv_consume(&add, s) < 0) {
- r = -ENOMEM;
- goto finish;
- }
+ if (strv_consume(&add, s) < 0)
+ return -ENOMEM;
}
- if (!strv_isempty(add)) {
- char **e;
+ if (strv_isempty(add)) {
+ /* If no locale is configured then default to C.UTF-8. */
- e = strv_env_merge(2, *environment, add);
- if (!e) {
- r = -ENOMEM;
- goto finish;
- }
-
- strv_free_and_replace(*environment, e);
+ add = strv_new("LANG=C.UTF-8");
+ if (!add)
+ return -ENOMEM;
}
- r = 0;
+ if (strv_isempty(*environment))
+ strv_free_and_replace(*environment, add);
+ else {
+ char **merged;
-finish:
- strv_free(add);
+ merged = strv_env_merge(2, *environment, add);
+ if (!merged)
+ return -ENOMEM;
- for (i = 0; i < _VARIABLE_LC_MAX; i++)
- free(variables[i]);
+ strv_free_and_replace(*environment, merged);
+ }
- return r;
+ return 0;
}
diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c
index 835553ec8f..f613db83ce 100644
--- a/src/core/loopback-setup.c
+++ b/src/core/loopback-setup.c
@@ -53,7 +53,7 @@ static int start_loopback(sd_netlink *rtnl, struct state *s) {
if (r < 0)
return r;
- r = sd_netlink_call_async(rtnl, req, generic_handler, s, LOOPBACK_SETUP_TIMEOUT_USEC, NULL);
+ r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, LOOPBACK_SETUP_TIMEOUT_USEC, "systemd-start-loopback");
if (r < 0)
return r;
@@ -88,7 +88,7 @@ static int add_ipv4_address(sd_netlink *rtnl, struct state *s) {
if (r < 0)
return r;
- r = sd_netlink_call_async(rtnl, req, generic_handler, s, USEC_INFINITY, NULL);
+ r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, USEC_INFINITY, "systemd-loopback-ipv4");
if (r < 0)
return r;
@@ -123,7 +123,7 @@ static int add_ipv6_address(sd_netlink *rtnl, struct state *s) {
if (r < 0)
return r;
- r = sd_netlink_call_async(rtnl, req, generic_handler, s, USEC_INFINITY, NULL);
+ r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, USEC_INFINITY, "systemd-loopback-ipv6");
if (r < 0)
return r;
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 11528f83c4..aae548064e 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -15,7 +15,7 @@
#include "machine-id-setup.h"
#include "macro.h"
#include "mkdir.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "process-util.h"
#include "stat-util.h"
@@ -73,7 +73,7 @@ static int generate_machine_id(const char *root, sd_id128_t *ret) {
/* If that didn't work, generate a random machine id */
r = sd_id128_randomize(ret);
if (r < 0)
- return log_error_errno(r, "Failed to generate randomized : %m");
+ return log_error_errno(r, "Failed to generate randomized machine ID: %m");
log_info("Initializing machine ID from random generator.");
return 0;
@@ -108,8 +108,7 @@ int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {
"2) /etc/machine-id exists and is empty.\n"
"3) /etc/machine-id is missing and /etc is writable.\n");
else
- return log_error_errno(errno,
- "Cannot open %s: %m", etc_machine_id);
+ return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
}
writable = false;
@@ -201,14 +200,14 @@ int machine_id_commit(const char *root) {
r = fd_is_temporary_fs(fd);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id);
- if (r == 0) {
- log_error("%s is not on a temporary file system.", etc_machine_id);
- return -EROFS;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EROFS),
+ "%s is not on a temporary file system.",
+ etc_machine_id);
r = id128_read_fd(fd, ID128_PLAIN, &id);
if (r < 0)
- return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id);
+ return log_error_errno(r, "We didn't find a valid machine ID in %s: %m", etc_machine_id);
fd = safe_close(fd);
diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in
index f3b74f4273..9ccad5ebfe 100644
--- a/src/core/macros.systemd.in
+++ b/src/core/macros.systemd.in
@@ -2,8 +2,6 @@
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
-#
-# Copyright 2012 Lennart Poettering
# RPM macros for packages installing systemd unit files
@@ -18,7 +16,7 @@
%_sysctldir @sysctldir@
%_sysusersdir @sysusersdir@
%_tmpfilesdir @tmpfilesdir@
-%_environmnentdir @environmentdir@
+%_environmentdir @environmentdir@
%_modulesloaddir @modulesloaddir@
%_modprobedir @modprobedir@
%_systemdgeneratordir @systemgeneratordir@
@@ -26,6 +24,10 @@
%_systemd_system_env_generator_dir @systemenvgeneratordir@
%_systemd_user_env_generator_dir @userenvgeneratordir@
+# Because we had one release with a typo...
+# This is temporary (Remove after systemd 240 is released)
+%_environmnentdir %{warn:Use %%_environmentdir instead}%_environmentdir
+
%systemd_requires \
Requires(post): systemd \
Requires(preun): systemd \
diff --git a/src/core/main.c b/src/core/main.c
index 44dd8348be..839dc062ff 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -28,6 +28,7 @@
#include "bus-error.h"
#include "bus-util.h"
#include "capability-util.h"
+#include "cgroup-util.h"
#include "clock-util.h"
#include "conf-parser.h"
#include "cpu-set-util.h"
@@ -57,6 +58,7 @@
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "proc-cmdline.h"
#include "process-util.h"
#include "raw-clone.h"
@@ -73,6 +75,7 @@
#include "stdio-util.h"
#include "strv.h"
#include "switch-root.h"
+#include "sysctl-util.h"
#include "terminal-util.h"
#include "umask-util.h"
#include "user-util.h"
@@ -95,11 +98,10 @@ static int arg_crash_chvt = -1;
static bool arg_crash_shell = false;
static bool arg_crash_reboot = false;
static char *arg_confirm_spawn = NULL;
-static ShowStatus arg_show_status = _SHOW_STATUS_UNSET;
+static ShowStatus arg_show_status = _SHOW_STATUS_INVALID;
static bool arg_switched_root = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static bool arg_service_watchdogs = true;
-static char ***arg_join_controllers = NULL;
static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL;
static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT;
static usec_t arg_default_restart_usec = DEFAULT_RESTART_USEC;
@@ -109,6 +111,7 @@ static usec_t arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL;
static unsigned arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST;
static usec_t arg_runtime_watchdog = 0;
static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
+static char *arg_early_core_pattern = NULL;
static char *arg_watchdog_device = NULL;
static char **arg_default_environment = NULL;
static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
@@ -118,7 +121,7 @@ static nsec_t arg_timer_slack_nsec = NSEC_INFINITY;
static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
static Set* arg_syscall_archs = NULL;
static FILE* arg_serialization = NULL;
-static bool arg_default_cpu_accounting = false;
+static int arg_default_cpu_accounting = -1;
static bool arg_default_io_accounting = false;
static bool arg_default_ip_accounting = false;
static bool arg_default_blockio_accounting = false;
@@ -128,7 +131,14 @@ static uint64_t arg_default_tasks_max = UINT64_MAX;
static sd_id128_t arg_machine_id = {};
static EmergencyAction arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE;
-_noreturn_ static void freeze_or_reboot(void) {
+_noreturn_ static void freeze_or_exit_or_reboot(void) {
+
+ /* If we are running in a contianer, let's prefer exiting, after all we can propagate an exit code to the
+ * container manager, and thus inform it that something went wrong. */
+ if (detect_container() > 0) {
+ log_emergency("Exiting PID 1...");
+ exit(EXIT_EXCEPTION);
+ }
if (arg_crash_reboot) {
log_notice("Rebooting in 10s...");
@@ -183,7 +193,7 @@ _noreturn_ static void crash(int sig) {
(void) kill(pid, sig); /* raise() would kill the parent */
assert_not_reached("We shouldn't be here...");
- _exit(EXIT_FAILURE);
+ _exit(EXIT_EXCEPTION);
} else {
siginfo_t status;
int r;
@@ -226,17 +236,18 @@ _noreturn_ static void crash(int sig) {
else if (pid == 0) {
(void) setsid();
(void) make_console_stdio();
+ (void) rlimit_nofile_safe();
(void) execle("/bin/sh", "/bin/sh", NULL, environ);
log_emergency_errno(errno, "execle() failed: %m");
- _exit(EXIT_FAILURE);
+ _exit(EXIT_EXCEPTION);
} else {
log_info("Spawned crash shell as PID "PID_FMT".", pid);
(void) wait_for_terminate(pid, NULL);
}
}
- freeze_or_reboot();
+ freeze_or_exit_or_reboot();
}
static void install_crash_handler(void) {
@@ -347,22 +358,35 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
r = value ? parse_boolean(value) : true;
if (r < 0)
- log_warning("Failed to parse dump core switch %s. Ignoring.", value);
+ log_warning_errno(r, "Failed to parse dump core switch %s, ignoring: %m", value);
else
arg_dump_core = r;
+ } else if (proc_cmdline_key_streq(key, "systemd.early_core_pattern")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ if (path_is_absolute(value))
+ (void) parse_path_argument_and_warn(value, false, &arg_early_core_pattern);
+ else
+ log_warning("Specified core pattern '%s' is not an absolute path, ignoring.", value);
+
} else if (proc_cmdline_key_streq(key, "systemd.crash_chvt")) {
if (!value)
arg_crash_chvt = 0; /* turn on */
- else if (parse_crash_chvt(value) < 0)
- log_warning("Failed to parse crash chvt switch %s. Ignoring.", value);
+ else {
+ r = parse_crash_chvt(value);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse crash chvt switch %s, ignoring: %m", value);
+ }
} else if (proc_cmdline_key_streq(key, "systemd.crash_shell")) {
r = value ? parse_boolean(value) : true;
if (r < 0)
- log_warning("Failed to parse crash shell switch %s. Ignoring.", value);
+ log_warning_errno(r, "Failed to parse crash shell switch %s, ignoring: %m", value);
else
arg_crash_shell = r;
@@ -370,7 +394,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
r = value ? parse_boolean(value) : true;
if (r < 0)
- log_warning("Failed to parse crash reboot switch %s. Ignoring.", value);
+ log_warning_errno(r, "Failed to parse crash reboot switch %s, ignoring: %m", value);
else
arg_crash_reboot = r;
@@ -379,17 +403,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
r = parse_confirm_spawn(value, &s);
if (r < 0)
- log_warning_errno(r, "Failed to parse confirm_spawn switch %s. Ignoring.", value);
- else {
- free(arg_confirm_spawn);
- arg_confirm_spawn = s;
- }
+ log_warning_errno(r, "Failed to parse confirm_spawn switch %s, ignoring: %m", value);
+ else
+ free_and_replace(arg_confirm_spawn, s);
} else if (proc_cmdline_key_streq(key, "systemd.service_watchdogs")) {
r = value ? parse_boolean(value) : true;
if (r < 0)
- log_warning("Failed to parse service watchdog switch %s. Ignoring.", value);
+ log_warning_errno(r, "Failed to parse service watchdog switch %s, ignoring: %m", value);
else
arg_service_watchdogs = r;
@@ -398,7 +420,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (value) {
r = parse_show_status(value, &arg_show_status);
if (r < 0)
- log_warning("Failed to parse show status switch %s. Ignoring.", value);
+ log_warning_errno(r, "Failed to parse show status switch %s, ignoring: %m", value);
} else
arg_show_status = SHOW_STATUS_YES;
@@ -409,7 +431,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
r = exec_output_from_string(value);
if (r < 0)
- log_warning("Failed to parse default standard output switch %s. Ignoring.", value);
+ log_warning_errno(r, "Failed to parse default standard output switch %s, ignoring: %m", value);
else
arg_default_std_output = r;
@@ -420,7 +442,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
r = exec_output_from_string(value);
if (r < 0)
- log_warning("Failed to parse default standard error switch %s. Ignoring.", value);
+ log_warning_errno(r, "Failed to parse default standard error switch %s, ignoring: %m", value);
else
arg_default_std_error = r;
@@ -447,7 +469,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
r = set_machine_id(value);
if (r < 0)
- log_warning("MachineID '%s' is not valid. Ignoring.", value);
+ log_warning_errno(r, "MachineID '%s' is not valid, ignoring: %m", value);
} else if (proc_cmdline_key_streq(key, "systemd.default_timeout_start_sec")) {
@@ -456,7 +478,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
r = parse_sec(value, &arg_default_timeout_start_usec);
if (r < 0)
- log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value);
+ log_warning_errno(r, "Failed to parse default start timeout '%s', ignoring: %m", value);
if (arg_default_timeout_start_usec <= 0)
arg_default_timeout_start_usec = USEC_INFINITY;
@@ -466,11 +488,11 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (proc_cmdline_value_missing(key, value))
return 0;
- parse_path_argument_and_warn(value, false, &arg_watchdog_device);
+ (void) parse_path_argument_and_warn(value, false, &arg_watchdog_device);
} else if (streq(key, "quiet") && !value) {
- if (arg_show_status == _SHOW_STATUS_UNSET)
+ if (arg_show_status == _SHOW_STATUS_INVALID)
arg_show_status = SHOW_STATUS_AUTO;
} else if (streq(key, "debug") && !value) {
@@ -604,8 +626,8 @@ static int config_parse_output_restricted(
return 0;
}
- if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE)) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file: are not supported as defaults, ignoring: %s", rvalue);
+ if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue);
return 0;
}
@@ -654,7 +676,7 @@ static int parse_config_file(void) {
{ "Manager", "CrashReboot", config_parse_bool, 0, &arg_crash_reboot },
{ "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status },
{ "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, NULL },
- { "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
+ { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL },
{ "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
{ "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
{ "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device },
@@ -690,7 +712,7 @@ static int parse_config_file(void) {
{ "Manager", "DefaultLimitNICE", config_parse_rlimit, RLIMIT_NICE, arg_default_rlimit },
{ "Manager", "DefaultLimitRTPRIO", config_parse_rlimit, RLIMIT_RTPRIO, arg_default_rlimit },
{ "Manager", "DefaultLimitRTTIME", config_parse_rlimit, RLIMIT_RTTIME, arg_default_rlimit },
- { "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting },
+ { "Manager", "DefaultCPUAccounting", config_parse_tristate, 0, &arg_default_cpu_accounting },
{ "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_accounting },
{ "Manager", "DefaultIPAccounting", config_parse_bool, 0, &arg_default_ip_accounting },
{ "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting },
@@ -739,7 +761,14 @@ static void set_manager_defaults(Manager *m) {
m->default_restart_usec = arg_default_restart_usec;
m->default_start_limit_interval = arg_default_start_limit_interval;
m->default_start_limit_burst = arg_default_start_limit_burst;
- m->default_cpu_accounting = arg_default_cpu_accounting;
+
+ /* On 4.15+ with unified hierarchy, CPU accounting is essentially free as it doesn't require the CPU
+ * controller to be enabled, so the default is to enable it unless we got told otherwise. */
+ if (arg_default_cpu_accounting >= 0)
+ m->default_cpu_accounting = arg_default_cpu_accounting;
+ else
+ m->default_cpu_accounting = cpu_accounting_is_cheap();
+
m->default_io_accounting = arg_default_io_accounting;
m->default_ip_accounting = arg_default_ip_accounting;
m->default_blockio_accounting = arg_default_blockio_accounting;
@@ -747,8 +776,10 @@ static void set_manager_defaults(Manager *m) {
m->default_tasks_accounting = arg_default_tasks_accounting;
m->default_tasks_max = arg_default_tasks_max;
- manager_set_default_rlimits(m, arg_default_rlimit);
- manager_environment_add(m, NULL, arg_default_environment);
+ (void) manager_set_default_rlimits(m, arg_default_rlimit);
+
+ (void) manager_default_environment(m);
+ (void) manager_transient_environment_add(m, arg_default_environment);
}
static void set_manager_settings(Manager *m) {
@@ -838,19 +869,15 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_LOG_LEVEL:
r = log_set_max_level_from_string(optarg);
- if (r < 0) {
- log_error("Failed to parse log level %s.", optarg);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse log level \"%s\": %m", optarg);
break;
case ARG_LOG_TARGET:
r = log_set_target_from_string(optarg);
- if (r < 0) {
- log_error("Failed to parse log target %s.", optarg);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse log target \"%s\": %m", optarg);
break;
@@ -858,10 +885,9 @@ static int parse_argv(int argc, char *argv[]) {
if (optarg) {
r = log_show_color_from_string(optarg);
- if (r < 0) {
- log_error("Failed to parse log color setting %s.", optarg);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse log color setting \"%s\": %m",
+ optarg);
} else
log_show_color(true);
@@ -870,10 +896,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_LOG_LOCATION:
if (optarg) {
r = log_show_location_from_string(optarg);
- if (r < 0) {
- log_error("Failed to parse log location setting %s.", optarg);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse log location setting \"%s\": %m",
+ optarg);
} else
log_show_location(true);
@@ -881,26 +906,24 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_DEFAULT_STD_OUTPUT:
r = exec_output_from_string(optarg);
- if (r < 0) {
- log_error("Failed to parse default standard output setting %s.", optarg);
- return r;
- } else
- arg_default_std_output = r;
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse default standard output setting \"%s\": %m",
+ optarg);
+ arg_default_std_output = r;
break;
case ARG_DEFAULT_STD_ERROR:
r = exec_output_from_string(optarg);
- if (r < 0) {
- log_error("Failed to parse default standard error output setting %s.", optarg);
- return r;
- } else
- arg_default_std_error = r;
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse default standard error output setting \"%s\": %m",
+ optarg);
+ arg_default_std_error = r;
break;
case ARG_UNIT:
r = free_and_strdup(&arg_default_unit, optarg);
if (r < 0)
- return log_error_errno(r, "Failed to set default unit %s: %m", optarg);
+ return log_error_errno(r, "Failed to set default unit \"%s\": %m", optarg);
break;
@@ -917,7 +940,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_VERSION:
@@ -938,7 +961,8 @@ static int parse_argv(int argc, char *argv[]) {
else {
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse dump core boolean: %s", optarg);
+ return log_error_errno(r, "Failed to parse dump core boolean: \"%s\": %m",
+ optarg);
arg_dump_core = r;
}
break;
@@ -946,7 +970,8 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_CRASH_CHVT:
r = parse_crash_chvt(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse crash virtual terminal index: %s", optarg);
+ return log_error_errno(r, "Failed to parse crash virtual terminal index: \"%s\": %m",
+ optarg);
break;
case ARG_CRASH_SHELL:
@@ -955,7 +980,8 @@ static int parse_argv(int argc, char *argv[]) {
else {
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg);
+ return log_error_errno(r, "Failed to parse crash shell boolean: \"%s\": %m",
+ optarg);
arg_crash_shell = r;
}
break;
@@ -966,7 +992,8 @@ static int parse_argv(int argc, char *argv[]) {
else {
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg);
+ return log_error_errno(r, "Failed to parse crash shell boolean: \"%s\": %m",
+ optarg);
arg_crash_reboot = r;
}
break;
@@ -976,23 +1003,24 @@ static int parse_argv(int argc, char *argv[]) {
r = parse_confirm_spawn(optarg, &arg_confirm_spawn);
if (r < 0)
- return log_error_errno(r, "Failed to parse confirm spawn option: %m");
+ return log_error_errno(r, "Failed to parse confirm spawn option: \"%s\": %m",
+ optarg);
break;
case ARG_SERVICE_WATCHDOGS:
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse service watchdogs boolean: %s", optarg);
+ return log_error_errno(r, "Failed to parse service watchdogs boolean: \"%s\": %m",
+ optarg);
arg_service_watchdogs = r;
break;
case ARG_SHOW_STATUS:
if (optarg) {
r = parse_show_status(optarg, &arg_show_status);
- if (r < 0) {
- log_error("Failed to parse show status boolean %s.", optarg);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse show status boolean: \"%s\": %m",
+ optarg);
} else
arg_show_status = SHOW_STATUS_YES;
break;
@@ -1002,16 +1030,18 @@ static int parse_argv(int argc, char *argv[]) {
FILE *f;
r = safe_atoi(optarg, &fd);
- if (r < 0 || fd < 0) {
- log_error("Failed to parse deserialize option %s.", optarg);
- return -EINVAL;
- }
+ if (r < 0)
+ log_error_errno(r, "Failed to parse deserialize option \"%s\": %m", optarg);
+ if (fd < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid deserialize fd: %d",
+ fd);
(void) fd_cloexec(fd, true);
f = fdopen(fd, "r");
if (!f)
- return log_error_errno(errno, "Failed to open serialization fd: %m");
+ return log_error_errno(errno, "Failed to open serialization fd %d: %m", fd);
safe_fclose(arg_serialization);
arg_serialization = f;
@@ -1026,7 +1056,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_MACHINE_ID:
r = set_machine_id(optarg);
if (r < 0)
- return log_error_errno(r, "MachineID '%s' is not valid.", optarg);
+ return log_error_errno(r, "MachineID '%s' is not valid: %m", optarg);
break;
case 'h':
@@ -1059,14 +1089,20 @@ static int parse_argv(int argc, char *argv[]) {
/* Hmm, when we aren't run as init system
* let's complain about excess arguments */
- log_error("Excess arguments.");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Excess arguments.");
}
return 0;
}
static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd", "1", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [OPTIONS...]\n\n"
"Starts up and maintains the system or user services.\n\n"
@@ -1090,20 +1126,28 @@ static int help(void) {
" --log-color[=BOOL] Highlight important log messages\n"
" --log-location[=BOOL] Include code location in log messages\n"
" --default-standard-output= Set default standard output for services\n"
- " --default-standard-error= Set default standard error output for services\n",
- program_invocation_short_name);
+ " --default-standard-error= Set default standard error output for services\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
-static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching_root) {
+static int prepare_reexecute(
+ Manager *m,
+ FILE **ret_f,
+ FDSet **ret_fds,
+ bool switching_root) {
+
_cleanup_fdset_free_ FDSet *fds = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(m);
- assert(_f);
- assert(_fds);
+ assert(ret_f);
+ assert(ret_fds);
r = manager_open_serialization(m, &f);
if (r < 0)
@@ -1119,7 +1163,7 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching
r = manager_serialize(m, f, fds, switching_root);
if (r < 0)
- return log_error_errno(r, "Failed to serialize state: %m");
+ return r;
if (fseeko(f, 0, SEEK_SET) == (off_t) -1)
return log_error_errno(errno, "Failed to rewind serialization fd: %m");
@@ -1132,24 +1176,108 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching
if (r < 0)
return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m");
- *_f = TAKE_PTR(f);
- *_fds = TAKE_PTR(fds);
+ *ret_f = TAKE_PTR(f);
+ *ret_fds = TAKE_PTR(fds);
return 0;
}
+static void bump_file_max_and_nr_open(void) {
+
+ /* Let's bump fs.file-max and fs.nr_open to their respective maximums. On current kernels large numbers of file
+ * descriptors are no longer a performance problem and their memory is properly tracked by memcg, thus counting
+ * them and limiting them in another two layers of limits is unnecessary and just complicates things. This
+ * function hence turns off 2 of the 4 levels of limits on file descriptors, and makes RLIMIT_NOLIMIT (soft +
+ * hard) the only ones that really matter. */
+
+#if BUMP_PROC_SYS_FS_FILE_MAX || BUMP_PROC_SYS_FS_NR_OPEN
+ _cleanup_free_ char *t = NULL;
+ int r;
+#endif
+
+#if BUMP_PROC_SYS_FS_FILE_MAX
+ /* I so wanted to use STRINGIFY(ULONG_MAX) here, but alas we can't as glibc/gcc define that as
+ * "(0x7fffffffffffffffL * 2UL + 1UL)". Seriously. 😢 */
+ if (asprintf(&t, "%lu\n", ULONG_MAX) < 0) {
+ log_oom();
+ return;
+ }
+
+ r = sysctl_write("fs/file-max", t);
+ if (r < 0)
+ log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to bump fs.file-max, ignoring: %m");
+#endif
+
+#if BUMP_PROC_SYS_FS_FILE_MAX && BUMP_PROC_SYS_FS_NR_OPEN
+ t = mfree(t);
+#endif
+
+#if BUMP_PROC_SYS_FS_NR_OPEN
+ int v = INT_MAX;
+
+ /* Arg! The kernel enforces maximum and minimum values on the fs.nr_open, but we don't really know what they
+ * are. The expression by which the maximum is determined is dependent on the architecture, and is something we
+ * don't really want to copy to userspace, as it is dependent on implementation details of the kernel. Since
+ * the kernel doesn't expose the maximum value to us, we can only try and hope. Hence, let's start with
+ * INT_MAX, and then keep halving the value until we find one that works. Ugly? Yes, absolutely, but kernel
+ * APIs are kernel APIs, so what do can we do... 🤯 */
+
+ for (;;) {
+ int k;
+
+ v &= ~(__SIZEOF_POINTER__ - 1); /* Round down to next multiple of the pointer size */
+ if (v < 1024) {
+ log_warning("Can't bump fs.nr_open, value too small.");
+ break;
+ }
+
+ k = read_nr_open();
+ if (k < 0) {
+ log_error_errno(k, "Failed to read fs.nr_open: %m");
+ break;
+ }
+ if (k >= v) { /* Already larger */
+ log_debug("Skipping bump, value is already larger.");
+ break;
+ }
+
+ if (asprintf(&t, "%i\n", v) < 0) {
+ log_oom();
+ return;
+ }
+
+ r = sysctl_write("fs/nr_open", t);
+ t = mfree(t);
+ if (r == -EINVAL) {
+ log_debug("Couldn't write fs.nr_open as %i, halving it.", v);
+ v /= 2;
+ continue;
+ }
+ if (r < 0) {
+ log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to bump fs.nr_open, ignoring: %m");
+ break;
+ }
+
+ log_debug("Successfully bumped fs.nr_open to %i", v);
+ break;
+ }
+#endif
+}
+
static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
int r, nr;
assert(saved_rlimit);
- /* Save the original RLIMIT_NOFILE so that we can reset it
- * later when transitioning from the initrd to the main
+ /* Save the original RLIMIT_NOFILE so that we can reset it later when transitioning from the initrd to the main
* systemd or suchlike. */
if (getrlimit(RLIMIT_NOFILE, saved_rlimit) < 0)
return log_warning_errno(errno, "Reading RLIMIT_NOFILE failed, ignoring: %m");
- /* Make sure forked processes get the default kernel setting */
+ /* Get the underlying absolute limit the kernel enforces */
+ nr = read_nr_open();
+
+ /* Make sure forked processes get limits based on the original kernel setting */
if (!arg_default_rlimit[RLIMIT_NOFILE]) {
struct rlimit *rl;
@@ -1157,11 +1285,25 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
if (!rl)
return log_oom();
+ /* Bump the hard limit for system services to a substantially higher value. The default hard limit
+ * current kernels set is pretty low (4K), mostly for historical reasons. According to kernel
+ * developers, the fd handling in recent kernels has been optimized substantially enough, so that we
+ * can bump the limit now, without paying too high a price in memory or performance. Note however that
+ * we only bump the hard limit, not the soft limit. That's because select() works the way it works, and
+ * chokes on fds >= 1024. If we'd bump the soft limit globally, it might accidentally happen to
+ * unexpecting programs that they get fds higher than what they can process using select(). By only
+ * bumping the hard limit but leaving the low limit as it is we avoid this pitfall: programs that are
+ * written by folks aware of the select() problem in mind (and thus use poll()/epoll instead of
+ * select(), the way everybody should) can explicitly opt into high fds by bumping their soft limit
+ * beyond 1024, to the hard limit we pass. */
+ if (arg_system)
+ rl->rlim_max = MIN((rlim_t) nr, MAX(rl->rlim_max, (rlim_t) HIGH_RLIMIT_NOFILE));
+
arg_default_rlimit[RLIMIT_NOFILE] = rl;
}
- /* Bump up the resource limit for ourselves substantially, all the way to the maximum the kernel allows */
- nr = read_nr_open();
+ /* Bump up the resource limit for ourselves substantially, all the way to the maximum the kernel allows, for
+ * both hard and soft. */
r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(nr));
if (r < 0)
return log_warning_errno(r, "Setting RLIMIT_NOFILE failed, ignoring: %m");
@@ -1173,16 +1315,15 @@ static int bump_rlimit_memlock(struct rlimit *saved_rlimit) {
int r;
assert(saved_rlimit);
- assert(getuid() == 0);
- /* BPF_MAP_TYPE_LPM_TRIE bpf maps are charged against RLIMIT_MEMLOCK, even though we have CAP_IPC_LOCK which
- * should normally disable such checks. We need them to implement IPAccessAllow= and IPAccessDeny=, hence let's
- * bump the value high enough for the root user. */
+ /* BPF_MAP_TYPE_LPM_TRIE bpf maps are charged against RLIMIT_MEMLOCK, even if we have CAP_IPC_LOCK which should
+ * normally disable such checks. We need them to implement IPAccessAllow= and IPAccessDeny=, hence let's bump
+ * the value high enough for our user. */
if (getrlimit(RLIMIT_MEMLOCK, saved_rlimit) < 0)
return log_warning_errno(errno, "Reading RLIMIT_MEMLOCK failed, ignoring: %m");
- r = setrlimit_closest(RLIMIT_MEMLOCK, &RLIMIT_MAKE_CONST(1024ULL*1024ULL*16ULL));
+ r = setrlimit_closest(RLIMIT_MEMLOCK, &RLIMIT_MAKE_CONST(HIGH_RLIMIT_MEMLOCK));
if (r < 0)
return log_warning_errno(r, "Setting RLIMIT_MEMLOCK failed, ignoring: %m");
@@ -1219,7 +1360,7 @@ static int status_welcome(void) {
_cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
int r;
- if (arg_show_status <= 0)
+ if (IN_SET(arg_show_status, SHOW_STATUS_NO, SHOW_STATUS_AUTO))
return 0;
r = parse_os_release(NULL,
@@ -1231,12 +1372,12 @@ static int status_welcome(void) {
"Failed to read os-release file, ignoring: %m");
if (log_get_show_color())
- return status_printf(NULL, false, false,
+ return status_printf(NULL, 0,
"\nWelcome to \x1B[%sm%s\x1B[0m!\n",
isempty(ansi_color) ? "1" : ansi_color,
isempty(pretty_name) ? "Linux" : pretty_name);
else
- return status_printf(NULL, false, false,
+ return status_printf(NULL, 0,
"\nWelcome to %s!\n",
isempty(pretty_name) ? "Linux" : pretty_name);
}
@@ -1268,7 +1409,7 @@ static int bump_unix_max_dgram_qlen(void) {
r = read_one_line_file("/proc/sys/net/unix/max_dgram_qlen", &qlen);
if (r < 0)
- return log_warning_errno(r, "Failed to read AF_UNIX datagram queue length, ignoring: %m");
+ return log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, "Failed to read AF_UNIX datagram queue length, ignoring: %m");
r = safe_atolu(qlen, &v);
if (r < 0)
@@ -1277,7 +1418,7 @@ static int bump_unix_max_dgram_qlen(void) {
if (v >= DEFAULT_UNIX_MAX_DGRAM_QLEN)
return 0;
- r = write_string_filef("/proc/sys/net/unix/max_dgram_qlen", 0, "%lu", DEFAULT_UNIX_MAX_DGRAM_QLEN);
+ r = write_string_filef("/proc/sys/net/unix/max_dgram_qlen", WRITE_STRING_FILE_DISABLE_BUFFER, "%lu", DEFAULT_UNIX_MAX_DGRAM_QLEN);
if (r < 0)
return log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to bump AF_UNIX datagram queue length, ignoring: %m");
@@ -1474,13 +1615,29 @@ static void initialize_coredump(bool skip_setup) {
if (setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)) < 0)
log_warning_errno(errno, "Failed to set RLIMIT_CORE: %m");
- /* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored
- * until the systemd-coredump tool is enabled via sysctl. */
+ /* But at the same time, turn off the core_pattern logic by default, so that no
+ * coredumps are stored until the systemd-coredump tool is enabled via
+ * sysctl. However it can be changed via the kernel command line later so core
+ * dumps can still be generated during early startup and in initramfs. */
if (!skip_setup)
disable_coredumps();
#endif
}
+static void initialize_core_pattern(bool skip_setup) {
+ int r;
+
+ if (skip_setup || !arg_early_core_pattern)
+ return;
+
+ if (getpid_cached() != 1)
+ return;
+
+ r = write_string_file("/proc/sys/kernel/core_pattern", arg_early_core_pattern, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0)
+ log_warning_errno(r, "Failed to write '%s' to /proc/sys/kernel/core_pattern, ignoring: %m", arg_early_core_pattern);
+}
+
static void do_reexecute(
int argc,
char *argv[],
@@ -1577,6 +1734,7 @@ static void do_reexecute(
/* Reenable any blocked signals, especially important if we switch from initial ramdisk to init=... */
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
+ (void) rlimit_nofile_safe();
if (switch_root_init) {
args[0] = switch_root_init;
@@ -1633,7 +1791,7 @@ static int invoke_main_loop(
return log_emergency_errno(r, "Failed to run main loop: %m");
}
- switch (m->exit_code) {
+ switch ((ManagerObjective) r) {
case MANAGER_RELOAD: {
LogTarget saved_log_target;
@@ -1660,7 +1818,8 @@ static int invoke_main_loop(
r = manager_reload(m);
if (r < 0)
- log_warning_errno(r, "Failed to reload, ignoring: %m");
+ /* Reloading failed before the point of no return. Let's continue running as if nothing happened. */
+ m->objective = MANAGER_OK;
break;
}
@@ -1724,19 +1883,19 @@ static int invoke_main_loop(
case MANAGER_POWEROFF:
case MANAGER_HALT:
case MANAGER_KEXEC: {
- static const char * const table[_MANAGER_EXIT_CODE_MAX] = {
- [MANAGER_EXIT] = "exit",
- [MANAGER_REBOOT] = "reboot",
+ static const char * const table[_MANAGER_OBJECTIVE_MAX] = {
+ [MANAGER_EXIT] = "exit",
+ [MANAGER_REBOOT] = "reboot",
[MANAGER_POWEROFF] = "poweroff",
- [MANAGER_HALT] = "halt",
- [MANAGER_KEXEC] = "kexec"
+ [MANAGER_HALT] = "halt",
+ [MANAGER_KEXEC] = "kexec",
};
log_notice("Shutting down.");
*ret_reexecute = false;
*ret_retval = m->return_value;
- assert_se(*ret_shutdown_verb = table[m->exit_code]);
+ assert_se(*ret_shutdown_verb = table[m->objective]);
*ret_fds = NULL;
*ret_switch_root_dir = *ret_switch_root_init = NULL;
@@ -1744,7 +1903,7 @@ static int invoke_main_loop(
}
default:
- assert_not_reached("Unknown exit code.");
+ assert_not_reached("Unknown or unexpected manager objective.");
}
}
}
@@ -1816,7 +1975,7 @@ static int initialize_runtime(
install_crash_handler();
if (!skip_setup) {
- r = mount_cgroup_controllers(arg_join_controllers);
+ r = mount_cgroup_controllers();
if (r < 0) {
*ret_error_message = "Failed to mount cgroup hierarchies";
return r;
@@ -1827,6 +1986,7 @@ static int initialize_runtime(
machine_id_setup(NULL, arg_machine_id, NULL);
loopback_setup();
bump_unix_max_dgram_qlen();
+ bump_file_max_and_nr_open();
test_usr();
write_container_id();
}
@@ -1879,11 +2039,9 @@ static int initialize_runtime(
if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0)
log_warning_errno(errno, "Failed to make us a subreaper: %m");
- if (arg_system) {
- /* Bump up RLIMIT_NOFILE for systemd itself */
- (void) bump_rlimit_nofile(saved_rlimit_nofile);
- (void) bump_rlimit_memlock(saved_rlimit_memlock);
- }
+ /* Bump up RLIMIT_NOFILE for systemd itself */
+ (void) bump_rlimit_nofile(saved_rlimit_nofile);
+ (void) bump_rlimit_memlock(saved_rlimit_memlock);
return 0;
}
@@ -1942,7 +2100,6 @@ static void free_arguments(void) {
arg_default_unit = mfree(arg_default_unit);
arg_confirm_spawn = mfree(arg_confirm_spawn);
- arg_join_controllers = strv_free_free(arg_join_controllers);
arg_default_environment = strv_free(arg_default_environment);
arg_syscall_archs = set_free(arg_syscall_archs);
}
@@ -1985,7 +2142,7 @@ static int load_configuration(int argc, char **argv, const char **ret_error_mess
}
/* Initialize the show status setting if it hasn't been set explicitly yet */
- if (arg_show_status == _SHOW_STATUS_UNSET)
+ if (arg_show_status == _SHOW_STATUS_INVALID)
arg_show_status = SHOW_STATUS_YES;
return 0;
@@ -1994,50 +2151,43 @@ static int load_configuration(int argc, char **argv, const char **ret_error_mess
static int safety_checks(void) {
if (getpid_cached() == 1 &&
- arg_action != ACTION_RUN) {
- log_error("Unsupported execution mode while PID 1.");
- return -EPERM;
- }
+ arg_action != ACTION_RUN)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Unsupported execution mode while PID 1.");
if (getpid_cached() == 1 &&
- !arg_system) {
- log_error("Can't run --user mode as PID 1.");
- return -EPERM;
- }
+ !arg_system)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Can't run --user mode as PID 1.");
if (arg_action == ACTION_RUN &&
arg_system &&
- getpid_cached() != 1) {
- log_error("Can't run system mode unless PID 1.");
- return -EPERM;
- }
+ getpid_cached() != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Can't run system mode unless PID 1.");
if (arg_action == ACTION_TEST &&
- geteuid() == 0) {
- log_error("Don't run test mode as root.");
- return -EPERM;
- }
+ geteuid() == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Don't run test mode as root.");
if (!arg_system &&
arg_action == ACTION_RUN &&
- sd_booted() <= 0) {
- log_error("Trying to run as user instance, but the system has not been booted with systemd.");
- return -EOPNOTSUPP;
- }
+ sd_booted() <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Trying to run as user instance, but the system has not been booted with systemd.");
if (!arg_system &&
arg_action == ACTION_RUN &&
- !getenv("XDG_RUNTIME_DIR")) {
- log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
- return -EUNATCH;
- }
+ !getenv("XDG_RUNTIME_DIR"))
+ return log_error_errno(SYNTHETIC_ERRNO(EUNATCH),
+ "Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
if (arg_system &&
arg_action == ACTION_RUN &&
- running_in_chroot() > 0) {
- log_error("Cannot be run in a chroot() environment.");
- return -EOPNOTSUPP;
- }
+ running_in_chroot() > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Cannot be run in a chroot() environment.");
return 0;
}
@@ -2309,13 +2459,13 @@ int main(int argc, char *argv[]) {
goto finish;
if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS, ACTION_DUMP_BUS_PROPERTIES))
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (arg_action != ACTION_RUN)
skip_setup = true;
if (arg_action == ACTION_HELP) {
- retval = help();
+ retval = help() < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
goto finish;
} else if (arg_action == ACTION_VERSION) {
retval = version();
@@ -2337,6 +2487,9 @@ int main(int argc, char *argv[]) {
if (arg_action == ACTION_RUN) {
+ /* A core pattern might have been specified via the cmdline. */
+ initialize_core_pattern(skip_setup);
+
/* Close logging fds, in order not to confuse collecting passed fds and terminal logic below */
log_close();
@@ -2373,8 +2526,8 @@ int main(int argc, char *argv[]) {
m->timestamps[MANAGER_TIMESTAMP_KERNEL] = kernel_timestamp;
m->timestamps[MANAGER_TIMESTAMP_INITRD] = initrd_timestamp;
m->timestamps[MANAGER_TIMESTAMP_USERSPACE] = userspace_timestamp;
- m->timestamps[MANAGER_TIMESTAMP_SECURITY_START] = security_start_timestamp;
- m->timestamps[MANAGER_TIMESTAMP_SECURITY_FINISH] = security_finish_timestamp;
+ m->timestamps[manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_SECURITY_START)] = security_start_timestamp;
+ m->timestamps[manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_SECURITY_FINISH)] = security_finish_timestamp;
set_manager_defaults(m);
set_manager_settings(m);
@@ -2387,7 +2540,6 @@ int main(int argc, char *argv[]) {
r = manager_startup(m, arg_serialization, fds);
if (r < 0) {
- log_error_errno(r, "Failed to fully start up daemon: %m");
error_message = "Failed to start up manager";
goto finish;
}
@@ -2473,8 +2625,8 @@ finish:
if (error_message)
manager_status_printf(NULL, STATUS_TYPE_EMERGENCY,
ANSI_HIGHLIGHT_RED "!!!!!!" ANSI_NORMAL,
- "%s, freezing.", error_message);
- freeze_or_reboot();
+ "%s.", error_message);
+ freeze_or_exit_or_reboot();
}
return retval;
diff --git a/src/core/manager.c b/src/core/manager.c
index 930df4e23a..35d9753b12 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -22,8 +22,8 @@
#include "sd-messages.h"
#include "sd-path.h"
-#include "alloc-util.h"
#include "all-units.h"
+#include "alloc-util.h"
#include "audit-fd.h"
#include "boot-timestamps.h"
#include "bus-common-errors.h"
@@ -61,6 +61,7 @@
#include "ratelimit.h"
#include "rlimit-util.h"
#include "rm-rf.h"
+#include "serialize.h"
#include "signal-util.h"
#include "socket-util.h"
#include "special.h"
@@ -351,7 +352,7 @@ static int manager_setup_time_change(Manager *m) {
assert(m);
- if (m->test_run_flags)
+ if (MANAGER_IS_TEST_RUN(m))
return 0;
m->time_change_event_source = sd_event_source_unref(m->time_change_event_source);
@@ -407,7 +408,7 @@ static int manager_setup_timezone_change(Manager *m) {
assert(m);
- if (m->test_run_flags != 0)
+ if (MANAGER_IS_TEST_RUN(m))
return 0;
/* We watch /etc/localtime for three events: change of the link count (which might mean removal from /etc even
@@ -423,10 +424,14 @@ static int manager_setup_timezone_change(Manager *m) {
r = sd_event_add_inotify(m->event, &new_event, "/etc/localtime",
IN_ATTRIB|IN_MOVE_SELF|IN_CLOSE_WRITE|IN_DONT_FOLLOW, manager_dispatch_timezone_change, m);
- if (r == -ENOENT) /* If the file doesn't exist yet, subscribe to /etc instead, and wait until it is created
- * either by O_CREATE or by rename() */
+ if (r == -ENOENT) {
+ /* If the file doesn't exist yet, subscribe to /etc instead, and wait until it is created either by
+ * O_CREATE or by rename() */
+
+ log_debug_errno(r, "/etc/localtime doesn't exist yet, watching /etc instead.");
r = sd_event_add_inotify(m->event, &new_event, "/etc",
IN_CREATE|IN_MOVED_TO|IN_ONLYDIR, manager_dispatch_timezone_change, m);
+ }
if (r < 0)
return log_error_errno(r, "Failed to create timezone change event source: %m");
@@ -446,7 +451,7 @@ static int enable_special_signals(Manager *m) {
assert(m);
- if (m->test_run_flags)
+ if (MANAGER_IS_TEST_RUN(m))
return 0;
/* Enable that we get SIGINT on control-alt-del. In containers
@@ -567,12 +572,11 @@ static int manager_setup_signals(Manager *m) {
return 0;
}
-static void manager_sanitize_environment(Manager *m) {
- assert(m);
+static char** sanitize_environment(char **l) {
/* Let's remove some environment variables that we need ourselves to communicate with our clients */
strv_env_unset_many(
- m->environment,
+ l,
"EXIT_CODE",
"EXIT_STATUS",
"INVOCATION_ID",
@@ -591,12 +595,16 @@ static void manager_sanitize_environment(Manager *m) {
NULL);
/* Let's order the environment alphabetically, just to make it pretty */
- strv_sort(m->environment);
+ strv_sort(l);
+
+ return l;
}
-static int manager_default_environment(Manager *m) {
+int manager_default_environment(Manager *m) {
assert(m);
+ m->transient_environment = strv_free(m->transient_environment);
+
if (MANAGER_IS_SYSTEM(m)) {
/* The system manager always starts with a clean
* environment for its children. It does not import
@@ -605,20 +613,19 @@ static int manager_default_environment(Manager *m) {
* The initial passed environment is untouched to keep
* /proc/self/environ valid; it is used for tagging
* the init process inside containers. */
- m->environment = strv_new("PATH=" DEFAULT_PATH,
- NULL);
+ m->transient_environment = strv_new("PATH=" DEFAULT_PATH);
/* Import locale variables LC_*= from configuration */
- locale_setup(&m->environment);
+ (void) locale_setup(&m->transient_environment);
} else
/* The user manager passes its own environment
* along to its children. */
- m->environment = strv_copy(environ);
+ m->transient_environment = strv_copy(environ);
- if (!m->environment)
- return -ENOMEM;
+ if (!m->transient_environment)
+ return log_oom();
- manager_sanitize_environment(m);
+ sanitize_environment(m->transient_environment);
return 0;
}
@@ -711,28 +718,51 @@ static int manager_setup_sigchld_event_source(Manager *m) {
return 0;
}
-int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
+int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager **_m) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
assert(_m);
assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER));
- m = new0(Manager, 1);
+ m = new(Manager, 1);
if (!m)
return -ENOMEM;
- m->unit_file_scope = scope;
- m->exit_code = _MANAGER_EXIT_CODE_INVALID;
- m->default_timer_accuracy_usec = USEC_PER_MINUTE;
- m->default_memory_accounting = MEMORY_ACCOUNTING_DEFAULT;
- m->default_tasks_accounting = true;
- m->default_tasks_max = UINT64_MAX;
- m->default_timeout_start_usec = DEFAULT_TIMEOUT_USEC;
- m->default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
- m->default_restart_usec = DEFAULT_RESTART_USEC;
- m->original_log_level = -1;
- m->original_log_target = _LOG_TARGET_INVALID;
+ *m = (Manager) {
+ .unit_file_scope = scope,
+ .objective = _MANAGER_OBJECTIVE_INVALID,
+
+ .default_timer_accuracy_usec = USEC_PER_MINUTE,
+ .default_memory_accounting = MEMORY_ACCOUNTING_DEFAULT,
+ .default_tasks_accounting = true,
+ .default_tasks_max = UINT64_MAX,
+ .default_timeout_start_usec = DEFAULT_TIMEOUT_USEC,
+ .default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC,
+ .default_restart_usec = DEFAULT_RESTART_USEC,
+
+ .original_log_level = -1,
+ .original_log_target = _LOG_TARGET_INVALID,
+
+ .notify_fd = -1,
+ .cgroups_agent_fd = -1,
+ .signal_fd = -1,
+ .time_change_fd = -1,
+ .user_lookup_fds = { -1, -1 },
+ .private_listen_fd = -1,
+ .dev_autofs_fd = -1,
+ .cgroup_inotify_fd = -1,
+ .pin_cgroupfs_fd = -1,
+ .ask_password_inotify_fd = -1,
+ .idle_pipe = { -1, -1, -1, -1},
+
+ /* start as id #1, so that we can leave #0 around as "null-like" value */
+ .current_job_id = 1,
+
+ .have_ask_password = -EINVAL, /* we don't know */
+ .first_boot = -1,
+ .test_run_flags = test_run_flags,
+ };
#if ENABLE_EFI
if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0)
@@ -756,21 +786,6 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
m->invocation_log_format_string = "USER_INVOCATION_ID=%s";
}
- m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
-
- m->pin_cgroupfs_fd = m->notify_fd = m->cgroups_agent_fd = m->signal_fd = m->time_change_fd =
- m->dev_autofs_fd = m->private_listen_fd = m->cgroup_inotify_fd =
- m->ask_password_inotify_fd = -1;
-
- m->user_lookup_fds[0] = m->user_lookup_fds[1] = -1;
-
- m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
-
- m->have_ask_password = -EINVAL; /* we don't know */
- m->first_boot = -1;
-
- m->test_run_flags = test_run_flags;
-
/* Reboot immediately if the user hits C-A-D more often than 7x per 2s */
RATELIMIT_INIT(m->ctrl_alt_del_ratelimit, 2 * USEC_PER_SEC, 7);
@@ -798,10 +813,6 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
if (r < 0)
return r;
- m->udev = udev_new();
- if (!m->udev)
- return -ENOMEM;
-
r = sd_event_default(&m->event);
if (r < 0)
return r;
@@ -831,9 +842,7 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
if (r < 0)
return r;
- r = manager_setup_timezone_change(m);
- if (r < 0)
- return r;
+ (void) manager_setup_timezone_change(m);
r = manager_setup_sigchld_event_source(m);
if (r < 0)
@@ -861,15 +870,13 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
static int manager_setup_notify(Manager *m) {
int r;
- if (m->test_run_flags)
+ if (MANAGER_IS_TEST_RUN(m))
return 0;
if (m->notify_fd < 0) {
_cleanup_close_ int fd = -1;
- union sockaddr_union sa = {
- .sa.sa_family = AF_UNIX,
- };
- static const int one = 1;
+ union sockaddr_union sa = {};
+ int salen;
/* First free all secondary fields */
m->notify_socket = mfree(m->notify_socket);
@@ -885,17 +892,20 @@ 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);
+
(void) mkdir_parents_label(m->notify_socket, 0755);
- (void) unlink(m->notify_socket);
+ (void) sockaddr_un_unlink(&sa.un);
- strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1);
- r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+ r = bind(fd, &sa.sa, salen);
if (r < 0)
- return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
+ return log_error_errno(errno, "bind(%s) failed: %m", m->notify_socket);
- r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- return log_error_errno(errno, "SO_PASSCRED failed: %m");
+ return log_error_errno(r, "SO_PASSCRED failed: %m");
m->notify_fd = TAKE_FD(fd);
@@ -940,7 +950,7 @@ static int manager_setup_cgroups_agent(Manager *m) {
* to it. The system instance hence listens on this special socket, but the user instances listen on the system
* bus for these messages. */
- if (m->test_run_flags)
+ if (MANAGER_IS_TEST_RUN(m))
return 0;
if (!MANAGER_IS_SYSTEM(m))
@@ -964,7 +974,7 @@ static int manager_setup_cgroups_agent(Manager *m) {
fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE);
- (void) unlink(sa.un.sun_path);
+ (void) sockaddr_un_unlink(&sa.un);
/* Only allow root to connect to this socket */
RUN_WITH_UMASK(0077)
@@ -972,8 +982,7 @@ static int manager_setup_cgroups_agent(Manager *m) {
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
- m->cgroups_agent_fd = fd;
- fd = -1;
+ m->cgroups_agent_fd = TAKE_FD(fd);
}
if (!m->cgroups_agent_event_source) {
@@ -1211,6 +1220,45 @@ static unsigned manager_dispatch_gc_job_queue(Manager *m) {
return n;
}
+static unsigned manager_dispatch_stop_when_unneeded_queue(Manager *m) {
+ unsigned n = 0;
+ Unit *u;
+ int r;
+
+ assert(m);
+
+ while ((u = m->stop_when_unneeded_queue)) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ assert(m->stop_when_unneeded_queue);
+
+ assert(u->in_stop_when_unneeded_queue);
+ LIST_REMOVE(stop_when_unneeded_queue, m->stop_when_unneeded_queue, u);
+ u->in_stop_when_unneeded_queue = false;
+
+ n++;
+
+ if (!unit_is_unneeded(u))
+ continue;
+
+ log_unit_debug(u, "Unit is not needed anymore.");
+
+ /* If stopping a unit fails continuously we might enter a stop loop here, hence stop acting on the
+ * service being unnecessary after a while. */
+
+ if (!ratelimit_below(&u->auto_stop_ratelimit)) {
+ log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently.");
+ continue;
+ }
+
+ /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
+ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r));
+ }
+
+ return n;
+}
+
static void manager_clear_jobs_and_units(Manager *m) {
Unit *u;
@@ -1228,17 +1276,20 @@ static void manager_clear_jobs_and_units(Manager *m) {
assert(!m->cleanup_queue);
assert(!m->gc_unit_queue);
assert(!m->gc_job_queue);
+ assert(!m->stop_when_unneeded_queue);
assert(hashmap_isempty(m->jobs));
assert(hashmap_isempty(m->units));
m->n_on_console = 0;
m->n_running_jobs = 0;
+ m->n_installed_jobs = 0;
+ m->n_failed_jobs = 0;
}
Manager* manager_free(Manager *m) {
- UnitType c;
ExecDirectoryType dt;
+ UnitType c;
if (!m)
return NULL;
@@ -1249,8 +1300,8 @@ Manager* manager_free(Manager *m) {
if (unit_vtable[c]->shutdown)
unit_vtable[c]->shutdown(m);
- /* If we reexecute ourselves, we keep the root cgroup around */
- manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
+ /* Keep the cgroup hierarchy in place except when we know we are going down for good */
+ manager_shutdown_cgroup(m, IN_SET(m->objective, MANAGER_EXIT, MANAGER_REBOOT, MANAGER_POWEROFF, MANAGER_HALT, MANAGER_KEXEC));
lookup_paths_flush_generator(&m->lookup_paths);
@@ -1292,13 +1343,13 @@ Manager* manager_free(Manager *m) {
manager_close_idle_pipe(m);
- udev_unref(m->udev);
sd_event_unref(m->event);
free(m->notify_socket);
lookup_paths_free(&m->lookup_paths);
- strv_free(m->environment);
+ strv_free(m->transient_environment);
+ strv_free(m->client_environment);
hashmap_free(m->cgroup_unit);
set_free_free(m->unit_path_cache);
@@ -1476,14 +1527,9 @@ static bool manager_dbus_is_running(Manager *m, bool deserialized) {
* and the service unit. If the 'deserialized' parameter is true we'll check the deserialized state of the unit
* rather than the current one. */
- if (m->test_run_flags != 0)
+ if (MANAGER_IS_TEST_RUN(m))
return false;
- /* If we are in the user instance, and the env var is already set for us, then this means D-Bus is ran
- * somewhere outside of our own logic. Let's use it */
- if (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS"))
- return true;
-
u = manager_get_unit(m, SPECIAL_DBUS_SOCKET);
if (!u)
return false;
@@ -1529,7 +1575,7 @@ static void manager_preset_all(Manager *m) {
if (!MANAGER_IS_SYSTEM(m))
return;
- if (m->test_run_flags != 0)
+ if (MANAGER_IS_TEST_RUN(m))
return;
/* If this is the first boot, and we are in the host system, then preset everything */
@@ -1541,6 +1587,49 @@ static void manager_preset_all(Manager *m) {
log_info("Populated /etc with preset unit settings.");
}
+static void manager_vacuum(Manager *m) {
+ assert(m);
+
+ /* Release any dynamic users no longer referenced */
+ dynamic_user_vacuum(m, true);
+
+ /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
+ manager_vacuum_uid_refs(m);
+ manager_vacuum_gid_refs(m);
+
+ /* Release any runtimes no longer referenced */
+ exec_runtime_vacuum(m);
+}
+
+static void manager_ready(Manager *m) {
+ assert(m);
+
+ /* After having loaded everything, do the final round of catching up with what might have changed */
+
+ m->objective = MANAGER_OK; /* Tell everyone we are up now */
+
+ /* It might be safe to log to the journal now and connect to dbus */
+ 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);
+}
+
+static Manager* manager_reloading_start(Manager *m) {
+ m->n_reloading++;
+ return m;
+}
+static void manager_reloading_stopp(Manager **m) {
+ if (*m) {
+ assert((*m)->n_reloading > 0);
+ (*m)->n_reloading--;
+ }
+}
+
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
int r;
@@ -1549,98 +1638,92 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
/* If we are running in test mode, we still want to run the generators,
* but we should not touch the real generator directories. */
r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope,
- m->test_run_flags ? LOOKUP_PATHS_TEMPORARY_GENERATED : 0,
+ MANAGER_IS_TEST_RUN(m) ? LOOKUP_PATHS_TEMPORARY_GENERATED : 0,
NULL);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to initialize path lookup table: %m");
+ dual_timestamp_get(m->timestamps + manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_GENERATORS_START));
r = manager_run_environment_generators(m);
+ if (r >= 0)
+ r = manager_run_generators(m);
+ dual_timestamp_get(m->timestamps + manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_GENERATORS_FINISH));
if (r < 0)
return r;
- dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_GENERATORS_START);
- r = manager_run_generators(m);
- dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_GENERATORS_FINISH);
+ manager_preset_all(m);
+
+ r = lookup_paths_reduce(&m->lookup_paths);
if (r < 0)
- return r;
+ log_warning_errno(r, "Failed ot reduce unit file paths, ignoring: %m");
- manager_preset_all(m);
- lookup_paths_reduce(&m->lookup_paths);
manager_build_unit_path_cache(m);
- /* If we will deserialize make sure that during enumeration
- * this is already known, so we increase the counter here
- * already */
- if (serialization)
- m->n_reloading++;
+ {
+ /* This block is (optionally) done with the reloading counter bumped */
+ _cleanup_(manager_reloading_stopp) Manager *reloading = NULL;
- /* First, enumerate what we can from all config files */
- dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_UNITS_LOAD_START);
- manager_enumerate_perpetual(m);
- manager_enumerate(m);
- dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_UNITS_LOAD_FINISH);
+ /* If we will deserialize make sure that during enumeration this is already known, so we increase the
+ * counter here already */
+ if (serialization)
+ reloading = manager_reloading_start(m);
- /* Second, deserialize if there is something to deserialize */
- if (serialization) {
- r = manager_deserialize(m, serialization, fds);
- if (r < 0)
- return log_error_errno(r, "Deserialization failed: %m");
- }
-
- /* Any fds left? Find some unit which wants them. This is
- * useful to allow container managers to pass some file
- * descriptors to us pre-initialized. This enables
- * socket-based activation of entire containers. */
- manager_distribute_fds(m, fds);
+ /* First, enumerate what we can from all config files */
+ dual_timestamp_get(m->timestamps + manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_UNITS_LOAD_START));
+ manager_enumerate_perpetual(m);
+ manager_enumerate(m);
+ dual_timestamp_get(m->timestamps + manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_UNITS_LOAD_FINISH));
- /* We might have deserialized the notify fd, but if we didn't
- * then let's create the bus now */
- r = manager_setup_notify(m);
- if (r < 0)
- /* No sense to continue without notifications, our children would fail anyway. */
- return r;
-
- r = manager_setup_cgroups_agent(m);
- if (r < 0)
- /* Likewise, no sense to continue without empty cgroup notifications. */
- return r;
+ /* Second, deserialize if there is something to deserialize */
+ if (serialization) {
+ r = manager_deserialize(m, serialization, fds);
+ if (r < 0)
+ return log_error_errno(r, "Deserialization failed: %m");
+ }
- r = manager_setup_user_lookup_fd(m);
- if (r < 0)
- /* This shouldn't fail, except if things are really broken. */
- return r;
+ /* Any fds left? Find some unit which wants them. This is useful to allow container managers to pass
+ * some file descriptors to us pre-initialized. This enables socket-based activation of entire
+ * containers. */
+ manager_distribute_fds(m, fds);
- /* Connect to the bus if we are good for it */
- manager_setup_bus(m);
+ /* We might have deserialized the notify fd, but if we didn't then let's create the bus now */
+ r = manager_setup_notify(m);
+ if (r < 0)
+ /* No sense to continue without notifications, our children would fail anyway. */
+ return r;
- /* Now that we are connected to all possible busses, let's deserialize who is tracking us. */
- (void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
- m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
+ r = manager_setup_cgroups_agent(m);
+ if (r < 0)
+ /* Likewise, no sense to continue without empty cgroup notifications. */
+ return r;
- /* Third, fire things up! */
- manager_coldplug(m);
+ r = manager_setup_user_lookup_fd(m);
+ if (r < 0)
+ /* This shouldn't fail, except if things are really broken. */
+ return r;
- /* Release any dynamic users no longer referenced */
- dynamic_user_vacuum(m, true);
+ /* Connect to the bus if we are good for it */
+ manager_setup_bus(m);
- exec_runtime_vacuum(m);
+ /* Now that we are connected to all possible busses, let's deserialize who is tracking us. */
+ r = bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
+ if (r < 0)
+ log_warning_errno(r, "Failed to deserialized tracked clients, ignoring: %m");
+ m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
- /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
- manager_vacuum_uid_refs(m);
- manager_vacuum_gid_refs(m);
+ /* Third, fire things up! */
+ manager_coldplug(m);
- if (serialization) {
- assert(m->n_reloading > 0);
- m->n_reloading--;
+ /* Clean up runtime objects */
+ manager_vacuum(m);
- /* Let's wait for the UnitNew/JobNew messages being
- * sent, before we notify that the reload is
- * finished */
- m->send_reloading_done = true;
+ if (serialization)
+ /* Let's wait for the UnitNew/JobNew messages being sent, before we notify that the
+ * reload is finished */
+ m->send_reloading_done = true;
}
- /* Let's finally catch up with any changes that took place while we were reloading/reexecing */
- manager_catchup(m);
+ manager_ready(m);
return 0;
}
@@ -2055,7 +2138,7 @@ static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) {
assert(j->installed);
assert(j->in_run_queue);
- job_run_and_invalidate(j);
+ (void) job_run_and_invalidate(j);
}
if (m->n_running_jobs > 0)
@@ -2074,57 +2157,63 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) {
assert(m);
- if (m->dispatching_dbus_queue)
- return 0;
-
- /* Anything to do at all? */
- if (!m->dbus_unit_queue && !m->dbus_job_queue && !m->send_reloading_done && !m->queued_message)
- return 0;
-
- /* Do we have overly many messages queued at the moment? If so, let's not enqueue more on top, let's sit this
- * cycle out, and process things in a later cycle when the queues got a bit emptier. */
- if (manager_bus_n_queued_write(m) > MANAGER_BUS_BUSY_THRESHOLD)
- return 0;
+ /* When we are reloading, let's not wait with generating signals, since we need to exit the manager as quickly
+ * as we can. There's no point in throttling generation of signals in that case. */
+ if (MANAGER_IS_RELOADING(m) || m->send_reloading_done || m->pending_reload_message)
+ budget = (unsigned) -1; /* infinite budget in this case */
+ else {
+ /* Anything to do at all? */
+ if (!m->dbus_unit_queue && !m->dbus_job_queue)
+ return 0;
- /* Only process a certain number of units/jobs per event loop iteration. Even if the bus queue wasn't overly
- * full before this call we shouldn't increase it in size too wildly in one step, and we shouldn't monopolize
- * CPU time with generating these messages. Note the difference in counting of this "budget" and the
- * "threshold" above: the "budget" is decreased only once per generated message, regardless how many
- * busses/direct connections it is enqueued on, while the "threshold" is applied to each queued instance of bus
- * message, i.e. if the same message is enqueued to five busses/direct connections it will be counted five
- * times. This difference in counting ("references" vs. "instances") is primarily a result of the fact that
- * it's easier to implement it this way, however it also reflects the thinking that the "threshold" should put
- * a limit on used queue memory, i.e. space, while the "budget" should put a limit on time. Also note that
- * the "threshold" is currently chosen much higher than the "budget". */
- budget = MANAGER_BUS_MESSAGE_BUDGET;
+ /* Do we have overly many messages queued at the moment? If so, let's not enqueue more on top, let's
+ * sit this cycle out, and process things in a later cycle when the queues got a bit emptier. */
+ if (manager_bus_n_queued_write(m) > MANAGER_BUS_BUSY_THRESHOLD)
+ return 0;
- m->dispatching_dbus_queue = true;
+ /* Only process a certain number of units/jobs per event loop iteration. Even if the bus queue wasn't
+ * overly full before this call we shouldn't increase it in size too wildly in one step, and we
+ * shouldn't monopolize CPU time with generating these messages. Note the difference in counting of
+ * this "budget" and the "threshold" above: the "budget" is decreased only once per generated message,
+ * regardless how many busses/direct connections it is enqueued on, while the "threshold" is applied to
+ * each queued instance of bus message, i.e. if the same message is enqueued to five busses/direct
+ * connections it will be counted five times. This difference in counting ("references"
+ * vs. "instances") is primarily a result of the fact that it's easier to implement it this way,
+ * however it also reflects the thinking that the "threshold" should put a limit on used queue memory,
+ * i.e. space, while the "budget" should put a limit on time. Also note that the "threshold" is
+ * currently chosen much higher than the "budget". */
+ budget = MANAGER_BUS_MESSAGE_BUDGET;
+ }
- while (budget > 0 && (u = m->dbus_unit_queue)) {
+ while (budget != 0 && (u = m->dbus_unit_queue)) {
assert(u->in_dbus_queue);
bus_unit_send_change_signal(u);
- n++, budget--;
+ n++;
+
+ if (budget != (unsigned) -1)
+ budget--;
}
- while (budget > 0 && (j = m->dbus_job_queue)) {
+ while (budget != 0 && (j = m->dbus_job_queue)) {
assert(j->in_dbus_queue);
bus_job_send_change_signal(j);
- n++, budget--;
- }
+ n++;
- m->dispatching_dbus_queue = false;
+ if (budget != (unsigned) -1)
+ budget--;
+ }
- if (budget > 0 && m->send_reloading_done) {
+ if (m->send_reloading_done) {
m->send_reloading_done = false;
bus_manager_send_reloading(m, false);
- n++, budget--;
+ n++;
}
- if (budget > 0 && m->queued_message) {
- bus_send_queued_message(m);
+ if (m->pending_reload_message) {
+ bus_send_pending_reload_message(m);
n++;
}
@@ -2133,7 +2222,7 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) {
static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
- char buf[PATH_MAX+1];
+ char buf[PATH_MAX];
ssize_t n;
n = recv(fd, buf, sizeof(buf), 0);
@@ -2461,7 +2550,7 @@ static void manager_handle_ctrl_alt_del(Manager *m) {
if (ratelimit_below(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == EMERGENCY_ACTION_NONE)
manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
else
- emergency_action(m, m->cad_burst_action, NULL,
+ emergency_action(m, m->cad_burst_action, EMERGENCY_ACTION_WARN, NULL, -1,
"Ctrl-Alt-Del was pressed more than 7 times within 2s");
}
@@ -2511,9 +2600,10 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
case SIGTERM:
if (MANAGER_IS_SYSTEM(m)) {
/* This is for compatibility with the original sysvinit */
- r = verify_run_space_and_log("Refusing to reexecute");
- if (r >= 0)
- m->exit_code = MANAGER_REEXECUTE;
+ if (verify_run_space_and_log("Refusing to reexecute") < 0)
+ break;
+
+ m->objective = MANAGER_REEXECUTE;
break;
}
@@ -2569,9 +2659,10 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
}
case SIGHUP:
- r = verify_run_space_and_log("Refusing to reload");
- if (r >= 0)
- m->exit_code = MANAGER_RELOAD;
+ if (verify_run_space_and_log("Refusing to reload") < 0)
+ break;
+
+ m->objective = MANAGER_RELOAD;
break;
default: {
@@ -2591,7 +2682,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
};
/* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */
- static const ManagerExitCode code_table[] = {
+ static const ManagerObjective objective_table[] = {
[0] = MANAGER_HALT,
[1] = MANAGER_POWEROFF,
[2] = MANAGER_REBOOT,
@@ -2607,8 +2698,8 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
}
if ((int) sfsi.ssi_signo >= SIGRTMIN+13 &&
- (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) {
- m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13];
+ (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(objective_table)) {
+ m->objective = objective_table[sfsi.ssi_signo - SIGRTMIN - 13];
break;
}
@@ -2632,7 +2723,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
case 24:
if (MANAGER_IS_USER(m)) {
- m->exit_code = MANAGER_EXIT;
+ m->objective = MANAGER_EXIT;
return 0;
}
@@ -2697,10 +2788,8 @@ static int manager_dispatch_timezone_change(
log_debug("inotify event for /etc/localtime");
changed = manager_read_timezone_stat(m);
- if (changed < 0)
+ if (changed <= 0)
return changed;
- if (!changed)
- return 0;
/* Something changed, restart the watch, to ensure we watch the new /etc/localtime if it changed */
(void) manager_setup_timezone_change(m);
@@ -2761,7 +2850,7 @@ int manager_loop(Manager *m) {
RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 50000);
assert(m);
- m->exit_code = MANAGER_OK;
+ assert(m->objective == MANAGER_OK); /* Ensure manager_startup() has been called */
/* Release the path cache */
m->unit_path_cache = set_free_free(m->unit_path_cache);
@@ -2773,7 +2862,7 @@ int manager_loop(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to enable SIGCHLD event source: %m");
- while (m->exit_code == MANAGER_OK) {
+ while (m->objective == MANAGER_OK) {
usec_t wait_usec;
if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m))
@@ -2800,6 +2889,9 @@ int manager_loop(Manager *m) {
if (manager_dispatch_cgroup_realize_queue(m) > 0)
continue;
+ if (manager_dispatch_stop_when_unneeded_queue(m) > 0)
+ continue;
+
if (manager_dispatch_dbus_queue(m) > 0)
continue;
@@ -2816,7 +2908,7 @@ int manager_loop(Manager *m) {
return log_error_errno(r, "Failed to run event loop: %m");
}
- return m->exit_code;
+ return m->objective;
}
int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u) {
@@ -2998,7 +3090,25 @@ int manager_open_serialization(Manager *m, FILE **_f) {
return 0;
}
-int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
+static bool manager_timestamp_shall_serialize(ManagerTimestamp t) {
+
+ if (!in_initrd())
+ return true;
+
+ /* The following timestamps only apply to the host system, hence only serialize them there */
+ return !IN_SET(t,
+ MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH,
+ MANAGER_TIMESTAMP_SECURITY_START, MANAGER_TIMESTAMP_SECURITY_FINISH,
+ MANAGER_TIMESTAMP_GENERATORS_START, MANAGER_TIMESTAMP_GENERATORS_FINISH,
+ MANAGER_TIMESTAMP_UNITS_LOAD_START, MANAGER_TIMESTAMP_UNITS_LOAD_FINISH);
+}
+
+int manager_serialize(
+ Manager *m,
+ FILE *f,
+ FDSet *fds,
+ bool switching_root) {
+
ManagerTimestamp q;
const char *t;
Iterator i;
@@ -3009,56 +3119,53 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
assert(f);
assert(fds);
- m->n_reloading++;
+ _cleanup_(manager_reloading_stopp) _unused_ Manager *reloading = manager_reloading_start(m);
- fprintf(f, "current-job-id=%"PRIu32"\n", m->current_job_id);
- fprintf(f, "n-installed-jobs=%u\n", m->n_installed_jobs);
- fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs);
- fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
- fprintf(f, "ready-sent=%s\n", yes_no(m->ready_sent));
- fprintf(f, "taint-logged=%s\n", yes_no(m->taint_logged));
- fprintf(f, "service-watchdogs=%s\n", yes_no(m->service_watchdogs));
+ (void) serialize_item_format(f, "current-job-id", "%" PRIu32, m->current_job_id);
+ (void) serialize_item_format(f, "n-installed-jobs", "%u", m->n_installed_jobs);
+ (void) serialize_item_format(f, "n-failed-jobs", "%u", m->n_failed_jobs);
+ (void) serialize_bool(f, "taint-usr", m->taint_usr);
+ (void) serialize_bool(f, "ready-sent", m->ready_sent);
+ (void) serialize_bool(f, "taint-logged", m->taint_logged);
+ (void) serialize_bool(f, "service-watchdogs", m->service_watchdogs);
+
+ t = show_status_to_string(m->show_status);
+ if (t)
+ (void) serialize_item(f, "show-status", t);
if (m->log_level_overridden)
- fprintf(f, "log-level-override=%i\n", log_get_max_level());
+ (void) serialize_item_format(f, "log-level-override", "%i", log_get_max_level());
if (m->log_target_overridden)
- fprintf(f, "log-target-override=%s\n", log_target_to_string(log_get_target()));
+ (void) serialize_item(f, "log-target-override", log_target_to_string(log_get_target()));
for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
- /* The userspace and finish timestamps only apply to the host system, hence only serialize them there */
- if (in_initrd() && IN_SET(q, MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH))
+ _cleanup_free_ char *joined = NULL;
+
+ if (!manager_timestamp_shall_serialize(q))
continue;
- t = manager_timestamp_to_string(q);
- {
- char field[strlen(t) + STRLEN("-timestamp") + 1];
- strcpy(stpcpy(field, t), "-timestamp");
- dual_timestamp_serialize(f, field, m->timestamps + q);
- }
+ joined = strjoin(manager_timestamp_to_string(q), "-timestamp");
+ if (!joined)
+ return log_oom();
+
+ (void) serialize_dual_timestamp(f, joined, m->timestamps + q);
}
if (!switching_root)
- (void) serialize_environment(f, m->environment);
+ (void) serialize_strv(f, "env", m->client_environment);
if (m->notify_fd >= 0) {
- int copy;
-
- copy = fdset_put_dup(fds, m->notify_fd);
- if (copy < 0)
- return copy;
+ r = serialize_fd(f, fds, "notify-fd", m->notify_fd);
+ if (r < 0)
+ return r;
- fprintf(f, "notify-fd=%i\n", copy);
- fprintf(f, "notify-socket=%s\n", m->notify_socket);
+ (void) serialize_item(f, "notify-socket", m->notify_socket);
}
if (m->cgroups_agent_fd >= 0) {
- int copy;
-
- copy = fdset_put_dup(fds, m->cgroups_agent_fd);
- if (copy < 0)
- return copy;
-
- fprintf(f, "cgroups-agent-fd=%i\n", copy);
+ r = serialize_fd(f, fds, "cgroups-agent-fd", m->cgroups_agent_fd);
+ if (r < 0)
+ return r;
}
if (m->user_lookup_fds[0] >= 0) {
@@ -3066,13 +3173,13 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
copy0 = fdset_put_dup(fds, m->user_lookup_fds[0]);
if (copy0 < 0)
- return copy0;
+ return log_error_errno(copy0, "Failed to add user lookup fd to serialization: %m");
copy1 = fdset_put_dup(fds, m->user_lookup_fds[1]);
if (copy1 < 0)
- return copy1;
+ return log_error_errno(copy1, "Failed to add user lookup fd to serialization: %m");
- fprintf(f, "user-lookup=%i %i\n", copy0, copy1);
+ (void) serialize_item_format(f, "user-lookup", "%i %i", copy0, copy1);
}
bus_track_serialize(m->subscribed, f, "subscribed");
@@ -3099,22 +3206,66 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
fputc('\n', f);
r = unit_serialize(u, f, fds, !switching_root);
- if (r < 0) {
- m->n_reloading--;
+ if (r < 0)
return r;
- }
}
- assert(m->n_reloading > 0);
- m->n_reloading--;
-
r = fflush_and_check(f);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to flush serialization: %m");
r = bus_fdset_add_all(m, fds);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add bus sockets to serialization: %m");
+
+ return 0;
+}
+
+static int manager_deserialize_one_unit(Manager *m, const char *name, FILE *f, FDSet *fds) {
+ Unit *u;
+ int r;
+
+ r = manager_load_unit(m, name, NULL, NULL, &u);
+ if (r < 0) {
+ if (r == -ENOMEM)
+ return r;
+ return log_notice_errno(r, "Failed to load unit \"%s\", skipping deserialization: %m", name);
+ }
+
+ r = unit_deserialize(u, f, fds);
+ if (r < 0) {
+ if (r == -ENOMEM)
+ return r;
+ return log_notice_errno(r, "Failed to deserialize unit \"%s\", skipping: %m", name);
+ }
+
+ return 0;
+}
+
+static int manager_deserialize_units(Manager *m, FILE *f, FDSet *fds) {
+ _cleanup_free_ char *line = NULL;
+ const char *unit_name;
+ int r;
+
+ for (;;) {
+ /* Start marker */
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read serialization line: %m");
+ if (r == 0)
+ break;
+
+ unit_name = strstrip(line);
+
+ r = manager_deserialize_one_unit(m, unit_name, f, fds);
+ if (r == -ENOMEM)
+ return r;
+ if (r < 0) {
+ r = unit_deserialize_skip(f);
+ if (r < 0)
+ return r;
+ }
+ }
return 0;
}
@@ -3127,32 +3278,30 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
log_debug("Deserializing state...");
- m->n_reloading++;
+ /* If we are not in reload mode yet, enter it now. Not that this is recursive, a caller might already have
+ * increased it to non-zero, which is why we just increase it by one here and down again at the end of this
+ * call. */
+ _cleanup_(manager_reloading_stopp) _unused_ Manager *reloading = manager_reloading_start(m);
for (;;) {
- char line[LINE_MAX];
+ _cleanup_free_ char *line = NULL;
const char *val, *l;
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- r = 0;
- else
- r = -errno;
-
- goto finish;
- }
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read serialization line: %m");
+ if (r == 0)
+ break;
- char_array_0(line);
l = strstrip(line);
-
- if (l[0] == 0)
+ if (isempty(l)) /* end marker */
break;
if ((val = startswith(l, "current-job-id="))) {
uint32_t id;
if (safe_atou32(val, &id) < 0)
- log_notice("Failed to parse current job id value %s", val);
+ log_notice("Failed to parse current job id value '%s', ignoring.", val);
else
m->current_job_id = MAX(m->current_job_id, id);
@@ -3160,7 +3309,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
uint32_t n;
if (safe_atou32(val, &n) < 0)
- log_notice("Failed to parse installed jobs counter %s", val);
+ log_notice("Failed to parse installed jobs counter '%s', ignoring.", val);
else
m->n_installed_jobs += n;
@@ -3168,7 +3317,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
uint32_t n;
if (safe_atou32(val, &n) < 0)
- log_notice("Failed to parse failed jobs counter %s", val);
+ log_notice("Failed to parse failed jobs counter '%s', ignoring.", val);
else
m->n_failed_jobs += n;
@@ -3177,7 +3326,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
b = parse_boolean(val);
if (b < 0)
- log_notice("Failed to parse taint /usr flag %s", val);
+ log_notice("Failed to parse taint /usr flag '%s', ignoring.", val);
else
m->taint_usr = m->taint_usr || b;
@@ -3186,7 +3335,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
b = parse_boolean(val);
if (b < 0)
- log_notice("Failed to parse ready-sent flag %s", val);
+ log_notice("Failed to parse ready-sent flag '%s', ignoring.", val);
else
m->ready_sent = m->ready_sent || b;
@@ -3195,7 +3344,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
b = parse_boolean(val);
if (b < 0)
- log_notice("Failed to parse taint-logged flag %s", val);
+ log_notice("Failed to parse taint-logged flag '%s', ignoring.", val);
else
m->taint_logged = m->taint_logged || b;
@@ -3204,10 +3353,19 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
b = parse_boolean(val);
if (b < 0)
- log_notice("Failed to parse service-watchdogs flag %s", val);
+ log_notice("Failed to parse service-watchdogs flag '%s', ignoring.", val);
else
m->service_watchdogs = b;
+ } else if ((val = startswith(l, "show-status="))) {
+ ShowStatus s;
+
+ s = show_status_from_string(val);
+ if (s < 0)
+ log_notice("Failed to parse show-status flag '%s', ignoring.", val);
+ else
+ manager_set_show_status(m, s);
+
} else if ((val = startswith(l, "log-level-override="))) {
int level;
@@ -3227,17 +3385,15 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
manager_override_log_target(m, target);
} else if (startswith(l, "env=")) {
- r = deserialize_environment(&m->environment, l);
- if (r == -ENOMEM)
- goto finish;
+ r = deserialize_environment(l + 4, &m->client_environment);
if (r < 0)
- log_notice_errno(r, "Failed to parse environment entry: \"%s\": %m", l);
+ log_notice_errno(r, "Failed to parse environment entry: \"%s\", ignoring: %m", l);
} else if ((val = startswith(l, "notify-fd="))) {
int fd;
if (safe_atoi(val, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_notice("Failed to parse notify fd: \"%s\"", val);
+ log_notice("Failed to parse notify fd, ignoring: \"%s\"", val);
else {
m->notify_event_source = sd_event_source_unref(m->notify_event_source);
safe_close(m->notify_fd);
@@ -3245,22 +3401,15 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
}
} else if ((val = startswith(l, "notify-socket="))) {
- char *n;
-
- n = strdup(val);
- if (!n) {
- r = -ENOMEM;
- goto finish;
- }
-
- free(m->notify_socket);
- m->notify_socket = n;
+ r = free_and_strdup(&m->notify_socket, val);
+ if (r < 0)
+ return r;
} else if ((val = startswith(l, "cgroups-agent-fd="))) {
int fd;
if (safe_atoi(val, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_notice("Failed to parse cgroups agent fd: %s", val);
+ log_notice("Failed to parse cgroups agent fd, ignoring.: %s", val);
else {
m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source);
safe_close(m->cgroups_agent_fd);
@@ -3271,7 +3420,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
int fd0, fd1;
if (sscanf(val, "%i %i", &fd0, &fd1) != 2 || fd0 < 0 || fd1 < 0 || fd0 == fd1 || !fdset_contains(fds, fd0) || !fdset_contains(fds, fd1))
- log_notice("Failed to parse user lookup fd: %s", val);
+ log_notice("Failed to parse user lookup fd, ignoring: %s", val);
else {
m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source);
safe_close_pair(m->user_lookup_fds);
@@ -3290,7 +3439,8 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
else if ((val = startswith(l, "subscribed="))) {
if (strv_extend(&m->deserialized_subscribed, val) < 0)
- log_oom();
+ return -ENOMEM;
+
} else {
ManagerTimestamp q;
@@ -3305,100 +3455,50 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
}
if (q < _MANAGER_TIMESTAMP_MAX) /* found it */
- dual_timestamp_deserialize(val, m->timestamps + q);
+ (void) deserialize_dual_timestamp(val, m->timestamps + q);
else if (!startswith(l, "kdbus-fd=")) /* ignore kdbus */
- log_notice("Unknown serialization item '%s'", l);
- }
- }
-
- for (;;) {
- Unit *u;
- char name[UNIT_NAME_MAX+2];
- const char* unit_name;
-
- /* Start marker */
- if (!fgets(name, sizeof(name), f)) {
- if (feof(f))
- r = 0;
- else
- r = -errno;
-
- goto finish;
- }
-
- char_array_0(name);
- unit_name = strstrip(name);
-
- r = manager_load_unit(m, unit_name, NULL, NULL, &u);
- if (r < 0) {
- log_notice_errno(r, "Failed to load unit \"%s\", skipping deserialization: %m", unit_name);
- if (r == -ENOMEM)
- goto finish;
- unit_deserialize_skip(f);
- continue;
- }
-
- r = unit_deserialize(u, f, fds);
- if (r < 0) {
- log_notice_errno(r, "Failed to deserialize unit \"%s\": %m", unit_name);
- if (r == -ENOMEM)
- goto finish;
+ log_notice("Unknown serialization item '%s', ignoring.", l);
}
}
-finish:
- if (ferror(f))
- r = -EIO;
-
- assert(m->n_reloading > 0);
- m->n_reloading--;
-
- return r;
-}
-
-static void manager_flush_finished_jobs(Manager *m) {
- Job *j;
-
- while ((j = set_steal_first(m->pending_finished_jobs))) {
- bus_job_send_removed_signal(j);
- job_free(j);
- }
-
- m->pending_finished_jobs = set_free(m->pending_finished_jobs);
+ return manager_deserialize_units(m, f, fds);
}
int manager_reload(Manager *m) {
- int r, q;
- _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(manager_reloading_stopp) Manager *reloading = NULL;
_cleanup_fdset_free_ FDSet *fds = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
assert(m);
r = manager_open_serialization(m, &f);
if (r < 0)
- return r;
-
- m->n_reloading++;
- bus_manager_send_reloading(m, true);
+ return log_error_errno(r, "Failed to create serialization file: %m");
fds = fdset_new();
- if (!fds) {
- m->n_reloading--;
- return -ENOMEM;
- }
+ if (!fds)
+ return log_oom();
+
+ /* We are officially in reload mode from here on. */
+ reloading = manager_reloading_start(m);
r = manager_serialize(m, f, fds, false);
- if (r < 0) {
- m->n_reloading--;
+ if (r < 0)
return r;
- }
- if (fseeko(f, 0, SEEK_SET) < 0) {
- m->n_reloading--;
- return -errno;
- }
+ if (fseeko(f, 0, SEEK_SET) < 0)
+ return log_error_errno(errno, "Failed to seek to beginning of serialization: %m");
+
+ /* 💀 This is the point of no return, from here on there is no way back. 💀 */
+ reloading = NULL;
+
+ bus_manager_send_reloading(m, true);
+
+ /* Start by flushing out all jobs and units, all generated units, all runtime environments, all dynamic users
+ * and everything else that is worth flushing out. We'll get it all back from the serialization — if we need
+ * it.*/
- /* From here on there is no way back. */
manager_clear_jobs_and_units(m);
lookup_paths_flush_generator(&m->lookup_paths);
lookup_paths_free(&m->lookup_paths);
@@ -3407,82 +3507,50 @@ int manager_reload(Manager *m) {
m->uid_refs = hashmap_free(m->uid_refs);
m->gid_refs = hashmap_free(m->gid_refs);
- q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
- if (q < 0 && r >= 0)
- r = q;
+ r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to initialize path lookup table, ignoring: %m");
- q = manager_run_environment_generators(m);
- if (q < 0 && r >= 0)
- r = q;
+ (void) manager_run_environment_generators(m);
+ (void) manager_run_generators(m);
- /* Find new unit paths */
- q = manager_run_generators(m);
- if (q < 0 && r >= 0)
- r = q;
+ r = lookup_paths_reduce(&m->lookup_paths);
+ if (r < 0)
+ log_warning_errno(r, "Failed ot reduce unit file paths, ignoring: %m");
- lookup_paths_reduce(&m->lookup_paths);
manager_build_unit_path_cache(m);
- /* First, enumerate what we can from all config files */
+ /* First, enumerate what we can from kernel and suchlike */
+ manager_enumerate_perpetual(m);
manager_enumerate(m);
/* Second, deserialize our stored data */
- q = manager_deserialize(m, f, fds);
- if (q < 0) {
- log_error_errno(q, "Deserialization failed: %m");
-
- if (r >= 0)
- r = q;
- }
+ r = manager_deserialize(m, f, fds);
+ if (r < 0)
+ log_warning_errno(r, "Deserialization failed, proceeding anyway: %m");
+ /* We don't need the serialization anymore */
f = safe_fclose(f);
- /* Re-register notify_fd as event source */
- q = manager_setup_notify(m);
- if (q < 0 && r >= 0)
- r = q;
-
- q = manager_setup_cgroups_agent(m);
- if (q < 0 && r >= 0)
- r = q;
-
- q = manager_setup_user_lookup_fd(m);
- if (q < 0 && r >= 0)
- r = q;
+ /* Re-register notify_fd as event source, and set up other sockets/communication channels we might need */
+ (void) manager_setup_notify(m);
+ (void) manager_setup_cgroups_agent(m);
+ (void) manager_setup_user_lookup_fd(m);
/* Third, fire things up! */
manager_coldplug(m);
- /* Release any dynamic users no longer referenced */
- dynamic_user_vacuum(m, true);
-
- /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
- manager_vacuum_uid_refs(m);
- manager_vacuum_gid_refs(m);
-
- exec_runtime_vacuum(m);
+ /* Clean up runtime objects no longer referenced */
+ manager_vacuum(m);
+ /* Consider the reload process complete now. */
assert(m->n_reloading > 0);
m->n_reloading--;
- /* It might be safe to log to the journal now and connect to dbus */
- manager_recheck_journal(m);
- manager_recheck_dbus(m);
-
- /* Let's finally catch up with any changes that took place while we were reloading/reexecing */
- manager_catchup(m);
-
- /* Sync current state of bus names with our set of listening units */
- q = manager_enqueue_sync_bus_names(m);
- if (q < 0 && r >= 0)
- r = q;
-
- if (!MANAGER_IS_RELOADING(m))
- manager_flush_finished_jobs(m);
+ manager_ready(m);
m->send_reloading_done = true;
-
- return r;
+ return 0;
}
void manager_reset_failed(Manager *m) {
@@ -3533,7 +3601,7 @@ static void manager_notify_finished(Manager *m) {
char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec;
- if (m->test_run_flags)
+ if (MANAGER_IS_TEST_RUN(m))
return;
if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
@@ -3726,9 +3794,14 @@ static const char* user_env_generator_binary_paths[] = {
static int manager_run_environment_generators(Manager *m) {
char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
const char **paths;
- void* args[] = {&tmp, &tmp, &m->environment};
+ void* args[] = {
+ [STDOUT_GENERATE] = &tmp,
+ [STDOUT_COLLECT] = &tmp,
+ [STDOUT_CONSUME] = &m->transient_environment,
+ };
+ int r;
- if (m->test_run_flags && !(m->test_run_flags & MANAGER_TEST_RUN_ENV_GENERATORS))
+ 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;
@@ -3736,7 +3809,10 @@ static int manager_run_environment_generators(Manager *m) {
if (!generator_path_any(paths))
return 0;
- return execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
+ RUN_WITH_UMASK(0022)
+ r = execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, m->transient_environment);
+
+ return r;
}
static int manager_run_generators(Manager *m) {
@@ -3746,7 +3822,7 @@ static int manager_run_generators(Manager *m) {
assert(m);
- if (m->test_run_flags && !(m->test_run_flags & MANAGER_TEST_RUN_GENERATORS))
+ if (MANAGER_IS_TEST_RUN(m) && !(m->test_run_flags & MANAGER_TEST_RUN_GENERATORS))
return 0;
paths = generator_binary_paths(m->unit_file_scope);
@@ -3757,8 +3833,10 @@ static int manager_run_generators(Manager *m) {
return 0;
r = lookup_paths_mkdir_generator(&m->lookup_paths);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to create generator directories: %m");
goto finish;
+ }
argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */
argv[1] = m->lookup_paths.generator;
@@ -3767,19 +3845,46 @@ static int manager_run_generators(Manager *m) {
argv[4] = NULL;
RUN_WITH_UMASK(0022)
- execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC,
- NULL, NULL, (char**) argv);
+ (void) execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC,
+ NULL, NULL, (char**) argv, m->transient_environment);
+
+ r = 0;
finish:
lookup_paths_trim_generator(&m->lookup_paths);
return r;
}
-int manager_environment_add(Manager *m, char **minus, char **plus) {
+int manager_transient_environment_add(Manager *m, char **plus) {
+ char **a;
+
+ assert(m);
+
+ if (strv_isempty(plus))
+ return 0;
+
+ a = strv_env_merge(2, m->transient_environment, plus);
+ if (!a)
+ return log_oom();
+
+ sanitize_environment(a);
+
+ return strv_free_and_replace(m->transient_environment, a);
+}
+
+int manager_client_environment_modify(
+ Manager *m,
+ char **minus,
+ char **plus) {
+
char **a = NULL, **b = NULL, **l;
+
assert(m);
- l = m->environment;
+ if (strv_isempty(minus) && strv_isempty(plus))
+ return 0;
+
+ l = m->client_environment;
if (!strv_isempty(minus)) {
a = strv_env_delete(l, 1, minus);
@@ -3799,16 +3904,29 @@ int manager_environment_add(Manager *m, char **minus, char **plus) {
l = b;
}
- if (m->environment != l)
- strv_free(m->environment);
+ if (m->client_environment != l)
+ strv_free(m->client_environment);
+
if (a != l)
strv_free(a);
if (b != l)
strv_free(b);
- m->environment = l;
- manager_sanitize_environment(m);
+ m->client_environment = sanitize_environment(l);
+ return 0;
+}
+
+int manager_get_effective_environment(Manager *m, char ***ret) {
+ char **l;
+
+ assert(m);
+ assert(ret);
+
+ l = strv_env_merge(2, m->transient_environment, m->client_environment);
+ if (!l)
+ return -ENOMEM;
+ *ret = l;
return 0;
}
@@ -3860,7 +3978,7 @@ static bool manager_journal_is_running(Manager *m) {
assert(m);
- if (m->test_run_flags != 0)
+ if (MANAGER_IS_TEST_RUN(m))
return false;
/* If we are the user manager we can safely assume that the journal is up */
@@ -3915,7 +4033,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
mode == SHOW_STATUS_NO ? "Disabling" : "Enabling");
m->show_status = mode;
- if (mode > 0)
+ if (IN_SET(mode, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES))
(void) touch("/run/systemd/show-status");
else
(void) unlink("/run/systemd/show-status");
@@ -3937,7 +4055,7 @@ static bool manager_get_show_status(Manager *m, StatusType type) {
if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
return false;
- return m->show_status > 0;
+ return IN_SET(m->show_status, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
}
const char *manager_get_confirm_spawn(Manager *m) {
@@ -4024,7 +4142,7 @@ void manager_status_printf(Manager *m, StatusType type, const char *status, cons
return;
va_start(ap, format);
- status_vprintf(status, true, type == STATUS_TYPE_EPHEMERAL, format, ap);
+ status_vprintf(status, SHOW_STATUS_ELLIPSIZE|(type == STATUS_TYPE_EPHEMERAL ? SHOW_STATUS_EPHEMERAL : 0), format, ap);
va_end(ap);
}
@@ -4278,7 +4396,7 @@ static void manager_serialize_uid_refs_internal(
if (!(c & DESTROY_IPC_FLAG))
continue;
- fprintf(f, "%s=" UID_FMT "\n", field_name, uid);
+ (void) serialize_item_format(f, field_name, UID_FMT, uid);
}
}
@@ -4323,7 +4441,7 @@ static void manager_deserialize_uid_refs_one_internal(
r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
if (r < 0) {
- log_debug("Failed to add UID reference entry");
+ log_debug_errno(r, "Failed to add UID reference entry: %m");
return;
}
}
@@ -4517,6 +4635,14 @@ void manager_restore_original_log_target(Manager *m) {
m->log_target_overridden = false;
}
+ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s) {
+ if (in_initrd() &&
+ s >= MANAGER_TIMESTAMP_SECURITY_START &&
+ s <= MANAGER_TIMESTAMP_UNITS_LOAD_FINISH)
+ return s - MANAGER_TIMESTAMP_SECURITY_START + MANAGER_TIMESTAMP_INITRD_SECURITY_START;
+ return s;
+}
+
static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
[MANAGER_INITIALIZING] = "initializing",
[MANAGER_STARTING] = "starting",
@@ -4541,6 +4667,12 @@ static const char *const manager_timestamp_table[_MANAGER_TIMESTAMP_MAX] = {
[MANAGER_TIMESTAMP_GENERATORS_FINISH] = "generators-finish",
[MANAGER_TIMESTAMP_UNITS_LOAD_START] = "units-load-start",
[MANAGER_TIMESTAMP_UNITS_LOAD_FINISH] = "units-load-finish",
+ [MANAGER_TIMESTAMP_INITRD_SECURITY_START] = "initrd-security-start",
+ [MANAGER_TIMESTAMP_INITRD_SECURITY_FINISH] = "initrd-security-finish",
+ [MANAGER_TIMESTAMP_INITRD_GENERATORS_START] = "initrd-generators-start",
+ [MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH] = "initrd-generators-finish",
+ [MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START] = "initrd-units-load-start",
+ [MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH] = "initrd-units-load-finish",
};
DEFINE_STRING_TABLE_LOOKUP(manager_timestamp, ManagerTimestamp);
diff --git a/src/core/manager.h b/src/core/manager.h
index ea5d425030..bce8020cfd 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -5,6 +5,7 @@
#include <stdio.h>
#include "sd-bus.h"
+#include "sd-device.h"
#include "sd-event.h"
#include "cgroup-util.h"
@@ -22,6 +23,8 @@ typedef struct Unit Unit;
typedef struct Manager Manager;
+/* An externally visible state. We don't actually maintain this as state variable, but derive it from various fields
+ * when requested */
typedef enum ManagerState {
MANAGER_INITIALIZING,
MANAGER_STARTING,
@@ -33,7 +36,7 @@ typedef enum ManagerState {
_MANAGER_STATE_INVALID = -1
} ManagerState;
-typedef enum ManagerExitCode {
+typedef enum ManagerObjective {
MANAGER_OK,
MANAGER_EXIT,
MANAGER_RELOAD,
@@ -43,9 +46,9 @@ typedef enum ManagerExitCode {
MANAGER_HALT,
MANAGER_KEXEC,
MANAGER_SWITCH_ROOT,
- _MANAGER_EXIT_CODE_MAX,
- _MANAGER_EXIT_CODE_INVALID = -1
-} ManagerExitCode;
+ _MANAGER_OBJECTIVE_MAX,
+ _MANAGER_OBJECTIVE_INVALID = -1
+} ManagerObjective;
typedef enum StatusType {
STATUS_TYPE_EPHEMERAL,
@@ -53,6 +56,27 @@ typedef enum StatusType {
STATUS_TYPE_EMERGENCY,
} StatusType;
+/* Notes:
+ * 1. TIMESTAMP_FIRMWARE, TIMESTAMP_LOADER, TIMESTAMP_KERNEL, TIMESTAMP_INITRD,
+ * TIMESTAMP_SECURITY_START, and TIMESTAMP_SECURITY_FINISH are set only when
+ * the manager is system and not running under container environment.
+ *
+ * 2. The monotonic timestamp of TIMESTAMP_KERNEL is always zero.
+ *
+ * 3. The realtime timestamp of TIMESTAMP_KERNEL will be unset if the system does not
+ * have RTC.
+ *
+ * 4. TIMESTAMP_FIRMWARE and TIMESTAMP_LOADER will be unset if the system does not
+ * have RTC, or systemd is built without EFI support.
+ *
+ * 5. The monotonic timestamps of TIMESTAMP_FIRMWARE and TIMESTAMP_LOADER are stored as
+ * negative of the actual value.
+ *
+ * 6. TIMESTAMP_USERSPACE is the timestamp of when the manager was started.
+ *
+ * 7. TIMESTAMP_INITRD_* are set only when the system is booted with an initrd.
+ */
+
typedef enum ManagerTimestamp {
MANAGER_TIMESTAMP_FIRMWARE,
MANAGER_TIMESTAMP_LOADER,
@@ -67,6 +91,13 @@ typedef enum ManagerTimestamp {
MANAGER_TIMESTAMP_GENERATORS_FINISH,
MANAGER_TIMESTAMP_UNITS_LOAD_START,
MANAGER_TIMESTAMP_UNITS_LOAD_FINISH,
+
+ MANAGER_TIMESTAMP_INITRD_SECURITY_START,
+ MANAGER_TIMESTAMP_INITRD_SECURITY_FINISH,
+ MANAGER_TIMESTAMP_INITRD_GENERATORS_START,
+ MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH,
+ MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START,
+ MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH,
_MANAGER_TIMESTAMP_MAX,
_MANAGER_TIMESTAMP_INVALID = -1,
} ManagerTimestamp;
@@ -77,14 +108,15 @@ typedef enum ManagerTimestamp {
#include "show-status.h"
#include "unit-name.h"
-enum {
- /* 0 = run normally */
- MANAGER_TEST_RUN_MINIMAL = 1 << 1, /* create basic data structures */
- MANAGER_TEST_RUN_BASIC = 1 << 2, /* interact with the environment */
- MANAGER_TEST_RUN_ENV_GENERATORS = 1 << 3, /* also run env generators */
- MANAGER_TEST_RUN_GENERATORS = 1 << 4, /* also run unit generators */
+typedef enum ManagerTestRunFlags {
+ MANAGER_TEST_NORMAL = 0, /* run normally */
+ MANAGER_TEST_RUN_MINIMAL = 1 << 0, /* create basic data structures */
+ MANAGER_TEST_RUN_BASIC = 1 << 1, /* interact with the environment */
+ MANAGER_TEST_RUN_ENV_GENERATORS = 1 << 2, /* also run env generators */
+ MANAGER_TEST_RUN_GENERATORS = 1 << 3, /* also run unit generators */
MANAGER_TEST_FULL = MANAGER_TEST_RUN_BASIC | MANAGER_TEST_RUN_ENV_GENERATORS | MANAGER_TEST_RUN_GENERATORS,
-};
+} ManagerTestRunFlags;
+
assert_cc((MANAGER_TEST_FULL & UINT8_MAX) == MANAGER_TEST_FULL);
struct Manager {
@@ -130,6 +162,9 @@ struct Manager {
/* Target units whose default target dependencies haven't been set yet */
LIST_HEAD(Unit, target_deps_queue);
+ /* Units that might be subject to StopWhenUnneeded= clean-up */
+ LIST_HEAD(Unit, stop_when_unneeded_queue);
+
sd_event *event;
/* This maps PIDs we care about to units that are interested in. We allow multiple units to he interested in
@@ -177,18 +212,16 @@ struct Manager {
LookupPaths lookup_paths;
Set *unit_path_cache;
- char **environment;
+ char **transient_environment; /* The environment, as determined from config files, kernel cmdline and environment generators */
+ char **client_environment; /* Environment variables created by clients through the bus API */
usec_t runtime_watchdog;
usec_t shutdown_watchdog;
dual_timestamp timestamps[_MANAGER_TIMESTAMP_MAX];
- struct udev* udev;
-
/* Data specific to the device subsystem */
- struct udev_monitor* udev_monitor;
- sd_event_source *udev_event_source;
+ sd_device_monitor *device_monitor;
Hashmap *devices_by_sysfs;
/* Data specific to the mount subsystem */
@@ -215,7 +248,7 @@ struct Manager {
/* This is used during reloading: before the reload we queue
* the reply message here, and afterwards we send it */
- sd_bus_message *queued_message;
+ sd_bus_message *pending_reload_message;
Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */
@@ -250,11 +283,10 @@ struct Manager {
usec_t etc_localtime_mtime;
bool etc_localtime_accessible:1;
- /* Flags */
- ManagerExitCode exit_code:5;
+ ManagerObjective objective:5;
+ /* Flags */
bool dispatching_load_queue:1;
- bool dispatching_dbus_queue:1;
bool taint_usr:1;
@@ -267,7 +299,7 @@ struct Manager {
/* Have we ever changed the "kernel.pid_max" sysctl? */
bool sysctl_pid_max_changed:1;
- unsigned test_run_flags:8;
+ ManagerTestRunFlags test_run_flags:8;
/* If non-zero, exit with the following value when the systemd
* process terminate. Useful for containers: systemd-nspawn could get
@@ -305,9 +337,6 @@ struct Manager {
/* non-zero if we are reloading or reexecuting, */
int n_reloading;
- /* A set which contains all jobs that started before reload and finished
- * during it */
- Set *pending_finished_jobs;
unsigned n_installed_jobs;
unsigned n_failed_jobs;
@@ -375,10 +404,12 @@ struct Manager {
#define MANAGER_IS_FINISHED(m) (dual_timestamp_is_set((m)->timestamps + MANAGER_TIMESTAMP_FINISH))
-/* The exit code is set to OK as soon as we enter the main loop, and set otherwise as soon as we are done with it */
-#define MANAGER_IS_RUNNING(m) ((m)->exit_code == MANAGER_OK)
+/* The objective is set to OK as soon as we enter the main loop, and set otherwise as soon as we are done with it */
+#define MANAGER_IS_RUNNING(m) ((m)->objective == MANAGER_OK)
-int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **m);
+#define MANAGER_IS_TEST_RUN(m) ((m)->test_run_flags != 0)
+
+int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager **m);
Manager* manager_free(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
@@ -408,7 +439,11 @@ void manager_clear_jobs(Manager *m);
unsigned manager_dispatch_load_queue(Manager *m);
-int manager_environment_add(Manager *m, char **minus, char **plus);
+int manager_default_environment(Manager *m);
+int manager_transient_environment_add(Manager *m, char **plus);
+int manager_client_environment_modify(Manager *m, char **minus, char **plus);
+int manager_get_effective_environment(Manager *m, char ***ret);
+
int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit);
int manager_loop(Manager *m);
@@ -479,3 +514,4 @@ void manager_disable_confirm_spawn(void);
const char *manager_timestamp_to_string(ManagerTimestamp m) _const_;
ManagerTimestamp manager_timestamp_from_string(const char *s) _pure_;
+ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s);
diff --git a/src/core/meson.build b/src/core/meson.build
index 3852c5e9d8..450d6f72a9 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -5,6 +5,8 @@ libcore_la_sources = '''
audit-fd.h
automount.c
automount.h
+ bpf-devices.c
+ bpf-devices.h
bpf-firewall.c
bpf-firewall.h
cgroup.c
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 16880e6157..3ce6164b06 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -11,7 +11,9 @@
#include "bus-util.h"
#include "cgroup-util.h"
#include "dev-setup.h"
+#include "dirent-util.h"
#include "efivars.h"
+#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "label.h"
@@ -20,7 +22,7 @@
#include "missing.h"
#include "mkdir.h"
#include "mount-setup.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "set.h"
#include "smack-util.h"
@@ -229,76 +231,105 @@ int mount_setup_early(void) {
return mount_points_setup(N_EARLY_MOUNT, false);
}
-int mount_cgroup_controllers(char ***join_controllers) {
+static const char *join_with(const char *controller) {
+
+ static const char* const pairs[] = {
+ "cpu", "cpuacct",
+ "net_cls", "net_prio",
+ NULL
+ };
+
+ const char *const *x, *const *y;
+
+ assert(controller);
+
+ /* This will lookup which controller to mount another controller with. Input is a controller name, and output
+ * is the other controller name. The function works both ways: you can input one and get the other, and input
+ * the other to get the one. */
+
+ STRV_FOREACH_PAIR(x, y, pairs) {
+ if (streq(controller, *x))
+ return *y;
+ if (streq(controller, *y))
+ return *x;
+ }
+
+ return NULL;
+}
+
+static int symlink_controller(const char *target, const char *alias) {
+ const char *a;
+ int r;
+
+ assert(target);
+ assert(alias);
+
+ a = strjoina("/sys/fs/cgroup/", alias);
+
+ r = symlink_idempotent(target, a, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create symlink %s: %m", a);
+
+#ifdef SMACK_RUN_LABEL
+ const char *p;
+
+ p = strjoina("/sys/fs/cgroup/", target);
+
+ r = mac_smack_copy(a, p);
+ if (r < 0 && r != -EOPNOTSUPP)
+ return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", p, a);
+#endif
+
+ return 0;
+}
+
+int mount_cgroup_controllers(void) {
_cleanup_set_free_free_ Set *controllers = NULL;
- bool has_argument = !!join_controllers;
int r;
if (!cg_is_legacy_wanted())
return 0;
/* Mount all available cgroup controllers that are built into the kernel. */
-
- if (!has_argument)
- /* The defaults:
- * mount "cpu" + "cpuacct" together, and "net_cls" + "net_prio".
- *
- * We'd like to add "cpuset" to the mix, but "cpuset" doesn't really
- * work for groups with no initialized attributes.
- */
- join_controllers = (char**[]) {
- STRV_MAKE("cpu", "cpuacct"),
- STRV_MAKE("net_cls", "net_prio"),
- NULL,
- };
-
r = cg_kernel_controllers(&controllers);
if (r < 0)
return log_error_errno(r, "Failed to enumerate cgroup controllers: %m");
for (;;) {
_cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL;
+ const char *other_controller;
MountPoint p = {
.what = "cgroup",
.type = "cgroup",
.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
.mode = MNT_IN_CONTAINER,
};
- char ***k = NULL;
controller = set_steal_first(controllers);
if (!controller)
break;
- for (k = join_controllers; *k; k++)
- if (strv_find(*k, controller))
- break;
-
- if (*k) {
- char **i, **j;
-
- for (i = *k, j = *k; *i; i++) {
-
- if (!streq(*i, controller)) {
- _cleanup_free_ char *t;
-
- t = set_remove(controllers, *i);
- if (!t) {
- if (has_argument)
- free(*i);
- continue;
- }
- }
-
- *(j++) = *i;
+ /* Check if we shall mount this together with another controller */
+ other_controller = join_with(controller);
+ if (other_controller) {
+ _cleanup_free_ char *c = NULL;
+
+ /* Check if the other controller is actually available in the kernel too */
+ c = set_remove(controllers, other_controller);
+ if (c) {
+
+ /* Join the two controllers into one string, and maintain a stable ordering */
+ if (strcmp(controller, other_controller) < 0)
+ options = strjoin(controller, ",", other_controller);
+ else
+ options = strjoin(other_controller, ",", controller);
+ if (!options)
+ return log_oom();
}
+ }
- *j = NULL;
-
- options = strv_join(*k, ",");
- if (!options)
- return log_oom();
- } else
+ /* The simple case, where there's only one controller to mount together */
+ if (!options)
options = TAKE_PTR(controller);
where = strappend("/sys/fs/cgroup/", options);
@@ -312,35 +343,14 @@ int mount_cgroup_controllers(char ***join_controllers) {
if (r < 0)
return r;
- if (r > 0 && *k) {
- char **i;
-
- for (i = *k; *i; i++) {
- _cleanup_free_ char *t = NULL;
-
- t = strappend("/sys/fs/cgroup/", *i);
- if (!t)
- return log_oom();
-
- r = symlink(options, t);
- if (r >= 0) {
-#ifdef SMACK_RUN_LABEL
- _cleanup_free_ char *src;
- src = strappend("/sys/fs/cgroup/", options);
- if (!src)
- return log_oom();
- r = mac_smack_copy(t, src);
- if (r < 0 && r != -EOPNOTSUPP)
- return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", src, t);
-#endif
- } else if (errno != EEXIST)
- return log_error_errno(errno, "Failed to create symlink %s: %m", t);
- }
- }
+ /* Create symlinks from the individual controller names, in case we have a joined mount */
+ if (controller)
+ (void) symlink_controller(options, controller);
+ if (other_controller)
+ (void) symlink_controller(options, other_controller);
}
- /* Now that we mounted everything, let's make the tmpfs the
- * cgroup file systems are mounted into read-only. */
+ /* Now that we mounted everything, let's make the tmpfs the cgroup file systems are mounted into read-only. */
(void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
return 0;
@@ -396,6 +406,100 @@ static int relabel_cgroup_filesystems(void) {
return 0;
}
+
+static int relabel_extra(void) {
+ _cleanup_closedir_ DIR *d = NULL;
+ int r, c = 0;
+
+ /* Support for relabelling additional files or directories after loading the policy. For this, code in the
+ * initrd simply has to drop in *.relabel files into /run/systemd/relabel-extra.d/. We'll read all such files
+ * expecting one absolute path by line and will relabel each (and everyone below that in case the path refers
+ * to a directory). These drop-in files are supposed to be absolutely minimal, and do not understand comments
+ * and such. After the operation succeeded the files are removed, and the drop-in directory as well, if
+ * possible.
+ */
+
+ d = opendir("/run/systemd/relabel-extra.d/");
+ if (!d) {
+ if (errno == ENOENT)
+ return 0;
+
+ return log_warning_errno(errno, "Failed to open /run/systemd/relabel-extra.d/, ignoring: %m");
+ }
+
+ for (;;) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_close_ int fd = -1;
+ struct dirent *de;
+
+ errno = 0;
+ de = readdir_no_dot(d);
+ if (!de) {
+ if (errno != 0)
+ return log_error_errno(errno, "Failed read directory /run/systemd/relabel-extra.d/, ignoring: %m");
+ break;
+ }
+
+ if (hidden_or_backup_file(de->d_name))
+ continue;
+
+ if (!endswith(de->d_name, ".relabel"))
+ continue;
+
+ if (!IN_SET(de->d_type, DT_REG, DT_UNKNOWN))
+ continue;
+
+ fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ if (fd < 0) {
+ log_warning_errno(errno, "Failed to open /run/systemd/relabel-extra.d/%s, ignoring: %m", de->d_name);
+ continue;
+ }
+
+ f = fdopen(fd, "r");
+ if (!f) {
+ log_warning_errno(errno, "Failed to convert file descriptor into file object, ignoring: %m");
+ continue;
+ }
+ TAKE_FD(fd);
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read from /run/systemd/relabel-extra.d/%s, ignoring: %m", de->d_name);
+ break;
+ }
+ if (r == 0) /* EOF */
+ break;
+
+ path_simplify(line, true);
+
+ if (!path_is_normalized(line)) {
+ log_warning("Path to relabel is not normalized, ignoring: %s", line);
+ continue;
+ }
+
+ if (!path_is_absolute(line)) {
+ log_warning("Path to relabel is not absolute, ignoring: %s", line);
+ continue;
+ }
+
+ log_debug("Relabelling additional file/directory '%s'.", line);
+ (void) nftw(line, nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
+ c++;
+ }
+
+ if (unlinkat(dirfd(d), de->d_name, 0) < 0)
+ log_warning_errno(errno, "Failed to remove /run/systemd/relabel-extra.d/%s, ignoring: %m", de->d_name);
+ }
+
+ /* Remove when we completing things. */
+ if (rmdir("/run/systemd/relabel-extra.d") < 0)
+ log_warning_errno(errno, "Failed to remove /run/systemd/relabel-extra.d/ directory: %m");
+
+ return c;
+}
#endif
int mount_setup(bool loaded_policy) {
@@ -413,20 +517,22 @@ int mount_setup(bool loaded_policy) {
if (loaded_policy) {
usec_t before_relabel, after_relabel;
char timespan[FORMAT_TIMESPAN_MAX];
+ const char *i;
+ int n_extra;
before_relabel = now(CLOCK_MONOTONIC);
- (void) nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
- (void) nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
- (void) nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
+ FOREACH_STRING(i, "/dev", "/dev/shm", "/run")
+ (void) nftw(i, nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
- r = relabel_cgroup_filesystems();
- if (r < 0)
- return r;
+ (void) relabel_cgroup_filesystems();
+
+ n_extra = relabel_extra();
after_relabel = now(CLOCK_MONOTONIC);
- log_info("Relabelled /dev, /run and /sys/fs/cgroup in %s.",
+ log_info("Relabelled /dev, /dev/shm, /run, /sys/fs/cgroup%s in %s.",
+ n_extra > 0 ? ", additional files" : "",
format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0));
}
#endif
@@ -452,20 +558,9 @@ int mount_setup(bool loaded_policy) {
(void) mkdir_label("/run/systemd", 0755);
(void) mkdir_label("/run/systemd/system", 0755);
- /* Set up inaccessible (and empty) file nodes of all types */
- (void) mkdir_label("/run/systemd/inaccessible", 0000);
- (void) mknod("/run/systemd/inaccessible/reg", S_IFREG | 0000, 0);
- (void) mkdir_label("/run/systemd/inaccessible/dir", 0000);
- (void) mkfifo("/run/systemd/inaccessible/fifo", 0000);
- (void) mknod("/run/systemd/inaccessible/sock", S_IFSOCK | 0000, 0);
-
- /* The following two are likely to fail if we lack the privs for it (for example in an userns environment, if
- * CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0 device nodes to be
- * created). But that's entirely fine. Consumers of these files should carry fallback to use a different node
- * then, for example /run/systemd/inaccessible/sock, which is close enough in behaviour and semantics for most
- * uses. */
- (void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0));
- (void) mknod("/run/systemd/inaccessible/blk", S_IFBLK | 0000, makedev(0, 0));
+ /* Also create /run/systemd/inaccessible nodes, so that we always have something to mount inaccessible nodes
+ * from. */
+ (void) make_inaccessible_nodes(NULL, UID_INVALID, GID_INVALID);
return 0;
}
diff --git a/src/core/mount-setup.h b/src/core/mount-setup.h
index 43cd8908de..b4ca2cf4b4 100644
--- a/src/core/mount-setup.h
+++ b/src/core/mount-setup.h
@@ -6,7 +6,7 @@
int mount_setup_early(void);
int mount_setup(bool loaded_policy);
-int mount_cgroup_controllers(char ***join_controllers);
+int mount_cgroup_controllers(void);
bool mount_point_is_api(const char *path);
bool mount_point_ignore(const char *path);
diff --git a/src/core/mount.c b/src/core/mount.c
index 21437dad08..ead9bc1f44 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -11,6 +11,7 @@
#include "alloc-util.h"
#include "dbus-mount.h"
+#include "dbus-unit.h"
#include "device.h"
#include "escape.h"
#include "exit-status.h"
@@ -20,11 +21,12 @@
#include "manager.h"
#include "mkdir.h"
#include "mount-setup.h"
-#include "mount-util.h"
#include "mount.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "serialize.h"
#include "special.h"
#include "string-table.h"
#include "string-util.h"
@@ -66,22 +68,18 @@ static bool MOUNT_STATE_WITH_PROCESS(MountState state) {
MOUNT_UNMOUNTING_SIGKILL);
}
-static bool mount_needs_network(const char *options, const char *fstype) {
- if (fstab_test_option(options, "_netdev\0"))
+static bool mount_is_network(const MountParameters *p) {
+ assert(p);
+
+ if (fstab_test_option(p->options, "_netdev\0"))
return true;
- if (fstype && fstype_is_network(fstype))
+ if (p->fstype && fstype_is_network(p->fstype))
return true;
return false;
}
-static bool mount_is_network(const MountParameters *p) {
- assert(p);
-
- return mount_needs_network(p->options, p->fstype);
-}
-
static bool mount_is_loop(const MountParameters *p) {
assert(p);
@@ -127,11 +125,11 @@ static bool mount_is_bound_to_device(const Mount *m) {
return fstab_test_option(p->options, "x-systemd.device-bound\0");
}
-static bool needs_quota(const MountParameters *p) {
+static bool mount_needs_quota(const MountParameters *p) {
assert(p);
- /* Quotas are not enabled on network filesystems,
- * but we want them, for example, on storage connected via iscsi */
+ /* Quotas are not enabled on network filesystems, but we want them, for example, on storage connected via
+ * iscsi. We hence don't use mount_is_network() here, as that would also return true for _netdev devices. */
if (p->fstype && fstype_is_network(p->fstype))
return false;
@@ -209,11 +207,9 @@ static void mount_unwatch_control_pid(Mount *m) {
static void mount_parameters_done(MountParameters *p) {
assert(p);
- free(p->what);
- free(p->options);
- free(p->fstype);
-
- p->what = p->options = p->fstype = NULL;
+ p->what = mfree(p->what);
+ p->options = mfree(p->options);
+ p->fstype = mfree(p->fstype);
}
static void mount_done(Unit *u) {
@@ -316,7 +312,7 @@ static int mount_add_mount_dependencies(Mount *m) {
}
static int mount_add_device_dependencies(Mount *m) {
- bool device_wants_mount = false;
+ bool device_wants_mount;
UnitDependencyMask mask;
MountParameters *p;
UnitDependency dep;
@@ -346,8 +342,8 @@ static int mount_add_device_dependencies(Mount *m) {
if (path_equal(m->where, "/"))
return 0;
- if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager))
- device_wants_mount = true;
+ device_wants_mount =
+ mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager);
/* Mount units from /proc/self/mountinfo are not bound to devices
* by default since they're subject to races when devices are
@@ -379,16 +375,16 @@ static int mount_add_quota_dependencies(Mount *m) {
if (!p)
return 0;
- if (!needs_quota(p))
+ if (!mount_needs_quota(p))
return 0;
mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT;
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true, mask);
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, true, mask);
if (r < 0)
return r;
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true, mask);
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, true, mask);
if (r < 0)
return r;
@@ -427,10 +423,10 @@ static bool mount_is_extrinsic(Mount *m) {
}
static int mount_add_default_dependencies(Mount *m) {
+ const char *after, *before;
UnitDependencyMask mask;
- int r;
MountParameters *p;
- const char *after;
+ int r;
assert(m);
@@ -456,7 +452,7 @@ static int mount_add_default_dependencies(Mount *m) {
* network.target, so that they are shut down only
* after this mount unit is stopped. */
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, NULL, true, mask);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, true, mask);
if (r < 0)
return r;
@@ -467,25 +463,32 @@ static int mount_add_default_dependencies(Mount *m) {
* whose purpose it is to delay this until the network
* is "up". */
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, NULL, true, mask);
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, true, mask);
if (r < 0)
return r;
after = SPECIAL_REMOTE_FS_PRE_TARGET;
- } else
+ before = SPECIAL_REMOTE_FS_TARGET;
+ } else {
after = SPECIAL_LOCAL_FS_PRE_TARGET;
+ before = SPECIAL_LOCAL_FS_TARGET;
+ }
+
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_BEFORE, before, true, mask);
+ if (r < 0)
+ return r;
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true, mask);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, true, mask);
if (r < 0)
return r;
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true, mask);
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, true, mask);
if (r < 0)
return r;
/* If this is a tmpfs mount then we have to unmount it before we try to deactivate swaps */
if (streq_ptr(p->fstype, "tmpfs")) {
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, NULL, true, mask);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, true, mask);
if (r < 0)
return r;
}
@@ -540,6 +543,10 @@ static int mount_add_extras(Mount *m) {
assert(m);
+ /* Note: this call might be called after we already have been loaded once (and even when it has already been
+ * activated), in case data from /proc/self/mountinfo has changed. This means all code here needs to be ready
+ * to run with an already set up unit. */
+
if (u->fragment_path)
m->from_fragment = true;
@@ -609,28 +616,33 @@ static int mount_load_root_mount(Unit *u) {
static int mount_load(Unit *u) {
Mount *m = MOUNT(u);
- int r;
+ int r, q, w;
assert(u);
assert(u->load_state == UNIT_STUB);
r = mount_load_root_mount(u);
- if (r < 0)
- return r;
if (m->from_proc_self_mountinfo || u->perpetual)
- r = unit_load_fragment_and_dropin_optional(u);
+ q = unit_load_fragment_and_dropin_optional(u);
else
- r = unit_load_fragment_and_dropin(u);
+ q = unit_load_fragment_and_dropin(u);
+
+ /* Add in some extras. Note we do this in all cases (even if we failed to load the unit) when announced by the
+ * kernel, because we need some things to be set up no matter what when the kernel establishes a mount and thus
+ * we need to update the state in our unit to track it. After all, consider that we don't allow changing the
+ * 'slice' field for a unit once it is active. */
+ if (u->load_state == UNIT_LOADED || m->from_proc_self_mountinfo || u->perpetual)
+ w = mount_add_extras(m);
+ else
+ w = 0;
+
if (r < 0)
return r;
-
- /* This is a new unit? Then let's add in some extras */
- if (u->load_state == UNIT_LOADED) {
- r = mount_add_extras(m);
- if (r < 0)
- return r;
- }
+ if (q < 0)
+ return q;
+ if (w < 0)
+ return w;
return mount_verify(m);
}
@@ -639,6 +651,9 @@ static void mount_set_state(Mount *m, MountState state) {
MountState old_state;
assert(m);
+ if (m->state != state)
+ bus_unit_send_pending_change_signal(UNIT(m), false);
+
old_state = m->state;
m->state = state;
@@ -746,11 +761,12 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
- ExecParameters exec_params = {
- .flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
- .stdin_fd = -1,
- .stdout_fd = -1,
- .stderr_fd = -1,
+ _cleanup_(exec_params_clear) ExecParameters exec_params = {
+ .flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
+ .exec_fd = -1,
};
pid_t pid;
int r;
@@ -767,7 +783,9 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
if (r < 0)
return r;
- unit_set_exec_params(UNIT(m), &exec_params);
+ r = unit_set_exec_params(UNIT(m), &exec_params);
+ if (r < 0)
+ return r;
r = exec_spawn(UNIT(m),
c,
@@ -795,9 +813,7 @@ static void mount_enter_dead(Mount *m, MountResult f) {
if (m->result == MOUNT_SUCCESS)
m->result = f;
- if (m->result != MOUNT_SUCCESS)
- log_unit_warning(UNIT(m), "Failed with result '%s'.", mount_result_to_string(m->result));
-
+ unit_log_result(UNIT(m), m->result == MOUNT_SUCCESS, mount_result_to_string(m->result));
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
m->exec_runtime = exec_runtime_unref(m->exec_runtime, true);
@@ -938,7 +954,6 @@ static void mount_enter_mounting(Mount *m) {
(void) mkdir_p_label(m->where, m->directory_mode);
unit_warn_if_dir_nonempty(UNIT(m), m->where);
-
unit_warn_leftover_processes(UNIT(m));
m->control_command_id = MOUNT_EXEC_MOUNT;
@@ -1042,6 +1057,17 @@ fail:
mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
}
+static void mount_cycle_clear(Mount *m) {
+ assert(m);
+
+ /* Clear all state we shall forget for this new cycle */
+
+ m->result = MOUNT_SUCCESS;
+ m->reload_result = MOUNT_SUCCESS;
+ exec_command_reset_status_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
+ UNIT(m)->reset_accounting = true;
+}
+
static int mount_start(Unit *u) {
Mount *m = MOUNT(u);
int r;
@@ -1072,12 +1098,9 @@ static int mount_start(Unit *u) {
if (r < 0)
return r;
- m->result = MOUNT_SUCCESS;
- m->reload_result = MOUNT_SUCCESS;
-
- u->reset_accounting = true;
-
+ mount_cycle_clear(m);
mount_enter_mounting(m);
+
return 1;
}
@@ -1138,21 +1161,23 @@ static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", mount_state_to_string(m->state));
- unit_serialize_item(u, f, "result", mount_result_to_string(m->result));
- unit_serialize_item(u, f, "reload-result", mount_result_to_string(m->reload_result));
+ (void) serialize_item(f, "state", mount_state_to_string(m->state));
+ (void) serialize_item(f, "result", mount_result_to_string(m->result));
+ (void) serialize_item(f, "reload-result", mount_result_to_string(m->reload_result));
+ (void) serialize_item_format(f, "n-retry-umount", "%u", m->n_retry_umount);
if (m->control_pid > 0)
- unit_serialize_item_format(u, f, "control-pid", PID_FMT, m->control_pid);
+ (void) serialize_item_format(f, "control-pid", PID_FMT, m->control_pid);
if (m->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id));
+ (void) serialize_item(f, "control-command", mount_exec_command_to_string(m->control_command_id));
return 0;
}
static int mount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
Mount *m = MOUNT(u);
+ int r;
assert(u);
assert(key);
@@ -1166,6 +1191,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
log_unit_debug(u, "Failed to parse state value: %s", value);
else
m->deserialized_state = state;
+
} else if (streq(key, "result")) {
MountResult f;
@@ -1184,13 +1210,17 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
else if (f != MOUNT_SUCCESS)
m->reload_result = f;
+ } else if (streq(key, "n-retry-umount")) {
+
+ r = safe_atou(value, &m->n_retry_umount);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse n-retry-umount value: %s", value);
+
} else if (streq(key, "control-pid")) {
- pid_t pid;
- if (parse_pid(value, &pid) < 0)
+ if (parse_pid(value, &m->control_pid) < 0)
log_unit_debug(u, "Failed to parse control-pid value: %s", value);
- else
- m->control_pid = pid;
+
} else if (streq(key, "control-command")) {
MountExecCommand id;
@@ -1265,8 +1295,11 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
}
- log_unit_full(u, f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
- "Mount process exited, code=%s status=%i", sigchld_code_to_string(code), status);
+ unit_log_process_exit(
+ u, f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "Mount process",
+ mount_exec_command_to_string(m->control_command_id),
+ code, status);
/* Note that due to the io event priority logic, we can be sure the new mountinfo is loaded
* before we process the SIGCHLD for the mount command. */
@@ -1395,59 +1428,77 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
return 0;
}
-typedef struct {
- bool is_mounted;
- bool just_mounted;
- bool just_changed;
-} MountSetupFlags;
+static int update_parameters_proc_self_mount_info(
+ Mount *m,
+ const char *what,
+ const char *options,
+ const char *fstype) {
+
+ MountParameters *p;
+ int r, q, w;
+
+ p = &m->parameters_proc_self_mountinfo;
+
+ r = free_and_strdup(&p->what, what);
+ if (r < 0)
+ return r;
+
+ q = free_and_strdup(&p->options, options);
+ if (q < 0)
+ return q;
+
+ w = free_and_strdup(&p->fstype, fstype);
+ if (w < 0)
+ return w;
+
+ return r > 0 || q > 0 || w > 0;
+}
static int mount_setup_new_unit(
- Unit *u,
+ Manager *m,
+ const char *name,
const char *what,
const char *where,
const char *options,
const char *fstype,
- MountSetupFlags *flags) {
-
- MountParameters *p;
+ MountProcFlags *ret_flags,
+ Unit **ret) {
- assert(u);
- assert(flags);
+ _cleanup_(unit_freep) Unit *u = NULL;
+ int r;
- u->source_path = strdup("/proc/self/mountinfo");
- MOUNT(u)->where = strdup(where);
- if (!u->source_path || !MOUNT(u)->where)
- return -ENOMEM;
+ assert(m);
+ assert(name);
+ assert(ret_flags);
+ assert(ret);
- /* Make sure to initialize those fields before mount_is_extrinsic(). */
- MOUNT(u)->from_proc_self_mountinfo = true;
- p = &MOUNT(u)->parameters_proc_self_mountinfo;
+ r = unit_new_for_name(m, sizeof(Mount), name, &u);
+ if (r < 0)
+ return r;
- p->what = strdup(what);
- p->options = strdup(options);
- p->fstype = strdup(fstype);
- if (!p->what || !p->options || !p->fstype)
- return -ENOMEM;
+ r = free_and_strdup(&u->source_path, "/proc/self/mountinfo");
+ if (r < 0)
+ return r;
- if (!mount_is_extrinsic(MOUNT(u))) {
- const char *target;
- int r;
+ r = free_and_strdup(&MOUNT(u)->where, where);
+ if (r < 0)
+ return r;
- target = mount_is_network(p) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET;
- r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
- if (r < 0)
- return r;
+ r = update_parameters_proc_self_mount_info(MOUNT(u), what, options, fstype);
+ if (r < 0)
+ return r;
- r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
- if (r < 0)
- return r;
- }
+ /* This unit was generated because /proc/self/mountinfo reported it. Remember this, so that by the time we load
+ * the unit file for it (and thus add in extra deps right after) we know what source to attributes the deps
+ * to.*/
+ MOUNT(u)->from_proc_self_mountinfo = true;
+ /* We have only allocated the stub now, let's enqueue this unit for loading now, so that everything else is
+ * loaded in now. */
unit_add_to_load_queue(u);
- flags->is_mounted = true;
- flags->just_mounted = true;
- flags->just_changed = true;
+ *ret_flags = MOUNT_PROC_IS_MOUNTED | MOUNT_PROC_JUST_MOUNTED | MOUNT_PROC_JUST_CHANGED;
+ *ret = TAKE_PTR(u);
return 0;
}
@@ -1457,11 +1508,10 @@ static int mount_setup_existing_unit(
const char *where,
const char *options,
const char *fstype,
- MountSetupFlags *flags) {
+ MountProcFlags *ret_flags) {
- MountParameters *p;
- bool load_extras = false;
- int r1, r2, r3;
+ MountProcFlags flags = MOUNT_PROC_IS_MOUNTED;
+ int r;
assert(u);
assert(flags);
@@ -1472,49 +1522,38 @@ static int mount_setup_existing_unit(
return -ENOMEM;
}
- /* Make sure to initialize those fields before mount_is_extrinsic(). */
- p = &MOUNT(u)->parameters_proc_self_mountinfo;
-
- r1 = free_and_strdup(&p->what, what);
- r2 = free_and_strdup(&p->options, options);
- r3 = free_and_strdup(&p->fstype, fstype);
- if (r1 < 0 || r2 < 0 || r3 < 0)
- return -ENOMEM;
-
- flags->just_changed = r1 > 0 || r2 > 0 || r3 > 0;
- flags->is_mounted = true;
- flags->just_mounted = !MOUNT(u)->from_proc_self_mountinfo || MOUNT(u)->just_mounted;
-
- MOUNT(u)->from_proc_self_mountinfo = true;
+ r = update_parameters_proc_self_mount_info(MOUNT(u), what, options, fstype);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ flags |= MOUNT_PROC_JUST_CHANGED;
- if (!mount_is_extrinsic(MOUNT(u)) && mount_is_network(p)) {
- /* _netdev option may have shown up late, or on a
- * remount. Add remote-fs dependencies, even though
- * local-fs ones may already be there.
- *
- * Note: due to a current limitation (we don't track
- * in the dependency "Set*" objects who created a
- * dependency), we can only add deps, never lose them,
- * until the next full daemon-reload. */
- unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, NULL, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
- load_extras = true;
+ if (!MOUNT(u)->from_proc_self_mountinfo) {
+ flags |= MOUNT_PROC_JUST_MOUNTED;
+ MOUNT(u)->from_proc_self_mountinfo = true;
}
- if (u->load_state == UNIT_NOT_FOUND) {
+ if (IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_BAD_SETTING, UNIT_ERROR)) {
+ /* The unit was previously not found or otherwise not loaded. Now that the unit shows up in
+ * /proc/self/mountinfo we should reconsider it this, hence set it to UNIT_LOADED. */
u->load_state = UNIT_LOADED;
u->load_error = 0;
- /* Load in the extras later on, after we
- * finished initialization of the unit */
-
- /* FIXME: since we're going to load the unit later on, why setting load_extras=true ? */
- load_extras = true;
- flags->just_changed = true;
+ flags |= MOUNT_PROC_JUST_CHANGED;
}
- if (load_extras)
- return mount_add_extras(MOUNT(u));
+ if (FLAGS_SET(flags, MOUNT_PROC_JUST_CHANGED)) {
+ /* If things changed, then make sure that all deps are regenerated. Let's
+ * first remove all automatic deps, and then add in the new ones. */
+
+ unit_remove_dependencies(u, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
+ r = mount_add_extras(MOUNT(u));
+ if (r < 0)
+ return r;
+ }
+
+ *ret_flags = flags;
return 0;
}
@@ -1527,7 +1566,7 @@ static int mount_setup_unit(
bool set_flags) {
_cleanup_free_ char *e = NULL;
- MountSetupFlags flags;
+ MountProcFlags flags;
Unit *u;
int r;
@@ -1551,45 +1590,32 @@ static int mount_setup_unit(
r = unit_name_from_path(where, ".mount", &e);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to generate unit name from path '%s': %m", where);
u = manager_get_unit(m, e);
- if (!u) {
- /* First time we see this mount point meaning that it's
- * not been initiated by a mount unit but rather by the
- * sysadmin having called mount(8) directly. */
- r = unit_new_for_name(m, sizeof(Mount), e, &u);
- if (r < 0)
- goto fail;
-
- r = mount_setup_new_unit(u, what, where, options, fstype, &flags);
- if (r < 0)
- unit_free(u);
- } else
+ if (u)
r = mount_setup_existing_unit(u, what, where, options, fstype, &flags);
-
+ else
+ /* First time we see this mount point meaning that it's not been initiated by a mount unit but rather
+ * by the sysadmin having called mount(8) directly. */
+ r = mount_setup_new_unit(m, e, what, where, options, fstype, &flags, &u);
if (r < 0)
- goto fail;
+ return log_warning_errno(r, "Failed to set up mount unit: %m");
- if (set_flags) {
- MOUNT(u)->is_mounted = flags.is_mounted;
- MOUNT(u)->just_mounted = flags.just_mounted;
- MOUNT(u)->just_changed = flags.just_changed;
- }
-
- if (flags.just_changed)
+ /* If the mount changed properties or state, let's notify our clients */
+ if (flags & (MOUNT_PROC_JUST_CHANGED|MOUNT_PROC_JUST_MOUNTED))
unit_add_to_dbus_queue(u);
+ if (set_flags)
+ MOUNT(u)->proc_flags = flags;
+
return 0;
-fail:
- log_warning_errno(r, "Failed to set up mount unit: %m");
- return r;
}
static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
_cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
_cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
- int r = 0;
+ int r;
assert(m);
@@ -1602,7 +1628,6 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
if (r < 0)
return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m");
- r = 0;
for (;;) {
struct libmnt_fs *fs;
const char *device, *path, *options, *fstype;
@@ -1631,12 +1656,10 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
device_found_node(m, d, DEVICE_FOUND_MOUNT, DEVICE_FOUND_MOUNT);
- k = mount_setup_unit(m, d, p, options, fstype, set_flags);
- if (r == 0 && k < 0)
- r = k;
+ (void) mount_setup_unit(m, d, p, options, fstype, set_flags);
}
- return r;
+ return 0;
}
static void mount_shutdown(Manager *m) {
@@ -1694,7 +1717,7 @@ static void mount_enumerate_perpetual(Manager *m) {
static bool mount_is_mounted(Mount *m) {
assert(m);
- return UNIT(m)->perpetual || m->is_mounted;
+ return UNIT(m)->perpetual || FLAGS_SET(m->proc_flags, MOUNT_PROC_IS_MOUNTED);
}
static void mount_enumerate(Manager *m) {
@@ -1758,7 +1781,7 @@ fail:
}
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
- _cleanup_set_free_ Set *around = NULL, *gone = NULL;
+ _cleanup_set_free_free_ Set *around = NULL, *gone = NULL;
Manager *m = userdata;
const char *what;
Iterator i;
@@ -1783,7 +1806,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
if (r == 0)
rescan = true;
else if (r < 0)
- return log_error_errno(r, "Failed to drain libmount events");
+ return log_error_errno(r, "Failed to drain libmount events: %m");
} while (r == 0);
log_debug("libmount event [rescan: %s]", yes_no(rescan));
@@ -1794,11 +1817,8 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
r = mount_load_proc_self_mountinfo(m, true);
if (r < 0) {
/* Reset flags, just in case, for later calls */
- LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
- Mount *mount = MOUNT(u);
-
- mount->is_mounted = mount->just_mounted = mount->just_changed = false;
- }
+ LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT])
+ MOUNT(u)->proc_flags = 0;
return 0;
}
@@ -1819,7 +1839,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
/* Remember that this device might just have disappeared */
if (set_ensure_allocated(&gone, &path_hash_ops) < 0 ||
- set_put(gone, mount->parameters_proc_self_mountinfo.what) < 0)
+ set_put_strdup(gone, mount->parameters_proc_self_mountinfo.what) < 0)
log_oom(); /* we don't care too much about OOM here... */
}
@@ -1828,10 +1848,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
switch (mount->state) {
case MOUNT_MOUNTED:
- /* This has just been unmounted by
- * somebody else, follow the state
- * change. */
- mount->result = MOUNT_SUCCESS; /* make sure we forget any earlier umount failures */
+ /* This has just been unmounted by somebody else, follow the state change. */
mount_enter_dead(mount, MOUNT_SUCCESS);
break;
@@ -1839,7 +1856,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
break;
}
- } else if (mount->just_mounted || mount->just_changed) {
+ } else if (mount->proc_flags & (MOUNT_PROC_JUST_MOUNTED|MOUNT_PROC_JUST_CHANGED)) {
/* A mount point was added or changed */
@@ -1850,7 +1867,8 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
/* This has just been mounted by somebody else, follow the state change, but let's
* generate a new invocation ID for this implicitly and automatically. */
- (void) unit_acquire_invocation_id(UNIT(mount));
+ (void) unit_acquire_invocation_id(u);
+ mount_cycle_clear(mount);
mount_enter_mounted(mount, MOUNT_SUCCESS);
break;
@@ -1872,14 +1890,15 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
if (mount_is_mounted(mount) &&
mount->from_proc_self_mountinfo &&
mount->parameters_proc_self_mountinfo.what) {
+ /* Track devices currently used */
if (set_ensure_allocated(&around, &path_hash_ops) < 0 ||
- set_put(around, mount->parameters_proc_self_mountinfo.what) < 0)
+ set_put_strdup(around, mount->parameters_proc_self_mountinfo.what) < 0)
log_oom();
}
/* Reset the flags for later calls */
- mount->is_mounted = mount->just_mounted = mount->just_changed = false;
+ mount->proc_flags = 0;
}
SET_FOREACH(what, gone, i) {
diff --git a/src/core/mount.h b/src/core/mount.h
index 67ab8ecf93..2e59f1fe04 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -34,6 +34,13 @@ typedef struct MountParameters {
char *fstype;
} MountParameters;
+/* Used while looking for mount points that vanished or got added from/to /proc/self/mountinfo */
+typedef enum MountProcFlags {
+ MOUNT_PROC_IS_MOUNTED = 1 << 0,
+ MOUNT_PROC_JUST_MOUNTED = 1 << 1,
+ MOUNT_PROC_JUST_CHANGED = 1 << 2,
+} MountProcFlags;
+
struct Mount {
Unit meta;
@@ -45,11 +52,7 @@ struct Mount {
bool from_proc_self_mountinfo:1;
bool from_fragment:1;
- /* Used while looking for mount points that vanished or got
- * added from/to /proc/self/mountinfo */
- bool is_mounted:1;
- bool just_mounted:1;
- bool just_changed:1;
+ MountProcFlags proc_flags;
bool sloppy_options;
diff --git a/src/core/namespace.c b/src/core/namespace.c
index e4930db15c..c2ca3e0334 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -20,6 +20,7 @@
#include "missing.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "namespace.h"
#include "path-util.h"
#include "selinux-util.h"
@@ -236,7 +237,8 @@ static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, boo
}
if (!path_is_absolute(e))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Path is not absolute: %s", e);
*((*p)++) = (MountEntry) {
.path_const = e,
@@ -263,7 +265,6 @@ static int append_empty_dir_mounts(MountEntry **p, char **strv) {
.path_const = *i,
.mode = EMPTY_DIR,
.ignore = false,
- .has_prefix = false,
.read_only = true,
.options_const = "mode=755",
.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
@@ -302,35 +303,33 @@ static int append_tmpfs_mounts(MountEntry **p, const TemporaryFileSystem *tmpfs,
for (i = 0; i < n; i++) {
const TemporaryFileSystem *t = tmpfs + i;
_cleanup_free_ char *o = NULL, *str = NULL;
- unsigned long flags = MS_NODEV|MS_STRICTATIME;
+ unsigned long flags;
bool ro = false;
if (!path_is_absolute(t->path))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Path is not absolute: %s",
+ t->path);
- if (!isempty(t->options)) {
- str = strjoin("mode=0755,", t->options);
- if (!str)
- return -ENOMEM;
+ str = strjoin("mode=0755,", t->options);
+ if (!str)
+ return -ENOMEM;
- r = mount_option_mangle(str, MS_NODEV|MS_STRICTATIME, &flags, &o);
- if (r < 0)
- return r;
+ r = mount_option_mangle(str, MS_NODEV|MS_STRICTATIME, &flags, &o);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse mount option '%s': %m", str);
- ro = flags & MS_RDONLY;
- if (ro)
- flags ^= MS_RDONLY;
- }
+ ro = flags & MS_RDONLY;
+ if (ro)
+ flags ^= MS_RDONLY;
*((*p)++) = (MountEntry) {
.path_const = t->path,
.mode = TMPFS,
.read_only = ro,
- .options_malloc = o,
+ .options_malloc = TAKE_PTR(o),
.flags = flags,
};
-
- o = NULL;
}
return 0;
@@ -398,32 +397,22 @@ static int append_protect_system(MountEntry **p, ProtectSystem protect_system, b
}
}
-static int mount_path_compare(const void *a, const void *b) {
- const MountEntry *p = a, *q = b;
+static int mount_path_compare(const MountEntry *a, const MountEntry *b) {
int d;
/* If the paths are not equal, then order prefixes first */
- d = path_compare(mount_entry_path(p), mount_entry_path(q));
+ d = path_compare(mount_entry_path(a), mount_entry_path(b));
if (d != 0)
return d;
/* If the paths are equal, check the mode */
- if (p->mode < q->mode)
- return -1;
- if (p->mode > q->mode)
- return 1;
-
- return 0;
+ return CMP((int) a->mode, (int) b->mode);
}
static int prefix_where_needed(MountEntry *m, size_t n, const char *root_directory) {
size_t i;
- /* Prefixes all paths in the bind mount table with the root directory if it is specified and the entry needs
- * that. */
-
- if (!root_directory)
- return 0;
+ /* Prefixes all paths in the bind mount table with the root directory if the entry needs that. */
for (i = 0; i < n; i++) {
char *s;
@@ -566,36 +555,44 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, size_t
*n = t - m;
}
-static int clone_device_node(const char *d, const char *temporary_mount, bool *make_devnode) {
- const char *dn;
+static int clone_device_node(
+ const char *d,
+ const char *temporary_mount,
+ bool *make_devnode) {
+
+ _cleanup_free_ char *sl = NULL;
+ const char *dn, *bn, *t;
struct stat st;
int r;
if (stat(d, &st) < 0) {
- if (errno == ENOENT)
+ if (errno == ENOENT) {
+ log_debug_errno(errno, "Device node '%s' to clone does not exist, ignoring.", d);
return -ENXIO;
- return -errno;
+ }
+
+ return log_debug_errno(errno, "Failed to stat() device node '%s' to clone, ignoring: %m", d);
}
if (!S_ISBLK(st.st_mode) &&
!S_ISCHR(st.st_mode))
- return -EINVAL;
-
- if (st.st_rdev == 0)
- return -ENXIO;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Device node '%s' to clone is not a device node, ignoring.",
+ d);
dn = strjoina(temporary_mount, d);
+ /* First, try to create device node properly */
if (*make_devnode) {
mac_selinux_create_file_prepare(d, st.st_mode);
r = mknod(dn, st.st_mode, st.st_rdev);
mac_selinux_create_file_clear();
-
- if (r == 0)
- return 0;
+ if (r >= 0)
+ goto add_symlink;
if (errno != EPERM)
return log_debug_errno(errno, "mknod failed for %s: %m", d);
+ /* This didn't work, let's not try this again for the next iterations. */
*make_devnode = false;
}
@@ -604,9 +601,8 @@ static int clone_device_node(const char *d, const char *temporary_mount, bool *m
mac_selinux_create_file_prepare(d, 0);
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);
+ return log_debug_errno(errno, "mknod() fallback failed for '%s': %m", d);
/* Fallback to bind-mounting:
* The assumption here is that all used device nodes carry standard
@@ -614,7 +610,23 @@ static int clone_device_node(const char *d, const char *temporary_mount, bool *m
* either be owned by root:root or root:tty (e.g. /dev/tty, /dev/ptmx)
* and should not carry ACLs. */
if (mount(d, dn, NULL, MS_BIND, NULL) < 0)
- return log_debug_errno(errno, "mount failed for %s: %m", d);
+ return log_debug_errno(errno, "Bind mounting failed for '%s': %m", d);
+
+add_symlink:
+ bn = path_startswith(d, "/dev/");
+ if (!bn)
+ return 0;
+
+ /* Create symlinks like /dev/char/1:9 → ../urandom */
+ if (asprintf(&sl, "%s/dev/%s/%u:%u", temporary_mount, S_ISCHR(st.st_mode) ? "char" : "block", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ return log_oom();
+
+ (void) mkdir_parents(sl, 0755);
+
+ t = strjoina("../", bn);
+
+ if (symlink(t, sl) < 0)
+ log_debug_errno(errno, "Failed to symlink '%s' to '%s', ignoring: %m", t, sl);
return 0;
}
@@ -639,35 +651,34 @@ static int mount_private_dev(MountEntry *m) {
u = umask(0000);
if (!mkdtemp(temporary_mount))
- return -errno;
+ return log_debug_errno(errno, "Failed to create temporary directory '%s': %m", temporary_mount);
dev = strjoina(temporary_mount, "/dev");
(void) mkdir(dev, 0755);
if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755") < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to mount tmpfs on '%s': %m", dev);
goto fail;
}
devpts = strjoina(temporary_mount, "/dev/pts");
(void) mkdir(devpts, 0755);
if (mount("/dev/pts", devpts, NULL, MS_BIND, NULL) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to bind mount /dev/pts on '%s': %m", devpts);
goto fail;
}
- /* /dev/ptmx can either be a device node or a symlink to /dev/pts/ptmx
- * when /dev/ptmx a device node, /dev/pts/ptmx has 000 permissions making it inaccessible
- * thus, in that case make a clone
- *
- * in nspawn and other containers it will be a symlink, in that case make it a symlink
- */
+ /* /dev/ptmx can either be a device node or a symlink to /dev/pts/ptmx.
+ * When /dev/ptmx a device node, /dev/pts/ptmx has 000 permissions making it inaccessible.
+ * Thus, in that case make a clone.
+ * In nspawn and other containers it will be a symlink, in that case make it a symlink. */
r = is_symlink("/dev/ptmx");
- if (r < 0)
+ if (r < 0) {
+ log_debug_errno(r, "Failed to detect whether /dev/ptmx is a symlink or not: %m");
goto fail;
- if (r > 0) {
+ } else if (r > 0) {
devptmx = strjoina(temporary_mount, "/dev/ptmx");
if (symlink("pts/ptmx", devptmx) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to create a symlink '%s' to pts/ptmx: %m", devptmx);
goto fail;
}
} else {
@@ -680,20 +691,23 @@ static int mount_private_dev(MountEntry *m) {
(void) mkdir(devshm, 0755);
r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL);
if (r < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to bind mount /dev/shm on '%s': %m", devshm);
goto fail;
}
devmqueue = strjoina(temporary_mount, "/dev/mqueue");
(void) mkdir(devmqueue, 0755);
- (void) mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL);
+ if (mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL) < 0)
+ log_debug_errno(errno, "Failed to bind mount /dev/mqueue on '%s', ignoring: %m", devmqueue);
devhugepages = strjoina(temporary_mount, "/dev/hugepages");
(void) mkdir(devhugepages, 0755);
- (void) mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL);
+ if (mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL) < 0)
+ log_debug_errno(errno, "Failed to bind mount /dev/hugepages on '%s', ignoring: %m", devhugepages);
devlog = strjoina(temporary_mount, "/dev/log");
- (void) symlink("/run/systemd/journal/dev-log", devlog);
+ if (symlink("/run/systemd/journal/dev-log", devlog) < 0)
+ log_debug_errno(errno, "Failed to create a symlink '%s' to /run/systemd/journal/dev-log, ignoring: %m", devlog);
NULSTR_FOREACH(d, devnodes) {
r = clone_device_node(d, temporary_mount, &can_mknod);
@@ -702,7 +716,9 @@ static int mount_private_dev(MountEntry *m) {
goto fail;
}
- dev_setup(temporary_mount, UID_INVALID, GID_INVALID);
+ 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);
/* Create the /dev directory if missing. It is more likely to be
* missing when the service is started with RootDirectory. This is
@@ -711,9 +727,12 @@ static int mount_private_dev(MountEntry *m) {
(void) mkdir_p_label(mount_entry_path(m), 0755);
/* Unmount everything in old /dev */
- umount_recursive(mount_entry_path(m), 0);
+ r = umount_recursive(mount_entry_path(m), 0);
+ if (r < 0)
+ log_debug_errno(r, "Failed to unmount directories below '%s', ignoring: %m", mount_entry_path(m));
+
if (mount(dev, mount_entry_path(m), NULL, MS_MOVE, NULL) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to move mount point '%s' to '%s': %m", dev, mount_entry_path(m));
goto fail;
}
@@ -836,10 +855,10 @@ static int follow_symlink(
if (r > 0) /* Reached the end, nothing more to resolve */
return 1;
- if (m->n_followed >= CHASE_SYMLINKS_MAX) { /* put a boundary on things */
- log_debug("Symlink loop on '%s'.", mount_entry_path(m));
- return -ELOOP;
- }
+ if (m->n_followed >= CHASE_SYMLINKS_MAX) /* put a boundary on things */
+ return log_debug_errno(SYNTHETIC_ERRNO(ELOOP),
+ "Symlink loop on '%s'.",
+ mount_entry_path(m));
log_debug("Followed mount entry path symlink %s → %s.", mount_entry_path(m), target);
@@ -881,10 +900,9 @@ static int apply_mount(
}
what = mode_to_inaccessible_node(target.st_mode);
- if (!what) {
- log_debug("File type not supported for inaccessible mounts. Note that symlinks are not allowed");
- return -ELOOP;
- }
+ if (!what)
+ return log_debug_errno(SYNTHETIC_ERRNO(ELOOP),
+ "File type not supported for inaccessible mounts. Note that symlinks are not allowed");
break;
}
@@ -999,7 +1017,17 @@ static int apply_mount(
return 0;
}
+/* Change the per-mount readonly flag on an existing mount */
+static int remount_bind_readonly(const char *path, unsigned long orig_flags) {
+ int r;
+
+ r = mount(NULL, path, NULL, MS_REMOUNT | MS_BIND | MS_RDONLY | orig_flags, NULL);
+
+ return r < 0 ? -errno : 0;
+}
+
static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self_mountinfo) {
+ bool submounts = false;
int r = 0;
assert(m);
@@ -1007,15 +1035,15 @@ static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self
if (mount_entry_read_only(m)) {
if (IN_SET(m->mode, EMPTY_DIR, TMPFS)) {
- /* Make superblock readonly */
- if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT | MS_RDONLY | m->flags, mount_entry_options(m)) < 0)
- r = -errno;
- } else
+ r = remount_bind_readonly(mount_entry_path(m), m->flags);
+ } else {
+ submounts = true;
r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), true, blacklist, proc_self_mountinfo);
+ }
} else if (m->mode == PRIVATE_DEV) {
- /* Superblock can be readonly but the submounts can't */
- if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
- r = -errno;
+ /* Set /dev readonly, but not submounts like /dev/shm. Also, we only set the per-mount read-only flag.
+ * We can't set it on the superblock, if we are inside a user namespace and running Linux <= 4.17. */
+ r = remount_bind_readonly(mount_entry_path(m), DEV_MOUNT_OPTIONS);
} else
return 0;
@@ -1026,27 +1054,28 @@ static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self
if (r == -ENOENT && m->ignore)
r = 0;
- return r;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to re-mount '%s'%s read-only: %m", mount_entry_path(m),
+ submounts ? " and its submounts" : "");
+
+ return 0;
}
-static bool namespace_info_mount_apivfs(const char *root_directory, const NamespaceInfo *ns_info) {
+static bool namespace_info_mount_apivfs(const NamespaceInfo *ns_info) {
assert(ns_info);
/*
* ProtectControlGroups= and ProtectKernelTunables= imply MountAPIVFS=,
* since to protect the API VFS mounts, they need to be around in the
- * first place... and RootDirectory= or RootImage= need to be set.
+ * first place...
*/
- /* root_directory should point to a mount point */
- return root_directory &&
- (ns_info->mount_apivfs ||
- ns_info->protect_control_groups ||
- ns_info->protect_kernel_tunables);
+ return ns_info->mount_apivfs ||
+ ns_info->protect_control_groups ||
+ ns_info->protect_kernel_tunables;
}
static size_t namespace_calculate_mounts(
- const char* root_directory,
const NamespaceInfo *ns_info,
char** read_write_paths,
char** read_only_paths,
@@ -1088,14 +1117,15 @@ static size_t namespace_calculate_mounts(
(ns_info->protect_control_groups ? 1 : 0) +
(ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) +
protect_home_cnt + protect_system_cnt +
- (namespace_info_mount_apivfs(root_directory, ns_info) ? ELEMENTSOF(apivfs_table) : 0);
+ (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0);
}
static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
+ assert(root_directory);
assert(n_mounts);
assert(mounts || *n_mounts == 0);
- qsort_safe(mounts, *n_mounts, sizeof(MountEntry), mount_path_compare);
+ typesafe_qsort(mounts, *n_mounts, mount_path_compare);
drop_duplicates(mounts, n_mounts);
drop_outside_root(root_directory, mounts, n_mounts);
@@ -1127,11 +1157,9 @@ int setup_namespace(
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
_cleanup_free_ void *root_hash = NULL;
MountEntry *m, *mounts = NULL;
- size_t root_hash_size = 0;
- const char *root;
- size_t n_mounts;
- bool make_slave;
+ size_t n_mounts, root_hash_size = 0;
bool require_prefix = false;
+ const char *root;
int r = 0;
assert(ns_info);
@@ -1151,19 +1179,19 @@ int setup_namespace(
dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY : O_RDWR,
&loop_device);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create loop device for root image: %m");
r = root_hash_load(root_image, &root_hash, &root_hash_size);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to load root hash: %m");
r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to dissect image: %m");
r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to decrypt dissected image: %m");
}
if (root_directory)
@@ -1181,7 +1209,6 @@ int setup_namespace(
}
n_mounts = namespace_calculate_mounts(
- root,
ns_info,
read_write_paths,
read_only_paths,
@@ -1192,9 +1219,6 @@ int setup_namespace(
tmp_dir, var_tmp_dir,
protect_home, protect_system);
- /* Set mount slave mode */
- make_slave = root || n_mounts > 0 || ns_info->private_mounts;
-
if (n_mounts > 0) {
m = mounts = (MountEntry *) alloca0(n_mounts * sizeof(MountEntry));
r = append_access_mounts(&m, read_write_paths, READWRITE, require_prefix);
@@ -1271,7 +1295,7 @@ int setup_namespace(
if (r < 0)
goto finish;
- if (namespace_info_mount_apivfs(root, ns_info)) {
+ if (namespace_info_mount_apivfs(ns_info)) {
r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table), ns_info->ignore_protect_paths);
if (r < 0)
goto finish;
@@ -1284,33 +1308,44 @@ int setup_namespace(
if (r < 0)
goto finish;
- normalize_mounts(root_directory, mounts, &n_mounts);
+ normalize_mounts(root, mounts, &n_mounts);
}
+ /* All above is just preparation, figuring out what to do. Let's now actually start doing something. */
+
if (unshare(CLONE_NEWNS) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to unshare the mount namespace: %m");
+ if (IN_SET(r, -EACCES, -EPERM, -EOPNOTSUPP, -ENOSYS))
+ /* If the kernel doesn't support namespaces, or when there's a MAC or seccomp filter in place
+ * that doesn't allow us to create namespaces (or a missing cap), then propagate a recognizable
+ * error back, which the caller can use to detect this case (and only this) and optionally
+ * continue without namespacing applied. */
+ r = -ENOANO;
+
goto finish;
}
- if (make_slave) {
- /* Remount / as SLAVE so that nothing now mounted in the namespace
- shows up in the parent */
- if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
- r = -errno;
- goto finish;
- }
+ /* Remount / as SLAVE so that nothing now mounted in the namespace
+ * shows up in the parent */
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
+ r = log_debug_errno(errno, "Failed to remount '/' as SLAVE: %m");
+ goto finish;
}
if (root_image) {
/* A root image is specified, mount it to the right place */
r = dissected_image_mount(dissected_image, root, UID_INVALID, dissect_image_flags);
- if (r < 0)
+ if (r < 0) {
+ log_debug_errno(r, "Failed to mount root image: %m");
goto finish;
+ }
if (decrypted_image) {
r = decrypted_image_relinquish(decrypted_image);
- if (r < 0)
+ if (r < 0) {
+ log_debug_errno(r, "Failed to relinquish decrypted image: %m");
goto finish;
+ }
}
loop_device_relinquish(loop_device);
@@ -1319,20 +1354,22 @@ int setup_namespace(
/* A root directory is specified. Turn its directory into bind mount, if it isn't one yet. */
r = path_is_mount_point(root, NULL, AT_SYMLINK_FOLLOW);
- if (r < 0)
+ if (r < 0) {
+ log_debug_errno(r, "Failed to detect that %s is a mount point or not: %m", root);
goto finish;
+ }
if (r == 0) {
if (mount(root, root, NULL, MS_BIND|MS_REC, NULL) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to bind mount '%s': %m", root);
goto finish;
}
}
- } else if (root) {
+ } else {
/* Let's mount the main root directory to the root directory to use */
if (mount("/", root, NULL, MS_BIND|MS_REC, NULL) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to bind mount '/' on '%s': %m", root);
goto finish;
}
}
@@ -1350,7 +1387,7 @@ int setup_namespace(
* For example, this is the case with the option: 'InaccessiblePaths=/proc' */
proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
if (!proc_self_mountinfo) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to open /proc/self/mountinfo: %m");
goto finish;
}
@@ -1385,7 +1422,7 @@ int setup_namespace(
if (!again)
break;
- normalize_mounts(root_directory, mounts, &n_mounts);
+ normalize_mounts(root, mounts, &n_mounts);
}
/* Create a blacklist we can pass to bind_mount_recursive() */
@@ -1402,18 +1439,18 @@ int setup_namespace(
}
}
- if (root) {
- /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
- r = mount_move_root(root);
- if (r < 0)
- goto finish;
+ /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
+ r = mount_move_root(root);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to mount root with MS_MOVE: %m");
+ goto finish;
}
/* Remount / as the desired mode. Note that this will not
* reestablish propagation from our side to the host, since
* what's disconnected is disconnected. */
if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to remount '/' with desired mount flags: %m");
goto finish;
}
diff --git a/src/core/path.c b/src/core/path.c
index 68b13b610a..831e49df29 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -8,12 +8,14 @@
#include "bus-error.h"
#include "bus-util.h"
#include "dbus-path.h"
+#include "dbus-unit.h"
#include "fd-util.h"
#include "fs-util.h"
#include "glob-util.h"
#include "macro.h"
#include "mkdir.h"
#include "path.h"
+#include "serialize.h"
#include "special.h"
#include "stat-util.h"
#include "string-table.h"
@@ -145,10 +147,9 @@ int path_spec_fd_event(PathSpec *s, uint32_t revents) {
ssize_t l;
int r = 0;
- if (revents != EPOLLIN) {
- log_error("Got invalid poll event on inotify.");
- return -EINVAL;
- }
+ if (revents != EPOLLIN)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Got invalid poll event on inotify.");
l = read(s->inotify_fd, &buffer, sizeof(buffer));
if (l < 0) {
@@ -298,17 +299,17 @@ static int path_add_default_dependencies(Path *p) {
if (!UNIT(p)->default_dependencies)
return 0;
- r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) {
- r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
}
- return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
}
static int path_add_trigger_dependencies(Path *p) {
@@ -410,6 +411,9 @@ static void path_set_state(Path *p, PathState state) {
PathState old_state;
assert(p);
+ if (p->state != state)
+ bus_unit_send_pending_change_signal(UNIT(p), false);
+
old_state = p->state;
p->state = state;
@@ -448,9 +452,7 @@ static void path_enter_dead(Path *p, PathResult f) {
if (p->result == PATH_SUCCESS)
p->result = f;
- if (p->result != PATH_SUCCESS)
- log_unit_warning(UNIT(p), "Failed with result '%s'.", path_result_to_string(p->result));
-
+ unit_log_result(UNIT(p), p->result == PATH_SUCCESS, path_result_to_string(p->result));
path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
}
@@ -600,8 +602,8 @@ static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", path_state_to_string(p->state));
- unit_serialize_item(u, f, "result", path_result_to_string(p->result));
+ (void) serialize_item(f, "state", path_state_to_string(p->state));
+ (void) serialize_item(f, "result", path_result_to_string(p->result));
return 0;
}
diff --git a/src/core/scope.c b/src/core/scope.c
index 751556fecf..e478661f94 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -5,9 +5,11 @@
#include "alloc-util.h"
#include "dbus-scope.h"
+#include "dbus-unit.h"
#include "load-dropin.h"
#include "log.h"
#include "scope.h"
+#include "serialize.h"
#include "special.h"
#include "string-table.h"
#include "string-util.h"
@@ -81,6 +83,9 @@ static void scope_set_state(Scope *s, ScopeState state) {
ScopeState old_state;
assert(s);
+ if (s->state != state)
+ bus_unit_send_pending_change_signal(UNIT(s), false);
+
old_state = s->state;
s->state = state;
@@ -110,7 +115,7 @@ static int scope_add_default_dependencies(Scope *s) {
r = unit_add_two_dependencies_by_name(
UNIT(s),
UNIT_BEFORE, UNIT_CONFLICTS,
- SPECIAL_SHUTDOWN_TARGET, NULL, true,
+ SPECIAL_SHUTDOWN_TARGET, true,
UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
@@ -239,9 +244,7 @@ static void scope_enter_dead(Scope *s, ScopeResult f) {
if (s->result == SCOPE_SUCCESS)
s->result = f;
- if (s->result != SCOPE_SUCCESS)
- log_unit_warning(UNIT(s), "Failed with result '%s'.", scope_result_to_string(s->result));
-
+ unit_log_result(UNIT(s), s->result == SCOPE_SUCCESS, scope_result_to_string(s->result));
scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD);
}
@@ -402,11 +405,11 @@ static int scope_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", scope_state_to_string(s->state));
- unit_serialize_item(u, f, "was-abandoned", yes_no(s->was_abandoned));
+ (void) serialize_item(f, "state", scope_state_to_string(s->state));
+ (void) serialize_bool(f, "was-abandoned", s->was_abandoned);
if (s->controller)
- unit_serialize_item(u, f, "controller", s->controller);
+ (void) serialize_item(f, "controller", s->controller);
return 0;
}
@@ -441,7 +444,7 @@ static int scope_deserialize_item(Unit *u, const char *key, const char *value, F
r = free_and_strdup(&s->controller, value);
if (r < 0)
- log_oom();
+ return log_oom();
} else
log_unit_debug(u, "Unknown serialization key: %s", key);
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index 39e994afd7..0c6d885b8c 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2012 Dan Walsh
-***/
#include "selinux-access.h"
diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h
index 59f2e60c77..1e75930f57 100644
--- a/src/core/selinux-access.h
+++ b/src/core/selinux-access.h
@@ -1,10 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- Copyright © 2012 Dan Walsh
-***/
-
#include "sd-bus.h"
#include "bus-util.h"
diff --git a/src/core/service.c b/src/core/service.c
index db1356c417..cfa3271232 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -12,6 +12,7 @@
#include "bus-kernel.h"
#include "bus-util.h"
#include "dbus-service.h"
+#include "dbus-unit.h"
#include "def.h"
#include "env-util.h"
#include "escape.h"
@@ -27,6 +28,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "serialize.h"
#include "service.h"
#include "signal-util.h"
#include "special.h"
@@ -48,7 +50,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_EXITED] = UNIT_ACTIVE,
[SERVICE_RELOAD] = UNIT_RELOADING,
[SERVICE_STOP] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGABRT] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
[SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
[SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
[SERVICE_STOP_POST] = UNIT_DEACTIVATING,
@@ -69,7 +71,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
[SERVICE_EXITED] = UNIT_ACTIVE,
[SERVICE_RELOAD] = UNIT_RELOADING,
[SERVICE_STOP] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGABRT] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
[SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
[SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
[SERVICE_STOP_POST] = UNIT_DEACTIVATING,
@@ -79,9 +81,10 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
};
-static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
+static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata);
+static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
static void service_enter_reload_by_notify(Service *s);
@@ -105,6 +108,8 @@ static void service_init(Unit *u) {
s->exec_context.keyring_mode = MANAGER_IS_SYSTEM(u->manager) ?
EXEC_KEYRING_PRIVATE : EXEC_KEYRING_INHERIT;
+
+ s->watchdog_original_usec = USEC_INFINITY;
}
static void service_unwatch_control_pid(Service *s) {
@@ -193,19 +198,21 @@ static usec_t service_get_watchdog_usec(Service *s) {
if (s->watchdog_override_enable)
return s->watchdog_override_usec;
- else
- return s->watchdog_usec;
+
+ return s->watchdog_original_usec;
}
static void service_start_watchdog(Service *s) {
- int r;
usec_t watchdog_usec;
+ int r;
assert(s);
watchdog_usec = service_get_watchdog_usec(s);
- if (IN_SET(watchdog_usec, 0, USEC_INFINITY))
+ if (IN_SET(watchdog_usec, 0, USEC_INFINITY)) {
+ service_stop_watchdog(s);
return;
+ }
if (s->watchdog_event_source) {
r = sd_event_source_set_time(s->watchdog_event_source, usec_add(s->watchdog_timestamp.monotonic, watchdog_usec));
@@ -233,50 +240,55 @@ static void service_start_watchdog(Service *s) {
* of living before we consider a service died. */
r = sd_event_source_set_priority(s->watchdog_event_source, SD_EVENT_PRIORITY_IDLE);
}
-
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "Failed to install watchdog timer: %m");
}
-static void service_extend_timeout(Service *s, usec_t extend_timeout_usec) {
- assert(s);
+static void service_extend_event_source_timeout(Service *s, sd_event_source *source, usec_t extended) {
+ usec_t current;
+ int r;
- if (s->timer_event_source) {
- uint64_t current = 0, extended = 0;
- int r;
+ assert(s);
- if (IN_SET(extend_timeout_usec, 0, USEC_INFINITY))
- return;
+ /* Extends the specified event source timer to at least the specified time, unless it is already later
+ * anyway. */
- extended = usec_add(now(CLOCK_MONOTONIC), extend_timeout_usec);
+ if (!source)
+ return;
- r = sd_event_source_get_time(s->timer_event_source, &current);
- if (r < 0)
- log_unit_error_errno(UNIT(s), r, "Failed to retrieve timeout timer: %m");
- else if (extended > current) {
- r = sd_event_source_set_time(s->timer_event_source, extended);
- if (r < 0)
- log_unit_warning_errno(UNIT(s), r, "Failed to set timeout timer: %m");
- }
+ r = sd_event_source_get_time(source, &current);
+ if (r < 0) {
+ const char *desc;
+ (void) sd_event_source_get_description(s->timer_event_source, &desc);
+ log_unit_warning_errno(UNIT(s), r, "Failed to retrieve timeout time for event source '%s', ignoring: %m", strna(desc));
+ return;
+ }
- if (s->watchdog_event_source) {
- /* extend watchdog if necessary. We've asked for an extended timeout so we
- * shouldn't expect a watchdog timeout in the interval in between */
- r = sd_event_source_get_time(s->watchdog_event_source, &current);
- if (r < 0) {
- log_unit_error_errno(UNIT(s), r, "Failed to retrieve watchdog timer: %m");
- return;
- }
+ if (current >= extended) /* Current timeout is already longer, ignore this. */
+ return;
- if (extended > current) {
- r = sd_event_source_set_time(s->watchdog_event_source, extended);
- if (r < 0)
- log_unit_warning_errno(UNIT(s), r, "Failed to set watchdog timer: %m");
- }
- }
+ r = sd_event_source_set_time(source, extended);
+ if (r < 0) {
+ const char *desc;
+ (void) sd_event_source_get_description(s->timer_event_source, &desc);
+ log_unit_warning_errno(UNIT(s), r, "Failed to set timeout time for even source '%s', ignoring %m", strna(desc));
}
}
+static void service_extend_timeout(Service *s, usec_t extend_timeout_usec) {
+ usec_t extended;
+
+ assert(s);
+
+ if (IN_SET(extend_timeout_usec, 0, USEC_INFINITY))
+ return;
+
+ extended = usec_add(now(CLOCK_MONOTONIC), extend_timeout_usec);
+
+ service_extend_event_source_timeout(s, s->timer_event_source, extended);
+ service_extend_event_source_timeout(s, s->watchdog_event_source, extended);
+}
+
static void service_reset_watchdog(Service *s) {
assert(s);
@@ -284,7 +296,7 @@ static void service_reset_watchdog(Service *s) {
service_start_watchdog(s);
}
-static void service_reset_watchdog_timeout(Service *s, usec_t watchdog_override_usec) {
+static void service_override_watchdog_timeout(Service *s, usec_t watchdog_override_usec) {
assert(s);
s->watchdog_override_enable = true;
@@ -389,6 +401,7 @@ static void service_done(Unit *u) {
service_stop_watchdog(s);
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
+ s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source);
service_release_resources(u);
}
@@ -536,8 +549,13 @@ static int service_verify(Service *s) {
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
- if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) {
- log_unit_error(UNIT(s), "Service lacks both ExecStart= and ExecStop= setting. Refusing.");
+ if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]
+ && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
+ /* FailureAction= only makes sense if one of the start or stop commands is specified.
+ * SuccessAction= will be executed unconditionally if no commands are specified. Hence,
+ * either a command or SuccessAction= are required. */
+
+ log_unit_error(UNIT(s), "Service has no ExecStart=, ExecStop=, or SuccessAction=. Refusing.");
return -ENOEXEC;
}
@@ -546,8 +564,8 @@ static int service_verify(Service *s) {
return -ENOEXEC;
}
- if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) {
- log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing.");
+ if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START] && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
+ log_unit_error(UNIT(s), "Service has no ExecStart= and no SuccessAction= settings and does not have RemainAfterExit=yes set. Refusing.");
return -ENOEXEC;
}
@@ -607,7 +625,7 @@ static int service_add_default_dependencies(Service *s) {
* require it, so that we fail if we can't acquire
* it. */
- r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
} else {
@@ -615,7 +633,7 @@ static int service_add_default_dependencies(Service *s) {
/* In the --user instance there's no sysinit.target,
* in that case require basic.target instead. */
- r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_BASIC_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
}
@@ -623,12 +641,12 @@ static int service_add_default_dependencies(Service *s) {
/* Second, if the rest of the base system is in the same
* transaction, order us after it, but do not pull it in or
* even require it. */
- r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_BASIC_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_BASIC_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
/* Third, add us in for normal shutdown. */
- return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ 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) {
@@ -659,12 +677,12 @@ static int service_setup_bus_name(Service *s) {
if (!s->bus_name)
return 0;
- r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m");
/* We always want to be ordered against dbus.socket if both are in the transaction. */
- r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_DBUS_SOCKET, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_DBUS_SOCKET, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m");
@@ -917,8 +935,8 @@ static int service_load_pid_file(Service *s, bool may_warn) {
prio = may_warn ? LOG_INFO : LOG_DEBUG;
fd = chase_symlinks(s->pid_file, NULL, CHASE_OPEN|CHASE_SAFE, NULL);
- if (fd == -EPERM) {
- log_unit_full(UNIT(s), LOG_DEBUG, fd, "Permission denied while opening PID file or potentially unsafe symlink chain, will now retry with relaxed checks: %s", s->pid_file);
+ if (fd == -ENOLINK) {
+ log_unit_full(UNIT(s), LOG_DEBUG, fd, "Potentially unsafe symlink chain, will now retry with relaxed checks: %s", s->pid_file);
questionable_pid_file = true;
@@ -1018,6 +1036,9 @@ static void service_set_state(Service *s, ServiceState state) {
assert(s);
+ if (s->state != state)
+ bus_unit_send_pending_change_signal(UNIT(s), false);
+
table = s->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table;
old_state = s->state;
@@ -1029,7 +1050,7 @@ static void service_set_state(Service *s, ServiceState state) {
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING,
SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
SERVICE_AUTO_RESTART))
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
@@ -1037,7 +1058,7 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state,
SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING, SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
service_unwatch_main_pid(s);
s->main_command = NULL;
@@ -1046,7 +1067,7 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
service_unwatch_control_pid(s);
s->control_command = NULL;
@@ -1061,11 +1082,14 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING, SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
!(state == SERVICE_DEAD && UNIT(s)->job))
service_close_socket_fd(s);
+ if (state != SERVICE_START)
+ s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source);
+
if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
service_stop_watchdog(s);
@@ -1097,7 +1121,7 @@ static usec_t service_coldplug_timeout(Service *s) {
return usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec);
case SERVICE_STOP:
- case SERVICE_STOP_SIGABRT:
+ case SERVICE_STOP_WATCHDOG:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
case SERVICE_STOP_POST:
@@ -1132,7 +1156,7 @@ static int service_coldplug(Unit *u) {
(IN_SET(s->deserialized_state,
SERVICE_START, SERVICE_START_POST,
SERVICE_RUNNING, SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
r = unit_watch_pid(UNIT(s), s->main_pid);
if (r < 0)
@@ -1144,7 +1168,7 @@ static int service_coldplug(Unit *u) {
IN_SET(s->deserialized_state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
r = unit_watch_pid(UNIT(s), s->control_pid);
if (r < 0)
@@ -1178,21 +1202,23 @@ static int service_coldplug(Unit *u) {
return 0;
}
-static int service_collect_fds(Service *s,
- int **fds,
- char ***fd_names,
- unsigned *n_storage_fds,
- unsigned *n_socket_fds) {
+static int service_collect_fds(
+ Service *s,
+ int **fds,
+ char ***fd_names,
+ size_t *n_socket_fds,
+ size_t *n_storage_fds) {
_cleanup_strv_free_ char **rfd_names = NULL;
_cleanup_free_ int *rfds = NULL;
- unsigned rn_socket_fds = 0, rn_storage_fds = 0;
+ size_t rn_socket_fds = 0, rn_storage_fds = 0;
int r;
assert(s);
assert(fds);
assert(fd_names);
assert(n_socket_fds);
+ assert(n_storage_fds);
if (s->socket_fd >= 0) {
@@ -1203,7 +1229,7 @@ static int service_collect_fds(Service *s,
return -ENOMEM;
rfds[0] = s->socket_fd;
- rfd_names = strv_new("connection", NULL);
+ rfd_names = strv_new("connection");
if (!rfd_names)
return -ENOMEM;
@@ -1256,7 +1282,7 @@ static int service_collect_fds(Service *s,
if (s->n_fd_store > 0) {
ServiceFDStore *fs;
- unsigned n_fds;
+ size_t n_fds;
char **nl;
int *t;
@@ -1294,6 +1320,63 @@ static int service_collect_fds(Service *s,
return 0;
}
+static int service_allocate_exec_fd_event_source(
+ Service *s,
+ int fd,
+ sd_event_source **ret_event_source) {
+
+ _cleanup_(sd_event_source_unrefp) sd_event_source *source = NULL;
+ int r;
+
+ assert(s);
+ assert(fd >= 0);
+ assert(ret_event_source);
+
+ r = sd_event_add_io(UNIT(s)->manager->event, &source, fd, 0, service_dispatch_exec_io, s);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to allocate exec_fd event source: %m");
+
+ /* This is a bit lower priority than SIGCHLD, as that carries a lot more interesting failure information */
+
+ r = sd_event_source_set_priority(source, SD_EVENT_PRIORITY_NORMAL-3);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to adjust priority of exec_fd event source: %m");
+
+ (void) sd_event_source_set_description(source, "service event_fd");
+
+ r = sd_event_source_set_io_fd_own(source, true);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to pass ownership of fd to event source: %m");
+
+ *ret_event_source = TAKE_PTR(source);
+ return 0;
+}
+
+static int service_allocate_exec_fd(
+ Service *s,
+ sd_event_source **ret_event_source,
+ int* ret_exec_fd) {
+
+ _cleanup_close_pair_ int p[2] = { -1, -1 };
+ int r;
+
+ assert(s);
+ assert(ret_event_source);
+ assert(ret_exec_fd);
+
+ if (pipe2(p, O_CLOEXEC|O_NONBLOCK) < 0)
+ return log_unit_error_errno(UNIT(s), errno, "Failed to allocate exec_fd pipe: %m");
+
+ r = service_allocate_exec_fd_event_source(s, p[0], ret_event_source);
+ if (r < 0)
+ return r;
+
+ p[0] = -1;
+ *ret_exec_fd = TAKE_FD(p[1]);
+
+ return 0;
+}
+
static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) {
assert(s);
@@ -1320,14 +1403,17 @@ static int service_spawn(
ExecFlags flags,
pid_t *_pid) {
- ExecParameters exec_params = {
+ _cleanup_(exec_params_clear) ExecParameters exec_params = {
.flags = flags,
.stdin_fd = -1,
.stdout_fd = -1,
.stderr_fd = -1,
+ .exec_fd = -1,
};
_cleanup_strv_free_ char **final_env = NULL, **our_env = NULL, **fd_names = NULL;
- unsigned n_storage_fds = 0, n_socket_fds = 0, n_env = 0;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *exec_fd_source = NULL;
+ size_t n_socket_fds = 0, n_storage_fds = 0, n_env = 0;
+ _cleanup_close_ int exec_fd = -1;
_cleanup_free_ int *fds = NULL;
pid_t pid;
int r;
@@ -1336,7 +1422,7 @@ static int service_spawn(
assert(c);
assert(_pid);
- r = unit_prepare_exec(UNIT(s));
+ r = unit_prepare_exec(UNIT(s)); /* This realizes the cgroup, among other things */
if (r < 0)
return r;
@@ -1353,11 +1439,19 @@ static int service_spawn(
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
- r = service_collect_fds(s, &fds, &fd_names, &n_storage_fds, &n_socket_fds);
+ r = service_collect_fds(s, &fds, &fd_names, &n_socket_fds, &n_storage_fds);
if (r < 0)
return r;
- log_unit_debug(UNIT(s), "Passing %i fds to service", n_storage_fds + n_socket_fds);
+ log_unit_debug(UNIT(s), "Passing %zu fds to service", n_socket_fds + n_storage_fds);
+ }
+
+ if (!FLAGS_SET(flags, EXEC_IS_CONTROL) && s->type == SERVICE_EXEC) {
+ assert(!s->exec_fd_event_source);
+
+ r = service_allocate_exec_fd(s, &exec_fd_source, &exec_fd);
+ if (r < 0)
+ return r;
}
r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout));
@@ -1433,32 +1527,31 @@ static int service_spawn(
}
}
- unit_set_exec_params(UNIT(s), &exec_params);
+ r = unit_set_exec_params(UNIT(s), &exec_params);
+ if (r < 0)
+ return r;
final_env = strv_env_merge(2, exec_params.environment, our_env, NULL);
if (!final_env)
return -ENOMEM;
- /* System services should get a new keyring by default. */
- SET_FLAG(exec_params.flags, EXEC_NEW_KEYRING, MANAGER_IS_SYSTEM(UNIT(s)->manager));
-
/* System D-Bus needs nss-systemd disabled, so that we don't deadlock */
SET_FLAG(exec_params.flags, EXEC_NSS_BYPASS_BUS,
MANAGER_IS_SYSTEM(UNIT(s)->manager) && unit_has_name(UNIT(s), SPECIAL_DBUS_SERVICE));
- exec_params.argv = c->argv;
- exec_params.environment = final_env;
+ strv_free_and_replace(exec_params.environment, final_env);
exec_params.fds = fds;
exec_params.fd_names = fd_names;
- exec_params.n_storage_fds = n_storage_fds;
exec_params.n_socket_fds = n_socket_fds;
- exec_params.watchdog_usec = s->watchdog_usec;
+ exec_params.n_storage_fds = n_storage_fds;
+ exec_params.watchdog_usec = service_get_watchdog_usec(s);
exec_params.selinux_context_net = s->socket_fd_selinux_context_net;
if (s->type == SERVICE_IDLE)
exec_params.idle_pipe = UNIT(s)->manager->idle_pipe;
exec_params.stdin_fd = s->stdin_fd;
exec_params.stdout_fd = s->stdout_fd;
exec_params.stderr_fd = s->stderr_fd;
+ exec_params.exec_fd = exec_fd;
r = exec_spawn(UNIT(s),
c,
@@ -1470,6 +1563,9 @@ static int service_spawn(
if (r < 0)
return r;
+ s->exec_fd_event_source = TAKE_PTR(exec_fd_source);
+ s->exec_fd_hot = false;
+
r = unit_watch_pid(UNIT(s), pid);
if (r < 0) /* FIXME: we need to do something here */
return r;
@@ -1604,8 +1700,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
if (s->result == SERVICE_SUCCESS)
s->result = f;
- if (s->result != SERVICE_SUCCESS)
- log_unit_warning(UNIT(s), "Failed with result '%s'.", service_result_to_string(s->result));
+ unit_log_result(UNIT(s), s->result == SERVICE_SUCCESS, service_result_to_string(s->result));
if (allow_restart && service_shall_restart(s))
s->will_auto_restart = true;
@@ -1674,7 +1769,6 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
s->result = f;
service_unwatch_control_pid(s);
-
(void) unit_enqueue_rewatch_pids(UNIT(s));
s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST];
@@ -1684,7 +1778,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
r = service_spawn(s,
s->control_command,
s->timeout_stop_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL|EXEC_SETENV_RESULT,
+ EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL|EXEC_SETENV_RESULT|EXEC_CONTROL_CGROUP,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1703,8 +1797,8 @@ fail:
static int state_to_kill_operation(ServiceState state) {
switch (state) {
- case SERVICE_STOP_SIGABRT:
- return KILL_ABORT;
+ case SERVICE_STOP_WATCHDOG:
+ return KILL_WATCHDOG;
case SERVICE_STOP_SIGTERM:
case SERVICE_FINAL_SIGTERM:
@@ -1750,9 +1844,9 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
goto fail;
service_set_state(s, state);
- } else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill)
+ } else if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill)
service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_SUCCESS);
- else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
+ else if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
service_enter_stop_post(s, SERVICE_SUCCESS);
else if (state == SERVICE_FINAL_SIGTERM && s->kill_context.send_sigkill)
service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS);
@@ -1764,7 +1858,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
fail:
log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
- if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
+ if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES);
else
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
@@ -1799,7 +1893,7 @@ static void service_enter_stop(Service *s, ServiceResult f) {
r = service_spawn(s,
s->control_command,
s->timeout_stop_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_SETENV_RESULT,
+ EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_SETENV_RESULT|EXEC_CONTROL_CGROUP,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1877,7 +1971,7 @@ static void service_enter_start_post(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL,
+ EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1947,6 +2041,12 @@ static void service_enter_start(Service *s) {
goto fail;
}
+ /* We force a fake state transition here. Otherwise, the unit would go directly from
+ * SERVICE_DEAD to SERVICE_DEAD without SERVICE_ACTIVATING or SERVICE_ACTIVE
+ * inbetween. This way we can later trigger actions that depend on the state
+ * transition, including SuccessAction=. */
+ service_set_state(s, SERVICE_START);
+
service_enter_start_post(s);
return;
}
@@ -1981,14 +2081,12 @@ static void service_enter_start(Service *s) {
s->control_pid = pid;
service_set_state(s, SERVICE_START);
- } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY)) {
+ } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_EXEC)) {
- /* For oneshot services we wait until the start
- * process exited, too, but it is our main process. */
+ /* For oneshot services we wait until the start process exited, too, but it is our main process. */
- /* For D-Bus services we know the main pid right away,
- * but wait for the bus name to appear on the
- * bus. Notify services are similar. */
+ /* For D-Bus services we know the main pid right away, but wait for the bus name to appear on the
+ * bus. 'notify' and 'exec' services are similar. */
service_set_main_pid(s, pid);
service_set_state(s, SERVICE_START);
@@ -2117,7 +2215,7 @@ static void service_enter_reload(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL,
+ EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP,
&s->control_pid);
if (r < 0)
goto fail;
@@ -2157,7 +2255,8 @@ static void service_run_next_control(Service *s) {
timeout,
EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|
(IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)|
- (IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0),
+ (IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0)|
+ (IN_SET(s->control_command_id, SERVICE_EXEC_START_POST, SERVICE_EXEC_RELOAD, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_CONTROL_CGROUP : 0),
&s->control_pid);
if (r < 0)
goto fail;
@@ -2216,7 +2315,7 @@ static int service_start(Unit *u) {
/* We cannot fulfill this request right now, try again later
* please! */
if (IN_SET(s->state,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
return -EAGAIN;
@@ -2253,15 +2352,17 @@ static int service_start(Unit *u) {
s->main_pid_alien = false;
s->forbid_restart = false;
- u->reset_accounting = true;
-
s->status_text = mfree(s->status_text);
s->status_errno = 0;
s->notify_state = NOTIFY_UNKNOWN;
+ s->watchdog_original_usec = s->watchdog_usec;
s->watchdog_override_enable = false;
- s->watchdog_override_usec = 0;
+ s->watchdog_override_usec = USEC_INFINITY;
+
+ exec_command_reset_status_list_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
+ exec_status_reset(&s->main_exec_status);
/* This is not an automatic restart? Flush the restart counter then */
if (s->flush_n_restarts) {
@@ -2269,6 +2370,8 @@ static int service_start(Unit *u) {
s->flush_n_restarts = false;
}
+ u->reset_accounting = true;
+
service_enter_start_pre(s);
return 1;
}
@@ -2283,7 +2386,7 @@ static int service_stop(Unit *u) {
/* Already on it */
if (IN_SET(s->state,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
return 0;
@@ -2342,13 +2445,13 @@ static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecC
}
static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command) {
+ _cleanup_free_ char *args = NULL, *p = NULL;
+ size_t allocated = 0, length = 0;
Service *s = SERVICE(u);
+ const char *type, *key;
ServiceExecCommand id;
unsigned idx;
- const char *type;
char **arg;
- _cleanup_free_ char *args = NULL, *p = NULL;
- size_t allocated = 0, length = 0;
assert(s);
assert(f);
@@ -2367,16 +2470,16 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command
idx = service_exec_command_index(u, id, command);
STRV_FOREACH(arg, command->argv) {
- size_t n;
_cleanup_free_ char *e = NULL;
+ size_t n;
- e = xescape(*arg, WHITESPACE);
+ e = cescape(*arg);
if (!e)
- return -ENOMEM;
+ return log_oom();
n = strlen(e);
if (!GREEDY_REALLOC(args, allocated, length + 1 + n + 1))
- return -ENOMEM;
+ return log_oom();
if (length > 0)
args[length++] = ' ';
@@ -2386,16 +2489,16 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command
}
if (!GREEDY_REALLOC(args, allocated, length + 1))
- return -ENOMEM;
+ return log_oom();
+
args[length++] = 0;
- p = xescape(command->path, WHITESPACE);
+ p = cescape(command->path);
if (!p)
return -ENOMEM;
- fprintf(f, "%s-command=%s %u %s %s\n", type, service_exec_command_to_string(id), idx, p, args);
-
- return 0;
+ key = strjoina(type, "-command");
+ return serialize_item_format(f, key, "%s %u %s %s", service_exec_command_to_string(id), idx, p, args);
}
static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
@@ -2407,47 +2510,55 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", service_state_to_string(s->state));
- unit_serialize_item(u, f, "result", service_result_to_string(s->result));
- unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result));
+ (void) serialize_item(f, "state", service_state_to_string(s->state));
+ (void) serialize_item(f, "result", service_result_to_string(s->result));
+ (void) serialize_item(f, "reload-result", service_result_to_string(s->reload_result));
if (s->control_pid > 0)
- unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid);
+ (void) serialize_item_format(f, "control-pid", PID_FMT, s->control_pid);
if (s->main_pid_known && s->main_pid > 0)
- unit_serialize_item_format(u, f, "main-pid", PID_FMT, s->main_pid);
+ (void) serialize_item_format(f, "main-pid", PID_FMT, s->main_pid);
- unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
- unit_serialize_item(u, f, "bus-name-good", yes_no(s->bus_name_good));
- unit_serialize_item(u, f, "bus-name-owner", s->bus_name_owner);
+ (void) serialize_bool(f, "main-pid-known", s->main_pid_known);
+ (void) serialize_bool(f, "bus-name-good", s->bus_name_good);
+ (void) serialize_bool(f, "bus-name-owner", s->bus_name_owner);
- unit_serialize_item_format(u, f, "n-restarts", "%u", s->n_restarts);
- unit_serialize_item(u, f, "flush-n-restarts", yes_no(s->flush_n_restarts));
+ (void) serialize_item_format(f, "n-restarts", "%u", s->n_restarts);
+ (void) serialize_bool(f, "flush-n-restarts", s->flush_n_restarts);
- r = unit_serialize_item_escaped(u, f, "status-text", s->status_text);
+ r = serialize_item_escaped(f, "status-text", s->status_text);
if (r < 0)
return r;
service_serialize_exec_command(u, f, s->control_command);
service_serialize_exec_command(u, f, s->main_command);
- r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd);
+ r = serialize_fd(f, fds, "stdin-fd", s->stdin_fd);
if (r < 0)
return r;
- r = unit_serialize_item_fd(u, f, fds, "stdout-fd", s->stdout_fd);
+ r = serialize_fd(f, fds, "stdout-fd", s->stdout_fd);
if (r < 0)
return r;
- r = unit_serialize_item_fd(u, f, fds, "stderr-fd", s->stderr_fd);
+ r = serialize_fd(f, fds, "stderr-fd", s->stderr_fd);
if (r < 0)
return r;
+ if (s->exec_fd_event_source) {
+ r = serialize_fd(f, fds, "exec-fd", sd_event_source_get_io_fd(s->exec_fd_event_source));
+ if (r < 0)
+ return r;
+
+ (void) serialize_bool(f, "exec-fd-hot", s->exec_fd_hot);
+ }
+
if (UNIT_ISSET(s->accept_socket)) {
- r = unit_serialize_item(u, f, "accept-socket", UNIT_DEREF(s->accept_socket)->id);
+ r = serialize_item(f, "accept-socket", UNIT_DEREF(s->accept_socket)->id);
if (r < 0)
return r;
}
- r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_fd);
+ r = serialize_fd(f, fds, "socket-fd", s->socket_fd);
if (r < 0)
return r;
@@ -2457,30 +2568,34 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
copy = fdset_put_dup(fds, fs->fd);
if (copy < 0)
- return copy;
+ return log_error_errno(copy, "Failed to copy file descriptor for serialization: %m");
c = cescape(fs->fdname);
+ if (!c)
+ return log_oom();
- unit_serialize_item_format(u, f, "fd-store-fd", "%i %s", copy, strempty(c));
+ (void) serialize_item_format(f, "fd-store-fd", "%i %s", copy, c);
}
if (s->main_exec_status.pid > 0) {
- unit_serialize_item_format(u, f, "main-exec-status-pid", PID_FMT, s->main_exec_status.pid);
- dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
- dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
+ (void) serialize_item_format(f, "main-exec-status-pid", PID_FMT, s->main_exec_status.pid);
+ (void) serialize_dual_timestamp(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
+ (void) serialize_dual_timestamp(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
- unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code);
- unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status);
+ (void) serialize_item_format(f, "main-exec-status-code", "%i", s->main_exec_status.code);
+ (void) serialize_item_format(f, "main-exec-status-status", "%i", s->main_exec_status.status);
}
}
- dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
-
- unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart));
+ (void) serialize_dual_timestamp(f, "watchdog-timestamp", &s->watchdog_timestamp);
+ (void) serialize_bool(f, "forbid-restart", s->forbid_restart);
if (s->watchdog_override_enable)
- unit_serialize_item_format(u, f, "watchdog-override-usec", USEC_FMT, s->watchdog_override_usec);
+ (void) serialize_item_format(f, "watchdog-override-usec", USEC_FMT, s->watchdog_override_usec);
+
+ if (s->watchdog_original_usec != USEC_INFINITY)
+ (void) serialize_item_format(f, "watchdog-original-usec", USEC_FMT, s->watchdog_original_usec);
return 0;
}
@@ -2516,10 +2631,10 @@ static int service_deserialize_exec_command(Unit *u, const char *key, const char
_cleanup_free_ char *arg = NULL;
r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE);
+ if (r < 0)
+ return r;
if (r == 0)
break;
- else if (r < 0)
- return r;
switch (state) {
case STATE_EXEC_COMMAND_TYPE:
@@ -2658,18 +2773,16 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
r = cunescape(value, 0, &t);
if (r < 0)
- log_unit_debug_errno(u, r, "Failed to unescape status text: %s", value);
- else {
- free(s->status_text);
- s->status_text = t;
- }
+ log_unit_debug_errno(u, r, "Failed to unescape status text '%s': %m", value);
+ else
+ free_and_replace(s->status_text, t);
} else if (streq(key, "accept-socket")) {
Unit *socket;
r = manager_load_unit(u->manager, value, NULL, NULL, &socket);
if (r < 0)
- log_unit_debug_errno(u, r, "Failed to load accept-socket unit: %s", value);
+ log_unit_debug_errno(u, r, "Failed to load accept-socket unit '%s': %m", value);
else {
unit_ref_set(&s->accept_socket, u, socket);
SOCKET(socket)->n_connections++;
@@ -2731,11 +2844,11 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
else
s->main_exec_status.status = i;
} else if (streq(key, "main-exec-status-start"))
- dual_timestamp_deserialize(value, &s->main_exec_status.start_timestamp);
+ deserialize_dual_timestamp(value, &s->main_exec_status.start_timestamp);
else if (streq(key, "main-exec-status-exit"))
- dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
+ deserialize_dual_timestamp(value, &s->main_exec_status.exit_timestamp);
else if (streq(key, "watchdog-timestamp"))
- dual_timestamp_deserialize(value, &s->watchdog_timestamp);
+ deserialize_dual_timestamp(value, &s->watchdog_timestamp);
else if (streq(key, "forbid-restart")) {
int b;
@@ -2774,14 +2887,28 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
s->stderr_fd = fdset_remove(fds, fd);
s->exec_context.stdio_as_fds = true;
}
+ } else if (streq(key, "exec-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_unit_debug(u, "Failed to parse exec-fd value: %s", value);
+ else {
+ s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source);
+
+ fd = fdset_remove(fds, fd);
+ if (service_allocate_exec_fd_event_source(s, fd, &s->exec_fd_event_source) < 0)
+ safe_close(fd);
+ }
} else if (streq(key, "watchdog-override-usec")) {
- usec_t watchdog_override_usec;
- if (timestamp_deserialize(value, &watchdog_override_usec) < 0)
+ if (deserialize_usec(value, &s->watchdog_override_usec) < 0)
log_unit_debug(u, "Failed to parse watchdog_override_usec value: %s", value);
- else {
+ else
s->watchdog_override_enable = true;
- s->watchdog_override_usec = watchdog_override_usec;
- }
+
+ } else if (streq(key, "watchdog-original-usec")) {
+ if (deserialize_usec(value, &s->watchdog_original_usec) < 0)
+ log_unit_debug(u, "Failed to parse watchdog_original_usec value: %s", value);
+
} else if (STR_IN_SET(key, "main-command", "control-command")) {
r = service_deserialize_exec_command(u, key, value);
if (r < 0)
@@ -2857,7 +2984,7 @@ static int service_watch_pid_file(Service *s) {
log_unit_debug(UNIT(s), "Setting watch for PID file %s", s->pid_file_pathspec->path);
- r = path_spec_watch(s->pid_file_pathspec, service_dispatch_io);
+ r = path_spec_watch(s->pid_file_pathspec, service_dispatch_inotify_io);
if (r < 0)
goto fail;
@@ -2901,7 +3028,7 @@ static int service_demand_pid_file(Service *s) {
return service_watch_pid_file(s);
}
-static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata) {
+static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata) {
PathSpec *p = userdata;
Service *s;
@@ -2934,6 +3061,59 @@ fail:
return 0;
}
+static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t events, void *userdata) {
+ Service *s = SERVICE(userdata);
+
+ assert(s);
+
+ log_unit_debug(UNIT(s), "got exec-fd event");
+
+ /* If Type=exec is set, we'll consider a service started successfully the instant we invoked execve()
+ * successfully for it. We implement this through a pipe() towards the child, which the kernel automatically
+ * closes for us due to O_CLOEXEC on execve() in the child, which then triggers EOF on the pipe in the
+ * parent. We need to be careful however, as there are other reasons that we might cause the child's side of
+ * the pipe to be closed (for example, a simple exit()). To deal with that we'll ignore EOFs on the pipe unless
+ * the child signalled us first that it is about to call the execve(). It does so by sending us a simple
+ * non-zero byte via the pipe. We also provide the child with a way to inform us in case execve() failed: if it
+ * sends a zero byte we'll ignore POLLHUP on the fd again. */
+
+ for (;;) {
+ uint8_t x;
+ ssize_t n;
+
+ n = read(fd, &x, sizeof(x));
+ if (n < 0) {
+ if (errno == EAGAIN) /* O_NONBLOCK in effect → everything queued has now been processed. */
+ return 0;
+
+ return log_unit_error_errno(UNIT(s), errno, "Failed to read from exec_fd: %m");
+ }
+ if (n == 0) { /* EOF → the event we are waiting for */
+
+ s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source);
+
+ if (s->exec_fd_hot) { /* Did the child tell us to expect EOF now? */
+ log_unit_debug(UNIT(s), "Got EOF on exec-fd");
+
+ s->exec_fd_hot = false;
+
+ /* Nice! This is what we have been waiting for. Transition to next state. */
+ if (s->type == SERVICE_EXEC && s->state == SERVICE_START)
+ service_enter_start_post(s);
+ } else
+ log_unit_debug(UNIT(s), "Got EOF on exec-fd while it was disabled, ignoring.");
+
+ return 0;
+ }
+
+ /* A byte was read → this turns on/off the exec fd logic */
+ assert(n == sizeof(x));
+ s->exec_fd_hot = x;
+ }
+
+ return 0;
+}
+
static void service_notify_cgroup_empty_event(Unit *u) {
Service *s = SERVICE(u);
@@ -2980,7 +3160,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
service_enter_running(s, SERVICE_SUCCESS);
break;
- case SERVICE_STOP_SIGABRT:
+ case SERVICE_STOP_WATCHDOG:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
@@ -3054,21 +3234,13 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* When this is a successful exit, let's log about the exit code on DEBUG level. If this is a failure
* and the process exited on its own via exit(), then let's make this a NOTICE, under the assumption
- * that the service already logged the reason at a higher log level on its own. However, if the service
- * died due to a signal, then it most likely didn't say anything about any reason, hence let's raise
- * our log level to WARNING then. */
-
- log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG :
- (code == CLD_EXITED ? LOG_NOTICE : LOG_WARNING),
- LOG_UNIT_MESSAGE(u, "Main process exited, code=%s, status=%i/%s",
- sigchld_code_to_string(code), status,
- strna(code == CLD_EXITED
- ? exit_status_to_string(status, EXIT_STATUS_FULL)
- : signal_to_string(status))),
- "EXIT_CODE=%s", sigchld_code_to_string(code),
- "EXIT_STATUS=%i", status,
- LOG_UNIT_ID(u),
- LOG_UNIT_INVOCATION_ID(u));
+ * that the service already logged the reason at a higher log level on its own. (Internally,
+ * unit_log_process_exit() will possibly bump this to WARNING if the service died due to a signal.) */
+ unit_log_process_exit(
+ u, f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "Main process",
+ service_exec_command_to_string(SERVICE_EXEC_START),
+ code, status);
if (s->result == SERVICE_SUCCESS)
s->result = f;
@@ -3124,7 +3296,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
service_enter_running(s, f);
break;
- case SERVICE_STOP_SIGABRT:
+ case SERVICE_STOP_WATCHDOG:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
@@ -3157,9 +3329,11 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
f = SERVICE_SUCCESS;
}
- log_unit_full(u, f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
- "Control process exited, code=%s status=%i",
- sigchld_code_to_string(code), status);
+ unit_log_process_exit(
+ u, f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "Control process",
+ service_exec_command_to_string(s->control_command_id),
+ code, status);
if (s->result == SERVICE_SUCCESS)
s->result = f;
@@ -3259,7 +3433,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
break;
- case SERVICE_STOP_SIGABRT:
+ case SERVICE_STOP_WATCHDOG:
case SERVICE_STOP_SIGTERM:
case SERVICE_STOP_SIGKILL:
if (main_pid_good(s) <= 0)
@@ -3330,8 +3504,8 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
- case SERVICE_STOP_SIGABRT:
- log_unit_warning(UNIT(s), "State 'stop-sigabrt' timed out. Terminating.");
+ case SERVICE_STOP_WATCHDOG:
+ log_unit_warning(UNIT(s), "State 'stop-watchdog' timed out. Terminating.");
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
@@ -3410,7 +3584,7 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!",
format_timespan(t, sizeof(t), watchdog_usec, 1));
- service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG);
+ service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_WATCHDOG);
} else
log_unit_warning(UNIT(s), "Watchdog disabled! Ignoring watchdog timeout (limit %s)!",
format_timespan(t, sizeof(t), watchdog_usec, 1));
@@ -3498,7 +3672,11 @@ static void service_notify_message(
}
if (r > 0) {
service_set_main_pid(s, new_main_pid);
- unit_watch_pid(UNIT(s), new_main_pid);
+
+ r = unit_watch_pid(UNIT(s), new_main_pid);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", new_main_pid);
+
notify_dbus = true;
}
}
@@ -3549,8 +3727,12 @@ static void service_notify_message(
_cleanup_free_ char *t = NULL;
if (!isempty(e)) {
- if (!utf8_is_valid(e))
- log_unit_warning(u, "Status message in notification message is not UTF-8 clean.");
+ /* Note that this size limit check is mostly paranoia: since the datagram size we are willing
+ * to process is already limited to NOTIFY_BUFFER_MAX, this limit here should never be hit. */
+ if (strlen(e) > STATUS_TEXT_MAX)
+ log_unit_warning(u, "Status message overly long (%zu > %u), ignoring.", strlen(e), STATUS_TEXT_MAX);
+ else if (!utf8_is_valid(e))
+ log_unit_warning(u, "Status message in notification message is not UTF-8 clean, ignoring.");
else {
t = strdup(e);
if (!t)
@@ -3572,7 +3754,7 @@ static void service_notify_message(
status_errno = parse_errno(e);
if (status_errno < 0)
log_unit_warning_errno(u, status_errno,
- "Failed to parse ERRNO= field in notification message: %s", e);
+ "Failed to parse ERRNO= field value '%s' in notification message: %m", e);
else if (s->status_errno != status_errno) {
s->status_errno = status_errno;
notify_dbus = true;
@@ -3599,7 +3781,7 @@ static void service_notify_message(
if (safe_atou64(e, &watchdog_override_usec) < 0)
log_unit_warning(u, "Failed to parse WATCHDOG_USEC=%s", e);
else
- service_reset_watchdog_timeout(s, watchdog_override_usec);
+ service_override_watchdog_timeout(s, watchdog_override_usec);
}
/* Process FD store messages. Either FDSTOREREMOVE=1 for removal, or FDSTORE=1 for addition. In both cases,
@@ -3817,7 +3999,7 @@ static bool service_needs_console(Unit *u) {
SERVICE_RUNNING,
SERVICE_RELOAD,
SERVICE_STOP,
- SERVICE_STOP_SIGABRT,
+ SERVICE_STOP_WATCHDOG,
SERVICE_STOP_SIGTERM,
SERVICE_STOP_SIGKILL,
SERVICE_STOP_POST,
@@ -3825,6 +4007,21 @@ static bool service_needs_console(Unit *u) {
SERVICE_FINAL_SIGKILL);
}
+static int service_exit_status(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(u);
+
+ if (s->main_exec_status.pid <= 0 ||
+ !dual_timestamp_is_set(&s->main_exec_status.exit_timestamp))
+ return -ENODATA;
+
+ if (s->main_exec_status.code != CLD_EXITED)
+ return -EBADE;
+
+ return s->main_exec_status.status;
+}
+
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
@@ -3843,7 +4040,8 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
[SERVICE_ONESHOT] = "oneshot",
[SERVICE_DBUS] = "dbus",
[SERVICE_NOTIFY] = "notify",
- [SERVICE_IDLE] = "idle"
+ [SERVICE_IDLE] = "idle",
+ [SERVICE_EXEC] = "exec",
};
DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
@@ -3944,6 +4142,7 @@ const UnitVTable service_vtable = {
.get_timeout = service_get_timeout,
.needs_console = service_needs_console,
+ .exit_status = service_exit_status,
.status_message_formats = {
.starting_stopping = {
diff --git a/src/core/service.h b/src/core/service.h
index 9c06e91883..9c4340c70e 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -30,6 +30,7 @@ typedef enum ServiceType {
SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */
SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */
SERVICE_IDLE, /* much like simple, but delay exec() until all jobs are dispatched. */
+ SERVICE_EXEC, /* we fork and wait until we execute exec() (this means our own setup is waited for) */
_SERVICE_TYPE_MAX,
_SERVICE_TYPE_INVALID = -1
} ServiceType;
@@ -98,8 +99,9 @@ struct Service {
usec_t runtime_max_usec;
dual_timestamp watchdog_timestamp;
- usec_t watchdog_usec;
- usec_t watchdog_override_usec;
+ usec_t watchdog_usec; /* the requested watchdog timeout in the unit file */
+ usec_t watchdog_original_usec; /* the watchdog timeout that was in effect when the unit was started, i.e. the timeout the forked off processes currently see */
+ usec_t watchdog_override_usec; /* the watchdog timeout requested by the service itself through sd_notify() */
bool watchdog_override_enable;
sd_event_source *watchdog_event_source;
@@ -165,6 +167,8 @@ struct Service {
NotifyAccess notify_access;
NotifyState notify_state;
+ sd_event_source *exec_fd_event_source;
+
ServiceFDStore *fd_store;
size_t n_fd_store;
unsigned n_fd_store_max;
@@ -179,6 +183,7 @@ struct Service {
unsigned n_restarts;
bool flush_n_restarts;
+ bool exec_fd_hot;
};
extern const UnitVTable service_vtable;
@@ -202,3 +207,5 @@ const char* service_result_to_string(ServiceResult i) _const_;
ServiceResult service_result_from_string(const char *s) _pure_;
DEFINE_CAST(SERVICE, Service);
+
+#define STATUS_TEXT_MAX (16U*1024U)
diff --git a/src/core/show-status.c b/src/core/show-status.c
index 63262cc716..f748a82084 100644
--- a/src/core/show-status.c
+++ b/src/core/show-status.c
@@ -5,34 +5,38 @@
#include "io-util.h"
#include "parse-util.h"
#include "show-status.h"
+#include "string-table.h"
#include "string-util.h"
#include "terminal-util.h"
#include "util.h"
+static const char* const show_status_table[_SHOW_STATUS_MAX] = {
+ [SHOW_STATUS_NO] = "no",
+ [SHOW_STATUS_AUTO] = "auto",
+ [SHOW_STATUS_TEMPORARY] = "temporary",
+ [SHOW_STATUS_YES] = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(show_status, ShowStatus, SHOW_STATUS_YES);
+
int parse_show_status(const char *v, ShowStatus *ret) {
- int r;
+ ShowStatus s;
- assert(v);
assert(ret);
- if (streq(v, "auto")) {
- *ret = SHOW_STATUS_AUTO;
- return 0;
- }
-
- r = parse_boolean(v);
- if (r < 0)
- return r;
+ s = show_status_from_string(v);
+ if (s < 0 || s == SHOW_STATUS_TEMPORARY)
+ return -EINVAL;
- *ret = r ? SHOW_STATUS_YES : SHOW_STATUS_NO;
+ *ret = s;
return 0;
}
-int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
+int status_vprintf(const char *status, ShowStatusFlags flags, const char *format, va_list ap) {
static const char status_indent[] = " "; /* "[" STATUS "] " */
_cleanup_free_ char *s = NULL;
_cleanup_close_ int fd = -1;
- struct iovec iovec[6] = {};
+ struct iovec iovec[7] = {};
int n = 0;
static bool prev_ephemeral;
@@ -53,7 +57,7 @@ int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char
if (fd < 0)
return fd;
- if (ellipse) {
+ if (FLAGS_SET(flags, SHOW_STATUS_ELLIPSIZE)) {
char *e;
size_t emax, sl;
int c;
@@ -69,15 +73,12 @@ int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char
emax = 3;
e = ellipsize(s, emax, 50);
- if (e) {
- free(s);
- s = e;
- }
+ if (e)
+ free_and_replace(s, e);
}
if (prev_ephemeral)
- iovec[n++] = IOVEC_MAKE_STRING("\r" ANSI_ERASE_TO_END_OF_LINE);
- prev_ephemeral = ephemeral;
+ iovec[n++] = IOVEC_MAKE_STRING(ANSI_REVERSE_LINEFEED "\r" ANSI_ERASE_TO_END_OF_LINE);
if (status) {
if (!isempty(status)) {
@@ -89,8 +90,11 @@ int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char
}
iovec[n++] = IOVEC_MAKE_STRING(s);
- if (!ephemeral)
- iovec[n++] = IOVEC_MAKE_STRING("\n");
+ iovec[n++] = IOVEC_MAKE_STRING("\n");
+
+ if (prev_ephemeral && !FLAGS_SET(flags, SHOW_STATUS_EPHEMERAL))
+ iovec[n++] = IOVEC_MAKE_STRING(ANSI_ERASE_TO_END_OF_LINE);
+ prev_ephemeral = FLAGS_SET(flags, SHOW_STATUS_EPHEMERAL) ;
if (writev(fd, iovec, n) < 0)
return -errno;
@@ -98,14 +102,14 @@ int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char
return 0;
}
-int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
+int status_printf(const char *status, ShowStatusFlags flags, const char *format, ...) {
va_list ap;
int r;
assert(format);
va_start(ap, format);
- r = status_vprintf(status, ellipse, ephemeral, format, ap);
+ r = status_vprintf(status, flags, format, ap);
va_end(ap);
return r;
diff --git a/src/core/show-status.h b/src/core/show-status.h
index 1a80de33d9..f574d92d84 100644
--- a/src/core/show-status.h
+++ b/src/core/show-status.h
@@ -8,14 +8,22 @@
/* Manager status */
typedef enum ShowStatus {
- _SHOW_STATUS_UNSET = -2,
- SHOW_STATUS_AUTO = -1,
- SHOW_STATUS_NO = 0,
- SHOW_STATUS_YES = 1,
- SHOW_STATUS_TEMPORARY = 2,
+ SHOW_STATUS_NO,
+ SHOW_STATUS_AUTO,
+ SHOW_STATUS_TEMPORARY,
+ SHOW_STATUS_YES,
+ _SHOW_STATUS_MAX,
+ _SHOW_STATUS_INVALID = -1,
} ShowStatus;
+typedef enum ShowStatusFlags {
+ SHOW_STATUS_ELLIPSIZE = 1 << 0,
+ SHOW_STATUS_EPHEMERAL = 1 << 1,
+} ShowStatusFlags;
+
+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);
-int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
-int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
+int status_vprintf(const char *status, ShowStatusFlags flags, const char *format, va_list ap) _printf_(3,0);
+int status_printf(const char *status, ShowStatusFlags flags, const char *format, ...) _printf_(3,4);
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 038345b752..cb47ee8984 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -28,6 +28,7 @@
#include "parse-util.h"
#include "process-util.h"
#include "reboot-util.h"
+#include "rlimit-util.h"
#include "signal-util.h"
#include "string-util.h"
#include "switch-root.h"
@@ -77,14 +78,14 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_LOG_LEVEL:
r = log_set_max_level_from_string(optarg);
if (r < 0)
- log_error_errno(r, "Failed to parse log level %s, ignoring.", optarg);
+ log_error_errno(r, "Failed to parse log level %s, ignoring: %m", optarg);
break;
case ARG_LOG_TARGET:
r = log_set_target_from_string(optarg);
if (r < 0)
- log_error_errno(r, "Failed to parse log target %s, ignoring", optarg);
+ log_error_errno(r, "Failed to parse log target %s, ignoring: %m", optarg);
break;
@@ -93,7 +94,7 @@ static int parse_argv(int argc, char *argv[]) {
if (optarg) {
r = log_show_color_from_string(optarg);
if (r < 0)
- log_error_errno(r, "Failed to parse log color setting %s, ignoring", optarg);
+ log_error_errno(r, "Failed to parse log color setting %s, ignoring: %m", optarg);
} else
log_show_color(true);
@@ -103,7 +104,7 @@ static int parse_argv(int argc, char *argv[]) {
if (optarg) {
r = log_show_location_from_string(optarg);
if (r < 0)
- log_error_errno(r, "Failed to parse log location setting %s, ignoring", optarg);
+ log_error_errno(r, "Failed to parse log location setting %s, ignoring: %m", optarg);
} else
log_show_location(true);
@@ -112,14 +113,14 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_EXIT_CODE:
r = safe_atou8(optarg, &arg_exit_code);
if (r < 0)
- log_error_errno(r, "Failed to parse exit code %s, ignoring", optarg);
+ log_error_errno(r, "Failed to parse exit code %s, ignoring: %m", optarg);
break;
case ARG_TIMEOUT:
r = parse_sec(optarg, &arg_timeout);
if (r < 0)
- log_error_errno(r, "Failed to parse shutdown timeout %s, ignoring", optarg);
+ log_error_errno(r, "Failed to parse shutdown timeout %s, ignoring: %m", optarg);
break;
@@ -137,10 +138,9 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option code.");
}
- if (!arg_verb) {
- log_error("Verb argument missing.");
- return -EINVAL;
- }
+ if (!arg_verb)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Verb argument missing.");
return 0;
}
@@ -171,16 +171,23 @@ static int switch_root_initramfs(void) {
*/
static bool sync_making_progress(unsigned long long *prev_dirty) {
_cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
- bool r = false;
unsigned long long val = 0;
+ bool r = false;
f = fopen("/proc/meminfo", "re");
if (!f)
return log_warning_errno(errno, "Failed to open /proc/meminfo: %m");
- FOREACH_LINE(line, f, log_warning_errno(errno, "Failed to parse /proc/meminfo: %m")) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
unsigned long long ull = 0;
+ int q;
+
+ q = read_line(f, LONG_LINE_MAX, &line);
+ if (q < 0)
+ return log_warning_errno(q, "Failed to parse /proc/meminfo: %m");
+ if (q == 0)
+ break;
if (!first_word(line, "NFS_Unstable:") && !first_word(line, "Writeback:") && !first_word(line, "Dirty:"))
continue;
@@ -435,15 +442,17 @@ int main(int argc, char *argv[]) {
arguments[0] = NULL;
arguments[1] = arg_verb;
arguments[2] = NULL;
- execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL);
+
+ (void) rlimit_nofile_safe();
if (can_initrd) {
r = switch_root_initramfs();
if (r >= 0) {
argv[0] = (char*) "/shutdown";
- setsid();
- make_console_stdio();
+ (void) setsid();
+ (void) make_console_stdio();
log_info("Successfully changed into root pivot.\n"
"Returning to initrd...");
diff --git a/src/core/slice.c b/src/core/slice.c
index 58f18a4dad..15b18bcad3 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -4,7 +4,9 @@
#include "alloc-util.h"
#include "dbus-slice.h"
+#include "dbus-unit.h"
#include "log.h"
+#include "serialize.h"
#include "slice.h"
#include "special.h"
#include "string-util.h"
@@ -28,6 +30,9 @@ static void slice_set_state(Slice *t, SliceState state) {
SliceState old_state;
assert(t);
+ if (t->state != state)
+ bus_unit_send_pending_change_signal(UNIT(t), false);
+
old_state = t->state;
t->state = state;
@@ -74,7 +79,7 @@ static int slice_add_default_dependencies(Slice *s) {
r = unit_add_two_dependencies_by_name(
UNIT(s),
UNIT_BEFORE, UNIT_CONFLICTS,
- SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
@@ -123,7 +128,7 @@ static int slice_load_root_slice(Unit *u) {
if (!u->description)
u->description = strdup("Root Slice");
if (!u->documentation)
- u->documentation = strv_new("man:systemd.special(7)", NULL);
+ u->documentation = strv_new("man:systemd.special(7)");
return 1;
}
@@ -146,7 +151,7 @@ static int slice_load_system_slice(Unit *u) {
if (!u->description)
u->description = strdup("System Slice");
if (!u->documentation)
- u->documentation = strv_new("man:systemd.special(7)", NULL);
+ u->documentation = strv_new("man:systemd.special(7)");
return 1;
}
@@ -256,7 +261,8 @@ static int slice_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", slice_state_to_string(s->state));
+ (void) serialize_item(f, "state", slice_state_to_string(s->state));
+
return 0;
}
@@ -328,7 +334,7 @@ static void slice_enumerate_perpetual(Manager *m) {
assert(m);
r = slice_make_perpetual(m, SPECIAL_ROOT_SLICE, &u);
- if (r >= 0 && manager_owns_root_cgroup(m)) {
+ if (r >= 0 && manager_owns_host_root_cgroup(m)) {
Slice *s = SLICE(u);
/* If we are managing the root cgroup then this means our root slice covers the whole system, which
diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c
index 50115c0454..49b37aefc7 100644
--- a/src/core/smack-setup.c
+++ b/src/core/smack-setup.c
@@ -29,7 +29,6 @@ static int write_access2_rules(const char* srcdir) {
_cleanup_close_ int load2_fd = -1, change_fd = -1;
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *entry;
- char buf[NAME_MAX];
int dfd = -1;
int r = 0;
@@ -73,7 +72,7 @@ static int write_access2_rules(const char* srcdir) {
continue;
}
- policy = fdopen(fd, "re");
+ policy = fdopen(fd, "r");
if (!policy) {
if (r == 0)
r = -errno;
@@ -83,13 +82,17 @@ static int write_access2_rules(const char* srcdir) {
}
/* load2 write rules in the kernel require a line buffered stream */
- FOREACH_LINE(buf, policy,
- log_error_errno(errno, "Failed to read line from '%s': %m",
- entry->d_name)) {
+ for (;;) {
+ _cleanup_free_ char *buf = NULL, *sbj = NULL, *obj = NULL, *acc1 = NULL, *acc2 = NULL;
+ int q;
- _cleanup_free_ char *sbj = NULL, *obj = NULL, *acc1 = NULL, *acc2 = NULL;
+ q = read_line(policy, NAME_MAX, &buf);
+ if (q < 0)
+ return log_error_errno(q, "Failed to read line from '%s': %m", entry->d_name);
+ if (q == 0)
+ break;
- if (isempty(truncate_nl(buf)) || strchr(COMMENTS, *buf))
+ if (isempty(buf) || strchr(COMMENTS, buf[0]))
continue;
/* if 3 args -> load rule : subject object access1 */
@@ -102,7 +105,7 @@ static int write_access2_rules(const char* srcdir) {
if (write(isempty(acc2) ? load2_fd : change_fd, buf, strlen(buf)) < 0) {
if (r == 0)
r = -errno;
- log_error_errno(errno, "Failed to write '%s' to '%s' in '%s'",
+ log_error_errno(errno, "Failed to write '%s' to '%s' in '%s': %m",
buf, isempty(acc2) ? "/sys/fs/smackfs/load2" : "/sys/fs/smackfs/change-rule", entry->d_name);
}
}
@@ -115,7 +118,6 @@ static int write_cipso2_rules(const char* srcdir) {
_cleanup_close_ int cipso2_fd = -1;
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *entry;
- char buf[NAME_MAX];
int dfd = -1;
int r = 0;
@@ -152,7 +154,7 @@ static int write_cipso2_rules(const char* srcdir) {
continue;
}
- policy = fdopen(fd, "re");
+ policy = fdopen(fd, "r");
if (!policy) {
if (r == 0)
r = -errno;
@@ -162,17 +164,23 @@ static int write_cipso2_rules(const char* srcdir) {
}
/* cipso2 write rules in the kernel require a line buffered stream */
- FOREACH_LINE(buf, policy,
- log_error_errno(errno, "Failed to read line from '%s': %m",
- entry->d_name)) {
+ for (;;) {
+ _cleanup_free_ char *buf = NULL;
+ int q;
+
+ q = read_line(policy, NAME_MAX, &buf);
+ if (q < 0)
+ return log_error_errno(q, "Failed to read line from '%s': %m", entry->d_name);
+ if (q == 0)
+ break;
- if (isempty(truncate_nl(buf)) || strchr(COMMENTS, *buf))
+ if (isempty(buf) || strchr(COMMENTS, buf[0]))
continue;
if (write(cipso2_fd, buf, strlen(buf)) < 0) {
if (r == 0)
r = -errno;
- log_error_errno(errno, "Failed to write '%s' to '/sys/fs/smackfs/cipso2' in '%s'",
+ log_error_errno(errno, "Failed to write '%s' to '/sys/fs/smackfs/cipso2' in '%s': %m",
buf, entry->d_name);
break;
}
@@ -186,7 +194,6 @@ static int write_netlabel_rules(const char* srcdir) {
_cleanup_fclose_ FILE *dst = NULL;
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *entry;
- char buf[NAME_MAX];
int dfd = -1;
int r = 0;
@@ -220,7 +227,7 @@ static int write_netlabel_rules(const char* srcdir) {
continue;
}
- policy = fdopen(fd, "re");
+ policy = fdopen(fd, "r");
if (!policy) {
if (r == 0)
r = -errno;
@@ -232,15 +239,20 @@ static int write_netlabel_rules(const char* srcdir) {
(void) __fsetlocking(policy, FSETLOCKING_BYCALLER);
/* load2 write rules in the kernel require a line buffered stream */
- FOREACH_LINE(buf, policy,
- log_error_errno(errno, "Failed to read line from %s: %m", entry->d_name)) {
-
+ for (;;) {
+ _cleanup_free_ char *buf = NULL;
int q;
+ q = read_line(policy, NAME_MAX, &buf);
+ if (q < 0)
+ return log_error_errno(q, "Failed to read line from %s: %m", entry->d_name);
+ if (q == 0)
+ break;
+
if (!fputs(buf, dst)) {
if (r == 0)
r = -EINVAL;
- log_error_errno(errno, "Failed to write line to /sys/fs/smackfs/netlabel");
+ log_error_errno(errno, "Failed to write line to /sys/fs/smackfs/netlabel: %m");
break;
}
q = fflush_and_check(dst);
@@ -261,20 +273,27 @@ static int write_onlycap_list(void) {
_cleanup_free_ char *list = NULL;
_cleanup_fclose_ FILE *f = NULL;
size_t len = 0, allocated = 0;
- char buf[LINE_MAX];
int r;
f = fopen("/etc/smack/onlycap", "re");
if (!f) {
if (errno != ENOENT)
- log_warning_errno(errno, "Failed to read '/etc/smack/onlycap'");
+ log_warning_errno(errno, "Failed to read '/etc/smack/onlycap': %m");
+
return errno == ENOENT ? ENOENT : -errno;
}
- FOREACH_LINE(buf, f, return -errno) {
+ for (;;) {
+ _cleanup_free_ char *buf = NULL;
size_t l;
- if (isempty(truncate_nl(buf)) || strchr(COMMENTS, *buf))
+ r = read_line(f, LONG_LINE_MAX, &buf);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read line from /etc/smack/onlycap: %m");
+ if (r == 0)
+ break;
+
+ if (isempty(buf) || strchr(COMMENTS, *buf))
continue;
l = strlen(buf);
@@ -285,7 +304,7 @@ static int write_onlycap_list(void) {
len += l + 1;
}
- if (!len)
+ if (len == 0)
return 0;
list[len - 1] = 0;
@@ -293,13 +312,13 @@ static int write_onlycap_list(void) {
onlycap_fd = open("/sys/fs/smackfs/onlycap", O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (onlycap_fd < 0) {
if (errno != ENOENT)
- log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/onlycap'");
+ log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/onlycap': %m");
return -errno; /* negative error */
}
r = write(onlycap_fd, list, len);
if (r < 0)
- return log_error_errno(errno, "Failed to write onlycap list(%s) to '/sys/fs/smackfs/onlycap'", list);
+ return log_error_errno(errno, "Failed to write onlycap list(%s) to '/sys/fs/smackfs/onlycap': %m", list);
return 0;
}
@@ -331,17 +350,17 @@ int mac_smack_setup(bool *loaded_policy) {
}
#ifdef SMACK_RUN_LABEL
- r = write_string_file("/proc/self/attr/current", SMACK_RUN_LABEL, 0);
+ r = write_string_file("/proc/self/attr/current", SMACK_RUN_LABEL, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_warning_errno(r, "Failed to set SMACK label \"" SMACK_RUN_LABEL "\" on self: %m");
- r = write_string_file("/sys/fs/smackfs/ambient", SMACK_RUN_LABEL, 0);
+ r = write_string_file("/sys/fs/smackfs/ambient", SMACK_RUN_LABEL, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_warning_errno(r, "Failed to set SMACK ambient label \"" SMACK_RUN_LABEL "\": %m");
r = write_string_file("/sys/fs/smackfs/netlabel",
- "0.0.0.0/0 " SMACK_RUN_LABEL, 0);
+ "0.0.0.0/0 " SMACK_RUN_LABEL, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_warning_errno(r, "Failed to set SMACK netlabel rule \"0.0.0.0/0 " SMACK_RUN_LABEL "\": %m");
- r = write_string_file("/sys/fs/smackfs/netlabel", "127.0.0.1 -CIPSO", 0);
+ r = write_string_file("/sys/fs/smackfs/netlabel", "127.0.0.1 -CIPSO", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_warning_errno(r, "Failed to set SMACK netlabel rule \"127.0.0.1 -CIPSO\": %m");
#endif
@@ -390,7 +409,7 @@ int mac_smack_setup(bool *loaded_policy) {
log_info("Successfully wrote Smack onlycap list.");
break;
default:
- log_emergency_errno(r, "Failed to write Smack onlycap list.");
+ log_emergency_errno(r, "Failed to write Smack onlycap list: %m");
return r;
}
diff --git a/src/core/socket.c b/src/core/socket.c
index 56d32225c4..dd126a7f21 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -17,6 +17,7 @@
#include "bus-util.h"
#include "copy.h"
#include "dbus-socket.h"
+#include "dbus-unit.h"
#include "def.h"
#include "exit-status.h"
#include "fd-util.h"
@@ -24,6 +25,7 @@
#include "fs-util.h"
#include "in-addr-util.h"
#include "io-util.h"
+#include "ip-protocol-list.h"
#include "label.h"
#include "log.h"
#include "missing.h"
@@ -32,10 +34,10 @@
#include "path-util.h"
#include "process-util.h"
#include "selinux-util.h"
+#include "serialize.h"
#include "signal-util.h"
#include "smack-util.h"
#include "socket.h"
-#include "socket-protocol-list.h"
#include "special.h"
#include "string-table.h"
#include "string-util.h"
@@ -301,17 +303,17 @@ static int socket_add_default_dependencies(Socket *s) {
if (!UNIT(s)->default_dependencies)
return 0;
- r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) {
- r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
}
- return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
}
_pure_ static bool socket_has_exec(Socket *s) {
@@ -467,9 +469,7 @@ static int socket_verify(Socket *s) {
return 0;
}
-static void peer_address_hash_func(const void *p, struct siphash *state) {
- const SocketPeer *s = p;
-
+static void peer_address_hash_func(const SocketPeer *s, struct siphash *state) {
assert(s);
if (s->peer.sa.sa_family == AF_INET)
@@ -482,13 +482,12 @@ static void peer_address_hash_func(const void *p, struct siphash *state) {
assert_not_reached("Unknown address family.");
}
-static int peer_address_compare_func(const void *a, const void *b) {
- const SocketPeer *x = a, *y = b;
+static int peer_address_compare_func(const SocketPeer *x, const SocketPeer *y) {
+ int r;
- if (x->peer.sa.sa_family < y->peer.sa.sa_family)
- return -1;
- if (x->peer.sa.sa_family > y->peer.sa.sa_family)
- return 1;
+ r = CMP(x->peer.sa.sa_family, y->peer.sa.sa_family);
+ if (r != 0)
+ return r;
switch(x->peer.sa.sa_family) {
case AF_INET:
@@ -496,19 +495,12 @@ static int peer_address_compare_func(const void *a, const void *b) {
case AF_INET6:
return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr));
case AF_VSOCK:
- if (x->peer.vm.svm_cid < y->peer.vm.svm_cid)
- return -1;
- if (x->peer.vm.svm_cid > y->peer.vm.svm_cid)
- return 1;
- return 0;
+ return CMP(x->peer.vm.svm_cid, y->peer.vm.svm_cid);
}
assert_not_reached("Black sheep in the family!");
}
-const struct hash_ops peer_address_hash_ops = {
- .hash = peer_address_hash_func,
- .compare = peer_address_compare_func
-};
+DEFINE_PRIVATE_HASH_OPS(peer_address_hash_ops, SocketPeer, peer_address_hash_func, peer_address_compare_func);
static int socket_load(Unit *u) {
Socket *s = SOCKET(u);
@@ -547,26 +539,8 @@ static SocketPeer *socket_peer_new(void) {
return p;
}
-SocketPeer *socket_peer_ref(SocketPeer *p) {
- if (!p)
- return NULL;
-
- assert(p->n_ref > 0);
- p->n_ref++;
-
- return p;
-}
-
-SocketPeer *socket_peer_unref(SocketPeer *p) {
- if (!p)
- return NULL;
-
- assert(p->n_ref > 0);
-
- p->n_ref--;
-
- if (p->n_ref > 0)
- return NULL;
+static SocketPeer *socket_peer_free(SocketPeer *p) {
+ assert(p);
if (p->socket)
set_remove(p->socket->peers_by_address, p);
@@ -574,6 +548,8 @@ SocketPeer *socket_peer_unref(SocketPeer *p) {
return mfree(p);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(SocketPeer, socket_peer, socket_peer_free);
+
int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
_cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL;
SocketPeer sa = {}, *i;
@@ -833,7 +809,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC),
prefix, s->trigger_limit.burst);
- str = socket_protocol_to_name(s->socket_protocol);
+ str = ip_protocol_to_name(s->socket_protocol);
if (str)
fprintf(f, "%sSocketProtocol: %s\n", prefix, str);
@@ -1034,107 +1010,112 @@ static void socket_apply_socket_options(Socket *s, int fd) {
assert(fd >= 0);
if (s->keep_alive) {
- int one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "SO_KEEPALIVE failed: %m");
+ r = setsockopt_int(fd, SOL_SOCKET, SO_KEEPALIVE, true);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "SO_KEEPALIVE failed: %m");
}
if (s->keep_alive_time > 0) {
- int value = s->keep_alive_time / USEC_PER_SEC;
- if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &value, sizeof(value)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPIDLE failed: %m");
+ r = setsockopt_int(fd, SOL_TCP, TCP_KEEPIDLE, s->keep_alive_time / USEC_PER_SEC);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "TCP_KEEPIDLE failed: %m");
}
if (s->keep_alive_interval > 0) {
- int value = s->keep_alive_interval / USEC_PER_SEC;
- if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &value, sizeof(value)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPINTVL failed: %m");
+ r = setsockopt_int(fd, SOL_TCP, TCP_KEEPINTVL, s->keep_alive_interval / USEC_PER_SEC);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "TCP_KEEPINTVL failed: %m");
}
if (s->keep_alive_cnt > 0) {
- int value = s->keep_alive_cnt;
- if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &value, sizeof(value)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m");
+ r = setsockopt_int(fd, SOL_TCP, TCP_KEEPCNT, s->keep_alive_cnt);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "TCP_KEEPCNT failed: %m");
}
if (s->defer_accept > 0) {
- int value = s->defer_accept / USEC_PER_SEC;
- if (setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &value, sizeof(value)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "TCP_DEFER_ACCEPT failed: %m");
+ r = setsockopt_int(fd, SOL_TCP, TCP_DEFER_ACCEPT, s->defer_accept / USEC_PER_SEC);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "TCP_DEFER_ACCEPT failed: %m");
}
if (s->no_delay) {
- int one = 1;
-
if (s->socket_protocol == IPPROTO_SCTP) {
- if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &one, sizeof(one)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "SCTP_NODELAY failed: %m");
+ r = setsockopt_int(fd, SOL_SCTP, SCTP_NODELAY, true);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "SCTP_NODELAY failed: %m");
} else {
- if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
+ r = setsockopt_int(fd, SOL_TCP, TCP_NODELAY, true);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "TCP_NODELAY failed: %m");
}
}
if (s->broadcast) {
- int one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "SO_BROADCAST failed: %m");
+ r = setsockopt_int(fd, SOL_SOCKET, SO_BROADCAST, true);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "SO_BROADCAST failed: %m");
}
if (s->pass_cred) {
- int one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "SO_PASSCRED failed: %m");
+ r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "SO_PASSCRED failed: %m");
}
if (s->pass_sec) {
- int one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "SO_PASSSEC failed: %m");
+ r = setsockopt_int(fd, SOL_SOCKET, SO_PASSSEC, true);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "SO_PASSSEC failed: %m");
}
- if (s->priority >= 0)
- if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "SO_PRIORITY failed: %m");
+ if (s->priority >= 0) {
+ r = setsockopt_int(fd, SOL_SOCKET, SO_PRIORITY, s->priority);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "SO_PRIORITY failed: %m");
+ }
if (s->receive_buffer > 0) {
- int value = (int) s->receive_buffer;
-
/* We first try with SO_RCVBUFFORCE, in case we have the perms for that */
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "SO_RCVBUF failed: %m");
+ if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, s->receive_buffer) < 0) {
+ r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, s->receive_buffer);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "SO_RCVBUF failed: %m");
+ }
}
if (s->send_buffer > 0) {
- int value = (int) s->send_buffer;
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "SO_SNDBUF failed: %m");
+ if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, s->send_buffer) < 0) {
+ r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, s->send_buffer);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "SO_SNDBUF failed: %m");
+ }
}
- if (s->mark >= 0)
- if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "SO_MARK failed: %m");
+ if (s->mark >= 0) {
+ r = setsockopt_int(fd, SOL_SOCKET, SO_MARK, s->mark);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "SO_MARK failed: %m");
+ }
- if (s->ip_tos >= 0)
- if (setsockopt(fd, IPPROTO_IP, IP_TOS, &s->ip_tos, sizeof(s->ip_tos)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "IP_TOS failed: %m");
+ if (s->ip_tos >= 0) {
+ r = setsockopt_int(fd, IPPROTO_IP, IP_TOS, s->ip_tos);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "IP_TOS failed: %m");
+ }
if (s->ip_ttl >= 0) {
int x;
- r = setsockopt(fd, IPPROTO_IP, IP_TTL, &s->ip_ttl, sizeof(s->ip_ttl));
+ r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, s->ip_ttl);
if (socket_ipv6_is_supported())
- x = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &s->ip_ttl, sizeof(s->ip_ttl));
- else {
- x = -1;
- errno = EAFNOSUPPORT;
- }
+ x = setsockopt_int(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, s->ip_ttl);
+ else
+ x = -EAFNOSUPPORT;
if (r < 0 && x < 0)
- log_unit_warning_errno(UNIT(s), errno, "IP_TTL/IPV6_UNICAST_HOPS failed: %m");
+ log_unit_warning_errno(UNIT(s), r, "IP_TTL/IPV6_UNICAST_HOPS failed: %m");
}
if (s->tcp_congestion)
@@ -1329,7 +1310,7 @@ static int socket_symlink(Socket *s) {
STRV_FOREACH(i, s->symlinks) {
(void) mkdir_parents_label(*i, s->directory_mode);
- r = symlink_idempotent(p, *i);
+ r = symlink_idempotent(p, *i, false);
if (r == -EEXIST && s->remove_on_stop) {
/* If there's already something where we want to create the symlink, and the destructive
@@ -1337,7 +1318,7 @@ static int socket_symlink(Socket *s) {
* again. */
if (unlink(*i) >= 0)
- r = symlink_idempotent(p, *i);
+ r = symlink_idempotent(p, *i, false);
}
if (r < 0)
@@ -1375,8 +1356,10 @@ static int usbffs_dispatch_eps(SocketPort *p) {
n = (size_t) r;
p->auxiliary_fds = new(int, n);
- if (!p->auxiliary_fds)
- return -ENOMEM;
+ if (!p->auxiliary_fds) {
+ r = -ENOMEM;
+ goto clear;
+ }
p->n_auxiliary_fds = n;
@@ -1385,8 +1368,10 @@ static int usbffs_dispatch_eps(SocketPort *p) {
_cleanup_free_ char *ep = NULL;
ep = path_make_absolute(ent[i]->d_name, p->path);
- if (!ep)
- return -ENOMEM;
+ if (!ep) {
+ r = -ENOMEM;
+ goto fail;
+ }
path_simplify(ep, false);
@@ -1395,16 +1380,20 @@ static int usbffs_dispatch_eps(SocketPort *p) {
goto fail;
p->auxiliary_fds[k++] = r;
- free(ent[i]);
}
- return r;
+ r = 0;
+ goto clear;
fail:
close_many(p->auxiliary_fds, k);
p->auxiliary_fds = mfree(p->auxiliary_fds);
p->n_auxiliary_fds = 0;
+clear:
+ for (i = 0; i < n; ++i)
+ free(ent[i]);
+
return r;
}
@@ -1754,6 +1743,9 @@ static void socket_set_state(Socket *s, SocketState state) {
SocketState old_state;
assert(s);
+ if (s->state != state)
+ bus_unit_send_pending_change_signal(UNIT(s), false);
+
old_state = s->state;
s->state = state;
@@ -1866,11 +1858,12 @@ static int socket_coldplug(Unit *u) {
static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
- ExecParameters exec_params = {
- .flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
- .stdin_fd = -1,
- .stdout_fd = -1,
- .stderr_fd = -1,
+ _cleanup_(exec_params_clear) ExecParameters exec_params = {
+ .flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
+ .exec_fd = -1,
};
pid_t pid;
int r;
@@ -1887,9 +1880,9 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
if (r < 0)
return r;
- unit_set_exec_params(UNIT(s), &exec_params);
-
- exec_params.argv = c->argv;
+ r = unit_set_exec_params(UNIT(s), &exec_params);
+ if (r < 0)
+ return r;
r = exec_spawn(UNIT(s),
c,
@@ -1935,7 +1928,7 @@ static int socket_chown(Socket *s, pid_t *_pid) {
if (!isempty(s->user)) {
const char *user = s->user;
- r = get_user_creds(&user, &uid, &gid, NULL, NULL);
+ r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
if (r < 0) {
log_unit_error_errno(UNIT(s), r, "Failed to resolve user %s: %m", user);
_exit(EXIT_USER);
@@ -1945,7 +1938,7 @@ static int socket_chown(Socket *s, pid_t *_pid) {
if (!isempty(s->group)) {
const char *group = s->group;
- r = get_group_creds(&group, &gid);
+ r = get_group_creds(&group, &gid, 0);
if (r < 0) {
log_unit_error_errno(UNIT(s), r, "Failed to resolve group %s: %m", group);
_exit(EXIT_GROUP);
@@ -1990,8 +1983,10 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
if (s->result == SOCKET_SUCCESS)
s->result = f;
- if (s->result != SOCKET_SUCCESS)
- log_unit_warning(UNIT(s), "Failed with result '%s'.", socket_result_to_string(s->result));
+ if (s->result == SOCKET_SUCCESS)
+ unit_log_success(UNIT(s));
+ else
+ unit_log_failure(UNIT(s), socket_result_to_string(s->result));
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
@@ -2459,6 +2454,7 @@ static int socket_start(Unit *u) {
return r;
s->result = SOCKET_SUCCESS;
+ exec_command_reset_status_list_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
u->reset_accounting = true;
@@ -2506,16 +2502,16 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", socket_state_to_string(s->state));
- unit_serialize_item(u, f, "result", socket_result_to_string(s->result));
- unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted);
- unit_serialize_item_format(u, f, "n-refused", "%u", s->n_refused);
+ (void) serialize_item(f, "state", socket_state_to_string(s->state));
+ (void) serialize_item(f, "result", socket_result_to_string(s->result));
+ (void) serialize_item_format(f, "n-accepted", "%u", s->n_accepted);
+ (void) serialize_item_format(f, "n-refused", "%u", s->n_refused);
if (s->control_pid > 0)
- unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid);
+ (void) serialize_item_format(f, "control-pid", PID_FMT, s->control_pid);
if (s->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id));
+ (void) serialize_item(f, "control-command", socket_exec_command_to_string(s->control_command_id));
LIST_FOREACH(port, p, s->ports) {
int copy;
@@ -2525,29 +2521,28 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
copy = fdset_put_dup(fds, p->fd);
if (copy < 0)
- return copy;
+ return log_warning_errno(copy, "Failed to serialize socket fd: %m");
if (p->type == SOCKET_SOCKET) {
_cleanup_free_ char *t = NULL;
r = socket_address_print(&p->address, &t);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to format socket address: %m");
if (socket_address_family(&p->address) == AF_NETLINK)
- unit_serialize_item_format(u, f, "netlink", "%i %s", copy, t);
+ (void) serialize_item_format(f, "netlink", "%i %s", copy, t);
else
- unit_serialize_item_format(u, f, "socket", "%i %i %s", copy, p->address.type, t);
-
+ (void) serialize_item_format(f, "socket", "%i %i %s", copy, p->address.type, t);
} else if (p->type == SOCKET_SPECIAL)
- unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path);
+ (void) serialize_item_format(f, "special", "%i %s", copy, p->path);
else if (p->type == SOCKET_MQUEUE)
- unit_serialize_item_format(u, f, "mqueue", "%i %s", copy, p->path);
+ (void) serialize_item_format(f, "mqueue", "%i %s", copy, p->path);
else if (p->type == SOCKET_USB_FUNCTION)
- unit_serialize_item_format(u, f, "ffs", "%i %s", copy, p->path);
+ (void) serialize_item_format(f, "ffs", "%i %s", copy, p->path);
else {
assert(p->type == SOCKET_FIFO);
- unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path);
+ (void) serialize_item_format(f, "fifo", "%i %s", copy, p->path);
}
}
@@ -2555,6 +2550,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
}
static void socket_port_take_fd(SocketPort *p, FDSet *fds, int fd) {
+ assert(p);
+
safe_close(p->fd);
p->fd = fdset_remove(fds, fd);
}
@@ -2971,9 +2968,11 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
f = SOCKET_SUCCESS;
}
- log_unit_full(u, f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
- "Control process exited, code=%s status=%i",
- sigchld_code_to_string(code), status);
+ unit_log_process_exit(
+ u, f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "Control process",
+ socket_exec_command_to_string(s->control_command_id),
+ code, status);
if (s->result == SOCKET_SUCCESS)
s->result = f;
diff --git a/src/core/swap.c b/src/core/swap.c
index b78b1aa266..2d8463b8b1 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -5,10 +5,13 @@
#include <sys/stat.h>
#include <unistd.h>
-#include "libudev.h"
+#include "sd-device.h"
#include "alloc-util.h"
#include "dbus-swap.h"
+#include "dbus-unit.h"
+#include "device-private.h"
+#include "device-util.h"
#include "device.h"
#include "escape.h"
#include "exit-status.h"
@@ -18,11 +21,11 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "serialize.h"
#include "special.h"
#include "string-table.h"
#include "string-util.h"
#include "swap.h"
-#include "udev-util.h"
#include "unit-name.h"
#include "unit.h"
#include "virt.h"
@@ -196,7 +199,7 @@ static int swap_add_device_dependencies(Swap *s) {
/* 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, NULL, true, UNIT_DEPENDENCY_FILE);
+ return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, true, UNIT_DEPENDENCY_FILE);
}
static int swap_add_default_dependencies(Swap *s) {
@@ -215,11 +218,11 @@ static int swap_add_default_dependencies(Swap *s) {
/* swap units generated for the swap dev links are missing the
* ordering dep against the swap target. */
- r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
- return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
}
static int swap_verify(Swap *s) {
@@ -247,7 +250,7 @@ static int swap_verify(Swap *s) {
}
static int swap_load_devnode(Swap *s) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
struct stat st;
const char *p;
int r;
@@ -257,91 +260,105 @@ static int swap_load_devnode(Swap *s) {
if (stat(s->what, &st) < 0 || !S_ISBLK(st.st_mode))
return 0;
- r = udev_device_new_from_stat_rdev(UNIT(s)->manager->udev, &st, &d);
+ r = device_new_from_stat_rdev(&d, &st);
if (r < 0) {
log_unit_full(UNIT(s), r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to allocate udev device for swap %s: %m", s->what);
+ "Failed to allocate device for swap %s: %m", s->what);
return 0;
}
- p = udev_device_get_devnode(d);
- if (!p)
+ if (sd_device_get_devname(d, &p) < 0)
return 0;
return swap_set_devnode(s, p);
}
-static int swap_load(Unit *u) {
+static int swap_add_extras(Swap *s) {
int r;
- Swap *s = SWAP(u);
assert(s);
- assert(u->load_state == UNIT_STUB);
- /* Load a .swap file */
- if (SWAP(u)->from_proc_swaps)
- r = unit_load_fragment_and_dropin_optional(u);
- else
- r = unit_load_fragment_and_dropin(u);
+ if (UNIT(s)->fragment_path)
+ s->from_fragment = true;
+
+ if (!s->what) {
+ if (s->parameters_fragment.what)
+ s->what = strdup(s->parameters_fragment.what);
+ else if (s->parameters_proc_swaps.what)
+ s->what = strdup(s->parameters_proc_swaps.what);
+ else {
+ r = unit_name_to_path(UNIT(s)->id, &s->what);
+ if (r < 0)
+ return r;
+ }
+
+ if (!s->what)
+ return -ENOMEM;
+ }
+
+ path_simplify(s->what, false);
+
+ if (!UNIT(s)->description) {
+ r = unit_set_description(UNIT(s), s->what);
+ if (r < 0)
+ return r;
+ }
+
+ r = unit_require_mounts_for(UNIT(s), s->what, UNIT_DEPENDENCY_IMPLICIT);
if (r < 0)
return r;
- if (u->load_state == UNIT_LOADED) {
-
- if (UNIT(s)->fragment_path)
- s->from_fragment = true;
+ r = swap_add_device_dependencies(s);
+ if (r < 0)
+ return r;
- if (!s->what) {
- if (s->parameters_fragment.what)
- s->what = strdup(s->parameters_fragment.what);
- else if (s->parameters_proc_swaps.what)
- s->what = strdup(s->parameters_proc_swaps.what);
- else {
- r = unit_name_to_path(u->id, &s->what);
- if (r < 0)
- return r;
- }
+ r = swap_load_devnode(s);
+ if (r < 0)
+ return r;
- if (!s->what)
- return -ENOMEM;
- }
+ r = unit_patch_contexts(UNIT(s));
+ if (r < 0)
+ return r;
- path_simplify(s->what, false);
+ r = unit_add_exec_dependencies(UNIT(s), &s->exec_context);
+ if (r < 0)
+ return r;
- if (!UNIT(s)->description) {
- r = unit_set_description(u, s->what);
- if (r < 0)
- return r;
- }
+ r = unit_set_default_slice(UNIT(s));
+ if (r < 0)
+ return r;
- r = unit_require_mounts_for(UNIT(s), s->what, UNIT_DEPENDENCY_IMPLICIT);
- if (r < 0)
- return r;
+ r = swap_add_default_dependencies(s);
+ if (r < 0)
+ return r;
- r = swap_add_device_dependencies(s);
- if (r < 0)
- return r;
+ return 0;
+}
- r = swap_load_devnode(s);
- if (r < 0)
- return r;
+static int swap_load(Unit *u) {
+ Swap *s = SWAP(u);
+ int r, q;
- r = unit_patch_contexts(u);
- if (r < 0)
- return r;
+ assert(s);
+ assert(u->load_state == UNIT_STUB);
- r = unit_add_exec_dependencies(u, &s->exec_context);
- if (r < 0)
- return r;
+ /* Load a .swap file */
+ if (SWAP(u)->from_proc_swaps)
+ r = unit_load_fragment_and_dropin_optional(u);
+ else
+ r = unit_load_fragment_and_dropin(u);
- r = unit_set_default_slice(u);
- if (r < 0)
- return r;
+ /* Add in some extras, and do so either when we successfully loaded something or when /proc/swaps is already
+ * active. */
+ if (u->load_state == UNIT_LOADED || s->from_proc_swaps)
+ q = swap_add_extras(s);
+ else
+ q = 0;
- r = swap_add_default_dependencies(s);
- if (r < 0)
- return r;
- }
+ if (r < 0)
+ return r;
+ if (q < 0)
+ return q;
return swap_verify(s);
}
@@ -368,13 +385,12 @@ static int swap_setup_unit(
return log_unit_error_errno(u, r, "Failed to generate unit name from path: %m");
u = manager_get_unit(m, e);
-
if (u &&
SWAP(u)->from_proc_swaps &&
- !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps)) {
- log_error("Swap %s appeared twice with different device paths %s and %s", e, SWAP(u)->parameters_proc_swaps.what, what_proc_swaps);
- return -EEXIST;
- }
+ !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps))
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "Swap %s appeared twice with different device paths %s and %s",
+ e, SWAP(u)->parameters_proc_swaps.what, what_proc_swaps);
if (!u) {
delete = true;
@@ -403,6 +419,13 @@ static int swap_setup_unit(
}
}
+ /* The unit is definitely around now, mark it as loaded if it was previously referenced but could not be
+ * loaded. After all we can load it now, from the data in /proc/swaps. */
+ if (IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_BAD_SETTING, UNIT_ERROR)) {
+ u->load_state = UNIT_LOADED;
+ u->load_error = 0;
+ }
+
if (set_flags) {
SWAP(u)->is_active = true;
SWAP(u)->just_activated = !SWAP(u)->from_proc_swaps;
@@ -425,10 +448,9 @@ fail:
}
static int swap_process_new(Manager *m, const char *device, int prio, bool set_flags) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
- const char *dn;
- struct stat st;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+ const char *dn, *devlink;
+ struct stat st, st_link;
int r;
assert(m);
@@ -442,41 +464,36 @@ static int swap_process_new(Manager *m, const char *device, int prio, bool set_f
if (stat(device, &st) < 0 || !S_ISBLK(st.st_mode))
return 0;
- r = udev_device_new_from_stat_rdev(m->udev, &st, &d);
+ r = device_new_from_stat_rdev(&d, &st);
if (r < 0) {
log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to allocate udev device for swap %s: %m", device);
+ "Failed to allocate device for swap %s: %m", device);
return 0;
}
/* Add the main device node */
- dn = udev_device_get_devnode(d);
- if (dn && !streq(dn, device))
+ if (sd_device_get_devname(d, &dn) >= 0 && !streq(dn, device))
swap_setup_unit(m, dn, device, prio, set_flags);
/* Add additional units for all symlinks */
- first = udev_device_get_devlinks_list_entry(d);
- udev_list_entry_foreach(item, first) {
- const char *p;
+ FOREACH_DEVICE_DEVLINK(d, devlink) {
/* Don't bother with the /dev/block links */
- p = udev_list_entry_get_name(item);
-
- if (streq(p, device))
+ if (streq(devlink, device))
continue;
- if (path_startswith(p, "/dev/block/"))
+ if (path_startswith(devlink, "/dev/block/"))
continue;
- if (stat(p, &st) >= 0)
- if (!S_ISBLK(st.st_mode) ||
- st.st_rdev != udev_device_get_devnum(d))
- continue;
+ if (stat(devlink, &st_link) >= 0 &&
+ (!S_ISBLK(st_link.st_mode) ||
+ st_link.st_rdev != st.st_rdev))
+ continue;
- swap_setup_unit(m, p, device, prio, set_flags);
+ swap_setup_unit(m, devlink, device, prio, set_flags);
}
- return r;
+ return 0;
}
static void swap_set_state(Swap *s, SwapState state) {
@@ -485,6 +502,9 @@ static void swap_set_state(Swap *s, SwapState state) {
assert(s);
+ if (s->state != state)
+ bus_unit_send_pending_change_signal(UNIT(s), false);
+
old_state = s->state;
s->state = state;
@@ -601,11 +621,12 @@ static void swap_dump(Unit *u, FILE *f, const char *prefix) {
static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
- ExecParameters exec_params = {
+ _cleanup_(exec_params_clear) ExecParameters exec_params = {
.flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
.stdin_fd = -1,
.stdout_fd = -1,
.stderr_fd = -1,
+ .exec_fd = -1,
};
pid_t pid;
int r;
@@ -622,7 +643,9 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
if (r < 0)
goto fail;
- unit_set_exec_params(UNIT(s), &exec_params);
+ r = unit_set_exec_params(UNIT(s), &exec_params);
+ if (r < 0)
+ goto fail;
r = exec_spawn(UNIT(s),
c,
@@ -655,9 +678,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
if (s->result == SWAP_SUCCESS)
s->result = f;
- if (s->result != SWAP_SUCCESS)
- log_unit_warning(UNIT(s), "Failed with result '%s'.", swap_result_to_string(s->result));
-
+ unit_log_result(UNIT(s), s->result == SWAP_SUCCESS, swap_result_to_string(s->result));
swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
@@ -813,6 +834,14 @@ fail:
swap_enter_dead_or_active(s, SWAP_FAILURE_RESOURCES);
}
+static void swap_cycle_clear(Swap *s) {
+ assert(s);
+
+ s->result = SWAP_SUCCESS;
+ exec_command_reset_status_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
+ UNIT(s)->reset_accounting = true;
+}
+
static int swap_start(Unit *u) {
Swap *s = SWAP(u), *other;
int r;
@@ -852,10 +881,7 @@ static int swap_start(Unit *u) {
if (r < 0)
return r;
- s->result = SWAP_SUCCESS;
-
- u->reset_accounting = true;
-
+ swap_cycle_clear(s);
swap_enter_activating(s);
return 1;
}
@@ -898,14 +924,14 @@ static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", swap_state_to_string(s->state));
- unit_serialize_item(u, f, "result", swap_result_to_string(s->result));
+ (void) serialize_item(f, "state", swap_state_to_string(s->state));
+ (void) serialize_item(f, "result", swap_result_to_string(s->result));
if (s->control_pid > 0)
- unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid);
+ (void) serialize_item_format(f, "control-pid", PID_FMT, s->control_pid);
if (s->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id));
+ (void) serialize_item(f, "control-command", swap_exec_command_to_string(s->control_command_id));
return 0;
}
@@ -1012,8 +1038,11 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
}
- log_unit_full(u, f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
- "Swap process exited, code=%s status=%i", sigchld_code_to_string(code), status);
+ unit_log_process_exit(
+ u, f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "Swap process",
+ swap_exec_command_to_string(s->control_command_id),
+ code, status);
switch (s->state) {
@@ -1084,7 +1113,6 @@ static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userd
static int swap_load_proc_swaps(Manager *m, bool set_flags) {
unsigned i;
- int r = 0;
assert(m);
@@ -1116,12 +1144,10 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) {
device_found_node(m, d, DEVICE_FOUND_SWAP, DEVICE_FOUND_SWAP);
- k = swap_process_new(m, d, prio, set_flags);
- if (k < 0)
- r = k;
+ (void) swap_process_new(m, d, prio, set_flags);
}
- return r;
+ return 0;
}
static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
@@ -1152,13 +1178,13 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
Swap *swap = SWAP(u);
if (!swap->is_active) {
- /* This has just been deactivated */
swap_unset_proc_swaps(swap);
switch (swap->state) {
case SWAP_ACTIVE:
+ /* This has just been deactivated */
swap_enter_dead(swap, SWAP_SUCCESS);
break;
@@ -1179,7 +1205,8 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
case SWAP_DEAD:
case SWAP_FAILED:
- (void) unit_acquire_invocation_id(UNIT(swap));
+ (void) unit_acquire_invocation_id(u);
+ swap_cycle_clear(swap);
swap_enter_active(swap, SWAP_SUCCESS);
break;
@@ -1320,18 +1347,17 @@ fail:
swap_shutdown(m);
}
-int swap_process_device_new(Manager *m, struct udev_device *dev) {
- struct udev_list_entry *item = NULL, *first = NULL;
+int swap_process_device_new(Manager *m, sd_device *dev) {
_cleanup_free_ char *e = NULL;
- const char *dn;
+ const char *dn, *devlink;
Unit *u;
int r = 0;
assert(m);
assert(dev);
- dn = udev_device_get_devnode(dev);
- if (!dn)
+ r = sd_device_get_devname(dev, &dn);
+ if (r < 0)
return 0;
r = unit_name_from_path(dn, ".swap", &e);
@@ -1342,12 +1368,11 @@ int swap_process_device_new(Manager *m, struct udev_device *dev) {
if (u)
r = swap_set_devnode(SWAP(u), dn);
- first = udev_device_get_devlinks_list_entry(dev);
- udev_list_entry_foreach(item, first) {
+ FOREACH_DEVICE_DEVLINK(dev, devlink) {
_cleanup_free_ char *n = NULL;
int q;
- q = unit_name_from_path(udev_list_entry_get_name(item), ".swap", &n);
+ q = unit_name_from_path(devlink, ".swap", &n);
if (q < 0)
return q;
@@ -1362,13 +1387,13 @@ int swap_process_device_new(Manager *m, struct udev_device *dev) {
return r;
}
-int swap_process_device_remove(Manager *m, struct udev_device *dev) {
+int swap_process_device_remove(Manager *m, sd_device *dev) {
const char *dn;
int r = 0;
Swap *s;
- dn = udev_device_get_devnode(dev);
- if (!dn)
+ r = sd_device_get_devname(dev, &dn);
+ if (r < 0)
return 0;
while ((s = hashmap_get(m->swaps_by_devnode, dn))) {
diff --git a/src/core/swap.h b/src/core/swap.h
index 1c0c7fcadc..1a4b60b957 100644
--- a/src/core/swap.h
+++ b/src/core/swap.h
@@ -5,7 +5,7 @@
Copyright © 2010 Maarten Lankhorst
***/
-#include "libudev.h"
+#include "sd-device.h"
#include "unit.h"
typedef struct Swap Swap;
@@ -85,8 +85,8 @@ struct Swap {
extern const UnitVTable swap_vtable;
-int swap_process_device_new(Manager *m, struct udev_device *dev);
-int swap_process_device_remove(Manager *m, struct udev_device *dev);
+int swap_process_device_new(Manager *m, sd_device *dev);
+int swap_process_device_remove(Manager *m, sd_device *dev);
const char* swap_exec_command_to_string(SwapExecCommand i) _const_;
SwapExecCommand swap_exec_command_from_string(const char *s) _pure_;
diff --git a/src/core/system.conf.in b/src/core/system.conf.in
index f0a59a79a5..0a58737b82 100644
--- a/src/core/system.conf.in
+++ b/src/core/system.conf.in
@@ -23,9 +23,9 @@
#CrashReboot=no
#CtrlAltDelBurstAction=reboot-force
#CPUAffinity=1 2
-#JoinControllers=cpu,cpuacct net_cls,net_prio
#RuntimeWatchdogSec=0
#ShutdownWatchdogSec=10min
+#WatchdogDevice=
#CapabilityBoundingSet=
#NoNewPrivileges=no
#SystemCallArchitectures=
@@ -52,7 +52,7 @@
#DefaultLimitSTACK=
#DefaultLimitCORE=
#DefaultLimitRSS=
-#DefaultLimitNOFILE=
+#DefaultLimitNOFILE=1024:@HIGH_RLIMIT_NOFILE@
#DefaultLimitAS=
#DefaultLimitNPROC=
#DefaultLimitMEMLOCK=
@@ -62,5 +62,3 @@
#DefaultLimitNICE=
#DefaultLimitRTPRIO=
#DefaultLimitRTTIME=
-#IPAddressAllow=
-#IPAddressDeny=
diff --git a/src/core/target.c b/src/core/target.c
index 6446767504..421a304c73 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -1,12 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "dbus-target.h"
+#include "dbus-unit.h"
#include "log.h"
+#include "serialize.h"
#include "special.h"
#include "string-util.h"
+#include "target.h"
#include "unit-name.h"
#include "unit.h"
-#include "target.h"
static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
[TARGET_DEAD] = UNIT_INACTIVE,
@@ -17,6 +19,9 @@ static void target_set_state(Target *t, TargetState state) {
TargetState old_state;
assert(t);
+ if (t->state != state)
+ bus_unit_send_pending_change_signal(UNIT(t), false);
+
old_state = t->state;
t->state = state;
@@ -66,7 +71,7 @@ static int target_add_default_dependencies(Target *t) {
return 0;
/* Make sure targets are unloaded on shutdown */
- return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
}
static int target_load(Unit *u) {
@@ -144,7 +149,7 @@ static int target_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", target_state_to_string(s->state));
+ (void) serialize_item(f, "state", target_state_to_string(s->state));
return 0;
}
diff --git a/src/core/timer.c b/src/core/timer.c
index db202971d3..d9ba2f76b3 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -6,9 +6,11 @@
#include "bus-error.h"
#include "bus-util.h"
#include "dbus-timer.h"
+#include "dbus-unit.h"
#include "fs-util.h"
#include "parse-util.h"
#include "random-util.h"
+#include "serialize.h"
#include "special.h"
#include "string-table.h"
#include "string-util.h"
@@ -88,18 +90,18 @@ static int timer_add_default_dependencies(Timer *t) {
if (!UNIT(t)->default_dependencies)
return 0;
- r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
- r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
LIST_FOREACH(value, v, t->values) {
if (v->base == TIMER_CALENDAR) {
- r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
break;
@@ -107,7 +109,7 @@ static int timer_add_default_dependencies(Timer *t) {
}
}
- return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+ return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
}
static int timer_add_trigger_dependencies(Timer *t) {
@@ -246,6 +248,9 @@ static void timer_set_state(Timer *t, TimerState state) {
TimerState old_state;
assert(t);
+ if (t->state != state)
+ bus_unit_send_pending_change_signal(UNIT(t), false);
+
old_state = t->state;
t->state = state;
@@ -262,7 +267,7 @@ static void timer_set_state(Timer *t, TimerState state) {
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
}
-static void timer_enter_waiting(Timer *t, bool initial);
+static void timer_enter_waiting(Timer *t, bool time_change);
static int timer_coldplug(Unit *u) {
Timer *t = TIMER(u);
@@ -287,9 +292,7 @@ static void timer_enter_dead(Timer *t, TimerResult f) {
if (t->result == TIMER_SUCCESS)
t->result = f;
- if (t->result != TIMER_SUCCESS)
- log_unit_warning(UNIT(t), "Failed with result '%s'.", timer_result_to_string(t->result));
-
+ unit_log_result(UNIT(t), t->result == TIMER_SUCCESS, timer_result_to_string(t->result));
timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
}
@@ -332,7 +335,7 @@ static void add_random(Timer *t, usec_t *v) {
log_unit_debug(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
}
-static void timer_enter_waiting(Timer *t, bool initial) {
+static void timer_enter_waiting(Timer *t, bool time_change) {
bool found_monotonic = false, found_realtime = false;
bool leave_around = false;
triple_timestamp ts;
@@ -442,7 +445,8 @@ static void timer_enter_waiting(Timer *t, bool initial) {
v->next_elapse = usec_add(usec_shift_clock(base, CLOCK_MONOTONIC, TIMER_MONOTONIC_CLOCK(t)), v->value);
- if (!initial &&
+ if (dual_timestamp_is_set(&t->last_trigger) &&
+ !time_change &&
v->next_elapse < triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)) &&
IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
/* This is a one time trigger, disable it now */
@@ -640,7 +644,7 @@ static int timer_start(Unit *u) {
}
t->result = TIMER_SUCCESS;
- timer_enter_waiting(t, true);
+ timer_enter_waiting(t, false);
return 1;
}
@@ -661,21 +665,20 @@ static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(f);
assert(fds);
- unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
- unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
+ (void) serialize_item(f, "state", timer_state_to_string(t->state));
+ (void) serialize_item(f, "result", timer_result_to_string(t->result));
if (t->last_trigger.realtime > 0)
- unit_serialize_item_format(u, f, "last-trigger-realtime", "%" PRIu64, t->last_trigger.realtime);
+ (void) serialize_usec(f, "last-trigger-realtime", t->last_trigger.realtime);
if (t->last_trigger.monotonic > 0)
- unit_serialize_item_format(u, f, "last-trigger-monotonic", "%" PRIu64, t->last_trigger.monotonic);
+ (void) serialize_usec(f, "last-trigger-monotonic", t->last_trigger.monotonic);
return 0;
}
static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
Timer *t = TIMER(u);
- int r;
assert(u);
assert(key);
@@ -690,6 +693,7 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F
log_unit_debug(u, "Failed to parse state value: %s", value);
else
t->deserialized_state = state;
+
} else if (streq(key, "result")) {
TimerResult f;
@@ -698,19 +702,12 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F
log_unit_debug(u, "Failed to parse result value: %s", value);
else if (f != TIMER_SUCCESS)
t->result = f;
- } else if (streq(key, "last-trigger-realtime")) {
-
- r = safe_atou64(value, &t->last_trigger.realtime);
- if (r < 0)
- log_unit_debug(u, "Failed to parse last-trigger-realtime value: %s", value);
-
- } else if (streq(key, "last-trigger-monotonic")) {
-
- r = safe_atou64(value, &t->last_trigger.monotonic);
- if (r < 0)
- log_unit_debug(u, "Failed to parse last-trigger-monotonic value: %s", value);
- } else
+ } else if (streq(key, "last-trigger-realtime"))
+ (void) deserialize_usec(value, &t->last_trigger.realtime);
+ else if (streq(key, "last-trigger-monotonic"))
+ (void) deserialize_usec(value, &t->last_trigger.monotonic);
+ else
log_unit_debug(u, "Unknown serialization key: %s", key);
return 0;
@@ -811,7 +808,7 @@ static void timer_time_change(Unit *u) {
t->last_trigger.realtime = ts;
log_unit_debug(u, "Time change, recalculating next elapse.");
- timer_enter_waiting(t, false);
+ timer_enter_waiting(t, true);
}
static void timer_timezone_change(Unit *u) {
diff --git a/src/core/transaction.c b/src/core/transaction.c
index 1c7efb207a..486c6a4a05 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -526,7 +526,9 @@ static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_erro
if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
job_type_is_conflicting(j->unit->job->type, j->type))
return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE,
- "Transaction is destructive.");
+ "Transaction for %s/%s is destructive (%s has '%s' job queued, but '%s' is included in transaction).",
+ tr->anchor_job->unit->id, job_type_to_string(tr->anchor_job->type),
+ j->unit->id, job_type_to_string(j->unit->job->type), job_type_to_string(j->type));
}
return 0;
@@ -695,10 +697,8 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error
if (r >= 0)
break;
- if (r != -EAGAIN) {
- log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r));
- return r;
- }
+ if (r != -EAGAIN)
+ return log_warning_errno(r, "Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r));
/* Let's see if the resulting transaction ordering
* graph is still cyclic... */
@@ -712,10 +712,8 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error
if (r >= 0)
break;
- if (r != -EAGAIN) {
- log_warning("Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r));
- return r;
- }
+ if (r != -EAGAIN)
+ return log_warning_errno(r, "Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r));
/* Seventh step: an entry got dropped, let's garbage
* collect its dependencies. */
@@ -731,10 +729,8 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error
/* Ninth step: check whether we can actually apply this */
r = transaction_is_destructive(tr, mode, e);
- if (r < 0) {
- log_notice("Requested transaction contradicts existing jobs: %s", bus_error_message(e, r));
- return r;
- }
+ if (r < 0)
+ return log_notice_errno(r, "Requested transaction contradicts existing jobs: %s", bus_error_message(e, r));
/* Tenth step: apply changes */
r = transaction_apply(tr, m, mode);
diff --git a/src/core/umount.c b/src/core/umount.c
index 241fe6fc62..7af0195aab 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -13,22 +13,24 @@
/* This needs to be after sys/mount.h :( */
#include <libmount.h>
-#include "libudev.h"
+#include "sd-device.h"
#include "alloc-util.h"
#include "blockdev-util.h"
#include "def.h"
+#include "device-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fstab-util.h"
#include "linux-3.13/dm-ioctl.h"
#include "mount-setup.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "process-util.h"
#include "signal-util.h"
#include "string-util.h"
-#include "udev-util.h"
+#include "strv.h"
#include "umount.h"
#include "util.h"
#include "virt.h"
@@ -72,7 +74,8 @@ int mount_points_list_get(const char *mountinfo, MountPoint **head) {
for (;;) {
struct libmnt_fs *fs;
- const char *path, *options, *fstype;
+ const char *path, *fstype;
+ _cleanup_free_ char *options = NULL;
_cleanup_free_ char *p = NULL;
unsigned long remount_flags = 0u;
_cleanup_free_ char *remount_options = NULL;
@@ -92,9 +95,25 @@ int mount_points_list_get(const char *mountinfo, MountPoint **head) {
if (cunescape(path, UNESCAPE_RELAX, &p) < 0)
return log_oom();
- options = mnt_fs_get_options(fs);
fstype = mnt_fs_get_fstype(fs);
+ /* Combine the generic VFS options with the FS-specific
+ * options. Duplicates are not a problem here, because the only
+ * options that should come up twice are typically ro/rw, which
+ * are turned into MS_RDONLY or the invertion of it.
+ *
+ * Even if there are duplicates later in mount_option_mangle()
+ * it shouldn't hurt anyways as they override each other.
+ */
+ if (!strextend_with_separator(&options, ",",
+ mnt_fs_get_vfs_options(fs),
+ NULL))
+ return log_oom();
+ if (!strextend_with_separator(&options, ",",
+ mnt_fs_get_fs_options(fs),
+ NULL))
+ return log_oom();
+
/* Ignore mount points we can't unmount because they
* are API or because we are keeping them open (like
* /dev/console). Also, ignore all mounts below API
@@ -104,9 +123,7 @@ int mount_points_list_get(const char *mountinfo, MountPoint **head) {
* unmount these things, hence don't bother. */
if (mount_point_is_api(p) ||
mount_point_ignore(p) ||
- path_startswith(p, "/dev") ||
- path_startswith(p, "/sys") ||
- path_startswith(p, "/proc"))
+ PATH_STARTSWITH_SET(p, "/dev", "/sys", "/proc"))
continue;
/* If we are in a container, don't attempt to
@@ -212,121 +229,105 @@ int swap_list_get(const char *swaps, MountPoint **head) {
}
static int loopback_list_get(MountPoint **head) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d;
int r;
assert(head);
- udev = udev_new();
- if (!udev)
- return -ENOMEM;
-
- e = udev_enumerate_new(udev);
- if (!e)
- return -ENOMEM;
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
- r = udev_enumerate_add_match_subsystem(e, "block");
+ r = sd_device_enumerator_allow_uninitialized(e);
if (r < 0)
return r;
- r = udev_enumerate_add_match_sysname(e, "loop*");
+ r = sd_device_enumerator_add_match_subsystem(e, "block", true);
if (r < 0)
return r;
- r = udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL);
+ r = sd_device_enumerator_add_match_sysname(e, "loop*");
if (r < 0)
return r;
- r = udev_enumerate_scan_devices(e);
+ r = sd_device_enumerator_add_match_sysattr(e, "loop/backing_file", NULL, true);
if (r < 0)
return r;
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *d;
+ FOREACH_DEVICE(e, d) {
+ _cleanup_free_ char *p = NULL;
const char *dn;
- _cleanup_free_ MountPoint *lb = NULL;
-
- d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!d)
- return -ENOMEM;
+ MountPoint *lb;
- dn = udev_device_get_devnode(d);
- if (!dn)
+ if (sd_device_get_devname(d, &dn) < 0)
continue;
- lb = new0(MountPoint, 1);
+ p = strdup(dn);
+ if (!p)
+ return -ENOMEM;
+
+ lb = new(MountPoint, 1);
if (!lb)
return -ENOMEM;
- r = free_and_strdup(&lb->path, dn);
- if (r < 0)
- return r;
+ *lb = (MountPoint) {
+ .path = TAKE_PTR(p),
+ };
LIST_PREPEND(mount_point, *head, lb);
- lb = NULL;
}
return 0;
}
static int dm_list_get(MountPoint **head) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d;
int r;
assert(head);
- udev = udev_new();
- if (!udev)
- return -ENOMEM;
-
- e = udev_enumerate_new(udev);
- if (!e)
- return -ENOMEM;
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
- r = udev_enumerate_add_match_subsystem(e, "block");
+ r = sd_device_enumerator_allow_uninitialized(e);
if (r < 0)
return r;
- r = udev_enumerate_add_match_sysname(e, "dm-*");
+ r = sd_device_enumerator_add_match_subsystem(e, "block", true);
if (r < 0)
return r;
- r = udev_enumerate_scan_devices(e);
+ r = sd_device_enumerator_add_match_sysname(e, "dm-*");
if (r < 0)
return r;
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *d;
- dev_t devnum;
+ FOREACH_DEVICE(e, d) {
+ _cleanup_free_ char *p = NULL;
const char *dn;
- _cleanup_free_ MountPoint *m = NULL;
-
- d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!d)
- return -ENOMEM;
+ MountPoint *m;
+ dev_t devnum;
- devnum = udev_device_get_devnum(d);
- dn = udev_device_get_devnode(d);
- if (major(devnum) == 0 || !dn)
+ if (sd_device_get_devnum(d, &devnum) < 0 ||
+ sd_device_get_devname(d, &dn) < 0)
continue;
- m = new0(MountPoint, 1);
+ p = strdup(dn);
+ if (!p)
+ return -ENOMEM;
+
+ m = new(MountPoint, 1);
if (!m)
return -ENOMEM;
- m->devnum = devnum;
- r = free_and_strdup(&m->path, dn);
- if (r < 0)
- return r;
+ *m = (MountPoint) {
+ .path = TAKE_PTR(p),
+ .devnum = devnum,
+ };
LIST_PREPEND(mount_point, *head, m);
- m = NULL;
}
return 0;
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
index 046e937e92..a8e84ebe80 100644
--- a/src/core/unit-printf.c
+++ b/src/core/unit-printf.c
@@ -12,25 +12,25 @@
#include "unit.h"
#include "user-util.h"
-static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_prefix_and_instance(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
assert(u);
return unit_name_to_prefix_and_instance(u->id, ret);
}
-static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_prefix(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
assert(u);
return unit_name_to_prefix(u->id, ret);
}
-static int specifier_prefix_unescaped(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_prefix_unescaped(char specifier, const void *data, const void *userdata, char **ret) {
_cleanup_free_ char *p = NULL;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
assert(u);
@@ -42,16 +42,16 @@ static int specifier_prefix_unescaped(char specifier, void *data, void *userdata
return unit_name_unescape(p, ret);
}
-static int specifier_instance_unescaped(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_instance_unescaped(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
assert(u);
return unit_name_unescape(strempty(u->instance), ret);
}
-static int specifier_last_component(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_last_component(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
_cleanup_free_ char *prefix = NULL;
char *dash;
int r;
@@ -70,7 +70,7 @@ static int specifier_last_component(char specifier, void *data, void *userdata,
return 0;
}
-static int specifier_last_component_unescaped(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_last_component_unescaped(char specifier, const void *data, const void *userdata, char **ret) {
_cleanup_free_ char *p = NULL;
int r;
@@ -81,8 +81,8 @@ static int specifier_last_component_unescaped(char specifier, void *data, void *
return unit_name_unescape(p, ret);
}
-static int specifier_filename(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_filename(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
assert(u);
@@ -92,12 +92,12 @@ static int specifier_filename(char specifier, void *data, void *userdata, char *
return unit_name_to_path(u->id, ret);
}
-static void bad_specifier(Unit *u, char specifier) {
+static void bad_specifier(const Unit *u, char specifier) {
log_unit_warning(u, "Specifier '%%%c' used in unit configuration, which is deprecated. Please update your unit file, as it does not work as intended.", specifier);
}
-static int specifier_cgroup(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_cgroup(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
char *n;
assert(u);
@@ -115,8 +115,8 @@ static int specifier_cgroup(char specifier, void *data, void *userdata, char **r
return 0;
}
-static int specifier_cgroup_root(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_cgroup_root(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
char *n;
assert(u);
@@ -131,8 +131,8 @@ static int specifier_cgroup_root(char specifier, void *data, void *userdata, cha
return 0;
}
-static int specifier_cgroup_slice(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_cgroup_slice(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
char *n;
assert(u);
@@ -140,7 +140,7 @@ static int specifier_cgroup_slice(char specifier, void *data, void *userdata, ch
bad_specifier(u, specifier);
if (UNIT_ISSET(u->slice)) {
- Unit *slice;
+ const Unit *slice;
slice = UNIT_DEREF(u->slice);
@@ -157,8 +157,8 @@ static int specifier_cgroup_slice(char specifier, void *data, void *userdata, ch
return 0;
}
-static int specifier_special_directory(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
+static int specifier_special_directory(char specifier, const void *data, const void *userdata, char **ret) {
+ const Unit *u = userdata;
char *n = NULL;
assert(u);
@@ -196,6 +196,8 @@ int unit_name_printf(Unit *u, const char* format, char **ret) {
{ 'p', specifier_prefix, NULL },
{ 'i', specifier_string, u->instance },
+ { 'g', specifier_group_name, NULL },
+ { 'G', specifier_group_id, NULL },
{ 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
@@ -264,6 +266,8 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
{ 'T', specifier_tmp_dir, NULL },
{ 'V', specifier_var_tmp_dir, NULL },
+ { 'g', specifier_group_name, NULL },
+ { 'G', specifier_group_id, NULL },
{ 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
{ 'h', specifier_user_home, NULL },
@@ -282,35 +286,3 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
return specifier_printf(format, table, u, ret);
}
-
-int unit_full_printf_strv(Unit *u, char **l, char ***ret) {
- size_t n;
- char **r, **i, **j;
- int q;
-
- /* Applies unit_full_printf to every entry in l */
-
- assert(u);
-
- n = strv_length(l);
- r = new(char*, n+1);
- if (!r)
- return -ENOMEM;
-
- for (i = l, j = r; *i; i++, j++) {
- q = unit_full_printf(u, *i, j);
- if (q < 0)
- goto fail;
- }
-
- *j = NULL;
- *ret = r;
- return 0;
-
-fail:
- for (j--; j >= r; j--)
- free(*j);
-
- free(r);
- return q;
-}
diff --git a/src/core/unit-printf.h b/src/core/unit-printf.h
index 5bd1d77bb2..f3dae159d5 100644
--- a/src/core/unit-printf.h
+++ b/src/core/unit-printf.h
@@ -5,4 +5,3 @@
int unit_name_printf(Unit *u, const char* text, char **ret);
int unit_full_printf(Unit *u, const char *text, char **ret);
-int unit_full_printf_strv(Unit *u, char **l, char ***ret);
diff --git a/src/core/unit.c b/src/core/unit.c
index 113205bf25..24b14fbcd6 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -10,8 +10,8 @@
#include "sd-id128.h"
#include "sd-messages.h"
-#include "alloc-util.h"
#include "all-units.h"
+#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-util.h"
#include "cgroup-util.h"
@@ -22,6 +22,7 @@
#include "execute.h"
#include "fd-util.h"
#include "fileio-label.h"
+#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "id128-util.h"
@@ -35,6 +36,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "serialize.h"
#include "set.h"
#include "signal-util.h"
#include "sparse-endian.h"
@@ -45,6 +47,8 @@
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
+#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "umask-util.h"
#include "unit-name.h"
#include "unit.h"
@@ -93,7 +97,8 @@ Unit *unit_new(Manager *m, size_t size) {
u->ref_uid = UID_INVALID;
u->ref_gid = GID_INVALID;
u->cpu_usage_last = NSEC_INFINITY;
- u->cgroup_bpf_state = UNIT_CGROUP_BPF_INVALIDATED;
+ u->cgroup_invalidated_mask |= CGROUP_MASK_BPF_FIREWALL;
+ u->failure_action_exit_status = u->success_action_exit_status = -1;
u->ip_accounting_ingress_map_fd = -1;
u->ip_accounting_egress_map_fd = -1;
@@ -127,7 +132,7 @@ int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret) {
return r;
}
-bool unit_has_name(Unit *u, const char *name) {
+bool unit_has_name(const Unit *u, const char *name) {
assert(u);
assert(name);
@@ -438,6 +443,22 @@ void unit_add_to_dbus_queue(Unit *u) {
u->in_dbus_queue = true;
}
+void unit_submit_to_stop_when_unneeded_queue(Unit *u) {
+ assert(u);
+
+ if (u->in_stop_when_unneeded_queue)
+ return;
+
+ if (!u->stop_when_unneeded)
+ return;
+
+ if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
+ return;
+
+ LIST_PREPEND(stop_when_unneeded_queue, u->manager->stop_when_unneeded_queue, u);
+ u->in_stop_when_unneeded_queue = true;
+}
+
static void bidi_set_free(Unit *u, Hashmap *h) {
Unit *other;
Iterator i;
@@ -553,6 +574,14 @@ void unit_free(Unit *u) {
if (!u)
return;
+ if (UNIT_ISSET(u->slice)) {
+ /* A unit is being dropped from the tree, make sure our parent slice recalculates the member mask */
+ unit_invalidate_cgroup_members_masks(UNIT_DEREF(u->slice));
+
+ /* And make sure the parent is realized again, updating cgroup memberships */
+ unit_add_to_cgroup_realize_queue(UNIT_DEREF(u->slice));
+ }
+
u->transient_file = safe_fclose(u->transient_file);
if (!MANAGER_IS_RELOADING(u->manager))
@@ -634,6 +663,9 @@ void unit_free(Unit *u) {
if (u->in_target_deps_queue)
LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u);
+ if (u->in_stop_when_unneeded_queue)
+ LIST_REMOVE(stop_when_unneeded_queue, u->manager->stop_when_unneeded_queue, u);
+
safe_close(u->ip_accounting_ingress_map_fd);
safe_close(u->ip_accounting_egress_map_fd);
@@ -647,6 +679,8 @@ void unit_free(Unit *u) {
bpf_program_unref(u->ip_bpf_egress);
bpf_program_unref(u->ip_bpf_egress_installed);
+ bpf_program_unref(u->bpf_device_control_installed);
+
condition_free_list(u->conditions);
condition_free_list(u->asserts);
@@ -943,7 +977,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
assert(u);
assert(c);
- if (c->working_directory) {
+ if (c->working_directory && !c->working_directory_missing_ok) {
r = unit_require_mounts_for(u, c->working_directory, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
@@ -990,7 +1024,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
return r;
}
- r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
@@ -1008,7 +1042,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
/* If syslog or kernel logging is requested, make sure our own
* logging daemon is run first. */
- r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
@@ -1133,17 +1167,20 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
(void) cg_mask_to_string(u->cgroup_realized_mask, &s);
fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
}
+
if (u->cgroup_enabled_mask != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
}
+
m = unit_get_own_mask(u);
if (m != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(m, &s);
fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
}
+
m = unit_get_members_mask(u);
if (m != 0) {
_cleanup_free_ char *s = NULL;
@@ -1151,6 +1188,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
}
+ m = unit_get_delegate_mask(u);
+ if (m != 0) {
+ _cleanup_free_ char *s = NULL;
+ (void) cg_mask_to_string(m, &s);
+ fprintf(f, "%s\tCGroup delegate mask: %s\n", prefix, strnull(s));
+ }
+
SET_FOREACH(t, u->names, i)
fprintf(f, "%s\tName: %s\n", prefix, t);
@@ -1184,8 +1228,12 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
if (u->failure_action != EMERGENCY_ACTION_NONE)
fprintf(f, "%s\tFailure Action: %s\n", prefix, emergency_action_to_string(u->failure_action));
+ if (u->failure_action_exit_status >= 0)
+ fprintf(f, "%s\tFailure Action Exit Status: %i\n", prefix, u->failure_action_exit_status);
if (u->success_action != EMERGENCY_ACTION_NONE)
fprintf(f, "%s\tSuccess Action: %s\n", prefix, emergency_action_to_string(u->success_action));
+ if (u->success_action_exit_status >= 0)
+ fprintf(f, "%s\tSuccess Action Exit Status: %i\n", prefix, u->success_action_exit_status);
if (u->job_timeout != USEC_INFINITY)
fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
@@ -1379,7 +1427,7 @@ static int unit_add_slice_dependencies(Unit *u) {
if (unit_has_name(u, SPECIAL_ROOT_SLICE))
return 0;
- return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_ROOT_SLICE, NULL, true, mask);
+ return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_ROOT_SLICE, true, mask);
}
static int unit_add_mount_dependencies(Unit *u) {
@@ -1469,6 +1517,9 @@ int unit_load(Unit *u) {
return 0;
if (u->transient_file) {
+ /* Finalize transient file: if this is a transient unit file, as soon as we reach unit_load() the setup
+ * is complete, hence let's synchronize the unit file we just wrote to disk. */
+
r = fflush_and_check(u->transient_file);
if (r < 0)
goto fail;
@@ -1512,7 +1563,8 @@ int unit_load(Unit *u) {
if (u->job_running_timeout != USEC_INFINITY && u->job_running_timeout > u->job_timeout)
log_unit_warning(u, "JobRunningTimeoutSec= is greater than JobTimeoutSec=, it has no effect.");
- unit_update_cgroup_members_masks(u);
+ /* We finished loading, let's ensure our parents recalculate the members mask */
+ unit_invalidate_cgroup_members_masks(u);
}
assert((u->load_state != UNIT_MERGED) == !u->merged_into);
@@ -1587,6 +1639,8 @@ static bool unit_condition_test(Unit *u) {
dual_timestamp_get(&u->condition_timestamp);
u->condition_result = unit_condition_test_list(u, u->conditions, condition_type_to_string);
+ unit_add_to_dbus_queue(u);
+
return u->condition_result;
}
@@ -1596,103 +1650,26 @@ static bool unit_assert_test(Unit *u) {
dual_timestamp_get(&u->assert_timestamp);
u->assert_result = unit_condition_test_list(u, u->asserts, assert_type_to_string);
+ unit_add_to_dbus_queue(u);
+
return u->assert_result;
}
void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
- DISABLE_WARNING_FORMAT_NONLITERAL;
- manager_status_printf(u->manager, STATUS_TYPE_NORMAL, status, unit_status_msg_format, unit_description(u));
- REENABLE_WARNING;
-}
-
-_pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
- const char *format;
- const UnitStatusMessageFormats *format_table;
-
- assert(u);
- assert(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD));
-
- if (t != JOB_RELOAD) {
- format_table = &UNIT_VTABLE(u)->status_message_formats;
- if (format_table) {
- format = format_table->starting_stopping[t == JOB_STOP];
- if (format)
- return format;
- }
- }
-
- /* Return generic strings */
- if (t == JOB_START)
- return "Starting %s.";
- else if (t == JOB_STOP)
- return "Stopping %s.";
- else
- return "Reloading %s.";
-}
-
-static void unit_status_print_starting_stopping(Unit *u, JobType t) {
- const char *format;
-
- assert(u);
-
- /* Reload status messages have traditionally not been printed to console. */
- if (!IN_SET(t, JOB_START, JOB_STOP))
- return;
+ const char *d;
- format = unit_get_status_message_format(u, t);
+ d = unit_description(u);
+ if (log_get_show_color())
+ d = strjoina(ANSI_HIGHLIGHT, d, ANSI_NORMAL);
DISABLE_WARNING_FORMAT_NONLITERAL;
- unit_status_printf(u, "", format);
+ manager_status_printf(u->manager, STATUS_TYPE_NORMAL, status, unit_status_msg_format, d);
REENABLE_WARNING;
}
-static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
- const char *format, *mid;
- char buf[LINE_MAX];
-
- assert(u);
-
- if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD))
- return;
-
- if (log_on_console())
- return;
-
- /* We log status messages for all units and all operations. */
-
- format = unit_get_status_message_format(u, t);
-
- DISABLE_WARNING_FORMAT_NONLITERAL;
- (void) snprintf(buf, sizeof buf, format, unit_description(u));
- REENABLE_WARNING;
-
- mid = t == JOB_START ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTING_STR :
- t == JOB_STOP ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPING_STR :
- "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADING_STR;
-
- /* Note that we deliberately use LOG_MESSAGE() instead of
- * LOG_UNIT_MESSAGE() here, since this is supposed to mimic
- * closely what is written to screen using the status output,
- * which is supposed the highest level, friendliest output
- * possible, which means we should avoid the low-level unit
- * name. */
- log_struct(LOG_INFO,
- LOG_MESSAGE("%s", buf),
- LOG_UNIT_ID(u),
- LOG_UNIT_INVOCATION_ID(u),
- mid);
-}
-
-void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) {
- assert(u);
- assert(t >= 0);
- assert(t < _JOB_TYPE_MAX);
-
- unit_status_log_starting_stopping_reloading(u, t);
- unit_status_print_starting_stopping(u, t);
-}
-
int unit_start_limit_test(Unit *u) {
+ const char *reason;
+
assert(u);
if (ratelimit_below(&u->start_limit)) {
@@ -1703,7 +1680,11 @@ int unit_start_limit_test(Unit *u) {
log_unit_warning(u, "Start request repeated too quickly.");
u->start_limit_hit = true;
- return emergency_action(u->manager, u->start_limit_action, u->reboot_arg, "unit failed");
+ reason = strjoina("unit ", u->id, " failed");
+
+ return emergency_action(u->manager, u->start_limit_action,
+ EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN,
+ u->reboot_arg, -1, reason);
}
bool unit_shall_confirm_spawn(Unit *u) {
@@ -1784,7 +1765,7 @@ int unit_start(Unit *u) {
if (state != UNIT_ACTIVATING &&
!unit_condition_test(u)) {
log_unit_debug(u, "Starting requested but condition failed. Not starting unit.");
- return -EALREADY;
+ return -ECOMM;
}
/* If the asserts failed, fail the entire job */
@@ -1950,55 +1931,71 @@ bool unit_can_reload(Unit *u) {
return UNIT_VTABLE(u)->reload;
}
-static void unit_check_unneeded(Unit *u) {
-
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-
- static const UnitDependency needed_dependencies[] = {
+bool unit_is_unneeded(Unit *u) {
+ static const UnitDependency deps[] = {
UNIT_REQUIRED_BY,
UNIT_REQUISITE_OF,
UNIT_WANTED_BY,
UNIT_BOUND_BY,
};
-
- unsigned j;
- int r;
+ size_t j;
assert(u);
- /* If this service shall be shut down when unneeded then do
- * so. */
-
if (!u->stop_when_unneeded)
- return;
+ return false;
- if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
- return;
+ /* Don't clean up while the unit is transitioning or is even inactive. */
+ if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
+ return false;
+ if (u->job)
+ return false;
- for (j = 0; j < ELEMENTSOF(needed_dependencies); j++) {
+ for (j = 0; j < ELEMENTSOF(deps); j++) {
Unit *other;
Iterator i;
void *v;
- HASHMAP_FOREACH_KEY(v, other, u->dependencies[needed_dependencies[j]], i)
- if (unit_active_or_pending(other) || unit_will_restart(other))
- return;
- }
+ /* If a dependent unit has a job queued, is active or transitioning, or is marked for
+ * restart, then don't clean this one up. */
- /* If stopping a unit fails continuously we might enter a stop
- * loop here, hence stop acting on the service being
- * unnecessary after a while. */
- if (!ratelimit_below(&u->auto_stop_ratelimit)) {
- log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently.");
- return;
+ HASHMAP_FOREACH_KEY(v, other, u->dependencies[deps[j]], i) {
+ if (other->job)
+ return false;
+
+ if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
+ return false;
+
+ if (unit_will_restart(other))
+ return false;
+ }
}
- log_unit_info(u, "Unit not needed anymore. Stopping.");
+ return true;
+}
- /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
- r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL);
- if (r < 0)
- log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r));
+static void check_unneeded_dependencies(Unit *u) {
+
+ static const UnitDependency deps[] = {
+ UNIT_REQUIRES,
+ UNIT_REQUISITE,
+ UNIT_WANTS,
+ UNIT_BINDS_TO,
+ };
+ size_t j;
+
+ assert(u);
+
+ /* Add all units this unit depends on to the queue that processes StopWhenUnneeded= behaviour. */
+
+ for (j = 0; j < ELEMENTSOF(deps); j++) {
+ Unit *other;
+ Iterator i;
+ void *v;
+
+ HASHMAP_FOREACH_KEY(v, other, u->dependencies[deps[j]], i)
+ unit_submit_to_stop_when_unneeded_queue(other);
+ }
}
static void unit_check_binds_to(Unit *u) {
@@ -2098,29 +2095,6 @@ static void retroactively_stop_dependencies(Unit *u) {
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
}
-static void check_unneeded_dependencies(Unit *u) {
- Unit *other;
- Iterator i;
- void *v;
-
- assert(u);
- assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
-
- /* Garbage collect services that might not be needed anymore, if enabled */
- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRES], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_WANTS], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUISITE], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
-}
-
void unit_start_on_failure(Unit *u) {
Unit *other;
Iterator i;
@@ -2156,8 +2130,9 @@ void unit_trigger_notify(Unit *u) {
}
static int unit_log_resources(Unit *u) {
-
struct iovec iovec[1 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + 4];
+ bool any_traffic = false, have_ip_accounting = false;
+ _cleanup_free_ char *igress = NULL, *egress = NULL;
size_t n_message_parts = 0, n_iovec = 0;
char* message_parts[3 + 1], *t;
nsec_t nsec = NSEC_INFINITY;
@@ -2190,7 +2165,7 @@ static int unit_log_resources(Unit *u) {
/* Format the CPU time for inclusion in the human language message string */
format_timespan(buf, sizeof(buf), nsec / NSEC_PER_USEC, USEC_PER_MSEC);
- t = strjoin(n_message_parts > 0 ? "consumed " : "Consumed ", buf, " CPU time");
+ t = strjoin("consumed ", buf, " CPU time");
if (!t) {
r = log_oom();
goto finish;
@@ -2209,6 +2184,10 @@ static int unit_log_resources(Unit *u) {
if (value == UINT64_MAX)
continue;
+ have_ip_accounting = true;
+ if (value > 0)
+ any_traffic = true;
+
/* Format IP accounting data for inclusion in the structured log message */
if (asprintf(&t, "%s=%" PRIu64, ip_fields[m], value) < 0) {
r = log_oom();
@@ -2218,22 +2197,41 @@ static int unit_log_resources(Unit *u) {
/* Format the IP accounting data for inclusion in the human language message string, but only for the
* bytes counters (and not for the packets counters) */
- if (m == CGROUP_IP_INGRESS_BYTES)
- t = strjoin(n_message_parts > 0 ? "received " : "Received ",
- format_bytes(buf, sizeof(buf), value),
- " IP traffic");
- else if (m == CGROUP_IP_EGRESS_BYTES)
- t = strjoin(n_message_parts > 0 ? "sent " : "Sent ",
- format_bytes(buf, sizeof(buf), value),
- " IP traffic");
- else
- continue;
- if (!t) {
- r = log_oom();
- goto finish;
+ if (m == CGROUP_IP_INGRESS_BYTES) {
+ assert(!igress);
+ igress = strjoin("received ", format_bytes(buf, sizeof(buf), value), " IP traffic");
+ if (!igress) {
+ r = log_oom();
+ goto finish;
+ }
+ } else if (m == CGROUP_IP_EGRESS_BYTES) {
+ assert(!egress);
+ egress = strjoin("sent ", format_bytes(buf, sizeof(buf), value), " IP traffic");
+ if (!egress) {
+ r = log_oom();
+ goto finish;
+ }
}
+ }
- message_parts[n_message_parts++] = t;
+ if (have_ip_accounting) {
+ if (any_traffic) {
+ if (igress)
+ message_parts[n_message_parts++] = TAKE_PTR(igress);
+ if (egress)
+ message_parts[n_message_parts++] = TAKE_PTR(egress);
+
+ } else {
+ char *k;
+
+ k = strdup("no IP traffic");
+ if (!k) {
+ r = log_oom();
+ goto finish;
+ }
+
+ message_parts[n_message_parts++] = k;
+ }
}
/* Is there any accounting data available at all? */
@@ -2243,7 +2241,7 @@ static int unit_log_resources(Unit *u) {
}
if (n_message_parts == 0)
- t = strjoina("MESSAGE=", u->id, ": Completed");
+ t = strjoina("MESSAGE=", u->id, ": Completed.");
else {
_cleanup_free_ char *joined;
@@ -2255,7 +2253,8 @@ static int unit_log_resources(Unit *u) {
goto finish;
}
- t = strjoina("MESSAGE=", u->id, ": ", joined);
+ joined[0] = ascii_toupper(joined[0]);
+ t = strjoina("MESSAGE=", u->id, ": ", joined, ".");
}
/* The following four fields we allocate on the stack or are static strings, we hence don't want to free them,
@@ -2299,8 +2298,105 @@ static void unit_update_on_console(Unit *u) {
manager_unref_console(u->manager);
}
+static void unit_emit_audit_start(Unit *u) {
+ assert(u);
+
+ if (u->type != UNIT_SERVICE)
+ return;
+
+ /* Write audit record if we have just finished starting up */
+ manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true);
+ u->in_audit = true;
+}
+
+static void unit_emit_audit_stop(Unit *u, UnitActiveState state) {
+ assert(u);
+
+ if (u->type != UNIT_SERVICE)
+ return;
+
+ if (u->in_audit) {
+ /* Write audit record if we have just finished shutting down */
+ manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, state == UNIT_INACTIVE);
+ u->in_audit = false;
+ } else {
+ /* Hmm, if there was no start record written write it now, so that we always have a nice pair */
+ manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, state == UNIT_INACTIVE);
+
+ if (state == UNIT_INACTIVE)
+ manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true);
+ }
+}
+
+static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags) {
+ bool unexpected = false;
+
+ assert(j);
+
+ if (j->state == JOB_WAITING)
+
+ /* So we reached a different state for this job. Let's see if we can run it now if it failed previously
+ * due to EAGAIN. */
+ job_add_to_run_queue(j);
+
+ /* Let's check whether the unit's new state constitutes a finished job, or maybe contradicts a running job and
+ * hence needs to invalidate jobs. */
+
+ switch (j->type) {
+
+ case JOB_START:
+ case JOB_VERIFY_ACTIVE:
+
+ if (UNIT_IS_ACTIVE_OR_RELOADING(ns))
+ job_finish_and_invalidate(j, JOB_DONE, true, false);
+ else if (j->state == JOB_RUNNING && ns != UNIT_ACTIVATING) {
+ unexpected = true;
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+ job_finish_and_invalidate(j, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false);
+ }
+
+ break;
+
+ case JOB_RELOAD:
+ case JOB_RELOAD_OR_START:
+ case JOB_TRY_RELOAD:
+
+ if (j->state == JOB_RUNNING) {
+ if (ns == UNIT_ACTIVE)
+ job_finish_and_invalidate(j, (flags & UNIT_NOTIFY_RELOAD_FAILURE) ? JOB_FAILED : JOB_DONE, true, false);
+ else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING)) {
+ unexpected = true;
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+ job_finish_and_invalidate(j, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false);
+ }
+ }
+
+ break;
+
+ case JOB_STOP:
+ case JOB_RESTART:
+ case JOB_TRY_RESTART:
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+ job_finish_and_invalidate(j, JOB_DONE, true, false);
+ else if (j->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) {
+ unexpected = true;
+ job_finish_and_invalidate(j, JOB_FAILED, true, false);
+ }
+
+ break;
+
+ default:
+ assert_not_reached("Job type unknown");
+ }
+
+ return unexpected;
+}
+
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags) {
- bool unexpected;
+ const char *reason;
Manager *m;
assert(u);
@@ -2313,6 +2409,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
m = u->manager;
+ /* Let's enqueue the change signal early. In case this unit has a job associated we want that this unit is in
+ * the bus queue, so that any job change signal queued will force out the unit change signal first. */
+ unit_add_to_dbus_queue(u);
+
/* Update timestamps for state changes */
if (!MANAGER_IS_RELOADING(m)) {
dual_timestamp_get(&u->state_change_timestamp);
@@ -2329,7 +2429,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
}
/* Keep track of failed units */
- (void) manager_update_failed_units(u->manager, u, ns == UNIT_FAILED);
+ (void) manager_update_failed_units(m, u, ns == UNIT_FAILED);
/* Make sure the cgroup and state files are always removed when we become inactive */
if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
@@ -2339,81 +2439,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
unit_update_on_console(u);
- if (u->job) {
- unexpected = false;
-
- if (u->job->state == JOB_WAITING)
-
- /* So we reached a different state for this
- * job. Let's see if we can run it now if it
- * failed previously due to EAGAIN. */
- job_add_to_run_queue(u->job);
-
- /* Let's check whether this state change constitutes a
- * finished job, or maybe contradicts a running job and
- * hence needs to invalidate jobs. */
-
- switch (u->job->type) {
-
- case JOB_START:
- case JOB_VERIFY_ACTIVE:
-
- if (UNIT_IS_ACTIVE_OR_RELOADING(ns))
- job_finish_and_invalidate(u->job, JOB_DONE, true, false);
- else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) {
- unexpected = true;
-
- if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false);
- }
-
- break;
-
- case JOB_RELOAD:
- case JOB_RELOAD_OR_START:
- case JOB_TRY_RELOAD:
-
- if (u->job->state == JOB_RUNNING) {
- if (ns == UNIT_ACTIVE)
- job_finish_and_invalidate(u->job, (flags & UNIT_NOTIFY_RELOAD_FAILURE) ? JOB_FAILED : JOB_DONE, true, false);
- else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING)) {
- unexpected = true;
-
- if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false);
- }
- }
-
- break;
-
- case JOB_STOP:
- case JOB_RESTART:
- case JOB_TRY_RESTART:
-
- if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- job_finish_and_invalidate(u->job, JOB_DONE, true, false);
- else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) {
- unexpected = true;
- job_finish_and_invalidate(u->job, JOB_FAILED, true, false);
- }
-
- break;
-
- default:
- assert_not_reached("Job type unknown");
- }
-
- } else
- unexpected = true;
-
if (!MANAGER_IS_RELOADING(m)) {
+ bool unexpected;
- /* If this state change happened without being
- * requested by a job, then let's retroactively start
- * or stop dependencies. We skip that step when
- * deserializing, since we don't want to create any
- * additional jobs just because something is already
- * activated. */
+ /* Let's propagate state changes to the job */
+ if (u->job)
+ unexpected = unit_process_job(u->job, ns, flags);
+ else
+ unexpected = true;
+
+ /* If this state change happened without being requested by a job, then let's retroactively start or
+ * stop dependencies. We skip that step when deserializing, since we don't want to create any
+ * additional jobs just because something is already activated. */
if (unexpected) {
if (UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns))
@@ -2423,7 +2460,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
}
/* stop unneeded units regardless if going down was expected or not */
- if (UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
check_unneeded_dependencies(u);
if (ns != os && ns == UNIT_FAILED) {
@@ -2432,46 +2469,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
if (!(flags & UNIT_NOTIFY_WILL_AUTO_RESTART))
unit_start_on_failure(u);
}
- }
-
- if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
- if (u->type == UNIT_SERVICE &&
- !UNIT_IS_ACTIVE_OR_RELOADING(os) &&
- !MANAGER_IS_RELOADING(m)) {
- /* Write audit record if we have just finished starting up */
- manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
- u->in_audit = true;
- }
+ if (UNIT_IS_ACTIVE_OR_RELOADING(ns) && !UNIT_IS_ACTIVE_OR_RELOADING(os)) {
+ /* This unit just finished starting up */
- if (!UNIT_IS_ACTIVE_OR_RELOADING(os))
+ unit_emit_audit_start(u);
manager_send_unit_plymouth(m, u);
+ }
- } else {
-
- if (UNIT_IS_INACTIVE_OR_FAILED(ns) &&
- !UNIT_IS_INACTIVE_OR_FAILED(os)
- && !MANAGER_IS_RELOADING(m)) {
-
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns) && !UNIT_IS_INACTIVE_OR_FAILED(os)) {
/* This unit just stopped/failed. */
- if (u->type == UNIT_SERVICE) {
-
- /* Hmm, if there was no start record written
- * write it now, so that we always have a nice
- * pair */
- if (!u->in_audit) {
- manager_send_unit_audit(m, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
- if (ns == UNIT_INACTIVE)
- manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, true);
- } else
- /* Write audit record if we have just finished shutting down */
- manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
-
- u->in_audit = false;
- }
-
- /* Write a log message about consumed resources */
+ unit_emit_audit_stop(u, ns);
unit_log_resources(u);
}
}
@@ -2481,22 +2490,24 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
unit_trigger_notify(u);
- if (!MANAGER_IS_RELOADING(u->manager)) {
+ if (!MANAGER_IS_RELOADING(m)) {
/* Maybe we finished startup and are now ready for being stopped because unneeded? */
- unit_check_unneeded(u);
+ unit_submit_to_stop_when_unneeded_queue(u);
/* Maybe we finished startup, but something we needed has vanished? Let's die then. (This happens when
* something BindsTo= to a Type=oneshot unit, as these units go directly from starting to inactive,
* without ever entering started.) */
unit_check_binds_to(u);
- if (os != UNIT_FAILED && ns == UNIT_FAILED)
- (void) emergency_action(u->manager, u->failure_action, u->reboot_arg, "unit failed");
- else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && ns == UNIT_INACTIVE)
- (void) emergency_action(u->manager, u->success_action, u->reboot_arg, "unit succeeded");
+ if (os != UNIT_FAILED && ns == UNIT_FAILED) {
+ reason = strjoina("unit ", u->id, " failed");
+ (void) emergency_action(m, u->failure_action, 0, u->reboot_arg, unit_failure_action_exit_status(u), reason);
+ } else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && ns == UNIT_INACTIVE) {
+ reason = strjoina("unit ", u->id, " succeeded");
+ (void) emergency_action(m, u->success_action, 0, u->reboot_arg, unit_success_action_exit_status(u), reason);
+ }
}
- unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
}
@@ -2882,17 +2893,14 @@ int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit
return unit_add_dependency(u, e, other, add_reference, mask);
}
-static int resolve_template(Unit *u, const char *name, const char*path, char **buf, const char **ret) {
+static int resolve_template(Unit *u, const char *name, char **buf, const char **ret) {
int r;
assert(u);
- assert(name || path);
+ assert(name);
assert(buf);
assert(ret);
- if (!name)
- name = basename(path);
-
if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
*buf = NULL;
*ret = name;
@@ -2917,38 +2925,38 @@ static int resolve_template(Unit *u, const char *name, const char*path, char **b
return 0;
}
-int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference, UnitDependencyMask mask) {
+int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, bool add_reference, UnitDependencyMask mask) {
_cleanup_free_ char *buf = NULL;
Unit *other;
int r;
assert(u);
- assert(name || path);
+ assert(name);
- r = resolve_template(u, name, path, &buf, &name);
+ r = resolve_template(u, name, &buf, &name);
if (r < 0)
return r;
- r = manager_load_unit(u->manager, name, path, NULL, &other);
+ r = manager_load_unit(u->manager, name, NULL, NULL, &other);
if (r < 0)
return r;
return unit_add_dependency(u, d, other, add_reference, mask);
}
-int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference, UnitDependencyMask mask) {
+int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, bool add_reference, UnitDependencyMask mask) {
_cleanup_free_ char *buf = NULL;
Unit *other;
int r;
assert(u);
- assert(name || path);
+ assert(name);
- r = resolve_template(u, name, path, &buf, &name);
+ r = resolve_template(u, name, &buf, &name);
if (r < 0)
return r;
- r = manager_load_unit(u->manager, name, path, NULL, &other);
+ r = manager_load_unit(u->manager, name, NULL, NULL, &other);
if (r < 0)
return r;
@@ -3019,7 +3027,6 @@ int unit_set_slice(Unit *u, Unit *slice) {
}
int unit_set_default_slice(Unit *u) {
- _cleanup_free_ char *b = NULL;
const char *slice_name;
Unit *slice;
int r;
@@ -3047,13 +3054,9 @@ int unit_set_default_slice(Unit *u) {
return -ENOMEM;
if (MANAGER_IS_SYSTEM(u->manager))
- b = strjoin("system-", escaped, ".slice");
+ slice_name = strjoina("system-", escaped, ".slice");
else
- b = strappend(escaped, ".slice");
- if (!b)
- return -ENOMEM;
-
- slice_name = b;
+ slice_name = strjoina(escaped, ".slice");
} else
slice_name =
MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE)
@@ -3178,23 +3181,21 @@ bool unit_can_serialize(Unit *u) {
return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item;
}
-static int unit_serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) {
+static int serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) {
_cleanup_free_ char *s = NULL;
- int r = 0;
+ int r;
assert(f);
assert(key);
- if (mask != 0) {
- r = cg_mask_to_string(mask, &s);
- if (r >= 0) {
- fputs(key, f);
- fputc('=', f);
- fputs(s, f);
- fputc('\n', f);
- }
- }
- return r;
+ if (mask == 0)
+ return 0;
+
+ r = cg_mask_to_string(mask, &s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to format cgroup mask: %m");
+
+ return serialize_item(f, key, s);
}
static const char *ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
@@ -3218,46 +3219,50 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
return r;
}
- dual_timestamp_serialize(f, "state-change-timestamp", &u->state_change_timestamp);
+ (void) serialize_dual_timestamp(f, "state-change-timestamp", &u->state_change_timestamp);
- dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
- dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp);
- dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp);
- dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
+ (void) serialize_dual_timestamp(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
+ (void) serialize_dual_timestamp(f, "active-enter-timestamp", &u->active_enter_timestamp);
+ (void) serialize_dual_timestamp(f, "active-exit-timestamp", &u->active_exit_timestamp);
+ (void) serialize_dual_timestamp(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
- dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp);
- dual_timestamp_serialize(f, "assert-timestamp", &u->assert_timestamp);
+ (void) serialize_dual_timestamp(f, "condition-timestamp", &u->condition_timestamp);
+ (void) serialize_dual_timestamp(f, "assert-timestamp", &u->assert_timestamp);
if (dual_timestamp_is_set(&u->condition_timestamp))
- unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result));
+ (void) serialize_bool(f, "condition-result", u->condition_result);
if (dual_timestamp_is_set(&u->assert_timestamp))
- unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result));
+ (void) serialize_bool(f, "assert-result", u->assert_result);
- unit_serialize_item(u, f, "transient", yes_no(u->transient));
+ (void) serialize_bool(f, "transient", u->transient);
+ (void) serialize_bool(f, "in-audit", u->in_audit);
- unit_serialize_item(u, f, "exported-invocation-id", yes_no(u->exported_invocation_id));
- unit_serialize_item(u, f, "exported-log-level-max", yes_no(u->exported_log_level_max));
- unit_serialize_item(u, f, "exported-log-extra-fields", yes_no(u->exported_log_extra_fields));
+ (void) serialize_bool(f, "exported-invocation-id", u->exported_invocation_id);
+ (void) serialize_bool(f, "exported-log-level-max", u->exported_log_level_max);
+ (void) serialize_bool(f, "exported-log-extra-fields", u->exported_log_extra_fields);
+ (void) serialize_bool(f, "exported-log-rate-limit-interval", u->exported_log_rate_limit_interval);
+ (void) serialize_bool(f, "exported-log-rate-limit-burst", u->exported_log_rate_limit_burst);
- unit_serialize_item_format(u, f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
+ (void) serialize_item_format(f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
if (u->cpu_usage_last != NSEC_INFINITY)
- unit_serialize_item_format(u, f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
+ (void) serialize_item_format(f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
if (u->cgroup_path)
- unit_serialize_item(u, f, "cgroup", u->cgroup_path);
- unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized));
- (void) unit_serialize_cgroup_mask(f, "cgroup-realized-mask", u->cgroup_realized_mask);
- (void) unit_serialize_cgroup_mask(f, "cgroup-enabled-mask", u->cgroup_enabled_mask);
- unit_serialize_item_format(u, f, "cgroup-bpf-realized", "%i", u->cgroup_bpf_state);
+ (void) serialize_item(f, "cgroup", u->cgroup_path);
+
+ (void) serialize_bool(f, "cgroup-realized", u->cgroup_realized);
+ (void) serialize_cgroup_mask(f, "cgroup-realized-mask", u->cgroup_realized_mask);
+ (void) serialize_cgroup_mask(f, "cgroup-enabled-mask", u->cgroup_enabled_mask);
+ (void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", u->cgroup_invalidated_mask);
if (uid_is_valid(u->ref_uid))
- unit_serialize_item_format(u, f, "ref-uid", UID_FMT, u->ref_uid);
+ (void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
if (gid_is_valid(u->ref_gid))
- unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid);
+ (void) serialize_item_format(f, "ref-gid", GID_FMT, u->ref_gid);
if (!sd_id128_is_null(u->invocation_id))
- unit_serialize_item_format(u, f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
+ (void) serialize_item_format(f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
bus_track_serialize(u->bus_track, f, "ref");
@@ -3266,17 +3271,17 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
r = unit_get_ip_accounting(u, m, &v);
if (r >= 0)
- unit_serialize_item_format(u, f, ip_accounting_metric_field[m], "%" PRIu64, v);
+ (void) serialize_item_format(f, ip_accounting_metric_field[m], "%" PRIu64, v);
}
if (serialize_jobs) {
if (u->job) {
- fprintf(f, "job\n");
+ fputs("job\n", f);
job_serialize(u->job, f);
}
if (u->nop_job) {
- fprintf(f, "job\n");
+ fputs("job\n", f);
job_serialize(u->nop_job, f);
}
}
@@ -3286,78 +3291,27 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
return 0;
}
-int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
- assert(u);
- assert(f);
- assert(key);
-
- if (!value)
- return 0;
-
- fputs(key, f);
- fputc('=', f);
- fputs(value, f);
- fputc('\n', f);
-
- return 1;
-}
-
-int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value) {
- _cleanup_free_ char *c = NULL;
-
- assert(u);
- assert(f);
- assert(key);
-
- if (!value)
- return 0;
-
- c = cescape(value);
- if (!c)
- return -ENOMEM;
-
- fputs(key, f);
- fputc('=', f);
- fputs(c, f);
- fputc('\n', f);
-
- return 1;
-}
-
-int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd) {
- int copy;
+static int unit_deserialize_job(Unit *u, FILE *f) {
+ _cleanup_(job_freep) Job *j = NULL;
+ int r;
assert(u);
assert(f);
- assert(key);
-
- if (fd < 0)
- return 0;
- copy = fdset_put_dup(fds, fd);
- if (copy < 0)
- return copy;
+ j = job_new_raw(u);
+ if (!j)
+ return log_oom();
- fprintf(f, "%s=%i\n", key, copy);
- return 1;
-}
-
-void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
- va_list ap;
-
- assert(u);
- assert(f);
- assert(key);
- assert(format);
-
- fputs(key, f);
- fputc('=', f);
+ r = job_deserialize(j, f);
+ if (r < 0)
+ return r;
- va_start(ap, format);
- vfprintf(f, format, ap);
- va_end(ap);
+ r = job_install_deserialized(j);
+ if (r < 0)
+ return r;
- fputc('\n', f);
+ TAKE_PTR(j);
+ return 0;
}
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
@@ -3368,21 +3322,19 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
assert(fds);
for (;;) {
- char line[LINE_MAX], *l, *v;
+ _cleanup_free_ char *line = NULL;
CGroupIPAccountingMetric m;
+ char *l, *v;
size_t k;
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- return 0;
- return -errno;
- }
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read serialization line: %m");
+ if (r == 0) /* eof */
+ break;
- char_array_0(line);
l = strstrip(line);
-
- /* End marker */
- if (isempty(l))
+ if (isempty(l)) /* End marker */
break;
k = strcspn(l, "=");
@@ -3395,54 +3347,33 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
if (streq(l, "job")) {
if (v[0] == '\0') {
- /* new-style serialized job */
- Job *j;
-
- j = job_new_raw(u);
- if (!j)
- return log_oom();
-
- r = job_deserialize(j, f);
- if (r < 0) {
- job_free(j);
- return r;
- }
-
- r = hashmap_put(u->manager->jobs, UINT32_TO_PTR(j->id), j);
- if (r < 0) {
- job_free(j);
- return r;
- }
-
- r = job_install_deserialized(j);
- if (r < 0) {
- hashmap_remove(u->manager->jobs, UINT32_TO_PTR(j->id));
- job_free(j);
+ /* New-style serialized job */
+ r = unit_deserialize_job(u, f);
+ if (r < 0)
return r;
- }
- } else /* legacy for pre-44 */
+ } else /* Legacy for pre-44 */
log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v);
continue;
} else if (streq(l, "state-change-timestamp")) {
- dual_timestamp_deserialize(v, &u->state_change_timestamp);
+ (void) deserialize_dual_timestamp(v, &u->state_change_timestamp);
continue;
} else if (streq(l, "inactive-exit-timestamp")) {
- dual_timestamp_deserialize(v, &u->inactive_exit_timestamp);
+ (void) deserialize_dual_timestamp(v, &u->inactive_exit_timestamp);
continue;
} else if (streq(l, "active-enter-timestamp")) {
- dual_timestamp_deserialize(v, &u->active_enter_timestamp);
+ (void) deserialize_dual_timestamp(v, &u->active_enter_timestamp);
continue;
} else if (streq(l, "active-exit-timestamp")) {
- dual_timestamp_deserialize(v, &u->active_exit_timestamp);
+ (void) deserialize_dual_timestamp(v, &u->active_exit_timestamp);
continue;
} else if (streq(l, "inactive-enter-timestamp")) {
- dual_timestamp_deserialize(v, &u->inactive_enter_timestamp);
+ (void) deserialize_dual_timestamp(v, &u->inactive_enter_timestamp);
continue;
} else if (streq(l, "condition-timestamp")) {
- dual_timestamp_deserialize(v, &u->condition_timestamp);
+ (void) deserialize_dual_timestamp(v, &u->condition_timestamp);
continue;
} else if (streq(l, "assert-timestamp")) {
- dual_timestamp_deserialize(v, &u->assert_timestamp);
+ (void) deserialize_dual_timestamp(v, &u->assert_timestamp);
continue;
} else if (streq(l, "condition-result")) {
@@ -3474,6 +3405,16 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
continue;
+ } else if (streq(l, "in-audit")) {
+
+ r = parse_boolean(v);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse in-audit bool %s, ignoring.", v);
+ else
+ u->in_audit = r;
+
+ continue;
+
} else if (streq(l, "exported-invocation-id")) {
r = parse_boolean(v);
@@ -3504,6 +3445,26 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
continue;
+ } else if (streq(l, "exported-log-rate-limit-interval")) {
+
+ r = parse_boolean(v);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse exported log rate limit interval %s, ignoring.", v);
+ else
+ u->exported_log_rate_limit_interval = r;
+
+ continue;
+
+ } else if (streq(l, "exported-log-rate-limit-burst")) {
+
+ r = parse_boolean(v);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse exported log rate limit burst %s, ignoring.", v);
+ else
+ u->exported_log_rate_limit_burst = r;
+
+ continue;
+
} else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) {
r = safe_atou64(v, &u->cpu_usage_base);
@@ -3554,18 +3515,11 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
log_unit_debug(u, "Failed to parse cgroup-enabled-mask %s, ignoring.", v);
continue;
- } else if (streq(l, "cgroup-bpf-realized")) {
- int i;
+ } else if (streq(l, "cgroup-invalidated-mask")) {
- r = safe_atoi(v, &i);
+ r = cg_mask_from_string(v, &u->cgroup_invalidated_mask);
if (r < 0)
- log_unit_debug(u, "Failed to parse cgroup BPF state %s, ignoring.", v);
- else
- u->cgroup_bpf_state =
- i < 0 ? UNIT_CGROUP_BPF_INVALIDATED :
- i > 0 ? UNIT_CGROUP_BPF_ON :
- UNIT_CGROUP_BPF_OFF;
-
+ log_unit_debug(u, "Failed to parse cgroup-invalidated-mask %s, ignoring.", v);
continue;
} else if (streq(l, "ref-uid")) {
@@ -3588,11 +3542,13 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
else
unit_ref_uid_gid(u, UID_INVALID, gid);
+ continue;
+
} else if (streq(l, "ref")) {
r = strv_extend(&u->deserialized_refs, v);
if (r < 0)
- log_oom();
+ return log_oom();
continue;
} else if (streq(l, "invocation-id")) {
@@ -3657,23 +3613,27 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
return 0;
}
-void unit_deserialize_skip(FILE *f) {
+int unit_deserialize_skip(FILE *f) {
+ int r;
assert(f);
/* Skip serialized data for this unit. We don't know what it is. */
for (;;) {
- char line[LINE_MAX], *l;
+ _cleanup_free_ char *line = NULL;
+ char *l;
- if (!fgets(line, sizeof line, f))
- return;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read serialization line: %m");
+ if (r == 0)
+ return 0;
- char_array_0(line);
l = strstrip(line);
/* End marker */
if (isempty(l))
- return;
+ return 1;
}
}
@@ -4143,12 +4103,28 @@ int unit_patch_contexts(Unit *u) {
}
cc = unit_get_cgroup_context(u);
- if (cc) {
+ if (cc && ec) {
- if (ec &&
- ec->private_devices &&
+ if (ec->private_devices &&
cc->device_policy == CGROUP_AUTO)
cc->device_policy = CGROUP_CLOSED;
+
+ if (ec->root_image &&
+ (cc->device_policy != CGROUP_AUTO || cc->device_allow)) {
+
+ /* When RootImage= is specified, the following devices are touched. */
+ r = cgroup_add_device_allow(cc, "/dev/loop-control", "rw");
+ if (r < 0)
+ return r;
+
+ r = cgroup_add_device_allow(cc, "block-loop", "rwm");
+ if (r < 0)
+ return r;
+
+ r = cgroup_add_device_allow(cc, "block-blkext", "rwm");
+ if (r < 0)
+ return r;
+ }
}
return 0;
@@ -4479,10 +4455,10 @@ static int operation_to_signal(KillContext *c, KillOperation k) {
return c->kill_signal;
case KILL_KILL:
- return SIGKILL;
+ return c->final_kill_signal;
- case KILL_ABORT:
- return SIGABRT;
+ case KILL_WATCHDOG:
+ return c->watchdog_signal;
default:
assert_not_reached("KillOperation unknown");
@@ -4609,7 +4585,6 @@ int unit_kill_context(
int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) {
_cleanup_free_ char *p = NULL;
- char *prefix;
UnitDependencyInfo di;
int r;
@@ -4649,7 +4624,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
return r;
p = NULL;
- prefix = alloca(strlen(path) + 1);
+ char prefix[strlen(path) + 1];
PATH_FOREACH_PREFIX_MORE(prefix, path) {
Set *x;
@@ -4766,7 +4741,7 @@ void unit_warn_if_dir_nonempty(Unit *u, const char* where) {
}
int unit_fail_if_noncanonical(Unit *u, const char* where) {
- _cleanup_free_ char *canonical_where;
+ _cleanup_free_ char *canonical_where = NULL;
int r;
assert(u);
@@ -4966,7 +4941,7 @@ void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) {
r = unit_ref_uid_gid(u, uid, gid);
if (r > 0)
- bus_unit_send_change_signal(u);
+ unit_add_to_dbus_queue(u);
}
int unit_set_invocation_id(Unit *u, sd_id128_t id) {
@@ -5020,15 +4995,21 @@ int unit_acquire_invocation_id(Unit *u) {
if (r < 0)
return log_unit_error_errno(u, r, "Failed to set invocation ID for unit: %m");
+ unit_add_to_dbus_queue(u);
return 0;
}
-void unit_set_exec_params(Unit *u, ExecParameters *p) {
+int unit_set_exec_params(Unit *u, ExecParameters *p) {
+ int r;
+
assert(u);
assert(p);
/* Copy parameters from manager */
- p->environment = u->manager->environment;
+ r = manager_get_effective_environment(u->manager, &p->environment);
+ if (r < 0)
+ return r;
+
p->confirm_spawn = manager_get_confirm_spawn(u->manager);
p->cgroup_supported = u->manager->cgroup_supported;
p->prefix = u->manager->prefix;
@@ -5037,6 +5018,8 @@ void unit_set_exec_params(Unit *u, ExecParameters *p) {
/* Copy paramaters from unit */
p->cgroup_path = u->cgroup_path;
SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, unit_cgroup_delegate(u));
+
+ return 0;
}
int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret) {
@@ -5242,6 +5225,60 @@ fail:
return r;
}
+static int unit_export_log_rate_limit_interval(Unit *u, const ExecContext *c) {
+ _cleanup_free_ char *buf = NULL;
+ const char *p;
+ int r;
+
+ assert(u);
+ assert(c);
+
+ if (u->exported_log_rate_limit_interval)
+ return 0;
+
+ if (c->log_rate_limit_interval_usec == 0)
+ return 0;
+
+ p = strjoina("/run/systemd/units/log-rate-limit-interval:", u->id);
+
+ if (asprintf(&buf, "%" PRIu64, c->log_rate_limit_interval_usec) < 0)
+ return log_oom();
+
+ r = symlink_atomic(buf, p);
+ if (r < 0)
+ return log_unit_debug_errno(u, r, "Failed to create log rate limit interval symlink %s: %m", p);
+
+ u->exported_log_rate_limit_interval = true;
+ return 0;
+}
+
+static int unit_export_log_rate_limit_burst(Unit *u, const ExecContext *c) {
+ _cleanup_free_ char *buf = NULL;
+ const char *p;
+ int r;
+
+ assert(u);
+ assert(c);
+
+ if (u->exported_log_rate_limit_burst)
+ return 0;
+
+ if (c->log_rate_limit_burst == 0)
+ return 0;
+
+ p = strjoina("/run/systemd/units/log-rate-limit-burst:", u->id);
+
+ if (asprintf(&buf, "%u", c->log_rate_limit_burst) < 0)
+ return log_oom();
+
+ r = symlink_atomic(buf, p);
+ if (r < 0)
+ return log_unit_debug_errno(u, r, "Failed to create log rate limit burst symlink %s: %m", p);
+
+ u->exported_log_rate_limit_burst = true;
+ return 0;
+}
+
void unit_export_state_files(Unit *u) {
const ExecContext *c;
@@ -5253,7 +5290,7 @@ void unit_export_state_files(Unit *u) {
if (!MANAGER_IS_SYSTEM(u->manager))
return;
- if (u->manager->test_run_flags != 0)
+ if (MANAGER_IS_TEST_RUN(u->manager))
return;
/* Exports a couple of unit properties to /run/systemd/units/, so that journald can quickly query this data
@@ -5275,6 +5312,8 @@ void unit_export_state_files(Unit *u) {
if (c) {
(void) unit_export_log_level_max(u, c);
(void) unit_export_log_extra_fields(u, c);
+ (void) unit_export_log_rate_limit_interval(u, c);
+ (void) unit_export_log_rate_limit_burst(u, c);
}
}
@@ -5311,6 +5350,20 @@ void unit_unlink_state_files(Unit *u) {
u->exported_log_extra_fields = false;
}
+
+ if (u->exported_log_rate_limit_interval) {
+ p = strjoina("/run/systemd/units/log-rate-limit-interval:", u->id);
+ (void) unlink(p);
+
+ u->exported_log_rate_limit_interval = false;
+ }
+
+ if (u->exported_log_rate_limit_burst) {
+ p = strjoina("/run/systemd/units/log-rate-limit-burst:", u->id);
+ (void) unlink(p);
+
+ u->exported_log_rate_limit_burst = false;
+ }
}
int unit_prepare_exec(Unit *u) {
@@ -5433,6 +5486,105 @@ int unit_pid_attachable(Unit *u, pid_t pid, sd_bus_error *error) {
return 0;
}
+void unit_log_success(Unit *u) {
+ assert(u);
+
+ log_struct(LOG_INFO,
+ "MESSAGE_ID=" SD_MESSAGE_UNIT_SUCCESS_STR,
+ LOG_UNIT_ID(u),
+ LOG_UNIT_INVOCATION_ID(u),
+ LOG_UNIT_MESSAGE(u, "Succeeded."));
+}
+
+void unit_log_failure(Unit *u, const char *result) {
+ assert(u);
+ assert(result);
+
+ log_struct(LOG_WARNING,
+ "MESSAGE_ID=" SD_MESSAGE_UNIT_FAILURE_RESULT_STR,
+ LOG_UNIT_ID(u),
+ LOG_UNIT_INVOCATION_ID(u),
+ LOG_UNIT_MESSAGE(u, "Failed with result '%s'.", result),
+ "UNIT_RESULT=%s", result);
+}
+
+void unit_log_process_exit(
+ Unit *u,
+ int level,
+ const char *kind,
+ const char *command,
+ int code,
+ int status) {
+
+ assert(u);
+ assert(kind);
+
+ if (code != CLD_EXITED)
+ level = LOG_WARNING;
+
+ log_struct(level,
+ "MESSAGE_ID=" SD_MESSAGE_UNIT_PROCESS_EXIT_STR,
+ LOG_UNIT_MESSAGE(u, "%s exited, code=%s, status=%i/%s",
+ kind,
+ sigchld_code_to_string(code), status,
+ strna(code == CLD_EXITED
+ ? exit_status_to_string(status, EXIT_STATUS_FULL)
+ : signal_to_string(status))),
+ "EXIT_CODE=%s", sigchld_code_to_string(code),
+ "EXIT_STATUS=%i", status,
+ "COMMAND=%s", strna(command),
+ LOG_UNIT_ID(u),
+ LOG_UNIT_INVOCATION_ID(u));
+}
+
+int unit_exit_status(Unit *u) {
+ assert(u);
+
+ /* Returns the exit status to propagate for the most recent cycle of this unit. Returns a value in the range
+ * 0…255 if there's something to propagate. EOPNOTSUPP if the concept does not apply to this unit type, ENODATA
+ * if no data is currently known (for example because the unit hasn't deactivated yet) and EBADE if the main
+ * service process has exited abnormally (signal/coredump). */
+
+ if (!UNIT_VTABLE(u)->exit_status)
+ return -EOPNOTSUPP;
+
+ return UNIT_VTABLE(u)->exit_status(u);
+}
+
+int unit_failure_action_exit_status(Unit *u) {
+ int r;
+
+ assert(u);
+
+ /* Returns the exit status to propagate on failure, or an error if there's nothing to propagate */
+
+ if (u->failure_action_exit_status >= 0)
+ return u->failure_action_exit_status;
+
+ r = unit_exit_status(u);
+ if (r == -EBADE) /* Exited, but not cleanly (i.e. by signal or such) */
+ return 255;
+
+ return r;
+}
+
+int unit_success_action_exit_status(Unit *u) {
+ int r;
+
+ assert(u);
+
+ /* Returns the exit status to propagate on success, or an error if there's nothing to propagate */
+
+ if (u->success_action_exit_status >= 0)
+ return u->success_action_exit_status;
+
+ r = unit_exit_status(u);
+ if (r == -EBADE) /* Exited, but not cleanly (i.e. by signal or such) */
+ return 255;
+
+ return r;
+}
+
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
[COLLECT_INACTIVE] = "inactive",
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
diff --git a/src/core/unit.h b/src/core/unit.h
index b3131eba1b..6fd39eaca3 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -19,7 +19,7 @@ typedef enum KillOperation {
KILL_TERMINATE,
KILL_TERMINATE_AND_LOG,
KILL_KILL,
- KILL_ABORT,
+ KILL_WATCHDOG,
_KILL_OPERATION_MAX,
_KILL_OPERATION_INVALID = -1
} KillOperation;
@@ -105,12 +105,6 @@ struct UnitRef {
LIST_FIELDS(UnitRef, refs_by_target);
};
-typedef enum UnitCGroupBPFState {
- UNIT_CGROUP_BPF_OFF = 0,
- UNIT_CGROUP_BPF_ON = 1,
- UNIT_CGROUP_BPF_INVALIDATED = -1,
-} UnitCGroupBPFState;
-
typedef struct Unit {
Manager *manager;
@@ -188,9 +182,6 @@ typedef struct Unit {
/* Per type list */
LIST_FIELDS(Unit, units_by_type);
- /* All units which have requires_mounts_for set */
- LIST_FIELDS(Unit, has_requires_mounts_for);
-
/* Load queue */
LIST_FIELDS(Unit, load_queue);
@@ -212,6 +203,9 @@ typedef struct Unit {
/* Target dependencies queue */
LIST_FIELDS(Unit, target_deps_queue);
+ /* Queue of units with StopWhenUnneeded set that shell be checked for clean-up. */
+ LIST_FIELDS(Unit, stop_when_unneeded_queue);
+
/* PIDs we keep an eye on. Note that a unit might have many
* more, but these are the ones we care enough about to
* process SIGCHLD for */
@@ -232,8 +226,9 @@ typedef struct Unit {
RateLimit start_limit;
EmergencyAction start_limit_action;
- EmergencyAction failure_action;
- EmergencyAction success_action;
+ /* What to do on failure or success */
+ EmergencyAction success_action, failure_action;
+ int success_action_exit_status, failure_action_exit_status;
char *reboot_arg;
/* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
@@ -253,12 +248,15 @@ typedef struct Unit {
/* Counterparts in the cgroup filesystem */
char *cgroup_path;
- CGroupMask cgroup_realized_mask;
- CGroupMask cgroup_enabled_mask;
- CGroupMask cgroup_subtree_mask;
- CGroupMask cgroup_members_mask;
+ CGroupMask cgroup_realized_mask; /* In which hierarchies does this unit's cgroup exist? (only relevant on cgroupsv1) */
+ CGroupMask cgroup_enabled_mask; /* Which controllers are enabled (or more correctly: enabled for the children) for this unit's cgroup? (only relevant on cgroupsv2) */
+ CGroupMask cgroup_invalidated_mask; /* A mask specifiying controllers which shall be considered invalidated, and require re-realization */
+ CGroupMask cgroup_members_mask; /* A cache for the controllers required by all children of this cgroup (only relevant for slice units) */
int cgroup_inotify_wd;
+ /* Device Controller BPF program */
+ BPFProgram *bpf_device_control_installed;
+
/* IP BPF Firewalling/accounting */
int ip_accounting_ingress_map_fd;
int ip_accounting_egress_map_fd;
@@ -315,6 +313,7 @@ typedef struct Unit {
/* Is this a unit that is always running and cannot be stopped? */
bool perpetual;
+ /* Booleans indicating membership of this unit in the various queues */
bool in_load_queue:1;
bool in_dbus_queue:1;
bool in_cleanup_queue:1;
@@ -322,6 +321,7 @@ typedef struct Unit {
bool in_cgroup_realize_queue:1;
bool in_cgroup_empty_queue:1;
bool in_target_deps_queue:1;
+ bool in_stop_when_unneeded_queue:1;
bool sent_dbus_new_signal:1;
@@ -330,9 +330,6 @@ typedef struct Unit {
bool cgroup_realized:1;
bool cgroup_members_mask_valid:1;
- bool cgroup_subtree_mask_valid:1;
-
- UnitCGroupBPFState cgroup_bpf_state:2;
/* Reset cgroup accounting next time we fork something off */
bool reset_accounting:1;
@@ -349,10 +346,12 @@ typedef struct Unit {
bool exported_invocation_id:1;
bool exported_log_level_max:1;
bool exported_log_extra_fields:1;
+ bool exported_log_rate_limit_interval:1;
+ bool exported_log_rate_limit_burst:1;
/* When writing transient unit files, stores which section we stored last. If < 0, we didn't write any yet. If
* == 0 we are in the [Unit] section, if > 0 we are in the unit type-specific section. */
- int last_section_private:2;
+ signed int last_section_private:2;
} Unit;
typedef struct UnitStatusMessageFormats {
@@ -380,7 +379,9 @@ typedef enum UnitWriteFlags {
} UnitWriteFlags;
/* Returns true if neither persistent, nor runtime storage is requested, i.e. this is a check invocation only */
-#define UNIT_WRITE_FLAGS_NOOP(flags) (((flags) & (UNIT_RUNTIME|UNIT_PERSISTENT)) == 0)
+static inline bool UNIT_WRITE_FLAGS_NOOP(UnitWriteFlags flags) {
+ return (flags & (UNIT_RUNTIME|UNIT_PERSISTENT)) == 0;
+}
#include "kill.h"
@@ -432,11 +433,16 @@ typedef struct UnitVTable {
int (*load)(Unit *u);
/* During deserialization we only record the intended state to return to. With coldplug() we actually put the
- * deserialized state in effect. This is where unit_notify() should be called to start things up. */
+ * deserialized state in effect. This is where unit_notify() should be called to start things up. Note that
+ * this callback is invoked *before* we leave the reloading state of the manager, i.e. *before* we consider the
+ * reloading to be complete. Thus, this callback should just restore the exact same state for any unit that was
+ * in effect before the reload, i.e. units should not catch up with changes happened during the reload. That's
+ * what catchup() below is for. */
int (*coldplug)(Unit *u);
- /* This is called shortly after all units' coldplug() call was invoked. It's supposed to catch up state changes
- * we missed so far (for example because they took place while we were reloading/reexecing) */
+ /* This is called shortly after all units' coldplug() call was invoked, and *after* the manager left the
+ * reloading state. It's supposed to catch up with state changes due to external events we missed so far (for
+ * example because they took place while we were reloading/reexecing) */
void (*catchup)(Unit *u);
void (*dump)(Unit *u, FILE *f, const char *prefix);
@@ -529,6 +535,10 @@ typedef struct UnitVTable {
/* Returns true if the unit currently needs access to the console */
bool (*needs_console)(Unit *u);
+ /* Returns the exit status to propagate in case of FailureAction=exit/SuccessAction=exit; usually returns the
+ * exit code of the "main" process of the service or similar. */
+ int (*exit_status)(Unit *u);
+
/* Like the enumerate() callback further down, but only enumerates the perpetual units, i.e. all units that
* unconditionally exist and are always active. The main reason to keep both enumeration functions separate is
* philosophical: the state of perpetual units should be put in place by coldplug(), while the state of those
@@ -568,7 +578,9 @@ typedef struct UnitVTable {
extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
-#define UNIT_VTABLE(u) unit_vtable[(u)->type]
+static inline const UnitVTable* UNIT_VTABLE(Unit *u) {
+ return unit_vtable[u->type];
+}
/* For casting a unit into the various unit types */
#define DEFINE_CAST(UPPERCASE, MixedCase) \
@@ -580,13 +592,20 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
}
/* For casting the various unit types into a unit */
-#define UNIT(u) (&(u)->meta)
+#define UNIT(u) \
+ ({ \
+ typeof(u) _u_ = (u); \
+ Unit *_w_ = _u_ ? &(_u_)->meta : NULL; \
+ _w_; \
+ })
#define UNIT_HAS_EXEC_CONTEXT(u) (UNIT_VTABLE(u)->exec_context_offset > 0)
#define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0)
#define UNIT_HAS_KILL_CONTEXT(u) (UNIT_VTABLE(u)->kill_context_offset > 0)
-#define UNIT_TRIGGER(u) ((Unit*) hashmap_first_key((u)->dependencies[UNIT_TRIGGERS]))
+static inline Unit* UNIT_TRIGGER(Unit *u) {
+ return hashmap_first_key(u->dependencies[UNIT_TRIGGERS]);
+}
Unit *unit_new(Manager *m, size_t size);
void unit_free(Unit *u);
@@ -598,8 +617,8 @@ int unit_add_name(Unit *u, const char *name);
int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference, UnitDependencyMask mask);
int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference, UnitDependencyMask mask);
-int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference, UnitDependencyMask mask);
-int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference, UnitDependencyMask mask);
+int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, bool add_reference, UnitDependencyMask mask);
+int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, bool add_reference, UnitDependencyMask mask);
int unit_add_exec_dependencies(Unit *u, ExecContext *c);
@@ -613,6 +632,7 @@ void unit_add_to_dbus_queue(Unit *u);
void unit_add_to_cleanup_queue(Unit *u);
void unit_add_to_gc_queue(Unit *u);
void unit_add_to_target_deps_queue(Unit *u);
+void unit_submit_to_stop_when_unneeded_queue(Unit *u);
int unit_merge(Unit *u, Unit *other);
int unit_merge_by_name(Unit *u, const char *other);
@@ -628,7 +648,7 @@ int unit_set_default_slice(Unit *u);
const char *unit_description(Unit *u) _pure_;
-bool unit_has_name(Unit *u, const char *name);
+bool unit_has_name(const Unit *u, const char *name);
UnitActiveState unit_active_state(Unit *u);
@@ -679,12 +699,7 @@ bool unit_can_serialize(Unit *u) _pure_;
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs);
int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
-void unit_deserialize_skip(FILE *f);
-
-int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
-int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value);
-int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd);
-void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_(4,5);
+int unit_deserialize_skip(FILE *f);
int unit_add_node_dependency(Unit *u, const char *what, bool wants, UnitDependency d, UnitDependencyMask mask);
@@ -692,7 +707,6 @@ 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_emit_starting_stopping_reloading(Unit *u, JobType t);
bool unit_need_daemon_reload(Unit *u);
@@ -749,6 +763,8 @@ bool unit_type_supported(UnitType t);
bool unit_is_pristine(Unit *u);
+bool unit_is_unneeded(Unit *u);
+
pid_t unit_control_pid(Unit *u);
pid_t unit_main_pid(Unit *u);
@@ -777,7 +793,7 @@ int unit_acquire_invocation_id(Unit *u);
bool unit_shall_confirm_spawn(Unit *u);
-void unit_set_exec_params(Unit *s, ExecParameters *p);
+int unit_set_exec_params(Unit *s, ExecParameters *p);
int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret);
@@ -796,6 +812,21 @@ const char *unit_label_path(Unit *u);
int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error);
+void unit_log_success(Unit *u);
+void unit_log_failure(Unit *u, const char *result);
+static inline void unit_log_result(Unit *u, bool success, const char *result) {
+ if (success)
+ unit_log_success(u);
+ else
+ unit_log_failure(u, result);
+}
+
+void unit_log_process_exit(Unit *u, int level, const char *kind, const char *command, int code, int status);
+
+int unit_exit_status(Unit *u);
+int unit_success_action_exit_status(Unit *u);
+int unit_failure_action_exit_status(Unit *u);
+
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index 20a1cbdd45..0c888b26f9 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -34,6 +34,7 @@
#include "journal-importer.h"
#include "log.h"
#include "macro.h"
+#include "main-func.h"
#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
@@ -45,6 +46,7 @@
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
@@ -55,10 +57,15 @@
#define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
/* The maximum size up to which we store the coredump in the journal */
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
+#else
+/* oss-fuzz limits memory usage. */
+#define JOURNAL_SIZE_MAX ((size_t) (10LU*1024LU*1024LU))
+#endif
/* Make sure to not make this larger than the maximum journal entry
- * size. See DATA_SIZE_MAX in journald-native.c. */
+ * size. See DATA_SIZE_MAX in journal-importer.h. */
assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
enum {
@@ -340,21 +347,20 @@ static int save_external_coredump(
r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit);
if (r < 0)
- return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]);
+ return log_error_errno(r, "Failed to parse resource limit '%s': %m", context[CONTEXT_RLIMIT]);
if (rlimit < page_size()) {
/* Is coredumping disabled? Then don't bother saving/processing the coredump.
* Anything below PAGE_SIZE cannot give a readable coredump (the kernel uses
* ELF_EXEC_PAGESIZE which is not easily accessible, but is usually the same as PAGE_SIZE. */
- log_info("Resource limits disable core dumping for process %s (%s).",
- context[CONTEXT_PID], context[CONTEXT_COMM]);
- return -EBADSLT;
+ return log_info_errno(SYNTHETIC_ERRNO(EBADSLT),
+ "Resource limits disable core dumping for process %s (%s).",
+ context[CONTEXT_PID], context[CONTEXT_COMM]);
}
process_limit = MAX(arg_process_size_max, storage_size_max());
- if (process_limit == 0) {
- log_debug("Limits for coredump processing and storage are both 0, not dumping core.");
- return -EBADSLT;
- }
+ if (process_limit == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADSLT),
+ "Limits for coredump processing and storage are both 0, not dumping core.");
/* Never store more than the process configured, or than we actually shall keep or process */
max_size = MIN(rlimit, process_limit);
@@ -478,10 +484,9 @@ static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_s
n = read(fd, field + 9, size);
if (n < 0)
return log_error_errno((int) n, "Failed to read core data: %m");
- if ((size_t) n < size) {
- log_error("Core data too short.");
- return -EIO;
- }
+ if ((size_t) n < size)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Core data too short.");
*ret = TAKE_PTR(field);
*ret_size = size + 9;
@@ -511,7 +516,7 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
const char *fddelim = "", *path;
struct dirent *dent = NULL;
size_t size = 0;
- int r = 0;
+ int r;
assert(pid >= 0);
assert(open_fds != NULL);
@@ -534,7 +539,6 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
FOREACH_DIRENT(dent, proc_fd_dir, return -errno) {
_cleanup_fclose_ FILE *fdinfo = NULL;
_cleanup_free_ char *fdname = NULL;
- char line[LINE_MAX];
int fd;
r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname);
@@ -549,16 +553,23 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
if (fd < 0)
continue;
- fdinfo = fdopen(fd, "re");
+ fdinfo = fdopen(fd, "r");
if (!fdinfo) {
safe_close(fd);
continue;
}
- FOREACH_LINE(line, fdinfo, break) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+
+ r = read_line(fdinfo, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
fputs(line, stream);
- if (!endswith(line, "\n"))
- fputc('\n', stream);
+ fputc('\n', stream);
}
}
@@ -672,7 +683,7 @@ static int change_uid_gid(const char *context[]) {
if (uid <= SYSTEM_UID_MAX) {
const char *user = "systemd-coredump";
- r = get_user_creds(&user, &uid, &gid, NULL, NULL);
+ r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
if (r < 0) {
log_warning_errno(r, "Cannot resolve %s user. Proceeding to dump core as root: %m", user);
uid = gid = 0;
@@ -871,9 +882,7 @@ static int process_socket(int fd) {
assert(fd >= 0);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
log_debug("Processing coredump received on stdin...");
@@ -1226,10 +1235,10 @@ static int process_kernel(int argc, char* argv[]) {
log_debug("Processing coredump received from the kernel...");
- if (argc < CONTEXT_COMM + 1) {
- log_error("Not enough arguments passed by the kernel (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1);
- return -EINVAL;
- }
+ if (argc < CONTEXT_COMM + 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not enough arguments passed by the kernel (%i, expected %i).",
+ argc - 1, CONTEXT_COMM + 1 - 1);
context[CONTEXT_PID] = argv[1 + CONTEXT_PID];
context[CONTEXT_UID] = argv[1 + CONTEXT_UID];
@@ -1283,10 +1292,10 @@ static int process_backtrace(int argc, char *argv[]) {
log_debug("Processing backtrace on stdin...");
- if (argc < CONTEXT_COMM + 1) {
- log_error("Not enough arguments passed (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1);
- return -EINVAL;
- }
+ if (argc < CONTEXT_COMM + 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not enough arguments passed (%i, expected %i).",
+ argc - 1, CONTEXT_COMM + 1 - 1);
context[CONTEXT_PID] = argv[2 + CONTEXT_PID];
context[CONTEXT_UID] = argv[2 + CONTEXT_UID];
@@ -1365,7 +1374,7 @@ static int process_backtrace(int argc, char *argv[]) {
return r;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
/* First, log to a safe place, since we don't know what crashed and it might
@@ -1384,25 +1393,21 @@ int main(int argc, char *argv[]) {
log_debug("Selected compression %s.", yes_no(arg_compress));
r = sd_listen_fds(false);
- if (r < 0) {
- log_error_errno(r, "Failed to determine number of file descriptor: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine the number of file descriptors: %m");
/* If we got an fd passed, we are running in coredumpd mode. Otherwise we
* are invoked from the kernel as coredump handler. */
if (r == 0) {
if (streq_ptr(argv[1], "--backtrace"))
- r = process_backtrace(argc, argv);
+ return process_backtrace(argc, argv);
else
- r = process_kernel(argc, argv);
+ return process_kernel(argc, argv);
} else if (r == 1)
- r = process_socket(SD_LISTEN_FDS_START);
- else {
- log_error("Received unexpected number of file descriptors.");
- r = -EINVAL;
- }
+ return process_socket(SD_LISTEN_FDS_START);
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Received unexpected number of file descriptors.");
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index 99d07c14fb..fbee242962 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -15,22 +15,26 @@
#include "bus-error.h"
#include "bus-util.h"
#include "compress.h"
+#include "def.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "journal-internal.h"
#include "journal-util.h"
#include "log.h"
#include "macro.h"
+#include "main-func.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "sigbus.h"
#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
#include "verbs.h"
@@ -41,10 +45,10 @@ static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY;
static const char* arg_field = NULL;
static const char *arg_debugger = NULL;
static const char *arg_directory = NULL;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static int arg_no_legend = false;
static int arg_one = false;
-static FILE* arg_output = NULL;
+static const char* arg_output = NULL;
static bool arg_reverse = false;
static bool arg_quiet = false;
@@ -135,6 +139,13 @@ static int acquire_journal(sd_journal **ret, char **matches) {
}
static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("coredumpctl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"List or retrieve coredumps from the journal.\n\n"
"Flags:\n"
@@ -156,7 +167,10 @@ static int help(void) {
" info [MATCHES...] Show detailed information about one or more coredumps\n"
" dump [MATCHES...] Print first matching coredump to stdout\n"
" debug [MATCHES...] Start a debugger for the first matching coredump\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
@@ -199,7 +213,7 @@ static int parse_argv(int argc, char *argv[]) {
return version();
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
@@ -211,34 +225,29 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'o':
- if (arg_output) {
- log_error("Cannot set output more than once.");
- return -EINVAL;
- }
-
- arg_output = fopen(optarg, "we");
- if (!arg_output)
- return log_error_errno(errno, "writing to '%s': %m", optarg);
+ if (arg_output)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot set output more than once.");
+ arg_output = optarg;
break;
case 'S':
r = parse_timestamp(optarg, &arg_since);
if (r < 0)
- return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
+ return log_error_errno(r, "Failed to parse timestamp '%s': %m", optarg);
break;
case 'U':
r = parse_timestamp(optarg, &arg_until);
if (r < 0)
- return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
+ return log_error_errno(r, "Failed to parse timestamp '%s': %m", optarg);
break;
case 'F':
- if (arg_field) {
- log_error("Cannot use --field/-F more than once.");
- return -EINVAL;
- }
+ if (arg_field)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot use --field/-F more than once.");
arg_field = optarg;
break;
@@ -266,10 +275,9 @@ static int parse_argv(int argc, char *argv[]) {
}
if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
- arg_since > arg_until) {
- log_error("--since= must be before --until=.");
- return -EINVAL;
- }
+ arg_since > arg_until)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--since= must be before --until=.");
return 1;
}
@@ -617,10 +625,9 @@ static int focus(sd_journal *j) {
r = sd_journal_previous(j);
if (r < 0)
return log_error_errno(r, "Failed to search journal: %m");
- if (r == 0) {
- log_error("No match found.");
- return -ESRCH;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+ "No match found.");
return r;
}
@@ -647,14 +654,15 @@ static int dump_list(int argc, char **argv, void *userdata) {
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
/* The coredumps are likely to compressed, and for just
* listing them we don't need to decompress them, so let's
* pick a fairly low data threshold here */
sd_journal_set_data_threshold(j, 4096);
- if (arg_one) {
+ /* "info" without pattern implies "-1" */
+ if (arg_one || (verb_is_info && argc == 1)) {
r = focus(j);
if (r < 0)
return r;
@@ -788,7 +796,8 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp)
*/
if (!file) {
if (on_tty())
- return log_error_errno(ENOTTY, "Refusing to dump core to tty"
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
+ "Refusing to dump core to tty"
" (use shell redirection or specify --output).");
file = stdout;
}
@@ -858,6 +867,7 @@ error:
static int dump_core(int argc, char **argv, void *userdata) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
int r;
if (arg_field) {
@@ -873,9 +883,15 @@ static int dump_core(int argc, char **argv, void *userdata) {
if (r < 0)
return r;
- print_info(arg_output ? stdout : stderr, j, false);
+ if (arg_output) {
+ f = fopen(arg_output, "we");
+ if (!f)
+ return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", arg_output);
+ }
- r = save_core(j, arg_output, NULL, NULL);
+ print_info(f ? stdout : stderr, j, false);
+
+ r = save_core(j, f, NULL, NULL);
if (r < 0)
return r;
@@ -952,7 +968,7 @@ static int run_debug(int argc, char **argv, void *userdata) {
fork_name = strjoina("(", arg_debugger, ")");
- r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
goto finish;
if (r == 0) {
@@ -976,7 +992,7 @@ finish:
}
static int check_units_active(void) {
- _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
@@ -1049,16 +1065,19 @@ static int coredumpctl_main(int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, NULL);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r, units_active;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
+ /* The journal merging logic potentially needs a lot of fds. */
+ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
r = parse_argv(argc, argv);
if (r <= 0)
- goto end;
+ return r;
sigbus_install();
@@ -1071,10 +1090,7 @@ int main(int argc, char *argv[]) {
ansi_highlight_red(),
units_active, units_active == 1 ? "unit is running" : "units are running",
ansi_normal());
-end:
- pager_close();
-
- safe_fclose(arg_output);
-
- return r >= 0 ? r : EXIT_FAILURE;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/coredump/meson.build b/src/coredump/meson.build
index bfba7ef58c..7fa5942697 100644
--- a/src/coredump/meson.build
+++ b/src/coredump/meson.build
@@ -13,8 +13,10 @@ endif
coredumpctl_sources = files('coredumpctl.c')
-install_data('coredump.conf',
- install_dir : pkgsysconfdir)
+if conf.get('ENABLE_COREDUMP') == 1
+ install_data('coredump.conf',
+ install_dir : pkgsysconfdir)
+endif
tests += [
[['src/coredump/test-coredump-vacuum.c',
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
index f5a81829b9..8759a26148 100644
--- a/src/cryptsetup/cryptsetup-generator.c
+++ b/src/cryptsetup/cryptsetup-generator.c
@@ -5,11 +5,13 @@
#include "alloc-util.h"
#include "dropin.h"
+#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "fstab-util.h"
#include "generator.h"
#include "hashmap.h"
+#include "id128-util.h"
#include "log.h"
#include "mkdir.h"
#include "parse-util.h"
@@ -24,12 +26,13 @@
typedef struct crypto_device {
char *uuid;
char *keyfile;
+ char *keydev;
char *name;
char *options;
bool create;
} crypto_device;
-static const char *arg_dest = "/tmp";
+static const char *arg_dest = NULL;
static bool arg_enabled = true;
static bool arg_read_crypttab = true;
static bool arg_whitelist = false;
@@ -37,14 +40,79 @@ static Hashmap *arg_disks = NULL;
static char *arg_default_options = NULL;
static char *arg_default_keyfile = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_disks, hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_default_options, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_default_keyfile, freep);
+
+static int generate_keydev_mount(const char *name, const char *keydev, char **unit, char **mount) {
+ _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(name);
+ assert(keydev);
+ assert(unit);
+ assert(mount);
+
+ r = mkdir_parents("/run/systemd/cryptsetup", 0755);
+ if (r < 0)
+ return r;
+
+ r = mkdir("/run/systemd/cryptsetup", 0700);
+ if (r < 0 && errno != EEXIST)
+ return -errno;
+
+ name_escaped = cescape(name);
+ if (!name_escaped)
+ return -ENOMEM;
+
+ where = strjoin("/run/systemd/cryptsetup/keydev-", name_escaped);
+ if (!where)
+ return -ENOMEM;
+
+ r = mkdir(where, 0700);
+ if (r < 0 && errno != EEXIST)
+ return -errno;
+
+ r = unit_name_from_path(where, ".mount", &u);
+ if (r < 0)
+ return r;
+
+ r = generator_open_unit_file(arg_dest, NULL, u, &f);
+ if (r < 0)
+ return r;
+
+ what = fstab_node_to_udev_node(keydev);
+ if (!what)
+ return -ENOMEM;
+
+ fprintf(f,
+ "[Unit]\n"
+ "DefaultDependencies=no\n\n"
+ "[Mount]\n"
+ "What=%s\n"
+ "Where=%s\n"
+ "Options=ro\n", what, where);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
+
+ *unit = TAKE_PTR(u);
+ *mount = TAKE_PTR(where);
+
+ return 0;
+}
+
static int create_disk(
const char *name,
const char *device,
+ const char *keydev,
const char *password,
const char *options) {
_cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
- *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL;
+ *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *keydev_mount = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *dmname;
bool noauto, nofail, tmp, swap, netdev;
@@ -59,10 +127,10 @@ static int create_disk(
swap = fstab_test_option(options, "swap\0");
netdev = fstab_test_option(options, "_netdev\0");
- if (tmp && swap) {
- log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
- return -EINVAL;
- }
+ if (tmp && swap)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.",
+ name);
name_escaped = specifier_escape(name);
if (!name_escaped)
@@ -94,6 +162,10 @@ static int create_disk(
return log_oom();
}
+ if (keydev && !password)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Key device is specified, but path to the password file is missing.");
+
r = generator_open_unit_file(arg_dest, NULL, n, &f);
if (r < 0)
return r;
@@ -109,13 +181,27 @@ static int create_disk(
"After=%s\n",
netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
+ if (keydev) {
+ _cleanup_free_ char *unit = NULL, *p = NULL;
+
+ r = generate_keydev_mount(name, keydev, &unit, &keydev_mount);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate keydev mount unit: %m");
+
+ p = prefix_root(keydev_mount, password_escaped);
+ if (!p)
+ return log_oom();
+
+ free_and_replace(password_escaped, p);
+ }
+
if (!nofail)
fprintf(f,
"Before=%s\n",
netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
if (password) {
- if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
+ if (PATH_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
fputs("After=systemd-random-seed.service\n", f);
else if (!STR_IN_SET(password, "-", "none")) {
_cleanup_free_ char *uu;
@@ -151,8 +237,13 @@ static int create_disk(
fputs("Before=dev-mapper-%i.swap\n",
f);
} 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) */
fprintf(f,
- "RequiresMountsFor=%s\n",
+ "RequiresMountsFor=%s\n"
+ "Requires=systemd-tmpfiles-setup-dev.service\n"
+ "After=systemd-tmpfiles-setup-dev.service\n",
u_escaped);
r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
@@ -186,6 +277,11 @@ static int create_disk(
"ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
name_escaped);
+ if (keydev)
+ fprintf(f,
+ "ExecStartPost=" UMOUNT_PATH " %s\n\n",
+ keydev_mount);
+
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write unit file %s: %m", n);
@@ -218,12 +314,16 @@ static int create_disk(
return 0;
}
-static void crypt_device_free(crypto_device *d) {
+static crypto_device* crypt_device_free(crypto_device *d) {
+ if (!d)
+ return NULL;
+
free(d->uuid);
free(d->keyfile);
+ free(d->keydev);
free(d->name);
free(d->options);
- free(d);
+ return mfree(d);
}
static crypto_device *get_crypto_device(const char *uuid) {
@@ -303,20 +403,52 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
return log_oom();
} else if (streq(key, "luks.key")) {
+ size_t n;
+ _cleanup_free_ char *keyfile = NULL, *keydev = NULL;
+ char *c;
+ const char *keyspec;
if (proc_cmdline_value_missing(key, value))
return 0;
- r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
- if (r == 2) {
- d = get_crypto_device(uuid);
- if (!d)
- return log_oom();
+ n = strspn(value, LETTERS DIGITS "-");
+ if (value[n] != '=') {
+ if (free_and_strdup(&arg_default_keyfile, value) < 0)
+ return log_oom();
+ return 0;
+ }
+
+ uuid = strndup(value, n);
+ if (!uuid)
+ return log_oom();
+
+ if (!id128_is_valid(uuid)) {
+ log_warning("Failed to parse luks.key= kernel command line switch. UUID is invalid, ignoring.");
+ return 0;
+ }
- free_and_replace(d->keyfile, uuid_value);
- } else if (free_and_strdup(&arg_default_keyfile, value) < 0)
+ d = get_crypto_device(uuid);
+ if (!d)
return log_oom();
+ keyspec = value + n + 1;
+ c = strrchr(keyspec, ':');
+ if (c) {
+ *c = '\0';
+ keyfile = strdup(keyspec);
+ keydev = strdup(c + 1);
+
+ if (!keyfile || !keydev)
+ return log_oom();
+ } else {
+ /* No keydev specified */
+ keyfile = strdup(keyspec);
+ if (!keyfile)
+ return log_oom();
+ }
+
+ free_and_replace(d->keyfile, keyfile);
+ free_and_replace(d->keydev, keydev);
} else if (streq(key, "luks.name")) {
if (proc_cmdline_value_missing(key, value))
@@ -339,9 +471,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
}
static int add_crypttab_devices(void) {
- struct stat st;
- unsigned crypttab_line = 0;
_cleanup_fclose_ FILE *f = NULL;
+ unsigned crypttab_line = 0;
+ struct stat st;
+ int r;
if (!arg_read_crypttab)
return 0;
@@ -361,18 +494,21 @@ static int add_crypttab_devices(void) {
}
for (;;) {
- int r, k;
- char line[LINE_MAX], *l, *uuid;
+ _cleanup_free_ char *line = NULL, *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
crypto_device *d = NULL;
- _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
+ char *l, *uuid;
+ int k;
- if (!fgets(line, sizeof(line), f))
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read /etc/crypttab: %m");
+ if (r == 0)
break;
crypttab_line++;
l = strstrip(line);
- if (IN_SET(*l, 0, '#'))
+ if (IN_SET(l[0], 0, '#'))
continue;
k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
@@ -381,11 +517,9 @@ static int add_crypttab_devices(void) {
continue;
}
- uuid = startswith(device, "UUID=");
+ uuid = STARTSWITH_SET(device, "UUID=", "luks-");
if (!uuid)
uuid = path_startswith(device, "/dev/disk/by-uuid/");
- if (!uuid)
- uuid = startswith(name, "luks-");
if (uuid)
d = hashmap_get(arg_disks, uuid);
@@ -394,7 +528,7 @@ static int add_crypttab_devices(void) {
continue;
}
- r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
+ r = create_disk(name, device, NULL, keyfile, (d && d->options) ? d->options : options);
if (r < 0)
return r;
@@ -434,7 +568,7 @@ static int add_proc_cmdline_devices(void) {
else
options = "timeout=0";
- r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
+ r = create_disk(d->name, device, d->keydev, d->keyfile ?: arg_default_keyfile, options);
if (r < 0)
return r;
}
@@ -442,55 +576,34 @@ static int add_proc_cmdline_devices(void) {
return 0;
}
-int main(int argc, char *argv[]) {
- int r;
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(crypt_device_hash_ops, char, string_hash_func, string_compare_func,
+ crypto_device, crypt_device_free);
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1)
- arg_dest = argv[1];
-
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+ int r;
- umask(0022);
+ assert_se(arg_dest = dest);
- arg_disks = hashmap_new(&string_hash_ops);
- if (!arg_disks) {
- r = log_oom();
- goto finish;
- }
+ arg_disks = hashmap_new(&crypt_device_hash_ops);
+ if (!arg_disks)
+ return log_oom();
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
- if (r < 0) {
- log_warning_errno(r, "Failed to parse kernel command line: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse kernel command line: %m");
- if (!arg_enabled) {
- r = 0;
- goto finish;
- }
+ if (!arg_enabled)
+ return 0;
r = add_crypttab_devices();
if (r < 0)
- goto finish;
+ return r;
r = add_proc_cmdline_devices();
if (r < 0)
- goto finish;
-
- r = 0;
-
-finish:
- hashmap_free_with_destructor(arg_disks, crypt_device_free);
- free(arg_default_options);
- free(arg_default_keyfile);
+ return r;
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 832168184a..daf26aad70 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -14,19 +14,27 @@
#include "escape.h"
#include "fileio.h"
#include "log.h"
+#include "main-func.h"
#include "mount-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "string-util.h"
#include "strv.h"
+#include "pretty-print.h"
#include "util.h"
/* internal helper */
#define ANY_LUKS "LUKS"
+/* as in src/cryptsetup.h */
+#define CRYPT_SECTOR_SIZE 512
+#define CRYPT_MAX_SECTOR_SIZE 4096
static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT or CRYPT_PLAIN */
static char *arg_cipher = NULL;
static unsigned arg_key_size = 0;
+#if HAVE_LIBCRYPTSETUP_SECTOR_SIZE
+static unsigned arg_sector_size = CRYPT_SECTOR_SIZE;
+#endif
static int arg_key_slot = CRYPT_ANY_SLOT;
static unsigned arg_keyfile_size = 0;
static uint64_t arg_keyfile_offset = 0;
@@ -46,6 +54,11 @@ static uint64_t arg_offset = 0;
static uint64_t arg_skip = 0;
static usec_t arg_timeout = USEC_INFINITY;
+STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_header, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tcrypt_keyfiles, strv_freep);
+
/* Options Debian's crypttab knows we don't:
precheck=
@@ -86,6 +99,29 @@ static int parse_one_option(const char *option) {
arg_key_size /= 8;
+ } else if ((val = startswith(option, "sector-size="))) {
+
+#if HAVE_LIBCRYPTSETUP_SECTOR_SIZE
+ r = safe_atou(val, &arg_sector_size);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+ return 0;
+ }
+
+ if (arg_sector_size % 2) {
+ log_error("sector-size= not a multiple of 2, ignoring.");
+ return 0;
+ }
+
+ if (arg_sector_size < CRYPT_SECTOR_SIZE || arg_sector_size > CRYPT_MAX_SECTOR_SIZE) {
+ log_error("sector-size= is outside of %u and %u, ignoring.", CRYPT_SECTOR_SIZE, CRYPT_MAX_SECTOR_SIZE);
+ return 0;
+ }
+#else
+ log_error("sector-size= is not supported, compiled with old libcryptsetup.");
+ return 0;
+#endif
+
} else if ((val = startswith(option, "key-slot="))) {
arg_type = ANY_LUKS;
@@ -137,15 +173,13 @@ static int parse_one_option(const char *option) {
} else if ((val = startswith(option, "header="))) {
arg_type = ANY_LUKS;
- if (!path_is_absolute(val)) {
- log_error("Header path \"%s\" is not absolute, refusing.", val);
- return -EINVAL;
- }
+ if (!path_is_absolute(val))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Header path \"%s\" is not absolute, refusing.", val);
- if (arg_header) {
- log_error("Duplicate header= option, refusing.");
- return -EINVAL;
- }
+ if (arg_header)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Duplicate header= option, refusing.");
arg_header = strdup(val);
if (!arg_header)
@@ -180,8 +214,8 @@ static int parse_one_option(const char *option) {
arg_type = CRYPT_TCRYPT;
arg_tcrypt_veracrypt = true;
#else
- log_error("This version of cryptsetup does not support tcrypt-veracrypt; refusing.");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "This version of cryptsetup does not support tcrypt-veracrypt; refusing.");
#endif
} else if (STR_IN_SET(option, "plain", "swap", "tmp"))
arg_type = CRYPT_PLAIN;
@@ -241,7 +275,6 @@ static int parse_options(const char *options) {
}
static char* disk_description(const char *path) {
-
static const char name_fields[] =
"ID_PART_ENTRY_NAME\0"
"DM_NAME\0"
@@ -249,9 +282,8 @@ static char* disk_description(const char *path) {
"ID_MODEL\0";
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ const char *i, *name;
struct stat st;
- const char *i;
- int r;
assert(path);
@@ -261,17 +293,13 @@ static char* disk_description(const char *path) {
if (!S_ISBLK(st.st_mode))
return NULL;
- r = sd_device_new_from_devnum(&device, 'b', st.st_rdev);
- if (r < 0)
+ if (sd_device_new_from_devnum(&device, 'b', st.st_rdev) < 0)
return NULL;
- NULSTR_FOREACH(i, name_fields) {
- const char *name;
-
- r = sd_device_get_property_value(device, i, &name);
- if (r >= 0 && !isempty(name))
+ NULSTR_FOREACH(i, name_fields)
+ if (sd_device_get_property_value(device, i, &name) >= 0 &&
+ !isempty(name))
return strdup(name);
- }
return NULL;
}
@@ -333,7 +361,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc
name = name_buffer ? name_buffer : vol;
- if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
+ if (asprintf(&text, "Please enter passphrase for disk %s:", name) < 0)
return log_oom();
id = strjoina("cryptsetup:", disk_path);
@@ -349,7 +377,7 @@ static int get_password(const char *vol, const char *src, usec_t until, bool acc
assert(strv_length(passwords) == 1);
- if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
+ if (asprintf(&text, "Please enter passphrase for disk %s (verification):", name) < 0)
return log_oom();
id = strjoina("cryptsetup-verification:", disk_path);
@@ -433,10 +461,10 @@ static int attach_tcrypt(
r = crypt_load(cd, CRYPT_TCRYPT, &params);
if (r < 0) {
- if (key_file && r == -EPERM) {
- log_error("Failed to activate using password file '%s'.", key_file);
- return -EAGAIN;
- }
+ if (key_file && r == -EPERM)
+ return log_error_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "Failed to activate using password file '%s'.",
+ key_file);
return r;
}
@@ -471,6 +499,9 @@ static int attach_luks_or_plain(struct crypt_device *cd,
struct crypt_params_plain params = {
.offset = arg_offset,
.skip = arg_skip,
+#if HAVE_LIBCRYPTSETUP_SECTOR_SIZE
+ .sector_size = arg_sector_size,
+#endif
};
const char *cipher, *cipher_mode;
_cleanup_free_ char *truncated_cipher = NULL;
@@ -549,33 +580,38 @@ static int attach_luks_or_plain(struct crypt_device *cd,
}
static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-cryptsetup@.service", "8", &link);
+ if (r < 0)
+ return log_oom();
printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
"%s detach VOLUME\n\n"
- "Attaches or detaches an encrypted block device.\n",
- program_invocation_short_name,
- program_invocation_short_name);
+ "Attaches or detaches an encrypted block device.\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
- int r = -EINVAL;
+ int r;
- if (argc <= 1) {
- r = help();
- goto finish;
- }
+ if (argc <= 1)
+ return help();
if (argc < 3) {
log_error("This program requires at least two arguments.");
- goto finish;
+ return -EINVAL;
}
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
@@ -590,7 +626,7 @@ int main(int argc, char *argv[]) {
if (argc < 4) {
log_error("attach requires at least two arguments.");
- goto finish;
+ return -EINVAL;
}
if (argc >= 5 &&
@@ -605,8 +641,9 @@ int main(int argc, char *argv[]) {
}
if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
- if (parse_options(argv[5]) < 0)
- goto finish;
+ r = parse_options(argv[5]);
+ if (r < 0)
+ return r;
}
/* A delicious drop of snake oil */
@@ -617,18 +654,15 @@ int main(int argc, char *argv[]) {
r = crypt_init(&cd, arg_header);
} else
r = crypt_init(&cd, argv[3]);
- if (r < 0) {
- log_error_errno(r, "crypt_init() failed: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "crypt_init() failed: %m");
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
status = crypt_status(cd, argv[2]);
if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) {
log_info("Volume %s already active.", argv[2]);
- r = 0;
- goto finish;
+ return 0;
}
if (arg_readonly)
@@ -661,7 +695,7 @@ int main(int argc, char *argv[]) {
if (r == -EAGAIN)
continue;
if (r < 0)
- goto finish;
+ return r;
}
if (streq_ptr(arg_type, CRYPT_TCRYPT))
@@ -679,18 +713,15 @@ int main(int argc, char *argv[]) {
key_file = NULL;
continue;
}
- if (r != -EPERM) {
- log_error_errno(r, "Failed to activate: %m");
- goto finish;
- }
+ if (r != -EPERM)
+ return log_error_errno(r, "Failed to activate: %m");
log_warning("Invalid passphrase.");
}
if (arg_tries != 0 && tries >= arg_tries) {
log_error("Too many attempts; giving up.");
- r = -EPERM;
- goto finish;
+ return -EPERM;
}
} else if (streq(argv[1], "detach")) {
@@ -698,34 +729,23 @@ int main(int argc, char *argv[]) {
r = crypt_init_by_name(&cd, argv[2]);
if (r == -ENODEV) {
log_info("Volume %s already inactive.", argv[2]);
- r = 0;
- goto finish;
- }
- if (r < 0) {
- log_error_errno(r, "crypt_init_by_name() failed: %m");
- goto finish;
+ return 0;
}
+ if (r < 0)
+ return log_error_errno(r, "crypt_init_by_name() failed: %m");
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
r = crypt_deactivate(cd, argv[2]);
- if (r < 0) {
- log_error_errno(r, "Failed to deactivate: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to deactivate: %m");
} else {
log_error("Unknown verb %s.", argv[1]);
- goto finish;
+ return -EINVAL;
}
- r = 0;
-
-finish:
- free(arg_cipher);
- free(arg_hash);
- free(arg_header);
- strv_free(arg_tcrypt_keyfiles);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c
index dd6ab94fa2..fa4ca57bbf 100644
--- a/src/debug-generator/debug-generator.c
+++ b/src/debug-generator/debug-generator.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
+#include "generator.h"
#include "mkdir.h"
#include "parse-util.h"
#include "proc-cmdline.h"
@@ -10,12 +11,16 @@
#include "unit-name.h"
#include "util.h"
+static const char *arg_dest = NULL;
static char *arg_default_unit = NULL;
-static const char *arg_dest = "/tmp";
static char **arg_mask = NULL;
static char **arg_wants = NULL;
static bool arg_debug_shell = false;
+STATIC_DESTRUCTOR_REGISTER(arg_default_unit, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_mask, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_wants, strv_freep);
+
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
@@ -136,44 +141,25 @@ static int generate_wants_symlinks(void) {
return r;
}
-int main(int argc, char *argv[]) {
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
int r, q;
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1)
- arg_dest = argv[2];
+ assert_se(arg_dest = dest_early);
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_RD_STRICT | PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
if (arg_debug_shell) {
r = strv_extend(&arg_wants, "debug-shell.service");
- if (r < 0) {
- r = log_oom();
- goto finish;
- }
+ if (r < 0)
+ return log_oom();
}
r = generate_mask_symlinks();
-
q = generate_wants_symlinks();
- if (q < 0)
- r = q;
-finish:
- arg_default_unit = mfree(arg_default_unit);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r < 0 ? r : q;
}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/delta/delta.c b/src/delta/delta.c
index 21cce7e8b5..1ffbc6c571 100644
--- a/src/delta/delta.c
+++ b/src/delta/delta.c
@@ -13,9 +13,11 @@
#include "hashmap.h"
#include "locale-util.h"
#include "log.h"
+#include "main-func.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "process-util.h"
#include "signal-util.h"
#include "stat-util.h"
@@ -52,7 +54,7 @@ static const char have_dropins[] =
"systemd/system\0"
"systemd/user\0";
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static int arg_diff = -1;
static enum {
@@ -88,7 +90,7 @@ static int notify_override_masked(const char *top, const char *bottom) {
printf("%s%s%s %s %s %s\n",
ansi_highlight_red(), "[MASKED]", ansi_normal(),
- top, special_glyph(ARROW), bottom);
+ top, special_glyph(SPECIAL_GLYPH_ARROW), bottom);
return 1;
}
@@ -98,7 +100,7 @@ static int notify_override_equivalent(const char *top, const char *bottom) {
printf("%s%s%s %s %s %s\n",
ansi_highlight_green(), "[EQUIVALENT]", ansi_normal(),
- top, special_glyph(ARROW), bottom);
+ top, special_glyph(SPECIAL_GLYPH_ARROW), bottom);
return 1;
}
@@ -108,7 +110,7 @@ static int notify_override_redirected(const char *top, const char *bottom) {
printf("%s%s%s %s %s %s\n",
ansi_highlight(), "[REDIRECTED]", ansi_normal(),
- top, special_glyph(ARROW), bottom);
+ top, special_glyph(SPECIAL_GLYPH_ARROW), bottom);
return 1;
}
@@ -118,7 +120,7 @@ static int notify_override_overridden(const char *top, const char *bottom) {
printf("%s%s%s %s %s %s\n",
ansi_highlight(), "[OVERRIDDEN]", ansi_normal(),
- top, special_glyph(ARROW), bottom);
+ top, special_glyph(SPECIAL_GLYPH_ARROW), bottom);
return 1;
}
@@ -128,7 +130,7 @@ static int notify_override_extended(const char *top, const char *bottom) {
printf("%s%s%s %s %s %s\n",
ansi_highlight(), "[EXTENDED]", ansi_normal(),
- top, special_glyph(ARROW), bottom);
+ top, special_glyph(SPECIAL_GLYPH_ARROW), bottom);
return 1;
}
@@ -167,7 +169,7 @@ static int found_override(const char *top, const char *bottom) {
fflush(stdout);
- r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {
@@ -233,7 +235,7 @@ static int enumerate_dir_d(
return -ENOMEM;
d = p + strlen(toppath) + 1;
- log_debug("Adding at top: %s %s %s", d, special_glyph(ARROW), p);
+ log_debug("Adding at top: %s %s %s", d, special_glyph(SPECIAL_GLYPH_ARROW), p);
k = ordered_hashmap_put(top, d, p);
if (k >= 0) {
p = strdup(p);
@@ -245,7 +247,7 @@ static int enumerate_dir_d(
return k;
}
- log_debug("Adding at bottom: %s %s %s", d, special_glyph(ARROW), p);
+ log_debug("Adding at bottom: %s %s %s", d, special_glyph(SPECIAL_GLYPH_ARROW), p);
free(ordered_hashmap_remove(bottom, d));
k = ordered_hashmap_put(bottom, d, p);
if (k < 0) {
@@ -269,7 +271,7 @@ static int enumerate_dir_d(
return -ENOMEM;
log_debug("Adding to drops: %s %s %s %s %s",
- unit, special_glyph(ARROW), basename(p), special_glyph(ARROW), p);
+ unit, special_glyph(SPECIAL_GLYPH_ARROW), basename(p), special_glyph(SPECIAL_GLYPH_ARROW), p);
k = ordered_hashmap_put(h, basename(p), p);
if (k < 0) {
free(p);
@@ -349,7 +351,7 @@ static int enumerate_dir(
if (!p)
return -ENOMEM;
- log_debug("Adding at top: %s %s %s", basename(p), special_glyph(ARROW), p);
+ log_debug("Adding at top: %s %s %s", basename(p), special_glyph(SPECIAL_GLYPH_ARROW), p);
r = ordered_hashmap_put(top, basename(p), p);
if (r >= 0) {
p = strdup(p);
@@ -358,7 +360,7 @@ static int enumerate_dir(
} else if (r != -EEXIST)
return r;
- log_debug("Adding at bottom: %s %s %s", basename(p), special_glyph(ARROW), p);
+ log_debug("Adding at bottom: %s %s %s", basename(p), special_glyph(SPECIAL_GLYPH_ARROW), p);
free(ordered_hashmap_remove(bottom, basename(p)));
r = ordered_hashmap_put(bottom, basename(p), p);
if (r < 0)
@@ -511,11 +513,18 @@ static int process_suffix_chop(const char *arg) {
}
}
- log_error("Invalid suffix specification %s.", arg);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid suffix specification %s.", arg);
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-delta", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] [SUFFIX...]\n\n"
"Find overridden configuration files.\n\n"
" -h --help Show this help\n"
@@ -523,7 +532,12 @@ static void help(void) {
" --no-pager Do not pipe output into a pager\n"
" --diff[=1|0] Show a diff when overridden files differ\n"
" -t --type=LIST... Only display a selected set of override types\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_flags(const char *flag_str, int flags) {
@@ -578,23 +592,21 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case 't': {
int f;
f = parse_flags(optarg, arg_flags);
- if (f < 0) {
- log_error("Failed to parse flags field.");
- return -EINVAL;
- }
+ if (f < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse flags field.");
arg_flags = f;
break;
}
@@ -606,10 +618,9 @@ static int parse_argv(int argc, char *argv[]) {
int b;
b = parse_boolean(optarg);
- if (b < 0) {
- log_error("Failed to parse diff boolean.");
- return -EINVAL;
- }
+ if (b < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse diff boolean.");
arg_diff = b;
}
@@ -625,7 +636,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r, k, n_found = 0;
log_parse_environment();
@@ -633,7 +644,7 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
if (arg_flags == 0)
arg_flags = SHOW_DEFAULTS;
@@ -643,7 +654,7 @@ int main(int argc, char *argv[]) {
else if (arg_diff)
arg_flags |= SHOW_OVERRIDDEN;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (optind < argc) {
int i;
@@ -668,9 +679,7 @@ int main(int argc, char *argv[]) {
if (r >= 0)
printf("%s%i overridden configuration files found.\n", n_found ? "\n" : "", n_found);
-
-finish:
- pager_close();
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c
index e8c6362a9c..f05f2279b3 100644
--- a/src/detect-virt/detect-virt.c
+++ b/src/detect-virt/detect-virt.c
@@ -5,6 +5,9 @@
#include <stdbool.h>
#include <stdlib.h>
+#include "alloc-util.h"
+#include "main-func.h"
+#include "pretty-print.h"
#include "string-table.h"
#include "util.h"
#include "virt.h"
@@ -18,7 +21,14 @@ static enum {
ONLY_PRIVATE_USERS,
} arg_mode = ANY_VIRTUALIZATION;
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-detect-virt", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"Detect execution in a virtualized environment.\n\n"
" -h --help Show this help\n"
@@ -29,7 +39,12 @@ static void help(void) {
" --private-users Only detect whether we are running in a user namespace\n"
" -q --quiet Don't output anything, just set return value\n"
" --list List all known and detectable types of virtualization\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -62,8 +77,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -99,15 +113,15 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind < argc) {
- log_error("%s takes no arguments.", program_invocation_short_name);
- return -EINVAL;
- }
+ if (optind < argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s takes no arguments.",
+ program_invocation_short_name);
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
/* This is mostly intended to be used for scripts which want
@@ -119,59 +133,45 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
switch (arg_mode) {
-
case ONLY_VM:
r = detect_vm();
- if (r < 0) {
- log_error_errno(r, "Failed to check for VM: %m");
- return EXIT_FAILURE;
- }
-
+ if (r < 0)
+ return log_error_errno(r, "Failed to check for VM: %m");
break;
case ONLY_CONTAINER:
r = detect_container();
- if (r < 0) {
- log_error_errno(r, "Failed to check for container: %m");
- return EXIT_FAILURE;
- }
-
+ if (r < 0)
+ return log_error_errno(r, "Failed to check for container: %m");
break;
case ONLY_CHROOT:
r = running_in_chroot();
- if (r < 0) {
- log_error_errno(r, "Failed to check for chroot() environment: %m");
- return EXIT_FAILURE;
- }
-
- return r ? EXIT_SUCCESS : EXIT_FAILURE;
+ if (r < 0)
+ return log_error_errno(r, "Failed to check for chroot() environment: %m");
+ return !r;
case ONLY_PRIVATE_USERS:
r = running_in_userns();
- if (r < 0) {
- log_error_errno(r, "Failed to check for user namespace: %m");
- return EXIT_FAILURE;
- }
-
- return r ? EXIT_SUCCESS : EXIT_FAILURE;
+ if (r < 0)
+ return log_error_errno(r, "Failed to check for user namespace: %m");
+ return !r;
case ANY_VIRTUALIZATION:
default:
r = detect_virtualization();
- if (r < 0) {
- log_error_errno(r, "Failed to check for virtualization: %m");
- return EXIT_FAILURE;
- }
-
+ if (r < 0)
+ return log_error_errno(r, "Failed to check for virtualization: %m");
break;
}
if (!arg_quiet)
puts(virtualization_to_string(r));
- return r != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE;
+ return r == VIRTUALIZATION_NONE;
}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index 0adeee6691..50de0afce6 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -9,6 +9,7 @@
#include "hexdecoct.h"
#include "log.h"
#include "loop-util.h"
+#include "main-func.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
@@ -24,6 +25,8 @@ static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DI
static void *arg_root_hash = NULL;
static size_t arg_root_hash_size = 0;
+STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
+
static void help(void) {
printf("%s [OPTIONS...] IMAGE\n"
"%s [OPTIONS...] --mount IMAGE PATH\n"
@@ -91,10 +94,10 @@ static int parse_argv(int argc, char *argv[]) {
flags = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
else if (streq(optarg, "crypt"))
flags = DISSECT_IMAGE_DISCARD_ANY;
- else {
- log_error("Unknown --discard= parameter: %s", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown --discard= parameter: %s",
+ optarg);
arg_flags = (arg_flags & ~DISSECT_IMAGE_DISCARD_ANY) | flags;
break;
@@ -106,7 +109,7 @@ static int parse_argv(int argc, char *argv[]) {
r = unhexmem(optarg, strlen(optarg), &p, &l);
if (r < 0)
- return log_error_errno(r, "Failed to parse root hash: %s", optarg);
+ return log_error_errno(r, "Failed to parse root hash '%s': %m", optarg);
if (l < sizeof(sd_id128_t)) {
log_error("Root hash must be at least 128bit long: %s", optarg);
free(p);
@@ -131,20 +134,18 @@ static int parse_argv(int argc, char *argv[]) {
switch (arg_action) {
case ACTION_DISSECT:
- if (optind + 1 != argc) {
- log_error("Expected a file path as only argument.");
- return -EINVAL;
- }
+ if (optind + 1 != argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Expected a file path as only argument.");
arg_image = argv[optind];
arg_flags |= DISSECT_IMAGE_READ_ONLY;
break;
case ACTION_MOUNT:
- if (optind + 2 != argc) {
- log_error("Expected a file path and mount point path as only arguments.");
- return -EINVAL;
- }
+ if (optind + 2 != argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Expected a file path and mount point path as only arguments.");
arg_image = argv[optind];
arg_path = argv[optind + 1];
@@ -157,7 +158,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
_cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
@@ -168,25 +169,21 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, &d);
- if (r < 0) {
- log_error_errno(r, "Failed to set up loopback device: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up loopback device: %m");
if (!arg_root_hash) {
r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size);
- if (r < 0) {
- log_error_errno(r, "Failed to read root hash file for %s: %m", arg_image);
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read root hash file for %s: %m", arg_image);
}
r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_flags, &m);
if (r < 0)
- goto finish;
+ return r;
switch (arg_action) {
@@ -227,10 +224,8 @@ int main(int argc, char *argv[]) {
}
r = dissected_image_acquire_metadata(m);
- if (r < 0) {
- log_error_errno(r, "Failed to acquire image metadata: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire image metadata: %m");
if (m->hostname)
printf(" Hostname: %s\n", m->hostname);
@@ -262,20 +257,16 @@ int main(int argc, char *argv[]) {
case ACTION_MOUNT:
r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_flags, &di);
if (r < 0)
- goto finish;
+ return r;
r = dissected_image_mount(m, arg_path, UID_INVALID, arg_flags);
- if (r < 0) {
- log_error_errno(r, "Failed to mount image: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to mount image: %m");
if (di) {
r = decrypted_image_relinquish(di);
- if (r < 0) {
- log_error_errno(r, "Failed to relinquish DM devices: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to relinquish DM devices: %m");
}
loop_device_relinquish(d);
@@ -285,7 +276,7 @@ int main(int argc, char *argv[]) {
assert_not_reached("Unknown action.");
}
-finish:
- free(arg_root_hash);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c
index 1719b69b98..9d64d95738 100644
--- a/src/environment-d-generator/environment-d-generator.c
+++ b/src/environment-d-generator/environment-d-generator.c
@@ -4,8 +4,8 @@
#include "conf-files.h"
#include "def.h"
+#include "env-file.h"
#include "escape.h"
-#include "fileio.h"
#include "log.h"
#include "path-lookup.h"
diff --git a/src/escape/escape.c b/src/escape/escape.c
index 371ddbe02b..8b38d9f679 100644
--- a/src/escape/escape.c
+++ b/src/escape/escape.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2014 Michael Biebl
-***/
#include <getopt.h>
#include <stdio.h>
@@ -9,6 +6,8 @@
#include "alloc-util.h"
#include "log.h"
+#include "main-func.h"
+#include "pretty-print.h"
#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
@@ -21,18 +20,32 @@ static enum {
static const char *arg_suffix = NULL;
static const char *arg_template = NULL;
static bool arg_path = false;
+static bool arg_instance = false;
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-escape", "1", &link);
+ if (r < 0)
+ return log_oom();
-static void help(void) {
printf("%s [OPTIONS...] [NAME...]\n\n"
"Escape strings for usage in systemd unit names.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --suffix=SUFFIX Unit suffix to append to escaped strings\n"
" --template=TEMPLATE Insert strings as instance into template\n"
+ " --instance With --unescape, show just the instance part\n"
" -u --unescape Unescape strings\n"
" -m --mangle Mangle strings\n"
" -p --path When escaping/unescaping assume the string is a path\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -51,6 +64,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "unescape", no_argument, NULL, 'u' },
{ "mangle", no_argument, NULL, 'm' },
{ "path", no_argument, NULL, 'p' },
+ { "instance", no_argument, NULL, 'i' },
{}
};
@@ -64,28 +78,25 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
case ARG_SUFFIX:
- if (unit_type_from_string(optarg) < 0) {
- log_error("Invalid unit suffix type %s.", optarg);
- return -EINVAL;
- }
+ if (unit_type_from_string(optarg) < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid unit suffix type %s.", optarg);
arg_suffix = optarg;
break;
case ARG_TEMPLATE:
- if (!unit_name_is_valid(optarg, UNIT_NAME_TEMPLATE)) {
- log_error("Template name %s is not valid.", optarg);
- return -EINVAL;
- }
+ if (!unit_name_is_valid(optarg, UNIT_NAME_TEMPLATE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Template name %s is not valid.", optarg);
arg_template = optarg;
break;
@@ -102,6 +113,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_path = true;
break;
+ case 'i':
+ arg_instance = true;
+ break;
+
case '?':
return -EINVAL;
@@ -109,30 +124,38 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind >= argc) {
- log_error("Not enough arguments.");
- return -EINVAL;
- }
+ if (optind >= argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not enough arguments.");
- if (arg_template && arg_suffix) {
- log_error("--suffix= and --template= may not be combined.");
- return -EINVAL;
- }
+ if (arg_template && arg_suffix)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--suffix= and --template= may not be combined.");
- if ((arg_template || arg_suffix) && arg_action != ACTION_ESCAPE) {
- log_error("--suffix= and --template= are not compatible with --unescape or --mangle.");
- return -EINVAL;
- }
+ if ((arg_template || arg_suffix) && arg_action == ACTION_MANGLE)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--suffix= and --template= are not compatible with --mangle.");
- if (arg_path && !IN_SET(arg_action, ACTION_ESCAPE, ACTION_UNESCAPE)) {
- log_error("--path may not be combined with --mangle.");
- return -EINVAL;
- }
+ if (arg_suffix && arg_action == ACTION_UNESCAPE)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--suffix is not compatible with --unescape.");
+
+ if (arg_path && !IN_SET(arg_action, ACTION_ESCAPE, ACTION_UNESCAPE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--path may not be combined with --mangle.");
+
+ if (arg_instance && arg_action != ACTION_UNESCAPE)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--instance must be used in conjunction with --unescape.");
+
+ if (arg_instance && arg_template)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--instance may not be combined with --template.");
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
char **i;
int r;
@@ -141,7 +164,7 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
STRV_FOREACH(i, argv + optind) {
_cleanup_free_ char *e = NULL;
@@ -151,66 +174,77 @@ int main(int argc, char *argv[]) {
case ACTION_ESCAPE:
if (arg_path) {
r = unit_name_path_escape(*i, &e);
- if (r < 0) {
- log_error_errno(r, "Failed to escape string: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to escape string: %m");
} else {
e = unit_name_escape(*i);
- if (!e) {
- r = log_oom();
- goto finish;
- }
+ if (!e)
+ return log_oom();
}
if (arg_template) {
char *x;
r = unit_name_replace_instance(arg_template, e, &x);
- if (r < 0) {
- log_error_errno(r, "Failed to replace instance: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to replace instance: %m");
- free(e);
- e = x;
+ free_and_replace(e, x);
} else if (arg_suffix) {
char *x;
x = strjoin(e, ".", arg_suffix);
- if (!x) {
- r = log_oom();
- goto finish;
- }
+ if (!x)
+ return log_oom();
- free(e);
- e = x;
+ free_and_replace(e, x);
}
break;
- case ACTION_UNESCAPE:
+ case ACTION_UNESCAPE: {
+ _cleanup_free_ char *name = NULL;
+
+ if (arg_template || arg_instance) {
+ _cleanup_free_ char *template = NULL;
+
+ r = unit_name_to_instance(*i, &name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract instance: %m");
+ if (isempty(name))
+ return log_error("Unit %s is missing the instance name.", *i);
+
+ r = unit_name_template(*i, &template);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract template: %m");
+ if (arg_template && !streq(arg_template, template))
+ return log_error("Unit %s template %s does not match specified template %s.",
+ *i, template, arg_template);
+ } else {
+ name = strdup(*i);
+ if (!name)
+ return log_oom();
+ }
+
if (arg_path)
- r = unit_name_path_unescape(*i, &e);
+ r = unit_name_path_unescape(name, &e);
else
- r = unit_name_unescape(*i, &e);
+ r = unit_name_unescape(name, &e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unescape string: %m");
- if (r < 0) {
- log_error_errno(r, "Failed to unescape string: %m");
- goto finish;
- }
break;
+ }
case ACTION_MANGLE:
r = unit_name_mangle(*i, 0, &e);
- if (r < 0) {
- log_error_errno(r, "Failed to mangle name: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle name: %m");
+
break;
}
- if (i != argv+optind)
+ if (i != argv + optind)
fputc(' ', stdout);
fputs(e, stdout);
@@ -218,6 +252,7 @@ int main(int argc, char *argv[]) {
fputc('\n', stdout);
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index a98e53b3a3..dde11576ea 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -4,7 +4,7 @@
#include <getopt.h>
#include <unistd.h>
-#ifdef HAVE_CRYPT_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.
@@ -22,15 +22,18 @@
#include "alloc-util.h"
#include "ask-password-api.h"
#include "copy.h"
+#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "locale-util.h"
+#include "main-func.h"
#include "mkdir.h"
#include "os-util.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "proc-cmdline.h"
#include "random-util.h"
#include "string-util.h"
@@ -58,6 +61,14 @@ static bool arg_copy_keymap = false;
static bool arg_copy_timezone = false;
static bool arg_copy_root_password = false;
+STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_locale, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_locale_messages, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_keymap, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_timezone, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_hostname, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_password, string_free_erasep);
+
static bool press_any_key(void) {
char k = 0;
bool need_nl = true;
@@ -151,7 +162,7 @@ static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *
_cleanup_free_ char *p = NULL;
unsigned u;
- r = ask_string(&p, "%s %s (empty to skip): ", special_glyph(TRIANGULAR_BULLET), text);
+ r = ask_string(&p, "%s %s (empty to skip): ", special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), text);
if (r < 0)
return log_error_errno(r, "Failed to query user: %m");
@@ -440,7 +451,7 @@ static int prompt_hostname(void) {
for (;;) {
_cleanup_free_ char *h = NULL;
- r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(TRIANGULAR_BULLET));
+ r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
if (r < 0)
return log_error_errno(r, "Failed to query hostname: %m");
@@ -527,17 +538,21 @@ static int prompt_root_password(void) {
print_welcome();
putchar('\n');
- msg1 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): ");
- msg2 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter new root password again: ");
+ msg1 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): ");
+ msg2 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter new root password again: ");
for (;;) {
- _cleanup_string_free_erase_ char *a = NULL, *b = NULL;
+ _cleanup_strv_free_erase_ char **a = NULL, **b = NULL;
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 (isempty(a)) {
+ if (isempty(*a)) {
log_warning("No password entered, skipping.");
break;
}
@@ -546,12 +561,12 @@ static int prompt_root_password(void) {
if (r < 0)
return log_error_errno(r, "Failed to query root password: %m");
- if (!streq(a, b)) {
+ if (!streq(*a, *b)) {
log_error("Entered passwords did not match, please try again.");
continue;
}
- arg_root_password = TAKE_PTR(a);
+ arg_root_password = TAKE_PTR(*a);
break;
}
@@ -643,7 +658,8 @@ static int process_root_password(void) {
if (!arg_root_password)
return 0;
- r = acquire_random_bytes(raw, 16, true);
+ /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */
+ r = genuine_random_bytes(raw, 16, RANDOM_BLOCK);
if (r < 0)
return log_error_errno(r, "Failed to get salt: %m");
@@ -674,7 +690,14 @@ static int process_root_password(void) {
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-firstboot", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"Configures basic settings of the system.\n\n"
" -h --help Show this help\n"
@@ -700,7 +723,12 @@ static void help(void) {
" --copy-root-password Copy root password from host\n"
" --copy Copy locale, keymap, timezone, root password\n"
" --setup-machine-id Generate a new random machine ID\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -767,8 +795,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -780,10 +807,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_LOCALE:
- if (!locale_is_valid(optarg)) {
- log_error("Locale %s is not valid.", optarg);
- return -EINVAL;
- }
+ if (!locale_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Locale %s is not valid.", optarg);
r = free_and_strdup(&arg_locale, optarg);
if (r < 0)
@@ -792,10 +818,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_LOCALE_MESSAGES:
- if (!locale_is_valid(optarg)) {
- log_error("Locale %s is not valid.", optarg);
- return -EINVAL;
- }
+ if (!locale_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Locale %s is not valid.", optarg);
r = free_and_strdup(&arg_locale_messages, optarg);
if (r < 0)
@@ -804,10 +829,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_KEYMAP:
- if (!keymap_is_valid(optarg)) {
- log_error("Keymap %s is not valid.", optarg);
- return -EINVAL;
- }
+ if (!keymap_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Keymap %s is not valid.", optarg);
r = free_and_strdup(&arg_keymap, optarg);
if (r < 0)
@@ -816,10 +840,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TIMEZONE:
- if (!timezone_is_valid(optarg, LOG_ERR)) {
- log_error("Timezone %s is not valid.", optarg);
- return -EINVAL;
- }
+ if (!timezone_is_valid(optarg, LOG_ERR))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Timezone %s is not valid.", optarg);
r = free_and_strdup(&arg_timezone, optarg);
if (r < 0)
@@ -843,10 +866,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_HOSTNAME:
- if (!hostname_is_valid(optarg, true)) {
- log_error("Host name %s is not valid.", optarg);
- return -EINVAL;
- }
+ if (!hostname_is_valid(optarg, true))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Host name %s is not valid.", optarg);
hostname_cleanup(optarg);
r = free_and_strdup(&arg_hostname, optarg);
@@ -856,10 +878,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_MACHINE_ID:
- if (sd_id128_from_string(optarg, &arg_machine_id) < 0) {
- log_error("Failed to parse machine id %s.", optarg);
- return -EINVAL;
- }
+ if (sd_id128_from_string(optarg, &arg_machine_id) < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse machine id %s.", optarg);
break;
@@ -925,63 +946,49 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
bool enabled;
int r;
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
r = proc_cmdline_get_bool("systemd.firstboot", &enabled);
- if (r < 0) {
- log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring.");
- goto finish;
- }
- if (r > 0 && !enabled) {
- r = 0; /* disabled */
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
+ if (r > 0 && !enabled)
+ return 0; /* disabled */
r = process_locale();
if (r < 0)
- goto finish;
+ return r;
r = process_keymap();
if (r < 0)
- goto finish;
+ return r;
r = process_timezone();
if (r < 0)
- goto finish;
+ return r;
r = process_hostname();
if (r < 0)
- goto finish;
+ return r;
r = process_machine_id();
if (r < 0)
- goto finish;
+ return r;
r = process_root_password();
if (r < 0)
- goto finish;
-
-finish:
- free(arg_root);
- free(arg_locale);
- free(arg_locale_messages);
- free(arg_keymap);
- free(arg_timezone);
- free(arg_hostname);
- string_erase(arg_root_password);
- free(arg_root_password);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
+
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
index 26fbd8bb03..ba39f596fc 100644
--- a/src/fsck/fsck.c
+++ b/src/fsck/fsck.c
@@ -22,10 +22,12 @@
#include "device-util.h"
#include "fd-util.h"
#include "fs-util.h"
+#include "main-func.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "special.h"
@@ -34,14 +36,14 @@
/* exit codes as defined in fsck(8) */
enum {
- FSCK_SUCCESS = 0,
- FSCK_ERROR_CORRECTED = 1,
- FSCK_SYSTEM_SHOULD_REBOOT = 2,
- FSCK_ERRORS_LEFT_UNCORRECTED = 4,
- FSCK_OPERATIONAL_ERROR = 8,
- FSCK_USAGE_OR_SYNTAX_ERROR = 16,
- FSCK_USER_CANCELLED = 32,
- FSCK_SHARED_LIB_ERROR = 128,
+ FSCK_SUCCESS = 0,
+ FSCK_ERROR_CORRECTED = 1 << 0,
+ FSCK_SYSTEM_SHOULD_REBOOT = 1 << 1,
+ FSCK_ERRORS_LEFT_UNCORRECTED = 1 << 2,
+ FSCK_OPERATIONAL_ERROR = 1 << 3,
+ FSCK_USAGE_OR_SYNTAX_ERROR = 1 << 4,
+ FSCK_USER_CANCELLED = 1 << 5,
+ FSCK_SHARED_LIB_ERROR = 1 << 7,
};
static bool arg_skip = false;
@@ -175,7 +177,7 @@ static int process_progress(int fd) {
if (fd < 0)
return 0;
- f = fdopen(fd, "re");
+ f = fdopen(fd, "r");
if (!f) {
safe_close(fd);
return -errno;
@@ -247,23 +249,20 @@ static int fsck_progress_socket(void) {
.un.sun_path = "/run/systemd/fsck.progress",
};
- int fd, r;
+ _cleanup_close_ int fd = -1;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
return log_warning_errno(errno, "socket(): %m");
- if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
- r = log_full_errno(IN_SET(errno, ECONNREFUSED, ENOENT) ? LOG_DEBUG : LOG_WARNING,
- errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path);
- safe_close(fd);
- return r;
- }
+ if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+ return log_full_errno(IN_SET(errno, ECONNREFUSED, ENOENT) ? LOG_DEBUG : LOG_WARNING,
+ errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path);
- return fd;
+ return TAKE_FD(fd);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_close_pair_ int progress_pipe[2] = { -1, -1 };
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
const char *device, *type;
@@ -272,15 +271,13 @@ int main(int argc, char *argv[]) {
int r, exit_status;
pid_t pid;
+ log_setup_service();
+
if (argc > 2) {
log_error("This program expects one or no arguments.");
- return EXIT_FAILURE;
+ return -EINVAL;
}
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
umask(0022);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
@@ -289,30 +286,23 @@ int main(int argc, char *argv[]) {
test_files();
- if (!arg_force && arg_skip) {
- r = 0;
- goto finish;
- }
+ if (!arg_force && arg_skip)
+ return 0;
if (argc > 1) {
device = argv[1];
- if (stat(device, &st) < 0) {
- r = log_error_errno(errno, "Failed to stat %s: %m", device);
- goto finish;
- }
+ if (stat(device, &st) < 0)
+ return log_error_errno(errno, "Failed to stat %s: %m", device);
if (!S_ISBLK(st.st_mode)) {
log_error("%s is not a block device.", device);
- r = -EINVAL;
- goto finish;
+ return -EINVAL;
}
r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev);
- if (r < 0) {
- log_error_errno(r, "Failed to detect device %s: %m", device);
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to detect device %s: %m", device);
root_directory = false;
} else {
@@ -320,16 +310,13 @@ int main(int argc, char *argv[]) {
/* Find root device */
- if (stat("/", &st) < 0) {
- r = log_error_errno(errno, "Failed to stat() the root directory: %m");
- goto finish;
- }
+ if (stat("/", &st) < 0)
+ return log_error_errno(errno, "Failed to stat() the root directory: %m");
/* Virtual root devices don't need an fsck */
if (major(st.st_dev) == 0) {
log_debug("Root directory is virtual or btrfs, skipping check.");
- r = 0;
- goto finish;
+ return 0;
}
/* check if we are already writable */
@@ -338,46 +325,37 @@ int main(int argc, char *argv[]) {
if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
log_info("Root directory is writable, skipping check.");
- r = 0;
- goto finish;
+ return 0;
}
r = sd_device_new_from_devnum(&dev, 'b', st.st_dev);
- if (r < 0) {
- log_error_errno(r, "Failed to detect root device: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to detect root device: %m");
r = sd_device_get_devname(dev, &device);
- if (r < 0) {
- log_error_errno(r, "Failed to detect device node of root directory: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to detect device node of root directory: %m");
root_directory = true;
}
- r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type);
- if (r >= 0) {
+ if (sd_device_get_property_value(dev, "ID_FS_TYPE", &type) >= 0) {
r = fsck_exists(type);
if (r < 0)
- log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s, proceeding: %m", type, device);
+ log_device_warning_errno(dev, r, "Couldn't detect if fsck.%s may be used, proceeding: %m", type);
else if (r == 0) {
- log_info("fsck.%s doesn't exist, not checking file system on %s.", type, device);
- goto finish;
+ log_device_info(dev, "fsck.%s doesn't exist, not checking file system.", type);
+ return 0;
}
}
- if (arg_show_progress) {
- if (pipe(progress_pipe) < 0) {
- r = log_error_errno(errno, "pipe(): %m");
- goto finish;
- }
- }
+ if (arg_show_progress &&
+ pipe(progress_pipe) < 0)
+ return log_error_errno(errno, "pipe(): %m");
r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
if (r < 0)
- goto finish;
+ return r;
if (r == 0) {
char dash_c[STRLEN("-C") + DECIMAL_STR_MAX(int) + 1];
int progress_socket = -1;
@@ -424,40 +402,36 @@ int main(int argc, char *argv[]) {
cmdline[i++] = device;
cmdline[i++] = NULL;
+ (void) rlimit_nofile_safe();
+
execv(cmdline[0], (char**) cmdline);
_exit(FSCK_OPERATIONAL_ERROR);
}
progress_pipe[1] = safe_close(progress_pipe[1]);
- (void) process_progress(progress_pipe[0]);
- progress_pipe[0] = -1;
+ (void) process_progress(TAKE_FD(progress_pipe[0]));
exit_status = wait_for_terminate_and_check("fsck", pid, WAIT_LOG_ABNORMAL);
- if (exit_status < 0) {
- r = exit_status;
- goto finish;
- }
+ if (exit_status < 0)
+ return exit_status;
if (exit_status & ~1) {
log_error("fsck failed with exit status %i.", exit_status);
if ((exit_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) {
/* System should be rebooted. */
start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly");
- r = -EINVAL;
- } else if (exit_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)) {
+ return -EINVAL;
+ } else if (exit_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED))
/* Some other problem */
start_target(SPECIAL_EMERGENCY_TARGET, "replace");
- r = -EINVAL;
- } else {
+ else
log_warning("Ignoring error.");
- r = 0;
- }
- } else
- r = 0;
+ }
if (exit_status & FSCK_ERROR_CORRECTED)
(void) touch("/run/systemd/quotacheck");
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return !!(exit_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED));
}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 482229b4a5..55a8242fcf 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -14,9 +14,11 @@
#include "fstab-util.h"
#include "generator.h"
#include "log.h"
+#include "main-func.h"
#include "mkdir.h"
#include "mount-setup.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
@@ -38,8 +40,8 @@ typedef enum MountpointFlags {
GROWFS = 1 << 4,
} MountpointFlags;
-static const char *arg_dest = "/tmp";
-static const char *arg_dest_late = "/tmp";
+static const char *arg_dest = NULL;
+static const char *arg_dest_late = NULL;
static bool arg_fstab_enabled = true;
static char *arg_root_what = NULL;
static char *arg_root_fstype = NULL;
@@ -51,6 +53,14 @@ static char *arg_usr_fstype = NULL;
static char *arg_usr_options = NULL;
static VolatileMode arg_volatile_mode = _VOLATILE_MODE_INVALID;
+STATIC_DESTRUCTOR_REGISTER(arg_root_what, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_fstype, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_usr_what, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_usr_fstype, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_usr_options, freep);
+
static int write_options(FILE *f, const char *options) {
_cleanup_free_ char *o = NULL;
@@ -528,7 +538,7 @@ static int parse_fstab(bool initrd) {
if (!what)
return log_oom();
- if (is_device_path(what) && path_is_read_only_fs("sys") > 0) {
+ if (is_device_path(what) && path_is_read_only_fs("/sys") > 0) {
log_info("Running in a container, ignoring fstab device entry for %s.", what);
continue;
}
@@ -857,25 +867,11 @@ static int determine_root(void) {
return 1;
}
-int main(int argc, char *argv[]) {
- int r = 0;
-
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1)
- arg_dest = argv[1];
- if (argc > 3)
- arg_dest_late = argv[3];
-
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+ int r;
- umask(0022);
+ assert_se(arg_dest = dest);
+ assert_se(arg_dest_late = dest_late);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
@@ -920,14 +916,7 @@ int main(int argc, char *argv[]) {
}
}
- free(arg_root_what);
- free(arg_root_fstype);
- free(arg_root_options);
- free(arg_root_hash);
-
- free(arg_usr_what);
- free(arg_usr_fstype);
- free(arg_usr_options);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/fuzz/fuzz-bus-message.c b/src/fuzz/fuzz-bus-message.c
new file mode 100644
index 0000000000..9842c62a6f
--- /dev/null
+++ b/src/fuzz/fuzz-bus-message.c
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "bus-dump.h"
+#include "bus-message.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fuzz.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ char *out = NULL; /* out should be freed after g */
+ size_t out_size;
+ _cleanup_fclose_ FILE *g = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_free_ void *buffer = NULL;
+ int r;
+
+ /* We don't want to fill the logs with messages about parse errors.
+ * Disable most logging if not running standalone */
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ r = sd_bus_new(&bus);
+ assert_se(r >= 0);
+
+ assert_se(buffer = memdup(data, size));
+
+ r = bus_message_from_malloc(bus, buffer, size, NULL, 0, NULL, &m);
+ if (r == -EBADMSG)
+ return 0;
+ assert_se(r >= 0);
+ TAKE_PTR(buffer);
+
+ if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0)
+ assert_se(g = open_memstream(&out, &out_size));
+
+ bus_message_dump(m, g ?: stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+
+ r = sd_bus_message_rewind(m, true);
+ assert_se(r >= 0);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-catalog.c b/src/fuzz/fuzz-catalog.c
new file mode 100644
index 0000000000..7ee9750141
--- /dev/null
+++ b/src/fuzz/fuzz-catalog.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "catalog.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "fuzz.h"
+#include "tmpfile-util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-catalog.XXXXXX";
+ _cleanup_close_ int fd = -1;
+ _cleanup_hashmap_free_free_free_ Hashmap *h = NULL;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ assert_se(h = hashmap_new(&catalog_hash_ops));
+
+ fd = mkostemp_safe(name);
+ assert_se(fd >= 0);
+ assert_se(write(fd, data, size) == (ssize_t) size);
+
+ (void) catalog_import_file(h, name);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-compress.c b/src/fuzz/fuzz-compress.c
new file mode 100644
index 0000000000..9c5dfc92c0
--- /dev/null
+++ b/src/fuzz/fuzz-compress.c
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+
+#include "alloc-util.h"
+#include "compress.h"
+#include "fuzz.h"
+
+static int compress(int alg,
+ const void *src, uint64_t src_size,
+ void *dst, size_t dst_alloc_size, size_t *dst_size) {
+
+ if (alg == OBJECT_COMPRESSED_LZ4)
+ return compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size);
+ if (alg == OBJECT_COMPRESSED_XZ)
+ return compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size);
+ return -EOPNOTSUPP;
+}
+
+typedef struct header {
+ uint32_t alg:2; /* We have only two compression algorithms so far, but we might add
+ * more in the future. Let's make this a bit wider so our fuzzer
+ * cases remain stable in the future. */
+ uint32_t sw_len;
+ uint32_t sw_alloc;
+ uint32_t reserved[3]; /* Extra space to keep fuzz cases stable in case we need to
+ * add stuff in the future. */
+ uint8_t data[];
+} header;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ void *buf = NULL, *buf2 = NULL;
+ int r;
+
+ if (size < offsetof(header, data) + 1)
+ return 0;
+
+ const header *h = (struct header*) data;
+ const size_t data_len = size - offsetof(header, data);
+
+ int alg = h->alg;
+
+ /* We don't want to fill the logs with messages about parse errors.
+ * Disable most logging if not running standalone */
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ log_info("Using compression %s, data size=%zu",
+ object_compressed_to_string(alg) ?: "(none)",
+ data_len);
+
+ buf = malloc(MAX(size, 128u)); /* Make the buffer a bit larger for very small data */
+ if (!buf) {
+ log_oom();
+ return 0;
+ }
+
+ size_t csize;
+ r = compress(alg, h->data, data_len, buf, size, &csize);
+ if (r < 0) {
+ log_error_errno(r, "Compression failed: %m");
+ return 0;
+ }
+
+ log_debug("Compressed %zu bytes to → %zu bytes", data_len, csize);
+
+ size_t sw_alloc = MAX(h->sw_alloc, 1u);
+ buf2 = malloc(sw_alloc);
+ if (!buf) {
+ log_oom();
+ return 0;
+ }
+
+ size_t sw_len = MIN(data_len - 1, h->sw_len);
+
+ r = decompress_startswith(alg, buf, csize, &buf2, &sw_alloc, h->data, sw_len, h->data[sw_len]);
+ assert_se(r > 0);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-dhcp6-client.c b/src/fuzz/fuzz-dhcp6-client.c
new file mode 100644
index 0000000000..c9bc2b3815
--- /dev/null
+++ b/src/fuzz/fuzz-dhcp6-client.c
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <unistd.h>
+
+#include "sd-dhcp6-client.h"
+#include "sd-event.h"
+
+#include "dhcp6-internal.h"
+#include "dhcp6-protocol.h"
+#include "fd-util.h"
+#include "fuzz.h"
+
+static int test_dhcp_fd[2] = { -1, -1 };
+
+int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
+ const void *packet, size_t len) {
+ return len;
+}
+
+int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) >= 0);
+ return test_dhcp_fd[0];
+}
+
+static void fuzz_client(const uint8_t *data, size_t size, bool is_information_request_enabled) {
+ _cleanup_(sd_event_unrefp) sd_event *e;
+ _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
+ struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
+
+ assert_se(sd_event_new(&e) >= 0);
+ assert_se(sd_dhcp6_client_new(&client) >= 0);
+ assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
+ assert_se(sd_dhcp6_client_set_ifindex(client, 42) == 0);
+ assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
+ assert_se(sd_dhcp6_client_set_information_request(client, is_information_request_enabled) == 0);
+
+ assert_se(sd_dhcp6_client_start(client) >= 0);
+
+ if (size >= sizeof(DHCP6Message))
+ assert_se(sd_dhcp6_client_set_transaction_id(client, htobe32(0x00ffffff) & ((const DHCP6Message *) data)->transaction_id) == 0);
+
+ assert_se(write(test_dhcp_fd[1], data, size) == (ssize_t) size);
+
+ sd_event_run(e, (uint64_t) -1);
+
+ assert_se(sd_dhcp6_client_stop(client) >= 0);
+
+ test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ /* This triggers client_receive_advertise */
+ fuzz_client(data, size, false);
+
+ /* This triggers client_receive_reply */
+ fuzz_client(data, size, true);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journal-remote.c b/src/fuzz/fuzz-journal-remote.c
index 432c687bc0..3ab4eb07fa 100644
--- a/src/fuzz/fuzz-journal-remote.c
+++ b/src/fuzz/fuzz-journal-remote.c
@@ -29,6 +29,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size <= 2)
return 0;
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
assert_se((fdin = memfd_new_and_map("fuzz-journal-remote", size, &mem)) >= 0);
memcpy(mem, data, size);
assert_se(munmap(mem, size) == 0);
diff --git a/src/fuzz/fuzz-journald-audit.c b/src/fuzz/fuzz-journald-audit.c
new file mode 100644
index 0000000000..3f3ce7e8ee
--- /dev/null
+++ b/src/fuzz/fuzz-journald-audit.c
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fuzz.h"
+#include "fuzz-journald.h"
+#include "journald-audit.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ Server s;
+
+ dummy_server_init(&s, data, size);
+ process_audit_string(&s, 0, s.buffer, size);
+ server_done(&s);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald-kmsg.c b/src/fuzz/fuzz-journald-kmsg.c
new file mode 100644
index 0000000000..f7426c8400
--- /dev/null
+++ b/src/fuzz/fuzz-journald-kmsg.c
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fuzz.h"
+#include "fuzz-journald.h"
+#include "journald-kmsg.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ Server s;
+
+ if (size == 0)
+ return 0;
+
+ dummy_server_init(&s, data, size);
+ dev_kmsg_record(&s, s.buffer, size);
+ server_done(&s);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald-native-fd.c b/src/fuzz/fuzz-journald-native-fd.c
new file mode 100644
index 0000000000..8e3e850fbb
--- /dev/null
+++ b/src/fuzz/fuzz-journald-native-fd.c
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "fuzz-journald.h"
+#include "fuzz.h"
+#include "journald-native.h"
+#include "memfd-util.h"
+#include "process-util.h"
+#include "tmpfile-util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ Server s;
+ _cleanup_close_ int sealed_fd = -1, unsealed_fd = -1;
+ _cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-journald-native-fd.XXXXXX";
+ char *label = NULL;
+ size_t label_len = 0;
+ struct ucred ucred;
+ struct timeval *tv = NULL;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ dummy_server_init(&s, NULL, 0);
+
+ sealed_fd = memfd_new(NULL);
+ assert_se(sealed_fd >= 0);
+ assert_se(write(sealed_fd, data, size) == (ssize_t) size);
+ assert_se(memfd_set_sealed(sealed_fd) >= 0);
+ assert_se(lseek(sealed_fd, 0, SEEK_SET) == 0);
+ ucred = (struct ucred) {
+ .pid = getpid_cached(),
+ .uid = geteuid(),
+ .gid = getegid(),
+ };
+ server_process_native_file(&s, sealed_fd, &ucred, tv, label, label_len);
+
+ unsealed_fd = mkostemp_safe(name);
+ assert_se(unsealed_fd >= 0);
+ assert_se(write(unsealed_fd, data, size) == (ssize_t) size);
+ assert_se(lseek(unsealed_fd, 0, SEEK_SET) == 0);
+ server_process_native_file(&s, unsealed_fd, &ucred, tv, label, label_len);
+
+ server_done(&s);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald-native.c b/src/fuzz/fuzz-journald-native.c
new file mode 100644
index 0000000000..f4de5fd8eb
--- /dev/null
+++ b/src/fuzz/fuzz-journald-native.c
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fuzz.h"
+#include "fuzz-journald.h"
+#include "journald-native.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ fuzz_journald_processing_function(data, size, server_process_native_message);
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald-stream.c b/src/fuzz/fuzz-journald-stream.c
new file mode 100644
index 0000000000..5d6c8eb8ca
--- /dev/null
+++ b/src/fuzz/fuzz-journald-stream.c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/sockios.h>
+#include <sys/ioctl.h>
+
+#include "fd-util.h"
+#include "fuzz.h"
+#include "fuzz-journald.h"
+#include "journald-stream.h"
+
+static int stream_fds[2] = { -1, -1 };
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ Server s;
+ StdoutStream *stream;
+ int v;
+
+ if (size == 0)
+ return 0;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, stream_fds) >= 0);
+ dummy_server_init(&s, NULL, 0);
+ assert_se(stdout_stream_install(&s, stream_fds[0], &stream) >= 0);
+ assert_se(write(stream_fds[1], data, size) == (ssize_t) size);
+ while (ioctl(stream_fds[0], SIOCINQ, &v) == 0 && v)
+ sd_event_run(s.event, (uint64_t) -1);
+ if (s.n_stdout_streams)
+ stdout_stream_destroy(stream);
+ server_done(&s);
+ stream_fds[1] = safe_close(stream_fds[1]);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald-syslog.c b/src/fuzz/fuzz-journald-syslog.c
new file mode 100644
index 0000000000..100f0ce691
--- /dev/null
+++ b/src/fuzz/fuzz-journald-syslog.c
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fuzz.h"
+#include "fuzz-journald.h"
+#include "journald-syslog.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ fuzz_journald_processing_function(data, size, server_process_syslog_message);
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald.c b/src/fuzz/fuzz-journald.c
new file mode 100644
index 0000000000..950e885cae
--- /dev/null
+++ b/src/fuzz/fuzz-journald.c
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "fuzz-journald.h"
+#include "journald-server.h"
+#include "sd-event.h"
+
+void dummy_server_init(Server *s, const uint8_t *buffer, size_t size) {
+ *s = (Server) {
+ .syslog_fd = -1,
+ .native_fd = -1,
+ .stdout_fd = -1,
+ .dev_kmsg_fd = -1,
+ .audit_fd = -1,
+ .hostname_fd = -1,
+ .notify_fd = -1,
+ .storage = STORAGE_NONE,
+ .line_max = 64,
+ };
+ assert_se(sd_event_default(&s->event) >= 0);
+
+ if (buffer) {
+ s->buffer = memdup_suffix0(buffer, size);
+ assert_se(s->buffer);
+ s->buffer_size = size + 1;
+ }
+}
+
+void fuzz_journald_processing_function(
+ const uint8_t *data,
+ size_t size,
+ void (*f)(Server *s, const char *buf, size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len)
+ ) {
+ Server s;
+ char *label = NULL;
+ size_t label_len = 0;
+ struct ucred *ucred = NULL;
+ struct timeval *tv = NULL;
+
+ if (size == 0)
+ return;
+
+ dummy_server_init(&s, data, size);
+ (*f)(&s, s.buffer, size, ucred, tv, label, label_len);
+ server_done(&s);
+}
diff --git a/src/fuzz/fuzz-journald.h b/src/fuzz/fuzz-journald.h
new file mode 100644
index 0000000000..77e3b0c064
--- /dev/null
+++ b/src/fuzz/fuzz-journald.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "journald-server.h"
+
+void dummy_server_init(Server *s, const uint8_t *buffer, size_t size);
+
+void fuzz_journald_processing_function(
+ const uint8_t *data,
+ size_t size,
+ void (*f)(Server *s, const char *buf, size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len)
+);
diff --git a/src/fuzz/fuzz-json.c b/src/fuzz/fuzz-json.c
new file mode 100644
index 0000000000..3aa9d089e6
--- /dev/null
+++ b/src/fuzz/fuzz-json.c
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fuzz.h"
+#include "json.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ char *out = NULL; /* out should be freed after g */
+ size_t out_size;
+ _cleanup_fclose_ FILE *f = NULL, *g = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ if (size == 0)
+ return 0;
+
+ f = fmemopen((char*) data, size, "re");
+ assert_se(f);
+
+ if (json_parse_file(f, NULL, &v, NULL, NULL) < 0)
+ return 0;
+
+ g = open_memstream(&out, &out_size);
+ assert_se(g);
+
+ json_variant_dump(v, 0, g, NULL);
+ json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, g, NULL);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-lldp.c b/src/fuzz/fuzz-lldp.c
new file mode 100644
index 0000000000..b9291d4783
--- /dev/null
+++ b/src/fuzz/fuzz-lldp.c
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "sd-event.h"
+#include "sd-lldp.h"
+
+#include "fd-util.h"
+#include "fuzz.h"
+#include "lldp-network.h"
+
+static int test_fd[2] = { -1, -1 };
+
+int lldp_network_bind_raw_socket(int ifindex) {
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
+ return -errno;
+
+ return test_fd[0];
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
+
+ assert_se(sd_event_new(&e) == 0);
+ assert_se(sd_lldp_new(&lldp) >= 0);
+ assert_se(sd_lldp_set_ifindex(lldp, 42) >= 0);
+ assert_se(sd_lldp_attach_event(lldp, e, 0) >= 0);
+ assert_se(sd_lldp_start(lldp) >= 0);
+
+ assert_se(write(test_fd[1], data, size) == (ssize_t) size);
+ assert_se(sd_event_run(e, 0) >= 0);
+
+ assert_se(sd_lldp_stop(lldp) >= 0);
+ assert_se(sd_lldp_detach_event(lldp) >= 0);
+ test_fd[1] = safe_close(test_fd[1]);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-main.c b/src/fuzz/fuzz-main.c
index d549dc95ff..d5c9984989 100644
--- a/src/fuzz/fuzz-main.c
+++ b/src/fuzz/fuzz-main.c
@@ -4,6 +4,7 @@
#include "log.h"
#include "fileio.h"
#include "fuzz.h"
+#include "tests.h"
/* This is a test driver for the systemd fuzzers that provides main function
* for regression testing outside of oss-fuzz (https://github.com/google/oss-fuzz)
@@ -11,14 +12,17 @@
* It reads files named on the command line and passes them one by one into the
* fuzzer that it is compiled into. */
+/* This one was borrowed from
+ * https://github.com/google/oss-fuzz/blob/646fca1b506b056db3a60d32c4a1a7398f171c94/infra/base-images/base-runner/bad_build_check#L19
+ */
+#define MIN_NUMBER_OF_RUNS 4
+
int main(int argc, char **argv) {
int i, r;
size_t size;
char *name;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
for (i = 1; i < argc; i++) {
_cleanup_free_ char *buf = NULL;
@@ -31,7 +35,9 @@ int main(int argc, char **argv) {
}
printf("%s... ", name);
fflush(stdout);
- (void) LLVMFuzzerTestOneInput((uint8_t*)buf, size);
+ for (int j = 0; j < MIN_NUMBER_OF_RUNS; j++)
+ if (LLVMFuzzerTestOneInput((uint8_t*)buf, size) == EXIT_TEST_SKIP)
+ return EXIT_TEST_SKIP;
printf("ok\n");
}
diff --git a/src/fuzz/fuzz-ndisc-rs.c b/src/fuzz/fuzz-ndisc-rs.c
new file mode 100644
index 0000000000..3a1e60fc5d
--- /dev/null
+++ b/src/fuzz/fuzz-ndisc-rs.c
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <arpa/inet.h>
+#include <netinet/icmp6.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "icmp6-util.h"
+#include "fuzz.h"
+#include "sd-ndisc.h"
+#include "socket-util.h"
+#include "ndisc-internal.h"
+
+static int test_fd[2] = { -1, -1 };
+
+int icmp6_bind_router_solicitation(int index) {
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
+ return test_fd[0];
+}
+
+int icmp6_bind_router_advertisement(int index) {
+ return -ENOSYS;
+}
+
+int icmp6_receive(int fd, void *iov_base, size_t iov_len,
+ struct in6_addr *dst, triple_timestamp *timestamp) {
+ assert_se(read(fd, iov_base, iov_len) == (ssize_t) iov_len);
+
+ if (timestamp)
+ triple_timestamp_get(timestamp);
+
+ return 0;
+}
+
+int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ struct ether_addr mac_addr = {
+ .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+ };
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
+
+ assert_se(sd_event_new(&e) >= 0);
+ assert_se(sd_ndisc_new(&nd) >= 0);
+ assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
+ assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
+ assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
+ assert_se(sd_ndisc_start(nd) >= 0);
+ assert_se(write(test_fd[1], data, size) == (ssize_t) size);
+ (void) sd_event_run(e, (uint64_t) -1);
+ assert_se(sd_ndisc_stop(nd) >= 0);
+ close(test_fd[1]);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-udev-rules.c b/src/fuzz/fuzz-udev-rules.c
new file mode 100644
index 0000000000..e894fa8d2d
--- /dev/null
+++ b/src/fuzz/fuzz-udev-rules.c
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "fuzz.h"
+#include "log.h"
+#include "mkdir.h"
+#include "missing.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "tests.h"
+#include "udev.h"
+
+static struct fakefs {
+ const char *target;
+ bool ignore_mount_error;
+ bool is_mounted;
+} fakefss[] = {
+ { "/sys", false, false },
+ { "/dev", false, false },
+ { "/run", false, false },
+ { "/etc", false, false },
+ { UDEVLIBEXECDIR "/rules.d", true, false },
+};
+
+static int setup_mount_namespace(void) {
+ static thread_local bool is_namespaced = false;
+
+ if (is_namespaced)
+ return 1;
+
+ if (unshare(CLONE_NEWNS) < 0)
+ return log_error_errno(errno, "Failed to call unshare(): %m");
+
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
+ return log_error_errno(errno, "Failed to mount / as private: %m");
+
+ is_namespaced = true;
+
+ return 1;
+}
+
+static int setup_fake_filesystems(const char *runtime_dir) {
+ for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) {
+ if (mount(runtime_dir, fakefss[i].target, NULL, MS_BIND, NULL) < 0) {
+ log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to mount %s: %m", fakefss[i].target);
+ if (!fakefss[i].ignore_mount_error)
+ return -errno;
+ } else
+ fakefss[i].is_mounted = true;
+ }
+
+ return 0;
+}
+
+static int cleanup_fake_filesystems(const char *runtime_dir) {
+ for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) {
+ if (!fakefss[i].is_mounted)
+ continue;
+
+ if (umount(fakefss[i].target) < 0) {
+ log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to umount %s: %m", fakefss[i].target);
+ if (!fakefss[i].ignore_mount_error)
+ return -errno;
+ } else
+ fakefss[i].is_mounted = false;
+ }
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
+ _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
+ FILE *f = NULL;
+
+ (void) setup_mount_namespace();
+
+ assert_se(runtime_dir = setup_fake_runtime_dir());
+
+ if (setup_fake_filesystems(runtime_dir) < 0) {
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ return EXIT_TEST_SKIP;
+#endif
+ }
+
+ if (!getenv("SYSTEMD_LOG_LEVEL")) {
+ log_set_max_level_realm(LOG_REALM_UDEV, LOG_CRIT);
+ log_set_max_level_realm(LOG_REALM_SYSTEMD, LOG_CRIT);
+ }
+
+ assert_se(mkdir_p("/etc/udev/rules.d", 0755) >= 0);
+ f = fopen("/etc/udev/rules.d/fuzz.rules", "we");
+ assert_se(f);
+ if (size != 0)
+ assert_se(fwrite(data, size, 1, f) == 1);
+ assert_se(fclose(f) == 0);
+
+ assert_se(udev_rules_new(&rules, RESOLVE_NAME_EARLY) == 0);
+
+ assert_se(cleanup_fake_filesystems(runtime_dir) >= 0);
+ return 0;
+}
diff --git a/src/fuzz/fuzz.h b/src/fuzz/fuzz.h
index e2bfab3168..1e56526259 100644
--- a/src/fuzz/fuzz.h
+++ b/src/fuzz/fuzz.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
#include <stddef.h>
#include <stdint.h>
diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build
index ad44778889..f628001a2a 100644
--- a/src/fuzz/meson.build
+++ b/src/fuzz/meson.build
@@ -1,6 +1,15 @@
# SPDX-License-Identifier: LGPL-2.1+
fuzzers += [
+ [['src/fuzz/fuzz-bus-message.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-catalog.c'],
+ [libjournal_core,
+ libshared],
+ []],
+
[['src/fuzz/fuzz-dns-packet.c',
dns_type_headers],
[libsystemd_resolve_core,
@@ -9,18 +18,94 @@ fuzzers += [
libgpg_error,
libm]],
+ [['src/fuzz/fuzz-dhcp6-client.c',
+ 'src/libsystemd-network/dhcp-identifier.h',
+ 'src/libsystemd-network/dhcp-identifier.c',
+ 'src/libsystemd-network/dhcp6-internal.h',
+ 'src/systemd/sd-dhcp6-client.h'],
+ [libshared,
+ libsystemd_network],
+ []],
+
[['src/fuzz/fuzz-dhcp-server.c'],
[libsystemd_network,
libshared],
[]],
+ [['src/fuzz/fuzz-lldp.c'],
+ [libshared,
+ libsystemd_network],
+ []],
+
+ [['src/fuzz/fuzz-ndisc-rs.c',
+ 'src/libsystemd-network/dhcp-identifier.h',
+ 'src/libsystemd-network/dhcp-identifier.c',
+ 'src/libsystemd-network/icmp6-util.h',
+ 'src/systemd/sd-dhcp6-client.h',
+ 'src/systemd/sd-ndisc.h'],
+ [libshared,
+ libsystemd_network],
+ []],
+
+ [['src/fuzz/fuzz-json.c'],
+ [libshared],
+ []],
+
[['src/fuzz/fuzz-unit-file.c'],
[libcore,
libshared],
[libmount]],
+ [['src/fuzz/fuzz-journald-audit.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journald-kmsg.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journald-native.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journald-native-fd.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journald-stream.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journald-syslog.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
[['src/fuzz/fuzz-journal-remote.c'],
[libsystemd_journal_remote,
libshared],
[]],
+
+ [['src/fuzz/fuzz-udev-rules.c'],
+ [libudev_core,
+ libudev_static,
+ libsystemd_network,
+ libshared],
+ [threads,
+ libacl]],
+
+ [['src/fuzz/fuzz-compress.c'],
+ [libshared],
+ []],
]
diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c
index 6af4c39137..35501b61f5 100644
--- a/src/getty-generator/getty-generator.c
+++ b/src/getty-generator/getty-generator.c
@@ -8,17 +8,18 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "generator.h"
#include "log.h"
#include "mkdir.h"
#include "path-util.h"
#include "process-util.h"
-#include "string-util.h"
+#include "strv.h"
#include "terminal-util.h"
#include "unit-name.h"
#include "util.h"
#include "virt.h"
-static const char *arg_dest = "/tmp";
+static const char *arg_dest = NULL;
static int add_symlink(const char *fservice, const char *tservice) {
char *from, *to;
@@ -98,42 +99,21 @@ static int verify_tty(const char *name) {
return 0;
}
-int main(int argc, char *argv[]) {
-
- static const char virtualization_consoles[] =
- "hvc0\0"
- "xvc0\0"
- "hvsi0\0"
- "sclp_line0\0"
- "ttysclp0\0"
- "3270!tty1\0";
-
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
_cleanup_free_ char *active = NULL;
const char *j;
int r;
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1)
- arg_dest = argv[1];
-
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
+ assert_se(arg_dest = dest);
if (detect_container() > 0) {
_cleanup_free_ char *container_ttys = NULL;
log_debug("Automatically adding console shell.");
- if (add_symlink("console-getty.service", "console-getty.service") < 0)
- return EXIT_FAILURE;
+ r = add_symlink("console-getty.service", "console-getty.service");
+ if (r < 0)
+ return r;
/* When $container_ttys is set for PID 1, spawn
* gettys on all ptys named therein. Note that despite
@@ -161,13 +141,14 @@ int main(int argc, char *argv[]) {
if (!t)
continue;
- if (add_container_getty(t) < 0)
- return EXIT_FAILURE;
+ r = add_container_getty(t);
+ if (r < 0)
+ return r;
}
}
/* Don't add any further magic if we are in a container */
- return EXIT_SUCCESS;
+ return 0;
}
if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
@@ -180,10 +161,8 @@ int main(int argc, char *argv[]) {
_cleanup_free_ char *tty = NULL;
tty = strndup(word, l);
- if (!tty) {
- log_oom();
- return EXIT_FAILURE;
- }
+ if (!tty)
+ return log_oom();
/* We assume that gettys on virtual terminals are
* started via manual configuration and do this magic
@@ -195,23 +174,33 @@ int main(int argc, char *argv[]) {
if (verify_tty(tty) < 0)
continue;
- if (add_serial_getty(tty) < 0)
- return EXIT_FAILURE;
+ r = add_serial_getty(tty);
+ if (r < 0)
+ return r;
}
}
/* Automatically add in a serial getty on the first
* virtualizer console */
- NULSTR_FOREACH(j, virtualization_consoles) {
- char *p;
+ FOREACH_STRING(j,
+ "hvc0",
+ "xvc0",
+ "hvsi0",
+ "sclp_line0",
+ "ttysclp0",
+ "3270!tty1") {
+ const char *p;
p = strjoina("/sys/class/tty/", j);
if (access(p, F_OK) < 0)
continue;
- if (add_serial_getty(j) < 0)
- return EXIT_FAILURE;
+ r = add_serial_getty(j);
+ if (r < 0)
+ return r;
}
- return EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index f72a55c20a..d9e29c47f3 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -5,13 +5,14 @@
#include <sys/statfs.h>
#include <unistd.h>
-#include "libudev.h"
+#include "sd-device.h"
#include "sd-id128.h"
#include "alloc-util.h"
#include "blkid-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
+#include "device-util.h"
#include "dirent-util.h"
#include "dissect-image.h"
#include "efivars.h"
@@ -22,7 +23,7 @@
#include "gpt.h"
#include "missing.h"
#include "mkdir.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
@@ -30,15 +31,15 @@
#include "specifier.h"
#include "stat-util.h"
#include "string-util.h"
-#include "udev-util.h"
+#include "strv.h"
#include "unit-name.h"
#include "util.h"
#include "virt.h"
-static const char *arg_dest = "/tmp";
+static const char *arg_dest = NULL;
static bool arg_enabled = true;
static bool arg_root_enabled = true;
-static bool arg_root_rw = false;
+static int arg_root_rw = -1;
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;
@@ -445,64 +446,96 @@ static int add_esp(DissectedPartition *p) {
}
#endif
+static int add_root_rw(DissectedPartition *p) {
+ const char *path;
+ int r;
+
+ assert(p);
+
+ if (in_initrd()) {
+ log_debug("In initrd, not generating drop-in for systemd-remount-fs.service.");
+ return 0;
+ }
+
+ if (arg_root_rw >= 0) {
+ log_debug("Parameter ro/rw specified on kernel command line, not generating drop-in for systemd-remount-fs.service.");
+ return 0;
+ }
+
+ if (!p->rw) {
+ log_debug("Root partition marked read-only in GPT partition table, not generating drop-in for systemd-remount-fs.service.");
+ return 0;
+ }
+
+ path = strjoina(arg_dest, "/systemd-remount-fs.service.d/50-remount-rw.conf");
+ (void) mkdir_parents(path, 0755);
+
+ r = write_string_file(path,
+ "# Automatically generated by systemd-gpt-generator\n\n"
+ "[Unit]\n"
+ "ConditionPathExists=\n\n" /* We need to turn off the ConditionPathExist= in the main unit file */
+ "[Service]\n"
+ "Environment=SYSTEMD_REMOUNT_ROOT_RW=1\n",
+ WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_NOFOLLOW);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write drop-in file %s: %m", path);
+
+ return 0;
+}
+
static int open_parent(dev_t devnum, int *ret) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
const char *name, *devtype, *node;
- struct udev_device *parent;
+ sd_device *parent;
dev_t pn;
- int fd;
+ int fd, r;
assert(ret);
- udev = udev_new();
- if (!udev)
- return log_oom();
-
- d = udev_device_new_from_devnum(udev, 'b', devnum);
- if (!d)
- return log_oom();
+ r = sd_device_new_from_devnum(&d, 'b', devnum);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to open device: %m");
- name = udev_device_get_devnode(d);
- if (!name)
- name = udev_device_get_syspath(d);
- if (!name) {
- log_debug("Device %u:%u does not have a name, ignoring.", major(devnum), minor(devnum));
- goto not_found;
+ if (sd_device_get_devname(d, &name) < 0) {
+ r = sd_device_get_syspath(d, &name);
+ if (r < 0) {
+ log_device_debug_errno(d, r, "Device %u:%u does not have a name, ignoring: %m", major(devnum), minor(devnum));
+ return 0;
+ }
}
- parent = udev_device_get_parent(d);
- if (!parent) {
- log_debug("%s: not a partitioned device, ignoring.", name);
- goto not_found;
+ r = sd_device_get_parent(d, &parent);
+ if (r < 0) {
+ log_device_debug_errno(d, r, "Not a partitioned device, ignoring: %m");
+ return 0;
}
/* Does it have a devtype? */
- devtype = udev_device_get_devtype(parent);
- if (!devtype) {
- log_debug("%s: parent doesn't have a device type, ignoring.", name);
- goto not_found;
+ r = sd_device_get_devtype(parent, &devtype);
+ if (r < 0) {
+ log_device_debug_errno(parent, r, "Parent doesn't have a device type, ignoring: %m");
+ return 0;
}
/* Is this a disk or a partition? We only care for disks... */
if (!streq(devtype, "disk")) {
- log_debug("%s: parent isn't a raw disk, ignoring.", name);
- goto not_found;
+ log_device_debug(parent, "Parent isn't a raw disk, ignoring.");
+ return 0;
}
/* Does it have a device node? */
- node = udev_device_get_devnode(parent);
- if (!node) {
- log_debug("%s: parent device does not have device node, ignoring.", name);
- goto not_found;
+ r = sd_device_get_devname(parent, &node);
+ if (r < 0) {
+ log_device_debug_errno(parent, r, "Parent device does not have device node, ignoring: %m");
+ return 0;
}
- log_debug("%s: root device %s.", name, node);
+ log_device_debug(d, "Root device %s.", node);
- pn = udev_device_get_devnum(parent);
- if (major(pn) == 0) {
- log_debug("%s: parent device is not a proper block device, ignoring.", name);
- goto not_found;
+ r = sd_device_get_devnum(parent, &pn);
+ if (r < 0) {
+ log_device_debug_errno(parent, r, "Parent device is not a proper block device, ignoring: %m");
+ return 0;
}
fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY);
@@ -511,14 +544,9 @@ static int open_parent(dev_t devnum, int *ret) {
*ret = fd;
return 1;
-
-not_found:
- *ret = -1;
- return 0;
}
static int enumerate_partitions(dev_t devnum) {
-
_cleanup_close_ int fd = -1;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
int r, k;
@@ -527,7 +555,7 @@ static int enumerate_partitions(dev_t devnum) {
if (r <= 0)
return r;
- r = dissect_image(fd, NULL, 0, DISSECT_IMAGE_GPT_ONLY, &m);
+ r = dissect_image(fd, NULL, 0, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
if (r == -ENOPKG) {
log_debug_errno(r, "No suitable partition table found, ignoring.");
return 0;
@@ -559,6 +587,12 @@ static int enumerate_partitions(dev_t devnum) {
r = k;
}
+ if (m->partitions[PARTITION_ROOT].found) {
+ k = add_root_rw(m->partitions + PARTITION_ROOT);
+ if (k < 0)
+ r = k;
+ }
+
return r;
}
@@ -567,15 +601,16 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(key);
- if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto")) {
+ if (proc_cmdline_key_streq(key, "systemd.gpt_auto") ||
+ proc_cmdline_key_streq(key, "rd.systemd.gpt_auto")) {
r = value ? parse_boolean(value) : 1;
if (r < 0)
- log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value);
+ log_warning_errno(r, "Failed to parse gpt-auto switch \"%s\", ignoring: %m", value);
else
arg_enabled = r;
- } else if (streq(key, "root")) {
+ } else if (proc_cmdline_key_streq(key, "root")) {
if (proc_cmdline_value_missing(key, value))
return 0;
@@ -585,7 +620,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
arg_root_enabled = streq(value, "gpt-auto");
- } else if (streq(key, "roothash")) {
+ } else if (proc_cmdline_key_streq(key, "roothash")) {
if (proc_cmdline_value_missing(key, value))
return 0;
@@ -594,9 +629,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
arg_root_enabled = false;
- } else if (streq(key, "rw") && !value)
+ } else if (proc_cmdline_key_streq(key, "rw") && !value)
arg_root_rw = true;
- else if (streq(key, "ro") && !value)
+ else if (proc_cmdline_key_streq(key, "ro") && !value)
arg_root_rw = false;
return 0;
@@ -648,7 +683,7 @@ static int add_root_mount(void) {
"/dev/gpt-auto-root",
in_initrd() ? "/sysroot" : "/",
NULL,
- arg_root_rw,
+ arg_root_rw > 0,
NULL,
"Root Partition",
in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
@@ -677,27 +712,14 @@ static int add_mounts(void) {
return enumerate_partitions(devno);
}
-int main(int argc, char *argv[]) {
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
int r, k;
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1)
- arg_dest = argv[3];
-
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
+ assert_se(arg_dest = dest_late);
if (detect_container() > 0) {
log_debug("In a container, exiting.");
- return EXIT_SUCCESS;
+ return 0;
}
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
@@ -706,19 +728,19 @@ int main(int argc, char *argv[]) {
if (!arg_enabled) {
log_debug("Disabled, exiting.");
- return EXIT_SUCCESS;
+ return 0;
}
if (arg_root_enabled)
r = add_root_mount();
- else
- r = 0;
if (!in_initrd()) {
k = add_mounts();
- if (k < 0)
+ if (r >= 0)
r = k;
}
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c
index 4b79e3def4..8b127ca85c 100644
--- a/src/hibernate-resume/hibernate-resume-generator.c
+++ b/src/hibernate-resume/hibernate-resume-generator.c
@@ -5,7 +5,9 @@
#include "alloc-util.h"
#include "fstab-util.h"
+#include "generator.h"
#include "log.h"
+#include "main-func.h"
#include "mkdir.h"
#include "proc-cmdline.h"
#include "special.h"
@@ -15,6 +17,9 @@
static const char *arg_dest = "/tmp";
static char *arg_resume_device = NULL;
+static bool arg_noresume = false;
+
+STATIC_DESTRUCTOR_REGISTER(arg_resume_device, freep);
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
@@ -28,8 +33,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (!s)
return log_oom();
- free(arg_resume_device);
- arg_resume_device = s;
+ free_and_replace(arg_resume_device, s);
+
+ } else if (streq(key, "noresume")) {
+ if (value) {
+ log_warning("\"noresume\" kernel command line switch specified with an argument, ignoring.");
+ return 0;
+ }
+
+ arg_noresume = true;
}
return 0;
@@ -57,34 +69,34 @@ static int process_resume(void) {
return 0;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r = 0;
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
- }
+ log_setup_generator();
+
+ if (argc > 1 && argc != 4)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "This program takes three or no arguments.");
if (argc > 1)
arg_dest = argv[1];
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
/* Don't even consider resuming outside of initramfs. */
- if (!in_initrd())
- return EXIT_SUCCESS;
+ if (!in_initrd()) {
+ log_debug("Not running in an initrd, quitting.");
+ return 0;
+ }
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
- r = process_resume();
- free(arg_resume_device);
+ if (arg_noresume) {
+ log_notice("Found \"noresume\" on the kernel command line, quitting.");
+ return 0;
+ }
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return process_resume();
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/hibernate-resume/hibernate-resume.c b/src/hibernate-resume/hibernate-resume.c
index 397757fa46..17e7cd1a00 100644
--- a/src/hibernate-resume/hibernate-resume.c
+++ b/src/hibernate-resume/hibernate-resume.c
@@ -20,9 +20,7 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
@@ -47,7 +45,7 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- r = write_string_file("/sys/power/resume", major_minor, WRITE_STRING_FILE_CREATE);
+ r = write_string_file("/sys/power/resume", major_minor, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0) {
log_error_errno(r, "Failed to write '%s' to /sys/power/resume: %m", major_minor);
return EXIT_FAILURE;
diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index 8587f5c59f..7f9bb49e0c 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -14,8 +14,9 @@
#include "bus-error.h"
#include "bus-util.h"
#include "hostname-util.h"
+#include "main-func.h"
+#include "pretty-print.h"
#include "spawn-polkit-agent.h"
-#include "terminal-util.h"
#include "util.h"
#include "verbs.h"
@@ -227,8 +228,9 @@ static int set_simple_string(sd_bus *bus, const char *method, const char *value)
&error, NULL,
"sb", value, arg_ask_password);
if (r < 0)
- log_error("Could not set property: %s", bus_error_message(&error, -r));
- return r;
+ return log_error_errno(r, "Could not set property: %s", bus_error_message(&error, -r));
+
+ return 0;
}
static int set_hostname(int argc, char **argv, void *userdata) {
@@ -300,6 +302,13 @@ static int set_location(int argc, char **argv, void *userdata) {
}
static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("hostnamectl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] COMMAND ...\n\n"
"Query or change system hostname.\n\n"
" -h --help Show this help\n"
@@ -317,7 +326,10 @@ static int help(void) {
" set-chassis NAME Set chassis type for host\n"
" set-deployment NAME Set deployment environment for host\n"
" set-location NAME Set location for host\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
@@ -415,7 +427,7 @@ static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, bus);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
@@ -425,16 +437,13 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
- if (r < 0) {
- log_error_errno(r, "Failed to create bus connection: %m");
- goto finish;
- }
-
- r = hostnamectl_main(bus, argc, argv);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return hostnamectl_main(bus, argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index ee51002ece..7777450c35 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -6,15 +6,24 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "bus-common-errors.h"
#include "bus-util.h"
#include "def.h"
+#include "env-file-label.h"
+#include "env-file.h"
#include "env-util.h"
#include "fileio-label.h"
+#include "fileio.h"
#include "hostname-util.h"
+#include "id128-util.h"
+#include "main-func.h"
+#include "missing_capability.h"
+#include "nscd-flush.h"
#include "os-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "selinux-util.h"
+#include "signal-util.h"
#include "strv.h"
#include "user-util.h"
#include "util.h"
@@ -42,6 +51,8 @@ enum {
typedef struct Context {
char *data[_PROP_MAX];
Hashmap *polkit_registry;
+ sd_id128_t uuid;
+ bool has_uuid;
} Context;
static void context_reset(Context *c) {
@@ -53,7 +64,7 @@ static void context_reset(Context *c) {
c->data[p] = mfree(c->data[p]);
}
-static void context_free(Context *c) {
+static void context_clear(Context *c) {
assert(c);
context_reset(c);
@@ -84,13 +95,12 @@ static int context_read_data(Context *c) {
if (r < 0 && r != -ENOENT)
return r;
- r = parse_env_file(NULL, "/etc/machine-info", NEWLINE,
+ r = parse_env_file(NULL, "/etc/machine-info",
"PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
"ICON_NAME", &c->data[PROP_ICON_NAME],
"CHASSIS", &c->data[PROP_CHASSIS],
"DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
- "LOCATION", &c->data[PROP_LOCATION],
- NULL);
+ "LOCATION", &c->data[PROP_LOCATION]);
if (r < 0 && r != -ENOENT)
return r;
@@ -102,6 +112,15 @@ static int context_read_data(Context *c) {
if (r < 0 && r != -ENOENT)
return r;
+ r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &c->uuid);
+ if (r < 0)
+ log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to read product UUID, ignoring: %m");
+ else if (sd_id128_is_null(c->uuid) || sd_id128_is_allf(c->uuid))
+ log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(c->uuid));
+ else
+ c->has_uuid = true;
+
return 0;
}
@@ -272,6 +291,8 @@ static int context_update_kernel_hostname(Context *c) {
if (sethostname_idempotent(hn) < 0)
return -errno;
+ (void) nscd_flush_cache(STRV_MAKE("hosts"));
+
return 0;
}
@@ -304,7 +325,7 @@ static int context_write_data_machine_info(Context *c) {
assert(c);
- r = load_env_file(NULL, "/etc/machine-info", NULL, &l);
+ r = load_env_file(NULL, "/etc/machine-info", &l);
if (r < 0 && r != -ENOENT)
return r;
@@ -597,6 +618,46 @@ static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *
return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error);
}
+static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Context *c = userdata;
+ int interactive, r;
+
+ assert(m);
+ assert(c);
+
+ if (!c->has_uuid)
+ return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID, "Failed to read product UUID from /sys/class/dmi/id/product_uuid");
+
+ r = sd_bus_message_read(m, "b", &interactive);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.hostname1.get-product-uuid",
+ NULL,
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_array(reply, 'y', &c->uuid, sizeof(c->uuid));
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
static const sd_bus_vtable hostname_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -619,6 +680,7 @@ static const sd_bus_vtable hostname_vtable[] = {
SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetProductUUID", "b", "ay", method_get_product_uuid, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
@@ -651,51 +713,51 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
return 0;
}
-int main(int argc, char *argv[]) {
- Context context = {};
+static int run(int argc, char *argv[]) {
+ _cleanup_(context_clear) Context context = {};
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
mac_selinux_init();
if (argc != 1) {
log_error("This program takes no arguments.");
- r = -EINVAL;
- goto finish;
+ return -EINVAL;
}
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
+
r = sd_event_default(&event);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate event loop: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
+
+ (void) sd_event_set_watchdog(event, true);
- sd_event_set_watchdog(event, true);
+ r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to install SIGINT handler: %m");
+
+ r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to install SIGTERM handler: %m");
r = connect_bus(&context, event, &bus);
if (r < 0)
- goto finish;
+ return r;
r = context_read_data(&context);
- if (r < 0) {
- log_error_errno(r, "Failed to read hostname and machine information: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read hostname and machine information: %m");
r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- goto finish;
- }
-
-finish:
- context_free(&context);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/hostname/org.freedesktop.hostname1.policy b/src/hostname/org.freedesktop.hostname1.policy
index e1519ce86b..5bedc0b694 100644
--- a/src/hostname/org.freedesktop.hostname1.policy
+++ b/src/hostname/org.freedesktop.hostname1.policy
@@ -47,4 +47,14 @@
</defaults>
</action>
+ <action id="org.freedesktop.hostname1.get-product-uuid">
+ <description gettext-domain="systemd">Get product UUID</description>
+ <message gettext-domain="systemd">Authentication is required to get product UUID.</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/hwdb/hwdb.c b/src/hwdb/hwdb.c
index 317cad8a67..b069a3eb8a 100644
--- a/src/hwdb/hwdb.c
+++ b/src/hwdb/hwdb.c
@@ -1,693 +1,38 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <ctype.h>
#include <getopt.h>
-#include <stdlib.h>
-#include <string.h>
+
+#include "sd-hwdb.h"
#include "alloc-util.h"
-#include "conf-files.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "fs-util.h"
-#include "hwdb-internal.h"
#include "hwdb-util.h"
-#include "label.h"
-#include "mkdir.h"
-#include "path-util.h"
+#include "main-func.h"
+#include "pretty-print.h"
#include "selinux-util.h"
-#include "strbuf.h"
-#include "string-util.h"
-#include "strv.h"
#include "util.h"
#include "verbs.h"
-/*
- * Generic udev properties, key-value database based on modalias strings.
- * Uses a Patricia/radix trie to index all matches for efficient lookup.
- */
-
-static const char *arg_hwdb_bin_dir = "/etc/udev";
-static const char *arg_root = "";
-static bool arg_strict;
-
-static const char * const conf_file_dirs[] = {
- "/etc/udev/hwdb.d",
- UDEVLIBEXECDIR "/hwdb.d",
- NULL
-};
-
-/* in-memory trie objects */
-struct trie {
- struct trie_node *root;
- struct strbuf *strings;
-
- size_t nodes_count;
- size_t children_count;
- size_t values_count;
-};
-
-struct trie_node {
- /* prefix, common part for all children of this node */
- size_t prefix_off;
-
- /* sorted array of pointers to children nodes */
- struct trie_child_entry *children;
- uint8_t children_count;
-
- /* sorted array of key-value pairs */
- struct trie_value_entry *values;
- size_t values_count;
-};
-
-/* children array item with char (0-255) index */
-struct trie_child_entry {
- uint8_t c;
- struct trie_node *child;
-};
-
-/* value array item with key-value pairs */
-struct trie_value_entry {
- size_t key_off;
- size_t value_off;
- size_t filename_off;
- uint32_t line_number;
- uint16_t file_priority;
-};
-
-static int trie_children_cmp(const void *v1, const void *v2) {
- const struct trie_child_entry *n1 = v1;
- const struct trie_child_entry *n2 = v2;
-
- return n1->c - n2->c;
-}
-
-static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) {
- struct trie_child_entry *child;
-
- /* extend array, add new entry, sort for bisection */
- child = reallocarray(node->children, node->children_count + 1, sizeof(struct trie_child_entry));
- if (!child)
- return -ENOMEM;
-
- node->children = child;
- trie->children_count++;
- node->children[node->children_count].c = c;
- node->children[node->children_count].child = node_child;
- node->children_count++;
- qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
- trie->nodes_count++;
-
- return 0;
-}
-
-static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) {
- struct trie_child_entry *child;
- struct trie_child_entry search;
-
- search.c = c;
- child = bsearch_safe(&search, node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
- if (child)
- return child->child;
- return NULL;
-}
-
-static void trie_node_cleanup(struct trie_node *node) {
- size_t i;
-
- for (i = 0; i < node->children_count; i++)
- trie_node_cleanup(node->children[i].child);
- free(node->children);
- free(node->values);
- free(node);
-}
-
-static void trie_free(struct trie *trie) {
- if (!trie)
- return;
-
- if (trie->root)
- trie_node_cleanup(trie->root);
-
- strbuf_cleanup(trie->strings);
- free(trie);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct trie*, trie_free);
-
-static int trie_values_cmp(const void *v1, const void *v2, void *arg) {
- const struct trie_value_entry *val1 = v1;
- const struct trie_value_entry *val2 = v2;
- struct trie *trie = arg;
-
- return strcmp(trie->strings->buf + val1->key_off,
- trie->strings->buf + val2->key_off);
-}
-
-static int trie_node_add_value(struct trie *trie, struct trie_node *node,
- const char *key, const char *value,
- const char *filename, uint16_t file_priority, uint32_t line_number) {
- ssize_t k, v, fn;
- struct trie_value_entry *val;
-
- k = strbuf_add_string(trie->strings, key, strlen(key));
- if (k < 0)
- return k;
- v = strbuf_add_string(trie->strings, value, strlen(value));
- if (v < 0)
- return v;
- fn = strbuf_add_string(trie->strings, filename, strlen(filename));
- if (fn < 0)
- return fn;
-
- if (node->values_count) {
- struct trie_value_entry search = {
- .key_off = k,
- .value_off = v,
- };
-
- val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
- if (val) {
- /* At this point we have 2 identical properties on the same match-string.
- * Since we process files in order, we just replace the previous value.
- */
- val->value_off = v;
- val->filename_off = fn;
- val->file_priority = file_priority;
- val->line_number = line_number;
- return 0;
- }
- }
-
- /* extend array, add new entry, sort for bisection */
- val = reallocarray(node->values, node->values_count + 1, sizeof(struct trie_value_entry));
- if (!val)
- return -ENOMEM;
- trie->values_count++;
- node->values = val;
- node->values[node->values_count].key_off = k;
- node->values[node->values_count].value_off = v;
- node->values[node->values_count].filename_off = fn;
- node->values[node->values_count].file_priority = file_priority;
- node->values[node->values_count].line_number = line_number;
- node->values_count++;
- qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
- return 0;
-}
-
-static int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
- const char *key, const char *value,
- const char *filename, uint16_t file_priority, uint32_t line_number) {
- size_t i = 0;
- int r = 0;
-
- for (;;) {
- size_t p;
- uint8_t c;
- struct trie_node *child;
-
- for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
- _cleanup_free_ char *s = NULL;
- ssize_t off;
- _cleanup_free_ struct trie_node *new_child = NULL;
-
- if (c == search[i + p])
- continue;
-
- /* split node */
- new_child = new0(struct trie_node, 1);
- if (!new_child)
- return -ENOMEM;
-
- /* move values from parent to child */
- new_child->prefix_off = node->prefix_off + p+1;
- new_child->children = node->children;
- new_child->children_count = node->children_count;
- new_child->values = node->values;
- new_child->values_count = node->values_count;
-
- /* update parent; use strdup() because the source gets realloc()d */
- s = strndup(trie->strings->buf + node->prefix_off, p);
- if (!s)
- return -ENOMEM;
-
- off = strbuf_add_string(trie->strings, s, p);
- if (off < 0)
- return off;
-
- node->prefix_off = off;
- node->children = NULL;
- node->children_count = 0;
- node->values = NULL;
- node->values_count = 0;
- r = node_add_child(trie, node, new_child, c);
- if (r < 0)
- return r;
-
- new_child = NULL; /* avoid cleanup */
- break;
- }
- i += p;
-
- c = search[i];
- if (c == '\0')
- return trie_node_add_value(trie, node, key, value, filename, file_priority, line_number);
-
- child = node_lookup(node, c);
- if (!child) {
- ssize_t off;
-
- /* new child */
- child = new0(struct trie_node, 1);
- if (!child)
- return -ENOMEM;
-
- off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
- if (off < 0) {
- free(child);
- return off;
- }
-
- child->prefix_off = off;
- r = node_add_child(trie, node, child, c);
- if (r < 0) {
- free(child);
- return r;
- }
-
- return trie_node_add_value(trie, child, key, value, filename, file_priority, line_number);
- }
-
- node = child;
- i++;
- }
-}
-
-struct trie_f {
- FILE *f;
- struct trie *trie;
- uint64_t strings_off;
-
- uint64_t nodes_count;
- uint64_t children_count;
- uint64_t values_count;
-};
-
-/* calculate the storage space for the nodes, children arrays, value arrays */
-static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) {
- uint64_t i;
+static const char *arg_hwdb_bin_dir = NULL;
+static const char *arg_root = NULL;
+static bool arg_strict = false;
- for (i = 0; i < node->children_count; i++)
- trie_store_nodes_size(trie, node->children[i].child);
-
- trie->strings_off += sizeof(struct trie_node_f);
- for (i = 0; i < node->children_count; i++)
- trie->strings_off += sizeof(struct trie_child_entry_f);
- for (i = 0; i < node->values_count; i++)
- trie->strings_off += sizeof(struct trie_value_entry2_f);
+static int verb_query(int argc, char *argv[], void *userdata) {
+ return hwdb_query(argv[1]);
}
-static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
- uint64_t i;
- struct trie_node_f n = {
- .prefix_off = htole64(trie->strings_off + node->prefix_off),
- .children_count = node->children_count,
- .values_count = htole64(node->values_count),
- };
- _cleanup_free_ struct trie_child_entry_f *children = NULL;
- int64_t node_off;
-
- if (node->children_count) {
- children = new(struct trie_child_entry_f, node->children_count);
- if (!children)
- return -ENOMEM;
- }
-
- /* post-order recursion */
- for (i = 0; i < node->children_count; i++) {
- int64_t child_off;
-
- child_off = trie_store_nodes(trie, node->children[i].child);
- if (child_off < 0)
- return child_off;
-
- children[i] = (struct trie_child_entry_f) {
- .c = node->children[i].c,
- .child_off = htole64(child_off),
- };
- }
-
- /* write node */
- node_off = ftello(trie->f);
- fwrite(&n, sizeof(struct trie_node_f), 1, trie->f);
- trie->nodes_count++;
-
- /* append children array */
- if (node->children_count) {
- fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
- trie->children_count += node->children_count;
- }
-
- /* append values array */
- for (i = 0; i < node->values_count; i++) {
- struct trie_value_entry2_f v = {
- .key_off = htole64(trie->strings_off + node->values[i].key_off),
- .value_off = htole64(trie->strings_off + node->values[i].value_off),
- .filename_off = htole64(trie->strings_off + node->values[i].filename_off),
- .line_number = htole32(node->values[i].line_number),
- .file_priority = htole16(node->values[i].file_priority),
- };
-
- fwrite(&v, sizeof(struct trie_value_entry2_f), 1, trie->f);
- }
- trie->values_count += node->values_count;
-
- return node_off;
+static int verb_update(int argc, char *argv[], void *userdata) {
+ return hwdb_update(arg_root, arg_hwdb_bin_dir, arg_strict, false);
}
-static int trie_store(struct trie *trie, const char *filename) {
- struct trie_f t = {
- .trie = trie,
- };
- _cleanup_free_ char *filename_tmp = NULL;
- int64_t pos;
- int64_t root_off;
- int64_t size;
- struct trie_header_f h = {
- .signature = HWDB_SIG,
- .tool_version = htole64(atoi(PACKAGE_VERSION)),
- .header_size = htole64(sizeof(struct trie_header_f)),
- .node_size = htole64(sizeof(struct trie_node_f)),
- .child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
- .value_entry_size = htole64(sizeof(struct trie_value_entry2_f)),
- };
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
int r;
- /* calculate size of header, nodes, children entries, value entries */
- t.strings_off = sizeof(struct trie_header_f);
- trie_store_nodes_size(&t, trie->root);
-
- r = fopen_temporary(filename, &t.f, &filename_tmp);
+ r = terminal_urlify_man("systemd-hwdb", "8", &link);
if (r < 0)
- return r;
- fchmod(fileno(t.f), 0444);
+ return log_oom();
- /* write nodes */
- if (fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET) < 0)
- goto error_fclose;
-
- root_off = trie_store_nodes(&t, trie->root);
- h.nodes_root_off = htole64(root_off);
- pos = ftello(t.f);
- h.nodes_len = htole64(pos - sizeof(struct trie_header_f));
-
- /* write string buffer */
- fwrite(trie->strings->buf, trie->strings->len, 1, t.f);
- h.strings_len = htole64(trie->strings->len);
-
- /* write header */
- size = ftello(t.f);
- h.file_size = htole64(size);
- if (fseeko(t.f, 0, SEEK_SET) < 0)
- goto error_fclose;
- fwrite(&h, sizeof(struct trie_header_f), 1, t.f);
-
- if (ferror(t.f))
- goto error_fclose;
- if (fflush(t.f) < 0)
- goto error_fclose;
- if (fsync(fileno(t.f)) < 0)
- goto error_fclose;
- if (rename(filename_tmp, filename) < 0)
- goto error_fclose;
-
- /* write succeeded */
- fclose(t.f);
-
- log_debug("=== trie on-disk ===");
- log_debug("size: %8"PRIi64" bytes", size);
- log_debug("header: %8zu bytes", sizeof(struct trie_header_f));
- log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")",
- t.nodes_count * sizeof(struct trie_node_f), t.nodes_count);
- log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")",
- t.children_count * sizeof(struct trie_child_entry_f), t.children_count);
- log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")",
- t.values_count * sizeof(struct trie_value_entry2_f), t.values_count);
- log_debug("string store: %8zu bytes", trie->strings->len);
- log_debug("strings start: %8"PRIu64, t.strings_off);
- return 0;
-
- error_fclose:
- r = -errno;
- fclose(t.f);
- unlink(filename_tmp);
- return r;
-}
-
-static int insert_data(struct trie *trie, char **match_list, char *line,
- const char *filename, uint16_t file_priority, uint32_t line_number) {
- char *value, **entry;
-
- assert(line[0] == ' ');
-
- value = strchr(line, '=');
- if (!value)
- return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
- "Key-value pair expected but got \"%s\", ignoring", line);
-
- value[0] = '\0';
- value++;
-
- /* Replace multiple leading spaces by a single space */
- while (isblank(line[0]) && isblank(line[1]))
- line++;
-
- if (isempty(line + 1) || isempty(value))
- return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
- "Empty %s in \"%s=%s\", ignoring",
- isempty(line + 1) ? "key" : "value",
- line, value);
-
- STRV_FOREACH(entry, match_list)
- trie_insert(trie, trie->root, *entry, line, value, filename, file_priority, line_number);
-
- return 0;
-}
-
-static int import_file(struct trie *trie, const char *filename, uint16_t file_priority) {
- enum {
- HW_NONE,
- HW_MATCH,
- HW_DATA,
- } state = HW_NONE;
- _cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
- _cleanup_strv_free_ char **match_list = NULL;
- uint32_t line_number = 0;
- char *match = NULL;
- int r = 0, err;
-
- f = fopen(filename, "re");
- if (!f)
- return -errno;
-
- while (fgets(line, sizeof(line), f)) {
- size_t len;
- char *pos;
-
- ++line_number;
-
- /* comment line */
- if (line[0] == '#')
- continue;
-
- /* strip trailing comment */
- pos = strchr(line, '#');
- if (pos)
- pos[0] = '\0';
-
- /* strip trailing whitespace */
- len = strlen(line);
- while (len > 0 && isspace(line[len-1]))
- len--;
- line[len] = '\0';
-
- switch (state) {
- case HW_NONE:
- if (len == 0)
- 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;
- break;
- }
-
- /* start of record, first match */
- state = HW_MATCH;
-
- match = strdup(line);
- if (!match)
- return -ENOMEM;
-
- err = strv_consume(&match_list, match);
- if (err < 0)
- return err;
-
- break;
-
- case HW_MATCH:
- if (len == 0) {
- log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
- "Property expected, ignoring record with no properties");
- r = -EINVAL;
- state = HW_NONE;
- strv_clear(match_list);
- break;
- }
-
- if (line[0] != ' ') {
- /* another match */
- match = strdup(line);
- if (!match)
- return -ENOMEM;
-
- err = strv_consume(&match_list, match);
- if (err < 0)
- return err;
-
- break;
- }
-
- /* first data */
- state = HW_DATA;
- err = insert_data(trie, match_list, line, filename, file_priority, line_number);
- if (err < 0)
- r = err;
- break;
-
- case HW_DATA:
- if (len == 0) {
- /* end of record */
- state = HW_NONE;
- strv_clear(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;
- state = HW_NONE;
- strv_clear(match_list);
- break;
- }
-
- err = insert_data(trie, match_list, line, filename, file_priority, line_number);
- if (err < 0)
- r = err;
- break;
- };
- }
-
- if (state == HW_MATCH)
- log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
- "Property expected, ignoring record with no properties");
-
- return r;
-}
-
-static int hwdb_query(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
- const char *key, *value;
- const char *modalias;
- int r;
-
- assert(argc >= 2);
- assert(argv);
-
- modalias = argv[1];
-
- r = sd_hwdb_new(&hwdb);
- if (r < 0)
- return r;
-
- SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value)
- printf("%s=%s\n", key, value);
-
- return 0;
-}
-
-static int hwdb_update(int argc, char *argv[], void *userdata) {
- _cleanup_free_ char *hwdb_bin = NULL;
- _cleanup_(trie_freep) struct trie *trie = NULL;
- _cleanup_strv_free_ char **files = NULL;
- char **f;
- uint16_t file_priority = 1;
- int r = 0, err;
-
- trie = new0(struct trie, 1);
- if (!trie)
- return -ENOMEM;
-
- /* string store */
- trie->strings = strbuf_new();
- if (!trie->strings)
- return -ENOMEM;
-
- /* index */
- trie->root = new0(struct trie_node, 1);
- if (!trie->root)
- return -ENOMEM;
-
- trie->nodes_count++;
-
- err = conf_files_list_strv(&files, ".hwdb", arg_root, 0, conf_file_dirs);
- if (err < 0)
- return log_error_errno(err, "Failed to enumerate hwdb files: %m");
-
- STRV_FOREACH(f, files) {
- log_debug("Reading file \"%s\"", *f);
- err = import_file(trie, *f, file_priority++);
- if (err < 0 && arg_strict)
- r = err;
- }
-
- strbuf_complete(trie->strings);
-
- log_debug("=== trie in-memory ===");
- log_debug("nodes: %8zu bytes (%8zu)",
- trie->nodes_count * sizeof(struct trie_node), trie->nodes_count);
- log_debug("children arrays: %8zu bytes (%8zu)",
- trie->children_count * sizeof(struct trie_child_entry), trie->children_count);
- log_debug("values arrays: %8zu bytes (%8zu)",
- trie->values_count * sizeof(struct trie_value_entry), trie->values_count);
- log_debug("strings: %8zu bytes",
- trie->strings->len);
- log_debug("strings incoming: %8zu bytes (%8zu)",
- trie->strings->in_len, trie->strings->in_count);
- log_debug("strings dedup'ed: %8zu bytes (%8zu)",
- trie->strings->dedup_len, trie->strings->dedup_count);
-
- hwdb_bin = path_join(arg_root, arg_hwdb_bin_dir, "hwdb.bin");
- if (!hwdb_bin)
- return -ENOMEM;
-
- mkdir_parents_label(hwdb_bin, 0755);
- err = trie_store(trie, hwdb_bin);
- if (err < 0)
- return log_error_errno(err, "Failure writing database %s: %m", hwdb_bin);
-
- err = label_fix(hwdb_bin, 0);
- if (err < 0)
- return err;
-
- return r;
-}
-
-static void help(void) {
- printf("Usage: %s OPTIONS COMMAND\n\n"
+ printf("%s OPTIONS COMMAND\n\n"
"Update or query the hardware database.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
@@ -696,8 +41,13 @@ static void help(void) {
" -r --root=PATH Alternative root path in the filesystem\n\n"
"Commands:\n"
" update Update the hwdb database\n"
- " query MODALIAS Query database and print result\n",
- program_invocation_short_name);
+ " query MODALIAS Query database and print result\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -720,12 +70,11 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "ust:r:h", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "ust:r:h", options, NULL)) >= 0)
switch(c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -748,22 +97,21 @@ static int parse_argv(int argc, char *argv[]) {
default:
assert_not_reached("Unknown option");
}
- }
return 1;
}
static int hwdb_main(int argc, char *argv[]) {
static const Verb verbs[] = {
- { "update", 1, 1, 0, hwdb_update },
- { "query", 2, 2, 0, hwdb_query },
+ { "update", 1, 1, 0, verb_update },
+ { "query", 2, 2, 0, verb_query },
{},
};
return dispatch_verb(argc, argv, verbs, NULL);
}
-int main (int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
log_parse_environment();
@@ -771,12 +119,11 @@ int main (int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
mac_selinux_init();
- r = hwdb_main(argc, argv);
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return hwdb_main(argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/id128/id128.c b/src/id128/id128.c
new file mode 100644
index 0000000000..06049e7ba2
--- /dev/null
+++ b/src/id128/id128.c
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "id128-print.h"
+#include "main-func.h"
+#include "pretty-print.h"
+#include "util.h"
+#include "verbs.h"
+
+static bool arg_pretty = false;
+static sd_id128_t arg_app = {};
+
+static int verb_new(int argc, char **argv, void *userdata) {
+ return id128_print_new(arg_pretty);
+}
+
+static int verb_machine_id(int argc, char **argv, void *userdata) {
+ sd_id128_t id;
+ int r;
+
+ if (sd_id128_is_null(arg_app))
+ r = sd_id128_get_machine(&id);
+ else
+ r = sd_id128_get_machine_app_specific(arg_app, &id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get %smachine-ID: %m",
+ sd_id128_is_null(arg_app) ? "" : "app-specific ");
+
+ return id128_pretty_print(id, arg_pretty);
+}
+
+static int verb_boot_id(int argc, char **argv, void *userdata) {
+ sd_id128_t id;
+ int r;
+
+ if (sd_id128_is_null(arg_app))
+ r = sd_id128_get_boot(&id);
+ else
+ r = sd_id128_get_boot_app_specific(arg_app, &id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get %sboot-ID: %m",
+ sd_id128_is_null(arg_app) ? "" : "app-specific ");
+
+ return id128_pretty_print(id, arg_pretty);
+}
+
+static int verb_invocation_id(int argc, char **argv, void *userdata) {
+ sd_id128_t id;
+ int r;
+
+ if (!sd_id128_is_null(arg_app))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Verb \"invocation-id\" cannot be combined with --app-specific=.");
+
+ r = sd_id128_get_invocation(&id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get invocation-ID: %m");
+
+ return id128_pretty_print(id, arg_pretty);
+}
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-id128", "1", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+ "Generate and print id128 strings.\n\n"
+ " -h --help Show this help\n"
+ " -p --pretty Generate samples of program code\n"
+ " -a --app-specific=ID Generate app-specific IDs\n"
+ "\nCommands:\n"
+ " new Generate a new id128 string\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"
+ " help Show this help\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
+}
+
+static int verb_help(int argc, char **argv, void *userdata) {
+ return help();
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_VERSION = 0x100,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "app-specific", required_argument, NULL, 'a' },
+ {},
+ };
+
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "hpa:", options, NULL)) >= 0)
+ switch (c) {
+
+ case 'h':
+ return help();
+
+ case ARG_VERSION:
+ return version();
+
+ case 'p':
+ arg_pretty = true;
+ break;
+
+ case 'a':
+ r = sd_id128_from_string(optarg, &arg_app);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse \"%s\" as application-ID: %m", optarg);
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+
+ return 1;
+}
+
+static int id128_main(int argc, char *argv[]) {
+ static const Verb verbs[] = {
+ { "new", VERB_ANY, 1, 0, verb_new },
+ { "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 },
+ { "help", VERB_ANY, VERB_ANY, 0, verb_help },
+ {}
+ };
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+static int run(int argc, char *argv[]) {
+ int r;
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ return id128_main(argc, argv);
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/import/curl-util.c b/src/import/curl-util.c
index b85462176d..05b17c3c78 100644
--- a/src/import/curl-util.c
+++ b/src/import/curl-util.c
@@ -41,10 +41,9 @@ static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *u
else
action = 0;
- if (curl_multi_socket_action(g->curl, translated_fd, action, &k) < 0) {
- log_debug("Failed to propagate IO event.");
- return -EINVAL;
- }
+ if (curl_multi_socket_action(g->curl, translated_fd, action, &k) != CURLM_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to propagate IO event.");
curl_glue_check_finished(g);
return 0;
@@ -151,10 +150,9 @@ static int curl_glue_on_timer(sd_event_source *s, uint64_t usec, void *userdata)
assert(s);
assert(g);
- if (curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK) {
- log_debug("Failed to propagate timeout.");
- return -EINVAL;
- }
+ if (curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to propagate timeout.");
curl_glue_check_finished(g);
return 0;
@@ -224,24 +222,31 @@ CurlGlue *curl_glue_unref(CurlGlue *g) {
int curl_glue_new(CurlGlue **glue, sd_event *event) {
_cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
+ _cleanup_(curl_multi_cleanupp) CURL *c = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
int r;
- g = new0(CurlGlue, 1);
- if (!g)
- return -ENOMEM;
-
if (event)
- g->event = sd_event_ref(event);
+ e = sd_event_ref(event);
else {
- r = sd_event_default(&g->event);
+ r = sd_event_default(&e);
if (r < 0)
return r;
}
- g->curl = curl_multi_init();
- if (!g->curl)
+ c = curl_multi_init();
+ if (!c)
+ return -ENOMEM;
+
+ g = new(CurlGlue, 1);
+ if (!g)
return -ENOMEM;
+ *g = (CurlGlue) {
+ .event = TAKE_PTR(e),
+ .curl = TAKE_PTR(c),
+ };
+
if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK)
return -EINVAL;
@@ -260,9 +265,8 @@ int curl_glue_new(CurlGlue **glue, sd_event *event) {
}
int curl_glue_make(CURL **ret, const char *url, void *userdata) {
+ _cleanup_(curl_easy_cleanupp) CURL *c = NULL;
const char *useragent;
- CURL *c;
- int r;
assert(ret);
assert(url);
@@ -273,33 +277,21 @@ int curl_glue_make(CURL **ret, const char *url, void *userdata) {
/* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */
- if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) {
- r = -EIO;
- goto fail;
- }
+ if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK)
+ return -EIO;
- if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) {
- r = -EIO;
- goto fail;
- }
+ if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK)
+ return -EIO;
useragent = strjoina(program_invocation_short_name, "/" PACKAGE_VERSION);
- if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) {
- r = -EIO;
- goto fail;
- }
+ if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK)
+ return -EIO;
- if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) {
- r = -EIO;
- goto fail;
- }
+ if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK)
+ return -EIO;
*ret = c;
return 0;
-
-fail:
- curl_easy_cleanup(c);
- return r;
}
int curl_glue_add(CurlGlue *g, CURL *c) {
@@ -363,7 +355,7 @@ int curl_header_strdup(const void *contents, size_t sz, const char *field, char
const char *p;
char *s;
- p = memory_startswith(contents, sz, field);
+ p = memory_startswith_no_case(contents, sz, field);
if (!p)
return 0;
diff --git a/src/import/curl-util.h b/src/import/curl-util.h
index 6626eefc56..a03e844030 100644
--- a/src/import/curl-util.h
+++ b/src/import/curl-util.h
@@ -35,4 +35,5 @@ int curl_header_strdup(const void *contents, size_t sz, const char *field, char
int curl_parse_http_time(const char *t, usec_t *ret);
DEFINE_TRIVIAL_CLEANUP_FUNC(CURL*, curl_easy_cleanup);
+DEFINE_TRIVIAL_CLEANUP_FUNC(CURL*, curl_multi_cleanup);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct curl_slist*, curl_slist_free_all);
diff --git a/src/import/export-raw.c b/src/import/export-raw.c
index f97ae461c5..6a02b47a17 100644
--- a/src/import/export-raw.c
+++ b/src/import/export-raw.c
@@ -15,12 +15,13 @@
#include "copy.h"
#include "export-raw.h"
#include "fd-util.h"
-#include "fileio.h"
+#include "fs-util.h"
#include "import-common.h"
#include "missing.h"
#include "ratelimit.h"
#include "stat-util.h"
#include "string-util.h"
+#include "tmpfile-util.h"
#include "util.h"
#define COPY_BUFFER_SIZE (16*1024)
@@ -85,16 +86,19 @@ int raw_export_new(
assert(ret);
- e = new0(RawExport, 1);
+ e = new(RawExport, 1);
if (!e)
return -ENOMEM;
- e->output_fd = e->input_fd = -1;
- e->on_finished = on_finished;
- e->userdata = userdata;
+ *e = (RawExport) {
+ .output_fd = -1,
+ .input_fd = -1,
+ .on_finished = on_finished,
+ .userdata = userdata,
+ .last_percent = (unsigned) -1,
+ };
RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
- e->last_percent = (unsigned) -1;
if (event)
e->event = sd_event_ref(event);
@@ -244,13 +248,9 @@ static int raw_export_on_defer(sd_event_source *s, void *userdata) {
}
static int reflink_snapshot(int fd, const char *path) {
- char *p, *d;
int new_fd, r;
- p = strdupa(path);
- d = dirname(p);
-
- new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600);
+ new_fd = open_parent(path, O_TMPFILE|O_CLOEXEC|O_RDWR, 0600);
if (new_fd < 0) {
_cleanup_free_ char *t = NULL;
diff --git a/src/import/export-tar.c b/src/import/export-tar.c
index 9e7c0eaa9d..ed546769f3 100644
--- a/src/import/export-tar.c
+++ b/src/import/export-tar.c
@@ -6,11 +6,11 @@
#include "btrfs-util.h"
#include "export-tar.h"
#include "fd-util.h"
-#include "fileio.h"
#include "import-common.h"
#include "process-util.h"
#include "ratelimit.h"
#include "string-util.h"
+#include "tmpfile-util.h"
#include "util.h"
#define COPY_BUFFER_SIZE (16*1024)
@@ -88,17 +88,20 @@ int tar_export_new(
assert(ret);
- e = new0(TarExport, 1);
+ e = new(TarExport, 1);
if (!e)
return -ENOMEM;
- e->output_fd = e->tar_fd = -1;
- e->on_finished = on_finished;
- e->userdata = userdata;
- e->quota_referenced = (uint64_t) -1;
+ *e = (TarExport) {
+ .output_fd = -1,
+ .tar_fd = -1,
+ .on_finished = on_finished,
+ .userdata = userdata,
+ .quota_referenced = (uint64_t) -1,
+ .last_percent = (unsigned) -1,
+ };
RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
- e->last_percent = (unsigned) -1;
if (event)
e->event = sd_event_ref(event);
@@ -138,6 +141,26 @@ static void tar_export_report_progress(TarExport *e) {
e->last_percent = percent;
}
+static int tar_export_finish(TarExport *e) {
+ int r;
+
+ assert(e);
+ assert(e->tar_fd >= 0);
+
+ if (e->tar_pid > 0) {
+ r = wait_for_terminate_and_check("tar", e->tar_pid, WAIT_LOG);
+ e->tar_pid = 0;
+ if (r < 0)
+ return r;
+ if (r != EXIT_SUCCESS)
+ return -EPROTO;
+ }
+
+ e->tar_fd = safe_close(e->tar_fd);
+
+ return 0;
+}
+
static int tar_export_process(TarExport *e) {
ssize_t l;
int r;
@@ -153,7 +176,7 @@ static int tar_export_process(TarExport *e) {
e->tried_splice = true;
} else if (l == 0) {
- r = 0;
+ r = tar_export_finish(e);
goto finish;
} else {
e->written_uncompressed += l;
@@ -169,7 +192,7 @@ static int tar_export_process(TarExport *e) {
uint8_t input[COPY_BUFFER_SIZE];
if (e->eof) {
- r = 0;
+ r = tar_export_finish(e);
goto finish;
}
diff --git a/src/import/export.c b/src/import/export.c
index 03eb3e40b2..490710678f 100644
--- a/src/import/export.c
+++ b/src/import/export.c
@@ -13,6 +13,7 @@
#include "hostname-util.h"
#include "import-util.h"
#include "machine-image.h"
+#include "main-func.h"
#include "signal-util.h"
#include "string-util.h"
#include "verbs.h"
@@ -94,7 +95,7 @@ static int export_tar(int argc, char *argv[], void *userdata) {
fd = STDOUT_FILENO;
- (void) readlink_malloc("/proc/self/fd/1", &pretty);
+ (void) fd_get_path(fd, &pretty);
log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress));
}
@@ -171,7 +172,7 @@ static int export_raw(int argc, char *argv[], void *userdata) {
fd = STDOUT_FILENO;
- (void) readlink_malloc("/proc/self/fd/1", &pretty);
+ (void) fd_get_path(fd, &pretty);
log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress));
}
@@ -252,10 +253,9 @@ static int parse_argv(int argc, char *argv[]) {
arg_compress = IMPORT_COMPRESS_GZIP;
else if (streq(optarg, "bzip2"))
arg_compress = IMPORT_COMPRESS_BZIP2;
- else {
- log_error("Unknown format: %s", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown format: %s", optarg);
break;
case '?':
@@ -269,7 +269,6 @@ static int parse_argv(int argc, char *argv[]) {
}
static int export_main(int argc, char *argv[]) {
-
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "tar", 2, 3, 0, export_tar },
@@ -280,7 +279,7 @@ static int export_main(int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, NULL);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
setlocale(LC_ALL, "");
@@ -289,12 +288,11 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
(void) ignore_signals(SIGPIPE, -1);
- r = export_main(argc, argv);
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return export_main(argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/import/import-common.c b/src/import/import-common.c
index e2de2c2dd0..89f03010d1 100644
--- a/src/import/import-common.c
+++ b/src/import/import-common.c
@@ -5,12 +5,18 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "alloc-util.h"
#include "btrfs-util.h"
#include "capability-util.h"
+#include "dirent-util.h"
#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "import-common.h"
+#include "os-util.h"
#include "process-util.h"
#include "signal-util.h"
+#include "tmpfile-util.h"
#include "util.h"
int import_make_read_only_fd(int fd) {
@@ -147,3 +153,107 @@ int import_fork_tar_c(const char *path, pid_t *ret) {
return TAKE_FD(pipefd[0]);
}
+
+int import_mangle_os_tree(const char *path) {
+ _cleanup_closedir_ DIR *d = NULL, *cd = NULL;
+ _cleanup_free_ char *child = NULL, *t = NULL;
+ const char *joined;
+ struct dirent *de;
+ int r;
+
+ assert(path);
+
+ /* Some tarballs contain a single top-level directory that contains the actual OS directory tree. Try to
+ * recognize this, and move the tree one level up. */
+
+ r = path_is_os_tree(path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", path);
+ if (r > 0) {
+ log_debug("Directory tree '%s' is a valid OS tree.", path);
+ return 0;
+ }
+
+ log_debug("Directory tree '%s' is not recognizable as OS tree, checking whether to rearrange it.", path);
+
+ d = opendir(path);
+ if (!d)
+ return log_error_errno(r, "Failed to open directory '%s': %m", path);
+
+ errno = 0;
+ de = readdir_no_dot(d);
+ if (!de) {
+ if (errno != 0)
+ return log_error_errno(errno, "Failed to iterate through directory '%s': %m", path);
+
+ log_debug("Directory '%s' is empty, leaving it as it is.", path);
+ return 0;
+ }
+
+ child = strdup(de->d_name);
+ if (!child)
+ return log_oom();
+
+ errno = 0;
+ de = readdir_no_dot(d);
+ if (de) {
+ if (errno != 0)
+ return log_error_errno(errno, "Failed to iterate through directory '%s': %m", path);
+
+ log_debug("Directory '%s' does not look like a directory tree, and has multiple children, leaving as it is.", path);
+ return 0;
+ }
+
+ joined = strjoina(path, "/", child);
+ r = path_is_os_tree(joined);
+ if (r == -ENOTDIR) {
+ log_debug("Directory '%s' does not look like a directory tree, and contains a single regular file only, leaving as it is.", path);
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", joined);
+ if (r == 0) {
+ log_debug("Neither '%s' nor '%s' is a valid OS tree, leaving them as they are.", path, joined);
+ return 0;
+ }
+
+ /* Nice, we have checked now:
+ *
+ * 1. The top-level directory does not qualify as OS tree
+ * 1. The top-level directory only contains one item
+ * 2. That item is a directory
+ * 3. And that directory qualifies as OS tree
+ *
+ * Let's now rearrange things, moving everything in the inner directory one level up */
+
+ cd = xopendirat(dirfd(d), child, O_NOFOLLOW);
+ if (!cd)
+ return log_error_errno(errno, "Can't open directory '%s': %m", joined);
+
+ log_info("Rearranging '%s', moving OS tree one directory up.", joined);
+
+ /* Let's rename the child to an unguessable name so that we can be sure all files contained in it can be
+ * safely moved up and won't collide with the name. */
+ r = tempfn_random(child, NULL, &t);
+ if (r < 0)
+ return log_oom();
+ r = rename_noreplace(dirfd(d), child, dirfd(d), t);
+ if (r < 0)
+ return log_error_errno(r, "Unable to rename '%s' to '%s/%s': %m", joined, path, t);
+
+ FOREACH_DIRENT_ALL(de, cd, return log_error_errno(errno, "Failed to iterate through directory '%s': %m", joined)) {
+ if (dot_or_dot_dot(de->d_name))
+ continue;
+
+ r = rename_noreplace(dirfd(cd), de->d_name, dirfd(d), de->d_name);
+ if (r < 0)
+ return log_error_errno(r, "Unable to move '%s/%s/%s' to '%s/%s': %m", path, t, de->d_name, path, de->d_name);
+ }
+
+ if (unlinkat(dirfd(d), t, AT_REMOVEDIR) < 0)
+ return log_error_errno(errno, "Failed to remove temporary directory '%s/%s': %m", path, t);
+
+ log_info("Successfully rearranged OS tree.");
+
+ return 0;
+}
diff --git a/src/import/import-common.h b/src/import/import-common.h
index 99ac5fe970..94d224f412 100644
--- a/src/import/import-common.h
+++ b/src/import/import-common.h
@@ -1,8 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <sys/types.h>
+
int import_make_read_only_fd(int fd);
int import_make_read_only(const char *path);
int import_fork_tar_c(const char *path, pid_t *ret);
int import_fork_tar_x(const char *path, pid_t *ret);
+
+int import_mangle_os_tree(const char *path);
diff --git a/src/import/import-compress.c b/src/import/import-compress.c
index 1cf29e26f1..3fbd067790 100644
--- a/src/import/import-compress.c
+++ b/src/import/import-compress.c
@@ -14,11 +14,13 @@ void import_compress_free(ImportCompress *c) {
deflateEnd(&c->gzip);
else
inflateEnd(&c->gzip);
+#if HAVE_BZIP2
} else if (c->type == IMPORT_COMPRESS_BZIP2) {
if (c->encoding)
BZ2_bzCompressEnd(&c->bzip2);
else
BZ2_bzDecompressEnd(&c->bzip2);
+#endif
}
c->type = IMPORT_COMPRESS_UNKNOWN;
@@ -65,12 +67,14 @@ int import_uncompress_detect(ImportCompress *c, const void *data, size_t size) {
c->type = IMPORT_COMPRESS_GZIP;
+#if HAVE_BZIP2
} else if (memcmp(data, bzip2_signature, sizeof(bzip2_signature)) == 0) {
r = BZ2_bzDecompressInit(&c->bzip2, 0, 0);
if (r != BZ_OK)
return -EIO;
c->type = IMPORT_COMPRESS_BZIP2;
+#endif
} else
c->type = IMPORT_COMPRESS_UNCOMPRESSED;
@@ -149,6 +153,7 @@ int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCo
break;
+#if HAVE_BZIP2
case IMPORT_COMPRESS_BZIP2:
c->bzip2.next_in = (void*) data;
c->bzip2.avail_in = size;
@@ -169,6 +174,7 @@ int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCo
}
break;
+#endif
default:
assert_not_reached("Unknown compression");
@@ -203,6 +209,7 @@ int import_compress_init(ImportCompress *c, ImportCompressType t) {
c->type = IMPORT_COMPRESS_GZIP;
break;
+#if HAVE_BZIP2
case IMPORT_COMPRESS_BZIP2:
r = BZ2_bzCompressInit(&c->bzip2, 9, 0, 0);
if (r != BZ_OK)
@@ -210,6 +217,7 @@ int import_compress_init(ImportCompress *c, ImportCompressType t) {
c->type = IMPORT_COMPRESS_BZIP2;
break;
+#endif
case IMPORT_COMPRESS_UNCOMPRESSED:
c->type = IMPORT_COMPRESS_UNCOMPRESSED;
@@ -307,6 +315,7 @@ int import_compress(ImportCompress *c, const void *data, size_t size, void **buf
break;
+#if HAVE_BZIP2
case IMPORT_COMPRESS_BZIP2:
c->bzip2.next_in = (void*) data;
@@ -328,6 +337,7 @@ int import_compress(ImportCompress *c, const void *data, size_t size, void **buf
}
break;
+#endif
case IMPORT_COMPRESS_UNCOMPRESSED:
@@ -411,6 +421,7 @@ int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size
break;
+#if HAVE_BZIP2
case IMPORT_COMPRESS_BZIP2:
c->bzip2.avail_in = 0;
@@ -430,6 +441,7 @@ int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size
} while (r != BZ_STREAM_END);
break;
+#endif
case IMPORT_COMPRESS_UNCOMPRESSED:
break;
@@ -446,7 +458,9 @@ static const char* const import_compress_type_table[_IMPORT_COMPRESS_TYPE_MAX] =
[IMPORT_COMPRESS_UNCOMPRESSED] = "uncompressed",
[IMPORT_COMPRESS_XZ] = "xz",
[IMPORT_COMPRESS_GZIP] = "gzip",
+#if HAVE_BZIP2
[IMPORT_COMPRESS_BZIP2] = "bzip2",
+#endif
};
DEFINE_STRING_TABLE_LOOKUP(import_compress_type, ImportCompressType);
diff --git a/src/import/import-compress.h b/src/import/import-compress.h
index 6fb87ccdfb..859bd0e1a4 100644
--- a/src/import/import-compress.h
+++ b/src/import/import-compress.h
@@ -1,7 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#if HAVE_BZIP2
#include <bzlib.h>
+#endif
#include <lzma.h>
#include <sys/types.h>
#include <zlib.h>
@@ -24,7 +26,9 @@ typedef struct ImportCompress {
union {
lzma_stream xz;
z_stream gzip;
+#if HAVE_BZIP2
bz_stream bzip2;
+#endif
};
} ImportCompress;
diff --git a/src/import/import-fs.c b/src/import/import-fs.c
new file mode 100644
index 0000000000..35ba6ba382
--- /dev/null
+++ b/src/import/import-fs.c
@@ -0,0 +1,327 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+
+#include "alloc-util.h"
+#include "btrfs-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "hostname-util.h"
+#include "import-common.h"
+#include "import-util.h"
+#include "machine-image.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "ratelimit.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "tmpfile-util.h"
+#include "verbs.h"
+
+static bool arg_force = false;
+static bool arg_read_only = false;
+static const char *arg_image_root = "/var/lib/machines";
+
+typedef struct ProgressInfo {
+ RateLimit limit;
+ char *path;
+ uint64_t size;
+ bool started;
+ bool logged_incomplete;
+} ProgressInfo;
+
+static volatile sig_atomic_t cancelled = false;
+
+static void sigterm_sigint(int sig) {
+ cancelled = true;
+}
+
+static void progress_info_free(ProgressInfo *p) {
+ free(p->path);
+}
+
+static void progress_show(ProgressInfo *p) {
+ assert(p);
+
+ /* Show progress only every now and then. */
+ if (!ratelimit_below(&p->limit))
+ return;
+
+ /* Suppress the first message, start with the second one */
+ if (!p->started) {
+ p->started = true;
+ return;
+ }
+
+ /* Mention the list is incomplete before showing first output. */
+ if (!p->logged_incomplete) {
+ log_notice("(Note, file list shown below is incomplete, and is intended as sporadic progress report only.)");
+ p->logged_incomplete = true;
+ }
+
+ if (p->size == 0)
+ log_info("Copying tree, currently at '%s'...", p->path);
+ else {
+ char buffer[FORMAT_BYTES_MAX];
+
+ log_info("Copying tree, currently at '%s' (@%s)...", p->path, format_bytes(buffer, sizeof(buffer), p->size));
+ }
+}
+
+static int progress_path(const char *path, const struct stat *st, void *userdata) {
+ ProgressInfo *p = userdata;
+ int r;
+
+ assert(p);
+
+ if (cancelled)
+ return -EOWNERDEAD;
+
+ r = free_and_strdup(&p->path, path);
+ if (r < 0)
+ return r;
+
+ p->size = 0;
+
+ progress_show(p);
+ return 0;
+}
+
+static int progress_bytes(uint64_t nbytes, void *userdata) {
+ ProgressInfo *p = userdata;
+
+ assert(p);
+ assert(p->size != UINT64_MAX);
+
+ if (cancelled)
+ return -EOWNERDEAD;
+
+ p->size += nbytes;
+
+ progress_show(p);
+ return 0;
+}
+
+static int import_fs(int argc, char *argv[], void *userdata) {
+ _cleanup_(rm_rf_subvolume_and_freep) char *temp_path = NULL;
+ _cleanup_(progress_info_free) ProgressInfo progress = {};
+ const char *path = NULL, *local = NULL, *final_path;
+ _cleanup_close_ int open_fd = -1;
+ struct sigaction old_sigint_sa, old_sigterm_sa;
+ static const struct sigaction sa = {
+ .sa_handler = sigterm_sigint,
+ .sa_flags = SA_RESTART,
+ };
+ int r, fd;
+
+ if (argc >= 2)
+ path = argv[1];
+ if (isempty(path) || streq(path, "-"))
+ path = NULL;
+
+ if (argc >= 3)
+ local = argv[2];
+ else if (path)
+ local = basename(path);
+ if (isempty(local) || streq(local, "-"))
+ local = NULL;
+
+ if (local) {
+ if (!machine_name_is_valid(local)) {
+ log_error("Local image name '%s' is not valid.", local);
+ return -EINVAL;
+ }
+
+ if (!arg_force) {
+ r = image_find(IMAGE_MACHINE, local, NULL);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
+ } else {
+ log_error("Image '%s' already exists.", local);
+ return -EEXIST;
+ }
+ }
+ } else
+ local = "imported";
+
+ if (path) {
+ open_fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+ if (open_fd < 0)
+ return log_error_errno(errno, "Failed to open directory to import: %m");
+
+ fd = open_fd;
+
+ log_info("Importing '%s', saving as '%s'.", path, local);
+ } else {
+ _cleanup_free_ char *pretty = NULL;
+
+ fd = STDIN_FILENO;
+
+ (void) fd_get_path(fd, &pretty);
+ log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
+ }
+
+ final_path = strjoina(arg_image_root, "/", local);
+
+ r = tempfn_random(final_path, NULL, &temp_path);
+ if (r < 0)
+ return log_oom();
+
+ (void) mkdir_parents_label(temp_path, 0700);
+
+ RATELIMIT_INIT(progress.limit, 200*USEC_PER_MSEC, 1);
+
+ /* Hook into SIGINT/SIGTERM, so that we can cancel things then */
+ assert(sigaction(SIGINT, &sa, &old_sigint_sa) >= 0);
+ assert(sigaction(SIGTERM, &sa, &old_sigterm_sa) >= 0);
+
+ r = btrfs_subvol_snapshot_fd_full(
+ fd,
+ temp_path,
+ BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|BTRFS_SNAPSHOT_QUOTA,
+ progress_path,
+ progress_bytes,
+ &progress);
+ if (r == -EOWNERDEAD) { /* SIGINT + SIGTERM cause this, see signal handler above */
+ log_error("Copy cancelled.");
+ goto finish;
+ }
+ if (r < 0) {
+ log_error_errno(r, "Failed to copy directory: %m");
+ goto finish;
+ }
+
+ r = import_mangle_os_tree(temp_path);
+ if (r < 0)
+ goto finish;
+
+ (void) import_assign_pool_quota_and_warn(temp_path);
+
+ if (arg_read_only) {
+ r = import_make_read_only(temp_path);
+ if (r < 0) {
+ log_error_errno(r, "Failed to make directory read-only: %m");
+ goto finish;
+ }
+ }
+
+ if (arg_force)
+ (void) rm_rf(final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+
+ r = rename_noreplace(AT_FDCWD, temp_path, AT_FDCWD, final_path);
+ if (r < 0) {
+ log_error_errno(r, "Failed to move image into place: %m");
+ goto finish;
+ }
+
+ temp_path = mfree(temp_path);
+
+ log_info("Exiting.");
+
+finish:
+ /* Put old signal handlers into place */
+ assert(sigaction(SIGINT, &old_sigint_sa, NULL) >= 0);
+ assert(sigaction(SIGTERM, &old_sigterm_sa, NULL) >= 0);
+
+ return 0;
+}
+
+static int help(int argc, char *argv[], void *userdata) {
+
+ printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+ "Import container images from a file system.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --force Force creation of image\n"
+ " --image-root=PATH Image root directory\n"
+ " --read-only Create a read-only image\n\n"
+ "Commands:\n"
+ " run DIRECTORY [NAME] Import a directory\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_FORCE,
+ ARG_IMAGE_ROOT,
+ ARG_READ_ONLY,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "force", no_argument, NULL, ARG_FORCE },
+ { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
+ { "read-only", no_argument, NULL, ARG_READ_ONLY },
+ {}
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+
+ switch (c) {
+
+ case 'h':
+ return help(0, NULL, NULL);
+
+ case ARG_VERSION:
+ return version();
+
+ case ARG_FORCE:
+ arg_force = true;
+ break;
+
+ case ARG_IMAGE_ROOT:
+ arg_image_root = optarg;
+ break;
+
+ case ARG_READ_ONLY:
+ arg_read_only = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+
+ return 1;
+}
+
+static int import_fs_main(int argc, char *argv[]) {
+
+ static const Verb verbs[] = {
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "run", 2, 3, 0, import_fs },
+ {}
+ };
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ setlocale(LC_ALL, "");
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ r = import_fs_main(argc, argv);
+
+finish:
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/import/import-raw.c b/src/import/import-raw.c
index f31432cd04..4b1161557d 100644
--- a/src/import/import-raw.c
+++ b/src/import/import-raw.c
@@ -10,7 +10,6 @@
#include "chattr-util.h"
#include "copy.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "import-common.h"
@@ -24,6 +23,7 @@
#include "ratelimit.h"
#include "rm-rf.h"
#include "string-util.h"
+#include "tmpfile-util.h"
#include "util.h"
struct RawImport {
@@ -37,7 +37,6 @@ struct RawImport {
char *local;
bool force_local;
bool read_only;
- bool grow_machine_directory;
char *temp_path;
char *final_path;
@@ -47,8 +46,6 @@ struct RawImport {
ImportCompress compress;
- uint64_t written_since_last_grow;
-
sd_event_source *input_event_source;
uint8_t buffer[16*1024];
@@ -94,26 +91,29 @@ int raw_import_new(
void *userdata) {
_cleanup_(raw_import_unrefp) RawImport *i = NULL;
+ _cleanup_free_ char *root = NULL;
int r;
assert(ret);
- i = new0(RawImport, 1);
+ root = strdup(image_root ?: "/var/lib/machines");
+ if (!root)
+ return -ENOMEM;
+
+ i = new(RawImport, 1);
if (!i)
return -ENOMEM;
- i->input_fd = i->output_fd = -1;
- i->on_finished = on_finished;
- i->userdata = userdata;
+ *i = (RawImport) {
+ .input_fd = -1,
+ .output_fd = -1,
+ .on_finished = on_finished,
+ .userdata = userdata,
+ .last_percent = (unsigned) -1,
+ .image_root = TAKE_PTR(root),
+ };
RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
- i->last_percent = (unsigned) -1;
-
- i->image_root = strdup(image_root ?: "/var/lib/machines");
- if (!i->image_root)
- return -ENOMEM;
-
- i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
if (event)
i->event = sd_event_ref(event);
@@ -175,7 +175,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);
+ 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);
@@ -260,7 +260,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);
+ 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);
@@ -300,19 +300,13 @@ static int raw_import_write(const void *p, size_t sz, void *userdata) {
RawImport *i = userdata;
ssize_t n;
- if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) {
- i->written_since_last_grow = 0;
- grow_machine_directory();
- }
-
n = sparse_write(i->output_fd, p, sz, 64);
if (n < 0)
- return -errno;
+ return (int) n;
if ((size_t) n < sz)
return -EIO;
i->written_uncompressed += sz;
- i->written_since_last_grow += sz;
return 0;
}
diff --git a/src/import/import-tar.c b/src/import/import-tar.c
index 89c95af279..4d0d9254d0 100644
--- a/src/import/import-tar.c
+++ b/src/import/import-tar.c
@@ -24,6 +24,7 @@
#include "ratelimit.h"
#include "rm-rf.h"
#include "string-util.h"
+#include "tmpfile-util.h"
#include "util.h"
struct TarImport {
@@ -37,7 +38,6 @@ struct TarImport {
char *local;
bool force_local;
bool read_only;
- bool grow_machine_directory;
char *temp_path;
char *final_path;
@@ -47,8 +47,6 @@ struct TarImport {
ImportCompress compress;
- uint64_t written_since_last_grow;
-
sd_event_source *input_event_source;
uint8_t buffer[16*1024];
@@ -101,26 +99,29 @@ int tar_import_new(
void *userdata) {
_cleanup_(tar_import_unrefp) TarImport *i = NULL;
+ _cleanup_free_ char *root = NULL;
int r;
assert(ret);
- i = new0(TarImport, 1);
+ root = strdup(image_root ?: "/var/lib/machines");
+ if (!root)
+ return -ENOMEM;
+
+ i = new(TarImport, 1);
if (!i)
return -ENOMEM;
- i->input_fd = i->tar_fd = -1;
- i->on_finished = on_finished;
- i->userdata = userdata;
+ *i = (TarImport) {
+ .input_fd = -1,
+ .tar_fd = -1,
+ .on_finished = on_finished,
+ .userdata = userdata,
+ .last_percent = (unsigned) -1,
+ .image_root = TAKE_PTR(root),
+ };
RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
- i->last_percent = (unsigned) -1;
-
- i->image_root = strdup(image_root ?: "/var/lib/machines");
- if (!i->image_root)
- return -ENOMEM;
-
- i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
if (event)
i->event = sd_event_ref(event);
@@ -175,8 +176,14 @@ static int tar_import_finish(TarImport *i) {
i->tar_pid = 0;
if (r < 0)
return r;
+ if (r != EXIT_SUCCESS)
+ return -EPROTO;
}
+ r = import_mangle_os_tree(i->temp_path);
+ if (r < 0)
+ return r;
+
if (i->read_only) {
r = import_make_read_only(i->temp_path);
if (r < 0)
@@ -234,17 +241,11 @@ static int tar_import_write(const void *p, size_t sz, void *userdata) {
TarImport *i = userdata;
int r;
- if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) {
- i->written_since_last_grow = 0;
- grow_machine_directory();
- }
-
r = loop_write(i->tar_fd, p, sz, false);
if (r < 0)
return r;
i->written_uncompressed += sz;
- i->written_since_last_grow += sz;
return 0;
}
diff --git a/src/import/import.c b/src/import/import.c
index 35c8e6d630..f34244acff 100644
--- a/src/import/import.c
+++ b/src/import/import.c
@@ -13,6 +13,7 @@
#include "import-tar.h"
#include "import-util.h"
#include "machine-image.h"
+#include "main-func.h"
#include "signal-util.h"
#include "string-util.h"
#include "verbs.h"
@@ -95,7 +96,7 @@ static int import_tar(int argc, char *argv[], void *userdata) {
fd = STDIN_FILENO;
- (void) readlink_malloc("/proc/self/fd/0", &pretty);
+ (void) fd_get_path(fd, &pretty);
log_info("Importing '%s', saving as '%s'.", strna(pretty), local);
}
@@ -191,7 +192,7 @@ static int import_raw(int argc, char *argv[], void *userdata) {
fd = STDIN_FILENO;
- (void) readlink_malloc("/proc/self/fd/0", &pretty);
+ (void) fd_get_path(fd, &pretty);
log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
}
@@ -292,7 +293,6 @@ static int parse_argv(int argc, char *argv[]) {
}
static int import_main(int argc, char *argv[]) {
-
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "tar", 2, 3, 0, import_tar },
@@ -303,7 +303,7 @@ static int import_main(int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, NULL);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
setlocale(LC_ALL, "");
@@ -312,12 +312,11 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return 0;
(void) ignore_signals(SIGPIPE, -1);
- r = import_main(argc, argv);
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return import_main(argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/import/importd.c b/src/import/importd.c
index 04563fb098..2426933558 100644
--- a/src/import/importd.c
+++ b/src/import/importd.c
@@ -10,9 +10,11 @@
#include "bus-util.h"
#include "def.h"
#include "fd-util.h"
+#include "float.h"
#include "hostname-util.h"
#include "import-util.h"
#include "machine-pool.h"
+#include "main-func.h"
#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
@@ -20,6 +22,7 @@
#include "process-util.h"
#include "signal-util.h"
#include "socket-util.h"
+#include "stat-util.h"
#include "string-table.h"
#include "strv.h"
#include "syslog-util.h"
@@ -33,6 +36,7 @@ typedef struct Manager Manager;
typedef enum TransferType {
TRANSFER_IMPORT_TAR,
TRANSFER_IMPORT_RAW,
+ TRANSFER_IMPORT_FS,
TRANSFER_EXPORT_TAR,
TRANSFER_EXPORT_RAW,
TRANSFER_PULL_TAR,
@@ -93,6 +97,7 @@ struct Manager {
static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
[TRANSFER_IMPORT_TAR] = "import-tar",
[TRANSFER_IMPORT_RAW] = "import-raw",
+ [TRANSFER_IMPORT_FS] = "import-fs",
[TRANSFER_EXPORT_TAR] = "export-tar",
[TRANSFER_EXPORT_RAW] = "export-raw",
[TRANSFER_PULL_TAR] = "pull-tar",
@@ -145,15 +150,18 @@ static int transfer_new(Manager *m, Transfer **ret) {
if (r < 0)
return r;
- t = new0(Transfer, 1);
+ t = new(Transfer, 1);
if (!t)
return -ENOMEM;
- t->type = _TRANSFER_TYPE_INVALID;
- t->log_fd = -1;
- t->stdin_fd = -1;
- t->stdout_fd = -1;
- t->verify = _IMPORT_VERIFY_INVALID;
+ *t = (Transfer) {
+ .type = _TRANSFER_TYPE_INVALID,
+ .log_fd = -1,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .verify = _IMPORT_VERIFY_INVALID,
+ .progress_percent= (unsigned) -1,
+ };
id = m->current_transfer_id + 1;
@@ -174,6 +182,15 @@ static int transfer_new(Manager *m, Transfer **ret) {
return 0;
}
+static double transfer_percent_as_double(Transfer *t) {
+ assert(t);
+
+ if (t->progress_percent == (unsigned) -1)
+ return -DBL_MAX;
+
+ return (double) t->progress_percent / 100.0;
+}
+
static void transfer_send_log_line(Transfer *t, const char *line) {
int r, priority = LOG_INFO;
@@ -193,7 +210,7 @@ static void transfer_send_log_line(Transfer *t, const char *line) {
priority,
line);
if (r < 0)
- log_error_errno(r, "Cannot emit message: %m");
+ log_warning_errno(r, "Cannot emit log message signal, ignoring: %m");
}
static void transfer_send_logs(Transfer *t, bool flush) {
@@ -298,17 +315,16 @@ static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userda
if (si->si_code == CLD_EXITED) {
if (si->si_status != 0)
- log_error("Import process failed with exit code %i.", si->si_status);
+ log_error("Transfer process failed with exit code %i.", si->si_status);
else {
- log_debug("Import process succeeded.");
+ log_debug("Transfer process succeeded.");
success = true;
}
} else if (IN_SET(si->si_code, CLD_KILLED, CLD_DUMPED))
-
- log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
+ log_error("Transfer process terminated by signal %s.", signal_to_string(si->si_status));
else
- log_error("Import process failed due to unknown reason.");
+ log_error("Transfer process failed due to unknown reason.");
t->pid = 0;
@@ -323,14 +339,12 @@ static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *u
assert(t);
l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
+ if (l < 0)
+ log_error_errno(errno, "Failed to read log message: %m");
if (l <= 0) {
/* EOF/read error. We just close the pipe here, and
* close the watch, waiting for the SIGCHLD to arrive,
* before we do anything else. */
-
- if (l < 0)
- log_error_errno(errno, "Failed to read log message: %m");
-
t->log_event_source = sd_event_source_unref(t->log_event_source);
return 0;
}
@@ -357,7 +371,7 @@ static int transfer_start(Transfer *t) {
return r;
if (r == 0) {
const char *cmd[] = {
- NULL, /* systemd-import, systemd-export or systemd-pull */
+ NULL, /* systemd-import, systemd-import-fs, systemd-export or systemd-pull */
NULL, /* tar, raw */
NULL, /* --verify= */
NULL, /* verify argument */
@@ -390,17 +404,52 @@ static int transfer_start(Transfer *t) {
_exit(EXIT_FAILURE);
}
- if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW))
+ switch (t->type) {
+
+ case TRANSFER_IMPORT_TAR:
+ case TRANSFER_IMPORT_RAW:
cmd[k++] = SYSTEMD_IMPORT_PATH;
- else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW))
+ break;
+
+ case TRANSFER_IMPORT_FS:
+ cmd[k++] = SYSTEMD_IMPORT_FS_PATH;
+ break;
+
+ case TRANSFER_EXPORT_TAR:
+ case TRANSFER_EXPORT_RAW:
cmd[k++] = SYSTEMD_EXPORT_PATH;
- else
+ break;
+
+ case TRANSFER_PULL_TAR:
+ case TRANSFER_PULL_RAW:
cmd[k++] = SYSTEMD_PULL_PATH;
+ break;
- if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR))
+ default:
+ assert_not_reached("Unexpected transfer type");
+ }
+
+ switch (t->type) {
+
+ case TRANSFER_IMPORT_TAR:
+ case TRANSFER_EXPORT_TAR:
+ case TRANSFER_PULL_TAR:
cmd[k++] = "tar";
- else
+ break;
+
+ case TRANSFER_IMPORT_RAW:
+ case TRANSFER_EXPORT_RAW:
+ case TRANSFER_PULL_RAW:
cmd[k++] = "raw";
+ break;
+
+ case TRANSFER_IMPORT_FS:
+ cmd[k++] = "run";
+ break;
+
+ default:
+ break;
+ }
if (t->verify != _IMPORT_VERIFY_INVALID) {
cmd[k++] = "--verify";
@@ -510,7 +559,6 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void
struct ucred *ucred = NULL;
Manager *m = userdata;
struct cmsghdr *cmsg;
- unsigned percent;
char *p, *e;
Transfer *t;
Iterator i;
@@ -566,15 +614,15 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void
e = strchrnul(p, '\n');
*e = 0;
- r = safe_atou(p, &percent);
- if (r < 0 || percent > 100) {
+ r = parse_percent(p);
+ if (r < 0) {
log_warning("Got invalid percent value, ignoring.");
return 0;
}
- t->progress_percent = percent;
+ t->progress_percent = (unsigned) r;
- log_debug("Got percentage from client: %u%%", percent);
+ log_debug("Got percentage from client: %u%%", t->progress_percent);
return 0;
}
@@ -584,7 +632,6 @@ static int manager_new(Manager **ret) {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/import/notify",
};
- static const int one = 1;
int r;
assert(ret);
@@ -608,13 +655,14 @@ static int manager_new(Manager **ret) {
return -errno;
(void) mkdir_parents_label(sa.un.sun_path, 0755);
- (void) unlink(sa.un.sun_path);
+ (void) sockaddr_un_unlink(&sa.un);
if (bind(m->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
return -errno;
- if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
- return -errno;
+ r = setsockopt_int(m->notify_fd, SOL_SOCKET, SO_PASSCRED, true);
+ if (r < 0)
+ return r;
r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
if (r < 0)
@@ -633,12 +681,9 @@ static Transfer *manager_find(Manager *m, TransferType type, const char *remote)
assert(type >= 0);
assert(type < _TRANSFER_TYPE_MAX);
- HASHMAP_FOREACH(t, m->transfers, i) {
-
- if (t->type == type &&
- streq_ptr(t->remote, remote))
+ HASHMAP_FOREACH(t, m->transfers, i)
+ if (t->type == type && streq_ptr(t->remote, remote))
return t;
- }
return NULL;
}
@@ -672,10 +717,14 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (r < 0)
return r;
+ r = fd_verify_regular(fd);
+ if (r < 0)
+ return r;
+
if (!machine_name_is_valid(local))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
- r = setup_machine_directory((uint64_t) -1, error);
+ r = setup_machine_directory(error);
if (r < 0)
return r;
@@ -708,6 +757,72 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
return sd_bus_reply_method_return(msg, "uo", id, object);
}
+static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
+ _cleanup_(transfer_unrefp) Transfer *t = NULL;
+ int fd, force, read_only, r;
+ const char *local, *object;
+ Manager *m = userdata;
+ uint32_t id;
+
+ assert(msg);
+ assert(m);
+
+ r = bus_verify_polkit_async(
+ msg,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.import1.import",
+ NULL,
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
+ if (r < 0)
+ return r;
+
+ r = fd_verify_directory(fd);
+ if (r < 0)
+ return r;
+
+ if (!machine_name_is_valid(local))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
+
+ r = setup_machine_directory(error);
+ if (r < 0)
+ return r;
+
+ r = transfer_new(m, &t);
+ if (r < 0)
+ return r;
+
+ t->type = TRANSFER_IMPORT_FS;
+ t->force_local = force;
+ t->read_only = read_only;
+
+ t->local = strdup(local);
+ if (!t->local)
+ return -ENOMEM;
+
+ t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (t->stdin_fd < 0)
+ return -errno;
+
+ r = transfer_start(t);
+ if (r < 0)
+ return r;
+
+ object = t->object_path;
+ id = t->id;
+ t = NULL;
+
+ return sd_bus_reply_method_return(msg, "uo", id, object);
+}
+
static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(transfer_unrefp) Transfer *t = NULL;
int fd, r;
@@ -740,6 +855,10 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (!machine_name_is_valid(local))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
+ r = fd_verify_regular(fd);
+ if (r < 0)
+ return r;
+
type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;
r = transfer_new(m, &t);
@@ -818,7 +937,7 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
if (v < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
- r = setup_machine_directory((uint64_t) -1, error);
+ r = setup_machine_directory(error);
if (r < 0)
return r;
@@ -883,7 +1002,7 @@ static int method_list_transfers(sd_bus_message *msg, void *userdata, sd_bus_err
transfer_type_to_string(t->type),
t->remote,
t->local,
- (double) t->progress_percent / 100.0,
+ transfer_percent_as_double(t),
t->object_path);
if (r < 0)
return r;
@@ -979,7 +1098,7 @@ static int property_get_progress(
assert(reply);
assert(t);
- return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
+ return sd_bus_message_append(reply, "d", transfer_percent_as_double(t));
}
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
@@ -1002,6 +1121,7 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ImportFileSystem", "hsbb", "uo", method_import_fs, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1113,40 +1233,34 @@ static int manager_run(Manager *m) {
m);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_(manager_unrefp) Manager *m = NULL;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
if (argc != 1) {
log_error("This program takes no arguments.");
- r = -EINVAL;
- goto finish;
+ return -EINVAL;
}
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
r = manager_new(&m);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate manager object: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate manager object: %m");
r = manager_add_bus_objects(m);
if (r < 0)
- goto finish;
+ return r;
r = manager_run(m);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/import/meson.build b/src/import/meson.build
index 283ba08c67..1c15fd883f 100644
--- a/src/import/meson.build
+++ b/src/import/meson.build
@@ -38,6 +38,12 @@ systemd_import_sources = files('''
qcow2-util.h
'''.split())
+systemd_import_fs_sources = files('''
+ import-fs.c
+ import-common.c
+ import-common.h
+'''.split())
+
systemd_export_sources = files('''
export.c
export-tar.c
diff --git a/src/import/org.freedesktop.import1.conf b/src/import/org.freedesktop.import1.conf
index 9889cd6b10..2fdb2ba77c 100644
--- a/src/import/org.freedesktop.import1.conf
+++ b/src/import/org.freedesktop.import1.conf
@@ -48,6 +48,26 @@
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
+ send_member="ImportTar"/>
+
+ <allow send_destination="org.freedesktop.import1"
+ send_interface="org.freedesktop.import1.Manager"
+ send_member="ImportRaw"/>
+
+ <allow send_destination="org.freedesktop.import1"
+ send_interface="org.freedesktop.import1.Manager"
+ send_member="ImportFileSystem"/>
+
+ <allow send_destination="org.freedesktop.import1"
+ send_interface="org.freedesktop.import1.Manager"
+ send_member="ExportTar"/>
+
+ <allow send_destination="org.freedesktop.import1"
+ send_interface="org.freedesktop.import1.Manager"
+ send_member="ExportRaw"/>
+
+ <allow send_destination="org.freedesktop.import1"
+ send_interface="org.freedesktop.import1.Manager"
send_member="PullTar"/>
<allow send_destination="org.freedesktop.import1"
diff --git a/src/import/pull-common.c b/src/import/pull-common.c
index 4117ed2e6a..acfe380969 100644
--- a/src/import/pull-common.c
+++ b/src/import/pull-common.c
@@ -14,6 +14,7 @@
#include "process-util.h"
#include "pull-common.h"
#include "pull-job.h"
+#include "rlimit-util.h"
#include "rm-rf.h"
#include "signal-util.h"
#include "siphash24.h"
@@ -338,10 +339,9 @@ static int verify_one(PullJob *checksum_job, PullJob *job) {
if (r < 0)
return log_oom();
- if (!filename_is_valid(fn)) {
- log_error("Cannot verify checksum, could not determine server-side file name.");
- return -EBADMSG;
- }
+ if (!filename_is_valid(fn))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Cannot verify checksum, could not determine server-side file name.");
line = strjoina(job->checksum, " *", fn, "\n");
@@ -359,10 +359,9 @@ static int verify_one(PullJob *checksum_job, PullJob *job) {
strlen(line));
}
- if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
- log_error("DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn);
- return -EBADMSG;
- }
+ if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n'))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn);
log_info("SHA256 checksum of %s is valid.", job->url);
return 1;
@@ -474,6 +473,8 @@ int pull_verify(PullJob *main_job,
_exit(EXIT_FAILURE);
}
+ (void) rlimit_nofile_safe();
+
cmd[k++] = strjoina("--homedir=", gpg_home);
/* We add the user keyring only to the command line
@@ -520,8 +521,7 @@ int pull_verify(PullJob *main_job,
}
finish:
- if (sig_file >= 0)
- (void) unlink(sig_file_path);
+ (void) unlink(sig_file_path);
if (gpg_home_created)
(void) rm_rf(gpg_home, REMOVE_ROOT|REMOVE_PHYSICAL);
diff --git a/src/import/pull-job.c b/src/import/pull-job.c
index 89dd20afa3..a44e0a7eda 100644
--- a/src/import/pull-job.c
+++ b/src/import/pull-job.c
@@ -4,6 +4,7 @@
#include "alloc-util.h"
#include "fd-util.h"
+#include "gcrypt-util.h"
#include "hexdecoct.h"
#include "import-util.h"
#include "io-util.h"
@@ -73,7 +74,6 @@ static int pull_job_restart(PullJob *j) {
j->payload_allocated = 0;
j->written_compressed = 0;
j->written_uncompressed = 0;
- j->written_since_last_grow = 0;
r = pull_job_begin(j);
if (r < 0)
@@ -213,33 +213,27 @@ static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata)
if (sz <= 0)
return 0;
- if (j->written_uncompressed + sz < j->written_uncompressed) {
- log_error("File too large, overflow");
- return -EOVERFLOW;
- }
+ if (j->written_uncompressed + sz < j->written_uncompressed)
+ return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW),
+ "File too large, overflow");
- if (j->written_uncompressed + sz > j->uncompressed_max) {
- log_error("File overly large, refusing");
- return -EFBIG;
- }
+ if (j->written_uncompressed + sz > j->uncompressed_max)
+ return log_error_errno(SYNTHETIC_ERRNO(EFBIG),
+ "File overly large, refusing");
if (j->disk_fd >= 0) {
- if (j->grow_machine_directory && j->written_since_last_grow >= GROW_INTERVAL_BYTES) {
- j->written_since_last_grow = 0;
- grow_machine_directory();
- }
-
if (j->allow_sparse)
n = sparse_write(j->disk_fd, p, sz, 64);
- else
+ else {
n = write(j->disk_fd, p, sz);
- if (n < 0)
- return log_error_errno(errno, "Failed to write file: %m");
- if ((size_t) n < sz) {
- log_error("Short write");
- return -EIO;
+ if (n < 0)
+ n = -errno;
}
+ if (n < 0)
+ return log_error_errno((int) n, "Failed to write file: %m");
+ if ((size_t) n < sz)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write");
} else {
if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz))
@@ -250,7 +244,6 @@ static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata)
}
j->written_uncompressed += sz;
- j->written_since_last_grow += sz;
return 0;
}
@@ -264,21 +257,16 @@ static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) {
if (sz <= 0)
return 0;
- if (j->written_compressed + sz < j->written_compressed) {
- log_error("File too large, overflow");
- return -EOVERFLOW;
- }
+ if (j->written_compressed + sz < j->written_compressed)
+ return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow");
- if (j->written_compressed + sz > j->compressed_max) {
- log_error("File overly large, refusing.");
- return -EFBIG;
- }
+ if (j->written_compressed + sz > j->compressed_max)
+ return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "File overly large, refusing.");
if (j->content_length != (uint64_t) -1 &&
- j->written_compressed + sz > j->content_length) {
- log_error("Content length incorrect.");
- return -EFBIG;
- }
+ j->written_compressed + sz > j->content_length)
+ return log_error_errno(SYNTHETIC_ERRNO(EFBIG),
+ "Content length incorrect.");
if (j->checksum_context)
gcry_md_write(j->checksum_context, p, sz);
@@ -317,10 +305,11 @@ static int pull_job_open_disk(PullJob *j) {
}
if (j->calc_checksum) {
- if (gcry_md_open(&j->checksum_context, GCRY_MD_SHA256, 0) != 0) {
- log_error("Failed to initialize hash context.");
- return -EIO;
- }
+ initialize_libgcrypt(false);
+
+ if (gcry_md_open(&j->checksum_context, GCRY_MD_SHA256, 0) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to initialize hash context.");
}
return 0;
@@ -541,28 +530,33 @@ static int pull_job_progress_callback(void *userdata, curl_off_t dltotal, curl_o
int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata) {
_cleanup_(pull_job_unrefp) PullJob *j = NULL;
+ _cleanup_free_ char *u = NULL;
assert(url);
assert(glue);
assert(ret);
- j = new0(PullJob, 1);
- if (!j)
+ u = strdup(url);
+ if (u)
return -ENOMEM;
- j->state = PULL_JOB_INIT;
- j->disk_fd = -1;
- j->userdata = userdata;
- j->glue = glue;
- j->content_length = (uint64_t) -1;
- j->start_usec = now(CLOCK_MONOTONIC);
- j->compressed_max = j->uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU; /* 64GB safety limit */
- j->style = VERIFICATION_STYLE_UNSET;
-
- j->url = strdup(url);
- if (!j->url)
+ j = new(PullJob, 1);
+ if (!j)
return -ENOMEM;
+ *j = (PullJob) {
+ .state = PULL_JOB_INIT,
+ .disk_fd = -1,
+ .userdata = userdata,
+ .glue = glue,
+ .content_length = (uint64_t) -1,
+ .start_usec = now(CLOCK_MONOTONIC),
+ .compressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
+ .uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
+ .style = VERIFICATION_STYLE_UNSET,
+ .url = TAKE_PTR(u),
+ };
+
*ret = TAKE_PTR(j);
return 0;
@@ -576,9 +570,6 @@ int pull_job_begin(PullJob *j) {
if (j->state != PULL_JOB_INIT)
return -EBUSY;
- if (j->grow_machine_directory)
- grow_machine_directory();
-
r = curl_glue_make(&j->curl, j->url, j);
if (r < 0)
return r;
diff --git a/src/import/pull-job.h b/src/import/pull-job.h
index 0c104af758..c907e74060 100644
--- a/src/import/pull-job.h
+++ b/src/import/pull-job.h
@@ -80,9 +80,6 @@ struct PullJob {
char *checksum;
- bool grow_machine_directory;
- uint64_t written_since_last_grow;
-
VerificationStyle style;
};
diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c
index e68f197c79..3a3e015df8 100644
--- a/src/import/pull-raw.c
+++ b/src/import/pull-raw.c
@@ -12,7 +12,6 @@
#include "copy.h"
#include "curl-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "import-common.h"
@@ -27,6 +26,7 @@
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "utf8.h"
#include "util.h"
#include "web-util.h"
@@ -56,7 +56,6 @@ struct RawPull {
char *local;
bool force_local;
- bool grow_machine_directory;
bool settings;
bool roothash;
@@ -115,36 +114,42 @@ int raw_pull_new(
RawPullFinished on_finished,
void *userdata) {
+ _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_(raw_pull_unrefp) RawPull *i = NULL;
+ _cleanup_free_ char *root = NULL;
int r;
assert(ret);
- i = new0(RawPull, 1);
- if (!i)
+ root = strdup(image_root ?: "/var/lib/machines");
+ if (!root)
return -ENOMEM;
- i->on_finished = on_finished;
- i->userdata = userdata;
-
- i->image_root = strdup(image_root ?: "/var/lib/machines");
- if (!i->image_root)
- return -ENOMEM;
-
- i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
-
if (event)
- i->event = sd_event_ref(event);
+ e = sd_event_ref(event);
else {
- r = sd_event_default(&i->event);
+ r = sd_event_default(&e);
if (r < 0)
return r;
}
- r = curl_glue_new(&i->glue, i->event);
+ r = curl_glue_new(&g, e);
if (r < 0)
return r;
+ i = new(RawPull, 1);
+ if (!i)
+ return -ENOMEM;
+
+ *i = (RawPull) {
+ .on_finished = on_finished,
+ .userdata = userdata,
+ .image_root = TAKE_PTR(root),
+ .event = TAKE_PTR(e),
+ .glue = TAKE_PTR(g),
+ };
+
i->glue->on_finished = pull_job_curl_on_finished;
i->glue->userdata = i;
@@ -237,7 +242,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);
+ 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);
@@ -353,7 +358,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
* 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);
+ 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);
@@ -595,7 +600,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);
+ 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);
@@ -679,7 +684,6 @@ int raw_pull_start(
i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
i->raw_job->on_progress = raw_pull_job_on_progress;
i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO;
- i->raw_job->grow_machine_directory = i->grow_machine_directory;
r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
if (r < 0)
diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c
index 56ec252e98..e7a208e904 100644
--- a/src/import/pull-tar.c
+++ b/src/import/pull-tar.c
@@ -10,7 +10,6 @@
#include "copy.h"
#include "curl-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "import-common.h"
@@ -25,6 +24,7 @@
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "utf8.h"
#include "util.h"
#include "web-util.h"
@@ -52,7 +52,6 @@ struct TarPull {
char *local;
bool force_local;
- bool grow_machine_directory;
bool settings;
pid_t tar_pid;
@@ -108,36 +107,42 @@ int tar_pull_new(
TarPullFinished on_finished,
void *userdata) {
+ _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_(tar_pull_unrefp) TarPull *i = NULL;
+ _cleanup_free_ char *root = NULL;
int r;
assert(ret);
- i = new0(TarPull, 1);
- if (!i)
+ root = strdup(image_root ?: "/var/lib/machines");
+ if (!root)
return -ENOMEM;
- i->on_finished = on_finished;
- i->userdata = userdata;
-
- i->image_root = strdup(image_root ?: "/var/lib/machines");
- if (!i->image_root)
- return -ENOMEM;
-
- i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
-
if (event)
- i->event = sd_event_ref(event);
+ e = sd_event_ref(event);
else {
- r = sd_event_default(&i->event);
+ r = sd_event_default(&e);
if (r < 0)
return r;
}
- r = curl_glue_new(&i->glue, i->event);
+ r = curl_glue_new(&g, e);
if (r < 0)
return r;
+ i = new(TarPull, 1);
+ if (!i)
+ return -ENOMEM;
+
+ *i = (TarPull) {
+ .on_finished = on_finished,
+ .userdata = userdata,
+ .image_root = TAKE_PTR(root),
+ .event = TAKE_PTR(e),
+ .glue = TAKE_PTR(g),
+ };
+
i->glue->on_finished = pull_job_curl_on_finished;
i->glue->userdata = i;
@@ -502,7 +507,6 @@ int tar_pull_start(
i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar;
i->tar_job->on_progress = tar_pull_job_on_progress;
i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
- i->tar_job->grow_machine_directory = i->grow_machine_directory;
r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
if (r < 0)
diff --git a/src/import/pull.c b/src/import/pull.c
index 26be5bbf0b..3376992588 100644
--- a/src/import/pull.c
+++ b/src/import/pull.c
@@ -9,6 +9,7 @@
#include "hostname-util.h"
#include "import-util.h"
#include "machine-image.h"
+#include "main-func.h"
#include "parse-util.h"
#include "pull-raw.h"
#include "pull-tar.h"
@@ -270,17 +271,16 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERIFY:
arg_verify = import_verify_from_string(optarg);
- if (arg_verify < 0) {
- log_error("Invalid verification setting '%s'", optarg);
- return -EINVAL;
- }
+ if (arg_verify < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid verification setting '%s'", optarg);
break;
case ARG_SETTINGS:
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse --settings= parameter '%s'", optarg);
+ return log_error_errno(r, "Failed to parse --settings= parameter '%s': %m", optarg);
arg_settings = r;
break;
@@ -288,7 +288,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_ROOTHASH:
r = parse_boolean(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse --roothash= parameter '%s'", optarg);
+ return log_error_errno(r, "Failed to parse --roothash= parameter '%s': %m", optarg);
arg_roothash = r;
break;
@@ -304,7 +304,6 @@ static int parse_argv(int argc, char *argv[]) {
}
static int pull_main(int argc, char *argv[]) {
-
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "tar", 2, 3, 0, pull_tar },
@@ -315,7 +314,7 @@ static int pull_main(int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, NULL);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
setlocale(LC_ALL, "");
@@ -324,12 +323,11 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
(void) ignore_signals(SIGPIPE, -1);
- r = pull_main(argc, argv);
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return pull_main(argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c
index 0c5aab6968..c60d4bd740 100644
--- a/src/initctl/initctl.c
+++ b/src/initctl/initctl.c
@@ -81,7 +81,7 @@ static const char *translate_runlevel(int runlevel, bool *isolate) {
return NULL;
}
-static void change_runlevel(Server *s, int runlevel) {
+static int change_runlevel(Server *s, int runlevel) {
const char *target;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *mode;
@@ -93,7 +93,7 @@ static void change_runlevel(Server *s, int runlevel) {
target = translate_runlevel(runlevel, &isolate);
if (!target) {
log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
- return;
+ return 0;
}
if (isolate)
@@ -112,10 +112,10 @@ static void change_runlevel(Server *s, int runlevel) {
&error,
NULL,
"ss", target, mode);
- if (r < 0) {
- log_error("Failed to change runlevel: %s", bus_error_message(&error, -r));
- return;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to change runlevel: %s", bus_error_message(&error, -r));
+
+ return 0;
}
static void request_process(Server *s, const struct init_request *req) {
@@ -156,7 +156,7 @@ static void request_process(Server *s, const struct init_request *req) {
break;
default:
- change_runlevel(s, req->runlevel);
+ (void) change_runlevel(s, req->runlevel);
}
return;
@@ -322,10 +322,9 @@ static int process_event(Server *s, struct epoll_event *ev) {
assert(s);
- if (!(ev->events & EPOLLIN)) {
- log_info("Got invalid event from epoll. (3)");
- return -EIO;
- }
+ if (!(ev->events & EPOLLIN))
+ return log_info_errno(SYNTHETIC_ERRNO(EIO),
+ "Got invalid event from epoll. (3)");
f = (Fifo*) ev->data.ptr;
r = fifo_process(f);
@@ -352,9 +351,7 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
diff --git a/src/journal-remote/browse.html b/src/journal-remote/browse.html
index 32848c7673..e5162d1088 100644
--- a/src/journal-remote/browse.html
+++ b/src/journal-remote/browse.html
@@ -236,10 +236,12 @@
function entriesLoad(range) {
- if (range == null)
- range = localStorage["cursor"] + ":0";
- if (range == null)
- range = "";
+ if (range == null) {
+ if (localStorage["cursor"] != null && localStorage["cursor"] != "")
+ range = localStorage["cursor"] + ":0";
+ else
+ range = "";
+ }
var url = "/entries";
@@ -304,7 +306,6 @@
var buf = '';
for (i in l) {
-
if (l[i] == '')
continue;
@@ -322,6 +323,7 @@
else
priority = 6;
+ var clazz;
if (priority <= 3)
clazz = "message-error";
else if (priority <= 5)
@@ -388,7 +390,7 @@
var d = JSON.parse(event.currentTarget.responseText);
document.getElementById("diventry").style.display = "block";
- entry = document.getElementById("tableentry");
+ var entry = document.getElementById("tableentry");
var buf = "";
for (var key in d) {
@@ -455,7 +457,7 @@
(event.currentTarget.status != 200 && event.currentTarget.status != 0))
return;
- f = document.getElementById("filter");
+ var f = document.getElementById("filter");
var l = event.currentTarget.responseText.split('\n');
var buf = '<option>No filter</option>';
@@ -511,11 +513,12 @@
}
function initFilter() {
- f = document.getElementById("filter");
+ var f = document.getElementById("filter");
var buf = '<option>No filter</option>';
var filter = localStorage["filter"];
+ var j;
if (filter != null && filter != "") {
buf += '<option value="' + escape(filter) + '">' + escapeHTML(filter) + '</option>';
j = 1;
@@ -529,7 +532,7 @@
function installHandlers() {
document.onkeyup = onKeyUp;
- logs = document.getElementById("divlogs");
+ var logs = document.getElementById("divlogs");
logs.addEventListener("mousewheel", onMouseWheel, false);
logs.addEventListener("DOMMouseScroll", onMouseWheel, false);
}
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 9e77e314ff..4185af63e1 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -18,10 +18,13 @@
#include "hostname-util.h"
#include "log.h"
#include "logs-show.h"
+#include "main-func.h"
#include "microhttpd-util.h"
#include "os-util.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "sigbus.h"
+#include "tmpfile-util.h"
#include "util.h"
#define JOURNAL_WAIT_TIMEOUT (10*USEC_PER_SEC)
@@ -29,7 +32,11 @@
static char *arg_key_pem = NULL;
static char *arg_cert_pem = NULL;
static char *arg_trust_pem = NULL;
-static char *arg_directory = NULL;
+static const char *arg_directory = NULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_key_pem, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep);
typedef struct RequestMeta {
sd_journal *journal;
@@ -57,6 +64,7 @@ static const char* const mime_types[_OUTPUT_MODE_MAX] = {
[OUTPUT_SHORT] = "text/plain",
[OUTPUT_JSON] = "application/json",
[OUTPUT_JSON_SSE] = "text/event-stream",
+ [OUTPUT_JSON_SEQ] = "application/json-seq",
[OUTPUT_EXPORT] = "application/vnd.fdo.journal",
};
@@ -266,6 +274,8 @@ static int request_parse_accept(
m->mode = OUTPUT_JSON;
else if (streq(header, mime_types[OUTPUT_JSON_SSE]))
m->mode = OUTPUT_JSON_SSE;
+ else if (streq(header, mime_types[OUTPUT_JSON_SEQ]))
+ m->mode = OUTPUT_JSON_SEQ;
else if (streq(header, mime_types[OUTPUT_EXPORT]))
m->mode = OUTPUT_EXPORT;
else
@@ -859,7 +869,14 @@ static int request_handler(
return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-journal-gatewayd.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] ...\n\n"
"HTTP server for journal events.\n\n"
" -h --help Show this help\n"
@@ -867,8 +884,13 @@ static void help(void) {
" --cert=CERT.PEM Server certificate in PEM format\n"
" --key=KEY.PEM Server key in PEM format\n"
" --trust=CERT.PEM Certificate authority certificate in PEM format\n"
- " -D --directory=PATH Serve journal files in directory\n",
- program_invocation_short_name);
+ " -D --directory=PATH Serve journal files in directory\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -899,17 +921,15 @@ static int parse_argv(int argc, char *argv[]) {
switch(c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
case ARG_KEY:
- if (arg_key_pem) {
- log_error("Key file specified twice");
- return -EINVAL;
- }
+ if (arg_key_pem)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Key file specified twice");
r = read_full_file(optarg, &arg_key_pem, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read key file: %m");
@@ -917,10 +937,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CERT:
- if (arg_cert_pem) {
- log_error("Certificate file specified twice");
- return -EINVAL;
- }
+ if (arg_cert_pem)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Certificate file specified twice");
r = read_full_file(optarg, &arg_cert_pem, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read certificate file: %m");
@@ -929,18 +948,17 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_TRUST:
#if HAVE_GNUTLS
- if (arg_trust_pem) {
- log_error("CA certificate file specified twice");
- return -EINVAL;
- }
+ if (arg_trust_pem)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "CA certificate file specified twice");
r = read_full_file(optarg, &arg_trust_pem, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read CA certificate file: %m");
assert(arg_trust_pem);
break;
#else
- log_error("Option --trust is not available.");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --trust is not available.");
#endif
case 'D':
arg_directory = optarg;
@@ -953,115 +971,100 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind < argc) {
- log_error("This program does not take arguments.");
- return -EINVAL;
- }
+ if (optind < argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "This program does not take arguments.");
- if (!!arg_key_pem != !!arg_cert_pem) {
- log_error("Certificate and key files must be specified together");
- return -EINVAL;
- }
+ if (!!arg_key_pem != !!arg_cert_pem)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Certificate and key files must be specified together");
- if (arg_trust_pem && !arg_key_pem) {
- log_error("CA certificate can only be used with certificate file");
- return -EINVAL;
- }
+ if (arg_trust_pem && !arg_key_pem)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "CA certificate can only be used with certificate file");
return 1;
}
-int main(int argc, char *argv[]) {
- struct MHD_Daemon *d = NULL;
+static int run(int argc, char *argv[]) {
+ _cleanup_(MHD_stop_daemonp) struct MHD_Daemon *d = NULL;
+ struct MHD_OptionItem opts[] = {
+ { MHD_OPTION_NOTIFY_COMPLETED,
+ (intptr_t) request_meta_free, NULL },
+ { MHD_OPTION_EXTERNAL_LOGGER,
+ (intptr_t) microhttpd_logger, NULL },
+ { MHD_OPTION_END, 0, NULL },
+ { MHD_OPTION_END, 0, NULL },
+ { MHD_OPTION_END, 0, NULL },
+ { MHD_OPTION_END, 0, NULL },
+ { MHD_OPTION_END, 0, NULL },
+ };
+ int opts_pos = 2;
+
+ /* We force MHD_USE_ITC here, in order to make sure
+ * libmicrohttpd doesn't use shutdown() on our listening
+ * socket, which would break socket re-activation. See
+ *
+ * https://lists.gnu.org/archive/html/libmicrohttpd/2015-09/msg00014.html
+ * https://github.com/systemd/systemd/pull/1286
+ */
+
+ int flags =
+ MHD_USE_DEBUG |
+ MHD_USE_DUAL_STACK |
+ MHD_USE_ITC |
+ MHD_USE_POLL_INTERNAL_THREAD |
+ MHD_USE_THREAD_PER_CONNECTION;
int r, n;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
r = parse_argv(argc, argv);
- if (r < 0)
- return EXIT_FAILURE;
- if (r == 0)
- return EXIT_SUCCESS;
+ if (r <= 0)
+ return r;
sigbus_install();
r = setup_gnutls_logger(NULL);
if (r < 0)
- return EXIT_FAILURE;
+ return r;
n = sd_listen_fds(1);
- if (n < 0) {
- log_error_errno(n, "Failed to determine passed sockets: %m");
- goto finish;
- } else if (n > 1) {
- log_error("Can't listen on more than one socket.");
- goto finish;
- } else {
- struct MHD_OptionItem opts[] = {
- { MHD_OPTION_NOTIFY_COMPLETED,
- (intptr_t) request_meta_free, NULL },
- { MHD_OPTION_EXTERNAL_LOGGER,
- (intptr_t) microhttpd_logger, NULL },
- { MHD_OPTION_END, 0, NULL },
- { MHD_OPTION_END, 0, NULL },
- { MHD_OPTION_END, 0, NULL },
- { MHD_OPTION_END, 0, NULL },
- { MHD_OPTION_END, 0, NULL }};
- int opts_pos = 2;
-
- /* We force MHD_USE_ITC here, in order to make sure
- * libmicrohttpd doesn't use shutdown() on our listening
- * socket, which would break socket re-activation. See
- *
- * https://lists.gnu.org/archive/html/libmicrohttpd/2015-09/msg00014.html
- * https://github.com/systemd/systemd/pull/1286
- */
-
- int flags =
- MHD_USE_DEBUG |
- MHD_USE_DUAL_STACK |
- MHD_USE_ITC |
- MHD_USE_POLL_INTERNAL_THREAD |
- MHD_USE_THREAD_PER_CONNECTION;
-
- if (n > 0)
- opts[opts_pos++] = (struct MHD_OptionItem)
- {MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START};
- if (arg_key_pem) {
- assert(arg_cert_pem);
- opts[opts_pos++] = (struct MHD_OptionItem)
- {MHD_OPTION_HTTPS_MEM_KEY, 0, arg_key_pem};
- opts[opts_pos++] = (struct MHD_OptionItem)
- {MHD_OPTION_HTTPS_MEM_CERT, 0, arg_cert_pem};
- flags |= MHD_USE_TLS;
- }
- if (arg_trust_pem) {
- assert(flags & MHD_USE_TLS);
- opts[opts_pos++] = (struct MHD_OptionItem)
- {MHD_OPTION_HTTPS_MEM_TRUST, 0, arg_trust_pem};
- }
-
- d = MHD_start_daemon(flags, 19531,
- NULL, NULL,
- request_handler, NULL,
- MHD_OPTION_ARRAY, opts,
- MHD_OPTION_END);
+ if (n < 0)
+ return log_error_errno(n, "Failed to determine passed sockets: %m");
+ if (n > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't listen on more than one socket.");
+
+ if (n == 1)
+ opts[opts_pos++] = (struct MHD_OptionItem)
+ { MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START };
+
+ if (arg_key_pem) {
+ assert(arg_cert_pem);
+ opts[opts_pos++] = (struct MHD_OptionItem)
+ { MHD_OPTION_HTTPS_MEM_KEY, 0, arg_key_pem };
+ opts[opts_pos++] = (struct MHD_OptionItem)
+ { MHD_OPTION_HTTPS_MEM_CERT, 0, arg_cert_pem };
+ flags |= MHD_USE_TLS;
}
- if (!d) {
- log_error("Failed to start daemon!");
- goto finish;
+ if (arg_trust_pem) {
+ assert(flags & MHD_USE_TLS);
+ opts[opts_pos++] = (struct MHD_OptionItem)
+ { MHD_OPTION_HTTPS_MEM_TRUST, 0, arg_trust_pem };
}
- pause();
-
- r = EXIT_SUCCESS;
+ d = MHD_start_daemon(flags, 19531,
+ NULL, NULL,
+ request_handler, NULL,
+ MHD_OPTION_ARRAY, opts,
+ MHD_OPTION_END);
+ if (!d)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to start daemon!");
-finish:
- if (d)
- MHD_stop_daemon(d);
+ pause();
- return r;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c
index 8fda9d1499..e1748cb46b 100644
--- a/src/journal-remote/journal-remote-main.c
+++ b/src/journal-remote/journal-remote-main.c
@@ -6,12 +6,16 @@
#include "sd-daemon.h"
#include "conf-parser.h"
+#include "daemon-util.h"
#include "def.h"
#include "fd-util.h"
#include "fileio.h"
#include "journal-remote-write.h"
#include "journal-remote.h"
+#include "main-func.h"
+#include "pretty-print.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "stat-util.h"
@@ -22,25 +26,30 @@
#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem"
#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
-static char* arg_url = NULL;
-static char* arg_getter = NULL;
-static char* arg_listen_raw = NULL;
-static char* arg_listen_http = NULL;
-static char* arg_listen_https = NULL;
-static char** arg_files = NULL;
+static const char* arg_url = NULL;
+static const char* arg_getter = NULL;
+static const char* arg_listen_raw = NULL;
+static const char* arg_listen_http = NULL;
+static const char* arg_listen_https = NULL;
+static char** arg_files = NULL; /* Do not free this. */
static int arg_compress = true;
static int arg_seal = false;
static int http_socket = -1, https_socket = -1;
static char** arg_gnutls_log = NULL;
static JournalWriteSplitMode arg_split_mode = _JOURNAL_WRITE_SPLIT_INVALID;
-static char* arg_output = NULL;
+static const char* arg_output = NULL;
static char *arg_key = NULL;
static char *arg_cert = NULL;
static char *arg_trust = NULL;
static bool arg_trust_all = false;
+STATIC_DESTRUCTOR_REGISTER(arg_gnutls_log, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_key, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_cert, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_trust, freep);
+
static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
[JOURNAL_WRITE_SPLIT_NONE] = "none",
[JOURNAL_WRITE_SPLIT_HOST] = "host",
@@ -79,6 +88,8 @@ static int spawn_child(const char* child, char** argv) {
_exit(EXIT_FAILURE);
}
+ (void) rlimit_nofile_safe();
+
execvp(child, argv);
log_error_errno(errno, "Failed to exec child %s: %m", child);
_exit(EXIT_FAILURE);
@@ -492,11 +503,10 @@ static int dispatch_http_event(sd_event_source *event,
assert(d);
r = MHD_run(d->daemon);
- if (r == MHD_NO) {
- log_error("MHD_run failed!");
- // XXX: unregister daemon
- return -EINVAL;
- }
+ if (r == MHD_NO)
+ // FIXME: unregister daemon
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "MHD_run failed!");
if (MHD_get_timeout(d->daemon, &timeout) == MHD_NO)
timeout = ULONG_LONG_MAX;
@@ -558,7 +568,9 @@ static int create_remoteserver(
if (r < 0)
return r;
- setup_signals(s);
+ r = setup_signals(s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up signals: %m");
n = sd_listen_fds(true);
if (n < 0)
@@ -566,10 +578,9 @@ static int create_remoteserver(
else
log_debug("Received %d descriptors", n);
- if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
- log_error("Received fewer sockets than expected");
- return -EBADFD;
- }
+ if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADFD),
+ "Received fewer sockets than expected");
for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
@@ -591,15 +602,12 @@ static int create_remoteserver(
log_debug("Received a connection socket (fd:%d) from %s", fd, hostname);
r = journal_remote_add_source(s, fd, hostname, true);
- } else {
- log_error("Unknown socket passed on fd:%d", fd);
-
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown socket passed on fd:%d", fd);
if (r < 0)
- return log_error_errno(r, "Failed to register socket (fd:%d): %m",
- fd);
+ return log_error_errno(r, "Failed to register socket (fd:%d): %m", fd);
}
if (arg_getter) {
@@ -614,16 +622,14 @@ static int create_remoteserver(
}
if (arg_url) {
- const char *url;
- char *hostname, *p;
+ const char *url, *hostname;
if (!strstr(arg_url, "/entries")) {
if (endswith(arg_url, "/"))
url = strjoina(arg_url, "entries");
else
url = strjoina(arg_url, "/entries");
- }
- else
+ } else
url = strdupa(arg_url);
log_info("Spawning curl %s...", url);
@@ -631,18 +637,13 @@ static int create_remoteserver(
if (fd < 0)
return fd;
- hostname =
- startswith(arg_url, "https://") ?:
- startswith(arg_url, "http://") ?:
- arg_url;
+ hostname = STARTSWITH_SET(arg_url, "https://", "http://");
+ if (!hostname)
+ hostname = arg_url;
- hostname = strdupa(hostname);
- if ((p = strchr(hostname, '/')))
- *p = '\0';
- if ((p = strchr(hostname, ':')))
- *p = '\0';
+ hostname = strndupa(hostname, strcspn(hostname, "/:"));
- r = journal_remote_add_source(s, fd, hostname, false);
+ r = journal_remote_add_source(s, fd, (char *) hostname, false);
if (r < 0)
return r;
}
@@ -688,10 +689,9 @@ static int create_remoteserver(
return r;
}
- if (s->active == 0) {
- log_error("Zero sources specified");
- return -EINVAL;
- }
+ if (s->active == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Zero sources specified");
if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
/* In this case we know what the writer will be
@@ -727,7 +727,8 @@ static int parse_config(void) {
{ "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
{ "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
{ "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
- {}};
+ {}
+ };
return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf",
CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
@@ -735,7 +736,14 @@ static int parse_config(void) {
CONFIG_PARSE_WARN, NULL);
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-journal-remote.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] {FILE|-}...\n\n"
"Write external journal events to journal file(s).\n\n"
" -h --help Show this help\n"
@@ -757,9 +765,13 @@ static void help(void) {
" --gnutls-log=CATEGORY...\n"
" Specify a list of gnutls logging categories\n"
" --split-mode=none|host How many output files to create\n"
- "\n"
- "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
- , program_invocation_short_name);
+ "\nNote: file descriptors from sd_listen_fds() will be consumed, too.\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -806,45 +818,41 @@ static int parse_argv(int argc, char *argv[]) {
while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
switch(c) {
+
case 'h':
- help();
- return 0 /* done */;
+ return help();
case ARG_VERSION:
return version();
case ARG_URL:
- if (arg_url) {
- log_error("cannot currently set more than one --url");
- return -EINVAL;
- }
+ if (arg_url)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot currently set more than one --url");
arg_url = optarg;
break;
case ARG_GETTER:
- if (arg_getter) {
- log_error("cannot currently use --getter more than once");
- return -EINVAL;
- }
+ if (arg_getter)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot currently use --getter more than once");
arg_getter = optarg;
break;
case ARG_LISTEN_RAW:
- if (arg_listen_raw) {
- log_error("cannot currently use --listen-raw more than once");
- return -EINVAL;
- }
+ if (arg_listen_raw)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot currently use --listen-raw more than once");
arg_listen_raw = optarg;
break;
case ARG_LISTEN_HTTP:
- if (arg_listen_http || http_socket >= 0) {
- log_error("cannot currently use --listen-http more than once");
- return -EINVAL;
- }
+ if (arg_listen_http || http_socket >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot currently use --listen-http more than once");
r = negative_fd(optarg);
if (r >= 0)
@@ -854,10 +862,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_LISTEN_HTTPS:
- if (arg_listen_https || https_socket >= 0) {
- log_error("cannot currently use --listen-https more than once");
- return -EINVAL;
- }
+ if (arg_listen_https || https_socket >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot currently use --listen-https more than once");
r = negative_fd(optarg);
if (r >= 0)
@@ -868,10 +875,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_KEY:
- if (arg_key) {
- log_error("Key file specified twice");
- return -EINVAL;
- }
+ if (arg_key)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Key file specified twice");
arg_key = strdup(optarg);
if (!arg_key)
@@ -880,10 +886,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CERT:
- if (arg_cert) {
- log_error("Certificate file specified twice");
- return -EINVAL;
- }
+ if (arg_cert)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Certificate file specified twice");
arg_cert = strdup(optarg);
if (!arg_cert)
@@ -892,10 +897,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TRUST:
- if (arg_trust || arg_trust_all) {
- log_error("Confusing trusted CA configuration");
- return -EINVAL;
- }
+ if (arg_trust || arg_trust_all)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Confusing trusted CA configuration");
if (streq(optarg, "all"))
arg_trust_all = true;
@@ -905,37 +909,34 @@ static int parse_argv(int argc, char *argv[]) {
if (!arg_trust)
return log_oom();
#else
- log_error("Option --trust is not available.");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --trust is not available.");
#endif
}
break;
case 'o':
- if (arg_output) {
- log_error("cannot use --output/-o more than once");
- return -EINVAL;
- }
+ if (arg_output)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot use --output/-o more than once");
arg_output = optarg;
break;
case ARG_SPLIT_MODE:
arg_split_mode = journal_write_split_mode_from_string(optarg);
- if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
- log_error("Invalid split mode: %s", optarg);
- return -EINVAL;
- }
+ if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid split mode: %s", optarg);
break;
case ARG_COMPRESS:
if (optarg) {
r = parse_boolean(optarg);
- if (r < 0) {
- log_error("Failed to parse --compress= parameter.");
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse --compress= parameter.");
arg_compress = !!r;
} else
@@ -946,10 +947,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_SEAL:
if (optarg) {
r = parse_boolean(optarg);
- if (r < 0) {
- log_error("Failed to parse --seal= parameter.");
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse --seal= parameter.");
arg_seal = !!r;
} else
@@ -966,7 +966,6 @@ static int parse_argv(int argc, char *argv[]) {
r = extract_first_word(&p, &word, ",", 0);
if (r < 0)
return log_error_errno(r, "Failed to parse --gnutls-log= argument: %m");
-
if (r == 0)
break;
@@ -977,8 +976,8 @@ static int parse_argv(int argc, char *argv[]) {
}
break;
#else
- log_error("Option --gnutls-log is not available.");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --gnutls-log is not available.");
#endif
}
@@ -997,21 +996,18 @@ static int parse_argv(int argc, char *argv[]) {
|| arg_listen_raw
|| arg_listen_http || arg_listen_https
|| sd_listen_fds(false) > 0;
- if (type_a && type_b) {
- log_error("Cannot use file input or --getter with "
- "--arg-listen-... or socket activation.");
- return -EINVAL;
- }
+ if (type_a && type_b)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot use file input or --getter with "
+ "--arg-listen-... or socket activation.");
if (type_a) {
- if (!arg_output) {
- log_error("Option --output must be specified with file input or --getter.");
- return -EINVAL;
- }
+ if (!arg_output)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --output must be specified with file input or --getter.");
- if (!IN_SET(arg_split_mode, JOURNAL_WRITE_SPLIT_NONE, _JOURNAL_WRITE_SPLIT_INVALID)) {
- log_error("For active sources, only --split-mode=none is allowed.");
- return -EINVAL;
- }
+ if (!IN_SET(arg_split_mode, JOURNAL_WRITE_SPLIT_NONE, _JOURNAL_WRITE_SPLIT_INVALID))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "For active sources, only --split-mode=none is allowed.");
arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
}
@@ -1020,21 +1016,18 @@ static int parse_argv(int argc, char *argv[]) {
arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE && arg_output) {
- if (is_dir(arg_output, true) > 0) {
- log_error("For SplitMode=none, output must be a file.");
- return -EINVAL;
- }
- if (!endswith(arg_output, ".journal")) {
- log_error("For SplitMode=none, output file name must end with .journal.");
- return -EINVAL;
- }
+ if (is_dir(arg_output, true) > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "For SplitMode=none, output must be a file.");
+ if (!endswith(arg_output, ".journal"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "For SplitMode=none, output file name must end with .journal.");
}
if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
- && arg_output && is_dir(arg_output, true) <= 0) {
- log_error("For SplitMode=host, output must be a directory.");
- return -EINVAL;
- }
+ && arg_output && is_dir(arg_output, true) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "For SplitMode=host, output must be a directory.");
log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
journal_write_split_mode_to_string(arg_split_mode),
@@ -1067,79 +1060,82 @@ static int load_certificates(char **key, char **cert, char **trust) {
arg_trust ?: TRUST_FILE);
}
- if ((arg_listen_raw || arg_listen_http) && *trust) {
- log_error("Option --trust makes all non-HTTPS connections untrusted.");
- return -EINVAL;
- }
+ if ((arg_listen_raw || arg_listen_http) && *trust)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --trust makes all non-HTTPS connections untrusted.");
return 0;
}
-int main(int argc, char **argv) {
- RemoteServer s = {};
- int r;
+static int run(int argc, char **argv) {
+ _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
+ _cleanup_(journal_remote_server_destroy) RemoteServer s = {};
_cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
+ int r;
log_show_color(true);
log_parse_environment();
+ /* The journal merging logic potentially needs a lot of fds. */
+ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
r = parse_config();
if (r < 0)
- return EXIT_FAILURE;
+ return r;
r = parse_argv(argc, argv);
if (r <= 0)
- return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ return r;
if (arg_listen_http || arg_listen_https) {
r = setup_gnutls_logger(arg_gnutls_log);
if (r < 0)
- return EXIT_FAILURE;
+ return r;
}
- if (arg_listen_https || https_socket >= 0)
- if (load_certificates(&key, &cert, &trust) < 0)
- return EXIT_FAILURE;
+ if (arg_listen_https || https_socket >= 0) {
+ r = load_certificates(&key, &cert, &trust);
+ if (r < 0)
+ return r;
+
+ s.check_trust = !arg_trust_all;
+ }
- if (create_remoteserver(&s, key, cert, trust) < 0)
- return EXIT_FAILURE;
+ r = create_remoteserver(&s, key, cert, trust);
+ if (r < 0)
+ return r;
r = sd_event_set_watchdog(s.events, true);
if (r < 0)
- log_error_errno(r, "Failed to enable watchdog: %m");
- else
- log_debug("Watchdog is %sd.", enable_disable(r > 0));
+ return log_error_errno(r, "Failed to enable watchdog: %m");
+
+ log_debug("Watchdog is %sd.", enable_disable(r > 0));
log_debug("%s running as pid "PID_FMT,
program_invocation_short_name, getpid_cached());
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing requests...");
+
+ notify_message = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
while (s.active) {
r = sd_event_get_state(s.events);
if (r < 0)
- break;
+ return r;
if (r == SD_EVENT_FINISHED)
break;
r = sd_event_run(s.events, -1);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- break;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
}
- sd_notifyf(false,
- "STOPPING=1\n"
- "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
- log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
+ notify_message = NULL;
+ (void) sd_notifyf(false,
+ "STOPPING=1\n"
+ "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
- journal_remote_server_destroy(&s);
-
- free(arg_key);
- free(arg_cert);
- free(arg_trust);
+ log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
- return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c
index 9211e7df82..188ff3582d 100644
--- a/src/journal-remote/journal-remote-write.c
+++ b/src/journal-remote/journal-remote-write.c
@@ -34,7 +34,7 @@ Writer* writer_new(RemoteServer *server) {
return w;
}
-Writer* writer_free(Writer *w) {
+static Writer* writer_free(Writer *w) {
if (!w)
return NULL;
@@ -54,19 +54,7 @@ Writer* writer_free(Writer *w) {
return mfree(w);
}
-Writer* writer_unref(Writer *w) {
- if (w && (-- w->n_ref <= 0))
- writer_free(w);
-
- return NULL;
-}
-
-Writer* writer_ref(Writer *w) {
- if (w)
- assert_se(++ w->n_ref >= 2);
-
- return w;
-}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Writer, writer, writer_free);
int writer_write(Writer *w,
struct iovec_wrapper *iovw,
diff --git a/src/journal-remote/journal-remote-write.h b/src/journal-remote/journal-remote-write.h
index 7ed32aa5aa..e445859ecf 100644
--- a/src/journal-remote/journal-remote-write.h
+++ b/src/journal-remote/journal-remote-write.h
@@ -16,12 +16,10 @@ typedef struct Writer {
uint64_t seqnum;
- int n_ref;
+ unsigned n_ref;
} Writer;
Writer* writer_new(RemoteServer* server);
-Writer* writer_free(Writer *w);
-
Writer* writer_ref(Writer *w);
Writer* writer_unref(Writer *w);
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index beb75a1cb4..3c0916c438 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -346,7 +346,7 @@ static void MHDDaemonWrapper_free(MHDDaemonWrapper *d) {
}
#endif
-RemoteServer* journal_remote_server_destroy(RemoteServer *s) {
+void journal_remote_server_destroy(RemoteServer *s) {
size_t i;
#if HAVE_MICROHTTPD
@@ -370,7 +370,6 @@ RemoteServer* journal_remote_server_destroy(RemoteServer *s) {
journal_remote_server_global = NULL;
/* fds that we're listening on remain open... */
- return NULL;
}
/**********************************************************************
@@ -408,7 +407,7 @@ int journal_remote_handle_raw_source(
log_debug("%zu active sources remaining", s->active);
return 0;
} else if (r == -E2BIG) {
- log_notice_errno(E2BIG, "Entry too big, skipped");
+ log_notice("Entry too big, skipped");
return 1;
} else if (r == -EAGAIN) {
return 0;
diff --git a/src/journal-remote/journal-remote.conf.in b/src/journal-remote/journal-remote.conf.in
index 7122d63362..edc3abab4b 100644
--- a/src/journal-remote/journal-remote.conf.in
+++ b/src/journal-remote/journal-remote.conf.in
@@ -1,3 +1,16 @@
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
+#
+# See journal-remote.conf(5) for details
+
[Remote]
# Seal=false
# SplitMode=host
diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h
index e083ea9c74..4c25d43abf 100644
--- a/src/journal-remote/journal-remote.h
+++ b/src/journal-remote/journal-remote.h
@@ -62,4 +62,4 @@ int journal_remote_handle_raw_source(
uint32_t revents,
RemoteServer *s);
-RemoteServer* journal_remote_server_destroy(RemoteServer *s);
+void journal_remote_server_destroy(RemoteServer *s);
diff --git a/src/journal-remote/journal-upload-journal.c b/src/journal-remote/journal-upload-journal.c
index 3991dcb7d2..be39f7c047 100644
--- a/src/journal-remote/journal-upload-journal.c
+++ b/src/journal-remote/journal-upload-journal.c
@@ -34,7 +34,8 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) {
r = snprintf(buf + pos, size - pos,
"__CURSOR=%s\n", u->current_cursor);
- if (pos + r > size)
+ assert(r >= 0);
+ if ((size_t) r > size - pos)
/* not enough space */
return pos;
@@ -58,7 +59,8 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) {
r = snprintf(buf + pos, size - pos,
"__REALTIME_TIMESTAMP="USEC_FMT"\n", realtime);
- if (r + pos > size)
+ assert(r >= 0);
+ if ((size_t) r > size - pos)
/* not enough space */
return pos;
@@ -83,7 +85,8 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) {
r = snprintf(buf + pos, size - pos,
"__MONOTONIC_TIMESTAMP="USEC_FMT"\n", monotonic);
- if (r + pos > size)
+ assert(r >= 0);
+ if ((size_t) r > size - pos)
/* not enough space */
return pos;
@@ -108,7 +111,8 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) {
r = snprintf(buf + pos, size - pos,
"_BOOT_ID=%s\n", sd_id128_to_string(boot_id, sid));
- if (r + pos > size)
+ assert(r >= 0);
+ if ((size_t) r > size - pos)
/* not enough space */
return pos;
@@ -180,10 +184,9 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) {
size_t len;
c = memchr(u->field_data, '=', u->field_length);
- if (!c || c == u->field_data) {
- log_error("Invalid field.");
- return -EINVAL;
- }
+ if (!c || c == u->field_data)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid field.");
len = c - (const char*)u->field_data;
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index 209e5011d9..1e08fcc554 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -10,19 +10,26 @@
#include "alloc-util.h"
#include "conf-parser.h"
+#include "daemon-util.h"
#include "def.h"
+#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "glob-util.h"
#include "journal-upload.h"
#include "log.h"
+#include "main-func.h"
#include "mkdir.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "sigbus.h"
#include "signal-util.h"
#include "string-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
#include "util.h"
#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
@@ -75,8 +82,7 @@ static size_t output_callback(char *buf,
if (nmemb && !u->answer) {
u->answer = strndup(buf, size*nmemb);
if (!u->answer)
- log_warning_errno(ENOMEM, "Failed to store server answer (%zu bytes): %m",
- size*nmemb);
+ log_warning("Failed to store server answer (%zu bytes): out of memory", size*nmemb);
}
return size * nmemb;
@@ -147,10 +153,7 @@ static int load_cursor_state(Uploader *u) {
if (!u->state_file)
return 0;
- r = parse_env_file(NULL, u->state_file, NEWLINE,
- "LAST_CURSOR", &u->last_cursor,
- NULL);
-
+ r = parse_env_file(NULL, u->state_file, "LAST_CURSOR", &u->last_cursor);
if (r == -ENOENT)
log_debug("State file %s is not present.", u->state_file);
else if (r < 0)
@@ -199,10 +202,9 @@ int start_upload(Uploader *u,
CURL *curl;
curl = curl_easy_init();
- if (!curl) {
- log_error("Call to curl_easy_init failed.");
- return -ENOSR;
- }
+ if (!curl)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOSR),
+ "Call to curl_easy_init failed.");
/* tell it to POST to the URL */
easy_setopt(curl, CURLOPT_POST, 1L,
@@ -266,11 +268,10 @@ int start_upload(Uploader *u,
/* upload to this place */
code = curl_easy_setopt(u->easy, CURLOPT_URL, u->url);
- if (code) {
- log_error("curl_easy_setopt CURLOPT_URL failed: %s",
- curl_easy_strerror(code));
- return -EXFULL;
- }
+ if (code)
+ return log_error_errno(SYNTHETIC_ERRNO(EXFULL),
+ "curl_easy_setopt CURLOPT_URL failed: %s",
+ curl_easy_strerror(code));
u->uploading = true;
@@ -279,30 +280,30 @@ int start_upload(Uploader *u,
static size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *userp) {
Uploader *u = userp;
-
- ssize_t r;
+ ssize_t n;
assert(u);
- assert(nmemb <= SSIZE_MAX / size);
+ assert(nmemb < SSIZE_MAX / size);
if (u->input < 0)
return 0;
- r = read(u->input, buf, size * nmemb);
- log_debug("%s: allowed %zu, read %zd", __func__, size*nmemb, r);
+ assert(!size_multiply_overflow(size, nmemb));
- if (r > 0)
- return r;
+ n = read(u->input, buf, size * nmemb);
+ log_debug("%s: allowed %zu, read %zd", __func__, size*nmemb, n);
+ if (n > 0)
+ return n;
u->uploading = false;
- if (r == 0) {
- log_debug("Reached EOF");
- close_fd_input(u);
- return 0;
- } else {
+ if (n < 0) {
log_error_errno(errno, "Aborting transfer after read error on input: %m.");
return CURL_READFUNC_ABORT;
}
+
+ log_debug("Reached EOF");
+ close_fd_input(u);
+ return 0;
}
static void close_fd_input(Uploader *u) {
@@ -409,10 +410,12 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file)
assert(u);
assert(url);
- memzero(u, sizeof(Uploader));
- u->input = -1;
+ *u = (Uploader) {
+ .input = -1
+ };
- if (!(host = startswith(url, "http://")) && !(host = startswith(url, "https://"))) {
+ host = STARTSWITH_SET(url, "http://", "https://");
+ if (!host) {
host = url;
proto = "https://";
}
@@ -489,21 +492,20 @@ static int perform_upload(Uploader *u) {
}
code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status);
- if (code) {
- log_error("Failed to retrieve response code: %s",
- curl_easy_strerror(code));
- return -EUCLEAN;
- }
-
- if (status >= 300) {
- log_error("Upload to %s failed with code %ld: %s",
- u->url, status, strna(u->answer));
- return -EIO;
- } else if (status < 200) {
- log_error("Upload to %s finished with unexpected code %ld: %s",
- u->url, status, strna(u->answer));
- return -EIO;
- } else
+ if (code)
+ return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN),
+ "Failed to retrieve response code: %s",
+ curl_easy_strerror(code));
+
+ if (status >= 300)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Upload to %s failed with code %ld: %s",
+ u->url, status, strna(u->answer));
+ else if (status < 200)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Upload to %s finished with unexpected code %ld: %s",
+ u->url, status, strna(u->answer));
+ else
log_debug("Upload finished successfully with code %ld: %s",
status, strna(u->answer));
@@ -526,7 +528,14 @@ static int parse_config(void) {
CONFIG_PARSE_WARN, NULL);
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-journal-upload.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s -u URL {FILE|-}...\n\n"
"Upload journal events to a remote server.\n\n"
" -h --help Show this help\n"
@@ -550,7 +559,12 @@ static void help(void) {
" --follow[=BOOL] Do [not] wait for input\n"
" --save-state[=FILE] Save uploaded cursors (default \n"
" " STATE_FILE ")\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -598,44 +612,39 @@ static int parse_argv(int argc, char *argv[]) {
while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
switch(c) {
case 'h':
- help();
- return 0 /* done */;
+ return help();
case ARG_VERSION:
return version();
case 'u':
- if (arg_url) {
- log_error("cannot use more than one --url");
- return -EINVAL;
- }
+ if (arg_url)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot use more than one --url");
arg_url = optarg;
break;
case ARG_KEY:
- if (arg_key) {
- log_error("cannot use more than one --key");
- return -EINVAL;
- }
+ if (arg_key)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot use more than one --key");
arg_key = optarg;
break;
case ARG_CERT:
- if (arg_cert) {
- log_error("cannot use more than one --cert");
- return -EINVAL;
- }
+ if (arg_cert)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot use more than one --cert");
arg_cert = optarg;
break;
case ARG_TRUST:
- if (arg_trust) {
- log_error("cannot use more than one --trust");
- return -EINVAL;
- }
+ if (arg_trust)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot use more than one --trust");
arg_trust = optarg;
break;
@@ -653,19 +662,17 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'M':
- if (arg_machine) {
- log_error("cannot use more than one --machine/-M");
- return -EINVAL;
- }
+ if (arg_machine)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot use more than one --machine/-M");
arg_machine = optarg;
break;
case 'D':
- if (arg_directory) {
- log_error("cannot use more than one --directory/-D");
- return -EINVAL;
- }
+ if (arg_directory)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot use more than one --directory/-D");
arg_directory = optarg;
break;
@@ -677,19 +684,17 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CURSOR:
- if (arg_cursor) {
- log_error("cannot use more than one --cursor/--after-cursor");
- return -EINVAL;
- }
+ if (arg_cursor)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot use more than one --cursor/--after-cursor");
arg_cursor = optarg;
break;
case ARG_AFTER_CURSOR:
- if (arg_cursor) {
- log_error("cannot use more than one --cursor/--after-cursor");
- return -EINVAL;
- }
+ if (arg_cursor)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "cannot use more than one --cursor/--after-cursor");
arg_cursor = optarg;
arg_after_cursor = true;
@@ -698,10 +703,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_FOLLOW:
if (optarg) {
r = parse_boolean(optarg);
- if (r < 0) {
- log_error("Failed to parse --follow= parameter.");
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse --follow= parameter.");
arg_follow = !!r;
} else
@@ -714,31 +718,30 @@ static int parse_argv(int argc, char *argv[]) {
break;
case '?':
- log_error("Unknown option %s.", argv[optind-1]);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown option %s.",
+ argv[optind - 1]);
case ':':
- log_error("Missing argument to %s.", argv[optind-1]);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Missing argument to %s.",
+ argv[optind - 1]);
default:
assert_not_reached("Unhandled option code.");
}
- if (!arg_url) {
- log_error("Required --url/-u option missing.");
- return -EINVAL;
- }
+ if (!arg_url)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Required --url/-u option missing.");
- if (!!arg_key != !!arg_cert) {
- log_error("Options --key and --cert must be used together.");
- return -EINVAL;
- }
+ if (!!arg_key != !!arg_cert)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Options --key and --cert must be used together.");
- if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type)) {
- log_error("Input arguments make no sense with journal input.");
- return -EINVAL;
- }
+ if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Input arguments make no sense with journal input.");
return 1;
}
@@ -760,33 +763,37 @@ static int open_journal(sd_journal **j) {
return r;
}
-int main(int argc, char **argv) {
- Uploader u;
- int r;
+static int run(int argc, char **argv) {
+ _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
+ _cleanup_(destroy_uploader) Uploader u = {};
bool use_journal;
+ int r;
log_show_color(true);
log_parse_environment();
+ /* The journal merging logic potentially needs a lot of fds. */
+ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
r = parse_config();
if (r < 0)
- goto finish;
+ return r;
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
sigbus_install();
r = setup_uploader(&u, arg_url, arg_save_state);
if (r < 0)
- goto cleanup;
+ return r;
sd_event_set_watchdog(u.events, true);
r = check_cursor_updating(&u);
if (r < 0)
- goto cleanup;
+ return r;
log_debug("%s running as pid "PID_FMT,
program_invocation_short_name, getpid_cached());
@@ -796,61 +803,51 @@ int main(int argc, char **argv) {
sd_journal *j;
r = open_journal(&j);
if (r < 0)
- goto finish;
+ return r;
r = open_journal_for_upload(&u, j,
arg_cursor ?: u.last_cursor,
arg_cursor ? arg_after_cursor : true,
!!arg_follow);
if (r < 0)
- goto finish;
+ return r;
}
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing input...");
+ notify_message = notify_start("READY=1\n"
+ "STATUS=Processing input...",
+ NOTIFY_STOPPING);
for (;;) {
r = sd_event_get_state(u.events);
if (r < 0)
- break;
+ return r;
if (r == SD_EVENT_FINISHED)
- break;
+ return 0;
if (use_journal) {
if (!u.journal)
- break;
+ return 0;
r = check_journal_input(&u);
} else if (u.input < 0 && !use_journal) {
if (optind >= argc)
- break;
+ return 0;
log_debug("Using %s as input.", argv[optind]);
r = open_file_for_upload(&u, argv[optind++]);
}
if (r < 0)
- goto cleanup;
+ return r;
if (u.uploading) {
r = perform_upload(&u);
if (r < 0)
- break;
+ return r;
}
r = sd_event_run(u.events, u.timeout);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- break;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
}
-
-cleanup:
- sd_notify(false,
- "STOPPING=1\n"
- "STATUS=Shutting down...");
-
- destroy_uploader(&u);
-
-finish:
- return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/journal-remote/journal-upload.conf.in b/src/journal-remote/journal-upload.conf.in
index c5670682e8..5f59a6fe80 100644
--- a/src/journal-remote/journal-upload.conf.in
+++ b/src/journal-remote/journal-upload.conf.in
@@ -1,3 +1,16 @@
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
+#
+# See journal-upload.conf(5) for details
+
[Upload]
# URL=
# ServerKeyFile=@CERTIFICATEROOT@/private/journal-upload.pem
diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c
index 34dd9ea555..adf40b5abd 100644
--- a/src/journal-remote/microhttpd-util.c
+++ b/src/journal-remote/microhttpd-util.c
@@ -153,8 +153,7 @@ static int log_enable_gnutls_category(const char *cat) {
log_reset_gnutls_level();
return 0;
}
- log_error("No such log category: %s", cat);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No such log category: %s", cat);
}
int setup_gnutls_logger(char **categories) {
@@ -206,10 +205,9 @@ static int get_client_cert(gnutls_session_t session, gnutls_x509_crt_t *client_c
assert(client_cert);
pcert = gnutls_certificate_get_peers(session, &listsize);
- if (!pcert || !listsize) {
- log_error("Failed to retrieve certificate chain");
- return -EINVAL;
- }
+ if (!pcert || !listsize)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to retrieve certificate chain");
r = gnutls_x509_crt_init(&cert);
if (r < 0) {
diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h
index a50a2a75c7..364cd0f7cf 100644
--- a/src/journal-remote/microhttpd-util.h
+++ b/src/journal-remote/microhttpd-util.h
@@ -73,3 +73,5 @@ int check_permissions(struct MHD_Connection *connection, int *code, char **hostn
* interesting events without overwhelming detail.
*/
int setup_gnutls_logger(char **categories);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct MHD_Daemon*, MHD_stop_daemon);
diff --git a/src/journal/audit-type.c b/src/journal/audit-type.c
index b30963da66..7b3dc1e9ab 100644
--- a/src/journal/audit-type.c
+++ b/src/journal/audit-type.c
@@ -1,12 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <stdio.h>
-#include <linux/audit.h>
-#if HAVE_AUDIT
-# include <libaudit.h>
-#endif
-
-#include "missing.h"
#include "audit-type.h"
+#include "missing_audit.h"
+
#include "audit_type-to-name.h"
-#include "macro.h"
diff --git a/src/journal/audit-type.h b/src/journal/audit-type.h
index 965b044cbf..069a883490 100644
--- a/src/journal/audit-type.h
+++ b/src/journal/audit-type.h
@@ -1,6 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <alloca.h>
+#include <stdio.h>
+
#include "macro.h"
const char *audit_type_to_string(int type);
diff --git a/src/journal/cat.c b/src/journal/cat.c
index 8a11c8c3d1..a84350fbc9 100644
--- a/src/journal/cat.c
+++ b/src/journal/cat.c
@@ -9,8 +9,11 @@
#include "sd-journal.h"
+#include "alloc-util.h"
#include "fd-util.h"
+#include "main-func.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "string-util.h"
#include "syslog-util.h"
#include "util.h"
@@ -19,7 +22,14 @@ static const char *arg_identifier = NULL;
static int arg_priority = LOG_INFO;
static bool arg_level_prefix = true;
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-cat", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Execute process with stdout/stderr connected to the journal.\n\n"
" -h --help Show this help\n"
@@ -27,7 +37,12 @@ static void help(void) {
" -t --identifier=STRING Set syslog identifier\n"
" -p --priority=PRIORITY Set priority value (0..7)\n"
" --level-prefix=BOOL Control whether level prefix shall be parsed\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -71,10 +86,9 @@ static int parse_argv(int argc, char *argv[]) {
case 'p':
arg_priority = log_level_from_string(optarg);
- if (arg_priority < 0) {
- log_error("Failed to parse priority value.");
- return -EINVAL;
- }
+ if (arg_priority < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse priority value.");
break;
case ARG_LEVEL_PREFIX: {
@@ -98,7 +112,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_close_ int fd = -1, saved_stderr = -1;
int r;
@@ -107,22 +121,18 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix);
- if (fd < 0) {
- r = log_error_errno(fd, "Failed to create stream fd: %m");
- goto finish;
- }
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to create stream fd: %m");
saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3);
r = rearrange_stdio(STDIN_FILENO, fd, fd); /* Invalidates fd on succcess + error! */
- fd = -1;
- if (r < 0) {
- log_error_errno(r, "Failed to rearrange stdout/stderr: %m");
- goto finish;
- }
+ TAKE_FD(fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to rearrange stdout/stderr: %m");
if (argc <= optind)
(void) execl("/bin/cat", "/bin/cat", NULL);
@@ -134,8 +144,7 @@ int main(int argc, char *argv[]) {
if (saved_stderr >= 0)
(void) dup3(saved_stderr, STDERR_FILENO, 0);
- log_error_errno(r, "Failed to execute process: %m");
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return log_error_errno(r, "Failed to execute process: %m");
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/journal/catalog.c b/src/journal/catalog.c
index f9118f0b62..3556a101bf 100644
--- a/src/journal/catalog.c
+++ b/src/journal/catalog.c
@@ -24,6 +24,7 @@
#include "strbuf.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "util.h"
const char * const catalog_file_dirs[] = {
@@ -49,31 +50,25 @@ typedef struct CatalogItem {
le64_t offset;
} CatalogItem;
-static void catalog_hash_func(const void *p, struct siphash *state) {
- const CatalogItem *i = p;
-
+static void catalog_hash_func(const CatalogItem *i, struct siphash *state) {
siphash24_compress(&i->id, sizeof(i->id), state);
siphash24_compress(i->language, strlen(i->language), state);
}
-static int catalog_compare_func(const void *a, const void *b) {
- const CatalogItem *i = a, *j = b;
+static int catalog_compare_func(const CatalogItem *a, const CatalogItem *b) {
unsigned k;
+ int r;
- for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) {
- if (i->id.bytes[k] < j->id.bytes[k])
- return -1;
- if (i->id.bytes[k] > j->id.bytes[k])
- return 1;
+ for (k = 0; k < ELEMENTSOF(b->id.bytes); k++) {
+ r = CMP(a->id.bytes[k], b->id.bytes[k]);
+ if (r != 0)
+ return r;
}
- return strcmp(i->language, j->language);
+ return strcmp(a->language, b->language);
}
-const struct hash_ops catalog_hash_ops = {
- .hash = catalog_hash_func,
- .compare = catalog_compare_func
-};
+DEFINE_HASH_OPS(catalog_hash_ops, CatalogItem, catalog_hash_func, catalog_compare_func);
static bool next_header(const char **s) {
const char *e;
@@ -218,14 +213,14 @@ static int catalog_entry_lang(const char* filename, int line,
size_t c;
c = strlen(t);
- if (c == 0) {
- log_error("[%s:%u] Language too short.", filename, line);
- return -EINVAL;
- }
- if (c > 31) {
- log_error("[%s:%u] language too long.", filename, line);
- return -EINVAL;
- }
+ if (c < 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "[%s:%u] Language too short.",
+ filename, line);
+ if (c > 31)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "[%s:%u] language too long.", filename,
+ line);
if (deflang) {
if (streq(t, deflang)) {
@@ -268,26 +263,23 @@ int catalog_import_file(Hashmap *h, const char *path) {
log_debug("File %s has language %s.", path, deflang);
for (;;) {
- char line[LINE_MAX];
+ _cleanup_free_ char *line = NULL;
size_t line_len;
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- break;
-
- return log_error_errno(errno, "Failed to read file %s: %m", path);
- }
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read file %s: %m", path);
+ if (r == 0)
+ break;
n++;
- truncate_nl(line);
-
- if (line[0] == 0) {
+ if (isempty(line)) {
empty_line = true;
continue;
}
- if (strchr(COMMENTS "\n", line[0]))
+ if (strchr(COMMENTS, line[0]))
continue;
if (empty_line &&
@@ -308,10 +300,11 @@ int catalog_import_file(Hashmap *h, const char *path) {
if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
if (got_id) {
- if (payload_size == 0) {
- log_error("[%s:%u] No payload text.", path, n);
- return -EINVAL;
- }
+ if (payload_size == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "[%s:%u] No payload text.",
+ path,
+ n);
r = finish_item(h, id, lang ?: deflang, payload, payload_size);
if (r < 0)
@@ -339,10 +332,10 @@ int catalog_import_file(Hashmap *h, const char *path) {
}
/* Payload */
- if (!got_id) {
- log_error("[%s:%u] Got payload before ID.", path, n);
- return -EINVAL;
- }
+ if (!got_id)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "[%s:%u] Got payload before ID.",
+ path, n);
line_len = strlen(line);
if (!GREEDY_REALLOC(payload, payload_allocated,
@@ -360,10 +353,10 @@ int catalog_import_file(Hashmap *h, const char *path) {
}
if (got_id) {
- if (payload_size == 0) {
- log_error("[%s:%u] No payload text.", path, n);
- return -EINVAL;
- }
+ if (payload_size == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "[%s:%u] No payload text.",
+ path, n);
r = finish_item(h, id, lang ?: deflang, payload, payload_size);
if (r < 0)
@@ -495,7 +488,7 @@ int catalog_update(const char* database, const char* root, const char* const* di
}
assert(n == hashmap_size(h));
- qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func);
+ typesafe_qsort(items, n, catalog_compare_func);
strbuf_complete(sb);
@@ -567,21 +560,21 @@ static const char *find_id(void *p, sd_id128_t id) {
strncpy(key.language, loc, sizeof(key.language));
key.language[strcspn(key.language, ".@")] = 0;
- f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
+ f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), (comparison_fn_t) catalog_compare_func);
if (!f) {
char *e;
e = strchr(key.language, '_');
if (e) {
*e = 0;
- f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
+ f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), (comparison_fn_t) catalog_compare_func);
}
}
}
if (!f) {
zero(key.language);
- f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
+ f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), (comparison_fn_t) catalog_compare_func);
}
if (!f)
diff --git a/src/journal/compress.c b/src/journal/compress.c
index 6baf15c8ff..e95ce2bcaa 100644
--- a/src/journal/compress.c
+++ b/src/journal/compress.c
@@ -95,11 +95,7 @@ int compress_blob_lz4(const void *src, uint64_t src_size,
if (src_size < 9)
return -ENOBUFS;
-#if LZ4_VERSION_NUMBER >= 10700
r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
-#else
- r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
-#endif
if (r <= 0)
return -ENOBUFS;
@@ -294,7 +290,6 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
* prefix */
int r;
- size_t size;
assert(src);
assert(src_size > 0);
@@ -311,23 +306,37 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8,
prefix_len + 1, *buffer_size);
- if (r >= 0)
- size = (unsigned) r;
- else {
- /* lz4 always tries to decode full "sequence", so in
- * pathological cases might need to decompress the
- * full field. */
+ /* One lz4 < 1.8.3, we might get "failure" (r < 0), or "success" where
+ * just a part of the buffer is decompressed. But if we get a smaller
+ * amount of bytes than requested, we don't know whether there isn't enough
+ * data to fill the requested size or whether we just got a partial answer.
+ */
+ if (r < 0 || (size_t) r < prefix_len + 1) {
+ size_t size;
+
+ if (LZ4_versionNumber() >= 10803)
+ /* We trust that the newer lz4 decompresses the number of bytes we
+ * requested if available in the compressed string. */
+ return 0;
+
+ if (r > 0)
+ /* Compare what we have first, in case of mismatch we can
+ * shortcut the full comparison. */
+ if (memcmp(*buffer, prefix, r) != 0)
+ return 0;
+
+ /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
+ * so in pathological cases might need to decompress the full field. */
r = decompress_blob_lz4(src, src_size, buffer, buffer_size, &size, 0);
if (r < 0)
return r;
- }
- if (size >= prefix_len + 1)
- return memcmp(*buffer, prefix, prefix_len) == 0 &&
- ((const uint8_t*) *buffer)[prefix_len] == extra;
- else
- return 0;
+ if (size < prefix_len + 1)
+ return 0;
+ }
+ return memcmp(*buffer, prefix, prefix_len) == 0 &&
+ ((const uint8_t*) *buffer)[prefix_len] == extra;
#else
return -EPROTONOSUPPORT;
#endif
diff --git a/src/journal/fsprg.h b/src/journal/fsprg.h
index bf78c3e9c3..33412675b8 100644
--- a/src/journal/fsprg.h
+++ b/src/journal/fsprg.h
@@ -1,7 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#ifndef __fsprgh__
-#define __fsprgh__
+#pragma once
/*
* fsprg v0.1 - (seekable) forward-secure pseudorandom generator
@@ -22,7 +20,6 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
- *
*/
#include <inttypes.h>
@@ -63,5 +60,3 @@ void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx);
#ifdef __cplusplus
}
#endif
-
-#endif
diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h
index 43f70c861a..e48260206f 100644
--- a/src/journal/journal-def.h
+++ b/src/journal/journal-def.h
@@ -10,7 +10,6 @@
* If you change this file you probably should also change its documentation:
*
* http://www.freedesktop.org/wiki/Software/systemd/journal-files
- *
*/
typedef struct Header Header;
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 62e7f68a13..56827f9f36 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -10,6 +10,8 @@
#include <sys/uio.h>
#include <unistd.h>
+#include "sd-event.h"
+
#include "alloc-util.h"
#include "btrfs-util.h"
#include "chattr-util.h"
@@ -23,7 +25,6 @@
#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
-#include "sd-event.h"
#include "set.h"
#include "stat-util.h"
#include "string-util.h"
@@ -235,8 +236,7 @@ int journal_file_set_offline(JournalFile *f, bool wait) {
sigset_t ss, saved_ss;
int k;
- if (sigfillset(&ss) < 0)
- return -errno;
+ assert_se(sigfillset(&ss) >= 0);
r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss);
if (r > 0)
@@ -349,11 +349,8 @@ JournalFile* journal_file_close(JournalFile *f) {
#endif
if (f->post_change_timer) {
- int enabled;
-
- if (sd_event_source_get_enabled(f->post_change_timer, &enabled) >= 0)
- if (enabled == SD_EVENT_ONESHOT)
- journal_file_post_change(f);
+ if (sd_event_source_get_enabled(f->post_change_timer, NULL) > 0)
+ journal_file_post_change(f);
(void) sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_OFF);
sd_event_source_unref(f->post_change_timer);
@@ -372,7 +369,7 @@ JournalFile* journal_file_close(JournalFile *f) {
* reenable all the good bits COW usually provides
* (such as data checksumming). */
- (void) chattr_fd(f->fd, 0, FS_NOCOW_FL);
+ (void) chattr_fd(f->fd, 0, FS_NOCOW_FL, NULL);
(void) btrfs_defrag_fd(f->fd);
}
@@ -568,13 +565,14 @@ static int journal_file_verify_header(JournalFile *f) {
if (state == STATE_ARCHIVED)
return -ESHUTDOWN; /* Already archived */
- else if (state == STATE_ONLINE) {
- log_debug("Journal file %s is already online. Assuming unclean closing.", f->path);
- return -EBUSY;
- } else if (state != STATE_OFFLINE) {
- log_debug("Journal file %s has unknown state %i.", f->path, state);
- return -EBUSY;
- }
+ else if (state == STATE_ONLINE)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBUSY),
+ "Journal file %s is already online. Assuming unclean closing.",
+ f->path);
+ else if (state != STATE_OFFLINE)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBUSY),
+ "Journal file %s has unknown state %i.",
+ f->path, state);
if (f->header->field_hash_table_size == 0 || f->header->data_hash_table_size == 0)
return -EBADMSG;
@@ -582,10 +580,10 @@ static int journal_file_verify_header(JournalFile *f) {
/* Don't permit appending to files from the future. Because otherwise the realtime timestamps wouldn't
* be strictly ordered in the entries in the file anymore, and we can't have that since it breaks
* bisection. */
- if (le64toh(f->header->tail_entry_realtime) > now(CLOCK_REALTIME)) {
- log_debug("Journal file %s is from the future, refusing to append new data to it that'd be older.", f->path);
- return -ETXTBSY;
- }
+ if (le64toh(f->header->tail_entry_realtime) > now(CLOCK_REALTIME))
+ return log_debug_errno(SYNTHETIC_ERRNO(ETXTBSY),
+ "Journal file %s is from the future, refusing to append new data to it that'd be older.",
+ f->path);
}
f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header);
@@ -749,153 +747,124 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
switch (o->object.type) {
case OBJECT_DATA: {
- if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
- log_debug("Bad n_entries: %"PRIu64": %"PRIu64,
- le64toh(o->data.n_entries), offset);
- return -EBADMSG;
- }
-
- if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
- log_debug("Bad object size (<= %zu): %"PRIu64": %"PRIu64,
- offsetof(DataObject, payload),
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
+ if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Bad n_entries: %" PRIu64 ": %" PRIu64,
+ le64toh(o->data.n_entries),
+ offset);
+
+ if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Bad object size (<= %zu): %" PRIu64 ": %" PRIu64,
+ offsetof(DataObject, payload),
+ le64toh(o->object.size),
+ offset);
if (!VALID64(le64toh(o->data.next_hash_offset)) ||
!VALID64(le64toh(o->data.next_field_offset)) ||
!VALID64(le64toh(o->data.entry_offset)) ||
- !VALID64(le64toh(o->data.entry_array_offset))) {
- log_debug("Invalid offset, next_hash_offset="OFSfmt", next_field_offset="OFSfmt
- ", entry_offset="OFSfmt", entry_array_offset="OFSfmt": %"PRIu64,
- le64toh(o->data.next_hash_offset),
- le64toh(o->data.next_field_offset),
- le64toh(o->data.entry_offset),
- le64toh(o->data.entry_array_offset),
- offset);
- return -EBADMSG;
- }
+ !VALID64(le64toh(o->data.entry_array_offset)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid offset, next_hash_offset=" OFSfmt ", next_field_offset=" OFSfmt ", entry_offset=" OFSfmt ", entry_array_offset=" OFSfmt ": %" PRIu64,
+ le64toh(o->data.next_hash_offset),
+ le64toh(o->data.next_field_offset),
+ le64toh(o->data.entry_offset),
+ le64toh(o->data.entry_array_offset),
+ offset);
break;
}
case OBJECT_FIELD:
- if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
- log_debug(
- "Bad field size (<= %zu): %"PRIu64": %"PRIu64,
- offsetof(FieldObject, payload),
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
+ if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Bad field size (<= %zu): %" PRIu64 ": %" PRIu64,
+ offsetof(FieldObject, payload),
+ le64toh(o->object.size),
+ offset);
if (!VALID64(le64toh(o->field.next_hash_offset)) ||
- !VALID64(le64toh(o->field.head_data_offset))) {
- log_debug(
- "Invalid offset, next_hash_offset="OFSfmt
- ", head_data_offset="OFSfmt": %"PRIu64,
- le64toh(o->field.next_hash_offset),
- le64toh(o->field.head_data_offset),
- offset);
- return -EBADMSG;
- }
+ !VALID64(le64toh(o->field.head_data_offset)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid offset, next_hash_offset=" OFSfmt ", head_data_offset=" OFSfmt ": %" PRIu64,
+ le64toh(o->field.next_hash_offset),
+ le64toh(o->field.head_data_offset),
+ offset);
break;
case OBJECT_ENTRY:
- if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
- log_debug(
- "Bad entry size (<= %zu): %"PRIu64": %"PRIu64,
- offsetof(EntryObject, items),
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
-
- if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
- log_debug(
- "Invalid number items in entry: %"PRIu64": %"PRIu64,
- (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem),
- offset);
- return -EBADMSG;
- }
-
- if (le64toh(o->entry.seqnum) <= 0) {
- log_debug(
- "Invalid entry seqnum: %"PRIx64": %"PRIu64,
- le64toh(o->entry.seqnum),
- offset);
- return -EBADMSG;
- }
-
- if (!VALID_REALTIME(le64toh(o->entry.realtime))) {
- log_debug(
- "Invalid entry realtime timestamp: %"PRIu64": %"PRIu64,
- le64toh(o->entry.realtime),
- offset);
- return -EBADMSG;
- }
-
- if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) {
- log_debug(
- "Invalid entry monotonic timestamp: %"PRIu64": %"PRIu64,
- le64toh(o->entry.monotonic),
- offset);
- return -EBADMSG;
- }
+ if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Bad entry size (<= %zu): %" PRIu64 ": %" PRIu64,
+ offsetof(EntryObject, items),
+ le64toh(o->object.size),
+ offset);
+
+ if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid number items in entry: %" PRIu64 ": %" PRIu64,
+ (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem),
+ offset);
+
+ if (le64toh(o->entry.seqnum) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid entry seqnum: %" PRIx64 ": %" PRIu64,
+ le64toh(o->entry.seqnum),
+ offset);
+
+ if (!VALID_REALTIME(le64toh(o->entry.realtime)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid entry realtime timestamp: %" PRIu64 ": %" PRIu64,
+ le64toh(o->entry.realtime),
+ offset);
+
+ if (!VALID_MONOTONIC(le64toh(o->entry.monotonic)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid entry monotonic timestamp: %" PRIu64 ": %" PRIu64,
+ le64toh(o->entry.monotonic),
+ offset);
break;
case OBJECT_DATA_HASH_TABLE:
case OBJECT_FIELD_HASH_TABLE:
if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
- (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) {
- log_debug(
- "Invalid %s hash table size: %"PRIu64": %"PRIu64,
- o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
+ (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid %s hash table size: %" PRIu64 ": %" PRIu64,
+ o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
+ le64toh(o->object.size),
+ offset);
break;
case OBJECT_ENTRY_ARRAY:
if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
- (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) {
- log_debug(
- "Invalid object entry array size: %"PRIu64": %"PRIu64,
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
-
- if (!VALID64(le64toh(o->entry_array.next_entry_array_offset))) {
- log_debug(
- "Invalid object entry array next_entry_array_offset: "OFSfmt": %"PRIu64,
- le64toh(o->entry_array.next_entry_array_offset),
- offset);
- return -EBADMSG;
- }
+ (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid object entry array size: %" PRIu64 ": %" PRIu64,
+ le64toh(o->object.size),
+ offset);
+
+ if (!VALID64(le64toh(o->entry_array.next_entry_array_offset)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid object entry array next_entry_array_offset: " OFSfmt ": %" PRIu64,
+ le64toh(o->entry_array.next_entry_array_offset),
+ offset);
break;
case OBJECT_TAG:
- if (le64toh(o->object.size) != sizeof(TagObject)) {
- log_debug(
- "Invalid object tag size: %"PRIu64": %"PRIu64,
- le64toh(o->object.size),
- offset);
- return -EBADMSG;
- }
+ if (le64toh(o->object.size) != sizeof(TagObject))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid object tag size: %" PRIu64 ": %" PRIu64,
+ le64toh(o->object.size),
+ offset);
- if (!VALID_EPOCH(le64toh(o->tag.epoch))) {
- log_debug(
- "Invalid object tag epoch: %"PRIu64": %"PRIu64,
- le64toh(o->tag.epoch),
- offset);
- return -EBADMSG;
- }
+ if (!VALID_EPOCH(le64toh(o->tag.epoch)))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid object tag epoch: %" PRIu64 ": %" PRIu64,
+ le64toh(o->tag.epoch), offset);
break;
}
@@ -914,16 +883,16 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
assert(ret);
/* Objects may only be located at multiple of 64 bit */
- if (!VALID64(offset)) {
- log_debug("Attempt to move to object at non-64bit boundary: %" PRIu64, offset);
- return -EBADMSG;
- }
+ if (!VALID64(offset))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to object at non-64bit boundary: %" PRIu64,
+ offset);
/* Object may not be located in the file header */
- if (offset < le64toh(f->header->header_size)) {
- log_debug("Attempt to move to object located in file header: %" PRIu64, offset);
- return -EBADMSG;
- }
+ if (offset < le64toh(f->header->header_size))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to object located in file header: %" PRIu64,
+ offset);
r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t, &tsize);
if (r < 0)
@@ -932,29 +901,29 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
o = (Object*) t;
s = le64toh(o->object.size);
- if (s == 0) {
- log_debug("Attempt to move to uninitialized object: %" PRIu64, offset);
- return -EBADMSG;
- }
- if (s < sizeof(ObjectHeader)) {
- log_debug("Attempt to move to overly short object: %" PRIu64, offset);
- return -EBADMSG;
- }
-
- if (o->object.type <= OBJECT_UNUSED) {
- log_debug("Attempt to move to object with invalid type: %" PRIu64, offset);
- return -EBADMSG;
- }
-
- if (s < minimum_header_size(o)) {
- log_debug("Attempt to move to truncated object: %" PRIu64, offset);
- return -EBADMSG;
- }
-
- if (type > OBJECT_UNUSED && o->object.type != type) {
- log_debug("Attempt to move to object of unexpected type: %" PRIu64, offset);
- return -EBADMSG;
- }
+ if (s == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to uninitialized object: %" PRIu64,
+ offset);
+ if (s < sizeof(ObjectHeader))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to overly short object: %" PRIu64,
+ offset);
+
+ if (o->object.type <= OBJECT_UNUSED)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to object with invalid type: %" PRIu64,
+ offset);
+
+ if (s < minimum_header_size(o))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to truncated object: %" PRIu64,
+ offset);
+
+ if (type > OBJECT_UNUSED && o->object.type != type)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Attempt to move to object of unexpected type: %" PRIu64,
+ offset);
if (s > tsize) {
r = journal_file_move_to(f, type, false, offset, s, &t, NULL);
@@ -1846,6 +1815,9 @@ static int journal_file_append_entry_internal(
void journal_file_post_change(JournalFile *f) {
assert(f);
+ if (f->fd < 0)
+ return;
+
/* inotify() does not receive IN_MODIFY events from file
* accesses done via mmap(). After each access we hence
* trigger IN_MODIFY by truncating the journal file to its
@@ -1866,37 +1838,33 @@ static int post_change_thunk(sd_event_source *timer, uint64_t usec, void *userda
}
static void schedule_post_change(JournalFile *f) {
- sd_event_source *timer;
- int enabled, r;
uint64_t now;
+ int r;
assert(f);
assert(f->post_change_timer);
- timer = f->post_change_timer;
-
- r = sd_event_source_get_enabled(timer, &enabled);
+ r = sd_event_source_get_enabled(f->post_change_timer, NULL);
if (r < 0) {
log_debug_errno(r, "Failed to get ftruncate timer state: %m");
goto fail;
}
-
- if (enabled == SD_EVENT_ONESHOT)
+ if (r > 0)
return;
- r = sd_event_now(sd_event_source_get_event(timer), CLOCK_MONOTONIC, &now);
+ r = sd_event_now(sd_event_source_get_event(f->post_change_timer), CLOCK_MONOTONIC, &now);
if (r < 0) {
log_debug_errno(r, "Failed to get clock's now for scheduling ftruncate: %m");
goto fail;
}
- r = sd_event_source_set_time(timer, now+f->post_change_timer_period);
+ r = sd_event_source_set_time(f->post_change_timer, now + f->post_change_timer_period);
if (r < 0) {
log_debug_errno(r, "Failed to set time for scheduling ftruncate: %m");
goto fail;
}
- r = sd_event_source_set_enabled(timer, SD_EVENT_ONESHOT);
+ r = sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_ONESHOT);
if (r < 0) {
log_debug_errno(r, "Failed to enable scheduled ftruncate: %m");
goto fail;
@@ -1933,14 +1901,8 @@ int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t)
return r;
}
-static int entry_item_cmp(const void *_a, const void *_b) {
- const EntryItem *a = _a, *b = _b;
-
- if (le64toh(a->object_offset) < le64toh(b->object_offset))
- return -1;
- if (le64toh(a->object_offset) > le64toh(b->object_offset))
- return 1;
- return 0;
+static int entry_item_cmp(const EntryItem *a, const EntryItem *b) {
+ return CMP(le64toh(a->object_offset), le64toh(b->object_offset));
}
int journal_file_append_entry(
@@ -1962,14 +1924,14 @@ int journal_file_append_entry(
assert(iovec || n_iovec == 0);
if (ts) {
- if (!VALID_REALTIME(ts->realtime)) {
- log_debug("Invalid realtime timestamp %"PRIu64", refusing entry.", ts->realtime);
- return -EBADMSG;
- }
- if (!VALID_MONOTONIC(ts->monotonic)) {
- log_debug("Invalid monotomic timestamp %"PRIu64", refusing entry.", ts->monotonic);
- return -EBADMSG;
- }
+ if (!VALID_REALTIME(ts->realtime))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid realtime timestamp %" PRIu64 ", refusing entry.",
+ ts->realtime);
+ if (!VALID_MONOTONIC(ts->monotonic))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid monotomic timestamp %" PRIu64 ", refusing entry.",
+ ts->monotonic);
} else {
dual_timestamp_get(&_ts);
ts = &_ts;
@@ -1999,7 +1961,7 @@ int journal_file_append_entry(
/* Order by the position on disk, in order to improve seek
* times for rotating media. */
- qsort_safe(items, n_iovec, sizeof(EntryItem), entry_item_cmp);
+ typesafe_qsort(items, n_iovec, entry_item_cmp);
r = journal_file_append_entry_internal(f, ts, boot_id, xor_hash, items, n_iovec, seqnum, ret, offset);
@@ -2619,6 +2581,8 @@ void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) {
}
int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
+ int r;
+
assert(af);
assert(af->header);
assert(bf);
@@ -2638,10 +2602,9 @@ int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
/* If this is from the same seqnum source, compare
* seqnums */
- if (af->current_seqnum < bf->current_seqnum)
- return -1;
- if (af->current_seqnum > bf->current_seqnum)
- return 1;
+ r = CMP(af->current_seqnum, bf->current_seqnum);
+ if (r != 0)
+ return r;
/* Wow! This is weird, different data but the same
* seqnums? Something is borked, but let's make the
@@ -2651,25 +2614,18 @@ int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
if (sd_id128_equal(af->current_boot_id, bf->current_boot_id)) {
/* If the boot id matches, compare monotonic time */
- if (af->current_monotonic < bf->current_monotonic)
- return -1;
- if (af->current_monotonic > bf->current_monotonic)
- return 1;
+ r = CMP(af->current_monotonic, bf->current_monotonic);
+ if (r != 0)
+ return r;
}
/* Otherwise, compare UTC time */
- if (af->current_realtime < bf->current_realtime)
- return -1;
- if (af->current_realtime > bf->current_realtime)
- return 1;
+ r = CMP(af->current_realtime, bf->current_realtime);
+ if (r != 0)
+ return r;
/* Finally, compare by contents */
- if (af->current_xor_hash < bf->current_xor_hash)
- return -1;
- if (af->current_xor_hash > bf->current_xor_hash)
- return 1;
-
- return 0;
+ return CMP(af->current_xor_hash, bf->current_xor_hash);
}
static int bump_array_index(uint64_t *i, direction_t direction, uint64_t n) {
@@ -2759,10 +2715,10 @@ int journal_file_next_entry(
}
/* Ensure our array is properly ordered. */
- if (p > 0 && !check_properly_ordered(ofs, p, direction)) {
- log_debug("%s: entry array not properly ordered at entry %" PRIu64, f->path, i);
- return -EBADMSG;
- }
+ if (p > 0 && !check_properly_ordered(ofs, p, direction))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s: entry array not properly ordered at entry %" PRIu64,
+ f->path, i);
if (offset)
*offset = ofs;
@@ -2835,10 +2791,10 @@ int journal_file_next_entry_for_data(
}
/* Ensure our array is properly ordered. */
- if (p > 0 && check_properly_ordered(ofs, p, direction)) {
- log_debug("%s data entry array not properly ordered at entry %" PRIu64, f->path, i);
- return -EBADMSG;
- }
+ if (p > 0 && check_properly_ordered(ofs, p, direction))
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s data entry array not properly ordered at entry %" PRIu64,
+ f->path, i);
if (offset)
*offset = ofs;
@@ -3233,30 +3189,30 @@ int journal_file_open(
if (fname && (flags & O_CREAT) && !endswith(fname, ".journal"))
return -EINVAL;
- f = new0(JournalFile, 1);
+ f = new(JournalFile, 1);
if (!f)
return -ENOMEM;
- f->fd = fd;
- f->mode = mode;
+ *f = (JournalFile) {
+ .fd = fd,
+ .mode = mode,
+
+ .flags = flags,
+ .prot = prot_from_flags(flags),
+ .writable = (flags & O_ACCMODE) != O_RDONLY,
- f->flags = flags;
- f->prot = prot_from_flags(flags);
- f->writable = (flags & O_ACCMODE) != O_RDONLY;
#if HAVE_LZ4
- f->compress_lz4 = compress;
+ .compress_lz4 = compress,
#elif HAVE_XZ
- f->compress_xz = compress;
+ .compress_xz = compress,
#endif
-
- if (compress_threshold_bytes == (uint64_t) -1)
- f->compress_threshold_bytes = DEFAULT_COMPRESS_THRESHOLD;
- else
- f->compress_threshold_bytes = MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes);
-
+ .compress_threshold_bytes = compress_threshold_bytes == (uint64_t) -1 ?
+ DEFAULT_COMPRESS_THRESHOLD :
+ MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes),
#if HAVE_GCRYPT
- f->seal = seal;
+ .seal = seal,
#endif
+ };
log_debug("Journal effective settings seal=%s compress=%s compress_threshold_bytes=%s",
yes_no(f->seal), yes_no(JOURNAL_FILE_COMPRESS(f)),
@@ -3446,74 +3402,145 @@ fail:
return r;
}
-int journal_file_rotate(JournalFile **f, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes) {
+int journal_file_archive(JournalFile *f) {
_cleanup_free_ char *p = NULL;
- size_t l;
- JournalFile *old_file, *new_file = NULL;
- int r;
assert(f);
- assert(*f);
- old_file = *f;
-
- if (!old_file->writable)
+ if (!f->writable)
return -EINVAL;
/* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse
* rotation, since we don't know the actual path, and couldn't rename the file hence. */
- if (path_startswith(old_file->path, "/proc/self/fd"))
+ if (path_startswith(f->path, "/proc/self/fd"))
return -EINVAL;
- if (!endswith(old_file->path, ".journal"))
+ if (!endswith(f->path, ".journal"))
return -EINVAL;
- l = strlen(old_file->path);
- r = asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal",
- (int) l - 8, old_file->path,
- SD_ID128_FORMAT_VAL(old_file->header->seqnum_id),
- le64toh((*f)->header->head_entry_seqnum),
- le64toh((*f)->header->head_entry_realtime));
- if (r < 0)
+ if (asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal",
+ (int) strlen(f->path) - 8, f->path,
+ SD_ID128_FORMAT_VAL(f->header->seqnum_id),
+ le64toh(f->header->head_entry_seqnum),
+ le64toh(f->header->head_entry_realtime)) < 0)
return -ENOMEM;
- /* Try to rename the file to the archived version. If the file
- * already was deleted, we'll get ENOENT, let's ignore that
- * case. */
- r = rename(old_file->path, p);
- if (r < 0 && errno != ENOENT)
+ /* Try to rename the file to the archived version. If the file already was deleted, we'll get ENOENT, let's
+ * ignore that case. */
+ if (rename(f->path, p) < 0 && errno != ENOENT)
return -errno;
/* Sync the rename to disk */
- (void) fsync_directory_of_file(old_file->fd);
-
- /* Set as archive so offlining commits w/state=STATE_ARCHIVED.
- * Previously we would set old_file->header->state to STATE_ARCHIVED directly here,
- * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which
- * would result in the rotated journal never getting fsync() called before closing.
- * Now we simply queue the archive state by setting an archive bit, leaving the state
- * as STATE_ONLINE so proper offlining occurs. */
- old_file->archive = true;
-
- /* Currently, btrfs is not very good with out write patterns
- * and fragments heavily. Let's defrag our journal files when
- * we archive them */
- old_file->defrag_on_close = true;
-
- r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress,
- compress_threshold_bytes, seal, NULL, old_file->mmap, deferred_closes,
- old_file, &new_file);
-
- if (deferred_closes &&
- set_put(deferred_closes, old_file) >= 0)
- (void) journal_file_set_offline(old_file, false);
- else
- (void) journal_file_close(old_file);
+ (void) fsync_directory_of_file(f->fd);
+
+ /* Set as archive so offlining commits w/state=STATE_ARCHIVED. Previously we would set old_file->header->state
+ * to STATE_ARCHIVED directly here, but journal_file_set_offline() short-circuits when state != STATE_ONLINE,
+ * which would result in the rotated journal never getting fsync() called before closing. Now we simply queue
+ * the archive state by setting an archive bit, leaving the state as STATE_ONLINE so proper offlining
+ * occurs. */
+ f->archive = true;
+
+ /* Currently, btrfs is not very good with out write patterns and fragments heavily. Let's defrag our journal
+ * files when we archive them */
+ f->defrag_on_close = true;
+
+ return 0;
+}
+
+JournalFile* journal_initiate_close(
+ JournalFile *f,
+ Set *deferred_closes) {
+
+ int r;
+
+ assert(f);
+
+ if (deferred_closes) {
+
+ r = set_put(deferred_closes, f);
+ if (r < 0)
+ log_debug_errno(r, "Failed to add file to deferred close set, closing immediately.");
+ else {
+ (void) journal_file_set_offline(f, false);
+ return NULL;
+ }
+ }
+
+ return journal_file_close(f);
+}
+
+int journal_file_rotate(
+ JournalFile **f,
+ bool compress,
+ uint64_t compress_threshold_bytes,
+ bool seal,
+ Set *deferred_closes) {
+ JournalFile *new_file = NULL;
+ int r;
+
+ assert(f);
+ assert(*f);
+
+ r = journal_file_archive(*f);
+ if (r < 0)
+ return r;
+
+ r = journal_file_open(
+ -1,
+ (*f)->path,
+ (*f)->flags,
+ (*f)->mode,
+ compress,
+ compress_threshold_bytes,
+ seal,
+ NULL, /* metrics */
+ (*f)->mmap,
+ deferred_closes,
+ *f, /* template */
+ &new_file);
+
+ journal_initiate_close(*f, deferred_closes);
*f = new_file;
+
return r;
}
+int journal_file_dispose(int dir_fd, const char *fname) {
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+
+ assert(fname);
+
+ /* Renames a journal file to *.journal~, i.e. to mark it as corruped or otherwise uncleanly shutdown. Note that
+ * this is done without looking into the file or changing any of its contents. The idea is that this is called
+ * whenever something is suspicious and we want to move the file away and make clear that it is not accessed
+ * for writing anymore. */
+
+ if (!endswith(fname, ".journal"))
+ return -EINVAL;
+
+ if (asprintf(&p, "%.*s@%016" PRIx64 "-%016" PRIx64 ".journal~",
+ (int) strlen(fname) - 8, fname,
+ now(CLOCK_REALTIME),
+ random_u64()) < 0)
+ return -ENOMEM;
+
+ if (renameat(dir_fd, fname, dir_fd, p) < 0)
+ return -errno;
+
+ /* btrfs doesn't cope well with our write pattern and fragments heavily. Let's defrag all files we rotate */
+ fd = openat(dir_fd, p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ log_debug_errno(errno, "Failed to open file for defragmentation/FS_NOCOW_FL, ignoring: %m");
+ else {
+ (void) chattr_fd(fd, 0, FS_NOCOW_FL, NULL);
+ (void) btrfs_defrag_fd(fd);
+ }
+
+ return 0;
+}
+
int journal_file_open_reliably(
const char *fname,
int flags,
@@ -3528,8 +3555,6 @@ int journal_file_open_reliably(
JournalFile **ret) {
int r;
- size_t l;
- _cleanup_free_ char *p = NULL;
r = journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache,
deferred_closes, template, ret);
@@ -3555,25 +3580,12 @@ int journal_file_open_reliably(
return r;
/* The file is corrupted. Rotate it away and try it again (but only once) */
-
- l = strlen(fname);
- if (asprintf(&p, "%.*s@%016"PRIx64 "-%016"PRIx64 ".journal~",
- (int) l - 8, fname,
- now(CLOCK_REALTIME),
- random_u64()) < 0)
- return -ENOMEM;
-
- if (rename(fname, p) < 0)
- return -errno;
-
- /* btrfs doesn't cope well with our write pattern and
- * fragments heavily. Let's defrag all files we rotate */
-
- (void) chattr_path(p, 0, FS_NOCOW_FL);
- (void) btrfs_defrag(p);
-
log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
+ r = journal_file_dispose(AT_FDCWD, fname);
+ if (r < 0)
+ return r;
+
return journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache,
deferred_closes, template, ret);
}
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index c8114ee2d0..29e324d8cf 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -4,16 +4,16 @@
#include <inttypes.h>
#if HAVE_GCRYPT
-#include <gcrypt.h>
+# include <gcrypt.h>
#endif
+#include "sd-event.h"
#include "sd-id128.h"
#include "hashmap.h"
#include "journal-def.h"
#include "macro.h"
#include "mmap-cache.h"
-#include "sd-event.h"
#include "sparse-endian.h"
typedef struct JournalMetrics {
@@ -235,8 +235,12 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
void journal_file_dump(JournalFile *f);
void journal_file_print_header(JournalFile *f);
+int journal_file_archive(JournalFile *f);
+JournalFile* journal_initiate_close(JournalFile *f, Set *deferred_closes);
int journal_file_rotate(JournalFile **f, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes);
+int journal_file_dispose(int dir_fd, const char *fname);
+
void journal_file_post_change(JournalFile *f);
int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t);
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index a0621524a9..87056435fc 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -14,12 +14,12 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "io-util.h"
#include "memfd-util.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "tmpfile-util.h"
#include "util.h"
#define SNDBUF_SIZE (8*1024*1024)
diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c
index 8d3ae71440..2778ce40c5 100644
--- a/src/journal/journal-vacuum.c
+++ b/src/journal/journal-vacuum.c
@@ -15,6 +15,7 @@
#include "journal-vacuum.h"
#include "parse-util.h"
#include "string-util.h"
+#include "time-util.h"
#include "util.h"
#include "xattr-util.h"
@@ -29,30 +30,21 @@ struct vacuum_info {
bool have_seqnum;
};
-static int vacuum_compare(const void *_a, const void *_b) {
- const struct vacuum_info *a, *b;
-
- a = _a;
- b = _b;
+static int vacuum_compare(const struct vacuum_info *a, const struct vacuum_info *b) {
+ int r;
if (a->have_seqnum && b->have_seqnum &&
- sd_id128_equal(a->seqnum_id, b->seqnum_id)) {
- if (a->seqnum < b->seqnum)
- return -1;
- else if (a->seqnum > b->seqnum)
- return 1;
- else
- return 0;
- }
+ sd_id128_equal(a->seqnum_id, b->seqnum_id))
+ return CMP(a->seqnum, b->seqnum);
- if (a->realtime < b->realtime)
- return -1;
- else if (a->realtime > b->realtime)
- return 1;
- else if (a->have_seqnum && b->have_seqnum)
+ r = CMP(a->realtime, b->realtime);
+ if (r != 0)
+ return r;
+
+ if (a->have_seqnum && b->have_seqnum)
return memcmp(&a->seqnum_id, &b->seqnum_id, 16);
- else
- return strcmp(a->filename, b->filename);
+
+ return strcmp(a->filename, b->filename);
}
static void patch_realtime(
@@ -134,11 +126,10 @@ int journal_directory_vacuum(
usec_t *oldest_usec,
bool verbose) {
+ uint64_t sum = 0, freed = 0, n_active_files = 0;
+ size_t n_list = 0, n_allocated = 0, i;
_cleanup_closedir_ DIR *d = NULL;
struct vacuum_info *list = NULL;
- unsigned n_list = 0, i, n_active_files = 0;
- size_t n_allocated = 0;
- uint64_t sum = 0, freed = 0;
usec_t retention_limit = 0;
char sbytes[FORMAT_BYTES_MAX];
struct dirent *de;
@@ -149,13 +140,8 @@ int journal_directory_vacuum(
if (max_use <= 0 && max_retention_usec <= 0 && n_max_files <= 0)
return 0;
- if (max_retention_usec > 0) {
- retention_limit = now(CLOCK_REALTIME);
- if (retention_limit > max_retention_usec)
- retention_limit -= max_retention_usec;
- else
- max_retention_usec = retention_limit = 0;
- }
+ if (max_retention_usec > 0)
+ retention_limit = usec_sub_unsigned(now(CLOCK_REALTIME), max_retention_usec);
d = opendir(directory);
if (!d)
@@ -281,21 +267,22 @@ int journal_directory_vacuum(
goto finish;
}
- list[n_list].filename = TAKE_PTR(p);
- list[n_list].usage = size;
- list[n_list].seqnum = seqnum;
- list[n_list].realtime = realtime;
- list[n_list].seqnum_id = seqnum_id;
- list[n_list].have_seqnum = have_seqnum;
- n_list++;
+ list[n_list++] = (struct vacuum_info) {
+ .filename = TAKE_PTR(p),
+ .usage = size,
+ .seqnum = seqnum,
+ .realtime = realtime,
+ .seqnum_id = seqnum_id,
+ .have_seqnum = have_seqnum,
+ };
sum += size;
}
- qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
+ typesafe_qsort(list, n_list, vacuum_compare);
for (i = 0; i < n_list; i++) {
- unsigned left;
+ uint64_t left;
left = n_active_files + n_list - i;
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index c2f0467c6e..5eff80a99f 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -17,6 +17,7 @@
#include "lookup3.h"
#include "macro.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "util.h"
static void draw_progress(uint64_t p, usec_t *last_usec) {
@@ -60,10 +61,11 @@ static void draw_progress(uint64_t p, usec_t *last_usec) {
}
static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) {
+ /* Calculates scale * p / m, but handles m == 0 safely, and saturates.
+ * Currently all callers use m >= 1, but we keep the check to be defensive.
+ */
- /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
-
- if (p >= m || m == 0)
+ if (p >= m || m == 0) /* lgtm [cpp/constant-comparison] */
return scale;
return scale * p / m;
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 56b1be530d..14a02eda74 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -22,6 +22,7 @@
#endif
#include "sd-bus.h"
+#include "sd-device.h"
#include "sd-journal.h"
#include "acl-util.h"
@@ -30,12 +31,15 @@
#include "bus-util.h"
#include "catalog.h"
#include "chattr-util.h"
+#include "def.h"
+#include "device-private.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "fsprg.h"
#include "glob-util.h"
#include "hostname-util.h"
+#include "id128-print.h"
#include "io-util.h"
#include "journal-def.h"
#include "journal-internal.h"
@@ -50,6 +54,7 @@
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "rlimit-util.h"
#include "set.h"
#include "sigbus.h"
@@ -57,8 +62,7 @@
#include "strv.h"
#include "syslog-util.h"
#include "terminal-util.h"
-#include "udev-util.h"
-#include "udev.h"
+#include "tmpfile-util.h"
#include "unit-name.h"
#include "user-util.h"
@@ -82,10 +86,9 @@ static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out
r = pcre2_get_error_message(errorcode, buf, sizeof buf);
- log_error("Bad pattern \"%s\": %s",
- pattern,
- r < 0 ? "unknown error" : (char*) buf);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Bad pattern \"%s\": %s", pattern,
+ r < 0 ? "unknown error" : (char *)buf);
}
*out = p;
@@ -102,11 +105,10 @@ enum {
static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_utc = false;
-static bool arg_pager_end = false;
static bool arg_follow = false;
static bool arg_full = true;
static bool arg_all = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static int arg_lines = ARG_LINES_DEFAULT;
static bool arg_no_tail = false;
static bool arg_quiet = false;
@@ -165,6 +167,7 @@ static enum {
ACTION_SYNC,
ACTION_ROTATE,
ACTION_VACUUM,
+ ACTION_ROTATE_AND_VACUUM,
ACTION_LIST_FIELDS,
ACTION_LIST_FIELD_NAMES,
} arg_action = ACTION_SHOW;
@@ -177,9 +180,8 @@ typedef struct BootId {
} BootId;
static int add_matches_for_device(sd_journal *j, const char *devpath) {
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
- _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
- struct udev_device *d = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ sd_device *d = NULL;
struct stat st;
int r;
@@ -191,33 +193,25 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) {
return -EINVAL;
}
- udev = udev_new();
- if (!udev)
- return log_oom();
-
if (stat(devpath, &st) < 0)
return log_error_errno(errno, "Couldn't stat file: %m");
- r = udev_device_new_from_stat_rdev(udev, &st, &device);
+ r = device_new_from_stat_rdev(&device, &st);
if (r < 0)
- return log_error_errno(r, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
+ return log_error_errno(r, "Failed to get device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
- d = device;
- while (d) {
+ for (d = device; d; ) {
_cleanup_free_ char *match = NULL;
const char *subsys, *sysname, *devnode;
+ sd_device *parent;
- subsys = udev_device_get_subsystem(d);
- if (!subsys) {
- d = udev_device_get_parent(d);
- continue;
- }
+ r = sd_device_get_subsystem(d, &subsys);
+ if (r < 0)
+ goto get_parent;
- sysname = udev_device_get_sysname(d);
- if (!sysname) {
- d = udev_device_get_parent(d);
- continue;
- }
+ r = sd_device_get_sysname(d, &sysname);
+ if (r < 0)
+ goto get_parent;
match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname);
if (!match)
@@ -227,8 +221,7 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) {
if (r < 0)
return log_error_errno(r, "Failed to add match: %m");
- devnode = udev_device_get_devnode(d);
- if (devnode) {
+ if (sd_device_get_devname(d, &devnode) >= 0) {
_cleanup_free_ char *match1 = NULL;
r = stat(devnode, &st);
@@ -244,7 +237,11 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) {
return log_error_errno(r, "Failed to add match: %m");
}
- d = udev_device_get_parent(d);
+get_parent:
+ if (sd_device_get_parent(d, &parent) < 0)
+ break;
+
+ d = parent;
}
r = add_match_this_boot(j, arg_machine);
@@ -297,9 +294,15 @@ static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
+
+ r = terminal_urlify_man("journalctl", "1", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [OPTIONS...] [MATCHES...]\n\n"
"Query the journal.\n\n"
@@ -329,7 +332,8 @@ static void help(void) {
" -o --output=STRING Change journal output mode (short, short-precise,\n"
" short-iso, short-iso-precise, short-full,\n"
" short-monotonic, short-unix, verbose, export,\n"
- " json, json-pretty, json-sse, cat, with-unit)\n"
+ " json, json-pretty, json-sse, json-seq, cat,\n"
+ " with-unit)\n"
" --output-fields=LIST Select fields to print in verbose/export/json modes\n"
" --utc Express time in Coordinated Universal Time (UTC)\n"
" -x --catalog Add message explanations where available\n"
@@ -342,11 +346,9 @@ static void 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"
-#if HAVE_GCRYPT
" --interval=TIME Time interval for changing the FSS sealing key\n"
" --verify-key=KEY Specify FSS verification key\n"
" --force Override of the FSS key pair with --setup-keys\n"
-#endif
"\nCommands:\n"
" -h --help Show this help text\n"
" --version Show package version\n"
@@ -364,11 +366,13 @@ static void help(void) {
" --list-catalog Show all message IDs in the catalog\n"
" --dump-catalog Show entries in the message catalog\n"
" --update-catalog Update the message catalog database\n"
- " --new-id128 Generate a new 128-bit ID\n"
-#if HAVE_GCRYPT
" --setup-keys Generate a new FSS key pair\n"
-#endif
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -423,7 +427,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "no-full", no_argument, NULL, ARG_NO_FULL },
{ "lines", optional_argument, NULL, 'n' },
{ "no-tail", no_argument, NULL, ARG_NO_TAIL },
- { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
+ { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, /* deprecated */
{ "quiet", no_argument, NULL, 'q' },
{ "merge", no_argument, NULL, 'm' },
{ "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */
@@ -482,18 +486,17 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case 'e':
- arg_pager_end = true;
+ arg_pager_flags |= PAGER_JUMP_TO_END;
if (arg_lines == ARG_LINES_DEFAULT)
arg_lines = 1000;
@@ -516,7 +519,7 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_CAT))
+ if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT))
arg_quiet = true;
break;
@@ -685,7 +688,7 @@ static int parse_argv(int argc, char *argv[]) {
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_FILES:
@@ -695,7 +698,7 @@ static int parse_argv(int argc, char *argv[]) {
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_TIME:
@@ -705,7 +708,7 @@ static int parse_argv(int argc, char *argv[]) {
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
#if HAVE_GCRYPT
@@ -741,7 +744,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERIFY_KEY:
case ARG_INTERVAL:
case ARG_FORCE:
- log_error("Forward-secure sealing not available.");
+ log_error("Compiled without forward-secure sealing support.");
return -EOPNOTSUPP;
#endif
@@ -894,7 +897,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_ROTATE:
- arg_action = ACTION_ROTATE;
+ arg_action = arg_action == ACTION_VACUUM ? ACTION_ROTATE_AND_VACUUM : ACTION_ROTATE;
break;
case ARG_SYNC:
@@ -1007,35 +1010,6 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int generate_new_id128(void) {
- sd_id128_t id;
- int r;
- unsigned i;
-
- r = sd_id128_randomize(&id);
- if (r < 0)
- return log_error_errno(r, "Failed to generate ID: %m");
-
- printf("As string:\n"
- SD_ID128_FORMAT_STR "\n\n"
- "As UUID:\n"
- "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
- "As man:sd-id128(3) macro:\n"
- "#define MESSAGE_XYZ SD_ID128_MAKE(",
- SD_ID128_FORMAT_VAL(id),
- SD_ID128_FORMAT_VAL(id));
- for (i = 0; i < 16; i++)
- printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
- fputs(")\n\n", stdout);
-
- printf("As Python constant:\n"
- ">>> import uuid\n"
- ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
- SD_ID128_FORMAT_VAL(id));
-
- return 0;
-}
-
static int add_matches(sd_journal *j, char **args) {
char **i;
bool have_term = false;
@@ -1096,10 +1070,10 @@ static int add_matches(sd_journal *j, char **args) {
r = add_matches_for_device(j, p);
if (r < 0)
return r;
- } else {
- log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "File is neither a device node, nor regular file, nor executable: %s",
+ *i);
have_term = true;
} else {
@@ -1111,10 +1085,9 @@ static int add_matches(sd_journal *j, char **args) {
return log_error_errno(r, "Failed to add match '%s': %m", *i);
}
- if (!strv_isempty(args) && !have_term) {
- log_error("\"+\" can only be used between terms");
- return -EINVAL;
- }
+ if (!strv_isempty(args) && !have_term)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "\"+\" can only be used between terms");
return 0;
}
@@ -1205,10 +1178,9 @@ static int discover_next_boot(sd_journal *j,
r = sd_journal_previous(j);
if (r < 0)
return r;
- else if (r == 0) {
- log_debug("Whoopsie! We found a boot ID but can't read its last entry.");
- return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */
- }
+ else if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
+ "Whoopsie! We found a boot ID but can't read its last entry."); /* This shouldn't happen. We just came from this very boot ID. */
r = sd_journal_get_realtime_usec(j, &next_boot->last);
if (r < 0)
@@ -1352,7 +1324,7 @@ static int list_boots(sd_journal *j) {
if (count == 0)
return count;
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
/* numbers are one less, but we need an extra char for the sign */
w = DECIMAL_STR_WIDTH(count - 1) + 1;
@@ -1761,7 +1733,7 @@ static int setup_keys(void) {
/* Enable secure remove, exclusion from dump, synchronous
* writing and in-place updating */
- r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL);
+ r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, NULL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes: %m");
@@ -1859,8 +1831,8 @@ finish:
return r;
#else
- log_error("Forward-secure sealing not available.");
- return -EOPNOTSUPP;
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Forward-secure sealing not available.");
#endif
}
@@ -1997,7 +1969,7 @@ static int send_signal_and_wait(int sig, const char *watch_path) {
/* See if a sync happened by now. */
r = read_timestamp_file(watch_path, &tstamp);
if (r < 0 && r != -ENOENT)
- return log_error_errno(errno, "Failed to read %s: %m", watch_path);
+ return log_error_errno(r, "Failed to read %s: %m", watch_path);
if (r >= 0 && tstamp >= start)
return 0;
@@ -2064,19 +2036,59 @@ static int sync_journal(void) {
return send_signal_and_wait(SIGRTMIN+1, "/run/systemd/journal/synced");
}
-int main(int argc, char *argv[]) {
+static int wait_for_change(sd_journal *j, int poll_fd) {
+ struct pollfd pollfds[] = {
+ { .fd = poll_fd, .events = POLLIN },
+ { .fd = STDOUT_FILENO },
+ };
+
+ struct timespec ts;
+ usec_t timeout;
int r;
+
+ assert(j);
+ assert(poll_fd >= 0);
+
+ /* Much like sd_journal_wait() but also keeps an eye on STDOUT, and exits as soon as we see a POLLHUP on that,
+ * i.e. when it is closed. */
+
+ r = sd_journal_get_timeout(j, &timeout);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine journal waiting time: %m");
+
+ if (ppoll(pollfds, ELEMENTSOF(pollfds),
+ timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL) < 0) {
+ if (errno == EINTR)
+ return 0;
+
+ return log_error_errno(errno, "Couldn't wait for journal event: %m");
+ }
+
+ if (pollfds[1].revents & (POLLHUP|POLLERR)) /* STDOUT has been closed? */
+ return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED),
+ "Standard output has been closed.");
+
+ r = sd_journal_process(j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to process journal events: %m");
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ bool previous_boot_id_valid = false, first_line = true, ellipsized = false, need_seek = false;
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
- bool need_seek = false;
sd_id128_t previous_boot_id;
- bool previous_boot_id_valid = false, first_line = true;
- int n_shown = 0;
- bool ellipsized = false;
+ int n_shown = 0, r, poll_fd = -1;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
+ /* Increase max number of open files if we can, we might needs this when browsing journal files, which might be
+ * split up into many files. */
+ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
@@ -2084,15 +2096,10 @@ int main(int argc, char *argv[]) {
signal(SIGWINCH, columns_lines_cache_reset);
sigbus_install();
- /* Increase max number of open files to 16K if we can, we
- * might needs this when browsing journal files, which might
- * be split up into many files. */
- setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384));
-
switch (arg_action) {
case ACTION_NEW_ID128:
- r = generate_new_id128();
+ r = id128_print_new(true);
goto finish;
case ACTION_SETUP_KEYS:
@@ -2104,7 +2111,7 @@ int main(int argc, char *argv[]) {
case ACTION_UPDATE_CATALOG: {
_cleanup_free_ char *database;
- database = path_join(arg_root, CATALOG_DATABASE, NULL);
+ database = path_join(arg_root, CATALOG_DATABASE);
if (!database) {
r = log_oom();
goto finish;
@@ -2117,7 +2124,7 @@ int main(int argc, char *argv[]) {
} else {
bool oneline = arg_action == ACTION_LIST_CATALOG;
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
if (optind < argc)
r = catalog_list_items(stdout, database, oneline, argv + optind);
@@ -2148,6 +2155,7 @@ int main(int argc, char *argv[]) {
case ACTION_DISK_USAGE:
case ACTION_LIST_BOOTS:
case ACTION_VACUUM:
+ case ACTION_ROTATE_AND_VACUUM:
case ACTION_LIST_FIELDS:
case ACTION_LIST_FIELD_NAMES:
/* These ones require access to the journal files, continue below. */
@@ -2265,6 +2273,14 @@ int main(int argc, char *argv[]) {
r = list_boots(j);
goto finish;
+ case ACTION_ROTATE_AND_VACUUM:
+
+ r = rotate();
+ if (r < 0)
+ goto finish;
+
+ _fallthrough_;
+
case ACTION_VACUUM: {
Directory *d;
Iterator i;
@@ -2391,15 +2407,15 @@ int main(int argc, char *argv[]) {
/* Opening the fd now means the first sd_journal_wait() will actually wait */
if (arg_follow) {
- r = sd_journal_get_fd(j);
- if (r == -EMFILE) {
- log_warning("Insufficent watch descriptors available. Reverting to -n.");
+ poll_fd = sd_journal_get_fd(j);
+ if (poll_fd == -EMFILE) {
+ log_warning_errno(poll_fd, "Insufficent watch descriptors available. Reverting to -n.");
arg_follow = false;
- } else if (r == -EMEDIUMTYPE) {
- log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN.");
+ } else if (poll_fd == -EMEDIUMTYPE) {
+ log_error_errno(poll_fd, "The --follow switch is not supported in conjunction with reading from STDIN.");
goto finish;
- } else if (r < 0) {
- log_error_errno(r, "Failed to get journal fd: %m");
+ } else if (poll_fd < 0) {
+ log_error_errno(poll_fd, "Failed to get journal fd: %m");
goto finish;
}
}
@@ -2476,7 +2492,7 @@ int main(int argc, char *argv[]) {
need_seek = true;
if (!arg_follow)
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
if (!arg_quiet && (arg_lines != 0 || arg_follow)) {
usec_t start, end;
@@ -2621,7 +2637,7 @@ int main(int argc, char *argv[]) {
need_seek = true;
if (r == -EADDRNOTAVAIL)
break;
- else if (r < 0 || ferror(stdout))
+ else if (r < 0)
goto finish;
n_shown++;
@@ -2659,11 +2675,10 @@ int main(int argc, char *argv[]) {
}
fflush(stdout);
- r = sd_journal_wait(j, (uint64_t) -1);
- if (r < 0) {
- log_error_errno(r, "Couldn't wait for journal event: %m");
+
+ r = wait_for_change(j, poll_fd);
+ if (r < 0)
goto finish;
- }
first_line = false;
}
diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c
index 87726684af..345e43ef44 100644
--- a/src/journal/journald-audit.c
+++ b/src/journal/journald-audit.c
@@ -313,7 +313,7 @@ static int map_all_fields(
}
}
-static void process_audit_string(Server *s, int type, const char *data, size_t size) {
+void process_audit_string(Server *s, int type, const char *data, size_t size) {
size_t n_iov_allocated = 0, n_iov = 0, z;
_cleanup_free_ struct iovec *iov = NULL;
uint64_t seconds, msec, id;
@@ -341,11 +341,12 @@ static void process_audit_string(Server *s, int type, const char *data, size_t s
if (!p)
return;
+ k = 0;
if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n",
&seconds,
&msec,
&id,
- &k) != 3)
+ &k) != 3 || k == 0)
return;
p += k;
@@ -497,7 +498,6 @@ static int enable_audit(int fd, bool b) {
}
int server_open_audit(Server *s) {
- static const int one = 1;
int r;
if (s->audit_fd < 0) {
@@ -526,11 +526,11 @@ int server_open_audit(Server *s) {
return 0;
}
} else
- fd_nonblock(s->audit_fd, 1);
+ (void) fd_nonblock(s->audit_fd, true);
- r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ r = setsockopt_int(s->audit_fd, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- return log_error_errno(errno, "Failed to set SO_PASSCRED on audit socket: %m");
+ return log_error_errno(r, "Failed to set SO_PASSCRED on audit socket: %m");
r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s);
if (r < 0)
diff --git a/src/journal/journald-audit.h b/src/journal/journald-audit.h
index 57bb1711c9..df41f81435 100644
--- a/src/journal/journald-audit.h
+++ b/src/journal/journald-audit.h
@@ -6,4 +6,6 @@
void server_process_audit_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const union sockaddr_union *sa, socklen_t salen);
-int server_open_audit(Server*s);
+void process_audit_string(Server *s, int type, const char *data, size_t size);
+
+int server_open_audit(Server *s);
diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c
index ce07de1bfb..8253a45128 100644
--- a/src/journal/journald-context.c
+++ b/src/journal/journald-context.c
@@ -13,6 +13,8 @@
#include "io-util.h"
#include "journal-util.h"
#include "journald-context.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
#include "string-util.h"
#include "syslog-util.h"
@@ -62,18 +64,13 @@
static int client_context_compare(const void *a, const void *b) {
const ClientContext *x = a, *y = b;
+ int r;
- if (x->timestamp < y->timestamp)
- return -1;
- if (x->timestamp > y->timestamp)
- return 1;
-
- if (x->pid < y->pid)
- return -1;
- if (x->pid > y->pid)
- return 1;
+ r = CMP(x->timestamp, y->timestamp);
+ if (r != 0)
+ return r;
- return 0;
+ return CMP(x->pid, y->pid);
}
static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
@@ -107,6 +104,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
c->timestamp = USEC_INFINITY;
c->extra_fields_mtime = NSEC_INFINITY;
c->log_level_max = -1;
+ c->log_rate_limit_interval = s->rate_limit_interval;
+ c->log_rate_limit_burst = s->rate_limit_burst;
r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c);
if (r < 0) {
@@ -118,7 +117,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
return 0;
}
-static void client_context_reset(ClientContext *c) {
+static void client_context_reset(Server *s, ClientContext *c) {
+ assert(s);
assert(c);
c->timestamp = USEC_INFINITY;
@@ -153,6 +153,9 @@ static void client_context_reset(ClientContext *c) {
c->extra_fields_mtime = NSEC_INFINITY;
c->log_level_max = -1;
+
+ c->log_rate_limit_interval = s->rate_limit_interval;
+ c->log_rate_limit_burst = s->rate_limit_burst;
}
static ClientContext* client_context_free(Server *s, ClientContext *c) {
@@ -166,7 +169,7 @@ static ClientContext* client_context_free(Server *s, ClientContext *c) {
if (c->in_lru)
assert_se(prioq_remove(s->client_contexts_lru, c, &c->lru_index) >= 0);
- client_context_reset(c);
+ client_context_reset(s, c);
return mfree(c);
}
@@ -250,9 +253,11 @@ static int client_context_read_cgroup(Server *s, ClientContext *c, const char *u
/* Try to acquire the current cgroup path */
r = cg_pid_get_path_shifted(c->pid, s->cgroup_root, &t);
- if (r < 0) {
+ if (r < 0 || empty_or_root(t)) {
- /* If that didn't work, we use the unit ID passed in as fallback, if we have nothing cached yet */
+ /* We use the unit ID passed in as fallback if we have nothing cached yet and cg_pid_get_path_shifted()
+ * failed or process is running in a root cgroup. Zombie processes are automatically migrated to root cgroup
+ * on cgroupsv1 and we want to be able to map log messages from them too. */
if (unit_id && !c->unit) {
c->unit = strdup(unit_id);
if (c->unit)
@@ -429,6 +434,42 @@ static int client_context_read_extra_fields(
return 0;
}
+static int client_context_read_log_rate_limit_interval(ClientContext *c) {
+ _cleanup_free_ char *value = NULL;
+ const char *p;
+ int r;
+
+ assert(c);
+
+ if (!c->unit)
+ return 0;
+
+ p = strjoina("/run/systemd/units/log-rate-limit-interval:", c->unit);
+ r = readlink_malloc(p, &value);
+ if (r < 0)
+ return r;
+
+ return safe_atou64(value, &c->log_rate_limit_interval);
+}
+
+static int client_context_read_log_rate_limit_burst(ClientContext *c) {
+ _cleanup_free_ char *value = NULL;
+ const char *p;
+ int r;
+
+ assert(c);
+
+ if (!c->unit)
+ return 0;
+
+ p = strjoina("/run/systemd/units/log-rate-limit-burst:", c->unit);
+ r = readlink_malloc(p, &value);
+ if (r < 0)
+ return r;
+
+ return safe_atou(value, &c->log_rate_limit_burst);
+}
+
static void client_context_really_refresh(
Server *s,
ClientContext *c,
@@ -455,6 +496,8 @@ static void client_context_really_refresh(
(void) client_context_read_invocation_id(s, c);
(void) client_context_read_log_level_max(s, c);
(void) client_context_read_extra_fields(s, c);
+ (void) client_context_read_log_rate_limit_interval(c);
+ (void) client_context_read_log_rate_limit_burst(c);
c->timestamp = timestamp;
@@ -485,7 +528,7 @@ void client_context_maybe_refresh(
/* If the data isn't pinned and if the cashed data is older than the upper limit, we flush it out
* entirely. This follows the logic that as long as an entry is pinned the PID reuse is unlikely. */
if (c->n_ref == 0 && c->timestamp + MAX_USEC < timestamp) {
- client_context_reset(c);
+ client_context_reset(s, c);
goto refresh;
}
diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h
index 9df3a38eff..5e19c71f14 100644
--- a/src/journal/journald-context.h
+++ b/src/journal/journald-context.h
@@ -49,6 +49,9 @@ struct ClientContext {
size_t extra_fields_n_iovec;
void *extra_fields_data;
nsec_t extra_fields_mtime;
+
+ usec_t log_rate_limit_interval;
+ unsigned log_rate_limit_burst;
};
int client_context_get(
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index e9aff13168..ce82102eed 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -6,10 +6,11 @@
#include <sys/socket.h>
#include <unistd.h>
-#include "libudev.h"
+#include "sd-device.h"
#include "sd-messages.h"
#include "alloc-util.h"
+#include "device-util.h"
#include "escape.h"
#include "fd-util.h"
#include "format-util.h"
@@ -93,7 +94,7 @@ static bool is_us(const char *identifier, const char *pid) {
streq(identifier, program_invocation_short_name);
}
-static void dev_kmsg_record(Server *s, const char *p, size_t l) {
+void dev_kmsg_record(Server *s, char *p, size_t l) {
_cleanup_free_ char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL, *identifier = NULL, *pid = NULL;
struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS];
@@ -191,7 +192,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
e = memchr(k, '\n', l);
if (!e)
- return;
+ goto finish;
*e = 0;
@@ -209,16 +210,13 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
}
if (kernel_device) {
- struct udev_device *ud;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
- ud = udev_device_new_from_device_id(s->udev, kernel_device);
- if (ud) {
+ if (sd_device_new_from_device_id(&d, kernel_device) >= 0) {
const char *g;
- struct udev_list_entry *ll;
char *b;
- g = udev_device_get_devnode(ud);
- if (g) {
+ if (sd_device_get_devname(d, &g) >= 0) {
b = strappend("_UDEV_DEVNODE=", g);
if (b) {
iovec[n++] = IOVEC_MAKE_STRING(b);
@@ -226,8 +224,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
}
}
- g = udev_device_get_sysname(ud);
- if (g) {
+ if (sd_device_get_sysname(d, &g) >= 0) {
b = strappend("_UDEV_SYSNAME=", g);
if (b) {
iovec[n++] = IOVEC_MAKE_STRING(b);
@@ -236,25 +233,19 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
}
j = 0;
- ll = udev_device_get_devlinks_list_entry(ud);
- udev_list_entry_foreach(ll, ll) {
+ FOREACH_DEVICE_DEVLINK(d, g) {
- if (j > N_IOVEC_UDEV_FIELDS)
+ if (j >= N_IOVEC_UDEV_FIELDS)
break;
- g = udev_list_entry_get_name(ll);
- if (g) {
- b = strappend("_UDEV_DEVLINK=", g);
- if (b) {
- iovec[n++] = IOVEC_MAKE_STRING(b);
- z++;
- }
+ b = strappend("_UDEV_DEVLINK=", g);
+ if (b) {
+ iovec[n++] = IOVEC_MAKE_STRING(b);
+ z++;
}
j++;
}
-
- udev_device_unref(ud);
}
}
diff --git a/src/journal/journald-kmsg.h b/src/journal/journald-kmsg.h
index bff24ac310..2326bc8c93 100644
--- a/src/journal/journald-kmsg.h
+++ b/src/journal/journald-kmsg.h
@@ -9,3 +9,5 @@ int server_flush_dev_kmsg(Server *s);
void server_forward_kmsg(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred);
int server_open_kernel_seqnum(Server *s);
+
+void dev_kmsg_record(Server *s, char *p, size_t l);
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index 5ff22a10af..e86178ed77 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -25,6 +25,7 @@
#include "selinux-util.h"
#include "socket-util.h"
#include "string-util.h"
+#include "strv.h"
#include "unaligned.h"
static bool allow_object_pid(const struct ucred *ucred) {
@@ -205,8 +206,7 @@ static int server_process_entry(
memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
if (journal_field_valid(p, e - p, false)) {
- iovec[n].iov_base = k;
- iovec[n].iov_len = (e - p) + 1 + l;
+ iovec[n] = IOVEC_MAKE(k, (e - p) + 1 + l);
entry_size += iovec[n].iov_len;
n++;
@@ -277,7 +277,7 @@ finish:
void server_process_native_message(
Server *s,
- const void *buffer, size_t buffer_size,
+ const char *buffer, size_t buffer_size,
const struct ucred *ucred,
const struct timeval *tv,
const char *label, size_t label_len) {
@@ -337,11 +337,7 @@ void server_process_native_file(
return;
}
- e = path_startswith(k, "/dev/shm/");
- if (!e)
- e = path_startswith(k, "/tmp/");
- if (!e)
- e = path_startswith(k, "/var/tmp/");
+ e = PATH_STARTSWITH_SET(k, "/dev/shm/", "/tmp/", "/var/tmp/");
if (!e) {
log_error("Received file outside of allowed directories. Refusing.");
return;
@@ -437,13 +433,12 @@ void server_process_native_file(
}
}
-int server_open_native_socket(Server*s) {
+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",
};
- static const int one = 1;
int r;
assert(s);
@@ -453,7 +448,7 @@ int server_open_native_socket(Server*s) {
if (s->native_fd < 0)
return log_error_errno(errno, "socket() failed: %m");
- (void) unlink(sa.un.sun_path);
+ (void) sockaddr_un_unlink(&sa.un);
r = bind(s->native_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (r < 0)
@@ -461,23 +456,23 @@ int server_open_native_socket(Server*s) {
(void) chmod(sa.un.sun_path, 0666);
} else
- fd_nonblock(s->native_fd, 1);
+ (void) fd_nonblock(s->native_fd, true);
- r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- return log_error_errno(errno, "SO_PASSCRED failed: %m");
+ return log_error_errno(r, "SO_PASSCRED failed: %m");
#if HAVE_SELINUX
if (mac_selinux_use()) {
- r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
+ r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_PASSSEC, true);
if (r < 0)
- log_warning_errno(errno, "SO_PASSSEC failed: %m");
+ log_warning_errno(r, "SO_PASSSEC failed: %m");
}
#endif
- r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
+ r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, true);
if (r < 0)
- return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
+ return log_error_errno(r, "SO_TIMESTAMP failed: %m");
r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, server_process_datagram, s);
if (r < 0)
diff --git a/src/journal/journald-native.h b/src/journal/journald-native.h
index 7211d4fab4..2a33ef74c5 100644
--- a/src/journal/journald-native.h
+++ b/src/journal/journald-native.h
@@ -5,7 +5,7 @@
void server_process_native_message(
Server *s,
- const void *buffer,
+ const char *buffer,
size_t buffer_size,
const struct ucred *ucred,
const struct timeval *tv,
diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c
index 6a8a36a736..0b42d53760 100644
--- a/src/journal/journald-rate-limit.c
+++ b/src/journal/journald-rate-limit.c
@@ -39,6 +39,10 @@ struct JournalRateLimitGroup {
JournalRateLimit *parent;
char *id;
+
+ /* Interval is stored to keep track of when the group expires */
+ usec_t interval;
+
JournalRateLimitPool pools[POOLS_MAX];
uint64_t hash;
@@ -47,8 +51,6 @@ struct JournalRateLimitGroup {
};
struct JournalRateLimit {
- usec_t interval;
- unsigned burst;
JournalRateLimitGroup* buckets[BUCKETS_MAX];
JournalRateLimitGroup *lru, *lru_tail;
@@ -58,18 +60,13 @@ struct JournalRateLimit {
uint8_t hash_key[16];
};
-JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) {
+JournalRateLimit *journal_rate_limit_new(void) {
JournalRateLimit *r;
- assert(interval > 0 || burst == 0);
-
r = new0(JournalRateLimit, 1);
if (!r)
return NULL;
- r->interval = interval;
- r->burst = burst;
-
random_bytes(r->hash_key, sizeof(r->hash_key));
return r;
@@ -109,7 +106,7 @@ _pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, us
assert(g);
for (i = 0; i < POOLS_MAX; i++)
- if (g->pools[i].begin + g->parent->interval >= ts)
+ if (g->pools[i].begin + g->interval >= ts)
return false;
return true;
@@ -126,9 +123,8 @@ static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) {
journal_rate_limit_group_free(r->lru_tail);
}
-static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) {
+static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t interval, usec_t ts) {
JournalRateLimitGroup *g;
- struct siphash state;
assert(r);
assert(id);
@@ -141,9 +137,9 @@ static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r,
if (!g->id)
goto fail;
- siphash24_init(&state, r->hash_key);
- string_hash_func(g->id, &state);
- g->hash = siphash24_finalize(&state);
+ g->hash = siphash24_string(g->id, r->hash_key);
+
+ g->interval = interval;
journal_rate_limit_vacuum(r, ts);
@@ -189,11 +185,10 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) {
return burst;
}
-int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) {
+int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available) {
uint64_t h;
JournalRateLimitGroup *g;
JournalRateLimitPool *p;
- struct siphash state;
unsigned burst;
usec_t ts;
@@ -209,16 +204,9 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
if (!r)
return 1;
- if (r->interval == 0 || r->burst == 0)
- return 1;
-
- burst = burst_modulate(r->burst, available);
-
ts = now(CLOCK_MONOTONIC);
- siphash24_init(&state, r->hash_key);
- string_hash_func(id, &state);
- h = siphash24_finalize(&state);
+ h = siphash24_string(id, r->hash_key);
g = r->buckets[h % BUCKETS_MAX];
LIST_FOREACH(bucket, g, g)
@@ -226,10 +214,16 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
break;
if (!g) {
- g = journal_rate_limit_group_new(r, id, ts);
+ g = journal_rate_limit_group_new(r, id, rl_interval, ts);
if (!g)
return -ENOMEM;
- }
+ } else
+ g->interval = rl_interval;
+
+ if (rl_interval == 0 || rl_burst == 0)
+ return 1;
+
+ burst = burst_modulate(rl_burst, available);
p = &g->pools[priority_map[priority]];
@@ -240,7 +234,7 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
return 1;
}
- if (p->begin + r->interval < ts) {
+ if (p->begin + rl_interval < ts) {
unsigned s;
s = p->suppressed;
diff --git a/src/journal/journald-rate-limit.h b/src/journal/journald-rate-limit.h
index 3a7f106de0..a2992800fe 100644
--- a/src/journal/journald-rate-limit.h
+++ b/src/journal/journald-rate-limit.h
@@ -5,6 +5,6 @@
typedef struct JournalRateLimit JournalRateLimit;
-JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst);
+JournalRateLimit *journal_rate_limit_new(void);
void journal_rate_limit_free(JournalRateLimit *r);
-int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available);
+int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available);
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 4f1550ec5b..434325c179 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -9,7 +9,6 @@
#include <sys/statvfs.h>
#include <linux/sockios.h>
-#include "libudev.h"
#include "sd-daemon.h"
#include "sd-journal.h"
#include "sd-messages.h"
@@ -75,6 +74,8 @@
* for a bit of additional metadata. */
#define DEFAULT_LINE_MAX (48*1024)
+#define DEFERRED_CLOSES_MAX (4096)
+
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;
@@ -253,8 +254,9 @@ static int open_journal(
bool seal,
JournalMetrics *metrics,
JournalFile **ret) {
- int r;
+
JournalFile *f;
+ int r;
assert(s);
assert(fname);
@@ -309,7 +311,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
server_add_acls(s->system_journal, 0);
(void) cache_space_refresh(s, &s->system_storage);
patch_min_use(&s->system_storage);
- } else if (r < 0) {
+ } else {
if (!IN_SET(r, -ENOENT, -EROFS))
log_warning_errno(r, "Failed to open system journal: %m");
@@ -400,17 +402,21 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
if (uid_for_system_journal(uid))
return s->system_journal;
- r = sd_id128_get_machine(&machine);
- if (r < 0)
- return s->system_journal;
-
f = ordered_hashmap_get(s->user_journals, UID_TO_PTR(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)
+ SD_ID128_FORMAT_VAL(machine), uid) < 0) {
+ log_oom();
return s->system_journal;
+ }
while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
/* Too many open? Then let's close one */
@@ -460,17 +466,81 @@ static int do_rotate(
return r;
}
+static void server_process_deferred_closes(Server *s) {
+ JournalFile *f;
+ Iterator i;
+
+ /* Perform any deferred closes which aren't still offlining. */
+ SET_FOREACH(f, s->deferred_closes, i) {
+ if (journal_file_is_offlining(f))
+ continue;
+
+ (void) set_remove(s->deferred_closes, f);
+ (void) journal_file_close(f);
+ }
+}
+
+static void server_vacuum_deferred_closes(Server *s) {
+ assert(s);
+
+ /* Make some room in the deferred closes list, so that it doesn't grow without bounds */
+ if (set_size(s->deferred_closes) < DEFERRED_CLOSES_MAX)
+ return;
+
+ /* Let's first remove all journal files that might already have completed closing */
+ server_process_deferred_closes(s);
+
+ /* And now, let's close some more until we reach the limit again. */
+ while (set_size(s->deferred_closes) >= DEFERRED_CLOSES_MAX) {
+ JournalFile *f;
+
+ assert_se(f = set_steal_first(s->deferred_closes));
+ journal_file_close(f);
+ }
+}
+
+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;
+ 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");
+
+ if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/", SD_ID128_FORMAT_VAL(machine)) < 0)
+ return log_oom();
+
+ dir = opendir(path);
+ if (!dir)
+ return log_error_errno(errno, "Failed to open user journal directory '%s': %m", path);
+
+ if (ret_dir)
+ *ret_dir = TAKE_PTR(dir);
+ if (ret_path)
+ *ret_path = TAKE_PTR(path);
+
+ return 0;
+}
+
void server_rotate(Server *s) {
+ _cleanup_free_ char *path = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
JournalFile *f;
- void *k;
Iterator i;
+ void *k;
int r;
log_debug("Rotating...");
+ /* First, rotate the system journal (either in its runtime flavour or in its runtime flavour) */
(void) do_rotate(s, &s->runtime_journal, "runtime", false, 0);
(void) do_rotate(s, &s->system_journal, "system", s->seal, 0);
+ /* Then, rotate all user journals we have open (keeping them open) */
ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
r = do_rotate(s, &f, "user", s->seal, PTR_TO_UID(k));
if (r >= 0)
@@ -480,12 +550,94 @@ void server_rotate(Server *s) {
ordered_hashmap_remove(s->user_journals, k);
}
- /* Perform any deferred closes which aren't still offlining. */
- SET_FOREACH(f, s->deferred_closes, i)
- if (!journal_file_is_offlining(f)) {
- (void) set_remove(s->deferred_closes, f);
- (void) journal_file_close(f);
+ /* 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);
}
+ }
+
+ server_process_deferred_closes(s);
}
void server_sync(Server *s) {
@@ -938,7 +1090,7 @@ void server_dispatch_message(
if (c && c->unit) {
(void) determine_space(s, &available, NULL);
- rl = journal_rate_limit_test(s->rate_limit, c->unit, priority & LOG_PRIMASK, available);
+ rl = journal_rate_limit_test(s->rate_limit, c->unit, c->log_rate_limit_interval, c->log_rate_limit_burst, priority & LOG_PRIMASK, available);
if (rl == 0)
return;
@@ -1036,7 +1188,8 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
r = 0;
finish:
- journal_file_post_change(s->system_journal);
+ if (s->system_journal)
+ journal_file_post_change(s->system_journal);
s->runtime_journal = journal_file_close(s->runtime_journal);
@@ -1096,10 +1249,10 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void
assert(s);
assert(fd == s->native_fd || fd == s->syslog_fd || fd == s->audit_fd);
- if (revents != EPOLLIN) {
- log_error("Got invalid event from epoll for datagram fd: %"PRIx32, revents);
- return -EIO;
- }
+ if (revents != EPOLLIN)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Got invalid event from epoll for datagram fd: %" PRIx32,
+ revents);
/* Try to get the right size, if we can. (Not all sockets support SIOCINQ, hence we just try, but don't rely on
* it.) */
@@ -1113,8 +1266,7 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void
if (!GREEDY_REALLOC(s->buffer, s->buffer_size, m))
return log_oom();
- iovec.iov_base = s->buffer;
- iovec.iov_len = s->buffer_size - 1; /* Leave room for trailing NUL we add later */
+ iovec = IOVEC_MAKE(s->buffer, s->buffer_size - 1); /* Leave room for trailing NUL we add later */
n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
if (n < 0) {
@@ -1595,11 +1747,9 @@ static int dispatch_watchdog(sd_event_source *es, uint64_t usec, void *userdata)
}
static int server_connect_notify(Server *s) {
- union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- };
+ union sockaddr_union sa = {};
const char *e;
- int r;
+ int r, salen;
assert(s);
assert(s->notify_fd < 0);
@@ -1628,15 +1778,9 @@ static int server_connect_notify(Server *s) {
if (!e)
return 0;
- if (!IN_SET(e[0], '@', '/') || e[1] == 0) {
- log_error("NOTIFY_SOCKET set to an invalid value: %s", e);
- return -EINVAL;
- }
-
- if (strlen(e) > sizeof(sa.un.sun_path)) {
- log_error("NOTIFY_SOCKET path too long: %s", e);
- return -EINVAL;
- }
+ 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);
s->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->notify_fd < 0)
@@ -1644,11 +1788,7 @@ static int server_connect_notify(Server *s) {
(void) fd_inc_sndbuf(s->notify_fd, NOTIFY_SNDBUF_SIZE);
- strncpy(sa.un.sun_path, e, sizeof(sa.un.sun_path));
- if (sa.un.sun_path[0] == '@')
- sa.un.sun_path[0] = 0;
-
- r = connect(s->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+ r = connect(s->notify_fd, &sa.sa, salen);
if (r < 0)
return log_error_errno(errno, "Failed to connect to notify socket: %m");
@@ -1745,38 +1885,34 @@ int server_init(Server *s) {
if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/socket", 0) > 0) {
- if (s->native_fd >= 0) {
- log_error("Too many native sockets passed.");
- return -EINVAL;
- }
+ if (s->native_fd >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many native sockets passed.");
s->native_fd = fd;
} else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, "/run/systemd/journal/stdout", 0) > 0) {
- if (s->stdout_fd >= 0) {
- log_error("Too many stdout sockets passed.");
- return -EINVAL;
- }
+ if (s->stdout_fd >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many stdout sockets passed.");
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) {
- if (s->syslog_fd >= 0) {
- log_error("Too many /dev/log sockets passed.");
- return -EINVAL;
- }
+ if (s->syslog_fd >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many /dev/log sockets passed.");
s->syslog_fd = fd;
} else if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) {
- if (s->audit_fd >= 0) {
- log_error("Too many audit sockets passed.");
- return -EINVAL;
- }
+ if (s->audit_fd >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many audit sockets passed.");
s->audit_fd = fd;
@@ -1845,11 +1981,7 @@ int server_init(Server *s) {
if (r < 0)
return r;
- s->udev = udev_new();
- if (!s->udev)
- return -ENOMEM;
-
- s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst);
+ s->rate_limit = journal_rate_limit_new();
if (!s->rate_limit)
return -ENOMEM;
@@ -1949,8 +2081,6 @@ void server_done(Server *s) {
if (s->mmap)
mmap_cache_unref(s->mmap);
-
- udev_unref(s->udev);
}
static const char* const storage_table[_STORAGE_MAX] = {
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index 983be8bb89..6d4847b0cd 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -137,8 +137,6 @@ struct Server {
Set *deferred_closes;
- struct udev *udev;
-
uint64_t *kernel_seqnum;
bool dev_kmsg_readable:1;
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index dbf3503a82..137c8f0446 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -12,6 +12,7 @@
#include "alloc-util.h"
#include "dirent-util.h"
+#include "env-file.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -31,6 +32,7 @@
#include "stdio-util.h"
#include "string-util.h"
#include "syslog-util.h"
+#include "tmpfile-util.h"
#include "unit-name.h"
#define STDOUT_STREAMS_MAX 4096
@@ -125,7 +127,7 @@ void stdout_stream_free(StdoutStream *s) {
DEFINE_TRIVIAL_CLEANUP_FUNC(StdoutStream*, stdout_stream_free);
-static void stdout_stream_destroy(StdoutStream *s) {
+void stdout_stream_destroy(StdoutStream *s) {
if (!s)
return;
@@ -534,7 +536,7 @@ terminate:
return 0;
}
-static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
+int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
_cleanup_(stdout_stream_freep) StdoutStream *stream = NULL;
sd_id128_t id;
int r;
@@ -596,10 +598,10 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
assert(s);
- if (revents != EPOLLIN) {
- log_error("Got invalid event from epoll for stdout server fd: %"PRIx32, revents);
- return -EIO;
- }
+ if (revents != EPOLLIN)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Got invalid event from epoll for stdout server fd: %" PRIx32,
+ revents);
fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (fd < 0) {
@@ -610,7 +612,17 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
}
if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
- log_warning("Too many stdout streams, refusing connection.");
+ struct ucred u;
+
+ r = getpeercred(fd, &u);
+
+ /* By closing fd here we make sure that the client won't wait too long for journald to
+ * gather all the data it adds to the error message to find out that the connection has
+ * just been refused.
+ */
+ fd = safe_close(fd);
+
+ server_driver_message(s, r < 0 ? 0 : u.pid, NULL, LOG_MESSAGE("Too many stdout streams, refusing connection."), NULL);
return 0;
}
@@ -641,7 +653,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
return log_oom();
}
- r = parse_env_file(NULL, stream->state_file, NEWLINE,
+ r = parse_env_file(NULL, stream->state_file,
"PRIORITY", &priority,
"LEVEL_PREFIX", &level_prefix,
"FORWARD_TO_SYSLOG", &forward_to_syslog,
@@ -649,8 +661,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
"FORWARD_TO_CONSOLE", &forward_to_console,
"IDENTIFIER", &stream->identifier,
"UNIT", &stream->unit_id,
- "STREAM_ID", &stream_id,
- NULL);
+ "STREAM_ID", &stream_id);
if (r < 0)
return log_error_errno(r, "Failed to read: %s", stream->state_file);
@@ -793,7 +804,7 @@ int server_open_stdout_socket(Server *s) {
if (s->stdout_fd < 0)
return log_error_errno(errno, "socket() failed: %m");
- (void) unlink(sa.un.sun_path);
+ (void) sockaddr_un_unlink(&sa.un);
r = bind(s->stdout_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (r < 0)
@@ -804,7 +815,7 @@ int server_open_stdout_socket(Server *s) {
if (listen(s->stdout_fd, SOMAXCONN) < 0)
return log_error_errno(errno, "listen(%s) failed: %m", sa.un.sun_path);
} else
- fd_nonblock(s->stdout_fd, 1);
+ (void) fd_nonblock(s->stdout_fd, true);
r = sd_event_add_io(s->event, &s->stdout_event_source, s->stdout_fd, EPOLLIN, stdout_stream_new, s);
if (r < 0)
diff --git a/src/journal/journald-stream.h b/src/journal/journald-stream.h
index bc5622ab3b..487376e763 100644
--- a/src/journal/journald-stream.h
+++ b/src/journal/journald-stream.h
@@ -10,4 +10,6 @@ int server_open_stdout_socket(Server *s);
int server_restore_streams(Server *s, FDSet *fds);
void stdout_stream_free(StdoutStream *s);
+int stdout_stream_install(Server *s, int fd, StdoutStream **ret);
+void stdout_stream_destroy(StdoutStream *s);
void stdout_stream_send_notify(StdoutStream *s);
diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
index 9dea116722..a60a259bc4 100644
--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -117,7 +117,7 @@ void server_forward_syslog(Server *s, int priority, const char *identifier, cons
header_pid[STRLEN("[]: ") + DECIMAL_STR_MAX(pid_t) + 1];
int n = 0;
time_t t;
- struct tm *tm;
+ struct tm tm;
_cleanup_free_ char *ident_buf = NULL;
assert(s);
@@ -134,10 +134,9 @@ void server_forward_syslog(Server *s, int priority, const char *identifier, cons
/* Second: timestamp */
t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
- tm = localtime(&t);
- if (!tm)
+ if (!localtime_r(&t, &tm))
return;
- if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
+ if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
return;
iovec[n++] = IOVEC_MAKE_STRING(header_time);
@@ -194,7 +193,7 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid)
e = l;
l--;
- if (p[l-1] == ']') {
+ if (l > 0 && p[l-1] == ']') {
size_t k = l-1;
for (;;) {
@@ -219,13 +218,16 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid)
if (t)
*identifier = t;
- if (strchr(WHITESPACE, p[e]))
+ /* Single space is used as separator */
+ if (p[e] != '\0' && strchr(WHITESPACE, p[e]))
e++;
+
+ l = (p - *buf) + e;
*buf = p + e;
- return e;
+ return l;
}
-static void syslog_skip_date(char **buf) {
+static int syslog_skip_timestamp(const char **buf) {
enum {
LETTER,
SPACE,
@@ -245,24 +247,21 @@ static void syslog_skip_date(char **buf) {
SPACE
};
- char *p;
+ const char *p, *t;
unsigned i;
assert(buf);
assert(*buf);
- p = *buf;
-
- for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
-
+ for (i = 0, p = *buf; i < ELEMENTSOF(sequence); i++, p++) {
if (!*p)
- return;
+ return 0;
switch (sequence[i]) {
case SPACE:
if (*p != ' ')
- return;
+ return 0;
break;
case SPACE_OR_NUMBER:
@@ -272,48 +271,57 @@ static void syslog_skip_date(char **buf) {
_fallthrough_;
case NUMBER:
if (*p < '0' || *p > '9')
- return;
+ return 0;
break;
case LETTER:
if (!(*p >= 'A' && *p <= 'Z') &&
!(*p >= 'a' && *p <= 'z'))
- return;
+ return 0;
break;
case COLON:
if (*p != ':')
- return;
+ return 0;
break;
}
}
+ t = *buf;
*buf = p;
+ return p - t;
}
void server_process_syslog_message(
Server *s,
const char *buf,
- size_t buf_len,
+ size_t raw_len,
const struct ucred *ucred,
const struct timeval *tv,
const char *label,
size_t label_len) {
- char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
- syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)], *msg;
- const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
- _cleanup_free_ char *identifier = NULL, *pid = NULL;
+ char *t, syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
+ syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
+ const char *msg, *syslog_ts, *a;
+ _cleanup_free_ char *identifier = NULL, *pid = NULL,
+ *dummy = NULL, *msg_msg = NULL, *msg_raw = NULL;
int priority = LOG_USER | LOG_INFO, r;
ClientContext *context = NULL;
struct iovec *iovec;
- size_t n = 0, m, i;
+ size_t n = 0, m, i, leading_ws, syslog_ts_len;
+ bool store_raw;
assert(s);
assert(buf);
+ /* The message cannot be empty. */
+ assert(raw_len > 0);
+ /* The buffer NUL-terminated and can be used a string. raw_len is the length
+ * without the terminating NUL byte, the buffer is actually one bigger. */
+ assert(buf[raw_len] == '\0');
if (ucred && pid_is_valid(ucred->pid)) {
r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
@@ -321,26 +329,50 @@ void server_process_syslog_message(
log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
}
- /* We are creating copy of the message because we want to forward original message verbatim to the legacy
- syslog implementation */
- for (i = buf_len; i > 0; i--)
+ /* We are creating a copy of the message because we want to forward the original message
+ verbatim to the legacy syslog implementation */
+ for (i = raw_len; i > 0; i--)
if (!strchr(WHITESPACE, buf[i-1]))
break;
- msg = newa(char, i + 1);
- *((char *) mempcpy(msg, buf, i)) = 0;
- msg = skip_leading_chars(msg, WHITESPACE);
+ leading_ws = strspn(buf, WHITESPACE);
+
+ if (i == 0)
+ /* The message contains only whitespaces */
+ msg = buf + raw_len;
+ else if (i == raw_len)
+ /* Nice! No need to strip anything on the end, let's optimize this a bit */
+ msg = buf + leading_ws;
+ else {
+ msg = dummy = new(char, i - leading_ws + 1);
+ if (!dummy) {
+ log_oom();
+ return;
+ }
- syslog_parse_priority((const char **)&msg, &priority, true);
+ memcpy(dummy, buf + leading_ws, i - leading_ws);
+ dummy[i - leading_ws] = 0;
+ }
+
+ /* We will add the SYSLOG_RAW= field when we stripped anything
+ * _or_ if the input message contained NUL bytes. */
+ store_raw = msg != buf || strlen(msg) != raw_len;
+
+ syslog_parse_priority(&msg, &priority, true);
if (!client_context_test_priority(context, priority))
return;
- if (s->forward_to_syslog)
- forward_syslog_raw(s, priority, buf, buf_len, ucred, tv);
+ syslog_ts = msg;
+ syslog_ts_len = syslog_skip_timestamp(&msg);
+ if (syslog_ts_len == 0)
+ /* We failed to parse the full timestamp, store the raw message too */
+ store_raw = true;
+
+ syslog_parse_identifier(&msg, &identifier, &pid);
- syslog_skip_date(&msg);
- syslog_parse_identifier((const char**)&msg, &identifier, &pid);
+ if (s->forward_to_syslog)
+ forward_syslog_raw(s, priority, buf, raw_len, ucred, tv);
if (s->forward_to_kmsg)
server_forward_kmsg(s, priority, identifier, msg, ucred);
@@ -351,7 +383,7 @@ void server_process_syslog_message(
if (s->forward_to_wall)
server_forward_wall(s, priority, identifier, msg, ucred);
- m = N_IOVEC_META_FIELDS + 6 + client_context_extra_fields_n_iovec(context);
+ m = N_IOVEC_META_FIELDS + 8 + client_context_extra_fields_n_iovec(context);
iovec = newa(struct iovec, m);
iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog");
@@ -365,18 +397,46 @@ void server_process_syslog_message(
}
if (identifier) {
- syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier);
- iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier);
+ a = strjoina("SYSLOG_IDENTIFIER=", identifier);
+ iovec[n++] = IOVEC_MAKE_STRING(a);
}
if (pid) {
- syslog_pid = strjoina("SYSLOG_PID=", pid);
- iovec[n++] = IOVEC_MAKE_STRING(syslog_pid);
+ a = strjoina("SYSLOG_PID=", pid);
+ iovec[n++] = IOVEC_MAKE_STRING(a);
}
- message = strjoina("MESSAGE=", msg);
- if (message)
- iovec[n++] = IOVEC_MAKE_STRING(message);
+ if (syslog_ts_len > 0) {
+ const size_t hlen = STRLEN("SYSLOG_TIMESTAMP=");
+
+ t = newa(char, hlen + syslog_ts_len);
+ memcpy(t, "SYSLOG_TIMESTAMP=", hlen);
+ memcpy(t + hlen, syslog_ts, syslog_ts_len);
+
+ iovec[n++] = IOVEC_MAKE(t, hlen + syslog_ts_len);
+ }
+
+ msg_msg = strjoin("MESSAGE=", msg);
+ if (!msg_msg) {
+ log_oom();
+ return;
+ }
+ iovec[n++] = IOVEC_MAKE_STRING(msg_msg);
+
+ if (store_raw) {
+ const size_t hlen = STRLEN("SYSLOG_RAW=");
+
+ msg_raw = new(char, hlen + raw_len);
+ if (!msg_raw) {
+ log_oom();
+ return;
+ }
+
+ memcpy(msg_raw, "SYSLOG_RAW=", hlen);
+ memcpy(msg_raw + hlen, buf, raw_len);
+
+ iovec[n++] = IOVEC_MAKE(msg_raw, hlen + raw_len);
+ }
server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
}
@@ -387,7 +447,6 @@ int server_open_syslog_socket(Server *s) {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/dev-log",
};
- static const int one = 1;
int r;
assert(s);
@@ -397,7 +456,7 @@ int server_open_syslog_socket(Server *s) {
if (s->syslog_fd < 0)
return log_error_errno(errno, "socket() failed: %m");
- (void) unlink(sa.un.sun_path);
+ (void) sockaddr_un_unlink(&sa.un);
r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (r < 0)
@@ -405,23 +464,23 @@ int server_open_syslog_socket(Server *s) {
(void) chmod(sa.un.sun_path, 0666);
} else
- fd_nonblock(s->syslog_fd, 1);
+ (void) fd_nonblock(s->syslog_fd, true);
- r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- return log_error_errno(errno, "SO_PASSCRED failed: %m");
+ return log_error_errno(r, "SO_PASSCRED failed: %m");
#if HAVE_SELINUX
if (mac_selinux_use()) {
- r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
+ r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, true);
if (r < 0)
- log_warning_errno(errno, "SO_PASSSEC failed: %m");
+ log_warning_errno(r, "SO_PASSSEC failed: %m");
}
#endif
- r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
+ r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, true);
if (r < 0)
- return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
+ return log_error_errno(r, "SO_TIMESTAMP failed: %m");
r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
if (r < 0)
diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c
index 75328aa94b..370c9b32e2 100644
--- a/src/journal/journald-wall.c
+++ b/src/journal/journald-wall.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2014 Sebastian Thorarensen
-***/
#include "alloc-util.h"
#include "format-util.h"
diff --git a/src/journal/journald-wall.h b/src/journal/journald-wall.h
index be27370d6b..d081c8254c 100644
--- a/src/journal/journald-wall.h
+++ b/src/journal/journald-wall.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- Copyright © 2014 Sebastian Thorarensen
-***/
#include "journald-server.h"
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index 94d5c678aa..2f1c661153 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -40,3 +40,4 @@
#MaxLevelConsole=info
#MaxLevelWall=emerg
#LineMax=48K
+#ReadKMsg=yes
diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c
index ff194dd951..6c61f17c7d 100644
--- a/src/journal/lookup3.c
+++ b/src/journal/lookup3.c
@@ -319,7 +319,7 @@ uint32_t jenkins_hashlittle( const void *key, size_t length, uint32_t initval)
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#if !VALGRIND && !defined(__SANITIZE_ADDRESS__)
+#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER
switch(length)
{
@@ -504,7 +504,7 @@ void jenkins_hashlittle2(
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#if !VALGRIND && !defined(__SANITIZE_ADDRESS__)
+#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER
switch(length)
{
@@ -680,7 +680,7 @@ uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval)
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#if !VALGRIND && !defined(__SANITIZE_ADDRESS__)
+#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER
switch(length)
{
diff --git a/src/journal/meson.build b/src/journal/meson.build
index 807dece545..e03d6dc232 100644
--- a/src/journal/meson.build
+++ b/src/journal/meson.build
@@ -34,7 +34,7 @@ endif
############################################################
audit_type_includes = [config_h,
- missing_h,
+ missing_audit_h,
'linux/audit.h']
if conf.get('HAVE_AUDIT') == 1
audit_type_includes += 'libaudit.h'
diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c
index 2366055f0a..90549f1c9f 100644
--- a/src/journal/mmap-cache.c
+++ b/src/journal/mmap-cache.c
@@ -53,7 +53,7 @@ struct MMapFileDescriptor {
};
struct MMapCache {
- int n_ref;
+ unsigned n_ref;
unsigned n_windows;
unsigned n_hit, n_missed;
@@ -85,14 +85,6 @@ MMapCache* mmap_cache_new(void) {
return m;
}
-MMapCache* mmap_cache_ref(MMapCache *m) {
- assert(m);
- assert(m->n_ref > 0);
-
- m->n_ref++;
- return m;
-}
-
static void window_unlink(Window *w) {
Context *c;
@@ -278,7 +270,7 @@ static void context_free(Context *c) {
free(c);
}
-static void mmap_cache_free(MMapCache *m) {
+static MMapCache *mmap_cache_free(MMapCache *m) {
int i;
assert(m);
@@ -292,22 +284,10 @@ static void mmap_cache_free(MMapCache *m) {
while (m->unused)
window_free(m->unused);
- free(m);
+ return mfree(m);
}
-MMapCache* mmap_cache_unref(MMapCache *m) {
-
- if (!m)
- return NULL;
-
- assert(m->n_ref > 0);
-
- m->n_ref--;
- if (m->n_ref == 0)
- mmap_cache_free(m);
-
- return NULL;
-}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(MMapCache, mmap_cache, mmap_cache_free);
static int make_room(MMapCache *m) {
assert(m);
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 83abd82d1c..b5ff5b64f3 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -16,6 +16,8 @@
#include "catalog.h"
#include "compress.h"
#include "dirent-util.h"
+#include "env-file.h"
+#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@@ -381,7 +383,7 @@ static char *match_make_string(Match *m) {
return strdup("none");
if (m->type == MATCH_DISCRETE)
- return strndup(m->data, m->size);
+ return cescape_length(m->data, m->size);
LIST_FOREACH(matches, i, m->matches) {
char *t, *k;
@@ -433,6 +435,8 @@ _public_ void sd_journal_flush_matches(sd_journal *j) {
}
_pure_ static int compare_with_location(JournalFile *f, Location *l) {
+ int r;
+
assert(f);
assert(l);
assert(f->location_type == LOCATION_SEEK);
@@ -449,35 +453,31 @@ _pure_ static int compare_with_location(JournalFile *f, Location *l) {
if (l->seqnum_set &&
sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
- if (f->current_seqnum < l->seqnum)
- return -1;
- if (f->current_seqnum > l->seqnum)
- return 1;
+ r = CMP(f->current_seqnum, l->seqnum);
+ if (r != 0)
+ return r;
}
if (l->monotonic_set &&
sd_id128_equal(f->current_boot_id, l->boot_id)) {
- if (f->current_monotonic < l->monotonic)
- return -1;
- if (f->current_monotonic > l->monotonic)
- return 1;
+ r = CMP(f->current_monotonic, l->monotonic);
+ if (r != 0)
+ return r;
}
if (l->realtime_set) {
- if (f->current_realtime < l->realtime)
- return -1;
- if (f->current_realtime > l->realtime)
- return 1;
+ r = CMP(f->current_realtime, l->realtime);
+ if (r != 0)
+ return r;
}
if (l->xor_hash_set) {
- if (f->current_xor_hash < l->xor_hash)
- return -1;
- if (f->current_xor_hash > l->xor_hash)
- return 1;
+ r = CMP(f->current_xor_hash, l->xor_hash);
+ if (r != 0)
+ return r;
}
return 0;
@@ -1184,8 +1184,7 @@ static bool file_has_type_prefix(const char *prefix, const char *filename) {
tilded = strjoina(full, "~");
atted = strjoina(prefix, "@");
- return streq(filename, full) ||
- streq(filename, tilded) ||
+ return STR_IN_SET(filename, full, tilded) ||
startswith(filename, atted);
}
@@ -1889,7 +1888,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
assert_return(machine_name_is_valid(machine), -EINVAL);
p = strjoina("/run/systemd/machines/", machine);
- r = parse_env_file(NULL, p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
+ r = parse_env_file(NULL, p,
+ "ROOT", &root,
+ "CLASS", &class);
if (r == -ENOENT)
return -EHOSTDOWN;
if (r < 0)
@@ -2824,31 +2825,30 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
return r;
/* Let's do the type check by hand, since we used 0 context above. */
- if (o->object.type != OBJECT_DATA) {
- log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
- j->unique_file->path, j->unique_offset,
- o->object.type, OBJECT_DATA);
- return -EBADMSG;
- }
+ if (o->object.type != OBJECT_DATA)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object has type %d, expected %d",
+ j->unique_file->path,
+ j->unique_offset,
+ o->object.type, OBJECT_DATA);
r = return_data(j, j->unique_file, o, &odata, &ol);
if (r < 0)
return r;
/* Check if we have at least the field name and "=". */
- if (ol <= k) {
- log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
- j->unique_file->path, j->unique_offset,
- ol, k + 1);
- return -EBADMSG;
- }
-
- if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
- log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
- j->unique_file->path, j->unique_offset,
- j->unique_field);
- return -EBADMSG;
- }
+ if (ol <= k)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
+ j->unique_file->path,
+ j->unique_offset, ol, k + 1);
+
+ if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=')
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object does not start with \"%s=\"",
+ j->unique_file->path,
+ j->unique_offset,
+ j->unique_field);
/* OK, now let's see if we already returned this data
* object by checking if it exists in the earlier
@@ -2979,10 +2979,11 @@ _public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) {
return r;
/* Because we used OBJECT_UNUSED above, we need to do our type check manually */
- if (o->object.type != OBJECT_FIELD) {
- log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD);
- return -EBADMSG;
- }
+ if (o->object.type != OBJECT_FIELD)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "%s:offset " OFSfmt ": object has type %i, expected %i",
+ f->path, j->fields_offset,
+ o->object.type, OBJECT_FIELD);
sz = le64toh(o->object.size) - offsetof(Object, field.payload);
diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c
index 8eae993780..192bb0cb07 100644
--- a/src/journal/test-catalog.c
+++ b/src/journal/test-catalog.c
@@ -11,17 +11,16 @@
#include "catalog.h"
#include "fd-util.h"
#include "fs-util.h"
-#include "fileio.h"
#include "log.h"
#include "macro.h"
+#include "path-util.h"
#include "string-util.h"
+#include "strv.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "util.h"
-static const char *catalog_dirs[] = {
- CATALOG_DIR,
- NULL,
-};
-
+static char** catalog_dirs = NULL;
static const char *no_catalog_dirs[] = {
"/bin/hopefully/with/no/catalog",
NULL
@@ -167,8 +166,8 @@ static void test_catalog_update(const char *database) {
assert_se(r == 0);
/* Make sure that we at least have some files loaded or the
- catalog_list below will fail. */
- r = catalog_update(database, NULL, catalog_dirs);
+ * catalog_list below will fail. */
+ r = catalog_update(database, NULL, (const char * const *) catalog_dirs);
assert_se(r == 0);
}
@@ -207,8 +206,14 @@ int main(int argc, char *argv[]) {
setlocale(LC_ALL, "de_DE.UTF-8");
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
+
+ /* If test-catalog is located at the build directory, then use catalogs in that.
+ * If it is not, e.g. installed by systemd-tests package, then use installed catalogs. */
+ catalog_dirs = STRV_MAKE(get_catalog_dir());
+
+ assert_se(access(catalog_dirs[0], F_OK) >= 0);
+ log_notice("Using catalog directory '%s'", catalog_dirs[0]);
test_catalog_file_lang();
diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c
index 411df3fa7a..7f13b611e6 100644
--- a/src/journal/test-compress-benchmark.c
+++ b/src/journal/test-compress-benchmark.c
@@ -8,6 +8,7 @@
#include "process-util.h"
#include "random-util.h"
#include "string-util.h"
+#include "tests.h"
#include "util.h"
typedef int (compress_t)(const void *src, uint64_t src_size, void *dst,
@@ -142,30 +143,23 @@ static void test_compress_decompress(const char* label, const char* type,
int main(int argc, char *argv[]) {
#if HAVE_XZ || HAVE_LZ4
- const char *i;
- int r;
-
- log_set_max_level(LOG_INFO);
+ test_setup_logging(LOG_INFO);
if (argc >= 2) {
unsigned x;
assert_se(safe_atou(argv[1], &x) >= 0);
arg_duration = x * USEC_PER_SEC;
- } else {
- bool slow;
-
- r = getenv_bool("SYSTEMD_SLOW_TESTS");
- slow = r >= 0 ? r : SYSTEMD_SLOW_TESTS_DEFAULT;
-
- arg_duration = slow ? 2 * USEC_PER_SEC : USEC_PER_SEC / 50;
- }
+ } else
+ arg_duration = slow_tests_enabled() ?
+ 2 * USEC_PER_SEC : USEC_PER_SEC / 50;
if (argc == 3)
(void) safe_atozu(argv[2], &arg_start);
else
arg_start = getpid_cached();
+ const char *i;
NULSTR_FOREACH(i, "zeros\0simple\0random\0") {
#if HAVE_XZ
test_compress_decompress("XZ", i, compress_blob_xz, decompress_blob_xz);
@@ -176,6 +170,6 @@ int main(int argc, char *argv[]) {
}
return 0;
#else
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("No compression feature is enabled");
#endif
}
diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c
index 791c6fdffb..1b050b7052 100644
--- a/src/journal/test-compress.c
+++ b/src/journal/test-compress.c
@@ -7,11 +7,12 @@
#include "alloc-util.h"
#include "compress.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "macro.h"
#include "path-util.h"
#include "random-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "util.h"
#if HAVE_XZ
@@ -131,6 +132,32 @@ static void test_decompress_startswith(int compression,
assert_se(r > 0);
}
+static void test_decompress_startswith_short(int compression,
+ compress_blob_t compress,
+ decompress_sw_t decompress_sw) {
+
+#define TEXT "HUGE=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+
+ char buf[1024];
+ size_t i, csize;
+ int r;
+
+ log_info("/* %s with %s */", __func__, object_compressed_to_string(compression));
+
+ r = compress(TEXT, sizeof TEXT, buf, sizeof buf, &csize);
+ assert_se(r == 0);
+
+ for (i = 1; i < strlen(TEXT); i++) {
+ size_t alloc_size = i;
+ _cleanup_free_ void *buf2 = NULL;
+
+ assert_se(buf2 = malloc(i));
+
+ assert_se(decompress_sw(buf, csize, &buf2, &alloc_size, TEXT, i, TEXT[i]) == 1);
+ assert_se(decompress_sw(buf, csize, &buf2, &alloc_size, TEXT, i, 'y') == 0);
+ }
+}
+
static void test_compress_stream(int compression,
const char* cat,
compress_stream_t compress,
@@ -197,21 +224,17 @@ static void test_compress_stream(int compression,
#if HAVE_LZ4
static void test_lz4_decompress_partial(void) {
- char buf[20000];
+ char buf[20000], buf2[100];
size_t buf_size = sizeof(buf), compressed;
int r;
_cleanup_free_ char *huge = NULL;
#define HUGE_SIZE (4096*1024)
- huge = malloc(HUGE_SIZE);
+ assert_se(huge = malloc(HUGE_SIZE));
memset(huge, 'x', HUGE_SIZE);
memcpy(huge, "HUGE=", 5);
-#if LZ4_VERSION_NUMBER >= 10700
r = LZ4_compress_default(huge, buf, HUGE_SIZE, buf_size);
-#else
- r = LZ4_compress_limitedOutput(huge, buf, HUGE_SIZE, buf_size);
-#endif
assert_se(r >= 0);
compressed = r;
log_info("Compressed %i → %zu", HUGE_SIZE, compressed);
@@ -226,14 +249,15 @@ static void test_lz4_decompress_partial(void) {
assert_se(r >= 0);
log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE, r);
- /* We expect this to fail, because that's how current lz4 works. If this
- * call succeeds, then lz4 has been fixed, and we need to change our code.
- */
- r = LZ4_decompress_safe_partial(buf, huge,
- compressed,
- 12, HUGE_SIZE-1);
- assert_se(r < 0);
- log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE-1, r);
+ for (size_t size = 1; size < sizeof(buf2); size++) {
+ /* This failed in older lz4s but works in newer ones. */
+ r = LZ4_decompress_safe_partial(buf, buf2, compressed, size, size);
+ log_info("Decompressed partial %zu/%zu → %i (%s)", size, size, r,
+ r < 0 ? "bad" : "good");
+ if (r >= 0 && LZ4_versionNumber() >= 10803)
+ /* lz4 <= 1.8.2 should fail that test, let's only check for newer ones */
+ assert_se(memcmp(buf2, huge, r) == 0);
+ }
}
#endif
@@ -253,7 +277,7 @@ int main(int argc, char *argv[]) {
memcpy(huge, "HUGE=", 5);
char_array_0(huge);
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
random_bytes(data + 7, sizeof(data) - 7);
@@ -275,6 +299,9 @@ int main(int argc, char *argv[]) {
test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat",
compress_stream_xz, decompress_stream_xz, srcfile);
+
+ test_decompress_startswith_short(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_startswith_xz);
+
#else
log_info("/* XZ test skipped */");
#endif
@@ -299,12 +326,16 @@ int main(int argc, char *argv[]) {
compress_stream_lz4, decompress_stream_lz4, srcfile);
test_lz4_decompress_partial();
+
+ test_decompress_startswith_short(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_startswith_lz4);
+
#else
log_info("/* LZ4 test skipped */");
#endif
return 0;
#else
+ log_info("/* XZ and LZ4 tests skipped */");
return EXIT_TEST_SKIP;
#endif
}
diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c
index b25a983498..8e839920b9 100644
--- a/src/journal/test-journal-enum.c
+++ b/src/journal/test-journal-enum.c
@@ -7,12 +7,13 @@
#include "journal-internal.h"
#include "log.h"
#include "macro.h"
+#include "tests.h"
int main(int argc, char *argv[]) {
unsigned n = 0;
- _cleanup_(sd_journal_closep) sd_journal*j = NULL;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY) >= 0);
diff --git a/src/journal/test-journal-init.c b/src/journal/test-journal-init.c
index a43672b6e1..860baca383 100644
--- a/src/journal/test-journal-init.c
+++ b/src/journal/test-journal-init.c
@@ -5,6 +5,7 @@
#include "log.h"
#include "parse-util.h"
#include "rm-rf.h"
+#include "tests.h"
#include "util.h"
int main(int argc, char *argv[]) {
@@ -12,7 +13,7 @@ int main(int argc, char *argv[]) {
int r, i, I = 100;
char t[] = "/tmp/journal-stream-XXXXXX";
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
if (argc >= 2) {
r = safe_atoi(argv[1], &I);
diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c
index 1f0c9f8f2a..cf0561df61 100644
--- a/src/journal/test-journal-interleaving.c
+++ b/src/journal/test-journal-interleaving.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2013 Marius Vollmer
-***/
#include <fcntl.h>
#include <unistd.h>
@@ -9,15 +6,16 @@
#include "sd-journal.h"
#include "alloc-util.h"
+#include "io-util.h"
#include "journal-file.h"
#include "journal-vacuum.h"
#include "log.h"
#include "parse-util.h"
#include "rm-rf.h"
+#include "tests.h"
#include "util.h"
-/* This program tests skipping around in a multi-file journal.
- */
+/* This program tests skipping around in a multi-file journal. */
static bool arg_keep = false;
@@ -61,8 +59,7 @@ static void append_number(JournalFile *f, int n, uint64_t *seqnum) {
previous_ts = ts;
assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
- iovec[0].iov_base = p;
- iovec[0].iov_len = strlen(p);
+ iovec[0] = IOVEC_MAKE_STRING(p);
assert_ret(journal_file_append_entry(f, &ts, NULL, iovec, 1, seqnum, NULL, NULL));
free(p);
}
@@ -274,11 +271,11 @@ static void test_sequence_numbers(void) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
/* journal_file_open requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("/etc/machine-id not found");
arg_keep = argc > 1;
diff --git a/src/journal/test-journal-match.c b/src/journal/test-journal-match.c
index 4e5ad1791a..b17527916c 100644
--- a/src/journal/test-journal-match.c
+++ b/src/journal/test-journal-match.c
@@ -8,13 +8,14 @@
#include "journal-internal.h"
#include "log.h"
#include "string-util.h"
+#include "tests.h"
#include "util.h"
int main(int argc, char *argv[]) {
- _cleanup_(sd_journal_closep) sd_journal*j = NULL;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
_cleanup_free_ char *t;
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(sd_journal_open(&j, 0) >= 0);
@@ -23,6 +24,8 @@ int main(int argc, char *argv[]) {
assert_se(sd_journal_add_match(j, "", 0) < 0);
assert_se(sd_journal_add_match(j, "=", 0) < 0);
assert_se(sd_journal_add_match(j, "=xxxxx", 0) < 0);
+ assert_se(sd_journal_add_match(j, (uint8_t[4]){'A', '=', '\1', '\2'}, 4) >= 0);
+ assert_se(sd_journal_add_match(j, (uint8_t[5]){'B', '=', 'C', '\0', 'D'}, 5) >= 0);
assert_se(sd_journal_add_match(j, "HALLO=WALDO", 0) >= 0);
assert_se(sd_journal_add_match(j, "QUUX=mmmm", 0) >= 0);
assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0);
@@ -53,7 +56,7 @@ int main(int argc, char *argv[]) {
printf("resulting match expression is: %s\n", t);
- assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO))))"));
+ assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO) AND B=C\\000D AND A=\\001\\002)))"));
return 0;
}
diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c
index ae35c91eff..226c30f80a 100644
--- a/src/journal/test-journal-stream.c
+++ b/src/journal/test-journal-stream.c
@@ -12,6 +12,7 @@
#include "macro.h"
#include "parse-util.h"
#include "rm-rf.h"
+#include "tests.h"
#include "util.h"
#define N_ENTRIES 200
@@ -68,9 +69,9 @@ int main(int argc, char *argv[]) {
/* journal_file_open requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("/etc/machine-id not found");
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c
index 9ba86f6c8a..45be7e5bc4 100644
--- a/src/journal/test-journal-syslog.c
+++ b/src/journal/test-journal-syslog.c
@@ -4,9 +4,10 @@
#include "journald-syslog.h"
#include "macro.h"
#include "string-util.h"
+#include "syslog-util.h"
-static void test_syslog_parse_identifier(const char* str,
- const char *ident, const char*pid, int ret) {
+static void test_syslog_parse_identifier(const char *str,
+ const char *ident, const char *pid, const char *rest, int ret) {
const char *buf = str;
_cleanup_free_ char *ident2 = NULL, *pid2 = NULL;
int ret2;
@@ -16,12 +17,43 @@ static void test_syslog_parse_identifier(const char* str,
assert_se(ret == ret2);
assert_se(ident == ident2 || streq_ptr(ident, ident2));
assert_se(pid == pid2 || streq_ptr(pid, pid2));
+ assert_se(streq(buf, rest));
+}
+
+static void test_syslog_parse_priority(const char *str, int priority, int ret) {
+ const char *buf = str;
+ int priority2 = 0, ret2;
+
+ ret2 = syslog_parse_priority(&buf, &priority2, false);
+
+ assert_se(ret == ret2);
+ if (ret2 == 1)
+ assert_se(priority == priority2);
}
int main(void) {
- test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", 11);
- test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 6);
- test_syslog_parse_identifier("pidu xxx", NULL, NULL, 0);
+ test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", "xxx", 11);
+ test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, "xxx", 6);
+ test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, " xxx", 6);
+ test_syslog_parse_identifier("pidu xxx", NULL, NULL, "pidu xxx", 0);
+ test_syslog_parse_identifier(" pidu xxx", NULL, NULL, " pidu xxx", 0);
+ test_syslog_parse_identifier("", NULL, NULL, "", 0);
+ test_syslog_parse_identifier(" ", NULL, NULL, " ", 0);
+ test_syslog_parse_identifier(":", "", NULL, "", 1);
+ test_syslog_parse_identifier(": ", "", NULL, " ", 2);
+ test_syslog_parse_identifier(" :", "", NULL, "", 2);
+ test_syslog_parse_identifier(" pidu:", "pidu", NULL, "", 8);
+ test_syslog_parse_identifier("pidu:", "pidu", NULL, "", 5);
+ test_syslog_parse_identifier("pidu: ", "pidu", NULL, "", 6);
+ test_syslog_parse_identifier("pidu : ", NULL, NULL, "pidu : ", 0);
+
+ test_syslog_parse_priority("<>", 0, 0);
+ test_syslog_parse_priority("<>aaa", 0, 0);
+ test_syslog_parse_priority("<aaaa>", 0, 0);
+ test_syslog_parse_priority("<aaaa>aaa", 0, 0);
+ test_syslog_parse_priority(" <aaaa>", 0, 0);
+ test_syslog_parse_priority(" <aaaa>aaa", 0, 0);
+ /* TODO: add test cases of valid priorities */
return 0;
}
diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c
index c1c3a82c42..c4fa41e076 100644
--- a/src/journal/test-journal-verify.c
+++ b/src/journal/test-journal-verify.c
@@ -5,11 +5,13 @@
#include <unistd.h>
#include "fd-util.h"
+#include "io-util.h"
#include "journal-file.h"
#include "journal-verify.h"
#include "log.h"
#include "rm-rf.h"
#include "terminal-util.h"
+#include "tests.h"
#include "util.h"
#define N_ENTRIES 6000
@@ -62,9 +64,9 @@ int main(int argc, char *argv[]) {
/* journal_file_open requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("/etc/machine-id not found");
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
@@ -82,8 +84,7 @@ int main(int argc, char *argv[]) {
assert_se(asprintf(&test, "RANDOM=%lu", random() % RANDOM_RANGE));
- iovec.iov_base = (void*) test;
- iovec.iov_len = strlen(test);
+ iovec = IOVEC_MAKE_STRING(test);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c
index 69bdff6760..0795e0da0a 100644
--- a/src/journal/test-journal.c
+++ b/src/journal/test-journal.c
@@ -3,11 +3,13 @@
#include <fcntl.h>
#include <unistd.h>
+#include "io-util.h"
#include "journal-authenticate.h"
#include "journal-file.h"
#include "journal-vacuum.h"
#include "log.h"
#include "rm-rf.h"
+#include "tests.h"
static bool arg_keep = false;
@@ -21,7 +23,7 @@ static void test_non_empty(void) {
sd_id128_t fake_boot_id;
char t[] = "/tmp/journal-XXXXXX";
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
@@ -31,16 +33,13 @@ static void test_non_empty(void) {
assert_se(dual_timestamp_get(&ts));
assert_se(sd_id128_randomize(&fake_boot_id) == 0);
- iovec.iov_base = (void*) test;
- iovec.iov_len = strlen(test);
+ iovec = IOVEC_MAKE_STRING(test);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
- iovec.iov_base = (void*) test2;
- iovec.iov_len = strlen(test2);
+ iovec = IOVEC_MAKE_STRING(test2);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
- iovec.iov_base = (void*) test;
- iovec.iov_len = strlen(test);
+ iovec = IOVEC_MAKE_STRING(test);
assert_se(journal_file_append_entry(f, &ts, &fake_boot_id, &iovec, 1, NULL, NULL, NULL) == 0);
#if HAVE_GCRYPT
@@ -112,7 +111,7 @@ static void test_empty(void) {
JournalFile *f1, *f2, *f3, *f4;
char t[] = "/tmp/journal-XXXXXX";
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
@@ -164,7 +163,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
assert_se(data_size <= sizeof(data));
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
@@ -173,8 +172,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
dual_timestamp_get(&ts);
- iovec.iov_base = (void*) data;
- iovec.iov_len = data_size;
+ iovec = IOVEC_MAKE(data, data_size);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0);
#if HAVE_GCRYPT
@@ -238,9 +236,11 @@ static void test_min_compress_size(void) {
int main(int argc, char *argv[]) {
arg_keep = argc > 1;
+ test_setup_logging(LOG_INFO);
+
/* journal_file_open requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("/etc/machine-id not found");
test_non_empty();
test_empty();
diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c
index 5c55b35f57..8f755efdde 100644
--- a/src/journal/test-mmap-cache.c
+++ b/src/journal/test-mmap-cache.c
@@ -6,9 +6,9 @@
#include <unistd.h>
#include "fd-util.h"
-#include "fileio.h"
#include "macro.h"
#include "mmap-cache.h"
+#include "tmpfile-util.h"
#include "util.h"
int main(int argc, char *argv[]) {
diff --git a/src/kernel-install/50-depmod.install b/src/kernel-install/50-depmod.install
index 56925c8a5d..88f550a486 100644
--- a/src/kernel-install/50-depmod.install
+++ b/src/kernel-install/50-depmod.install
@@ -6,6 +6,7 @@
case "$1" in
add)
+ [[ -d /lib/modules/"$2"/kernel ]] || exit 0
exec depmod -a "$2"
;;
remove)
diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install
index a271cdb8a0..39ec8a69c6 100644
--- a/src/kernel-install/90-loaderentry.install
+++ b/src/kernel-install/90-loaderentry.install
@@ -19,10 +19,11 @@ MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID
BOOT_DIR="/$MACHINE_ID/$KERNEL_VERSION"
BOOT_ROOT=${BOOT_DIR_ABS%$BOOT_DIR}
-LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"
if [[ $COMMAND == remove ]]; then
- exec rm -f "$LOADER_ENTRY"
+ rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"
+ rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+"*".conf"
+ exit 0
fi
if ! [[ $COMMAND == add ]]; then
@@ -63,6 +64,17 @@ if ! [[ ${BOOT_OPTIONS[*]} ]]; then
exit 1
fi
+if [[ -f /etc/kernel/tries ]]; then
+ read -r TRIES </etc/kernel/tries
+ if ! [[ "$TRIES" =~ ^[0-9]+$ ]] ; then
+ echo "/etc/kernel/tries does not contain an integer." >&2
+ exit 1
+ fi
+ LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+$TRIES.conf"
+else
+ LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"
+fi
+
cp "$KERNEL_IMAGE" "$BOOT_DIR_ABS/linux" &&
chown root:root "$BOOT_DIR_ABS/linux" &&
chmod 0644 "$BOOT_DIR_ABS/linux" || {
diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install
index f9d628a8eb..732d584bbe 100644
--- a/src/kernel-install/kernel-install
+++ b/src/kernel-install/kernel-install
@@ -5,7 +5,6 @@
#
# This file is part of systemd.
#
-# Copyright © 2013 Harald Hoyer
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c
index 91c485c6c2..07496adaa3 100644
--- a/src/libsystemd-network/dhcp-identifier.c
+++ b/src/libsystemd-network/dhcp-identifier.c
@@ -1,6 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include "libudev.h"
+#include <linux/if_infiniband.h>
+#include <net/if_arp.h>
+
+#include "sd-device.h"
#include "sd-id128.h"
#include "dhcp-identifier.h"
@@ -8,19 +11,26 @@
#include "network-internal.h"
#include "siphash24.h"
#include "sparse-endian.h"
-#include "udev-util.h"
#include "virt.h"
-#define SYSTEMD_PEN 43793
-#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
+#define SYSTEMD_PEN 43793
+#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
+#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
+#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
-int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) {
+int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) {
struct duid d;
assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
if (duid_len > MAX_DUID_LEN)
return -EINVAL;
+ if (!strict) {
+ /* Strict validation is not requested. We only ensure that the
+ * DUID is not too long. */
+ return 0;
+ }
+
switch (duid_type) {
case DUID_TYPE_LLT:
if (duid_len <= sizeof(d.llt))
@@ -45,6 +55,56 @@ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) {
return 0;
}
+int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
+ uint16_t time_from_2000y;
+
+ assert(duid);
+ assert(len);
+ assert(addr);
+
+ if (arp_type == ARPHRD_ETHER)
+ assert_return(addr_len == ETH_ALEN, -EINVAL);
+ else if (arp_type == ARPHRD_INFINIBAND)
+ assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+ else
+ return -EINVAL;
+
+ if (t < USEC_2000)
+ time_from_2000y = 0;
+ else
+ time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
+
+ unaligned_write_be16(&duid->type, DUID_TYPE_LLT);
+ unaligned_write_be16(&duid->llt.htype, arp_type);
+ unaligned_write_be32(&duid->llt.time, time_from_2000y);
+ memcpy(duid->llt.haddr, addr, addr_len);
+
+ *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len;
+
+ return 0;
+}
+
+int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
+ assert(duid);
+ assert(len);
+ assert(addr);
+
+ if (arp_type == ARPHRD_ETHER)
+ assert_return(addr_len == ETH_ALEN, -EINVAL);
+ else if (arp_type == ARPHRD_INFINIBAND)
+ assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+ else
+ return -EINVAL;
+
+ unaligned_write_be16(&duid->type, DUID_TYPE_LL);
+ unaligned_write_be16(&duid->ll.htype, arp_type);
+ memcpy(duid->ll.haddr, addr, addr_len);
+
+ *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len;
+
+ return 0;
+}
+
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
sd_id128_t machine_id;
uint64_t hash;
@@ -54,8 +114,13 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
assert(len);
r = sd_id128_get_machine(&machine_id);
- if (r < 0)
+ if (r < 0) {
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
+#else
return r;
+#endif
+ }
unaligned_write_be16(&duid->type, DUID_TYPE_EN);
unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
@@ -63,33 +128,56 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
*len = sizeof(duid->type) + sizeof(duid->en);
/* a bit of snake-oil perhaps, but no need to expose the machine-id
- directly; duid->en.id might not be aligned, so we need to copy */
+ * directly; duid->en.id might not be aligned, so we need to copy */
hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
memcpy(duid->en.id, &hash, sizeof(duid->en.id));
return 0;
}
-int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) {
- /* name is a pointer to memory in the udev_device struct, so must
- have the same scope */
- _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
+int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) {
+ sd_id128_t machine_id;
+ int r;
+
+ assert(duid);
+ assert(len);
+
+ r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
+ if (r < 0)
+ return r;
+
+ unaligned_write_be16(&duid->type, DUID_TYPE_UUID);
+ memcpy(&duid->raw.data, &machine_id, sizeof(machine_id));
+
+ *len = sizeof(duid->type) + sizeof(machine_id);
+
+ return 0;
+}
+
+int dhcp_identifier_set_iaid(
+ int ifindex,
+ const uint8_t *mac,
+ size_t mac_len,
+ bool legacy_unstable_byteorder,
+ void *_id) {
+ /* name is a pointer to memory in the sd_device struct, so must
+ * have the same scope */
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
const char *name = NULL;
uint64_t id;
+ uint32_t id32;
if (detect_container() <= 0) {
/* not in a container, udev will be around */
- _cleanup_(udev_unrefp) struct udev *udev;
char ifindex_str[2 + DECIMAL_STR_MAX(int)];
-
- udev = udev_new();
- if (!udev)
- return -ENOMEM;
+ int r;
sprintf(ifindex_str, "n%d", ifindex);
- device = udev_device_new_from_device_id(udev, ifindex_str);
- if (device) {
- if (udev_device_get_is_initialized(device) <= 0)
+ if (sd_device_new_from_device_id(&device, ifindex_str) >= 0) {
+ r = sd_device_get_is_initialized(device);
+ if (r < 0)
+ return r;
+ if (r == 0)
/* not yet ready */
return -EBUSY;
@@ -103,10 +191,18 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_i
/* fall back to MAC address if no predictable name available */
id = siphash24(mac, mac_len, HASH_KEY.bytes);
- id = htole64(id);
+ id32 = (id & 0xffffffff) ^ (id >> 32);
- /* fold into 32 bits */
- unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32));
+ if (legacy_unstable_byteorder)
+ /* for historical reasons (a bug), the bits were swapped and thus
+ * the result was endianness dependant. Preserve that behavior. */
+ id32 = __bswap_32(id32);
+ else
+ /* the fixed behavior returns a stable byte order. Since LE is expected
+ * to be more common, swap the bytes on LE to give the same as legacy
+ * behavior. */
+ id32 = be32toh(id32);
+ unaligned_write_ne32(_id, id32);
return 0;
}
diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h
index b92c580d40..b3115125d9 100644
--- a/src/libsystemd-network/dhcp-identifier.h
+++ b/src/libsystemd-network/dhcp-identifier.h
@@ -1,11 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-id128.h"
#include "macro.h"
#include "sparse-endian.h"
+#include "time-util.h"
#include "unaligned.h"
typedef enum DUIDType {
@@ -28,18 +28,18 @@ struct duid {
union {
struct {
/* DUID_TYPE_LLT */
- uint16_t htype;
- uint32_t time;
+ be16_t htype;
+ be32_t time;
uint8_t haddr[0];
} _packed_ llt;
struct {
/* DUID_TYPE_EN */
- uint32_t pen;
+ be32_t pen;
uint8_t id[8];
} _packed_ en;
struct {
/* DUID_TYPE_LL */
- int16_t htype;
+ be16_t htype;
uint8_t haddr[0];
} _packed_ ll;
struct {
@@ -52,6 +52,9 @@ struct duid {
};
} _packed_;
-int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len);
+int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict);
+int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len);
+int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len);
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
-int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id);
+int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len);
+int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, void *_id);
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
index 77638338f2..0e5b4147a9 100644
--- a/src/libsystemd-network/dhcp-network.c
+++ b/src/libsystemd-network/dhcp-network.c
@@ -78,7 +78,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
.filter = filter
};
_cleanup_close_ int s = -1;
- int r, on = 1;
+ int r;
assert(ifindex > 0);
assert(link);
@@ -87,9 +87,9 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
if (s < 0)
return -errno;
- r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on));
+ r = setsockopt_int(s, SOL_PACKET, PACKET_AUXDATA, true);
if (r < 0)
- return -errno;
+ return r;
r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
if (r < 0)
@@ -126,8 +126,6 @@ int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
const uint8_t *bcast_addr = NULL;
uint8_t dhcp_hlen = 0;
- assert_return(mac_addr_len > 0, -EINVAL);
-
if (arp_type == ARPHRD_ETHER) {
assert_return(mac_addr_len == ETH_ALEN, -EINVAL);
memcpy(&eth_mac, mac_addr, ETH_ALEN);
@@ -151,19 +149,19 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
};
_cleanup_close_ int s = -1;
char ifname[IF_NAMESIZE] = "";
- int r, on = 1, tos = IPTOS_CLASS_CS6;
+ int r;
s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (s < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+ r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6);
if (r < 0)
- return -errno;
+ return r;
- r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
- return -errno;
+ return r;
if (ifindex > 0) {
if (if_indextoname(ifindex, ifname) == 0)
@@ -175,18 +173,18 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
}
if (address == INADDR_ANY) {
- r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
+ r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
if (r < 0)
- return -errno;
+ return r;
- r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
+ r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true);
if (r < 0)
- return -errno;
+ return r;
} else {
- r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on));
+ r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true);
if (r < 0)
- return -errno;
+ return r;
}
r = bind(s, &src.sa, sizeof(src.in));
diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c
index d29cd06cb1..ad5f8e267a 100644
--- a/src/libsystemd-network/dhcp-packet.c
+++ b/src/libsystemd-network/dhcp-packet.c
@@ -106,70 +106,62 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui
/* IP */
- if (packet->ip.version != IPVERSION) {
- log_debug("ignoring packet: not IPv4");
- return -EINVAL;
- }
+ if (packet->ip.version != IPVERSION)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "ignoring packet: not IPv4");
- if (packet->ip.ihl < 5) {
- log_debug("ignoring packet: IPv4 IHL (%u words) invalid",
- packet->ip.ihl);
- return -EINVAL;
- }
+ if (packet->ip.ihl < 5)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "ignoring packet: IPv4 IHL (%u words) invalid",
+ packet->ip.ihl);
hdrlen = packet->ip.ihl * 4;
- if (hdrlen < 20) {
- log_debug("ignoring packet: IPv4 IHL (%zu bytes) "
- "smaller than minimum (20 bytes)", hdrlen);
- return -EINVAL;
- }
-
- if (len < hdrlen) {
- log_debug("ignoring packet: packet (%zu bytes) "
- "smaller than expected (%zu) by IP header", len,
- hdrlen);
- return -EINVAL;
- }
+ if (hdrlen < 20)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "ignoring packet: IPv4 IHL (%zu bytes) "
+ "smaller than minimum (20 bytes)",
+ hdrlen);
+
+ if (len < hdrlen)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "ignoring packet: packet (%zu bytes) "
+ "smaller than expected (%zu) by IP header",
+ len, hdrlen);
/* UDP */
- if (packet->ip.protocol != IPPROTO_UDP) {
- log_debug("ignoring packet: not UDP");
- return -EINVAL;
- }
+ if (packet->ip.protocol != IPPROTO_UDP)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "ignoring packet: not UDP");
- if (len < hdrlen + be16toh(packet->udp.len)) {
- log_debug("ignoring packet: packet (%zu bytes) "
- "smaller than expected (%zu) by UDP header", len,
- hdrlen + be16toh(packet->udp.len));
- return -EINVAL;
- }
+ if (len < hdrlen + be16toh(packet->udp.len))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "ignoring packet: packet (%zu bytes) "
+ "smaller than expected (%zu) by UDP header",
+ len, hdrlen + be16toh(packet->udp.len));
- if (be16toh(packet->udp.dest) != port) {
- log_debug("ignoring packet: to port %u, which "
- "is not the DHCP client port (%u)",
- be16toh(packet->udp.dest), port);
- return -EINVAL;
- }
+ if (be16toh(packet->udp.dest) != port)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "ignoring packet: to port %u, which "
+ "is not the DHCP client port (%u)",
+ be16toh(packet->udp.dest), port);
/* checksums - computing these is relatively expensive, so only do it
if all the other checks have passed
*/
- if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) {
- log_debug("ignoring packet: invalid IP checksum");
- return -EINVAL;
- }
+ if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "ignoring packet: invalid IP checksum");
if (checksum && packet->udp.check) {
packet->ip.check = packet->udp.len;
packet->ip.ttl = 0;
if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl,
- be16toh(packet->udp.len) + 12)) {
- log_debug("ignoring packet: invalid UDP checksum");
- return -EINVAL;
- }
+ be16toh(packet->udp.len) + 12))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "ignoring packet: invalid UDP checksum");
}
return 0;
diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h
index 6355128ddb..8a7c5bc28f 100644
--- a/src/libsystemd-network/dhcp-server-internal.h
+++ b/src/libsystemd-network/dhcp-server-internal.h
@@ -78,5 +78,5 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
DHCPRequest *req, DHCPPacket *packet,
int type, size_t optoffset);
-void client_id_hash_func(const void *p, struct siphash *state);
-int client_id_compare_func(const void *_a, const void *_b);
+void client_id_hash_func(const DHCPClientId *p, struct siphash *state);
+int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b);
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index f1cbd6a4f1..157fc0aadd 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -73,8 +73,6 @@ struct DHCP6IA {
struct ia_pd ia_pd;
struct ia_ta ia_ta;
};
- sd_event_source *timeout_t1;
- sd_event_source *timeout_t2;
LIST_HEAD(DHCP6Address, addresses);
};
@@ -86,12 +84,12 @@ typedef struct DHCP6IA DHCP6IA;
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval);
-int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
-int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd);
+int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
+int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd);
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue);
-int dhcp6_option_parse_status(DHCP6Option *option);
+int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
struct in6_addr **addrs, size_t count,
diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h
index ff0b0f00ce..e004f48b4e 100644
--- a/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libsystemd-network/dhcp6-lease-internal.h
@@ -37,7 +37,6 @@ struct sd_dhcp6_lease {
size_t ntp_fqdn_count;
};
-int dhcp6_lease_clear_timers(DHCP6IA *ia);
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
@@ -50,6 +49,7 @@ int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease);
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
+int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c
index 78cd383669..580f43ba40 100644
--- a/src/libsystemd-network/dhcp6-network.c
+++ b/src/libsystemd-network/dhcp6-network.c
@@ -25,7 +25,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
.in6.sin6_scope_id = index,
};
_cleanup_close_ int s = -1;
- int r, off = 0, on = 1;
+ int r;
assert(index > 0);
assert(local_address);
@@ -36,17 +36,17 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
if (s < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
if (r < 0)
- return -errno;
+ return r;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off));
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false);
if (r < 0)
- return -errno;
+ return r;
- r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
- return -errno;
+ return r;
r = bind(s, &src.sa, sizeof(src.in6));
if (r < 0)
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index 18196b1257..a2aac9a793 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -37,9 +37,9 @@ typedef struct DHCP6PDPrefixOption {
uint8_t options[];
} _packed_ DHCP6PDPrefixOption;
-#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
-#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
-#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
+#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
+#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
+#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
size_t optlen) {
@@ -49,14 +49,14 @@ static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
assert_return(*buf, -EINVAL);
assert_return(buflen, -EINVAL);
- if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option))
+ if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
return -ENOBUFS;
option->code = htobe16(optcode);
option->len = htobe16(optlen);
- *buf += sizeof(DHCP6Option);
- *buflen -= sizeof(DHCP6Option);
+ *buf += offsetof(DHCP6Option, data);
+ *buflen -= offsetof(DHCP6Option, data);
return 0;
}
@@ -79,14 +79,17 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
return 0;
}
-int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
+int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
uint16_t len;
uint8_t *ia_hdr;
size_t iaid_offset, ia_buflen, ia_addrlen = 0;
DHCP6Address *addr;
int r;
- assert_return(buf && *buf && buflen && ia, -EINVAL);
+ assert_return(buf, -EINVAL);
+ assert_return(*buf, -EINVAL);
+ assert_return(buflen, -EINVAL);
+ assert_return(ia, -EINVAL);
switch (ia->type) {
case SD_DHCP6_OPTION_IA_NA:
@@ -103,14 +106,14 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
return -EINVAL;
}
- if (*buflen < len)
+ if (*buflen < offsetof(DHCP6Option, data) + len)
return -ENOBUFS;
ia_hdr = *buf;
ia_buflen = *buflen;
- *buf += sizeof(DHCP6Option);
- *buflen -= sizeof(DHCP6Option);
+ *buf += offsetof(DHCP6Option, data);
+ *buflen -= offsetof(DHCP6Option, data);
memcpy(*buf, (char*) ia + iaid_offset, len);
@@ -128,7 +131,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
*buf += sizeof(addr->iaaddr);
*buflen -= sizeof(addr->iaaddr);
- ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr);
+ ia_addrlen += offsetof(DHCP6Option, data) + sizeof(addr->iaaddr);
}
r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
@@ -165,7 +168,7 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
return r;
}
-int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) {
+int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd) {
DHCP6Option *option = (DHCP6Option *)buf;
size_t i = sizeof(*option) + sizeof(pd->ia_pd);
DHCP6Address *prefix;
@@ -210,7 +213,7 @@ static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, si
assert_return(optcode, -EINVAL);
assert_return(optlen, -EINVAL);
- if (*buflen < sizeof(DHCP6Option))
+ if (*buflen < offsetof(DHCP6Option, data))
return -ENOMSG;
len = be16toh(option->len);
@@ -247,10 +250,11 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
return 0;
}
-int dhcp6_option_parse_status(DHCP6Option *option) {
+int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
- if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*statusopt))
+ if (len < sizeof(DHCP6StatusOption) ||
+ be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption))
return -ENOBUFS;
return be16toh(statusopt->status);
@@ -263,7 +267,7 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
uint32_t lt_valid, lt_pref;
int r;
- if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option))
+ if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option))
return -ENOBUFS;
lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
@@ -276,8 +280,8 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
return 0;
}
- if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) {
- r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options);
+ if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) {
+ r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option));
if (r != 0)
return r < 0 ? r: 0;
}
@@ -303,7 +307,7 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
uint32_t lt_valid, lt_pref;
int r;
- if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option))
+ if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option))
return -ENOBUFS;
lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
@@ -316,8 +320,8 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
return 0;
}
- if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) {
- r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options);
+ if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) {
+ r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option));
if (r != 0)
return r < 0 ? r: 0;
}
@@ -353,10 +357,8 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
switch (iatype) {
case SD_DHCP6_OPTION_IA_NA:
- if (len < DHCP6_OPTION_IA_NA_LEN) {
- r = -ENOBUFS;
- goto error;
- }
+ if (len < DHCP6_OPTION_IA_NA_LEN)
+ return -ENOBUFS;
iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
@@ -367,18 +369,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
lt_t1, lt_t2);
- r = -EINVAL;
- goto error;
+ return -EINVAL;
}
break;
case SD_DHCP6_OPTION_IA_PD:
- if (len < sizeof(ia->ia_pd)) {
- r = -ENOBUFS;
- goto error;
- }
+ if (len < sizeof(ia->ia_pd))
+ return -ENOBUFS;
iaaddr_offset = sizeof(ia->ia_pd);
memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
@@ -389,17 +388,14 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
lt_t1, lt_t2);
- r = -EINVAL;
- goto error;
+ return -EINVAL;
}
break;
case SD_DHCP6_OPTION_IA_TA:
- if (len < DHCP6_OPTION_IA_TA_LEN) {
- r = -ENOBUFS;
- goto error;
- }
+ if (len < DHCP6_OPTION_IA_TA_LEN)
+ return -ENOBUFS;
iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
@@ -407,8 +403,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
break;
default:
- r = -ENOMSG;
- goto error;
+ return -ENOMSG;
}
ia->type = iatype;
@@ -417,10 +412,8 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
while (i < len) {
DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
- if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) {
- r = -ENOBUFS;
- goto error;
- }
+ if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len))
+ return -ENOBUFS;
opt = be16toh(option->code);
optlen = be16toh(option->len);
@@ -430,13 +423,12 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
log_dhcp6_client(client, "IA Address option not in IA NA or TA option");
- r = -EINVAL;
- goto error;
+ return -EINVAL;
}
r = dhcp6_option_parse_address(option, ia, &lt_valid);
if (r < 0)
- goto error;
+ return r;
if (lt_valid < lt_min)
lt_min = lt_valid;
@@ -447,13 +439,12 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) {
log_dhcp6_client(client, "IA PD Prefix option not in IA PD option");
- r = -EINVAL;
- goto error;
+ return -EINVAL;
}
r = dhcp6_option_parse_pdprefix(option, ia, &lt_valid);
if (r < 0)
- goto error;
+ return r;
if (lt_valid < lt_min)
lt_min = lt_valid;
@@ -462,15 +453,14 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
case SD_DHCP6_OPTION_STATUS_CODE:
- status = dhcp6_option_parse_status(option);
- if (status) {
+ status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
+ if (status < 0)
+ return status;
+ if (status > 0) {
log_dhcp6_client(client, "IA status %d",
status);
- dhcp6_lease_free_ia(ia);
-
- r = -EINVAL;
- goto error;
+ return -EINVAL;
}
break;
@@ -514,8 +504,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
break;
}
-error:
- return r;
+ return 0;
}
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
@@ -550,6 +539,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
bool first = true;
for (;;) {
+ const char *label;
uint8_t c;
c = optval[pos++];
@@ -557,47 +547,41 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
if (c == 0)
/* End of name */
break;
- else if (c <= 63) {
- const char *label;
-
- /* Literal label */
- label = (const char *)&optval[pos];
- pos += c;
- if (pos > optlen)
- return -EMSGSIZE;
-
- if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (first)
- first = false;
- else
- ret[n++] = '.';
-
- r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
- if (r < 0)
- goto fail;
-
- n += r;
- continue;
- } else {
- r = -EBADMSG;
- goto fail;
- }
- }
+ if (c > 63)
+ return -EBADMSG;
+
+ /* Literal label */
+ label = (const char *)&optval[pos];
+ pos += c;
+ if (pos >= optlen)
+ return -EMSGSIZE;
+
+ if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
+ return -ENOMEM;
+
+ if (first)
+ first = false;
+ else
+ ret[n++] = '.';
- if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
- r = -ENOMEM;
- goto fail;
+ r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ return r;
+
+ n += r;
}
+ if (n == 0)
+ continue;
+
+ if (!GREEDY_REALLOC(ret, allocated, n + 1))
+ return -ENOMEM;
+
ret[n] = 0;
r = strv_extend(&names, ret);
if (r < 0)
- goto fail;
+ return r;
idx++;
}
@@ -605,7 +589,4 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
*str_arr = TAKE_PTR(names);
return idx;
-
-fail:
- return r;
}
diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c
index 736df222f0..e535b12cda 100644
--- a/src/libsystemd-network/icmp6-util.c
+++ b/src/libsystemd-network/icmp6-util.c
@@ -17,8 +17,9 @@
#include "fd-util.h"
#include "icmp6-util.h"
-#include "socket-util.h"
#include "in-addr-util.h"
+#include "io-util.h"
+#include "socket-util.h"
#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
@@ -33,7 +34,6 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter,
int index = mreq->ipv6mr_interface;
_cleanup_close_ int s = -1;
char ifname[IF_NAMESIZE] = "";
- static const int zero = 0, one = 1, hops = 255;
int r;
s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
@@ -52,29 +52,29 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter,
IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
Empirical experiments indicates otherwise and therefore an
IPV6_MULTICAST_IF socket option is used here instead */
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index));
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, index);
if (r < 0)
- return -errno;
+ return r;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero));
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false);
if (r < 0)
- return -errno;
+ return r;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops));
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
if (r < 0)
- return -errno;
+ return r;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, sizeof(hops));
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
if (r < 0)
- return -errno;
+ return r;
- r = setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
+ r = setsockopt_int(s, SOL_IPV6, IPV6_RECVHOPLIMIT, true);
if (r < 0)
- return -errno;
+ return r;
- r = setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
+ r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true);
if (r < 0)
- return -errno;
+ return r;
if (if_indextoname(index, ifname) == 0)
return -errno;
@@ -170,16 +170,11 @@ int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
struct cmsghdr *cmsg;
ssize_t len;
- iov.iov_base = buffer;
- iov.iov_len = size;
+ iov = IOVEC_MAKE(buffer, size);
len = recvmsg(fd, &msg, MSG_DONTWAIT);
- if (len < 0) {
- if (IN_SET(errno, EAGAIN, EINTR))
- return 0;
-
+ if (len < 0)
return -errno;
- }
if ((size_t) len != size)
return -EINVAL;
diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h
index 7c10a67a35..88b54933c3 100644
--- a/src/libsystemd-network/lldp-internal.h
+++ b/src/libsystemd-network/lldp-internal.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-event.h"
#include "sd-lldp.h"
@@ -35,3 +34,6 @@ struct sd_lldp {
#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)
+
+const char* lldp_event_to_string(sd_lldp_event e) _const_;
+sd_lldp_event lldp_event_from_string(const char *s) _pure_;
diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c
index 5dcb051373..43fc8e03c0 100644
--- a/src/libsystemd-network/lldp-neighbor.c
+++ b/src/libsystemd-network/lldp-neighbor.c
@@ -7,58 +7,29 @@
#include "in-addr-util.h"
#include "lldp-internal.h"
#include "lldp-neighbor.h"
+#include "missing.h"
#include "unaligned.h"
+#include "util.h"
-static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) {
- const LLDPNeighborID *id = p;
-
+static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
siphash24_compress(id->chassis_id, id->chassis_id_size, state);
siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
siphash24_compress(id->port_id, id->port_id_size, state);
siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
}
-static int lldp_neighbor_id_compare_func(const void *a, const void *b) {
- const LLDPNeighborID *x = a, *y = b;
- int r;
-
- r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size));
- if (r != 0)
- return r;
-
- if (x->chassis_id_size < y->chassis_id_size)
- return -1;
-
- if (x->chassis_id_size > y->chassis_id_size)
- return 1;
-
- r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size));
- if (r != 0)
- return r;
-
- if (x->port_id_size < y->port_id_size)
- return -1;
- if (x->port_id_size > y->port_id_size)
- return 1;
-
- return 0;
+int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
+ return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
+ ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
}
-const struct hash_ops lldp_neighbor_id_hash_ops = {
- .hash = lldp_neighbor_id_hash_func,
- .compare = lldp_neighbor_id_compare_func
-};
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
+ sd_lldp_neighbor, lldp_neighbor_unlink);
int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
const sd_lldp_neighbor *x = a, *y = b;
- if (x->until < y->until)
- return -1;
-
- if (x->until > y->until)
- return 1;
-
- return 0;
+ return CMP(x->until, y->until);
}
_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
@@ -111,7 +82,12 @@ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
if (!n->lldp)
return NULL;
- assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n);
+ /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
+ * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
+ * ourselves from the hashtable and sometimes are called after we already are de-registered. */
+
+ (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
+
assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
n->lldp = NULL;
diff --git a/src/libsystemd-network/lldp-neighbor.h b/src/libsystemd-network/lldp-neighbor.h
index 494bc51760..62dbff42ca 100644
--- a/src/libsystemd-network/lldp-neighbor.h
+++ b/src/libsystemd-network/lldp-neighbor.h
@@ -80,7 +80,8 @@ static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
}
-extern const struct hash_ops lldp_neighbor_id_hash_ops;
+extern const struct hash_ops lldp_neighbor_hash_ops;
+int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y);
int lldp_neighbor_prioq_compare_func(const void *a, const void *b);
sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n);
diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c
index a4020d1e19..870584c0db 100644
--- a/src/libsystemd-network/lldp-network.c
+++ b/src/libsystemd-network/lldp-network.c
@@ -5,6 +5,7 @@
#include "fd-util.h"
#include "lldp-network.h"
+#include "missing.h"
#include "socket-util.h"
int lldp_network_bind_raw_socket(int ifindex) {
diff --git a/src/libsystemd-network/lldp-network.h b/src/libsystemd-network/lldp-network.h
index 914139793c..e4ed2898a5 100644
--- a/src/libsystemd-network/lldp-network.h
+++ b/src/libsystemd-network/lldp-network.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-event.h"
int lldp_network_bind_raw_socket(int ifindex);
diff --git a/src/libsystemd-network/ndisc-internal.h b/src/libsystemd-network/ndisc-internal.h
index fdabbc1b0e..0c04fea8e5 100644
--- a/src/libsystemd-network/ndisc-internal.h
+++ b/src/libsystemd-network/ndisc-internal.h
@@ -38,3 +38,6 @@ struct sd_ndisc {
#define log_ndisc_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "NDISC: " fmt, ##__VA_ARGS__)
#define log_ndisc(fmt, ...) log_ndisc_errno(0, fmt, ##__VA_ARGS__)
+
+const char* ndisc_event_to_string(sd_ndisc_event e) _const_;
+sd_ndisc_event ndisc_event_from_string(const char *s) _pure_;
diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c
index 25b693a458..6935311b9a 100644
--- a/src/libsystemd-network/ndisc-router.c
+++ b/src/libsystemd-network/ndisc-router.c
@@ -15,28 +15,7 @@
#include "ndisc-router.h"
#include "strv.h"
-_public_ sd_ndisc_router* sd_ndisc_router_ref(sd_ndisc_router *rt) {
- if (!rt)
- return NULL;
-
- assert(rt->n_ref > 0);
- rt->n_ref++;
-
- return rt;
-}
-
-_public_ sd_ndisc_router* sd_ndisc_router_unref(sd_ndisc_router *rt) {
- if (!rt)
- return NULL;
-
- assert(rt->n_ref > 0);
- rt->n_ref--;
-
- if (rt->n_ref > 0)
- return NULL;
-
- return mfree(rt);
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, mfree);
sd_ndisc_router *ndisc_router_new(size_t raw_size) {
sd_ndisc_router *rt;
@@ -189,7 +168,7 @@ int ndisc_router_parse(sd_ndisc_router *rt) {
if (has_mtu) {
log_ndisc("MTU option specified twice, ignoring.");
- continue;
+ break;
}
if (length != 8) {
@@ -230,7 +209,7 @@ int ndisc_router_parse(sd_ndisc_router *rt) {
if (has_flag_extension) {
log_ndisc("Flags extension option specified twice, ignoring.");
- continue;
+ break;
}
if (length < 1*8) {
@@ -697,7 +676,7 @@ _public_ int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret)
_cleanup_free_ char *normalized = NULL;
e[n] = 0;
- r = dns_name_normalize(e, &normalized);
+ r = dns_name_normalize(e, 0, &normalized);
if (r < 0)
return r;
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index 0849b44ee2..b3b134d650 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -23,24 +23,22 @@
#include "utf8.h"
#include "util.h"
-const char *net_get_name(struct udev_device *device) {
+const char *net_get_name(sd_device *device) {
const char *name, *field;
assert(device);
/* fetch some persistent data unique (on this machine) to this device */
- FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") {
- name = udev_device_get_property_value(device, field);
- if (name)
+ FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC")
+ if (sd_device_get_property_value(device, field, &name) >= 0)
return name;
- }
return NULL;
}
#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
-int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result) {
+int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
size_t l, sz = 0;
const char *name = NULL;
int r;
@@ -124,7 +122,7 @@ bool net_match_config(Set *match_mac,
if (match_arch && condition_test(match_arch) <= 0)
return false;
- if (match_mac && dev_mac && !set_contains(match_mac, dev_mac))
+ if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac)))
return false;
if (!net_condition_test_strv(match_paths, dev_path))
@@ -257,11 +255,10 @@ int config_parse_ifalias(const char *unit,
return 0;
}
- free(*s);
- if (*n)
- *s = TAKE_PTR(n);
+ if (isempty(n))
+ *s = mfree(*s);
else
- *s = NULL;
+ free_and_replace(*s, n);
return 0;
}
@@ -296,7 +293,7 @@ int config_parse_hwaddr(const char *unit,
return 0;
}
- *hwaddr = TAKE_PTR(n);
+ free_and_replace(*hwaddr, n);
return 0;
}
@@ -374,36 +371,6 @@ int config_parse_hwaddrs(const char *unit,
return 0;
}
-int config_parse_iaid(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 iaid;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = safe_atou32(rvalue, &iaid);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Unable to read IAID, ignoring assignment: %s", rvalue);
- return 0;
- }
-
- *((uint32_t *)data) = iaid;
-
- return 0;
-}
-
int config_parse_bridge_port_priority(
const char *unit,
const char *filename,
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index 883f34b95c..0c8da848c1 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -1,15 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <stdbool.h>
+#include "sd-device.h"
#include "sd-dhcp-lease.h"
#include "condition.h"
#include "conf-parser.h"
#include "set.h"
-#include "udev.h"
#define LINK_BRIDGE_PORT_PRIORITY_INVALID 128
#define LINK_BRIDGE_PORT_PRIORITY_MAX 63
@@ -36,11 +35,10 @@ CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs);
CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
CONFIG_PARSER_PROTOTYPE(config_parse_ifalias);
-CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority);
-int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result);
-const char *net_get_name(struct udev_device *device);
+int net_get_unique_predictable_data(sd_device *device, uint64_t *result);
+const char *net_get_name(sd_device *device);
void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size);
int deserialize_in_addrs(struct in_addr **addresses, const char *string);
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h
index b221c6f6c5..cd44352307 100644
--- a/src/libsystemd-network/radv-internal.h
+++ b/src/libsystemd-network/radv-internal.h
@@ -85,5 +85,4 @@ struct sd_radv_prefix {
#define log_radv_full(level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__)
#define log_radv_errno(error, fmt, ...) log_radv_full(LOG_DEBUG, error, fmt, ##__VA_ARGS__)
-#define log_radv_warning_errno(error, fmt, ...) log_radv_full(LOG_WARNING, error, fmt, ##__VA_ARGS__)
#define log_radv(fmt, ...) log_radv_errno(0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index ff434f8ce7..2d7ffd22ca 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -21,11 +21,13 @@
#include "dhcp-lease-internal.h"
#include "dhcp-protocol.h"
#include "dns-domain.h"
+#include "event-util.h"
#include "hostname-util.h"
+#include "io-util.h"
#include "random-util.h"
#include "string-util.h"
-#include "util.h"
#include "strv.h"
+#include "util.h"
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
@@ -86,7 +88,7 @@ struct sd_dhcp_client {
uint32_t mtu;
uint32_t xid;
usec_t start_time;
- unsigned int attempt;
+ unsigned attempt;
usec_t request_sent;
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
@@ -298,27 +300,22 @@ int sd_dhcp_client_set_client_id(
assert_return(data, -EINVAL);
assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
- switch (type) {
-
- case ARPHRD_ETHER:
- if (data_len != ETH_ALEN)
- return -EINVAL;
- break;
-
- case ARPHRD_INFINIBAND:
- if (data_len != INFINIBAND_ALEN)
- return -EINVAL;
- break;
-
- default:
- break;
- }
-
if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
client->client_id.type == type &&
memcmp(&client->client_id.raw.data, data, data_len) == 0)
return 0;
+ /* For hardware types, log debug message about unexpected data length.
+ *
+ * Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only
+ * last last 8 bytes of the address are stable and suitable to put into
+ * the client-id. The caller is advised to account for that. */
+ if ((type == ARPHRD_ETHER && data_len != ETH_ALEN) ||
+ (type == ARPHRD_INFINIBAND && data_len != 8))
+ log_dhcp_client(client, "Changing client ID to hardware type %u with "
+ "unexpected address length %zu",
+ type, data_len);
+
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
log_dhcp_client(client, "Changing client ID on running DHCP "
"client, restarting");
@@ -341,13 +338,15 @@ int sd_dhcp_client_set_client_id(
* without further modification. Otherwise, if duid_type is supported, DUID
* is set based on that type. Otherwise, an error is returned.
*/
-static int dhcp_client_set_iaid_duid(
+static int dhcp_client_set_iaid_duid_internal(
sd_dhcp_client *client,
+ bool iaid_append,
+ bool iaid_set,
uint32_t iaid,
- bool append_iaid,
uint16_t duid_type,
const void *duid,
- size_t duid_len) {
+ size_t duid_len,
+ usec_t llt_time) {
DHCP_CLIENT_DONT_DESTROY(client);
int r;
@@ -357,7 +356,7 @@ static int dhcp_client_set_iaid_duid(
assert_return(duid_len == 0 || duid != NULL, -EINVAL);
if (duid != NULL) {
- r = dhcp_validate_duid_len(duid_type, duid_len);
+ r = dhcp_validate_duid_len(duid_type, duid_len, true);
if (r < 0)
return r;
}
@@ -365,34 +364,60 @@ static int dhcp_client_set_iaid_duid(
zero(client->client_id);
client->client_id.type = 255;
- if (append_iaid) {
- /* If IAID is not configured, generate it. */
- if (iaid == 0) {
+ if (iaid_append) {
+ if (iaid_set)
+ client->client_id.ns.iaid = htobe32(iaid);
+ else {
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
client->mac_addr_len,
+ true,
&client->client_id.ns.iaid);
if (r < 0)
return r;
- } else
- client->client_id.ns.iaid = htobe32(iaid);
+ }
}
if (duid != NULL) {
client->client_id.ns.duid.type = htobe16(duid_type);
memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
len = sizeof(client->client_id.ns.duid.type) + duid_len;
- } else if (duid_type == DUID_TYPE_EN) {
- r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
- if (r < 0)
- return r;
} else
- return -EOPNOTSUPP;
+ switch (duid_type) {
+ case DUID_TYPE_LLT:
+ if (client->mac_addr_len == 0)
+ return -EOPNOTSUPP;
+
+ r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
+ if (r < 0)
+ return r;
+ break;
+ case DUID_TYPE_EN:
+ r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
+ if (r < 0)
+ return r;
+ break;
+ case DUID_TYPE_LL:
+ if (client->mac_addr_len == 0)
+ return -EOPNOTSUPP;
+
+ r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
+ if (r < 0)
+ return r;
+ break;
+ case DUID_TYPE_UUID:
+ r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
+ if (r < 0)
+ return r;
+ break;
+ default:
+ return -EINVAL;
+ }
client->client_id_len = sizeof(client->client_id.type) + len +
- (append_iaid ? sizeof(client->client_id.ns.iaid) : 0);
+ (iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
- log_dhcp_client(client, "Configured IAID+DUID, restarting.");
+ log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
sd_dhcp_client_start(client);
}
@@ -402,11 +427,20 @@ static int dhcp_client_set_iaid_duid(
int sd_dhcp_client_set_iaid_duid(
sd_dhcp_client *client,
+ bool iaid_set,
uint32_t iaid,
uint16_t duid_type,
const void *duid,
size_t duid_len) {
- return dhcp_client_set_iaid_duid(client, iaid, true, duid_type, duid, duid_len);
+ return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0);
+}
+
+int sd_dhcp_client_set_iaid_duid_llt(
+ sd_dhcp_client *client,
+ bool iaid_set,
+ uint32_t iaid,
+ usec_t llt_time) {
+ return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time);
}
int sd_dhcp_client_set_duid(
@@ -414,7 +448,13 @@ int sd_dhcp_client_set_duid(
uint16_t duid_type,
const void *duid,
size_t duid_len) {
- return dhcp_client_set_iaid_duid(client, 0, false, duid_type, duid, duid_len);
+ return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0);
+}
+
+int sd_dhcp_client_set_duid_llt(
+ sd_dhcp_client *client,
+ usec_t llt_time) {
+ return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time);
}
int sd_dhcp_client_set_hostname(
@@ -506,11 +546,10 @@ static int client_initialize(sd_dhcp_client *client) {
client->fd = asynchronous_close(client->fd);
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-
- client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
- client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
- client->timeout_expire = sd_event_source_unref(client->timeout_expire);
+ (void) event_source_disable(client->timeout_resend);
+ (void) event_source_disable(client->timeout_t1);
+ (void) event_source_disable(client->timeout_t2);
+ (void) event_source_disable(client->timeout_expire);
client->attempt = 1;
@@ -611,7 +650,8 @@ static int client_message_init(
client->client_id.type = 255;
- r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
+ r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len,
+ true, &client->client_id.ns.iaid);
if (r < 0)
return r;
@@ -659,7 +699,7 @@ static int client_message_init(
let the server know how large the server may make its DHCP messages.
Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
- than the defined default size unless the Maximum Messge Size option
+ than the defined default size unless the Maximum Message Size option
is explicitly set
RFC3442 "Requirements to Avoid Sizing Constraints":
@@ -1024,22 +1064,11 @@ static int client_timeout_resend(
next_timeout += (random_u32() & 0x1fffff);
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-
- r = sd_event_add_time(client->event,
- &client->timeout_resend,
- clock_boottime_or_monotonic(),
- next_timeout, 10 * USEC_PER_MSEC,
- client_timeout_resend, client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
+ r = event_reset_time(client->event, &client->timeout_resend,
+ clock_boottime_or_monotonic(),
+ next_timeout, 10 * USEC_PER_MSEC,
+ client_timeout_resend, client,
+ client->event_priority, "dhcp4-resend-timer", true);
if (r < 0)
goto error;
@@ -1136,31 +1165,16 @@ static int client_initialize_time_events(sd_dhcp_client *client) {
assert(client);
assert(client->event);
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-
if (client->start_delay) {
assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0);
usec += client->start_delay;
}
- r = sd_event_add_time(client->event,
- &client->timeout_resend,
- clock_boottime_or_monotonic(),
- usec, 0,
- client_timeout_resend, client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
- if (r < 0)
- goto error;
-
-error:
+ r = event_reset_time(client->event, &client->timeout_resend,
+ clock_boottime_or_monotonic(),
+ usec, 0,
+ client_timeout_resend, client,
+ client->event_priority, "dhcp4-resend-timer", true);
if (r < 0)
client_stop(client, r);
@@ -1418,13 +1432,14 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
assert(client->lease);
assert(client->lease->lifetime);
- client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
- client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
- client->timeout_expire = sd_event_source_unref(client->timeout_expire);
-
/* don't set timers for infinite leases */
- if (client->lease->lifetime == 0xffffffff)
+ if (client->lease->lifetime == 0xffffffff) {
+ (void) event_source_disable(client->timeout_t1);
+ (void) event_source_disable(client->timeout_t2);
+ (void) event_source_disable(client->timeout_expire);
+
return 0;
+ }
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
@@ -1476,19 +1491,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
}
/* arm lifetime timeout */
- r = sd_event_add_time(client->event, &client->timeout_expire,
- clock_boottime_or_monotonic(),
- lifetime_timeout, 10 * USEC_PER_MSEC,
- client_timeout_expire, client);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(client->timeout_expire,
- client->event_priority);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime");
+ r = event_reset_time(client->event, &client->timeout_expire,
+ clock_boottime_or_monotonic(),
+ lifetime_timeout, 10 * USEC_PER_MSEC,
+ client_timeout_expire, client,
+ client->event_priority, "dhcp4-lifetime", true);
if (r < 0)
return r;
@@ -1500,21 +1507,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
return 0;
/* arm T2 timeout */
- r = sd_event_add_time(client->event,
- &client->timeout_t2,
- clock_boottime_or_monotonic(),
- t2_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_t2, client);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(client->timeout_t2,
- client->event_priority);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout");
+ r = event_reset_time(client->event, &client->timeout_t2,
+ clock_boottime_or_monotonic(),
+ t2_timeout, 10 * USEC_PER_MSEC,
+ client_timeout_t2, client,
+ client->event_priority, "dhcp4-t2-timeout", true);
if (r < 0)
return r;
@@ -1526,20 +1523,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
return 0;
/* arm T1 timeout */
- r = sd_event_add_time(client->event,
- &client->timeout_t1,
- clock_boottime_or_monotonic(),
- t1_timeout, 10 * USEC_PER_MSEC,
- client_timeout_t1, client);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(client->timeout_t1,
- client->event_priority);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer");
+ r = event_reset_time(client->event, &client->timeout_t1,
+ clock_boottime_or_monotonic(),
+ t1_timeout, 10 * USEC_PER_MSEC,
+ client_timeout_t1, client,
+ client->event_priority, "dhcp4-t1-timer", true);
if (r < 0)
return r;
@@ -1564,26 +1552,14 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
r = client_handle_offer(client, message, len);
if (r >= 0) {
- client->timeout_resend =
- sd_event_source_unref(client->timeout_resend);
-
client->state = DHCP_STATE_REQUESTING;
client->attempt = 1;
- r = sd_event_add_time(client->event,
- &client->timeout_resend,
- clock_boottime_or_monotonic(),
- 0, 0,
- client_timeout_resend, client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
+ r = event_reset_time(client->event, &client->timeout_resend,
+ clock_boottime_or_monotonic(),
+ 0, 0,
+ client_timeout_resend, client,
+ client->event_priority, "dhcp4-resend-timer", true);
if (r < 0)
goto error;
} else if (r == -ENOMSG)
@@ -1600,8 +1576,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
r = client_handle_ack(client, message, len);
if (r >= 0) {
client->start_delay = 0;
- client->timeout_resend =
- sd_event_source_unref(client->timeout_resend);
+ (void) event_source_disable(client->timeout_resend);
client->receive_message =
sd_event_source_unref(client->receive_message);
client->fd = asynchronous_close(client->fd);
@@ -1641,8 +1616,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
} else if (r == -EADDRNOTAVAIL) {
/* got a NAK, let's restart the client */
- client->timeout_resend =
- sd_event_source_unref(client->timeout_resend);
+ client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
r = client_initialize(client);
if (r < 0)
@@ -1809,8 +1783,7 @@ static int client_receive_message_raw(
if (!packet)
return -ENOMEM;
- iov.iov_base = packet;
- iov.iov_len = buflen;
+ iov = IOVEC_MAKE(packet, buflen);
len = recvmsg(fd, &msg, 0);
if (len < 0) {
@@ -1912,33 +1885,17 @@ sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
return client->event;
}
-sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
-
- if (!client)
- return NULL;
-
- assert(client->n_ref >= 1);
- client->n_ref++;
-
- return client;
-}
-
-sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
-
- if (!client)
- return NULL;
-
- assert(client->n_ref >= 1);
- client->n_ref--;
-
- if (client->n_ref > 0)
- return NULL;
+static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
+ assert(client);
log_dhcp_client(client, "FREE");
- client_initialize(client);
+ client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+ client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
+ client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
+ client->timeout_expire = sd_event_source_unref(client->timeout_expire);
- client->receive_message = sd_event_source_unref(client->receive_message);
+ client_initialize(client);
sd_dhcp_client_detach_event(client);
@@ -1951,24 +1908,27 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
return mfree(client);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_client, sd_dhcp_client, dhcp_client_free);
+
int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
_cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
assert_return(ret, -EINVAL);
- client = new0(sd_dhcp_client, 1);
+ client = new(sd_dhcp_client, 1);
if (!client)
return -ENOMEM;
- client->n_ref = 1;
- client->state = DHCP_STATE_INIT;
- client->ifindex = -1;
- client->fd = -1;
- client->attempt = 1;
- client->mtu = DHCP_DEFAULT_MIN_SIZE;
- client->port = DHCP_PORT_CLIENT;
-
- client->anonymize = !!anonymize;
+ *client = (sd_dhcp_client) {
+ .n_ref = 1,
+ .state = DHCP_STATE_INIT,
+ .ifindex = -1,
+ .fd = -1,
+ .attempt = 1,
+ .mtu = DHCP_DEFAULT_MIN_SIZE,
+ .port = DHCP_PORT_CLIENT,
+ .anonymize = !!anonymize,
+ };
/* NOTE: this could be moved to a function. */
if (anonymize) {
client->req_opts_size = ELEMENTSOF(default_req_opts_anonymize);
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index dbd80442e6..13badbf0bf 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -16,6 +16,7 @@
#include "dhcp-lease-internal.h"
#include "dhcp-protocol.h"
#include "dns-domain.h"
+#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
#include "hexdecoct.h"
@@ -25,6 +26,8 @@
#include "parse-util.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
#include "unaligned.h"
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
@@ -245,27 +248,8 @@ int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, s
return 0;
}
-sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
-
- if (!lease)
- return NULL;
-
- assert(lease->n_ref >= 1);
- lease->n_ref++;
-
- return lease;
-}
-
-sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
-
- if (!lease)
- return NULL;
-
- assert(lease->n_ref >= 1);
- lease->n_ref--;
-
- if (lease->n_ref > 0)
- return NULL;
+static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
+ assert(lease);
while (lease->private_options) {
struct sd_dhcp_raw_option *option = lease->private_options;
@@ -276,6 +260,8 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
free(option);
}
+ free(lease->root_path);
+ free(lease->timezone);
free(lease->hostname);
free(lease->domainname);
free(lease->dns);
@@ -287,6 +273,8 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
return mfree(lease);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_lease, sd_dhcp_lease, dhcp_lease_free);
+
static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
assert(option);
assert(ret);
@@ -346,8 +334,7 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
if (!string)
return -ENOMEM;
- free(*ret);
- *ret = string;
+ free_and_replace(*ret, string);
}
return 0;
@@ -368,7 +355,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
return 0;
}
- r = dns_name_normalize(name, &normalized);
+ r = dns_name_normalize(name, 0, &normalized);
if (r < 0)
return r;
@@ -1031,7 +1018,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
if (r < 0)
return r;
- r = parse_env_file(NULL, lease_file, NEWLINE,
+ r = parse_env_file(NULL, lease_file,
"ADDRESS", &address,
"ROUTER", &router,
"NETMASK", &netmask,
@@ -1082,8 +1069,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
"OPTION_251", &options[27],
"OPTION_252", &options[28],
"OPTION_253", &options[29],
- "OPTION_254", &options[30],
- NULL);
+ "OPTION_254", &options[30]);
if (r < 0)
return r;
@@ -1315,3 +1301,9 @@ int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
*gateway = route->gw_addr;
return 0;
}
+
+int sd_dhcp_route_get_option(sd_dhcp_route *route) {
+ assert_return(route, -EINVAL);
+
+ return route->option;
+}
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index 5ca46b3502..7cb44d1fdf 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -12,6 +12,7 @@
#include "dhcp-server-internal.h"
#include "fd-util.h"
#include "in-addr-util.h"
+#include "io-util.h"
#include "sd-id128.h"
#include "siphash24.h"
#include "string-util.h"
@@ -20,12 +21,12 @@
#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
-static void dhcp_lease_free(DHCPLease *lease) {
+static DHCPLease *dhcp_lease_free(DHCPLease *lease) {
if (!lease)
- return;
+ return NULL;
free(lease->client_id.data);
- free(lease);
+ return mfree(lease);
}
/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
@@ -89,7 +90,7 @@ int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *addres
server->bound_leases[server_off - offset] = &server->invalid_lease;
/* Drop any leases associated with the old address range */
- hashmap_clear_with_destructor(server->leases_by_client_id, dhcp_lease_free);
+ hashmap_clear(server->leases_by_client_id);
}
return 0;
@@ -101,20 +102,7 @@ int sd_dhcp_server_is_running(sd_dhcp_server *server) {
return !!server->receive_message;
}
-sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
-
- if (!server)
- return NULL;
-
- assert(server->n_ref >= 1);
- server->n_ref++;
-
- return server;
-}
-
-void client_id_hash_func(const void *p, struct siphash *state) {
- const DHCPClientId *id = p;
-
+void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
assert(id);
assert(id->length);
assert(id->data);
@@ -123,37 +111,24 @@ void client_id_hash_func(const void *p, struct siphash *state) {
siphash24_compress(id->data, id->length, state);
}
-int client_id_compare_func(const void *_a, const void *_b) {
- const DHCPClientId *a, *b;
-
- a = _a;
- b = _b;
+int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
+ int r;
assert(!a->length || a->data);
assert(!b->length || b->data);
- if (a->length != b->length)
- return a->length < b->length ? -1 : 1;
+ r = CMP(a->length, b->length);
+ if (r != 0)
+ return r;
return memcmp(a->data, b->data, a->length);
}
-static const struct hash_ops client_id_hash_ops = {
- .hash = client_id_hash_func,
- .compare = client_id_compare_func
-};
-
-sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
- DHCPLease *lease;
-
- if (!server)
- return NULL;
-
- assert(server->n_ref >= 1);
- server->n_ref--;
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(dhcp_lease_hash_ops, DHCPClientId, client_id_hash_func, client_id_compare_func,
+ DHCPLease, dhcp_lease_free);
- if (server->n_ref > 0)
- return NULL;
+static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
+ assert(server);
log_dhcp_server(server, "UNREF");
@@ -165,14 +140,14 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
free(server->dns);
free(server->ntp);
- while ((lease = hashmap_steal_first(server->leases_by_client_id)))
- dhcp_lease_free(lease);
hashmap_free(server->leases_by_client_id);
free(server->bound_leases);
return mfree(server);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_server, sd_dhcp_server, dhcp_server_free);
+
int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
@@ -190,7 +165,7 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
server->netmask = htobe32(INADDR_ANY);
server->ifindex = ifindex;
- server->leases_by_client_id = hashmap_new(&client_id_hash_ops);
+ server->leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
if (!server->leases_by_client_id)
return -ENOMEM;
@@ -957,8 +932,7 @@ static int server_receive_message(sd_event_source *s, int fd,
if (!message)
return -ENOMEM;
- iov.iov_base = message;
- iov.iov_len = buflen;
+ iov = IOVEC_MAKE(message, buflen);
len = recvmsg(fd, &msg, 0);
if (len < 0) {
@@ -1002,7 +976,7 @@ int sd_dhcp_server_start(sd_dhcp_server *server) {
assert_return(server->fd < 0, -EBUSY);
assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
- r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ r = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (r < 0) {
r = -errno;
sd_dhcp_server_stop(server);
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index b3bc259280..593ffd1b64 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -16,6 +16,7 @@
#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h"
#include "dns-domain.h"
+#include "event-util.h"
#include "fd-util.h"
#include "hostname-util.h"
#include "in-addr-util.h"
@@ -27,6 +28,13 @@
#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
+/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
+enum {
+ DHCP6_REQUEST_IA_NA = 1,
+ DHCP6_REQUEST_IA_TA = 2, /* currently not used */
+ DHCP6_REQUEST_IA_PD = 4,
+};
+
struct sd_dhcp6_client {
unsigned n_ref;
@@ -40,12 +48,15 @@ struct sd_dhcp6_client {
uint16_t arp_type;
DHCP6IA ia_na;
DHCP6IA ia_pd;
- bool prefix_delegation;
+ sd_event_source *timeout_t1;
+ sd_event_source *timeout_t2;
+ unsigned request;
be32_t transaction_id;
usec_t transaction_start;
struct sd_dhcp6_lease *lease;
int fd;
bool information_request;
+ bool iaid_set;
be16_t *req_opts;
size_t req_opts_allocated;
size_t req_opts_len;
@@ -182,43 +193,86 @@ static int client_ensure_duid(sd_dhcp6_client *client) {
* without further modification. Otherwise, if duid_type is supported, DUID
* is set based on that type. Otherwise, an error is returned.
*/
-int sd_dhcp6_client_set_duid(
+static int dhcp6_client_set_duid_internal(
sd_dhcp6_client *client,
uint16_t duid_type,
const void *duid,
- size_t duid_len) {
-
+ size_t duid_len,
+ usec_t llt_time) {
int r;
+
assert_return(client, -EINVAL);
assert_return(duid_len == 0 || duid != NULL, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
if (duid != NULL) {
- r = dhcp_validate_duid_len(duid_type, duid_len);
- if (r < 0)
- return r;
- }
+ r = dhcp_validate_duid_len(duid_type, duid_len, true);
+ if (r < 0) {
+ r = dhcp_validate_duid_len(duid_type, duid_len, false);
+ if (r < 0)
+ return r;
+ log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type);
+ }
- if (duid != NULL) {
client->duid.type = htobe16(duid_type);
memcpy(&client->duid.raw.data, duid, duid_len);
client->duid_len = sizeof(client->duid.type) + duid_len;
- } else if (duid_type == DUID_TYPE_EN) {
- r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
- if (r < 0)
- return r;
} else
- return -EOPNOTSUPP;
+ switch (duid_type) {
+ case DUID_TYPE_LLT:
+ if (client->mac_addr_len == 0)
+ return -EOPNOTSUPP;
+
+ r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
+ if (r < 0)
+ return r;
+ break;
+ case DUID_TYPE_EN:
+ r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+ if (r < 0)
+ return r;
+ break;
+ case DUID_TYPE_LL:
+ if (client->mac_addr_len == 0)
+ return -EOPNOTSUPP;
+
+ r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
+ if (r < 0)
+ return r;
+ break;
+ case DUID_TYPE_UUID:
+ r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
+ if (r < 0)
+ return r;
+ break;
+ default:
+ return -EINVAL;
+ }
return 0;
}
+int sd_dhcp6_client_set_duid(
+ sd_dhcp6_client *client,
+ uint16_t duid_type,
+ const void *duid,
+ size_t duid_len) {
+ return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
+}
+
+int sd_dhcp6_client_set_duid_llt(
+ sd_dhcp6_client *client,
+ usec_t llt_time) {
+ return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
+}
+
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
assert_return(client, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
client->ia_na.ia_na.id = htobe32(iaid);
client->ia_pd.ia_pd.id = htobe32(iaid);
+ client->iaid_set = true;
return 0;
}
@@ -287,10 +341,44 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
return 0;
}
-int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) {
+int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
assert_return(client, -EINVAL);
+ assert_return(delegation, -EINVAL);
- client->prefix_delegation = delegation;
+ *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD);
+
+ return 0;
+}
+
+int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
+ assert_return(client, -EINVAL);
+
+ SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation);
+
+ return 0;
+}
+
+int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
+ assert_return(client, -EINVAL);
+ assert_return(request, -EINVAL);
+
+ *request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA);
+
+ return 0;
+}
+
+int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
+ assert_return(client, -EINVAL);
+
+ SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request);
+
+ return 0;
+}
+
+int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
+ assert_return(client, -EINVAL);
+
+ client->transaction_id = transaction_id;
return 0;
}
@@ -314,21 +402,10 @@ static void client_notify(sd_dhcp6_client *client, int event) {
client->callback(client, event, client->userdata);
}
-static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) {
- assert(client);
-
- if (client->lease) {
- dhcp6_lease_clear_timers(&client->lease->ia);
- sd_dhcp6_lease_unref(client->lease);
- }
-
- client->lease = lease;
-}
-
static int client_reset(sd_dhcp6_client *client) {
assert(client);
- client_set_lease(client, NULL);
+ client->lease = sd_dhcp6_lease_unref(client->lease);
client->receive_message =
sd_event_source_unref(client->receive_message);
@@ -336,16 +413,13 @@ static int client_reset(sd_dhcp6_client *client) {
client->transaction_id = 0;
client->transaction_start = 0;
- client->ia_na.timeout_t1 =
- sd_event_source_unref(client->ia_na.timeout_t1);
- client->ia_na.timeout_t2 =
- sd_event_source_unref(client->ia_na.timeout_t2);
-
client->retransmit_time = 0;
client->retransmit_count = 0;
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
- client->timeout_resend_expire =
- sd_event_source_unref(client->timeout_resend_expire);
+
+ (void) event_source_disable(client->timeout_resend);
+ (void) event_source_disable(client->timeout_resend_expire);
+ (void) event_source_disable(client->timeout_t1);
+ (void) event_source_disable(client->timeout_t2);
client->state = DHCP6_STATE_STOPPED;
@@ -398,9 +472,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (r < 0)
return r;
- r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
- if (r < 0)
- return r;
+ if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
+ r = dhcp6_option_append_ia(&opt, &optlen,
+ &client->ia_na);
+ if (r < 0)
+ return r;
+ }
if (client->fqdn) {
r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
@@ -408,7 +485,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
- if (client->prefix_delegation) {
+ if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
if (r < 0)
return r;
@@ -433,9 +510,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (r < 0)
return r;
- r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
- if (r < 0)
- return r;
+ if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
+ r = dhcp6_option_append_ia(&opt, &optlen,
+ &client->lease->ia);
+ if (r < 0)
+ return r;
+ }
if (client->fqdn) {
r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
@@ -443,7 +523,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
- if (client->prefix_delegation) {
+ if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
if (r < 0)
return r;
@@ -457,9 +537,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
case DHCP6_STATE_REBIND:
message->type = DHCP6_REBIND;
- r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
- if (r < 0)
- return r;
+ if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
+ r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
+ if (r < 0)
+ return r;
+ }
if (client->fqdn) {
r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
@@ -467,7 +549,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
- if (client->prefix_delegation) {
+ if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
if (r < 0)
return r;
@@ -524,8 +606,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
assert(client);
assert(client->lease);
- client->lease->ia.timeout_t2 =
- sd_event_source_unref(client->lease->ia.timeout_t2);
+ (void) event_source_disable(client->timeout_t2);
log_dhcp6_client(client, "Timeout T2");
@@ -541,8 +622,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
assert(client);
assert(client->lease);
- client->lease->ia.timeout_t1 =
- sd_event_source_unref(client->lease->ia.timeout_t1);
+ (void) event_source_disable(client->timeout_t1);
log_dhcp6_client(client, "Timeout T1");
@@ -590,7 +670,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
assert(client);
assert(client->event);
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+ (void) event_source_disable(client->timeout_resend);
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
@@ -632,7 +712,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
init_retransmit_time = DHCP6_REB_TIMEOUT;
max_retransmit_time = DHCP6_REB_MAX_RT;
- if (!client->timeout_resend_expire) {
+ if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
&expire);
if (r < 0) {
@@ -681,43 +761,24 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
log_dhcp6_client(client, "Next retransmission in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
- r = sd_event_add_time(client->event, &client->timeout_resend,
- clock_boottime_or_monotonic(),
- time_now + client->retransmit_time,
- 10 * USEC_PER_MSEC, client_timeout_resend,
- client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer");
+ r = event_reset_time(client->event, &client->timeout_resend,
+ clock_boottime_or_monotonic(),
+ time_now + client->retransmit_time, 10 * USEC_PER_MSEC,
+ client_timeout_resend, client,
+ client->event_priority, "dhcp6-resend-timer", true);
if (r < 0)
goto error;
- if (max_retransmit_duration && !client->timeout_resend_expire) {
+ if (max_retransmit_duration && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
max_retransmit_duration / USEC_PER_SEC);
- r = sd_event_add_time(client->event,
- &client->timeout_resend_expire,
- clock_boottime_or_monotonic(),
- time_now + max_retransmit_duration,
- USEC_PER_SEC,
- client_timeout_resend_expire, client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend_expire,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
+ r = event_reset_time(client->event, &client->timeout_resend_expire,
+ clock_boottime_or_monotonic(),
+ time_now + max_retransmit_duration, USEC_PER_SEC,
+ client_timeout_resend_expire, client,
+ client->event_priority, "dhcp6-resend-expire-timer", true);
if (r < 0)
goto error;
}
@@ -731,19 +792,20 @@ error:
static int client_ensure_iaid(sd_dhcp6_client *client) {
int r;
- be32_t iaid;
+ uint32_t iaid;
assert(client);
- if (client->ia_na.ia_na.id)
+ if (client->iaid_set)
return 0;
- r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid);
+ r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid);
if (r < 0)
return r;
client->ia_na.ia_na.id = iaid;
client->ia_pd.ia_pd.id = iaid;
+ client->iaid_set = true;
return 0;
}
@@ -753,10 +815,11 @@ static int client_parse_message(
DHCP6Message *message,
size_t len,
sd_dhcp6_lease *lease) {
+
+ uint32_t lt_t1 = ~0, lt_t2 = ~0;
+ bool clientid = false;
size_t pos = 0;
int r;
- bool clientid = false;
- uint32_t lt_t1 = ~0, lt_t2 = ~0;
assert(client);
assert(message);
@@ -766,20 +829,22 @@ static int client_parse_message(
len -= sizeof(DHCP6Message);
while (pos < len) {
- DHCP6Option *option = (DHCP6Option *)&message->options[pos];
+ DHCP6Option *option = (DHCP6Option *) &message->options[pos];
uint16_t optcode, optlen;
- int status;
- uint8_t *optval;
be32_t iaid_lease;
+ uint8_t *optval;
+ int status;
- if (len < offsetof(DHCP6Option, data) ||
- len < offsetof(DHCP6Option, data) + be16toh(option->len))
+ if (len < pos + offsetof(DHCP6Option, data))
return -ENOBUFS;
optcode = be16toh(option->code);
optlen = be16toh(option->len);
optval = option->data;
+ if (len < pos + offsetof(DHCP6Option, data) + optlen)
+ return -ENOBUFS;
+
switch (optcode) {
case SD_DHCP6_OPTION_CLIENTID:
if (clientid) {
@@ -824,13 +889,14 @@ static int client_parse_message(
break;
case SD_DHCP6_OPTION_STATUS_CODE:
- status = dhcp6_option_parse_status(option);
- if (status) {
+ status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option));
+ if (status < 0)
+ return status;
+
+ if (status > 0) {
log_dhcp6_client(client, "%s Status %s",
dhcp6_message_type_to_string(message->type),
dhcp6_message_status_to_string(status));
- dhcp6_lease_free_ia(&lease->ia);
- dhcp6_lease_free_ia(&lease->pd);
return -EINVAL;
}
@@ -876,7 +942,7 @@ static int client_parse_message(
if (r < 0 && r != -ENOMSG)
return r;
- r = dhcp6_lease_get_iaid(lease, &iaid_lease);
+ r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
if (r < 0)
return r;
@@ -929,7 +995,7 @@ static int client_parse_message(
break;
}
- pos += sizeof(*option) + optlen;
+ pos += offsetof(DHCP6Option, data) + optlen;
}
if (!clientid) {
@@ -989,8 +1055,8 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si
return 0;
}
- client_set_lease(client, lease);
- lease = NULL;
+ sd_dhcp6_lease_unref(client->lease);
+ client->lease = TAKE_PTR(lease);
return DHCP6_STATE_BOUND;
}
@@ -1018,8 +1084,8 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver
r = dhcp6_lease_get_preference(client->lease, &pref_lease);
if (r < 0 || pref_advertise > pref_lease) {
- client_set_lease(client, lease);
- lease = NULL;
+ sd_dhcp6_lease_unref(client->lease);
+ client->lease = TAKE_PTR(lease);
r = 0;
}
@@ -1144,26 +1210,47 @@ static int client_receive_message(
return 0;
}
- if (r >= 0)
- log_dhcp6_client(client, "Recv %s",
- dhcp6_message_type_to_string(message->type));
+ log_dhcp6_client(client, "Recv %s",
+ dhcp6_message_type_to_string(message->type));
return 0;
}
+static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
+ uint32_t *lifetime_t2) {
+ assert_return(client, -EINVAL);
+ assert_return(client->lease, -EINVAL);
+
+ if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
+ *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
+ *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
+
+ return 0;
+ }
+
+ if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
+ *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
+ *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
+
+ return 0;
+ }
+
+ return -ENOMSG;
+}
+
static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
int r;
usec_t timeout, time_now;
char time_string[FORMAT_TIMESPAN_MAX];
+ uint32_t lifetime_t1, lifetime_t2;
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
assert_return(client->ifindex > 0, -EINVAL);
assert_return(client->state != state, -EINVAL);
- client->timeout_resend_expire =
- sd_event_source_unref(client->timeout_resend_expire);
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+ (void) event_source_disable(client->timeout_resend_expire);
+ (void) event_source_disable(client->timeout_resend);
client->retransmit_time = 0;
client->retransmit_count = 0;
@@ -1214,57 +1301,40 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
case DHCP6_STATE_BOUND:
- if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff ||
- client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) {
+ r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
+ if (r < 0)
+ goto error;
+ if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
- be32toh(client->lease->ia.ia_na.lifetime_t1),
- be32toh(client->lease->ia.ia_na.lifetime_t2));
+ lifetime_t1, lifetime_t2);
return 0;
}
- timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC);
+ timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
log_dhcp6_client(client, "T1 expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
- r = sd_event_add_time(client->event,
- &client->lease->ia.timeout_t1,
- clock_boottime_or_monotonic(), time_now + timeout,
- 10 * USEC_PER_SEC, client_timeout_t1,
- client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
+ r = event_reset_time(client->event, &client->timeout_t1,
+ clock_boottime_or_monotonic(),
+ time_now + timeout, 10 * USEC_PER_SEC,
+ client_timeout_t1, client,
+ client->event_priority, "dhcp6-t1-timeout", true);
if (r < 0)
goto error;
- timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC);
+ timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
log_dhcp6_client(client, "T2 expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
- r = sd_event_add_time(client->event,
- &client->lease->ia.timeout_t2,
- clock_boottime_or_monotonic(), time_now + timeout,
- 10 * USEC_PER_SEC, client_timeout_t2,
- client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
+ r = event_reset_time(client->event, &client->timeout_t2,
+ clock_boottime_or_monotonic(),
+ time_now + timeout, 10 * USEC_PER_SEC,
+ client_timeout_t2, client,
+ client->event_priority, "dhcp6-t2-timeout", true);
if (r < 0)
goto error;
@@ -1276,18 +1346,11 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
client->transaction_id = random_u32() & htobe32(0x00ffffff);
client->transaction_start = time_now;
- r = sd_event_add_time(client->event, &client->timeout_resend,
- clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
- client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
+ r = event_reset_time(client->event, &client->timeout_resend,
+ clock_boottime_or_monotonic(),
+ 0, 0,
+ client_timeout_resend, client,
+ client->event_priority, "dhcp6-resend-timeout", true);
if (r < 0)
goto error;
@@ -1326,6 +1389,9 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
return -EBUSY;
+ if (!client->information_request && !client->request)
+ return -EINVAL;
+
r = client_reset(client);
if (r < 0)
return r;
@@ -1394,27 +1460,13 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
return client->event;
}
-sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
-
- if (!client)
- return NULL;
-
- assert(client->n_ref >= 1);
- client->n_ref++;
-
- return client;
-}
-
-sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
-
- if (!client)
- return NULL;
-
- assert(client->n_ref >= 1);
- client->n_ref--;
+static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
+ assert(client);
- if (client->n_ref > 0)
- return NULL;
+ client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+ client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire);
+ client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
+ client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
client_reset(client);
@@ -1427,29 +1479,36 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
return mfree(client);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
+
int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
_cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
+ _cleanup_free_ be16_t *req_opts = NULL;
size_t t;
assert_return(ret, -EINVAL);
- client = new0(sd_dhcp6_client, 1);
- if (!client)
+ req_opts = new(be16_t, ELEMENTSOF(default_req_opts));
+ if (!req_opts)
return -ENOMEM;
- client->n_ref = 1;
- client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
- client->ia_pd.type = SD_DHCP6_OPTION_IA_PD;
- client->ifindex = -1;
- client->fd = -1;
+ for (t = 0; t < ELEMENTSOF(default_req_opts); t++)
+ req_opts[t] = htobe16(default_req_opts[t]);
- client->req_opts_len = ELEMENTSOF(default_req_opts);
- client->req_opts = new0(be16_t, client->req_opts_len);
- if (!client->req_opts)
+ client = new(sd_dhcp6_client, 1);
+ if (!client)
return -ENOMEM;
- for (t = 0; t < client->req_opts_len; t++)
- client->req_opts[t] = htobe16(default_req_opts[t]);
+ *client = (sd_dhcp6_client) {
+ .n_ref = 1,
+ .ia_na.type = SD_DHCP6_OPTION_IA_NA,
+ .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
+ .ifindex = -1,
+ .request = DHCP6_REQUEST_IA_NA,
+ .fd = -1,
+ .req_opts_len = ELEMENTSOF(default_req_opts),
+ .req_opts = TAKE_PTR(req_opts),
+ };
*ret = TAKE_PTR(client);
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index 779ad54b4a..8b424811ad 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -11,15 +11,6 @@
#include "strv.h"
#include "util.h"
-int dhcp6_lease_clear_timers(DHCP6IA *ia) {
- assert_return(ia, -EINVAL);
-
- ia->timeout_t1 = sd_event_source_unref(ia->timeout_t1);
- ia->timeout_t2 = sd_event_source_unref(ia->timeout_t2);
-
- return 0;
-}
-
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
DHCP6Address *addr;
uint32_t valid = 0, t;
@@ -48,8 +39,6 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
if (!ia)
return NULL;
- dhcp6_lease_clear_timers(ia);
-
while (ia->addresses) {
address = ia->addresses;
@@ -63,15 +52,16 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
size_t len) {
+ uint8_t *serverid;
+
assert_return(lease, -EINVAL);
assert_return(id, -EINVAL);
- free(lease->serverid);
-
- lease->serverid = memdup(id, len);
- if (!lease->serverid)
- return -EINVAL;
+ serverid = memdup(id, len);
+ if (!serverid)
+ return -ENOMEM;
+ free_and_replace(lease->serverid, serverid);
lease->serverid_len = len;
return 0;
@@ -136,6 +126,15 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
return 0;
}
+int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
+ assert_return(lease, -EINVAL);
+ assert_return(iaid, -EINVAL);
+
+ *iaid = lease->pd.ia_pd.id;
+
+ return 0;
+}
+
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid) {
@@ -374,27 +373,8 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
return -ENOENT;
}
-sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) {
-
- if (!lease)
- return NULL;
-
- assert(lease->n_ref >= 1);
- lease->n_ref++;
-
- return lease;
-}
-
-sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
-
- if (!lease)
- return NULL;
-
- assert(lease->n_ref >= 1);
- lease->n_ref--;
-
- if (lease->n_ref > 0)
- return NULL;
+static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
+ assert(lease);
free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia);
@@ -410,6 +390,8 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
return mfree(lease);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
+
int dhcp6_lease_new(sd_dhcp6_lease **ret) {
sd_dhcp6_lease *lease;
diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c
index a40d40db90..59359aec79 100644
--- a/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/libsystemd-network/sd-ipv4acd.c
@@ -14,6 +14,7 @@
#include "alloc-util.h"
#include "arp-util.h"
#include "ether-addr-util.h"
+#include "event-util.h"
#include "fd-util.h"
#include "in-addr-util.h"
#include "list.h"
@@ -89,7 +90,7 @@ static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_count
static void ipv4acd_reset(sd_ipv4acd *acd) {
assert(acd);
- acd->timer_event_source = sd_event_source_unref(acd->timer_event_source);
+ (void) event_source_disable(acd->timer_event_source);
acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source);
acd->fd = safe_close(acd->fd);
@@ -97,25 +98,10 @@ static void ipv4acd_reset(sd_ipv4acd *acd) {
ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
}
-sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd) {
- if (!acd)
- return NULL;
-
- assert_se(acd->n_ref >= 1);
- acd->n_ref++;
-
- return acd;
-}
-
-sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) {
- if (!acd)
- return NULL;
-
- assert_se(acd->n_ref >= 1);
- acd->n_ref--;
+static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) {
+ assert(acd);
- if (acd->n_ref > 0)
- return NULL;
+ acd->timer_event_source = sd_event_source_unref(acd->timer_event_source);
ipv4acd_reset(acd);
sd_ipv4acd_detach_event(acd);
@@ -123,19 +109,23 @@ sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) {
return mfree(acd);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free);
+
int sd_ipv4acd_new(sd_ipv4acd **ret) {
_cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
assert_return(ret, -EINVAL);
- acd = new0(sd_ipv4acd, 1);
+ acd = new(sd_ipv4acd, 1);
if (!acd)
return -ENOMEM;
- acd->n_ref = 1;
- acd->state = IPV4ACD_STATE_INIT;
- acd->ifindex = -1;
- acd->fd = -1;
+ *acd = (sd_ipv4acd) {
+ .n_ref = 1,
+ .state = IPV4ACD_STATE_INIT,
+ .ifindex = -1,
+ .fd = -1,
+ };
*ret = TAKE_PTR(acd);
@@ -166,9 +156,7 @@ int sd_ipv4acd_stop(sd_ipv4acd *acd) {
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) {
- _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
usec_t next_timeout, time_now;
- int r;
assert(acd);
@@ -179,20 +167,11 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u
assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
- r = sd_event_add_time(acd->event, &timer, clock_boottime_or_monotonic(), time_now + next_timeout, 0, ipv4acd_on_timeout, acd);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(timer, acd->event_priority);
- if (r < 0)
- return r;
-
- (void) sd_event_source_set_description(timer, "ipv4acd-timer");
-
- sd_event_source_unref(acd->timer_event_source);
- acd->timer_event_source = TAKE_PTR(timer);
-
- return 0;
+ return event_reset_time(acd->event, &acd->timer_event_source,
+ clock_boottime_or_monotonic(),
+ time_now + next_timeout, 0,
+ ipv4acd_on_timeout, acd,
+ acd->event_priority, "ipv4acd-timer", true);
}
static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index 7307e1668f..e451dff744 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -55,30 +55,15 @@ struct sd_ipv4ll {
static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
-sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
- if (!ll)
- return NULL;
-
- assert(ll->n_ref >= 1);
- ll->n_ref++;
-
- return ll;
-}
-
-sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
- if (!ll)
- return NULL;
-
- assert(ll->n_ref >= 1);
- ll->n_ref--;
-
- if (ll->n_ref > 0)
- return NULL;
+static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) {
+ assert(ll);
sd_ipv4acd_unref(ll->acd);
return mfree(ll);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4ll, sd_ipv4ll, ipv4ll_free);
+
int sd_ipv4ll_new(sd_ipv4ll **ret) {
_cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL;
int r;
diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c
index c75d6079e1..969fc71051 100644
--- a/src/libsystemd-network/sd-lldp.c
+++ b/src/libsystemd-network/sd-lldp.c
@@ -2,36 +2,47 @@
#include <arpa/inet.h>
#include <linux/sockios.h>
+#include <sys/ioctl.h>
#include "sd-lldp.h"
#include "alloc-util.h"
+#include "ether-addr-util.h"
+#include "event-util.h"
#include "fd-util.h"
#include "lldp-internal.h"
#include "lldp-neighbor.h"
#include "lldp-network.h"
#include "socket-util.h"
-#include "ether-addr-util.h"
+#include "string-table.h"
#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
-static void lldp_flush_neighbors(sd_lldp *lldp) {
- sd_lldp_neighbor *n;
+static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
+ [SD_LLDP_EVENT_ADDED] = "added",
+ [SD_LLDP_EVENT_REMOVED] = "removed",
+ [SD_LLDP_EVENT_UPDATED] = "updated",
+ [SD_LLDP_EVENT_REFRESHED] = "refreshed",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event);
+static void lldp_flush_neighbors(sd_lldp *lldp) {
assert(lldp);
- while ((n = hashmap_first(lldp->neighbor_by_id)))
- lldp_neighbor_unlink(n);
+ hashmap_clear(lldp->neighbor_by_id);
}
static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
assert(lldp);
+ assert(event >= 0 && event < _SD_LLDP_EVENT_MAX);
- log_lldp("Invoking callback for '%c'.", event);
-
- if (!lldp->callback)
+ if (!lldp->callback) {
+ log_lldp("Received '%s' event.", lldp_event_to_string(event));
return;
+ }
+ log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event));
lldp->callback(lldp, event, n, lldp->userdata);
}
@@ -119,7 +130,7 @@ static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
}
if (lldp_neighbor_equal(n, old)) {
- /* Is this equal, then restart the TTL counter, but don't do anyting else. */
+ /* Is this equal, then restart the TTL counter, but don't do anything else. */
old->timestamp = n->timestamp;
lldp_start_timer(lldp, old);
lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
@@ -223,7 +234,7 @@ static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, v
static void lldp_reset(sd_lldp *lldp) {
assert(lldp);
- lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
+ (void) event_source_disable(lldp->timer_event_source);
lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
lldp->fd = safe_close(lldp->fd);
}
@@ -329,27 +340,10 @@ _public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
return 0;
}
-_public_ sd_lldp* sd_lldp_ref(sd_lldp *lldp) {
-
- if (!lldp)
- return NULL;
-
- assert(lldp->n_ref > 0);
- lldp->n_ref++;
-
- return lldp;
-}
-
-_public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) {
-
- if (!lldp)
- return NULL;
-
- assert(lldp->n_ref > 0);
- lldp->n_ref --;
+static sd_lldp* lldp_free(sd_lldp *lldp) {
+ assert(lldp);
- if (lldp->n_ref > 0)
- return NULL;
+ lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
lldp_reset(lldp);
sd_lldp_detach_event(lldp);
@@ -360,22 +354,26 @@ _public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) {
return mfree(lldp);
}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free);
+
_public_ int sd_lldp_new(sd_lldp **ret) {
_cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
int r;
assert_return(ret, -EINVAL);
- lldp = new0(sd_lldp, 1);
+ lldp = new(sd_lldp, 1);
if (!lldp)
return -ENOMEM;
- lldp->n_ref = 1;
- lldp->fd = -1;
- lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX;
- lldp->capability_mask = (uint16_t) -1;
+ *lldp = (sd_lldp) {
+ .n_ref = 1,
+ .fd = -1,
+ .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
+ .capability_mask = (uint16_t) -1,
+ };
- lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops);
+ lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
if (!lldp->neighbor_by_id)
return -ENOMEM;
@@ -388,10 +386,8 @@ _public_ int sd_lldp_new(sd_lldp **ret) {
return 0;
}
-static int neighbor_compare_func(const void *a, const void *b) {
- const sd_lldp_neighbor * const*x = a, * const *y = b;
-
- return lldp_neighbor_id_hash_ops.compare(&(*x)->id, &(*y)->id);
+static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
+ return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
}
static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
@@ -411,7 +407,6 @@ static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
sd_lldp_neighbor *n;
- int r;
assert(lldp);
@@ -419,35 +414,17 @@ static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
lldp_neighbor_start_ttl(neighbor);
n = prioq_peek(lldp->neighbor_by_expiry);
- if (!n) {
-
- if (lldp->timer_event_source)
- return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_OFF);
-
- return 0;
- }
-
- if (lldp->timer_event_source) {
- r = sd_event_source_set_time(lldp->timer_event_source, n->until);
- if (r < 0)
- return r;
-
- return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_ONESHOT);
- }
+ if (!n)
+ return event_source_disable(lldp->timer_event_source);
if (!lldp->event)
return 0;
- r = sd_event_add_time(lldp->event, &lldp->timer_event_source, clock_boottime_or_monotonic(), n->until, 0, on_timer_event, lldp);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(lldp->timer_event_source, lldp->event_priority);
- if (r < 0)
- return r;
-
- (void) sd_event_source_set_description(lldp->timer_event_source, "lldp-timer");
- return 0;
+ return event_reset_time(lldp->event, &lldp->timer_event_source,
+ clock_boottime_or_monotonic(),
+ n->until, 0,
+ on_timer_event, lldp,
+ lldp->event_priority, "lldp-timer", true);
}
_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
@@ -479,7 +456,7 @@ _public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
/* Return things in a stable order */
- qsort(l, k, sizeof(sd_lldp_neighbor*), neighbor_compare_func);
+ typesafe_qsort(l, k, neighbor_compare_func);
*ret = l;
return k;
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c
index 2d81160b02..79b2ea8bf2 100644
--- a/src/libsystemd-network/sd-ndisc.c
+++ b/src/libsystemd-network/sd-ndisc.c
@@ -9,6 +9,7 @@
#include "sd-ndisc.h"
#include "alloc-util.h"
+#include "event-util.h"
#include "fd-util.h"
#include "icmp6-util.h"
#include "in-addr-util.h"
@@ -16,19 +17,30 @@
#include "ndisc-router.h"
#include "random-util.h"
#include "socket-util.h"
+#include "string-table.h"
#include "string-util.h"
#include "util.h"
#define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS)
+static const char * const ndisc_event_table[_SD_NDISC_EVENT_MAX] = {
+ [SD_NDISC_EVENT_TIMEOUT] = "timeout",
+ [SD_NDISC_EVENT_ROUTER] = "router",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ndisc_event, sd_ndisc_event);
+
static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) {
assert(ndisc);
+ assert(event >= 0 && event < _SD_NDISC_EVENT_MAX);
- log_ndisc("Invoking callback for '%c'.", event);
- if (!ndisc->callback)
+ if (!ndisc->callback) {
+ log_ndisc("Received '%s' event.", ndisc_event_to_string(event));
return;
+ }
+ log_ndisc("Invoking callback for '%s' event.", ndisc_event_to_string(event));
ndisc->callback(ndisc, event, rt, ndisc->userdata);
}
@@ -100,56 +112,42 @@ _public_ sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
return nd->event;
}
-_public_ sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
-
- if (!nd)
- return NULL;
-
- assert(nd->n_ref > 0);
- nd->n_ref++;
-
- return nd;
-}
-
-static int ndisc_reset(sd_ndisc *nd) {
+static void ndisc_reset(sd_ndisc *nd) {
assert(nd);
- nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
- nd->timeout_no_ra = sd_event_source_unref(nd->timeout_no_ra);
+ (void) event_source_disable(nd->timeout_event_source);
+ (void) event_source_disable(nd->timeout_no_ra);
nd->retransmit_time = 0;
nd->recv_event_source = sd_event_source_unref(nd->recv_event_source);
nd->fd = safe_close(nd->fd);
-
- return 0;
}
-_public_ sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
-
- if (!nd)
- return NULL;
-
- assert(nd->n_ref > 0);
- nd->n_ref--;
+static sd_ndisc *ndisc_free(sd_ndisc *nd) {
+ assert(nd);
- if (nd->n_ref > 0)
- return NULL;
+ nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
+ nd->timeout_no_ra = sd_event_source_unref(nd->timeout_no_ra);
ndisc_reset(nd);
sd_ndisc_detach_event(nd);
return mfree(nd);
}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc, sd_ndisc, ndisc_free);
+
_public_ int sd_ndisc_new(sd_ndisc **ret) {
_cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
assert_return(ret, -EINVAL);
- nd = new0(sd_ndisc, 1);
+ nd = new(sd_ndisc, 1);
if (!nd)
return -ENOMEM;
- nd->n_ref = 1;
- nd->fd = -1;
+ *nd = (sd_ndisc) {
+ .n_ref = 1,
+ .fd = -1,
+ };
*ret = TAKE_PTR(nd);
@@ -238,14 +236,21 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
break;
case -EPFNOSUPPORT:
- log_ndisc("Received invalid source address from ICMPv6 socket.");
+ log_ndisc("Received invalid source address from ICMPv6 socket. Ignoring.");
+ break;
+
+ case -EAGAIN: /* ignore spurious wakeups */
+ break;
+
+ default:
+ log_ndisc_errno(r, "Unexpected error while reading from ICMPv6, ignoring: %m");
break;
}
return 0;
}
- nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
+ (void) event_source_disable(nd->timeout_event_source);
return ndisc_handle_datagram(nd, rt);
}
@@ -257,10 +262,10 @@ static usec_t ndisc_timeout_compute_random(usec_t val) {
}
static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ char time_string[FORMAT_TIMESPAN_MAX];
sd_ndisc *nd = userdata;
usec_t time_now;
int r;
- char time_string[FORMAT_TIMESPAN_MAX];
assert(s);
assert(nd);
@@ -268,8 +273,6 @@ static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
- nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
-
if (!nd->retransmit_time)
nd->retransmit_time = ndisc_timeout_compute_random(NDISC_ROUTER_SOLICITATION_INTERVAL);
else {
@@ -279,25 +282,14 @@ static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
nd->retransmit_time += ndisc_timeout_compute_random(nd->retransmit_time);
}
- r = sd_event_add_time(nd->event, &nd->timeout_event_source,
- clock_boottime_or_monotonic(),
- time_now + nd->retransmit_time,
- 10 * USEC_PER_MSEC, ndisc_timeout, nd);
+ r = event_reset_time(nd->event, &nd->timeout_event_source,
+ clock_boottime_or_monotonic(),
+ time_now + nd->retransmit_time, 10 * USEC_PER_MSEC,
+ ndisc_timeout, nd,
+ nd->event_priority, "ndisc-timeout-no-ra", true);
if (r < 0)
goto fail;
- r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority);
- if (r < 0)
- goto fail;
-
- (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout-no-ra");
-
- r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT);
- if (r < 0) {
- log_ndisc_errno(r, "Error reenabling timer: %m");
- goto fail;
- }
-
r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
if (r < 0) {
log_ndisc_errno(r, "Error sending Router Solicitation: %m");
@@ -311,7 +303,7 @@ static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
return 0;
fail:
- sd_ndisc_stop(nd);
+ (void) sd_ndisc_stop(nd);
return 0;
}
@@ -323,7 +315,7 @@ static int ndisc_timeout_no_ra(sd_event_source *s, uint64_t usec, void *userdata
log_ndisc("No RA received before link confirmation timeout");
- nd->timeout_no_ra = sd_event_source_unref(nd->timeout_no_ra);
+ (void) event_source_disable(nd->timeout_no_ra);
ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL);
return 0;
@@ -353,7 +345,6 @@ _public_ int sd_ndisc_start(sd_ndisc *nd) {
return 0;
assert(!nd->recv_event_source);
- assert(!nd->timeout_event_source);
r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
@@ -373,29 +364,22 @@ _public_ int sd_ndisc_start(sd_ndisc *nd) {
(void) sd_event_source_set_description(nd->recv_event_source, "ndisc-receive-message");
- r = sd_event_add_time(nd->event, &nd->timeout_event_source, clock_boottime_or_monotonic(), 0, 0, ndisc_timeout, nd);
+ r = event_reset_time(nd->event, &nd->timeout_event_source,
+ clock_boottime_or_monotonic(),
+ 0, 0,
+ ndisc_timeout, nd,
+ nd->event_priority, "ndisc-timeout", true);
if (r < 0)
goto fail;
- r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority);
+ r = event_reset_time(nd->event, &nd->timeout_no_ra,
+ clock_boottime_or_monotonic(),
+ time_now + NDISC_TIMEOUT_NO_RA_USEC, 10 * USEC_PER_MSEC,
+ ndisc_timeout_no_ra, nd,
+ nd->event_priority, "ndisc-timeout-no-ra", true);
if (r < 0)
goto fail;
- (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout");
-
- r = sd_event_add_time(nd->event, &nd->timeout_no_ra,
- clock_boottime_or_monotonic(),
- time_now + NDISC_TIMEOUT_NO_RA_USEC,
- 10 * USEC_PER_MSEC, ndisc_timeout_no_ra, nd);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_priority(nd->timeout_no_ra, nd->event_priority);
- if (r < 0)
- goto fail;
-
- (void) sd_event_source_set_description(nd->timeout_no_ra, "ndisc-timeout-no-ra");
-
log_ndisc("Started IPv6 Router Solicitation client");
return 1;
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index 86750b876c..098e01fb82 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -9,32 +9,35 @@
#include "sd-radv.h"
-#include "macro.h"
#include "alloc-util.h"
#include "dns-domain.h"
+#include "ether-addr-util.h"
+#include "event-util.h"
#include "fd-util.h"
#include "icmp6-util.h"
#include "in-addr-util.h"
+#include "io-util.h"
+#include "macro.h"
#include "radv-internal.h"
+#include "random-util.h"
#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
-#include "random-util.h"
_public_ int sd_radv_new(sd_radv **ret) {
_cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
assert_return(ret, -EINVAL);
- ra = new0(sd_radv, 1);
+ ra = new(sd_radv, 1);
if (!ra)
return -ENOMEM;
- ra->n_ref = 1;
- ra->fd = -1;
-
- LIST_HEAD_INIT(ra->prefixes);
+ *ra = (sd_radv) {
+ .n_ref = 1,
+ .fd = -1,
+ };
*ret = TAKE_PTR(ra);
@@ -77,8 +80,7 @@ _public_ sd_event *sd_radv_get_event(sd_radv *ra) {
static void radv_reset(sd_radv *ra) {
assert(ra);
- ra->timeout_event_source =
- sd_event_source_unref(ra->timeout_event_source);
+ (void) event_source_disable(ra->timeout_event_source);
ra->recv_event_source =
sd_event_source_unref(ra->recv_event_source);
@@ -86,26 +88,10 @@ static void radv_reset(sd_radv *ra) {
ra->ra_sent = 0;
}
-_public_ sd_radv *sd_radv_ref(sd_radv *ra) {
+static sd_radv *radv_free(sd_radv *ra) {
if (!ra)
return NULL;
- assert(ra->n_ref > 0);
- ra->n_ref++;
-
- return ra;
-}
-
-_public_ sd_radv *sd_radv_unref(sd_radv *ra) {
- if (!ra)
- return NULL;
-
- assert(ra->n_ref > 0);
- ra->n_ref--;
-
- if (ra->n_ref > 0)
- return NULL;
-
while (ra->prefixes) {
sd_radv_prefix *p = ra->prefixes;
@@ -116,6 +102,8 @@ _public_ sd_radv *sd_radv_unref(sd_radv *ra) {
free(ra->rdnss);
free(ra->dnssl);
+ ra->timeout_event_source = sd_event_source_unref(ra->timeout_event_source);
+
radv_reset(ra);
sd_radv_detach_event(ra);
@@ -125,9 +113,9 @@ _public_ sd_radv *sd_radv_unref(sd_radv *ra) {
return mfree(ra);
}
-static int radv_send(sd_radv *ra, const struct in6_addr *dst,
- const uint32_t router_lifetime) {
- static const struct ether_addr mac_zero = {};
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv, sd_radv, radv_free);
+
+static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_lifetime) {
sd_radv_prefix *p;
struct sockaddr_in6 dst_addr = {
.sin6_family = AF_INET6,
@@ -159,35 +147,31 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst,
usec_t time_now;
int r;
+ assert(ra);
+
r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
return r;
- if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst))
+ if (dst && !IN6_IS_ADDR_UNSPECIFIED(dst))
dst_addr.sin6_addr = *dst;
adv.nd_ra_type = ND_ROUTER_ADVERT;
adv.nd_ra_curhoplimit = ra->hop_limit;
adv.nd_ra_flags_reserved = ra->flags;
adv.nd_ra_router_lifetime = htobe16(router_lifetime);
- iov[msg.msg_iovlen].iov_base = &adv;
- iov[msg.msg_iovlen].iov_len = sizeof(adv);
- msg.msg_iovlen++;
+ iov[msg.msg_iovlen++] = IOVEC_MAKE(&adv, sizeof(adv));
/* MAC address is optional, either because the link does not use L2
addresses or load sharing is desired. See RFC 4861, Section 4.2 */
- if (memcmp(&mac_zero, &ra->mac_addr, sizeof(mac_zero))) {
+ if (!ether_addr_is_null(&ra->mac_addr)) {
opt_mac.slladdr = ra->mac_addr;
- iov[msg.msg_iovlen].iov_base = &opt_mac;
- iov[msg.msg_iovlen].iov_len = sizeof(opt_mac);
- msg.msg_iovlen++;
+ iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mac, sizeof(opt_mac));
}
if (ra->mtu) {
opt_mtu.nd_opt_mtu_mtu = htobe32(ra->mtu);
- iov[msg.msg_iovlen].iov_base = &opt_mtu;
- iov[msg.msg_iovlen].iov_len = sizeof(opt_mtu);
- msg.msg_iovlen++;
+ iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mtu, sizeof(opt_mtu));
}
LIST_FOREACH(prefix, p, ra->prefixes) {
@@ -203,22 +187,14 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst,
else
p->opt.preferred_lifetime = htobe32((p->preferred_until - time_now) / USEC_PER_SEC);
}
- iov[msg.msg_iovlen].iov_base = &p->opt;
- iov[msg.msg_iovlen].iov_len = sizeof(p->opt);
- msg.msg_iovlen++;
+ iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt));
}
- if (ra->rdnss) {
- iov[msg.msg_iovlen].iov_base = ra->rdnss;
- iov[msg.msg_iovlen].iov_len = ra->rdnss->length * 8;
- msg.msg_iovlen++;
- }
+ if (ra->rdnss)
+ iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->rdnss, ra->rdnss->length * 8);
- if (ra->dnssl) {
- iov[msg.msg_iovlen].iov_base = ra->dnssl;
- iov[msg.msg_iovlen].iov_len = ra->dnssl->length * 8;
- msg.msg_iovlen++;
- }
+ if (ra->dnssl)
+ iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->dnssl, ra->dnssl->length * 8);
if (sendmsg(ra->fd, &msg, 0) < 0)
return -errno;
@@ -240,13 +216,12 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat
assert(ra->event);
buflen = next_datagram_size_fd(fd);
-
- if ((unsigned) buflen < sizeof(struct nd_router_solicit))
- return log_radv("Too short packet received");
+ if (buflen < 0)
+ return (int) buflen;
buf = new0(char, buflen);
if (!buf)
- return 0;
+ return -ENOMEM;
r = icmp6_receive(fd, buf, buflen, &src, &timestamp);
if (r < 0) {
@@ -264,21 +239,29 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat
log_radv("Received invalid source address from ICMPv6 socket. Ignoring.");
break;
+ case -EAGAIN: /* ignore spurious wakeups */
+ break;
+
default:
- log_radv_warning_errno(r, "Error receiving from ICMPv6 socket: %m");
+ log_radv_errno(r, "Unexpected error receiving from ICMPv6 socket: %m");
break;
}
return 0;
}
+ if ((size_t) buflen < sizeof(struct nd_router_solicit)) {
+ log_radv("Too short packet received");
+ return 0;
+ }
+
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr);
r = radv_send(ra, &src, ra->lifetime);
if (r < 0)
- log_radv_warning_errno(r, "Unable to send solicited Router Advertisement to %s: %m", addr);
+ log_radv_errno(r, "Unable to send solicited Router Advertisement to %s: %m", strnull(addr));
else
- log_radv("Sent solicited Router Advertisement to %s", addr);
+ log_radv("Sent solicited Router Advertisement to %s", strnull(addr));
return 0;
}
@@ -301,15 +284,13 @@ static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
assert(ra);
assert(ra->event);
- ra->timeout_event_source = sd_event_source_unref(ra->timeout_event_source);
-
r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
goto fail;
r = radv_send(ra, NULL, ra->lifetime);
if (r < 0)
- log_radv_warning_errno(r, "Unable to send Router Advertisement: %m");
+ log_radv_errno(r, "Unable to send Router Advertisement: %m");
/* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
if (ra->ra_sent < SD_RADV_MAX_INITIAL_RTR_ADVERTISEMENTS) {
@@ -323,28 +304,20 @@ static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
format_timespan(time_string, FORMAT_TIMESPAN_MAX,
timeout, USEC_PER_SEC));
- r = sd_event_add_time(ra->event, &ra->timeout_event_source,
- clock_boottime_or_monotonic(),
- time_now + timeout, MSEC_PER_SEC,
- radv_timeout, ra);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_priority(ra->timeout_event_source,
- ra->event_priority);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_description(ra->timeout_event_source,
- "radv-timeout");
+ r = event_reset_time(ra->event, &ra->timeout_event_source,
+ clock_boottime_or_monotonic(),
+ time_now + timeout, MSEC_PER_SEC,
+ radv_timeout, ra,
+ ra->event_priority, "radv-timeout", true);
if (r < 0)
goto fail;
ra->ra_sent++;
+ return 0;
+
fail:
- if (r < 0)
- sd_radv_stop(ra);
+ sd_radv_stop(ra);
return 0;
}
@@ -363,7 +336,7 @@ _public_ int sd_radv_stop(sd_radv *ra) {
with zero lifetime */
r = radv_send(ra, NULL, 0);
if (r < 0)
- log_radv_warning_errno(r, "Unable to send last Router Advertisement with router lifetime set to zero: %m");
+ log_radv_errno(r, "Unable to send last Router Advertisement with router lifetime set to zero: %m");
radv_reset(ra);
ra->fd = safe_close(ra->fd);
@@ -373,7 +346,7 @@ _public_ int sd_radv_stop(sd_radv *ra) {
}
_public_ int sd_radv_start(sd_radv *ra) {
- int r = 0;
+ int r;
assert_return(ra, -EINVAL);
assert_return(ra->event, -EINVAL);
@@ -382,20 +355,14 @@ _public_ int sd_radv_start(sd_radv *ra) {
if (ra->state != SD_RADV_STATE_IDLE)
return 0;
- r = sd_event_add_time(ra->event, &ra->timeout_event_source,
- clock_boottime_or_monotonic(), 0, 0,
- radv_timeout, ra);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_priority(ra->timeout_event_source,
- ra->event_priority);
+ r = event_reset_time(ra->event, &ra->timeout_event_source,
+ clock_boottime_or_monotonic(),
+ 0, 0,
+ radv_timeout, ra,
+ ra->event_priority, "radv-timeout", true);
if (r < 0)
goto fail;
- (void) sd_event_source_set_description(ra->timeout_event_source,
- "radv-timeout");
-
r = icmp6_bind_router_advertisement(ra->ifindex);
if (r < 0)
goto fail;
@@ -523,7 +490,7 @@ _public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
return r;
}
-_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic) {
+_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, int dynamic) {
sd_radv_prefix *cur;
int r;
_cleanup_free_ char *addr_p = NULL;
@@ -536,6 +503,10 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic) {
if (!p)
return -EINVAL;
+ /* Refuse prefixes that don't have a prefix set */
+ if (IN6_IS_ADDR_UNSPECIFIED(&p->opt.in6_addr))
+ return -ENOEXEC;
+
LIST_FOREACH(prefix, cur, ra->prefixes) {
r = in_addr_prefix_intersect(AF_INET6,
@@ -718,56 +689,33 @@ _public_ int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime,
}
_public_ int sd_radv_prefix_new(sd_radv_prefix **ret) {
- _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
+ sd_radv_prefix *p;
assert_return(ret, -EINVAL);
- p = new0(sd_radv_prefix, 1);
+ p = new(sd_radv_prefix, 1);
if (!p)
return -ENOMEM;
- p->n_ref = 1;
+ *p = (sd_radv_prefix) {
+ .n_ref = 1,
- p->opt.type = ND_OPT_PREFIX_INFORMATION;
- p->opt.length = (sizeof(p->opt) - 1) /8 + 1;
+ .opt.type = ND_OPT_PREFIX_INFORMATION,
+ .opt.length = (sizeof(p->opt) - 1)/8 + 1,
+ .opt.prefixlen = 64,
- p->opt.prefixlen = 64;
+ /* RFC 4861, Section 6.2.1 */
+ .opt.flags = ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO,
- /* RFC 4861, Section 6.2.1 */
- SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, true);
- SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, true);
- p->opt.preferred_lifetime = htobe32(604800);
- p->opt.valid_lifetime = htobe32(2592000);
-
- LIST_INIT(prefix, p);
-
- *ret = TAKE_PTR(p);
+ .opt.preferred_lifetime = htobe32(604800),
+ .opt.valid_lifetime = htobe32(2592000),
+ };
+ *ret = p;
return 0;
}
-_public_ sd_radv_prefix *sd_radv_prefix_ref(sd_radv_prefix *p) {
- if (!p)
- return NULL;
-
- assert(p->n_ref > 0);
- p->n_ref++;
-
- return p;
-}
-
-_public_ sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *p) {
- if (!p)
- return NULL;
-
- assert(p->n_ref > 0);
- p->n_ref--;
-
- if (p->n_ref > 0)
- return NULL;
-
- return mfree(p);
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_prefix, sd_radv_prefix, mfree);
_public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr,
unsigned char prefixlen) {
diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c
index 079e760996..302eea2c30 100644
--- a/src/libsystemd-network/test-acd.c
+++ b/src/libsystemd-network/test-acd.c
@@ -13,6 +13,7 @@
#include "in-addr-util.h"
#include "netlink-util.h"
+#include "tests.h"
#include "util.h"
static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) {
@@ -83,9 +84,7 @@ static int test_acd(const char *ifname, const char *address) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
if (argc == 3)
return test_acd(argv[1], argv[2]);
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index 0e257633b8..fe6788d91b 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -7,6 +7,7 @@
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
+#include <net/if.h>
#include "sd-dhcp-client.h"
#include "sd-event.h"
@@ -16,6 +17,8 @@
#include "dhcp-internal.h"
#include "dhcp-protocol.h"
#include "fd-util.h"
+#include "random-util.h"
+#include "tests.h"
#include "util.h"
static uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'};
@@ -152,6 +155,35 @@ static void test_checksum(void) {
assert_se(dhcp_packet_checksum((uint8_t*)&buf, 20) == be16toh(0x78ae));
}
+static void test_dhcp_identifier_set_iaid(void) {
+ uint32_t iaid_legacy;
+ be32_t iaid;
+ int ifindex;
+
+ for (;;) {
+ char ifname[IFNAMSIZ];
+
+ /* try to find an ifindex which does not exist. I causes dhcp_identifier_set_iaid()
+ * to hash the MAC address. */
+ pseudo_random_bytes(&ifindex, sizeof(ifindex));
+ if (ifindex > 0 && !if_indextoname(ifindex, ifname))
+ break;
+ }
+
+ assert_se(dhcp_identifier_set_iaid(ifindex, mac_addr, sizeof(mac_addr), true, &iaid_legacy) >= 0);
+ assert_se(dhcp_identifier_set_iaid(ifindex, mac_addr, sizeof(mac_addr), false, &iaid) >= 0);
+
+ /* we expect, that the MAC address was hashed. The legacy value is in native
+ * endianness. */
+ assert_se(iaid_legacy == 0x8dde4ba8u);
+ assert_se(iaid == htole32(0x8dde4ba8u));
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ assert_se(iaid == iaid_legacy);
+#else
+ assert_se(iaid == __bswap_32(iaid_legacy));
+#endif
+}
+
static int check_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
switch(code) {
case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
@@ -161,7 +193,7 @@ static int check_options(uint8_t code, uint8_t len, const void *option, void *us
size_t duid_len;
assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0);
- assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, &iaid) >= 0);
+ assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, true, &iaid) >= 0);
assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len);
assert_se(len == 19);
@@ -231,7 +263,7 @@ int dhcp_network_bind_raw_socket(
const uint8_t *addr, size_t addr_len,
uint16_t arp_type, uint16_t port) {
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
return -errno;
return test_fd[0];
@@ -240,7 +272,7 @@ int dhcp_network_bind_raw_socket(
int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
int fd;
- fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (fd < 0)
return -errno;
@@ -524,15 +556,14 @@ static void test_addr_acq(sd_event *e) {
int main(int argc, char *argv[]) {
_cleanup_(sd_event_unrefp) sd_event *e;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
assert_se(sd_event_new(&e) >= 0);
test_request_basic(e);
test_request_anonymize(e);
test_checksum();
+ test_dhcp_identifier_set_iaid();
test_discover_message(e);
test_addr_acq(e);
diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
index 815b11e997..ea998939bc 100644
--- a/src/libsystemd-network/test-dhcp-server.c
+++ b/src/libsystemd-network/test-dhcp-server.c
@@ -9,6 +9,7 @@
#include "sd-event.h"
#include "dhcp-server-internal.h"
+#include "tests.h"
static void test_pool(struct in_addr *address, unsigned size, int ret) {
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
@@ -54,9 +55,8 @@ static int test_basic(sd_event *event) {
test_pool(&address_lo, 1, 0);
r = sd_dhcp_server_start(server);
-
if (r == -EPERM)
- return EXIT_TEST_SKIP;
+ return log_info_errno(r, "sd_dhcp_server_start failed: %m");
assert_se(r >= 0);
assert_se(sd_dhcp_server_start(server) == -EBUSY);
@@ -229,15 +229,13 @@ int main(int argc, char *argv[]) {
_cleanup_(sd_event_unrefp) sd_event *e;
int r;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
assert_se(sd_event_new(&e) >= 0);
r = test_basic(e);
if (r != 0)
- return r;
+ return log_tests_skipped("cannot start dhcp server");
test_message_handler();
test_client_id_hash();
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 27c0002fe2..fa94b3cb75 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -19,14 +19,14 @@
#include "fd-util.h"
#include "macro.h"
#include "socket-util.h"
+#include "tests.h"
+#include "util.h"
#include "virt.h"
static struct ether_addr mac_addr = {
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
};
-static bool verbose = true;
-
static sd_event_source *hangcheck;
static int test_dhcp_fd[2];
static int test_index = 42;
@@ -36,9 +36,9 @@ static uint8_t test_duid[14] = { };
static int test_client_basic(sd_event *e) {
sd_dhcp6_client *client;
+ int v;
- if (verbose)
- printf("* %s\n", __FUNCTION__);
+ log_debug("/* %s */", __func__);
assert_se(sd_dhcp6_client_new(&client) >= 0);
assert_se(client);
@@ -67,6 +67,36 @@ static int test_client_basic(sd_event *e) {
assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
+ assert_se(sd_dhcp6_client_set_information_request(client, 1) >= 0);
+ v = 0;
+ assert_se(sd_dhcp6_client_get_information_request(client, &v) >= 0);
+ assert_se(v);
+ assert_se(sd_dhcp6_client_set_information_request(client, 0) >= 0);
+ v = 42;
+ assert_se(sd_dhcp6_client_get_information_request(client, &v) >= 0);
+ assert_se(v == 0);
+
+ v = 0;
+ assert_se(sd_dhcp6_client_get_address_request(client, &v) >= 0);
+ assert_se(v);
+ v = 0;
+ assert_se(sd_dhcp6_client_set_address_request(client, 1) >= 0);
+ assert_se(sd_dhcp6_client_get_address_request(client, &v) >= 0);
+ assert_se(v);
+ v = 42;
+ assert_se(sd_dhcp6_client_set_address_request(client, 1) >= 0);
+ assert_se(sd_dhcp6_client_get_address_request(client, &v) >= 0);
+ assert_se(v);
+
+ assert_se(sd_dhcp6_client_set_address_request(client, 1) >= 0);
+ assert_se(sd_dhcp6_client_set_prefix_delegation(client, 1) >= 0);
+ v = 0;
+ assert_se(sd_dhcp6_client_get_address_request(client, &v) >= 0);
+ assert_se(v);
+ v = 0;
+ assert_se(sd_dhcp6_client_get_prefix_delegation(client, &v) >= 0);
+ assert_se(v);
+
assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
assert_se(sd_dhcp6_client_detach_event(client) >= 0);
@@ -97,8 +127,7 @@ static int test_option(sd_event *e) {
size_t zero = 0, pos = 3;
size_t buflen = sizeof(packet), outlen = sizeof(result);
- if (verbose)
- printf("* %s\n", __FUNCTION__);
+ log_debug("/* %s */", __func__);
assert_se(buflen == outlen);
@@ -211,8 +240,7 @@ static int test_option_status(sd_event *e) {
DHCP6IA ia, pd;
int r = 0;
- if (verbose)
- printf("* %s\n", __FUNCTION__);
+ log_debug("/* %s */", __func__);
zero(ia);
option = (DHCP6Option *)option1;
@@ -345,8 +373,7 @@ static int test_advertise_option(sd_event *e) {
struct in6_addr *addrs;
char **domains;
- if (verbose)
- printf("* %s\n", __FUNCTION__);
+ log_debug("/* %s */", __func__);
assert_se(len >= sizeof(DHCP6Message));
@@ -493,6 +520,8 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
struct in6_addr *addrs;
char **domains;
+ log_debug("/* %s */", __func__);
+
assert_se(e);
assert_se(event == SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
@@ -510,9 +539,6 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
- if (verbose)
- printf(" got DHCPv6 event %d\n", event);
-
sd_event_exit(e, 0);
}
@@ -543,8 +569,9 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
be32_t val;
uint32_t lt_pref, lt_valid;
- assert_se(request->type == DHCP6_REQUEST);
+ log_debug("/* %s */", __func__);
+ assert_se(request->type == DHCP6_REQUEST);
assert_se(dhcp6_lease_new(&lease) >= 0);
len -= sizeof(DHCP6Message);
@@ -651,6 +678,8 @@ static int test_client_verify_solicit(DHCP6Message *solicit, size_t len) {
found_elapsed_time = false, found_fqdn = false;
size_t pos = 0;
+ log_debug("/* %s */", __func__);
+
assert_se(solicit->type == DHCP6_SOLICIT);
len -= sizeof(DHCP6Message);
@@ -718,6 +747,8 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event,
struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
char **domains;
+ log_debug("/* %s */", __func__);
+
assert_se(e);
assert_se(event == SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
@@ -733,9 +764,6 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event,
assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1);
assert_se(!memcmp(addrs, &msg_advertise[159], 16));
- if (verbose)
- printf(" got DHCPv6 event %d\n", event);
-
assert_se(sd_dhcp6_client_set_information_request(client, false) == -EBUSY);
assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0);
assert_se(sd_dhcp6_client_stop(client) >= 0);
@@ -759,8 +787,9 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
struct in6_addr addr;
uint32_t lt_pref, lt_valid;
- assert_se(information_request->type == DHCP6_INFORMATION_REQUEST);
+ log_debug("/* %s */", __func__);
+ assert_se(information_request->type == DHCP6_INFORMATION_REQUEST);
assert_se(dhcp6_lease_new(&lease) >= 0);
len -= sizeof(DHCP6Message);
@@ -824,7 +853,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
assert_se(server_address);
assert_se(packet);
assert_se(len > sizeof(DHCP6Message) + 4);
-
assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
message = (DHCP6Message *)packet;
@@ -851,7 +879,7 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
assert_se(index == test_index);
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) < 0)
return -errno;
return test_dhcp_fd[0];
@@ -861,10 +889,9 @@ static int test_client_solicit(sd_event *e) {
sd_dhcp6_client *client;
usec_t time_now = now(clock_boottime_or_monotonic());
struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
- int val = true;
+ int val;
- if (verbose)
- printf("* %s\n", __FUNCTION__);
+ log_debug("/* %s */", __func__);
assert_se(sd_dhcp6_client_new(&client) >= 0);
assert_se(client);
@@ -878,10 +905,10 @@ static int test_client_solicit(sd_event *e) {
assert_se(sd_dhcp6_client_set_fqdn(client, "host.lab.intra") == 1);
assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
- assert_se(val == false);
- assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0);
+ assert_se(val == 0);
+ assert_se(sd_dhcp6_client_set_information_request(client, 42) >= 0);
assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
- assert_se(val == true);
+ assert_se(val);
assert_se(sd_dhcp6_client_set_callback(client,
test_client_information_cb, e) >= 0);
@@ -910,9 +937,7 @@ int main(int argc, char *argv[]) {
assert_se(sd_event_new(&e) >= 0);
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_client_basic(e);
test_option(e);
diff --git a/src/libsystemd-network/test-ipv4ll-manual.c b/src/libsystemd-network/test-ipv4ll-manual.c
index 125133f039..fd827ff401 100644
--- a/src/libsystemd-network/test-ipv4ll-manual.c
+++ b/src/libsystemd-network/test-ipv4ll-manual.c
@@ -15,6 +15,7 @@
#include "netlink-util.h"
#include "parse-util.h"
#include "string-util.h"
+#include "tests.h"
#include "util.h"
static void ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
@@ -95,9 +96,7 @@ static int test_ll(const char *ifname, const char *seed) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
if (argc == 2)
return test_ll(argv[1], NULL);
diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c
index ee9cce02a8..2e1488cb0a 100644
--- a/src/libsystemd-network/test-ipv4ll.c
+++ b/src/libsystemd-network/test-ipv4ll.c
@@ -15,6 +15,7 @@
#include "arp-util.h"
#include "fd-util.h"
#include "socket-util.h"
+#include "tests.h"
#include "util.h"
static bool verbose = false;
@@ -78,7 +79,7 @@ int arp_send_announcement(int fd, int ifindex,
}
int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) {
- if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0)
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
return -errno;
return test_fd[0];
@@ -193,9 +194,7 @@ static void test_basic_request(sd_event *e) {
int main(int argc, char *argv[]) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
assert_se(sd_event_new(&e) >= 0);
diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c
index ac8ba2b707..cb4545d901 100644
--- a/src/libsystemd-network/test-lldp.c
+++ b/src/libsystemd-network/test-lldp.c
@@ -24,7 +24,7 @@ static int test_fd[2] = { -1, -1 };
static int lldp_handler_calls;
int lldp_network_bind_raw_socket(int ifindex) {
- if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0)
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
return -errno;
return test_fd[0];
@@ -229,6 +229,135 @@ static void test_receive_oui_packet(sd_event *e) {
assert_se(stop_lldp(lldp) == 0);
}
+static void test_multiple_neighbors_sorted(sd_event *e) {
+
+ static const uint8_t frame1[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */
+ 0x04, 0x04, 0x02, '2', '/', '3', /* Port component: "2/3" */
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
+ 0x00, 0x00 /* End Of LLDPDU */
+ };
+ static const uint8_t frame2[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x04, 0x01, '2', '/', '1', /* Chassis component: "2/1" */
+ 0x04, 0x04, 0x02, '1', '/', '3', /* Port component: "1/3" */
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
+ 0x00, 0x00 /* End Of LLDPDU */
+ };
+ static const uint8_t frame3[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x05, 0x01, '2', '/', '1', '0', /* Chassis component: "2/10" */
+ 0x04, 0x04, 0x02, '1', '/', '0', /* Port component: "1/0" */
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
+ 0x00, 0x00 /* End Of LLDPDU */
+ };
+ static const uint8_t frame4[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x05, 0x01, '2', '/', '1', '9', /* Chassis component: "2/19" */
+ 0x04, 0x04, 0x02, '1', '/', '0', /* Port component: "1/0" */
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
+ 0x00, 0x00 /* End Of LLDPDU */
+ };
+ static const uint8_t frame5[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */
+ 0x04, 0x05, 0x02, '2', '/', '1', '0', /* Port component: "2/10" */
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
+ 0x00, 0x00 /* End Of LLDPDU */
+ };
+ static const uint8_t frame6[] = {
+ /* Ethernet header */
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */
+ 0x88, 0xcc, /* Ethertype */
+ /* LLDP mandatory TLVs */
+ 0x02, 0x04, 0x01, '1', '/', '2', /* Chassis component: "1/2" */
+ 0x04, 0x05, 0x02, '2', '/', '3', '9', /* Port component: "2/10" */
+ 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */
+ 0x00, 0x00 /* End Of LLDPDU */
+ };
+ static const char* expected[] = {
+ /* ordered pairs of Chassis+Port */
+ "1/2", "2/10",
+ "1/2", "2/3",
+ "1/2", "2/39",
+ "2/1", "1/3",
+ "2/10", "1/0",
+ "2/19", "1/0",
+ };
+
+ sd_lldp *lldp;
+ sd_lldp_neighbor **neighbors;
+ int i;
+ uint8_t type;
+ const void *data;
+ size_t length, expected_length;
+ uint16_t ttl;
+
+ lldp_handler_calls = 0;
+ assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
+
+ assert_se(write(test_fd[1], frame1, sizeof(frame1)) == sizeof(frame1));
+ sd_event_run(e, 0);
+ assert_se(write(test_fd[1], frame2, sizeof(frame2)) == sizeof(frame2));
+ sd_event_run(e, 0);
+ assert_se(write(test_fd[1], frame3, sizeof(frame3)) == sizeof(frame3));
+ sd_event_run(e, 0);
+ assert_se(write(test_fd[1], frame4, sizeof(frame4)) == sizeof(frame4));
+ sd_event_run(e, 0);
+ assert_se(write(test_fd[1], frame5, sizeof(frame5)) == sizeof(frame5));
+ sd_event_run(e, 0);
+ assert_se(write(test_fd[1], frame6, sizeof(frame6)) == sizeof(frame6));
+ sd_event_run(e, 0);
+ assert_se(lldp_handler_calls == 6);
+
+ assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 6);
+
+ for (i = 0; i < 6; i++) {
+ assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[i], &type, &data, &length) == 0);
+ assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT);
+ expected_length = strlen(expected[2 * i]);
+ assert_se(length == expected_length);
+ assert_se(memcmp(data, expected[2 * i], expected_length) == 0);
+
+ assert_se(sd_lldp_neighbor_get_port_id(neighbors[i], &type, &data, &length) == 0);
+ assert_se(type == SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT);
+ expected_length = strlen(expected[2 * i + 1]);
+ assert_se(length == expected_length);
+ assert_se(memcmp(data, expected[2 * i + 1], expected_length) == 0);
+
+ assert_se(sd_lldp_neighbor_get_ttl(neighbors[i], &ttl) == 0);
+ assert_se(ttl == 120);
+ }
+
+ for (i = 0; i < 6; i++)
+ sd_lldp_neighbor_unref(neighbors[i]);
+ free(neighbors);
+
+ assert_se(stop_lldp(lldp) == 0);
+}
+
int main(int argc, char *argv[]) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
@@ -239,6 +368,7 @@ int main(int argc, char *argv[]) {
test_receive_basic_packet(e);
test_receive_incomplete_packet(e);
test_receive_oui_packet(e);
+ test_multiple_neighbors_sorted(e);
return 0;
}
diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c
index d5a0237663..c4c1c81140 100644
--- a/src/libsystemd-network/test-ndisc-ra.c
+++ b/src/libsystemd-network/test-ndisc-ra.c
@@ -13,6 +13,7 @@
#include "icmp6-util.h"
#include "socket-util.h"
#include "strv.h"
+#include "tests.h"
static struct ether_addr mac_addr = {
.ether_addr_octet = { 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53 }
@@ -292,11 +293,11 @@ static void test_ra(void) {
sd_event *e;
sd_radv *ra;
usec_t time_now = now(clock_boottime_or_monotonic());
- unsigned int i;
+ unsigned i;
printf("* %s\n", __FUNCTION__);
- assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, test_fd) >= 0);
+ assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
assert_se(sd_event_new(&e) >= 0);
@@ -357,9 +358,7 @@ static void test_ra(void) {
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_radv_prefix();
test_radv();
diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c
index b9d0e7dc90..caf94d10f8 100644
--- a/src/libsystemd-network/test-ndisc-rs.c
+++ b/src/libsystemd-network/test-ndisc-rs.c
@@ -14,6 +14,7 @@
#include "socket-util.h"
#include "strv.h"
#include "ndisc-internal.h"
+#include "tests.h"
static struct ether_addr mac_addr = {
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
@@ -175,7 +176,7 @@ static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
int icmp6_bind_router_solicitation(int index) {
assert_se(index == 42);
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) < 0)
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
return -errno;
return test_fd[0];
@@ -407,9 +408,7 @@ static void test_timeout(void) {
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_rs();
test_timeout();
diff --git a/src/libsystemd/disable-mempool.c b/src/libsystemd/disable-mempool.c
new file mode 100644
index 0000000000..034bd24dc4
--- /dev/null
+++ b/src/libsystemd/disable-mempool.c
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "mempool.h"
+
+const bool mempool_use_allowed = false;
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 1eec17db50..96e6347795 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -570,3 +570,104 @@ global:
sd_event_source_set_destroy_callback;
sd_event_source_get_destroy_callback;
} LIBSYSTEMD_238;
+
+LIBSYSTEMD_240 {
+global:
+ sd_bus_message_readv;
+ sd_bus_set_method_call_timeout;
+ sd_bus_get_method_call_timeout;
+
+ sd_bus_error_move;
+
+ sd_bus_set_close_on_exit;
+ sd_bus_get_close_on_exit;
+
+ sd_device_ref;
+ sd_device_unref;
+
+ sd_device_new_from_syspath;
+ sd_device_new_from_devnum;
+ sd_device_new_from_subsystem_sysname;
+ sd_device_new_from_device_id;
+
+ sd_device_get_parent;
+ sd_device_get_parent_with_subsystem_devtype;
+
+ sd_device_get_syspath;
+ sd_device_get_subsystem;
+ sd_device_get_devtype;
+ sd_device_get_devnum;
+ sd_device_get_ifindex;
+ sd_device_get_driver;
+ sd_device_get_devpath;
+ sd_device_get_devname;
+ sd_device_get_sysname;
+ sd_device_get_sysnum;
+
+ sd_device_get_is_initialized;
+ sd_device_get_usec_since_initialized;
+
+ sd_device_get_tag_first;
+ sd_device_get_tag_next;
+ sd_device_get_devlink_first;
+ sd_device_get_devlink_next;
+ sd_device_get_property_first;
+ sd_device_get_property_next;
+ sd_device_get_sysattr_first;
+ sd_device_get_sysattr_next;
+
+ sd_device_has_tag;
+ sd_device_get_property_value;
+ sd_device_get_sysattr_value;
+
+ sd_device_set_sysattr_value;
+
+ sd_device_enumerator_new;
+ sd_device_enumerator_ref;
+ sd_device_enumerator_unref;
+
+ sd_device_enumerator_get_device_first;
+ sd_device_enumerator_get_device_next;
+ sd_device_enumerator_get_subsystem_first;
+ sd_device_enumerator_get_subsystem_next;
+
+ sd_device_enumerator_add_match_subsystem;
+ sd_device_enumerator_add_match_sysattr;
+ sd_device_enumerator_add_match_property;
+ sd_device_enumerator_add_match_sysname;
+ sd_device_enumerator_add_match_tag;
+ sd_device_enumerator_add_match_parent;
+ sd_device_enumerator_allow_uninitialized;
+
+ sd_hwdb_ref;
+ sd_hwdb_unref;
+
+ sd_hwdb_new;
+
+ sd_hwdb_get;
+
+ sd_hwdb_seek;
+ sd_hwdb_enumerate;
+
+ sd_id128_get_boot_app_specific;
+
+ sd_device_monitor_new;
+ sd_device_monitor_ref;
+ sd_device_monitor_unref;
+
+ sd_device_monitor_set_receive_buffer_size;
+ sd_device_monitor_attach_event;
+ sd_device_monitor_detach_event;
+ sd_device_monitor_get_event;
+ sd_device_monitor_get_event_source;
+ sd_device_monitor_start;
+ sd_device_monitor_stop;
+
+ sd_device_monitor_filter_add_match_subsystem_devtype;
+ sd_device_monitor_filter_add_match_tag;
+ sd_device_monitor_filter_update;
+ sd_device_monitor_filter_remove;
+
+ sd_event_source_get_floating;
+ sd_event_source_set_floating;
+} LIBSYSTEMD_239;
diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build
index 070fd6c633..05d4ea0e7f 100644
--- a/src/libsystemd/meson.build
+++ b/src/libsystemd/meson.build
@@ -6,9 +6,16 @@ id128_sources = files('''
sd-id128/sd-id128.c
'''.split())
-sd_daemon_c = files('sd-daemon/sd-daemon.c')
-sd_event_c = files('sd-event/sd-event.c')
-sd_login_c = files('sd-login/sd-login.c')
+sd_daemon_sources = files('sd-daemon/sd-daemon.c')
+
+sd_event_sources = files('''
+ sd-event/event-source.h
+ sd-event/event-util.c
+ sd-event/event-util.h
+ sd-event/sd-event.c
+'''.split())
+
+sd_login_sources = files('sd-login/sd-login.c')
libsystemd_sources = files('''
sd-bus/bus-common-errors.c
@@ -53,11 +60,14 @@ libsystemd_sources = files('''
sd-device/device-enumerator-private.h
sd-device/device-enumerator.c
sd-device/device-internal.h
+ sd-device/device-monitor-private.h
+ sd-device/device-monitor.c
sd-device/device-private.c
sd-device/device-private.h
sd-device/device-util.h
sd-device/sd-device.c
sd-hwdb/hwdb-internal.h
+ sd-hwdb/hwdb-util.c
sd-hwdb/hwdb-util.h
sd-hwdb/sd-hwdb.c
sd-netlink/generic-netlink.c
@@ -65,6 +75,8 @@ libsystemd_sources = files('''
sd-netlink/local-addresses.h
sd-netlink/netlink-internal.h
sd-netlink/netlink-message.c
+ sd-netlink/netlink-slot.c
+ sd-netlink/netlink-slot.h
sd-netlink/netlink-socket.c
sd-netlink/netlink-types.c
sd-netlink/netlink-types.h
@@ -76,9 +88,12 @@ libsystemd_sources = files('''
sd-network/network-util.h
sd-network/sd-network.c
sd-path/sd-path.c
+ sd-resolve/resolve-private.h
sd-resolve/sd-resolve.c
sd-utf8/sd-utf8.c
-'''.split()) + id128_sources + sd_daemon_c + sd_event_c + sd_login_c
+'''.split()) + id128_sources + sd_daemon_sources + sd_event_sources + sd_login_sources
+
+disable_mempool_c = files('disable-mempool.c')
libsystemd_c_args = ['-fvisibility=default']
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index ff0790bf5a..6e5fe00e06 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -41,6 +41,9 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER_MAPPING, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_GROUP_MAPPING, ENXIO),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, ENOENT),
+ SD_BUS_ERROR_MAP(BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE, EMEDIUMTYPE),
+
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SESSION_FOR_PID, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER, ENXIO),
@@ -71,6 +74,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_LINK, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DNSSD_SERVICE, ENOENT),
+ SD_BUS_ERROR_MAP(BUS_ERROR_DNSSD_SERVICE_EXISTS, EEXIST),
SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "FORMERR", EBADMSG),
SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "SERVFAIL", EHOSTDOWN),
@@ -94,5 +99,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRODUCT_UUID, EOPNOTSUPP),
+
SD_BUS_ERROR_MAP_END
};
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index 3945c7f6ac..8339feb768 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include "bus-error.h"
#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit"
@@ -40,6 +37,7 @@
#define BUS_ERROR_NO_SUCH_GROUP_MAPPING "org.freedesktop.machine1.NoSuchGroupMapping"
#define BUS_ERROR_NO_SUCH_PORTABLE_IMAGE "org.freedesktop.portable1.NoSuchImage"
+#define BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE "org.freedesktop.portable1.BadImageType"
#define BUS_ERROR_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession"
#define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID"
@@ -78,4 +76,6 @@
#define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer"
#define BUS_ERROR_TRANSFER_IN_PROGRESS "org.freedesktop.import1.TransferInProgress"
+#define BUS_ERROR_NO_PRODUCT_UUID "org.freedesktop.hostname1.NoProductUUID"
+
BUS_ERROR_MAP_ELF_USE(bus_common_errors);
diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c
index f50274a6a4..2cfeefc2c3 100644
--- a/src/libsystemd/sd-bus/bus-container.c
+++ b/src/libsystemd/sd-bus/bus-container.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <fcntl.h>
#include <unistd.h>
@@ -47,51 +45,27 @@ int bus_container_connect_socket(sd_bus *b) {
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
return -errno;
- r = safe_fork("(sd-buscntr)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ r = namespace_fork("(sd-buscntrns)", "(sd-buscntr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child);
if (r < 0)
return r;
if (r == 0) {
- pid_t grandchild;
-
pair[0] = safe_close(pair[0]);
- r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- /* We just changed PID namespace, however it will only
- * take effect on the children we now fork. Hence,
- * let's fork another time, and connect from this
- * grandchild, so that SO_PEERCRED of our connection
- * comes from a process from within the container, and
- * not outside of it */
-
- r = safe_fork("(sd-buscntr2)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &grandchild);
- if (r < 0)
+ r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
+ if (r < 0) {
+ /* Try to send error up */
+ error_buf = errno;
+ (void) write(pair[1], &error_buf, sizeof(error_buf));
_exit(EXIT_FAILURE);
- if (r == 0) {
-
- r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
- if (r < 0) {
- /* Try to send error up */
- error_buf = errno;
- (void) write(pair[1], &error_buf, sizeof(error_buf));
- _exit(EXIT_FAILURE);
- }
-
- _exit(EXIT_SUCCESS);
}
- r = wait_for_terminate_and_check("(sd-buscntr2)", grandchild, 0);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- _exit(r);
+ _exit(EXIT_SUCCESS);
}
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-buscntr)", child, 0);
+ r = wait_for_terminate_and_check("(sd-buscntrns)", child, 0);
if (r < 0)
return r;
if (r != EXIT_SUCCESS)
diff --git a/src/libsystemd/sd-bus/bus-container.h b/src/libsystemd/sd-bus/bus-container.h
index dd115b4e24..f6ef688032 100644
--- a/src/libsystemd/sd-bus/bus-container.h
+++ b/src/libsystemd/sd-bus/bus-container.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include "sd-bus.h"
int bus_container_connect_socket(sd_bus *b);
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index 18a2cc2c9b..7775d2b376 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#if HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
@@ -432,7 +430,7 @@ _public_ int sd_bus_get_name_creds(
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL;
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
- const char *unique = NULL;
+ const char *unique;
pid_t pid = 0;
int r;
@@ -461,9 +459,12 @@ _public_ int sd_bus_get_name_creds(
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
- /* Only query the owner if the caller wants to know it or if
- * the caller just wants to check whether a name exists */
- if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) {
+ /* If the name is unique anyway, we can use it directly */
+ unique = name[0] == ':' ? name : NULL;
+
+ /* Only query the owner if the caller wants to know it and the name is not unique anyway, or if the caller just
+ * wants to check whether a name exists */
+ if ((FLAGS_SET(mask, SD_BUS_CREDS_UNIQUE_NAME) && !unique) || mask == 0) {
r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
@@ -485,6 +486,7 @@ _public_ int sd_bus_get_name_creds(
if (mask != 0) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bool need_pid, need_uid, need_selinux, need_separate_calls;
+
c = bus_creds_new();
if (!c)
return -ENOMEM;
@@ -663,7 +665,7 @@ _public_ int sd_bus_get_name_creds(
NULL,
&reply,
"s",
- unique ? unique : name);
+ unique ?: name);
if (r < 0)
return r;
@@ -690,7 +692,7 @@ _public_ int sd_bus_get_name_creds(
&error,
&reply,
"s",
- unique ? unique : name);
+ unique ?: name);
if (r < 0) {
if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
return r;
@@ -701,7 +703,7 @@ _public_ int sd_bus_get_name_creds(
if (r < 0)
return r;
- c->label = strndup(p, sz);
+ c->label = memdup_suffix0(p, sz);
if (!c->label)
return -ENOMEM;
diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h
index 9017297496..3fb52b67c6 100644
--- a/src/libsystemd/sd-bus/bus-control.h
+++ b/src/libsystemd/sd-bus/bus-control.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include "sd-bus.h"
int bus_add_match_internal(sd_bus *bus, const char *match);
diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c
index 41910515db..c4d4016fc0 100644
--- a/src/libsystemd/sd-bus/bus-convenience.c
+++ b/src/libsystemd/sd-bus/bus-convenience.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "bus-internal.h"
#include "bus-message.h"
diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c
index aae9fcd58b..81d97ff968 100644
--- a/src/libsystemd/sd-bus/bus-creds.c
+++ b/src/libsystemd/sd-bus/bus-creds.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <linux/capability.h>
#include <stdlib.h>
@@ -651,19 +649,22 @@ _public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) {
return 0;
}
-static int has_cap(sd_bus_creds *c, unsigned offset, int capability) {
+static int has_cap(sd_bus_creds *c, size_t offset, int capability) {
+ unsigned long lc;
size_t sz;
assert(c);
assert(capability >= 0);
assert(c->capability);
- if ((unsigned) capability > cap_last_cap())
+ lc = cap_last_cap();
+
+ if ((unsigned long) capability > lc)
return 0;
- sz = DIV_ROUND_UP(cap_last_cap(), 32U);
+ sz = DIV_ROUND_UP(lc, 32LU);
- return !!(c->capability[offset * sz + CAP_TO_INDEX(capability)] & CAP_TO_MASK(capability));
+ return !!(c->capability[offset * sz + CAP_TO_INDEX((uint32_t) capability)] & CAP_TO_MASK_CORRECTED((uint32_t) capability));
}
_public_ int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability) {
@@ -802,10 +803,15 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
else if (!IN_SET(errno, EPERM, EACCES))
return -errno;
} else {
- char line[LINE_MAX];
- FOREACH_LINE(line, f, return -errno) {
- truncate_nl(line);
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
if (missing & SD_BUS_CREDS_PPID) {
p = startswith(line, "PPid:");
diff --git a/src/libsystemd/sd-bus/bus-creds.h b/src/libsystemd/sd-bus/bus-creds.h
index 7b77a1d735..508ef9d352 100644
--- a/src/libsystemd/sd-bus/bus-creds.h
+++ b/src/libsystemd/sd-bus/bus-creds.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include <stdbool.h>
#include "sd-bus.h"
diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c
index 3a28c7c6e3..9a6a81d7aa 100644
--- a/src/libsystemd/sd-bus/bus-dump.c
+++ b/src/libsystemd/sd-bus/bus-dump.c
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
+
+#include <sys/time.h>
#include "alloc-util.h"
#include "bus-dump.h"
@@ -59,8 +59,14 @@ int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) {
"%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%"PRIi64,
m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() :
m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() :
- m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", special_glyph(TRIANGULAR_BULLET), ansi_normal(),
- ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_normal(),
+ m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "",
+ special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET),
+ ansi_normal(),
+
+ ansi_highlight(),
+ bus_message_type_to_string(m->header->type) ?: "(unknown)",
+ ansi_normal(),
+
m->header->endian,
m->header->flags,
m->header->version,
diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h
index 8e47411a45..373a86dd4f 100644
--- a/src/libsystemd/sd-bus/bus-dump.h
+++ b/src/libsystemd/sd-bus/bus-dump.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include <stdbool.h>
#include <stdio.h>
diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c
index ec359ac13c..dc952375b6 100644
--- a/src/libsystemd/sd-bus/bus-error.c
+++ b/src/libsystemd/sd-bus/bus-error.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <errno.h>
#include <stdarg.h>
@@ -56,8 +54,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = {
};
/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */
-extern const sd_bus_error_map __start_BUS_ERROR_MAP[];
-extern const sd_bus_error_map __stop_BUS_ERROR_MAP[];
+extern const sd_bus_error_map __start_SYSTEMD_BUS_ERROR_MAP[];
+extern const sd_bus_error_map __stop_SYSTEMD_BUS_ERROR_MAP[];
/* Additional maps registered with sd_bus_error_add_map() are in this
* NULL terminated array */
@@ -91,9 +89,8 @@ static int bus_error_name_to_errno(const char *name) {
return m->code;
}
- m = __start_BUS_ERROR_MAP;
-#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- while (m < __stop_BUS_ERROR_MAP) {
+ m = ALIGN_TO_PTR(__start_SYSTEMD_BUS_ERROR_MAP, sizeof(void*));
+ while (m < __stop_SYSTEMD_BUS_ERROR_MAP) {
/* For magic ELF error maps, the end marker might
* appear in the middle of things, since multiple maps
* might appear in the same section. Hence, let's skip
@@ -101,7 +98,7 @@ static int bus_error_name_to_errno(const char *name) {
* boundary, which is the selected alignment for the
* arrays. */
if (m->code == BUS_ERROR_MAP_END_MARKER) {
- m = ALIGN8_PTR(m+1);
+ m = ALIGN_TO_PTR(m + 1, sizeof(void*));
continue;
}
@@ -110,7 +107,6 @@ static int bus_error_name_to_errno(const char *name) {
m++;
}
-#endif
return EIO;
}
@@ -207,8 +203,7 @@ _public_ void sd_bus_error_free(sd_bus_error *e) {
free((void*) e->message);
}
- e->name = e->message = NULL;
- e->_need_free = 0;
+ *e = SD_BUS_ERROR_NULL;
}
_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
@@ -310,6 +305,28 @@ finish:
return -bus_error_name_to_errno(e->name);
}
+_public_ int sd_bus_error_move(sd_bus_error *dest, sd_bus_error *e) {
+ int r;
+
+ if (!sd_bus_error_is_set(e)) {
+
+ if (dest)
+ *dest = SD_BUS_ERROR_NULL;
+
+ return 0;
+ }
+
+ r = -bus_error_name_to_errno(e->name);
+
+ if (dest) {
+ *dest = *e;
+ *e = SD_BUS_ERROR_NULL;
+ } else
+ sd_bus_error_free(e);
+
+ return r;
+}
+
_public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
if (!name)
return 0;
diff --git a/src/libsystemd/sd-bus/bus-error.h b/src/libsystemd/sd-bus/bus-error.h
index 93cb9acd91..a6523e57a2 100644
--- a/src/libsystemd/sd-bus/bus-error.h
+++ b/src/libsystemd/sd-bus/bus-error.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include <stdbool.h>
#include "sd-bus.h"
@@ -34,13 +31,15 @@ int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_lis
*/
#define BUS_ERROR_MAP_ELF_REGISTER \
- __attribute__ ((__section__("BUS_ERROR_MAP"))) \
- __attribute__ ((__used__)) \
- __attribute__ ((aligned(8)))
+ _section_("SYSTEMD_BUS_ERROR_MAP") \
+ _used_ \
+ _alignptr_ \
+ _variable_no_sanitize_address_
#define BUS_ERROR_MAP_ELF_USE(errors) \
extern const sd_bus_error_map errors[]; \
- __attribute__ ((used)) static const sd_bus_error_map * const CONCATENATE(errors ## _copy_, __COUNTER__) = errors;
+ _used_ \
+ static const sd_bus_error_map * const CONCATENATE(errors ## _copy_, __COUNTER__) = errors;
/* We use something exotic as end marker, to ensure people build the
* maps using the macsd-ros. */
diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c
index 05b17589dd..ba503b3213 100644
--- a/src/libsystemd/sd-bus/bus-gvariant.c
+++ b/src/libsystemd/sd-bus/bus-gvariant.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <errno.h>
#include <string.h>
diff --git a/src/libsystemd/sd-bus/bus-gvariant.h b/src/libsystemd/sd-bus/bus-gvariant.h
index 40e3053ec6..644b5f4b20 100644
--- a/src/libsystemd/sd-bus/bus-gvariant.h
+++ b/src/libsystemd/sd-bus/bus-gvariant.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include "macro.h"
int bus_gvariant_get_size(const char *signature) _pure_;
diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c
index 7bb653338d..40acae2133 100644
--- a/src/libsystemd/sd-bus/bus-internal.c
+++ b/src/libsystemd/sd-bus/bus-internal.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "alloc-util.h"
#include "bus-internal.h"
@@ -153,26 +151,6 @@ bool service_name_is_valid(const char *p) {
return true;
}
-char* service_name_startswith(const char *a, const char *b) {
- const char *p;
-
- if (!service_name_is_valid(a) ||
- !service_name_is_valid(b))
- return NULL;
-
- p = startswith(a, b);
- if (!p)
- return NULL;
-
- if (*p == 0)
- return (char*) p;
-
- if (*p == '.')
- return (char*) p + 1;
-
- return NULL;
-}
-
bool member_name_is_valid(const char *p) {
const char *q;
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index 2087ef8eeb..f208b294d8 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include <pthread.h>
#include <sys/socket.h>
@@ -214,6 +211,7 @@ struct sd_bus {
bool accept_fd:1;
bool attach_timestamp:1;
bool connected_signal:1;
+ bool close_on_exit:1;
int use_memfd;
@@ -307,8 +305,6 @@ struct sd_bus {
sd_bus **default_bus_ptr;
pid_t tid;
- char *cgroup_root;
-
char *description;
char *patch_sender;
@@ -319,9 +315,12 @@ struct sd_bus {
int *inotify_watches;
size_t n_inotify_watches;
+
+ /* zero means use value specified by $SYSTEMD_BUS_TIMEOUT= environment variable or built-in default */
+ usec_t method_call_timeout;
};
-/* For method calls we time-out at 25s, like in the D-Bus reference implementation */
+/* For method calls we timeout at 25s, like in the D-Bus reference implementation */
#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
/* For the authentication phase we grant 90s, to provide extra room during boot, when RNGs and such are not filled up
@@ -336,8 +335,7 @@ struct sd_bus {
#define BUS_CONTAINER_DEPTH 128
-/* Defined by the specification as maximum size of an array in
- * bytes */
+/* Defined by the specification as maximum size of an array in bytes */
#define BUS_ARRAY_MAX_SIZE 67108864
#define BUS_FDS_MAX 1024
@@ -346,7 +344,6 @@ struct sd_bus {
bool interface_name_is_valid(const char *p) _pure_;
bool service_name_is_valid(const char *p) _pure_;
-char* service_name_startswith(const char *a, const char *b);
bool member_name_is_valid(const char *p) _pure_;
bool object_path_is_valid(const char *p) _pure_;
char *object_path_startswith(const char *a, const char *b) _pure_;
@@ -384,12 +381,11 @@ void bus_close_io_fds(sd_bus *b);
#define OBJECT_PATH_FOREACH_PREFIX(prefix, path) \
for (char *_slash = ({ strcpy((prefix), (path)); streq((prefix), "/") ? NULL : strrchr((prefix), '/'); }) ; \
- _slash && !(_slash[(_slash) == (prefix)] = 0); \
+ _slash && ((_slash[(_slash) == (prefix)] = 0), true); \
_slash = streq((prefix), "/") ? NULL : strrchr((prefix), '/'))
/* If we are invoking callbacks of a bus object, ensure unreffing the
- * bus from the callback doesn't destroy the object we are working
- * on */
+ * bus from the callback doesn't destroy the object we are working on */
#define BUS_DONT_DESTROY(bus) \
_cleanup_(sd_bus_unrefp) _unused_ sd_bus *_dont_destroy_##bus = sd_bus_ref(bus)
@@ -398,8 +394,6 @@ int bus_set_address_user(sd_bus *bus);
int bus_set_address_system_remote(sd_bus *b, const char *host);
int bus_set_address_system_machine(sd_bus *b, const char *machine);
-int bus_get_root_path(sd_bus *bus);
-
int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
#define bus_assert_return(expr, r, error) \
diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c
index cfcbd8b072..f623dd9ce0 100644
--- a/src/libsystemd/sd-bus/bus-introspect.c
+++ b/src/libsystemd/sd-bus/bus-introspect.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <stdio_ext.h>
@@ -63,7 +61,7 @@ int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefi
return 0;
}
-static void introspect_write_flags(struct introspect *i, int type, int flags) {
+static void introspect_write_flags(struct introspect *i, int type, uint64_t flags) {
if (flags & SD_BUS_VTABLE_DEPRECATED)
fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h
index 5dcaeace9d..bb2dd7ef7b 100644
--- a/src/libsystemd/sd-bus/bus-introspect.h
+++ b/src/libsystemd/sd-bus/bus-introspect.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include <stdio.h>
#include "sd-bus.h"
diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c
index 1f61bd3f95..34c8a9f8c8 100644
--- a/src/libsystemd/sd-bus/bus-kernel.c
+++ b/src/libsystemd/sd-bus/bus-kernel.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#if HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h
index 44c9a76311..fbbc43f6fa 100644
--- a/src/libsystemd/sd-bus/bus-kernel.h
+++ b/src/libsystemd/sd-bus/bus-kernel.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include "sd-bus.h"
#define MEMFD_CACHE_MAX 32
diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c
index 7d04cc4bd1..ad135406f6 100644
--- a/src/libsystemd/sd-bus/bus-match.c
+++ b/src/libsystemd/sd-bus/bus-match.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <stdio_ext.h>
@@ -544,43 +542,6 @@ fail:
return r;
}
-static int bus_match_find_compare_value(
- struct bus_match_node *where,
- enum bus_match_node_type t,
- uint8_t value_u8,
- const char *value_str,
- struct bus_match_node **ret) {
-
- struct bus_match_node *c, *n;
-
- assert(where);
- assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
- assert(BUS_MATCH_IS_COMPARE(t));
- assert(ret);
-
- for (c = where->child; c && c->type != t; c = c->next)
- ;
-
- if (!c)
- return 0;
-
- if (t == BUS_MATCH_MESSAGE_TYPE)
- n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
- else if (BUS_MATCH_CAN_HASH(t))
- n = hashmap_get(c->compare.children, value_str);
- else {
- for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
- ;
- }
-
- if (n) {
- *ret = n;
- return 1;
- }
-
- return 0;
-}
-
static int bus_match_add_leaf(
struct bus_match_node *where,
struct match_callback *callback) {
@@ -609,34 +570,6 @@ static int bus_match_add_leaf(
return 1;
}
-static int bus_match_find_leaf(
- struct bus_match_node *where,
- sd_bus_message_handler_t callback,
- void *userdata,
- struct bus_match_node **ret) {
-
- struct bus_match_node *c;
-
- assert(where);
- assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
- assert(ret);
-
- for (c = where->child; c; c = c->next) {
- sd_bus_slot *s;
-
- s = container_of(c->leaf.callback, sd_bus_slot, match_callback);
-
- if (c->type == BUS_MATCH_LEAF &&
- c->leaf.callback->callback == callback &&
- s->userdata == userdata) {
- *ret = c;
- return 1;
- }
- }
-
- return 0;
-}
-
enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
assert(k);
@@ -762,15 +695,8 @@ enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n
return -EINVAL;
}
-static int match_component_compare(const void *a, const void *b) {
- const struct bus_match_component *x = a, *y = b;
-
- if (x->type < y->type)
- return -1;
- if (x->type > y->type)
- return 1;
-
- return 0;
+static int match_component_compare(const struct bus_match_component *a, const struct bus_match_component *b) {
+ return CMP(a->type, b->type);
}
void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) {
@@ -903,7 +829,7 @@ int bus_match_parse(
}
/* Order the whole thing, so that we always generate the same tree */
- qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare);
+ typesafe_qsort(components, n_components, match_component_compare);
/* Check for duplicates */
for (i = 0; i+1 < n_components; i++)
@@ -938,7 +864,7 @@ char *bus_match_to_string(struct bus_match_component *components, unsigned n_com
if (!f)
return NULL;
- __fsetlocking(f, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
for (i = 0; i < n_components; i++) {
char buf[32];
@@ -1023,43 +949,6 @@ int bus_match_remove(
return 1;
}
-int bus_match_find(
- struct bus_match_node *root,
- struct bus_match_component *components,
- unsigned n_components,
- sd_bus_message_handler_t callback,
- void *userdata,
- struct match_callback **ret) {
-
- struct bus_match_node *n, **gc;
- unsigned i;
- int r;
-
- assert(root);
- assert(ret);
-
- gc = newa(struct bus_match_node*, n_components);
-
- n = root;
- for (i = 0; i < n_components; i++) {
- r = bus_match_find_compare_value(
- n, components[i].type,
- components[i].value_u8, components[i].value_str,
- &n);
- if (r <= 0)
- return r;
-
- gc[i] = n;
- }
-
- r = bus_match_find_leaf(n, callback, userdata, &n);
- if (r <= 0)
- return r;
-
- *ret = n->leaf.callback;
- return 1;
-}
-
void bus_match_free(struct bus_match_node *node) {
struct bus_match_node *c;
diff --git a/src/libsystemd/sd-bus/bus-match.h b/src/libsystemd/sd-bus/bus-match.h
index 050f4ba033..a6f67ce089 100644
--- a/src/libsystemd/sd-bus/bus-match.h
+++ b/src/libsystemd/sd-bus/bus-match.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include "sd-bus.h"
#include "hashmap.h"
@@ -69,8 +66,6 @@ int bus_match_run(sd_bus *bus, struct bus_match_node *root, sd_bus_message *m);
int bus_match_add(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, struct match_callback *callback);
int bus_match_remove(struct bus_match_node *root, struct match_callback *callback);
-int bus_match_find(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, sd_bus_message_handler_t callback, void *userdata, struct match_callback **ret);
-
void bus_match_free(struct bus_match_node *node);
void bus_match_dump(struct bus_match_node *node, unsigned level);
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
index 8d92bc2002..bb7e09c945 100644
--- a/src/libsystemd/sd-bus/bus-message.c
+++ b/src/libsystemd/sd-bus/bus-message.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <errno.h>
#include <fcntl.h>
@@ -77,19 +75,38 @@ static void message_reset_parts(sd_bus_message *m) {
m->cached_rindex_part_begin = 0;
}
-static void message_reset_containers(sd_bus_message *m) {
- unsigned i;
+static struct bus_container *message_get_last_container(sd_bus_message *m) {
+ assert(m);
+
+ if (m->n_containers == 0)
+ return &m->root_container;
+
+ assert(m->containers);
+ return m->containers + m->n_containers - 1;
+}
+
+static void message_free_last_container(sd_bus_message *m) {
+ struct bus_container *c;
+ c = message_get_last_container(m);
+
+ free(c->signature);
+ free(c->peeked_signature);
+ free(c->offsets);
+
+ /* Move to previous container, but not if we are on root container */
+ if (m->n_containers > 0)
+ m->n_containers--;
+}
+
+static void message_reset_containers(sd_bus_message *m) {
assert(m);
- for (i = 0; i < m->n_containers; i++) {
- free(m->containers[i].signature);
- free(m->containers[i].offsets);
- }
+ while (m->n_containers > 0)
+ message_free_last_container(m);
m->containers = mfree(m->containers);
-
- m->n_containers = m->containers_allocated = 0;
+ m->containers_allocated = 0;
m->root_container.index = 0;
}
@@ -112,10 +129,8 @@ static sd_bus_message* message_free(sd_bus_message *m) {
free(m->iovec);
message_reset_containers(m);
- free(m->root_container.signature);
- free(m->root_container.offsets);
-
- free(m->root_container.peeked_signature);
+ assert(m->n_containers == 0);
+ message_free_last_container(m);
bus_creds_done(&m->creds);
return mfree(m);
@@ -210,7 +225,7 @@ static int message_append_field_string(
/* dbus1 doesn't allow strings over 32bit, let's enforce this
* globally, to not risk convertability */
l = strlen(s);
- if (l > (size_t) (uint32_t) -1)
+ if (l > UINT32_MAX)
return -EINVAL;
/* Signature "(yv)" where the variant contains "s" */
@@ -539,8 +554,7 @@ int bus_message_from_malloc(
m->n_iovec = 1;
m->iovec = m->iovec_fixed;
- m->iovec[0].iov_base = buffer;
- m->iovec[0].iov_len = length;
+ m->iovec[0] = IOVEC_MAKE(buffer, length);
r = bus_message_parse_fields(m);
if (r < 0)
@@ -876,30 +890,7 @@ int bus_message_new_synthetic_error(
return 0;
}
-_public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) {
-
- if (!m)
- return NULL;
-
- assert(m->n_ref > 0);
- m->n_ref++;
-
- return m;
-}
-
-_public_ sd_bus_message* sd_bus_message_unref(sd_bus_message *m) {
-
- if (!m)
- return NULL;
-
- assert(m->n_ref > 0);
- m->n_ref--;
-
- if (m->n_ref > 0)
- return NULL;
-
- return message_free(m);
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus_message, sd_bus_message, message_free);
_public_ int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type) {
assert_return(m, -EINVAL);
@@ -1113,16 +1104,6 @@ _public_ int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *
return 0;
}
-static struct bus_container *message_get_container(sd_bus_message *m) {
- assert(m);
-
- if (m->n_containers == 0)
- return &m->root_container;
-
- assert(m->containers);
- return m->containers + m->n_containers - 1;
-}
-
struct bus_body_part *message_append_part(sd_bus_message *m) {
struct bus_body_part *part;
@@ -1213,7 +1194,7 @@ static int message_add_offset(sd_bus_message *m, size_t offset) {
/* Add offset to current container, unless this is the first
* item in it, which will have the 0 offset, which we can
* ignore. */
- c = message_get_container(m);
+ c = message_get_last_container(m);
if (!c->need_offsets)
return 0;
@@ -1385,7 +1366,7 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void
assert_return(bus_type_is_basic(type), -EINVAL);
assert_return(!m->poisoned, -ESTALE);
- c = message_get_container(m);
+ c = message_get_last_container(m);
if (c->signature && c->signature[c->index]) {
/* Container signature is already set */
@@ -1578,7 +1559,7 @@ _public_ int sd_bus_message_append_string_space(
assert_return(!m->sealed, -EPERM);
assert_return(!m->poisoned, -ESTALE);
- c = message_get_container(m);
+ c = message_get_last_container(m);
if (c->signature && c->signature[c->index]) {
/* Container signature is already set */
@@ -1949,7 +1930,7 @@ _public_ int sd_bus_message_open_container(
char type,
const char *contents) {
- struct bus_container *c, *w;
+ struct bus_container *c;
uint32_t *array_size = NULL;
_cleanup_free_ char *signature = NULL;
size_t before, begin = 0;
@@ -1967,7 +1948,7 @@ _public_ int sd_bus_message_open_container(
return -ENOMEM;
}
- c = message_get_container(m);
+ c = message_get_last_container(m);
signature = strdup(contents);
if (!signature) {
@@ -1994,16 +1975,14 @@ _public_ int sd_bus_message_open_container(
return r;
/* OK, let's fill it in */
- w = m->containers + m->n_containers++;
- w->enclosing = type;
- w->signature = TAKE_PTR(signature);
- w->index = 0;
- w->array_size = array_size;
- w->before = before;
- w->begin = begin;
- w->n_offsets = w->offsets_allocated = 0;
- w->offsets = NULL;
- w->need_offsets = need_offsets;
+ m->containers[m->n_containers++] = (struct bus_container) {
+ .enclosing = type,
+ .signature = TAKE_PTR(signature),
+ .array_size = array_size,
+ .before = before,
+ .begin = begin,
+ .need_offsets = need_offsets,
+ };
return 0;
}
@@ -2194,7 +2173,7 @@ _public_ int sd_bus_message_close_container(sd_bus_message *m) {
assert_return(m->n_containers > 0, -EINVAL);
assert_return(!m->poisoned, -ESTALE);
- c = message_get_container(m);
+ c = message_get_last_container(m);
if (c->enclosing != SD_BUS_TYPE_ARRAY)
if (c->signature && c->signature[c->index] != 0)
@@ -2464,11 +2443,6 @@ _public_ int sd_bus_message_append(sd_bus_message *m, const char *types, ...) {
va_list ap;
int r;
- assert_return(m, -EINVAL);
- assert_return(types, -EINVAL);
- assert_return(!m->sealed, -EPERM);
- assert_return(!m->poisoned, -ESTALE);
-
va_start(ap, types);
r = sd_bus_message_appendv(m, types, ap);
va_end(ap);
@@ -2698,7 +2672,7 @@ _public_ int sd_bus_message_append_string_memfd(
if (size > (uint64_t) (uint32_t) -1)
return -EINVAL;
- c = message_get_container(m);
+ c = message_get_last_container(m);
if (c->signature && c->signature[c->index]) {
/* Container signature is already set */
@@ -3031,7 +3005,7 @@ static bool message_end_of_signature(sd_bus_message *m) {
assert(m);
- c = message_get_container(m);
+ c = message_get_last_container(m);
return !c->signature || c->signature[c->index] == 0;
}
@@ -3040,7 +3014,7 @@ static bool message_end_of_array(sd_bus_message *m, size_t index) {
assert(m);
- c = message_get_container(m);
+ c = message_get_last_container(m);
if (c->enclosing != SD_BUS_TYPE_ARRAY)
return false;
@@ -3135,6 +3109,7 @@ static int container_next_item(sd_bus_message *m, struct bus_container *c, size_
assert(alignment > 0);
*rindex = ALIGN_TO(c->offsets[c->offset_index], alignment);
+ assert(c->offsets[c->offset_index+1] >= *rindex);
c->item_size = c->offsets[c->offset_index+1] - *rindex;
} else {
@@ -3174,6 +3149,7 @@ static int container_next_item(sd_bus_message *m, struct bus_container *c, size_
assert(alignment > 0);
*rindex = ALIGN_TO(c->offsets[c->offset_index], alignment);
+ assert(c->offsets[c->offset_index+1] >= *rindex);
c->item_size = c->offsets[c->offset_index+1] - *rindex;
c->offset_index++;
@@ -3301,7 +3277,7 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
if (message_end_of_array(m, m->rindex))
return 0;
- c = message_get_container(m);
+ c = message_get_last_container(m);
if (c->signature[c->index] != type)
return -ENXIO;
@@ -3312,6 +3288,12 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) {
bool ok;
+ /* D-Bus spec: The marshalling formats for the string-like types all end
+ * with a single zero (NUL) byte, but that byte is not considered to be part
+ * of the text. */
+ if (c->item_size == 0)
+ return -EBADMSG;
+
r = message_peek_body(m, &rindex, 1, c->item_size, &q);
if (r < 0)
return r;
@@ -3406,6 +3388,10 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
return r;
l = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
+ if (l == UINT32_MAX)
+ /* avoid overflow right below */
+ return -EBADMSG;
+
r = message_peek_body(m, &rindex, 1, l+1, &q);
if (r < 0)
return r;
@@ -3428,6 +3414,10 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
return r;
l = *(uint8_t*) q;
+ if (l == UINT8_MAX)
+ /* avoid overflow right below */
+ return -EBADMSG;
+
r = message_peek_body(m, &rindex, 1, l+1, &q);
if (r < 0)
return r;
@@ -3519,7 +3509,7 @@ static int bus_message_enter_array(
size_t rindex;
void *q;
- int r, alignment;
+ int r;
assert(m);
assert(c);
@@ -3545,6 +3535,7 @@ static int bus_message_enter_array(
if (!BUS_MESSAGE_IS_GVARIANT(m)) {
/* dbus1 */
+ int alignment;
r = message_peek_body(m, &rindex, 4, 4, &q);
if (r < 0)
@@ -3578,7 +3569,8 @@ static int bus_message_enter_array(
*n_offsets = 0;
} else {
- size_t where, p = 0, framing, sz;
+ size_t where, previous = 0, framing, sz;
+ int alignment;
unsigned i;
/* gvariant: variable length array */
@@ -3606,17 +3598,22 @@ static int bus_message_enter_array(
if (!*offsets)
return -ENOMEM;
+ alignment = bus_gvariant_get_alignment(c->signature);
+ assert(alignment > 0);
+
for (i = 0; i < *n_offsets; i++) {
- size_t x;
+ size_t x, start;
+
+ start = ALIGN_TO(previous, alignment);
x = bus_gvariant_read_word_le((uint8_t*) q + i * sz, sz);
if (x > c->item_size - sz)
return -EBADMSG;
- if (x < p)
+ if (x < start)
return -EBADMSG;
(*offsets)[i] = rindex + x;
- p = x;
+ previous = x;
}
*item_size = (*offsets)[0] - rindex;
@@ -3686,6 +3683,10 @@ static int bus_message_enter_variant(
return r;
l = *(uint8_t*) q;
+ if (l == UINT8_MAX)
+ /* avoid overflow right below */
+ return -EBADMSG;
+
r = message_peek_body(m, &rindex, 1, l+1, &q);
if (r < 0)
return r;
@@ -3714,7 +3715,7 @@ static int build_struct_offsets(
size_t *n_offsets) {
unsigned n_variable = 0, n_total = 0, v;
- size_t previous = 0, where;
+ size_t previous, where;
const char *p;
size_t sz;
void *q;
@@ -3793,6 +3794,7 @@ static int build_struct_offsets(
/* Second, loop again and build an offset table */
p = signature;
+ previous = m->rindex;
while (*p != 0) {
size_t n, offset;
int k;
@@ -3806,37 +3808,39 @@ static int build_struct_offsets(
memcpy(t, p, n);
t[n] = 0;
+ size_t align = bus_gvariant_get_alignment(t);
+ assert(align > 0);
+
+ /* The possible start of this member after including alignment */
+ size_t start = ALIGN_TO(previous, align);
+
k = bus_gvariant_get_size(t);
if (k < 0) {
size_t x;
- /* variable size */
+ /* Variable size */
if (v > 0) {
v--;
x = bus_gvariant_read_word_le((uint8_t*) q + v*sz, sz);
if (x >= size)
return -EBADMSG;
- if (m->rindex + x < previous)
- return -EBADMSG;
} else
- /* The last item's end
- * is determined from
- * the start of the
- * offset array */
+ /* The last item's end is determined
+ * from the start of the offset array */
x = size - (n_variable * sz);
offset = m->rindex + x;
-
- } else {
- size_t align;
-
- /* fixed size */
- align = bus_gvariant_get_alignment(t);
- assert(align > 0);
-
- offset = (*n_offsets == 0 ? m->rindex : ALIGN_TO((*offsets)[*n_offsets-1], align)) + k;
- }
+ if (offset < start)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "For type %s with alignment %zu, message specifies offset %zu which is smaller than previous end %zu + alignment = %zu",
+ t, align,
+ offset,
+ previous,
+ start);
+ } else
+ /* Fixed size */
+ offset = start + k;
}
previous = (*offsets)[(*n_offsets)++] = offset;
@@ -3966,10 +3970,10 @@ static int bus_message_enter_dict_entry(
_public_ int sd_bus_message_enter_container(sd_bus_message *m,
char type,
const char *contents) {
- struct bus_container *c, *w;
+ struct bus_container *c;
uint32_t *array_size = NULL;
_cleanup_free_ char *signature = NULL;
- size_t before;
+ size_t before, end;
_cleanup_free_ size_t *offsets = NULL;
size_t n_offsets = 0, item_size = 0;
int r;
@@ -4025,7 +4029,7 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m,
if (message_end_of_array(m, m->rindex))
return 0;
- c = message_get_container(m);
+ c = message_get_last_container(m);
signature = strdup(contents);
if (!signature)
@@ -4048,28 +4052,26 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m,
return r;
/* OK, let's fill it in */
- w = m->containers + m->n_containers++;
- w->enclosing = type;
- w->signature = TAKE_PTR(signature);
- w->peeked_signature = NULL;
- w->index = 0;
-
- w->before = before;
- w->begin = m->rindex;
-
- /* Unary type has fixed size of 1, but virtual size of 0 */
if (BUS_MESSAGE_IS_GVARIANT(m) &&
type == SD_BUS_TYPE_STRUCT &&
isempty(signature))
- w->end = m->rindex + 0;
+ end = m->rindex + 0;
else
- w->end = m->rindex + c->item_size;
-
- w->array_size = array_size;
- w->item_size = item_size;
- w->offsets = TAKE_PTR(offsets);
- w->n_offsets = n_offsets;
- w->offset_index = 0;
+ end = m->rindex + c->item_size;
+
+ m->containers[m->n_containers++] = (struct bus_container) {
+ .enclosing = type,
+ .signature = TAKE_PTR(signature),
+
+ .before = before,
+ .begin = m->rindex,
+ /* Unary type has fixed size of 1, but virtual size of 0 */
+ .end = end,
+ .array_size = array_size,
+ .item_size = item_size,
+ .offsets = TAKE_PTR(offsets),
+ .n_offsets = n_offsets,
+ };
return 1;
}
@@ -4083,7 +4085,7 @@ _public_ int sd_bus_message_exit_container(sd_bus_message *m) {
assert_return(m->sealed, -EPERM);
assert_return(m->n_containers > 0, -ENXIO);
- c = message_get_container(m);
+ c = message_get_last_container(m);
if (c->enclosing != SD_BUS_TYPE_ARRAY) {
if (c->signature && c->signature[c->index] != 0)
@@ -4102,13 +4104,9 @@ _public_ int sd_bus_message_exit_container(sd_bus_message *m) {
return -EBUSY;
}
- free(c->signature);
- free(c->peeked_signature);
- free(c->offsets);
- m->n_containers--;
-
- c = message_get_container(m);
+ message_free_last_container(m);
+ c = message_get_last_container(m);
saved = c->index;
c->index = c->saved_index;
r = container_next_item(m, c, &m->rindex);
@@ -4126,19 +4124,16 @@ static void message_quit_container(sd_bus_message *m) {
assert(m->sealed);
assert(m->n_containers > 0);
- c = message_get_container(m);
-
/* Undo seeks */
+ c = message_get_last_container(m);
assert(m->rindex >= c->before);
m->rindex = c->before;
/* Free container */
- free(c->signature);
- free(c->offsets);
- m->n_containers--;
+ message_free_last_container(m);
/* Correct index of new top-level container */
- c = message_get_container(m);
+ c = message_get_last_container(m);
c->index = c->saved_index;
}
@@ -4155,7 +4150,7 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
if (message_end_of_array(m, m->rindex))
goto eof;
- c = message_get_container(m);
+ c = message_get_last_container(m);
if (bus_type_is_basic(c->signature[c->index])) {
if (contents)
@@ -4169,20 +4164,20 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
if (contents) {
size_t l;
- char *sig;
r = signature_element_length(c->signature+c->index+1, &l);
if (r < 0)
return r;
- assert(l >= 1);
+ /* signature_element_length does verification internally */
- sig = strndup(c->signature + c->index + 1, l);
- if (!sig)
+ /* The array element must not be empty */
+ assert(l >= 1);
+ if (free_and_strndup(&c->peeked_signature,
+ c->signature + c->index + 1, l) < 0)
return -ENOMEM;
- free(c->peeked_signature);
- *contents = c->peeked_signature = sig;
+ *contents = c->peeked_signature;
}
if (type)
@@ -4195,19 +4190,17 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
if (contents) {
size_t l;
- char *sig;
r = signature_element_length(c->signature+c->index, &l);
if (r < 0)
return r;
- assert(l >= 2);
- sig = strndup(c->signature + c->index + 1, l - 2);
- if (!sig)
+ assert(l >= 3);
+ if (free_and_strndup(&c->peeked_signature,
+ c->signature + c->index + 1, l - 2) < 0)
return -ENOMEM;
- free(c->peeked_signature);
- *contents = c->peeked_signature = sig;
+ *contents = c->peeked_signature;
}
if (type)
@@ -4247,9 +4240,8 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
if (k > c->item_size)
return -EBADMSG;
- free(c->peeked_signature);
- c->peeked_signature = strndup((char*) q + 1, k - 1);
- if (!c->peeked_signature)
+ if (free_and_strndup(&c->peeked_signature,
+ (char*) q + 1, k - 1) < 0)
return -ENOMEM;
if (!signature_is_valid(c->peeked_signature, true))
@@ -4265,6 +4257,10 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
return r;
l = *(uint8_t*) q;
+ if (l == UINT8_MAX)
+ /* avoid overflow right below */
+ return -EBADMSG;
+
r = message_peek_body(m, &rindex, 1, l+1, &q);
if (r < 0)
return r;
@@ -4302,11 +4298,10 @@ _public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) {
message_reset_containers(m);
m->rindex = 0;
- c = message_get_container(m);
+ c = message_get_last_container(m);
} else {
- c = message_get_container(m);
+ c = message_get_last_container(m);
- c->offset_index = 0;
c->index = 0;
m->rindex = c->begin;
}
@@ -4317,7 +4312,7 @@ _public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) {
return !isempty(c->signature);
}
-static int message_read_ap(
+_public_ int sd_bus_message_readv(
sd_bus_message *m,
const char *types,
va_list ap) {
@@ -4328,7 +4323,9 @@ static int message_read_ap(
unsigned n_loop = 0;
int r;
- assert(m);
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EPERM);
+ assert_return(types, -EINVAL);
if (isempty(types))
return 0;
@@ -4519,12 +4516,8 @@ _public_ int sd_bus_message_read(sd_bus_message *m, const char *types, ...) {
va_list ap;
int r;
- assert_return(m, -EINVAL);
- assert_return(m->sealed, -EPERM);
- assert_return(types, -EINVAL);
-
va_start(ap, types);
- r = message_read_ap(m, types, ap);
+ r = sd_bus_message_readv(m, types, ap);
va_end(ap);
return r;
@@ -4547,7 +4540,7 @@ _public_ int sd_bus_message_skip(sd_bus_message *m, const char *types) {
if (message_end_of_array(m, m->rindex))
return 0;
- c = message_get_container(m);
+ c = message_get_last_container(m);
r = signature_element_length(c->signature + c->index, &l);
if (r < 0)
@@ -4713,7 +4706,7 @@ _public_ int sd_bus_message_read_array(
if (r <= 0)
return r;
- c = message_get_container(m);
+ c = message_get_last_container(m);
if (BUS_MESSAGE_IS_GVARIANT(m)) {
align = bus_gvariant_get_alignment(CHAR_TO_STR(type));
@@ -4850,6 +4843,10 @@ static int message_peek_field_string(
if (r < 0)
return r;
+ if (l == UINT32_MAX)
+ /* avoid overflow right below */
+ return -EBADMSG;
+
r = message_peek_fields(m, ri, 1, l+1, &q);
if (r < 0)
return r;
@@ -4901,6 +4898,10 @@ static int message_peek_field_signature(
return r;
l = *(uint8_t*) q;
+ if (l == UINT8_MAX)
+ /* avoid overflow right below */
+ return -EBADMSG;
+
r = message_peek_fields(m, ri, 1, l+1, &q);
if (r < 0)
return r;
@@ -4982,18 +4983,18 @@ static int message_skip_fields(
} else if (t == SD_BUS_TYPE_ARRAY) {
- r = signature_element_length(*signature+1, &l);
+ r = signature_element_length(*signature + 1, &l);
if (r < 0)
return r;
assert(l >= 1);
{
- char sig[l-1], *s;
+ char sig[l + 1], *s = sig;
uint32_t nas;
int alignment;
- strncpy(sig, *signature + 1, l-1);
- s = sig;
+ strncpy(sig, *signature + 1, l);
+ sig[l] = '\0';
alignment = bus_type_get_alignment(sig[0]);
if (alignment < 0)
@@ -5037,9 +5038,9 @@ static int message_skip_fields(
assert(l >= 2);
{
- char sig[l-1], *s;
- strncpy(sig, *signature + 1, l-1);
- s = sig;
+ char sig[l + 1], *s = sig;
+ strncpy(sig, *signature + 1, l);
+ sig[l] = '\0';
r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s);
if (r < 0)
@@ -5048,7 +5049,7 @@ static int message_skip_fields(
*signature += l;
} else
- return -EINVAL;
+ return -EBADMSG;
}
}
@@ -5078,26 +5079,25 @@ int bus_message_parse_fields(sd_bus_message *m) {
return -EBADMSG;
if (*p == 0) {
+ char *k;
size_t l;
- char *c;
/* We found the beginning of the signature
* string, yay! We require the body to be a
* structure, so verify it and then strip the
* opening/closing brackets. */
- l = ((char*) m->footer + m->footer_accessible) - p - (1 + sz);
+ l = (char*) m->footer + m->footer_accessible - p - (1 + sz);
if (l < 2 ||
p[1] != SD_BUS_TYPE_STRUCT_BEGIN ||
p[1 + l - 1] != SD_BUS_TYPE_STRUCT_END)
return -EBADMSG;
- c = strndup(p + 1 + 1, l - 2);
- if (!c)
+ k = memdup_suffix0(p + 1 + 1, l - 2);
+ if (!k)
return -ENOMEM;
- free(m->root_container.signature);
- m->root_container.signature = c;
+ free_and_replace(m->root_container.signature, k);
break;
}
@@ -5419,6 +5419,8 @@ int bus_message_parse_fields(sd_bus_message *m) {
&m->root_container.item_size,
&m->root_container.offsets,
&m->root_container.n_offsets);
+ if (r == -EINVAL)
+ return -EBADMSG;
if (r < 0)
return r;
}
@@ -5433,6 +5435,7 @@ int bus_message_parse_fields(sd_bus_message *m) {
_public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) {
assert_return(m, -EINVAL);
assert_return(destination, -EINVAL);
+ assert_return(service_name_is_valid(destination), -EINVAL);
assert_return(!m->sealed, -EPERM);
assert_return(!m->destination, -EEXIST);
@@ -5442,6 +5445,7 @@ _public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *desti
_public_ int sd_bus_message_set_sender(sd_bus_message *m, const char *sender) {
assert_return(m, -EINVAL);
assert_return(sender, -EINVAL);
+ assert_return(service_name_is_valid(sender), -EINVAL);
assert_return(!m->sealed, -EPERM);
assert_return(!m->sender, -EEXIST);
@@ -5612,7 +5616,7 @@ _public_ const char* sd_bus_message_get_signature(sd_bus_message *m, int complet
assert_return(m, NULL);
- c = complete ? &m->root_container : message_get_container(m);
+ c = complete ? &m->root_container : message_get_last_container(m);
return strempty(c->signature);
}
@@ -5809,8 +5813,11 @@ int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) {
return r;
timeout = (*m)->timeout;
- if (timeout == 0 && !((*m)->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED))
- timeout = BUS_DEFAULT_TIMEOUT;
+ if (timeout == 0 && !((*m)->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)) {
+ r = sd_bus_get_method_call_timeout(bus, &timeout);
+ if (r < 0)
+ return r;
+ }
r = sd_bus_message_seal(n, BUS_MESSAGE_COOKIE(*m), timeout);
if (r < 0)
@@ -5822,16 +5829,6 @@ int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) {
return 0;
}
-int bus_message_append_sender(sd_bus_message *m, const char *sender) {
- assert(m);
- assert(sender);
-
- assert_return(!m->sealed, -EPERM);
- assert_return(!m->sender, -EPERM);
-
- return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender);
-}
-
_public_ int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority) {
assert_return(m, -EINVAL);
assert_return(priority, -EINVAL);
diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h
index 97f6060e30..0115437d26 100644
--- a/src/libsystemd/sd-bus/bus-message.h
+++ b/src/libsystemd/sd-bus/bus-message.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include <byteswap.h>
#include <stdbool.h>
#include <sys/socket.h>
@@ -212,7 +209,5 @@ int bus_message_new_synthetic_error(sd_bus *bus, uint64_t serial, const sd_bus_e
int bus_message_remarshal(sd_bus *bus, sd_bus_message **m);
-int bus_message_append_sender(sd_bus_message *m, const char *sender);
-
void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m);
void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m);
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
index 9609834fa9..d0538104ae 100644
--- a/src/libsystemd/sd-bus/bus-objects.c
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "alloc-util.h"
#include "bus-internal.h"
@@ -11,6 +9,7 @@
#include "bus-slot.h"
#include "bus-type.h"
#include "bus-util.h"
+#include "missing_capability.h"
#include "set.h"
#include "string-util.h"
#include "strv.h"
@@ -166,7 +165,7 @@ static int add_subtree_to_set(
sd_bus *bus,
const char *prefix,
struct node *n,
- unsigned int flags,
+ unsigned flags,
Set *s,
sd_bus_error *error) {
@@ -215,7 +214,7 @@ static int get_child_nodes(
sd_bus *bus,
const char *prefix,
struct node *n,
- unsigned int flags,
+ unsigned flags,
Set **_s,
sd_bus_error *error) {
@@ -1584,9 +1583,7 @@ _public_ int sd_bus_add_fallback(
return bus_add_object(bus, slot, true, prefix, callback, userdata);
}
-static void vtable_member_hash_func(const void *a, struct siphash *state) {
- const struct vtable_member *m = a;
-
+static void vtable_member_hash_func(const struct vtable_member *m, struct siphash *state) {
assert(m);
string_hash_func(m->path, state);
@@ -1594,8 +1591,7 @@ static void vtable_member_hash_func(const void *a, struct siphash *state) {
string_hash_func(m->member, state);
}
-static int vtable_member_compare_func(const void *a, const void *b) {
- const struct vtable_member *x = a, *y = b;
+static int vtable_member_compare_func(const struct vtable_member *x, const struct vtable_member *y) {
int r;
assert(x);
@@ -1612,10 +1608,7 @@ static int vtable_member_compare_func(const void *a, const void *b) {
return strcmp(x->member, y->member);
}
-static const struct hash_ops vtable_member_hash_ops = {
- .hash = vtable_member_hash_func,
- .compare = vtable_member_compare_func
-};
+DEFINE_PRIVATE_HASH_OPS(vtable_member_hash_ops, struct vtable_member, vtable_member_hash_func, vtable_member_compare_func);
static int add_object_vtable_internal(
sd_bus *bus,
@@ -2090,7 +2083,6 @@ _public_ int sd_bus_emit_properties_changed_strv(
const char *interface,
char **names) {
- BUS_DONT_DESTROY(bus);
bool found_interface = false;
char *prefix;
int r;
@@ -2111,6 +2103,8 @@ _public_ int sd_bus_emit_properties_changed_strv(
if (names && names[0] == NULL)
return 0;
+ BUS_DONT_DESTROY(bus);
+
do {
bus->nodes_modified = false;
@@ -2310,8 +2304,6 @@ static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *p
}
_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
- BUS_DONT_DESTROY(bus);
-
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
struct node *object_manager;
int r;
@@ -2341,6 +2333,8 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
if (r == 0)
return -ESRCH;
+ BUS_DONT_DESTROY(bus);
+
do {
bus->nodes_modified = false;
m = sd_bus_message_unref(m);
@@ -2481,8 +2475,6 @@ static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char
}
_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
- BUS_DONT_DESTROY(bus);
-
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
struct node *object_manager;
int r;
@@ -2512,6 +2504,8 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
if (r == 0)
return -ESRCH;
+ BUS_DONT_DESTROY(bus);
+
do {
bus->nodes_modified = false;
m = sd_bus_message_unref(m);
@@ -2645,8 +2639,6 @@ static int interfaces_added_append_one(
}
_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
- BUS_DONT_DESTROY(bus);
-
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
struct node *object_manager;
char **i;
@@ -2669,6 +2661,8 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch
if (r == 0)
return -ESRCH;
+ BUS_DONT_DESTROY(bus);
+
do {
bus->nodes_modified = false;
m = sd_bus_message_unref(m);
diff --git a/src/libsystemd/sd-bus/bus-objects.h b/src/libsystemd/sd-bus/bus-objects.h
index e8e1a522cb..a119ff95c0 100644
--- a/src/libsystemd/sd-bus/bus-objects.h
+++ b/src/libsystemd/sd-bus/bus-objects.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include "bus-internal.h"
int bus_process_object(sd_bus *bus, sd_bus_message *m);
diff --git a/src/libsystemd/sd-bus/bus-protocol.h b/src/libsystemd/sd-bus/bus-protocol.h
index 20d19d4022..a5f4724aa9 100644
--- a/src/libsystemd/sd-bus/bus-protocol.h
+++ b/src/libsystemd/sd-bus/bus-protocol.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include <endian.h>
#include "macro.h"
diff --git a/src/libsystemd/sd-bus/bus-signature.c b/src/libsystemd/sd-bus/bus-signature.c
index 18c91e8707..1ecd6e8b7e 100644
--- a/src/libsystemd/sd-bus/bus-signature.c
+++ b/src/libsystemd/sd-bus/bus-signature.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <util.h>
@@ -58,6 +56,12 @@ static int signature_element_length_internal(
p += t;
}
+ if (p - s < 2)
+ /* D-Bus spec: Empty structures are not allowed; there
+ * must be at least one type code between the parentheses.
+ */
+ return -EINVAL;
+
*l = p - s + 1;
return 0;
}
diff --git a/src/libsystemd/sd-bus/bus-signature.h b/src/libsystemd/sd-bus/bus-signature.h
index d4b43bac00..b87bec8329 100644
--- a/src/libsystemd/sd-bus/bus-signature.h
+++ b/src/libsystemd/sd-bus/bus-signature.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include <stdbool.h>
bool signature_is_single(const char *s, bool allow_dict_entry);
diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c
index fbf37320d3..c9aca07f90 100644
--- a/src/libsystemd/sd-bus/bus-slot.c
+++ b/src/libsystemd/sd-bus/bus-slot.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "sd-bus.h"
@@ -39,18 +37,7 @@ sd_bus_slot *bus_slot_allocate(
return slot;
}
-_public_ sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot) {
-
- if (!slot)
- return NULL;
-
- assert(slot->n_ref > 0);
-
- slot->n_ref++;
- return slot;
-}
-
-void bus_slot_disconnect(sd_bus_slot *slot) {
+void bus_slot_disconnect(sd_bus_slot *slot, bool unref) {
sd_bus *bus;
assert(slot);
@@ -81,7 +68,7 @@ void bus_slot_disconnect(sd_bus_slot *slot) {
(void) bus_remove_match_internal(slot->bus, slot->match_callback.match_string);
if (slot->match_callback.install_slot) {
- bus_slot_disconnect(slot->match_callback.install_slot);
+ bus_slot_disconnect(slot->match_callback.install_slot, true);
slot->match_callback.install_slot = sd_bus_slot_unref(slot->match_callback.install_slot);
}
@@ -185,21 +172,14 @@ void bus_slot_disconnect(sd_bus_slot *slot) {
if (!slot->floating)
sd_bus_unref(bus);
+ else if (unref)
+ sd_bus_slot_unref(slot);
}
-_public_ sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) {
-
- if (!slot)
- return NULL;
-
- assert(slot->n_ref > 0);
-
- if (slot->n_ref > 1) {
- slot->n_ref--;
- return NULL;
- }
+static sd_bus_slot* bus_slot_free(sd_bus_slot *slot) {
+ assert(slot);
- bus_slot_disconnect(slot);
+ bus_slot_disconnect(slot, false);
if (slot->destroy_callback)
slot->destroy_callback(slot->userdata);
@@ -208,6 +188,8 @@ _public_ sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) {
return mfree(slot);
}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus_slot, sd_bus_slot, bus_slot_free);
+
_public_ sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot) {
assert_return(slot, NULL);
diff --git a/src/libsystemd/sd-bus/bus-slot.h b/src/libsystemd/sd-bus/bus-slot.h
index f1e1e23ac6..48eb0453dc 100644
--- a/src/libsystemd/sd-bus/bus-slot.h
+++ b/src/libsystemd/sd-bus/bus-slot.h
@@ -1,13 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include "sd-bus.h"
#include "bus-internal.h"
sd_bus_slot *bus_slot_allocate(sd_bus *bus, bool floating, BusSlotType type, size_t extra, void *userdata);
-void bus_slot_disconnect(sd_bus_slot *slot);
+void bus_slot_disconnect(sd_bus_slot *slot, bool unref);
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
index b147a3843a..ed185131b8 100644
--- a/src/libsystemd/sd-bus/bus-socket.c
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <endian.h>
#include <poll.h>
@@ -23,6 +21,7 @@
#include "missing.h"
#include "path-util.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "stdio-util.h"
@@ -46,8 +45,7 @@ static void iovec_advance(struct iovec iov[], unsigned *idx, size_t size) {
size -= i->iov_len;
- i->iov_base = NULL;
- i->iov_len = 0;
+ *i = IOVEC_MAKE(NULL, 0);
(*idx)++;
}
@@ -58,9 +56,7 @@ static int append_iovec(sd_bus_message *m, const void *p, size_t sz) {
assert(p);
assert(sz > 0);
- m->iovec[m->n_iovec].iov_base = (void*) p;
- m->iovec[m->n_iovec].iov_len = sz;
- m->n_iovec++;
+ m->iovec[m->n_iovec++] = IOVEC_MAKE((void*) p, sz);
return 0;
}
@@ -248,10 +244,7 @@ static bool line_begins(const char *s, size_t m, const char *word) {
const char *p;
p = memory_startswith(s, m, word);
- if (!p)
- return false;
-
- return IN_SET(*p, 0, ' ');
+ return p && (p == (s + m) || *p == ' ');
}
static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
@@ -521,8 +514,7 @@ static int bus_socket_read_auth(sd_bus *b) {
b->rbuffer = p;
- iov.iov_base = (uint8_t*) b->rbuffer + b->rbuffer_size;
- iov.iov_len = n - b->rbuffer_size;
+ iov = IOVEC_MAKE((uint8_t *)b->rbuffer + b->rbuffer_size, n - b->rbuffer_size);
if (b->prefer_readv)
k = readv(b->input_fd, &iov, 1);
@@ -639,12 +631,9 @@ static int bus_socket_start_auth_client(sd_bus *b) {
else
auth_suffix = "\r\nBEGIN\r\n";
- b->auth_iovec[0].iov_base = (void*) auth_prefix;
- b->auth_iovec[0].iov_len = 1 + strlen(auth_prefix + 1);
- b->auth_iovec[1].iov_base = (void*) b->auth_buffer;
- b->auth_iovec[1].iov_len = l * 2;
- b->auth_iovec[2].iov_base = (void*) auth_suffix;
- b->auth_iovec[2].iov_len = strlen(auth_suffix);
+ b->auth_iovec[0] = IOVEC_MAKE((void*) auth_prefix, 1 + strlen(auth_prefix + 1));
+ b->auth_iovec[1] = IOVEC_MAKE(b->auth_buffer, l * 2);
+ b->auth_iovec[2] = IOVEC_MAKE_STRING(auth_suffix);
return bus_socket_write_auth(b);
}
@@ -944,6 +933,8 @@ int bus_socket_exec(sd_bus *b) {
if (rearrange_stdio(s[1], s[1], STDERR_FILENO) < 0)
_exit(EXIT_FAILURE);
+ (void) rlimit_nofile_safe();
+
if (b->exec_argv)
execvp(b->exec_path, b->exec_argv);
else {
@@ -1151,8 +1142,7 @@ int bus_socket_read_message(sd_bus *bus) {
bus->rbuffer = b;
- iov.iov_base = (uint8_t*) bus->rbuffer + bus->rbuffer_size;
- iov.iov_len = need - bus->rbuffer_size;
+ iov = IOVEC_MAKE((uint8_t *)bus->rbuffer + bus->rbuffer_size, need - bus->rbuffer_size);
if (bus->prefer_readv)
k = readv(bus->input_fd, &iov, 1);
diff --git a/src/libsystemd/sd-bus/bus-socket.h b/src/libsystemd/sd-bus/bus-socket.h
index d1118ca1d4..f8d24556c9 100644
--- a/src/libsystemd/sd-bus/bus-socket.h
+++ b/src/libsystemd/sd-bus/bus-socket.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include "sd-bus.h"
void bus_socket_setup(sd_bus *b);
diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c
index 16bf615f50..efbd3ed5f0 100644
--- a/src/libsystemd/sd-bus/bus-track.c
+++ b/src/libsystemd/sd-bus/bus-track.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "sd-bus.h"
@@ -52,6 +50,8 @@ static struct track_item* track_item_free(struct track_item *i) {
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(track_item_hash_ops, char, string_hash_func, string_compare_func,
+ struct track_item, track_item_free);
static void bus_track_add_to_queue(sd_bus_track *track) {
assert(track);
@@ -145,33 +145,14 @@ _public_ int sd_bus_track_new(
return 0;
}
-_public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
-
- if (!track)
- return NULL;
-
- assert(track->n_ref > 0);
-
- track->n_ref++;
-
- return track;
-}
-
-_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
- if (!track)
- return NULL;
-
- assert(track->n_ref > 0);
- track->n_ref--;
-
- if (track->n_ref > 0)
- return NULL;
+static sd_bus_track *track_free(sd_bus_track *track) {
+ assert(track);
if (track->in_list)
LIST_REMOVE(tracks, track->bus->tracks, track);
bus_track_remove_from_queue(track);
- track->names = hashmap_free_with_destructor(track->names, track_item_free);
+ track->names = hashmap_free(track->names);
track->bus = sd_bus_unref(track->bus);
if (track->destroy_callback)
@@ -180,6 +161,8 @@ _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
return mfree(track);
}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus_track, sd_bus_track, track_free);
+
static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
sd_bus_track *track = userdata;
const char *name, *old, *new;
@@ -220,7 +203,7 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
return 0;
}
- r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
+ r = hashmap_ensure_allocated(&track->names, &track_item_hash_ops);
if (r < 0)
return r;
@@ -416,7 +399,7 @@ void bus_track_close(sd_bus_track *track) {
return;
/* Let's flush out all names */
- hashmap_clear_with_destructor(track->names, track_item_free);
+ hashmap_clear(track->names);
/* Invoke handler */
if (track->handler)
diff --git a/src/libsystemd/sd-bus/bus-track.h b/src/libsystemd/sd-bus/bus-track.h
index f9590265d7..209b989d27 100644
--- a/src/libsystemd/sd-bus/bus-track.h
+++ b/src/libsystemd/sd-bus/bus-track.h
@@ -1,8 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
void bus_track_dispatch(sd_bus_track *track);
void bus_track_close(sd_bus_track *track);
diff --git a/src/libsystemd/sd-bus/bus-type.c b/src/libsystemd/sd-bus/bus-type.c
index bc6726f9cf..18564a5383 100644
--- a/src/libsystemd/sd-bus/bus-type.c
+++ b/src/libsystemd/sd-bus/bus-type.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <errno.h>
@@ -32,32 +30,6 @@ bool bus_type_is_valid(char c) {
return !!memchr(valid, c, sizeof(valid));
}
-bool bus_type_is_valid_in_signature(char c) {
- static const char valid[] = {
- SD_BUS_TYPE_BYTE,
- SD_BUS_TYPE_BOOLEAN,
- SD_BUS_TYPE_INT16,
- SD_BUS_TYPE_UINT16,
- SD_BUS_TYPE_INT32,
- SD_BUS_TYPE_UINT32,
- SD_BUS_TYPE_INT64,
- SD_BUS_TYPE_UINT64,
- SD_BUS_TYPE_DOUBLE,
- SD_BUS_TYPE_STRING,
- SD_BUS_TYPE_OBJECT_PATH,
- SD_BUS_TYPE_SIGNATURE,
- SD_BUS_TYPE_ARRAY,
- SD_BUS_TYPE_VARIANT,
- SD_BUS_TYPE_STRUCT_BEGIN,
- SD_BUS_TYPE_STRUCT_END,
- SD_BUS_TYPE_DICT_ENTRY_BEGIN,
- SD_BUS_TYPE_DICT_ENTRY_END,
- SD_BUS_TYPE_UNIX_FD
- };
-
- return !!memchr(valid, c, sizeof(valid));
-}
-
bool bus_type_is_basic(char c) {
static const char valid[] = {
SD_BUS_TYPE_BYTE,
diff --git a/src/libsystemd/sd-bus/bus-type.h b/src/libsystemd/sd-bus/bus-type.h
index cdac55c62e..0ecd8513fd 100644
--- a/src/libsystemd/sd-bus/bus-type.h
+++ b/src/libsystemd/sd-bus/bus-type.h
@@ -1,15 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include <stdbool.h>
#include "macro.h"
bool bus_type_is_valid(char c) _const_;
-bool bus_type_is_valid_in_signature(char c) _const_;
bool bus_type_is_basic(char c) _const_;
/* "trivial" is systemd's term for what the D-Bus Specification calls
* a "fixed type": that is, a basic type of fixed length */
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 089b51a6d9..3b00bc8157 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <endian.h>
#include <netdb.h>
@@ -180,8 +178,7 @@ static sd_bus* bus_free(sd_bus *b) {
* apps, but are dead. */
assert(s->floating);
- bus_slot_disconnect(s);
- sd_bus_slot_unref(s);
+ bus_slot_disconnect(s, true);
}
if (b->default_bus_ptr)
@@ -197,7 +194,6 @@ static sd_bus* bus_free(sd_bus *b) {
free(b->auth_buffer);
free(b->address);
free(b->machine);
- free(b->cgroup_root);
free(b->description);
free(b->patch_sender);
@@ -235,18 +231,22 @@ _public_ int sd_bus_new(sd_bus **ret) {
assert_return(ret, -EINVAL);
- b = new0(sd_bus, 1);
+ b = new(sd_bus, 1);
if (!b)
return -ENOMEM;
- b->n_ref = REFCNT_INIT;
- b->input_fd = b->output_fd = -1;
- b->inotify_fd = -1;
- b->message_version = 1;
- b->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME;
- b->accept_fd = true;
- b->original_pid = getpid_cached();
- b->n_groups = (size_t) -1;
+ *b = (sd_bus) {
+ .n_ref = REFCNT_INIT,
+ .input_fd = -1,
+ .output_fd = -1,
+ .inotify_fd = -1,
+ .message_version = 1,
+ .creds_mask = SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME,
+ .accept_fd = true,
+ .original_pid = getpid_cached(),
+ .n_groups = (size_t) -1,
+ .close_on_exit = true,
+ };
assert_se(pthread_mutex_init(&b->memfd_cache_mutex, NULL) == 0);
@@ -733,20 +733,28 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
if (path) {
l = strlen(path);
- if (l > sizeof(b->sockaddr.un.sun_path))
+ if (l >= sizeof(b->sockaddr.un.sun_path)) /* We insist on NUL termination */
return -E2BIG;
- b->sockaddr.un.sun_family = AF_UNIX;
- strncpy(b->sockaddr.un.sun_path, path, sizeof(b->sockaddr.un.sun_path));
- b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l;
- } else if (abstract) {
+ b->sockaddr.un = (struct sockaddr_un) {
+ .sun_family = AF_UNIX,
+ };
+
+ memcpy(b->sockaddr.un.sun_path, path, l);
+ b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l + 1;
+
+ } else {
+ assert(abstract);
+
l = strlen(abstract);
- if (l > sizeof(b->sockaddr.un.sun_path) - 1)
+ if (l >= sizeof(b->sockaddr.un.sun_path) - 1) /* We insist on NUL termination */
return -E2BIG;
- b->sockaddr.un.sun_family = AF_UNIX;
- b->sockaddr.un.sun_path[0] = 0;
- strncpy(b->sockaddr.un.sun_path+1, abstract, sizeof(b->sockaddr.un.sun_path)-1);
+ b->sockaddr.un = (struct sockaddr_un) {
+ .sun_family = AF_UNIX,
+ };
+
+ memcpy(b->sockaddr.un.sun_path+1, abstract, l);
b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
}
@@ -952,7 +960,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid)
return -EINVAL;
if (machine) {
- if (!machine_name_is_valid(machine))
+ if (!streq(machine, ".host") && !machine_name_is_valid(machine))
return -EINVAL;
free_and_replace(b->machine, machine);
@@ -967,9 +975,11 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid)
} else
b->nspid = 0;
- b->sockaddr.un.sun_family = AF_UNIX;
- /* Note that we use the old /var/run prefix here, to increase compatibility with really old containers */
- strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path));
+ b->sockaddr.un = (struct sockaddr_un) {
+ .sun_family = AF_UNIX,
+ /* Note that we use the old /var/run prefix here, to increase compatibility with really old containers */
+ .sun_path = "/var/run/dbus/system_bus_socket",
+ };
b->sockaddr_size = SOCKADDR_UN_LEN(b->sockaddr.un);
b->is_local = false;
@@ -1360,38 +1370,88 @@ _public_ int sd_bus_open_user(sd_bus **ret) {
int bus_set_address_system_remote(sd_bus *b, const char *host) {
_cleanup_free_ char *e = NULL;
- char *m = NULL, *c = NULL, *a;
+ char *m = NULL, *c = NULL, *a, *rbracket = NULL, *p = NULL;
assert(b);
assert(host);
- /* Let's see if we shall enter some container */
- m = strchr(host, ':');
- if (m) {
- m++;
+ /* Skip ":"s in ipv6 addresses */
+ if (*host == '[') {
+ char *t;
- /* Let's make sure this is not a port of some kind,
- * and is a valid machine name. */
- if (!in_charset(m, DIGITS) && machine_name_is_valid(m)) {
- char *t;
-
- /* Cut out the host part */
- t = strndupa(host, m - host - 1);
+ rbracket = strchr(host, ']');
+ if (!rbracket)
+ return -EINVAL;
+ t = strndupa(host + 1, rbracket - host - 1);
+ e = bus_address_escape(t);
+ if (!e)
+ return -ENOMEM;
+ } else if ((a = strchr(host, '@'))) {
+ if (*(a + 1) == '[') {
+ _cleanup_free_ char *t = NULL;
+
+ rbracket = strchr(a + 1, ']');
+ if (!rbracket)
+ return -EINVAL;
+ t = new0(char, strlen(host));
+ if (!t)
+ return -ENOMEM;
+ strncat(t, host, a - host + 1);
+ strncat(t, a + 2, rbracket - a - 2);
e = bus_address_escape(t);
if (!e)
return -ENOMEM;
+ } else if (*(a + 1) == '\0' || strchr(a + 1, '@'))
+ return -EINVAL;
+ }
+
+ /* Let's see if a port was given */
+ m = strchr(rbracket ? rbracket + 1 : host, ':');
+ if (m) {
+ char *t;
+ bool got_forward_slash = false;
+
+ p = m + 1;
- c = strjoina(",argv5=--machine=", m);
+ t = strchr(p, '/');
+ if (t) {
+ p = strndupa(p, t - p);
+ got_forward_slash = true;
}
+
+ if (!in_charset(p, "0123456789") || *p == '\0') {
+ if (!machine_name_is_valid(p) || got_forward_slash)
+ return -EINVAL;
+
+ m = TAKE_PTR(p);
+ goto interpret_port_as_machine_old_syntax;
+ }
+ }
+
+ /* Let's see if a machine was given */
+ m = strchr(rbracket ? rbracket + 1 : host, '/');
+ if (m) {
+ m++;
+interpret_port_as_machine_old_syntax:
+ /* Let's make sure this is not a port of some kind,
+ * and is a valid machine name. */
+ if (!in_charset(m, "0123456789") && machine_name_is_valid(m))
+ c = strjoina(",argv", p ? "7" : "5", "=--machine=", m);
}
if (!e) {
- e = bus_address_escape(host);
+ char *t;
+
+ t = strndupa(host, strcspn(host, ":/"));
+
+ e = bus_address_escape(t);
if (!e)
return -ENOMEM;
}
- a = strjoin("unixexec:path=ssh,argv1=-xT,argv2=--,argv3=", e, ",argv4=systemd-stdio-bridge", c);
+ a = strjoin("unixexec:path=ssh,argv1=-xT", p ? ",argv2=-p,argv3=" : "", strempty(p),
+ ",argv", p ? "4" : "2", "=--,argv", p ? "5" : "3", "=", e,
+ ",argv", p ? "6" : "4", "=systemd-stdio-bridge", c);
if (!a)
return -ENOMEM;
@@ -1450,7 +1510,7 @@ _public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) {
assert_return(machine, -EINVAL);
assert_return(ret, -EINVAL);
- assert_return(machine_name_is_valid(machine), -EINVAL);
+ assert_return(streq(machine, ".host") || machine_name_is_valid(machine), -EINVAL);
r = sd_bus_new(&b);
if (r < 0)
@@ -1518,27 +1578,7 @@ void bus_enter_closing(sd_bus *bus) {
bus_set_state(bus, BUS_CLOSING);
}
-_public_ sd_bus *sd_bus_ref(sd_bus *bus) {
- if (!bus)
- return NULL;
-
- assert_se(REFCNT_INC(bus->n_ref) >= 2);
-
- return bus;
-}
-
-_public_ sd_bus *sd_bus_unref(sd_bus *bus) {
- unsigned i;
-
- if (!bus)
- return NULL;
-
- i = REFCNT_DEC(bus->n_ref);
- if (i > 0)
- return NULL;
-
- return bus_free(bus);
-}
+DEFINE_PUBLIC_ATOMIC_REF_UNREF_FUNC(sd_bus, sd_bus, bus_free);
_public_ int sd_bus_is_open(sd_bus *bus) {
assert_return(bus, -EINVAL);
@@ -1611,8 +1651,11 @@ static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) {
return 0;
}
- if (timeout == 0)
- timeout = BUS_DEFAULT_TIMEOUT;
+ if (timeout == 0) {
+ r = sd_bus_get_method_call_timeout(b, &timeout);
+ if (r < 0)
+ return r;
+ }
if (!m->sender && b->patch_sender) {
r = sd_bus_message_set_sender(m, b->patch_sender);
@@ -1913,13 +1956,7 @@ static int timeout_compare(const void *a, const void *b) {
if (x->timeout_usec == 0 && y->timeout_usec != 0)
return 1;
- if (x->timeout_usec < y->timeout_usec)
- return -1;
-
- if (x->timeout_usec > y->timeout_usec)
- return 1;
-
- return 0;
+ return CMP(x->timeout_usec, y->timeout_usec);
}
_public_ int sd_bus_call_async(
@@ -2359,10 +2396,8 @@ static int process_timeout(sd_bus *bus) {
bus->current_slot = NULL;
bus->current_message = NULL;
- if (slot->floating) {
- bus_slot_disconnect(slot);
- sd_bus_slot_unref(slot);
- }
+ if (slot->floating)
+ bus_slot_disconnect(slot, true);
sd_bus_slot_unref(slot);
@@ -2464,10 +2499,8 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
bus->current_handler = NULL;
bus->current_slot = NULL;
- if (slot->floating) {
- bus_slot_disconnect(slot);
- sd_bus_slot_unref(slot);
- }
+ if (slot->floating)
+ bus_slot_disconnect(slot, true);
sd_bus_slot_unref(slot);
@@ -2809,10 +2842,8 @@ static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c)
bus->current_slot = NULL;
bus->current_message = NULL;
- if (slot->floating) {
- bus_slot_disconnect(slot);
- sd_bus_slot_unref(slot);
- }
+ if (slot->floating)
+ bus_slot_disconnect(slot, true);
sd_bus_slot_unref(slot);
@@ -2883,7 +2914,6 @@ finish:
}
static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) {
- BUS_DONT_DESTROY(bus);
int r;
/* Returns 0 when we didn't do anything. This should cause the
@@ -2897,7 +2927,9 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
/* We don't allow recursively invoking sd_bus_process(). */
assert_return(!bus->current_message, -EBUSY);
- assert(!bus->current_slot);
+ assert(!bus->current_slot); /* This should be NULL whenever bus->current_message is */
+
+ BUS_DONT_DESTROY(bus);
switch (bus->state) {
@@ -3166,10 +3198,8 @@ static int add_match_callback(
r = 1;
}
- if (failed && match_slot->floating) {
- bus_slot_disconnect(match_slot);
- sd_bus_slot_unref(match_slot);
- }
+ if (failed && match_slot->floating)
+ bus_slot_disconnect(match_slot, true);
sd_bus_slot_unref(match_slot);
@@ -3382,8 +3412,10 @@ static int quit_callback(sd_event_source *event, void *userdata) {
assert(event);
- sd_bus_flush(bus);
- sd_bus_close(bus);
+ if (bus->close_on_exit) {
+ sd_bus_flush(bus);
+ sd_bus_close(bus);
+ }
return 1;
}
@@ -3896,24 +3928,6 @@ _public_ int sd_bus_get_description(sd_bus *bus, const char **description) {
return 0;
}
-int bus_get_root_path(sd_bus *bus) {
- int r;
-
- if (bus->cgroup_root)
- return 0;
-
- r = cg_get_root_path(&bus->cgroup_root);
- if (r == -ENOENT) {
- bus->cgroup_root = strdup("/");
- if (!bus->cgroup_root)
- return -ENOMEM;
-
- r = 0;
- }
-
- return r;
-}
-
_public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) {
assert_return(bus, -EINVAL);
assert_return(bus = bus_resolve(bus), -ENOPKG);
@@ -4075,3 +4089,51 @@ _public_ int sd_bus_get_n_queued_write(sd_bus *bus, uint64_t *ret) {
*ret = bus->wqueue_size;
return 0;
}
+
+_public_ int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+
+ bus->method_call_timeout = usec;
+ return 0;
+}
+
+_public_ int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) {
+ const char *e;
+ usec_t usec;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(ret, -EINVAL);
+
+ if (bus->method_call_timeout != 0) {
+ *ret = bus->method_call_timeout;
+ return 0;
+ }
+
+ e = secure_getenv("SYSTEMD_BUS_TIMEOUT");
+ if (e && parse_sec(e, &usec) >= 0 && usec != 0) {
+ /* Save the parsed value to avoid multiple parsing. To change the timeout value,
+ * use sd_bus_set_method_call_timeout() instead of setenv(). */
+ *ret = bus->method_call_timeout = usec;
+ return 0;
+ }
+
+ *ret = bus->method_call_timeout = BUS_DEFAULT_TIMEOUT;
+ return 0;
+}
+
+_public_ int sd_bus_set_close_on_exit(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+
+ bus->close_on_exit = b;
+ return 0;
+}
+
+_public_ int sd_bus_get_close_on_exit(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+
+ return bus->close_on_exit;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-address.c b/src/libsystemd/sd-bus/test-bus-address.c
new file mode 100644
index 0000000000..db5ff72ef4
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-address.c
@@ -0,0 +1,69 @@
+#include "sd-bus.h"
+
+#include "bus-internal.h"
+#include "log.h"
+#include "string-util.h"
+#include "strv.h"
+
+static void test_one_address(sd_bus *b,
+ const char *host,
+ int result, const char *expected) {
+ int r;
+
+ r = bus_set_address_system_remote(b, host);
+ log_info("\"%s\" → %d, \"%s\"", host, r, strna(r >= 0 ? b->address : NULL));
+ if (result < 0 || expected) {
+ assert(r == result);
+ if (r >= 0)
+ assert_se(streq(b->address, expected));
+ }
+}
+
+static void test_bus_set_address_system_remote(char **args) {
+ _cleanup_(sd_bus_unrefp) sd_bus *b = NULL;
+
+ assert_se(sd_bus_new(&b) >= 0);
+ if (!strv_isempty(args)) {
+ char **a;
+ STRV_FOREACH(a, args)
+ test_one_address(b, *a, 0, NULL);
+ return;
+ };
+
+ test_one_address(b, "host",
+ 0, "unixexec:path=ssh,argv1=-xT,argv2=--,argv3=host,argv4=systemd-stdio-bridge");
+ test_one_address(b, "host:123",
+ 0, "unixexec:path=ssh,argv1=-xT,argv2=-p,argv3=123,argv4=--,argv5=host,argv6=systemd-stdio-bridge");
+ test_one_address(b, "host:123:123",
+ -EINVAL, NULL);
+ test_one_address(b, "host:",
+ -EINVAL, NULL);
+ test_one_address(b, "user@host",
+ 0, "unixexec:path=ssh,argv1=-xT,argv2=--,argv3=user%40host,argv4=systemd-stdio-bridge");
+ test_one_address(b, "user@host@host",
+ -EINVAL, NULL);
+ test_one_address(b, "[::1]",
+ 0, "unixexec:path=ssh,argv1=-xT,argv2=--,argv3=%3a%3a1,argv4=systemd-stdio-bridge");
+ test_one_address(b, "user@[::1]",
+ 0, "unixexec:path=ssh,argv1=-xT,argv2=--,argv3=user%40%3a%3a1,argv4=systemd-stdio-bridge");
+ test_one_address(b, "user@[::1]:99",
+ 0, "unixexec:path=ssh,argv1=-xT,argv2=-p,argv3=99,argv4=--,argv5=user%40%3a%3a1,argv6=systemd-stdio-bridge");
+ test_one_address(b, "user@[::1]:",
+ -EINVAL, NULL);
+ test_one_address(b, "user@[::1:",
+ -EINVAL, NULL);
+ test_one_address(b, "user@",
+ -EINVAL, NULL);
+ test_one_address(b, "user@@",
+ -EINVAL, NULL);
+}
+
+int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_INFO);
+ log_parse_environment();
+ log_open();
+
+ test_bus_set_address_system_remote(argv + 1);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-benchmark.c b/src/libsystemd/sd-bus/test-bus-benchmark.c
index 8134abba2f..2dd3d41a30 100644
--- a/src/libsystemd/sd-bus/test-bus-benchmark.c
+++ b/src/libsystemd/sd-bus/test-bus-benchmark.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <sys/wait.h>
@@ -12,6 +10,7 @@
#include "bus-util.h"
#include "def.h"
#include "fd-util.h"
+#include "missing_resource.h"
#include "time-util.h"
#include "util.h"
diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c
index 1e25e94586..6181fb163e 100644
--- a/src/libsystemd/sd-bus/test-bus-chat.c
+++ b/src/libsystemd/sd-bus/test-bus-chat.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <fcntl.h>
#include <pthread.h>
@@ -18,6 +16,7 @@
#include "format-util.h"
#include "log.h"
#include "macro.h"
+#include "tests.h"
#include "util.h"
static int match_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
@@ -246,7 +245,7 @@ fail:
return r;
}
-static void* client1(void*p) {
+static void* client1(void *p) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -345,7 +344,7 @@ static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_er
return 1;
}
-static void* client2(void*p) {
+static void* client2(void *p) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -511,11 +510,11 @@ int main(int argc, char *argv[]) {
void *p;
int q, r;
+ test_setup_logging(LOG_INFO);
+
r = server_init(&bus);
- if (r < 0) {
- log_info("Failed to connect to bus, skipping tests.");
- return EXIT_TEST_SKIP;
- }
+ if (r < 0)
+ return log_tests_skipped("Failed to connect to bus");
log_info("Initialized...");
diff --git a/src/libsystemd/sd-bus/test-bus-cleanup.c b/src/libsystemd/sd-bus/test-bus-cleanup.c
index d1d962ebb2..bea722ba06 100644
--- a/src/libsystemd/sd-bus/test-bus-cleanup.c
+++ b/src/libsystemd/sd-bus/test-bus-cleanup.c
@@ -8,6 +8,9 @@
#include "bus-message.h"
#include "bus-util.h"
#include "refcnt.h"
+#include "tests.h"
+
+static bool use_system_bus = false;
static void test_bus_new(void) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
@@ -21,8 +24,12 @@ static int test_bus_open(void) {
int r;
r = sd_bus_open_user(&bus);
- if (IN_SET(r, -ECONNREFUSED, -ENOENT))
- return r;
+ if (IN_SET(r, -ECONNREFUSED, -ENOENT)) {
+ r = sd_bus_open_system(&bus);
+ if (IN_SET(r, -ECONNREFUSED, -ENOENT))
+ return r;
+ use_system_bus = true;
+ }
assert_se(r >= 0);
printf("after open: refcount %u\n", REFCNT_GET(bus->n_ref));
@@ -34,7 +41,7 @@ static void test_bus_new_method_call(void) {
sd_bus *bus = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- assert_se(sd_bus_open_user(&bus) >= 0);
+ assert_se(use_system_bus ? sd_bus_open_system(&bus) >= 0 : sd_bus_open_user(&bus) >= 0);
assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path", "an.interface.name", "AMethodName") >= 0);
@@ -48,7 +55,7 @@ static void test_bus_new_signal(void) {
sd_bus *bus = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- assert_se(sd_bus_open_user(&bus) >= 0);
+ assert_se(use_system_bus ? sd_bus_open_system(&bus) >= 0 : sd_bus_open_user(&bus) >= 0);
assert_se(sd_bus_message_new_signal(bus, &m, "/an/object/path", "an.interface.name", "Name") >= 0);
@@ -59,17 +66,12 @@ static void test_bus_new_signal(void) {
}
int main(int argc, char **argv) {
- int r;
-
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_INFO);
test_bus_new();
- r = test_bus_open();
- if (r < 0) {
- log_info("Failed to connect to bus, skipping tests.");
- return EXIT_TEST_SKIP;
- }
+
+ if (test_bus_open() < 0)
+ return log_tests_skipped("Failed to connect to bus");
test_bus_new_method_call();
test_bus_new_signal();
diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c
index 6746c0973e..c02c459663 100644
--- a/src/libsystemd/sd-bus/test-bus-creds.c
+++ b/src/libsystemd/sd-bus/test-bus-creds.c
@@ -1,25 +1,20 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "sd-bus.h"
#include "bus-dump.h"
#include "bus-util.h"
#include "cgroup-util.h"
+#include "tests.h"
int main(int argc, char *argv[]) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
int r;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
- if (cg_unified_flush() == -ENOMEDIUM) {
- log_info("Skipping test: /sys/fs/cgroup/ not available");
- return EXIT_TEST_SKIP;
- }
+ if (cg_unified_flush() == -ENOMEDIUM)
+ return log_tests_skipped("/sys/fs/cgroup/ not available");
r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL);
log_full_errno(r < 0 ? LOG_ERR : LOG_DEBUG, r, "sd_bus_creds_new_from_pid: %m");
diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c
index 250657b18e..f464b5b23d 100644
--- a/src/libsystemd/sd-bus/test-bus-error.c
+++ b/src/libsystemd/sd-bus/test-bus-error.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "sd-bus.h"
@@ -115,18 +113,18 @@ static void test_error(void) {
assert_se(!sd_bus_error_is_set(&error));
}
-extern const sd_bus_error_map __start_BUS_ERROR_MAP[];
-extern const sd_bus_error_map __stop_BUS_ERROR_MAP[];
+extern const sd_bus_error_map __start_SYSTEMD_BUS_ERROR_MAP[];
+extern const sd_bus_error_map __stop_SYSTEMD_BUS_ERROR_MAP[];
static void dump_mapping_table(void) {
const sd_bus_error_map *m;
printf("----- errno mappings ------\n");
- m = __start_BUS_ERROR_MAP;
- while (m < __stop_BUS_ERROR_MAP) {
+ m = ALIGN_TO_PTR(__start_SYSTEMD_BUS_ERROR_MAP, sizeof(void*));
+ while (m < __stop_SYSTEMD_BUS_ERROR_MAP) {
if (m->code == BUS_ERROR_MAP_END_MARKER) {
- m = ALIGN8_PTR(m+1);
+ m = ALIGN_TO_PTR(m + 1, sizeof(void*));
continue;
}
diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c
index 75804f3458..1a9a35d56b 100644
--- a/src/libsystemd/sd-bus/test-bus-gvariant.c
+++ b/src/libsystemd/sd-bus/test-bus-gvariant.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#if HAVE_GLIB
#include <glib.h>
@@ -15,11 +13,14 @@
#include "bus-message.h"
#include "bus-util.h"
#include "macro.h"
+#include "tests.h"
#include "util.h"
static void test_bus_gvariant_is_fixed_size(void) {
+ log_info("/* %s */", __func__);
+
assert_se(bus_gvariant_is_fixed_size("") > 0);
- assert_se(bus_gvariant_is_fixed_size("()") > 0);
+ assert_se(bus_gvariant_is_fixed_size("()") == -EINVAL);
assert_se(bus_gvariant_is_fixed_size("y") > 0);
assert_se(bus_gvariant_is_fixed_size("u") > 0);
assert_se(bus_gvariant_is_fixed_size("b") > 0);
@@ -43,8 +44,10 @@ static void test_bus_gvariant_is_fixed_size(void) {
}
static void test_bus_gvariant_get_size(void) {
+ log_info("/* %s */", __func__);
+
assert_se(bus_gvariant_get_size("") == 0);
- assert_se(bus_gvariant_get_size("()") == 1);
+ assert_se(bus_gvariant_get_size("()") == -EINVAL);
assert_se(bus_gvariant_get_size("y") == 1);
assert_se(bus_gvariant_get_size("u") == 4);
assert_se(bus_gvariant_get_size("b") == 1);
@@ -75,8 +78,10 @@ static void test_bus_gvariant_get_size(void) {
}
static void test_bus_gvariant_get_alignment(void) {
+ log_info("/* %s */", __func__);
+
assert_se(bus_gvariant_get_alignment("") == 1);
- assert_se(bus_gvariant_get_alignment("()") == 1);
+ assert_se(bus_gvariant_get_alignment("()") == -EINVAL);
assert_se(bus_gvariant_get_alignment("y") == 1);
assert_se(bus_gvariant_get_alignment("b") == 1);
assert_se(bus_gvariant_get_alignment("u") == 4);
@@ -115,20 +120,25 @@ static void test_bus_gvariant_get_alignment(void) {
assert_se(bus_gvariant_get_alignment("((t)(t))") == 8);
}
-static void test_marshal(void) {
+static int test_marshal(void) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *n = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_free_ void *blob;
+ _cleanup_free_ void *blob = NULL;
size_t sz;
int r;
r = sd_bus_open_user(&bus);
if (r < 0)
- exit(EXIT_TEST_SKIP);
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_tests_skipped_errno(r, "Failed to connect to bus");
bus->message_version = 2; /* dirty hack to enable gvariant */
- assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path/which/is/really/really/long/so/that/we/hit/the/eight/bit/boundary/by/quite/some/margin/to/test/this/stuff/that/it/really/works", "an.interface.name", "AMethodName") >= 0);
+ r = sd_bus_message_new_method_call(bus, &m, "a.service.name",
+ "/an/object/path/which/is/really/really/long/so/that/we/hit/the/eight/bit/boundary/by/quite/some/margin/to/test/this/stuff/that/it/really/works",
+ "an.interface.name", "AMethodName");
+ assert_se(r >= 0);
assert_cc(sizeof(struct bus_header) == 16);
@@ -196,14 +206,16 @@ static void test_marshal(void) {
assert_se(sd_bus_message_seal(m, 4712, 0) >= 0);
assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
+
+ return EXIT_SUCCESS;
}
int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_DEBUG);
test_bus_gvariant_is_fixed_size();
test_bus_gvariant_get_size();
test_bus_gvariant_get_alignment();
- test_marshal();
- return 0;
+ return test_marshal();
}
diff --git a/src/libsystemd/sd-bus/test-bus-introspect.c b/src/libsystemd/sd-bus/test-bus-introspect.c
index 00167cb643..940dbfe0e7 100644
--- a/src/libsystemd/sd-bus/test-bus-introspect.c
+++ b/src/libsystemd/sd-bus/test-bus-introspect.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "bus-introspect.h"
#include "log.h"
diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c
index c647f0ff12..1e9810ce4f 100644
--- a/src/libsystemd/sd-bus/test-bus-marshal.c
+++ b/src/libsystemd/sd-bus/test-bus-marshal.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <math.h>
#include <stdlib.h>
@@ -20,9 +18,10 @@
#include "bus-label.h"
#include "bus-message.h"
#include "bus-util.h"
+#include "escape.h"
#include "fd-util.h"
-#include "hexdecoct.h"
#include "log.h"
+#include "tests.h"
#include "util.h"
static void test_bus_path_encode_unique(void) {
@@ -112,7 +111,7 @@ int main(int argc, char *argv[]) {
uint8_t u, v;
void *buffer = NULL;
size_t sz;
- char *h;
+ _cleanup_free_ char *h = NULL;
const int32_t integer_array[] = { -1, -2, 0, 1, 2 }, *return_array;
char *s;
_cleanup_free_ char *first = NULL, *second = NULL, *third = NULL;
@@ -122,9 +121,13 @@ int main(int argc, char *argv[]) {
double dbl;
uint64_t u64;
+ test_setup_logging(LOG_INFO);
+
r = sd_bus_default_user(&bus);
if (r < 0)
- return EXIT_TEST_SKIP;
+ r = sd_bus_default_system(&bus);
+ if (r < 0)
+ return log_tests_skipped("Failed to connect to bus");
r = sd_bus_message_new_method_call(bus, &m, "foobar.waldo", "/", "foobar.waldo", "Piep");
assert_se(r >= 0);
@@ -151,7 +154,7 @@ int main(int argc, char *argv[]) {
assert_se(r >= 0);
r = sd_bus_message_append(m, "()");
- assert_se(r >= 0);
+ assert_se(r == -EINVAL);
r = sd_bus_message_append(m, "ba(ss)", 255, 3, "aaa", "1", "bbb", "2", "ccc", "3");
assert_se(r >= 0);
@@ -194,14 +197,12 @@ int main(int argc, char *argv[]) {
r = bus_message_get_blob(m, &buffer, &sz);
assert_se(r >= 0);
- h = hexmem(buffer, sz);
+ h = cescape_length(buffer, sz);
assert_se(h);
-
log_info("message size = %zu, contents =\n%s", sz, h);
- free(h);
#if HAVE_GLIB
-#ifndef __SANITIZE_ADDRESS__
+#if !HAS_FEATURE_ADDRESS_SANITIZER
{
GDBusMessage *g;
char *p;
@@ -295,7 +296,7 @@ int main(int argc, char *argv[]) {
assert_se(v == 10);
r = sd_bus_message_read(m, "()");
- assert_se(r > 0);
+ assert_se(r < 0);
r = sd_bus_message_read(m, "ba(ss)", &boolean, 3, &x, &y, &a, &b, &c, &d);
assert_se(r > 0);
@@ -376,7 +377,7 @@ int main(int argc, char *argv[]) {
assert_se(sd_bus_message_verify_type(m, 'a', "{yv}") > 0);
- r = sd_bus_message_skip(m, "a{yv}y(ty)y(yt)y()");
+ r = sd_bus_message_skip(m, "a{yv}y(ty)y(yt)y");
assert_se(r >= 0);
assert_se(sd_bus_message_verify_type(m, 'b', NULL) > 0);
diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c
index 2822a8f82a..0949d8dee6 100644
--- a/src/libsystemd/sd-bus/test-bus-match.c
+++ b/src/libsystemd/sd-bus/test-bus-match.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "bus-match.h"
#include "bus-message.h"
@@ -8,6 +6,7 @@
#include "bus-util.h"
#include "log.h"
#include "macro.h"
+#include "tests.h"
static bool mask[32];
@@ -79,9 +78,13 @@ int main(int argc, char *argv[]) {
sd_bus_slot slots[19];
int r;
+ test_setup_logging(LOG_INFO);
+
r = sd_bus_open_user(&bus);
if (r < 0)
- return EXIT_TEST_SKIP;
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_tests_skipped("Failed to connect to bus");
assert_se(match_add(slots, &root, "arg2='wal\\'do',sender='foo',type='signal',interface='bar.x',", 1) >= 0);
assert_se(match_add(slots, &root, "arg2='wal\\'do2',sender='foo',type='signal',interface='bar.x',", 2) >= 0);
diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c
index 094dd6c8a0..3c5bb88f4e 100644
--- a/src/libsystemd/sd-bus/test-bus-objects.c
+++ b/src/libsystemd/sd-bus/test-bus-objects.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <pthread.h>
#include <stdlib.h>
@@ -209,7 +207,7 @@ static const sd_bus_vtable vtable2[] = {
static int enumerator_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
if (object_path_startswith("/value", path))
- assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c", NULL));
+ assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c"));
return 1;
}
@@ -217,7 +215,7 @@ static int enumerator_callback(sd_bus *bus, const char *path, void *userdata, ch
static int enumerator2_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
if (object_path_startswith("/value/a", path))
- assert_se(*nodes = strv_new("/value/a/x", "/value/a/y", "/value/a/z", NULL));
+ assert_se(*nodes = strv_new("/value/a/x", "/value/a/y", "/value/a/z"));
return 1;
}
diff --git a/src/libsystemd/sd-bus/test-bus-server.c b/src/libsystemd/sd-bus/test-bus-server.c
index 31b54e252c..0f1b9645bc 100644
--- a/src/libsystemd/sd-bus/test-bus-server.c
+++ b/src/libsystemd/sd-bus/test-bus-server.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <pthread.h>
#include <stdlib.h>
@@ -130,10 +128,8 @@ static int client(struct context *c) {
return log_error_errno(r, "Failed to allocate method call: %m");
r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0) {
- log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, -r));
return 0;
}
diff --git a/src/libsystemd/sd-bus/test-bus-signature.c b/src/libsystemd/sd-bus/test-bus-signature.c
index 1ba1909198..84648dbc2a 100644
--- a/src/libsystemd/sd-bus/test-bus-signature.c
+++ b/src/libsystemd/sd-bus/test-bus-signature.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "bus-internal.h"
#include "bus-signature.h"
@@ -16,9 +14,9 @@ int main(int argc, char *argv[]) {
assert_se(signature_is_single("v", false));
assert_se(signature_is_single("as", false));
assert_se(signature_is_single("(ss)", false));
- assert_se(signature_is_single("()", false));
- assert_se(signature_is_single("(()()()()())", false));
- assert_se(signature_is_single("(((())))", false));
+ assert_se(!signature_is_single("()", false));
+ assert_se(!signature_is_single("(()()()()())", false));
+ assert_se(!signature_is_single("(((())))", false));
assert_se(signature_is_single("((((s))))", false));
assert_se(signature_is_single("{ss}", true));
assert_se(signature_is_single("a{ss}", false));
@@ -63,7 +61,7 @@ int main(int argc, char *argv[]) {
assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas", false));
assert_se(!signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau", false));
- assert_se(signature_is_valid("(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))", false));
+ assert_se(signature_is_valid("((((((((((((((((((((((((((((((((s))))))))))))))))))))))))))))))))", false));
assert_se(!signature_is_valid("((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))", false));
assert_se(namespace_complex_pattern("", ""));
diff --git a/src/libsystemd/sd-bus/test-bus-track.c b/src/libsystemd/sd-bus/test-bus-track.c
index 48d708b258..68a0010368 100644
--- a/src/libsystemd/sd-bus/test-bus-track.c
+++ b/src/libsystemd/sd-bus/test-bus-track.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <errno.h>
#include <sys/socket.h>
@@ -8,6 +6,7 @@
#include "sd-bus.h"
#include "macro.h"
+#include "tests.h"
static bool track_cb_called_x = false;
static bool track_cb_called_y = false;
@@ -46,23 +45,31 @@ int main(int argc, char *argv[]) {
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(sd_bus_track_unrefp) sd_bus_track *x = NULL, *y = NULL;
_cleanup_(sd_bus_unrefp) sd_bus *a = NULL, *b = NULL;
+ bool use_system_bus = false;
const char *unique;
int r;
+ test_setup_logging(LOG_INFO);
+
r = sd_event_default(&event);
assert_se(r >= 0);
r = sd_bus_open_user(&a);
if (IN_SET(r, -ECONNREFUSED, -ENOENT)) {
- log_info("Failed to connect to bus, skipping tests.");
- return EXIT_TEST_SKIP;
+ r = sd_bus_open_system(&a);
+ if (IN_SET(r, -ECONNREFUSED, -ENOENT))
+ return log_tests_skipped("Failed to connect to bus");
+ use_system_bus = true;
}
assert_se(r >= 0);
r = sd_bus_attach_event(a, event, SD_EVENT_PRIORITY_NORMAL);
assert_se(r >= 0);
- r = sd_bus_open_user(&b);
+ if (use_system_bus)
+ r = sd_bus_open_system(&b);
+ else
+ r = sd_bus_open_user(&b);
assert_se(r >= 0);
r = sd_bus_attach_event(b, event, SD_EVENT_PRIORITY_NORMAL);
diff --git a/src/libsystemd/sd-bus/test-bus-watch-bind.c b/src/libsystemd/sd-bus/test-bus-watch-bind.c
index 42a9ce5301..4b3da30079 100644
--- a/src/libsystemd/sd-bus/test-bus-watch-bind.c
+++ b/src/libsystemd/sd-bus/test-bus-watch-bind.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <pthread.h>
@@ -10,7 +8,6 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "mkdir.h"
#include "path-util.h"
@@ -18,6 +15,7 @@
#include "rm-rf.h"
#include "socket-util.h"
#include "string-util.h"
+#include "tmpfile-util.h"
static int method_foobar(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
log_info("Got Foobar() call.");
@@ -42,10 +40,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 = {
- .un.sun_family = AF_UNIX,
- };
+ union sockaddr_union u = {};
const char *path = p;
+ int salen;
log_debug("Initializing server");
@@ -68,12 +65,13 @@ static void* thread_server(void *p) {
assert_se(symlink(basename(suffixed), suffixed2) >= 0);
(void) usleep(100 * USEC_PER_MSEC);
- strncpy(u.un.sun_path, path, sizeof(u.un.sun_path));
+ salen = sockaddr_un_set_path(&u.un, path);
+ assert_se(salen >= 0);
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
assert_se(fd >= 0);
- assert_se(bind(fd, &u.sa, SOCKADDR_UN_LEN(u.un)) >= 0);
+ assert_se(bind(fd, &u.sa, salen) >= 0);
usleep(100 * USEC_PER_MSEC);
assert_se(listen(fd, SOMAXCONN) >= 0);
diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c
index 9e9b115557..218210f234 100644
--- a/src/libsystemd/sd-daemon/sd-daemon.c
+++ b/src/libsystemd/sd-daemon/sd-daemon.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <errno.h>
#include <limits.h>
@@ -21,6 +19,7 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "fs-util.h"
+#include "io-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
@@ -446,12 +445,8 @@ _public_ int sd_pid_notify_with_fds(
const int *fds,
unsigned n_fds) {
- union sockaddr_union sockaddr = {
- .sa.sa_family = AF_UNIX,
- };
- struct iovec iovec = {
- .iov_base = (char*) state,
- };
+ union sockaddr_union sockaddr = {};
+ struct iovec iovec;
struct msghdr msghdr = {
.msg_iov = &iovec,
.msg_iovlen = 1,
@@ -461,7 +456,7 @@ _public_ int sd_pid_notify_with_fds(
struct cmsghdr *cmsg = NULL;
const char *e;
bool send_ucred;
- int r;
+ int r, salen;
if (!state) {
r = -EINVAL;
@@ -477,14 +472,9 @@ _public_ int sd_pid_notify_with_fds(
if (!e)
return 0;
- /* Must be an abstract socket, or an absolute path */
- if (!IN_SET(e[0], '@', '/') || e[1] == 0) {
- r = -EINVAL;
- goto finish;
- }
-
- if (strlen(e) > sizeof(sockaddr.un.sun_path)) {
- r = -EINVAL;
+ salen = sockaddr_un_set_path(&sockaddr.un, e);
+ if (salen < 0) {
+ r = salen;
goto finish;
}
@@ -496,13 +486,8 @@ _public_ int sd_pid_notify_with_fds(
(void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
- iovec.iov_len = strlen(state);
-
- strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
- if (sockaddr.un.sun_path[0] == '@')
- sockaddr.un.sun_path[0] = 0;
-
- msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un);
+ iovec = IOVEC_MAKE_STRING(state);
+ msghdr.msg_namelen = salen;
send_ucred =
(pid != 0 && pid != getpid_cached()) ||
diff --git a/src/libsystemd/sd-device/device-enumerator-private.h b/src/libsystemd/sd-device/device-enumerator-private.h
index 5d58ac93ca..87411bfdd8 100644
--- a/src/libsystemd/sd-device/device-enumerator-private.h
+++ b/src/libsystemd/sd-device/device-enumerator-private.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-device.h"
int device_enumerator_scan_devices(sd_device_enumerator *enumeartor);
@@ -10,6 +9,7 @@ int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *de
int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator);
sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator);
sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator);
+sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size_t *ret_n_devices);
#define FOREACH_DEVICE_AND_SUBSYSTEM(enumerator, device) \
for (device = device_enumerator_get_first(enumerator); \
diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c
index ee71fa8810..20529aafd3 100644
--- a/src/libsystemd/sd-device/device-enumerator.c
+++ b/src/libsystemd/sd-device/device-enumerator.c
@@ -7,7 +7,6 @@
#include "device-util.h"
#include "dirent-util.h"
#include "fd-util.h"
-#include "prioq.h"
#include "set.h"
#include "string-util.h"
#include "strv.h"
@@ -26,7 +25,8 @@ struct sd_device_enumerator {
unsigned n_ref;
DeviceEnumerationType type;
- Prioq *devices;
+ sd_device **devices;
+ size_t n_devices, n_allocated, current_device_index;
bool scan_uptodate;
Set *match_subsystem;
@@ -45,50 +45,43 @@ _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
assert(ret);
- enumerator = new0(sd_device_enumerator, 1);
+ enumerator = new(sd_device_enumerator, 1);
if (!enumerator)
return -ENOMEM;
- enumerator->n_ref = 1;
- enumerator->type = _DEVICE_ENUMERATION_TYPE_INVALID;
+ *enumerator = (sd_device_enumerator) {
+ .n_ref = 1,
+ .type = _DEVICE_ENUMERATION_TYPE_INVALID,
+ };
*ret = TAKE_PTR(enumerator);
return 0;
}
-_public_ sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator) {
- assert_return(enumerator, NULL);
-
- assert_se((++ enumerator->n_ref) >= 2);
-
- return enumerator;
-}
-
-_public_ sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator) {
- if (enumerator && (-- enumerator->n_ref) == 0) {
- sd_device *device;
+static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumerator) {
+ size_t i;
- while ((device = prioq_pop(enumerator->devices)))
- sd_device_unref(device);
-
- prioq_free(enumerator->devices);
-
- set_free_free(enumerator->match_subsystem);
- set_free_free(enumerator->nomatch_subsystem);
- hashmap_free_free_free(enumerator->match_sysattr);
- hashmap_free_free_free(enumerator->nomatch_sysattr);
- hashmap_free_free_free(enumerator->match_property);
- set_free_free(enumerator->match_sysname);
- set_free_free(enumerator->match_tag);
- sd_device_unref(enumerator->match_parent);
+ assert(enumerator);
- free(enumerator);
- }
+ for (i = 0; i < enumerator->n_devices; i++)
+ sd_device_unref(enumerator->devices[i]);
+
+ free(enumerator->devices);
+ set_free_free(enumerator->match_subsystem);
+ set_free_free(enumerator->nomatch_subsystem);
+ hashmap_free_free_free(enumerator->match_sysattr);
+ hashmap_free_free_free(enumerator->nomatch_sysattr);
+ hashmap_free_free_free(enumerator->match_property);
+ set_free_free(enumerator->match_sysname);
+ set_free_free(enumerator->match_tag);
+ sd_device_unref(enumerator->match_parent);
- return NULL;
+ return mfree(enumerator);
}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_enumerator, sd_device_enumerator, device_enumerator_free);
+
_public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) {
Set **set;
int r;
@@ -256,10 +249,11 @@ int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator)
return 0;
}
-static int device_compare(const void *_a, const void *_b) {
- sd_device *a = (sd_device *)_a, *b = (sd_device *)_b;
+static int device_compare(sd_device * const *_a, sd_device * const *_b) {
+ sd_device *a = *(sd_device **)_a, *b = *(sd_device **)_b;
const char *devpath_a, *devpath_b, *sound_a;
bool delay_a, delay_b;
+ int r;
assert_se(sd_device_get_devpath(a, &devpath_a) >= 0);
assert_se(sd_device_get_devpath(b, &devpath_b) >= 0);
@@ -300,27 +294,21 @@ static int device_compare(const void *_a, const void *_b) {
/* md and dm devices are enumerated after all other devices */
delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-");
delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-");
- if (delay_a != delay_b)
- return delay_a - delay_b;
+ r = CMP(delay_a, delay_b);
+ if (r != 0)
+ return r;
return strcmp(devpath_a, devpath_b);
}
int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) {
- int r;
-
assert_return(enumerator, -EINVAL);
assert_return(device, -EINVAL);
- r = prioq_ensure_allocated(&enumerator->devices, device_compare);
- if (r < 0)
- return r;
-
- r = prioq_put(enumerator->devices, device, NULL);
- if (r < 0)
- return r;
+ if (!GREEDY_REALLOC(enumerator->devices, enumerator->n_allocated, enumerator->n_devices + 1))
+ return -ENOMEM;
- sd_device_ref(device);
+ enumerator->devices[enumerator->n_devices++] = sd_device_ref(device);
return 0;
}
@@ -470,8 +458,7 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator,
FOREACH_DIRENT_ALL(dent, dir, return -errno) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
char syspath[strlen(path) + 1 + strlen(dent->d_name) + 1];
- dev_t devnum;
- int ifindex, initialized, k;
+ int initialized, k;
if (dent->d_name[0] == '.')
continue;
@@ -479,7 +466,7 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator,
if (!match_sysname(enumerator, dent->d_name))
continue;
- (void)sprintf(syspath, "%s%s", path, dent->d_name);
+ (void) sprintf(syspath, "%s%s", path, dent->d_name);
k = sd_device_new_from_syspath(&device, syspath);
if (k < 0) {
@@ -490,21 +477,9 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator,
continue;
}
- k = sd_device_get_devnum(device, &devnum);
- if (k < 0) {
- r = k;
- continue;
- }
-
- k = sd_device_get_ifindex(device, &ifindex);
- if (k < 0) {
- r = k;
- continue;
- }
-
- k = sd_device_get_is_initialized(device, &initialized);
- if (k < 0) {
- r = k;
+ initialized = sd_device_get_is_initialized(device);
+ if (initialized < 0) {
+ r = initialized;
continue;
}
@@ -520,7 +495,8 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator,
*/
if (!enumerator->match_allow_uninitialized &&
!initialized &&
- (major(devnum) > 0 || ifindex > 0))
+ (sd_device_get_devnum(device, NULL) >= 0 ||
+ sd_device_get_ifindex(device, NULL) >= 0))
continue;
if (!match_parent(enumerator, device))
@@ -578,7 +554,7 @@ static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *bas
if (!dir)
return -errno;
- log_debug(" device-enumerator: scanning %s", path);
+ log_debug("sd-device-enumerator: Scanning %s", path);
FOREACH_DIRENT_ALL(dent, dir, return -errno) {
int k;
@@ -610,10 +586,9 @@ static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const c
dir = opendir(path);
if (!dir) {
- if (errno == ENOENT)
- return 0;
- else
- return log_error_errno(errno, "sd-device-enumerator: could not open tags directory %s: %m", path);
+ if (errno != ENOENT)
+ return log_debug_errno(errno, "sd-device-enumerator: Failed to open tags directory %s: %m", path);
+ return 0;
}
/* TODO: filter away subsystems? */
@@ -738,7 +713,7 @@ static int parent_crawl_children(sd_device_enumerator *enumerator, const char *p
dir = opendir(path);
if (!dir)
- return log_debug_errno(errno, "sd-device-enumerate: could not open parent directory %s: %m", path);
+ return log_debug_errno(errno, "sd-device-enumerator: Failed to open parent directory %s: %m", path);
FOREACH_DIRENT_ALL(dent, dir, return -errno) {
_cleanup_free_ char *child = NULL;
@@ -761,7 +736,7 @@ static int parent_crawl_children(sd_device_enumerator *enumerator, const char *p
if (maxdepth > 0)
parent_crawl_children(enumerator, child, maxdepth - 1);
else
- log_debug("device-enumerate: max depth reached, %s: ignoring devices", child);
+ log_debug("sd-device-enumerator: Max depth reached, %s: ignoring devices", child);
}
return r;
@@ -789,25 +764,25 @@ static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) {
static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
int r = 0;
- log_debug("device-enumerator: scan all dirs");
+ log_debug("sd-device-enumerator: Scan all dirs");
if (access("/sys/subsystem", F_OK) >= 0) {
/* we have /subsystem/, forget all the old stuff */
r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL);
if (r < 0)
- return log_debug_errno(r, "device-enumerator: failed to scan /sys/subsystem: %m");
+ return log_debug_errno(r, "sd-device-enumerator: Failed to scan /sys/subsystem: %m");
} else {
int k;
k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
if (k < 0) {
- log_debug_errno(k, "device-enumerator: failed to scan /sys/bus: %m");
+ log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
r = k;
}
k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
if (k < 0) {
- log_debug_errno(k, "device-enumerator: failed to scan /sys/class: %m");
+ log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m");
r = k;
}
}
@@ -815,9 +790,36 @@ static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
return r;
}
+static void device_enumerator_dedup_devices(sd_device_enumerator *enumerator) {
+ sd_device **a, **b, **end;
+
+ assert(enumerator);
+
+ if (enumerator->n_devices <= 1)
+ return;
+
+ a = enumerator->devices + 1;
+ b = enumerator->devices;
+ end = enumerator->devices + enumerator->n_devices;
+
+ for (; a < end; a++) {
+ const char *devpath_a, *devpath_b;
+
+ assert_se(sd_device_get_devpath(*a, &devpath_a) >= 0);
+ assert_se(sd_device_get_devpath(*b, &devpath_b) >= 0);
+
+ if (path_equal(devpath_a, devpath_b))
+ sd_device_unref(*a);
+ else
+ *(++b) = *a;
+ }
+
+ enumerator->n_devices = b - enumerator->devices + 1;
+}
+
int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
- sd_device *device;
int r = 0, k;
+ size_t i;
assert(enumerator);
@@ -825,8 +827,10 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES)
return 0;
- while ((device = prioq_pop(enumerator->devices)))
- sd_device_unref(device);
+ for (i = 0; i < enumerator->n_devices; i++)
+ sd_device_unref(enumerator->devices[i]);
+
+ enumerator->n_devices = 0;
if (!set_isempty(enumerator->match_tag)) {
k = enumerator_scan_devices_tags(enumerator);
@@ -842,7 +846,11 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
r = k;
}
+ typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
+ device_enumerator_dedup_devices(enumerator);
+
enumerator->scan_uptodate = true;
+ enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES;
return r;
}
@@ -856,27 +864,29 @@ _public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *
if (r < 0)
return NULL;
- enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES;
+ enumerator->current_device_index = 0;
+
+ if (enumerator->n_devices == 0)
+ return NULL;
- return prioq_peek(enumerator->devices);
+ return enumerator->devices[0];
}
_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) {
assert_return(enumerator, NULL);
if (!enumerator->scan_uptodate ||
- enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES)
+ enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES ||
+ enumerator->current_device_index + 1 >= enumerator->n_devices)
return NULL;
- sd_device_unref(prioq_pop(enumerator->devices));
-
- return prioq_peek(enumerator->devices);
+ return enumerator->devices[++enumerator->current_device_index];
}
int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
- sd_device *device;
const char *subsysdir;
int r = 0, k;
+ size_t i;
assert(enumerator);
@@ -884,14 +894,16 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS)
return 0;
- while ((device = prioq_pop(enumerator->devices)))
- sd_device_unref(device);
+ for (i = 0; i < enumerator->n_devices; i++)
+ sd_device_unref(enumerator->devices[i]);
+
+ enumerator->n_devices = 0;
/* modules */
if (match_subsystem(enumerator, "module")) {
k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL);
if (k < 0) {
- log_debug_errno(k, "device-enumerator: failed to scan modules: %m");
+ log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m");
r = k;
}
}
@@ -905,7 +917,7 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
if (match_subsystem(enumerator, "subsystem")) {
k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL);
if (k < 0) {
- log_debug_errno(k, "device-enumerator: failed to scan subsystems: %m");
+ log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m");
r = k;
}
}
@@ -914,12 +926,16 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
if (match_subsystem(enumerator, "drivers")) {
k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers");
if (k < 0) {
- log_debug_errno(k, "device-enumerator: failed to scan drivers: %m");
+ log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m");
r = k;
}
}
+ typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
+ device_enumerator_dedup_devices(enumerator);
+
enumerator->scan_uptodate = true;
+ enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS;
return r;
}
@@ -933,33 +949,56 @@ _public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerato
if (r < 0)
return NULL;
- enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS;
+ enumerator->current_device_index = 0;
+
+ if (enumerator->n_devices == 0)
+ return NULL;
- return prioq_peek(enumerator->devices);
+ return enumerator->devices[0];
}
_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) {
assert_return(enumerator, NULL);
- if (enumerator->scan_uptodate ||
- enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS)
+ if (!enumerator->scan_uptodate ||
+ enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS ||
+ enumerator->current_device_index + 1 >= enumerator->n_devices)
return NULL;
- sd_device_unref(prioq_pop(enumerator->devices));
-
- return prioq_peek(enumerator->devices);
+ return enumerator->devices[++enumerator->current_device_index];
}
sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) {
assert_return(enumerator, NULL);
- return prioq_peek(enumerator->devices);
+ if (!enumerator->scan_uptodate)
+ return NULL;
+
+ enumerator->current_device_index = 0;
+
+ if (enumerator->n_devices == 0)
+ return NULL;
+
+ return enumerator->devices[0];
}
sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) {
assert_return(enumerator, NULL);
- sd_device_unref(prioq_pop(enumerator->devices));
+ if (!enumerator->scan_uptodate ||
+ enumerator->current_device_index + 1 >= enumerator->n_devices)
+ return NULL;
+
+ return enumerator->devices[++enumerator->current_device_index];
+}
+
+sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size_t *ret_n_devices) {
+ assert(enumerator);
+ assert(ret_n_devices);
+
+ if (!enumerator->scan_uptodate)
+ return NULL;
- return prioq_peek(enumerator->devices);
+ *ret_n_devices = enumerator->n_devices;
+ return enumerator->devices;
}
diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h
index 64da0501fe..3ffca35cbe 100644
--- a/src/libsystemd/sd-device/device-internal.h
+++ b/src/libsystemd/sd-device/device-internal.h
@@ -1,15 +1,18 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include "sd-device.h"
#include "hashmap.h"
#include "set.h"
+#include "time-util.h"
struct sd_device {
- uint64_t n_ref;
+ unsigned n_ref;
+
+ int watch_handle;
sd_device *parent;
- bool parent_set; /* no need to try to reload parent */
OrderedHashmap *properties;
Iterator properties_iterator;
@@ -23,60 +26,59 @@ struct sd_device {
Set *sysattrs; /* names of sysattrs */
Iterator sysattrs_iterator;
- bool sysattrs_read; /* don't try to re-read sysattrs once read */
Set *tags;
Iterator tags_iterator;
uint64_t tags_generation; /* changes whenever the tags are changed */
uint64_t tags_iterator_generation; /* generation when iteration was started */
- bool property_tags_outdated; /* need to update TAGS= property */
Set *devlinks;
Iterator devlinks_iterator;
uint64_t devlinks_generation; /* changes whenever the devlinks are changed */
uint64_t devlinks_iterator_generation; /* generation when iteration was started */
- bool property_devlinks_outdated; /* need to update DEVLINKS= property */
int devlink_priority;
+ int ifindex;
+ char *devtype;
+ char *devname;
+ dev_t devnum;
+
char **properties_strv; /* the properties hashmap as a strv */
uint8_t *properties_nulstr; /* the same as a nulstr */
size_t properties_nulstr_len;
- bool properties_buf_outdated; /* need to reread hashmap */
-
- int watch_handle;
char *syspath;
const char *devpath;
const char *sysnum;
char *sysname;
- bool sysname_set; /* don't reread sysname */
-
- char *devtype;
- int ifindex;
- char *devname;
- dev_t devnum;
char *subsystem;
- bool subsystem_set; /* don't reread subsystem */
char *driver_subsystem; /* only set for the 'drivers' subsystem */
- bool driver_subsystem_set; /* don't reread subsystem */
char *driver;
- bool driver_set; /* don't reread driver */
char *id_filename;
- bool is_initialized;
uint64_t usec_initialized;
mode_t devmode;
uid_t devuid;
gid_t devgid;
- bool uevent_loaded; /* don't reread uevent */
+ bool parent_set:1; /* no need to try to reload parent */
+ bool sysattrs_read:1; /* don't try to re-read sysattrs once read */
+ bool property_tags_outdated:1; /* need to update TAGS= property */
+ bool property_devlinks_outdated:1; /* need to update DEVLINKS= property */
+ bool properties_buf_outdated:1; /* need to reread hashmap */
+ bool sysname_set:1; /* don't reread sysname */
+ bool subsystem_set:1; /* don't reread subsystem */
+ bool driver_subsystem_set:1; /* don't reread subsystem */
+ bool driver_set:1; /* don't reread driver */
+ bool uevent_loaded:1; /* don't reread uevent */
bool db_loaded; /* don't reread db */
- bool sealed; /* don't read more information from uevent/db */
- bool db_persist; /* don't clean up the db when switching from initrd to real root */
+ bool is_initialized:1;
+ bool sealed:1; /* don't read more information from uevent/db */
+ bool db_persist:1; /* don't clean up the db when switching from initrd to real root */
};
typedef enum DeviceAction {
@@ -96,7 +98,6 @@ int device_new_aux(sd_device **ret);
int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db);
int device_add_property_internal(sd_device *device, const char *key, const char *value);
int device_read_uevent_file(sd_device *device);
-int device_read_db_aux(sd_device *device, bool force);
int device_set_syspath(sd_device *device, const char *_syspath, bool verify);
int device_set_ifindex(sd_device *device, const char *ifindex);
@@ -106,7 +107,7 @@ int device_set_devtype(sd_device *device, const char *_devtype);
int device_set_devnum(sd_device *device, const char *major, const char *minor);
int device_set_subsystem(sd_device *device, const char *_subsystem);
int device_set_driver(sd_device *device, const char *_driver);
-int device_set_usec_initialized(sd_device *device, const char *initialized);
+int device_set_usec_initialized(sd_device *device, usec_t when);
DeviceAction device_action_from_string(const char *s) _pure_;
const char *device_action_to_string(DeviceAction a) _const_;
diff --git a/src/libsystemd/sd-device/device-monitor-private.h b/src/libsystemd/sd-device/device-monitor-private.h
new file mode 100644
index 0000000000..2659cc3c46
--- /dev/null
+++ b/src/libsystemd/sd-device/device-monitor-private.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-device.h"
+
+typedef enum MonitorNetlinkGroup {
+ MONITOR_GROUP_NONE,
+ MONITOR_GROUP_KERNEL,
+ MONITOR_GROUP_UDEV,
+ _MONITOR_NETLINK_GROUP_MAX,
+ _MONITOR_NETLINK_GROUP_INVALID = -1,
+} MonitorNetlinkGroup;
+
+int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group, int fd);
+int device_monitor_disconnect(sd_device_monitor *m);
+int device_monitor_allow_unicast_sender(sd_device_monitor *m, sd_device_monitor *sender);
+int device_monitor_enable_receiving(sd_device_monitor *m);
+int device_monitor_get_fd(sd_device_monitor *m);
+int device_monitor_send_device(sd_device_monitor *m, sd_device_monitor *destination, sd_device *device);
+int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret);
diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c
new file mode 100644
index 0000000000..b86932663e
--- /dev/null
+++ b/src/libsystemd/sd-device/device-monitor.c
@@ -0,0 +1,762 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <linux/filter.h>
+#include <linux/netlink.h>
+#include <sys/socket.h>
+
+#include "sd-device.h"
+#include "sd-event.h"
+
+#include "MurmurHash2.h"
+#include "alloc-util.h"
+#include "device-monitor-private.h"
+#include "device-private.h"
+#include "device-util.h"
+#include "fd-util.h"
+#include "format-util.h"
+#include "hashmap.h"
+#include "io-util.h"
+#include "missing.h"
+#include "mountpoint-util.h"
+#include "set.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "strv.h"
+
+struct sd_device_monitor {
+ unsigned n_ref;
+
+ int sock;
+ union sockaddr_union snl;
+ union sockaddr_union snl_trusted_sender;
+ bool bound;
+
+ Hashmap *subsystem_filter;
+ Set *tag_filter;
+ bool filter_uptodate;
+
+ sd_event *event;
+ sd_event_source *event_source;
+ sd_device_monitor_handler_t callback;
+ void *userdata;
+};
+
+#define UDEV_MONITOR_MAGIC 0xfeedcafe
+
+typedef struct monitor_netlink_header {
+ /* "libudev" prefix to distinguish libudev and kernel messages */
+ char prefix[8];
+ /* Magic to protect against daemon <-> Library message format mismatch
+ * Used in the kernel from socket filter rules; needs to be stored in network order */
+ unsigned magic;
+ /* Total length of header structure known to the sender */
+ unsigned header_size;
+ /* Properties string buffer */
+ unsigned properties_off;
+ unsigned properties_len;
+ /* Hashes of primary device properties strings, to let libudev subscribers
+ * use in-kernel socket filters; values need to be stored in network order */
+ unsigned filter_subsystem_hash;
+ unsigned filter_devtype_hash;
+ unsigned filter_tag_bloom_hi;
+ unsigned filter_tag_bloom_lo;
+} monitor_netlink_header;
+
+static int monitor_set_nl_address(sd_device_monitor *m) {
+ union sockaddr_union snl;
+ socklen_t addrlen;
+
+ assert(m);
+
+ /* Get the address the kernel has assigned us.
+ * It is usually, but not necessarily the pid. */
+ addrlen = sizeof(struct sockaddr_nl);
+ if (getsockname(m->sock, &snl.sa, &addrlen) < 0)
+ return -errno;
+
+ m->snl.nl.nl_pid = snl.nl.nl_pid;
+ return 0;
+}
+
+int device_monitor_allow_unicast_sender(sd_device_monitor *m, sd_device_monitor *sender) {
+ assert_return(m, -EINVAL);
+ assert_return(sender, -EINVAL);
+
+ m->snl_trusted_sender.nl.nl_pid = sender->snl.nl.nl_pid;
+ return 0;
+}
+
+_public_ int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size) {
+ int r, n = (int) size;
+
+ assert_return(m, -EINVAL);
+ assert_return((size_t) n == size, -EINVAL);
+
+ if (m->bound)
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "sd-device-monitor: Socket fd is already bound. "
+ "It may be dangerous to change buffer size. "
+ "Refusing to change buffer size.");
+
+ if (setsockopt_int(m->sock, SOL_SOCKET, SO_RCVBUF, n) < 0) {
+ r = setsockopt_int(m->sock, SOL_SOCKET, SO_RCVBUFFORCE, n);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int device_monitor_disconnect(sd_device_monitor *m) {
+ assert(m);
+
+ m->sock = safe_close(m->sock);
+ return 0;
+}
+
+int device_monitor_get_fd(sd_device_monitor *m) {
+ assert_return(m, -EINVAL);
+
+ return m->sock;
+}
+
+int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group, int fd) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
+ _cleanup_close_ int sock = -1;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(group >= 0 && group < _MONITOR_NETLINK_GROUP_MAX, -EINVAL);
+
+ if (group == MONITOR_GROUP_UDEV &&
+ access("/run/udev/control", F_OK) < 0 &&
+ dev_is_devtmpfs() <= 0) {
+
+ /*
+ * We do not support subscribing to uevents if no instance of
+ * udev is running. Uevents would otherwise broadcast the
+ * processing data of the host into containers, which is not
+ * desired.
+ *
+ * Containers will currently not get any udev uevents, until
+ * a supporting infrastructure is available.
+ *
+ * We do not set a netlink multicast group here, so the socket
+ * will not receive any messages.
+ */
+
+ log_debug("sd-device-monitor: The udev service seems not to be active, disabling the monitor");
+ group = MONITOR_GROUP_NONE;
+ }
+
+ if (fd < 0) {
+ sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
+ if (sock < 0)
+ return log_debug_errno(errno, "sd-device-monitor: Failed to create socket: %m");
+ }
+
+ m = new(sd_device_monitor, 1);
+ if (!m)
+ return -ENOMEM;
+
+ *m = (sd_device_monitor) {
+ .n_ref = 1,
+ .sock = fd >= 0 ? fd : TAKE_FD(sock),
+ .bound = fd >= 0,
+ .snl.nl.nl_family = AF_NETLINK,
+ .snl.nl.nl_groups = group,
+ };
+
+ if (fd >= 0) {
+ r = monitor_set_nl_address(m);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device-monitor: Failed to set netlink address: %m");
+ }
+
+ *ret = TAKE_PTR(m);
+ return 0;
+}
+
+_public_ int sd_device_monitor_new(sd_device_monitor **ret) {
+ return device_monitor_new_full(ret, MONITOR_GROUP_UDEV, -1);
+}
+
+_public_ int sd_device_monitor_stop(sd_device_monitor *m) {
+ assert_return(m, -EINVAL);
+
+ m->event_source = sd_event_source_unref(m->event_source);
+ (void) device_monitor_disconnect(m);
+
+ return 0;
+}
+
+static int device_monitor_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ sd_device_monitor *m = userdata;
+
+ assert(m);
+
+ if (device_monitor_receive_device(m, &device) <= 0)
+ return 0;
+
+ if (m->callback)
+ return m->callback(m, device, m->userdata);
+
+ return 0;
+}
+
+_public_ int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata) {
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ if (!m->event) {
+ r = sd_device_monitor_attach_event(m, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ r = device_monitor_enable_receiving(m);
+ if (r < 0)
+ return r;
+
+ m->callback = callback;
+ m->userdata = userdata;
+
+ r = sd_event_add_io(m->event, &m->event_source, m->sock, EPOLLIN, device_monitor_event_handler, m);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(m->event_source, "sd-device-monitor");
+
+ return 0;
+}
+
+_public_ int sd_device_monitor_detach_event(sd_device_monitor *m) {
+ assert_return(m, -EINVAL);
+
+ (void) sd_device_monitor_stop(m);
+ m->event = sd_event_unref(m->event);
+
+ return 0;
+}
+
+_public_ int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event) {
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->event, -EBUSY);
+
+ if (event)
+ m->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&m->event);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+_public_ sd_event *sd_device_monitor_get_event(sd_device_monitor *m) {
+ assert_return(m, NULL);
+
+ return m->event;
+}
+
+_public_ sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m) {
+ assert_return(m, NULL);
+
+ return m->event_source;
+}
+
+int device_monitor_enable_receiving(sd_device_monitor *m) {
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = sd_device_monitor_filter_update(m);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device-monitor: Failed to update filter: %m");
+
+ if (!m->bound) {
+ /* enable receiving of sender credentials */
+ r = setsockopt_int(m->sock, SOL_SOCKET, SO_PASSCRED, true);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device-monitor: Failed to set socket option SO_PASSCRED: %m");
+
+ if (bind(m->sock, &m->snl.sa, sizeof(struct sockaddr_nl)) < 0)
+ return log_debug_errno(errno, "sd-device-monitor: Failed to bind monitoring socket: %m");
+
+ m->bound = true;
+
+ r = monitor_set_nl_address(m);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device-monitor: Failed to set address: %m");
+ }
+
+ return 0;
+}
+
+static sd_device_monitor *device_monitor_free(sd_device_monitor *m) {
+ assert(m);
+
+ (void) sd_device_monitor_detach_event(m);
+
+ hashmap_free_free_free(m->subsystem_filter);
+ set_free_free(m->tag_filter);
+
+ return mfree(m);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_monitor, sd_device_monitor, device_monitor_free);
+
+static int passes_filter(sd_device_monitor *m, sd_device *device) {
+ const char *tag, *subsystem, *devtype, *s, *d = NULL;
+ Iterator i;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(device, -EINVAL);
+
+ if (hashmap_isempty(m->subsystem_filter))
+ goto tag;
+
+ r = sd_device_get_subsystem(device, &s);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_devtype(device, &d);
+ if (r < 0 && r != -ENOENT)
+ return r;
+
+ HASHMAP_FOREACH_KEY(devtype, subsystem, m->subsystem_filter, i) {
+ if (!streq(s, subsystem))
+ continue;
+
+ if (!devtype)
+ goto tag;
+
+ if (!d)
+ continue;
+
+ if (streq(d, devtype))
+ goto tag;
+ }
+
+ return 0;
+
+tag:
+ if (set_isempty(m->tag_filter))
+ return 1;
+
+ SET_FOREACH(tag, m->tag_filter, i)
+ if (sd_device_has_tag(device, tag) > 0)
+ return 1;
+
+ return 0;
+}
+
+int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ union {
+ monitor_netlink_header nlh;
+ char raw[8192];
+ } buf;
+ struct iovec iov = {
+ .iov_base = &buf,
+ .iov_len = sizeof(buf)
+ };
+ char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
+ union sockaddr_union snl;
+ struct msghdr smsg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cred_msg,
+ .msg_controllen = sizeof(cred_msg),
+ .msg_name = &snl,
+ .msg_namelen = sizeof(snl),
+ };
+ struct cmsghdr *cmsg;
+ struct ucred *cred;
+ ssize_t buflen, bufpos;
+ bool is_initialized = false;
+ int r;
+
+ assert(ret);
+
+ buflen = recvmsg(m->sock, &smsg, 0);
+ if (buflen < 0) {
+ if (errno != EINTR)
+ log_debug_errno(errno, "sd-device-monitor: Failed to receive message: %m");
+ return -errno;
+ }
+
+ if (buflen < 32 || (smsg.msg_flags & MSG_TRUNC))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "sd-device-monitor: Invalid message length.");
+
+ if (snl.nl.nl_groups == MONITOR_GROUP_NONE) {
+ /* unicast message, check if we trust the sender */
+ if (m->snl_trusted_sender.nl.nl_pid == 0 ||
+ snl.nl.nl_pid != m->snl_trusted_sender.nl.nl_pid)
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "sd-device-monitor: Unicast netlink message ignored.");
+
+ } else if (snl.nl.nl_groups == MONITOR_GROUP_KERNEL) {
+ if (snl.nl.nl_pid > 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "sd-device-monitor: Multicast kernel netlink message from PID %"PRIu32" ignored.", snl.nl.nl_pid);
+ }
+
+ cmsg = CMSG_FIRSTHDR(&smsg);
+ if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS)
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "sd-device-monitor: No sender credentials received, message ignored.");
+
+ cred = (struct ucred*) CMSG_DATA(cmsg);
+ if (cred->uid != 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "sd-device-monitor: Sender uid="UID_FMT", message ignored.", cred->uid);
+
+ if (streq(buf.raw, "libudev")) {
+ /* udev message needs proper version magic */
+ if (buf.nlh.magic != htobe32(UDEV_MONITOR_MAGIC))
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "sd-device-monitor: Invalid message signature (%x != %x)",
+ buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC));
+
+ if (buf.nlh.properties_off+32 > (size_t) buflen)
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "sd-device-monitor: Invalid message length (%u > %zd)",
+ buf.nlh.properties_off+32, buflen);
+
+ bufpos = buf.nlh.properties_off;
+
+ /* devices received from udev are always initialized */
+ is_initialized = true;
+
+ } else {
+ /* kernel message with header */
+ bufpos = strlen(buf.raw) + 1;
+ if ((size_t) bufpos < sizeof("a@/d") || bufpos >= buflen)
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "sd-device-monitor: Invalid message length");
+
+ /* check message header */
+ if (!strstr(buf.raw, "@/"))
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "sd-device-monitor: Invalid message header");
+ }
+
+ r = device_new_from_nulstr(&device, (uint8_t*) &buf.raw[bufpos], buflen - bufpos);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device-monitor: Failed to create device from received message: %m");
+
+ if (is_initialized)
+ device_set_is_initialized(device);
+
+ /* Skip device, if it does not pass the current filter */
+ r = passes_filter(m, device);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "sd-device-monitor: Failed to check received device passing filter: %m");
+ if (r == 0)
+ log_device_debug(device, "sd-device-monitor: Received device does not pass filter, ignoring");
+ else
+ *ret = TAKE_PTR(device);
+
+ return r;
+}
+
+static uint32_t string_hash32(const char *str) {
+ return MurmurHash2(str, strlen(str), 0);
+}
+
+/* Get a bunch of bit numbers out of the hash, and set the bits in our bit field */
+static uint64_t string_bloom64(const char *str) {
+ uint64_t bits = 0;
+ uint32_t hash = string_hash32(str);
+
+ bits |= 1LLU << (hash & 63);
+ bits |= 1LLU << ((hash >> 6) & 63);
+ bits |= 1LLU << ((hash >> 12) & 63);
+ bits |= 1LLU << ((hash >> 18) & 63);
+ return bits;
+}
+
+int device_monitor_send_device(
+ sd_device_monitor *m,
+ sd_device_monitor *destination,
+ sd_device *device) {
+
+ monitor_netlink_header nlh = {
+ .prefix = "libudev",
+ .magic = htobe32(UDEV_MONITOR_MAGIC),
+ .header_size = sizeof nlh,
+ };
+ struct iovec iov[2] = {
+ { .iov_base = &nlh, .iov_len = sizeof nlh },
+ };
+ struct msghdr smsg = {
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+ /* default destination for sending */
+ union sockaddr_union default_destination = {
+ .nl.nl_family = AF_NETLINK,
+ .nl.nl_groups = MONITOR_GROUP_UDEV,
+ };
+ uint64_t tag_bloom_bits;
+ const char *buf, *val;
+ ssize_t count;
+ size_t blen;
+ int r;
+
+ assert(m);
+ assert(device);
+
+ r = device_get_properties_nulstr(device, (const uint8_t **) &buf, &blen);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "sd-device-monitor: Failed to get device properties: %m");
+ if (blen < 32) {
+ log_device_debug(device, "sd-device-monitor: Length of device property nulstr is too small to contain valid device information");
+ return -EINVAL;
+ }
+
+ /* fill in versioned header */
+ r = sd_device_get_subsystem(device, &val);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "sd-device-monitor: Failed to get device subsystem: %m");
+ nlh.filter_subsystem_hash = htobe32(string_hash32(val));
+
+ if (sd_device_get_devtype(device, &val) >= 0)
+ nlh.filter_devtype_hash = htobe32(string_hash32(val));
+
+ /* add tag bloom filter */
+ tag_bloom_bits = 0;
+ FOREACH_DEVICE_TAG(device, val)
+ tag_bloom_bits |= string_bloom64(val);
+
+ if (tag_bloom_bits > 0) {
+ nlh.filter_tag_bloom_hi = htobe32(tag_bloom_bits >> 32);
+ nlh.filter_tag_bloom_lo = htobe32(tag_bloom_bits & 0xffffffff);
+ }
+
+ /* add properties list */
+ nlh.properties_off = iov[0].iov_len;
+ nlh.properties_len = blen;
+ iov[1] = IOVEC_MAKE((char*) buf, blen);
+
+ /*
+ * Use custom address for target, or the default one.
+ *
+ * If we send to a multicast group, we will get
+ * ECONNREFUSED, which is expected.
+ */
+ smsg.msg_name = destination ? &destination->snl : &default_destination;
+ smsg.msg_namelen = sizeof(struct sockaddr_nl);
+ count = sendmsg(m->sock, &smsg, 0);
+ if (count < 0) {
+ if (!destination && errno == ECONNREFUSED) {
+ log_device_debug(device, "sd-device-monitor: Passed to netlink monitor");
+ return 0;
+ } else
+ return log_device_debug_errno(device, errno, "sd-device-monitor: Failed to send device to netlink monitor: %m");
+ }
+
+ log_device_debug(device, "sd-device-monitor: Passed %zi byte to netlink monitor", count);
+ return count;
+}
+
+static void bpf_stmt(struct sock_filter *ins, unsigned *i,
+ unsigned short code, unsigned data) {
+ ins[(*i)++] = (struct sock_filter) {
+ .code = code,
+ .k = data,
+ };
+}
+
+static void bpf_jmp(struct sock_filter *ins, unsigned *i,
+ unsigned short code, unsigned data,
+ unsigned short jt, unsigned short jf) {
+ ins[(*i)++] = (struct sock_filter) {
+ .code = code,
+ .jt = jt,
+ .jf = jf,
+ .k = data,
+ };
+}
+
+_public_ int sd_device_monitor_filter_update(sd_device_monitor *m) {
+ struct sock_filter ins[512] = {};
+ struct sock_fprog filter;
+ const char *subsystem, *devtype, *tag;
+ unsigned i = 0;
+ Iterator it;
+
+ assert_return(m, -EINVAL);
+
+ if (m->filter_uptodate)
+ return 0;
+
+ if (hashmap_isempty(m->subsystem_filter) &&
+ set_isempty(m->tag_filter)) {
+ m->filter_uptodate = true;
+ return 0;
+ }
+
+ /* load magic in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, magic));
+ /* jump if magic matches */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0);
+ /* wrong magic, pass packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
+
+ if (!set_isempty(m->tag_filter)) {
+ int tag_matches = set_size(m->tag_filter);
+
+ /* add all tags matches */
+ SET_FOREACH(tag, m->tag_filter, it) {
+ uint64_t tag_bloom_bits = string_bloom64(tag);
+ uint32_t tag_bloom_hi = tag_bloom_bits >> 32;
+ uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff;
+
+ /* load device bloom bits in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_tag_bloom_hi));
+ /* clear bits (tag bits & bloom bits) */
+ bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi);
+ /* jump to next tag if it does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3);
+
+ /* load device bloom bits in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_tag_bloom_lo));
+ /* clear bits (tag bits & bloom bits) */
+ bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo);
+ /* jump behind end of tag match block if tag matches */
+ tag_matches--;
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0);
+ }
+
+ /* nothing matched, drop packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
+ }
+
+ /* add all subsystem matches */
+ if (!hashmap_isempty(m->subsystem_filter)) {
+ HASHMAP_FOREACH_KEY(devtype, subsystem, m->subsystem_filter, it) {
+ uint32_t hash = string_hash32(subsystem);
+
+ /* load device subsystem value in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_subsystem_hash));
+ if (!devtype) {
+ /* jump if subsystem does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ } else {
+ /* jump if subsystem does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3);
+ /* load device devtype value in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_devtype_hash));
+ /* jump if value does not match */
+ hash = string_hash32(devtype);
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ }
+
+ /* matched, pass packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
+
+ if (i+1 >= ELEMENTSOF(ins))
+ return -E2BIG;
+ }
+
+ /* nothing matched, drop packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
+ }
+
+ /* matched, pass packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
+
+ /* install filter */
+ filter = (struct sock_fprog) {
+ .len = i,
+ .filter = ins,
+ };
+ if (setsockopt(m->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0)
+ return -errno;
+
+ m->filter_uptodate = true;
+ return 0;
+}
+
+_public_ int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype) {
+ _cleanup_free_ char *s = NULL, *d = NULL;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(subsystem, -EINVAL);
+
+ s = strdup(subsystem);
+ if (!s)
+ return -ENOMEM;
+
+ if (devtype) {
+ d = strdup(devtype);
+ if (!d)
+ return -ENOMEM;
+ }
+
+ r = hashmap_ensure_allocated(&m->subsystem_filter, NULL);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->subsystem_filter, s, d);
+ if (r < 0)
+ return r;
+
+ s = d = NULL;
+ m->filter_uptodate = false;
+
+ return 0;
+}
+
+_public_ int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(tag, -EINVAL);
+
+ t = strdup(tag);
+ if (!t)
+ return -ENOMEM;
+
+ r = set_ensure_allocated(&m->tag_filter, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(m->tag_filter, t);
+ if (r == -EEXIST)
+ return 0;
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(t);
+ m->filter_uptodate = false;
+
+ return 0;
+}
+
+_public_ int sd_device_monitor_filter_remove(sd_device_monitor *m) {
+ static const struct sock_fprog filter = { 0, NULL };
+
+ assert_return(m, -EINVAL);
+
+ m->subsystem_filter = hashmap_free_free_free(m->subsystem_filter);
+ m->tag_filter = set_free_free(m->tag_filter);
+
+ if (setsockopt(m->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0)
+ return -errno;
+
+ m->filter_uptodate = true;
+ return 0;
+}
diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c
index d1f1b085ac..01a5aa3d3f 100644
--- a/src/libsystemd/sd-device/device-private.c
+++ b/src/libsystemd/sd-device/device-private.c
@@ -24,6 +24,7 @@
#include "string-util.h"
#include "strv.h"
#include "strxcpyx.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
@@ -46,81 +47,6 @@ int device_add_property(sd_device *device, const char *key, const char *value) {
return 0;
}
-static int device_add_property_internal_from_string(sd_device *device, const char *str) {
- _cleanup_free_ char *key = NULL;
- char *value;
-
- assert(device);
- assert(str);
-
- key = strdup(str);
- if (!key)
- return -ENOMEM;
-
- value = strchr(key, '=');
- if (!value)
- return -EINVAL;
-
- *value = '\0';
-
- if (isempty(++value))
- value = NULL;
-
- return device_add_property_internal(device, key, value);
-}
-
-static int handle_db_line(sd_device *device, char key, const char *value) {
- char *path;
- int r;
-
- assert(device);
- assert(value);
-
- switch (key) {
- case 'S':
- path = strjoina("/dev/", value);
- r = device_add_devlink(device, path);
- if (r < 0)
- return r;
-
- break;
- case 'L':
- r = safe_atoi(value, &device->devlink_priority);
- if (r < 0)
- return r;
-
- break;
- case 'E':
- r = device_add_property_internal_from_string(device, value);
- if (r < 0)
- return r;
-
- break;
- case 'G':
- r = device_add_tag(device, value);
- if (r < 0)
- return r;
-
- break;
- case 'W':
- r = safe_atoi(value, &device->watch_handle);
- if (r < 0)
- return r;
-
- break;
- case 'I':
- r = device_set_usec_initialized(device, value);
- if (r < 0)
- return r;
-
- break;
- default:
- log_debug("device db: unknown key '%c'", key);
- }
-
- return 0;
-}
-
void device_set_devlink_priority(sd_device *device, int priority) {
assert(device);
@@ -134,119 +60,16 @@ void device_set_is_initialized(sd_device *device) {
}
int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) {
- char num[DECIMAL_STR_MAX(usec_t)];
- usec_t usec_initialized;
- int r;
+ usec_t when;
assert(device);
if (device_old && device_old->usec_initialized > 0)
- usec_initialized = device_old->usec_initialized;
+ when = device_old->usec_initialized;
else
- usec_initialized = now(CLOCK_MONOTONIC);
-
- r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized);
- if (r < 0)
- return -errno;
-
- r = device_set_usec_initialized(device, num);
- if (r < 0)
- return r;
+ when = now(CLOCK_MONOTONIC);
- return 0;
-}
-
-static int device_read_db(sd_device *device) {
- _cleanup_free_ char *db = NULL;
- char *path;
- const char *id, *value;
- char key;
- size_t db_len;
- unsigned i;
- int r;
-
- enum {
- PRE_KEY,
- KEY,
- PRE_VALUE,
- VALUE,
- INVALID_LINE,
- } state = PRE_KEY;
-
- assert(device);
-
- if (device->db_loaded || device->sealed)
- return 0;
-
- r = device_get_id_filename(device, &id);
- if (r < 0)
- return r;
-
- path = strjoina("/run/udev/data/", id);
-
- r = read_full_file(path, &db, &db_len);
- if (r < 0) {
- if (r == -ENOENT)
- return 0;
- else
- return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path);
- }
-
- /* devices with a database entry are initialized */
- device_set_is_initialized(device);
-
- for (i = 0; i < db_len; i++) {
- switch (state) {
- case PRE_KEY:
- if (!strchr(NEWLINE, db[i])) {
- key = db[i];
-
- state = KEY;
- }
-
- break;
- case KEY:
- if (db[i] != ':') {
- log_debug("sd-device: ignoring invalid db entry with key '%c'", key);
-
- state = INVALID_LINE;
- } else {
- db[i] = '\0';
-
- state = PRE_VALUE;
- }
-
- break;
- case PRE_VALUE:
- value = &db[i];
-
- state = VALUE;
-
- break;
- case INVALID_LINE:
- if (strchr(NEWLINE, db[i]))
- state = PRE_KEY;
-
- break;
- case VALUE:
- if (strchr(NEWLINE, db[i])) {
- db[i] = '\0';
- r = handle_db_line(device, key, value);
- if (r < 0)
- log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value);
-
- state = PRE_KEY;
- }
-
- break;
- default:
- assert_not_reached("invalid state when parsing db");
- }
- }
-
- device->db_loaded = true;
-
- return 0;
+ return device_set_usec_initialized(device, when);
}
uint64_t device_get_properties_generation(sd_device *device) {
@@ -271,13 +94,16 @@ int device_get_devnode_mode(sd_device *device, mode_t *mode) {
int r;
assert(device);
- assert(mode);
r = device_read_db(device);
if (r < 0)
return r;
- *mode = device->devmode;
+ if (device->devmode == (mode_t) -1)
+ return -ENOENT;
+
+ if (mode)
+ *mode = device->devmode;
return 0;
}
@@ -286,13 +112,16 @@ int device_get_devnode_uid(sd_device *device, uid_t *uid) {
int r;
assert(device);
- assert(uid);
r = device_read_db(device);
if (r < 0)
return r;
- *uid = device->devuid;
+ if (device->devuid == (uid_t) -1)
+ return -ENOENT;
+
+ if (uid)
+ *uid = device->devuid;
return 0;
}
@@ -321,13 +150,16 @@ int device_get_devnode_gid(sd_device *device, gid_t *gid) {
int r;
assert(device);
- assert(gid);
r = device_read_db(device);
if (r < 0)
return r;
- *gid = device->devgid;
+ if (device->devgid == (gid_t) -1)
+ return -ENOENT;
+
+ if (gid)
+ *gid = device->devgid;
return 0;
}
@@ -367,43 +199,49 @@ static int device_amend(sd_device *device, const char *key, const char *value) {
/* the caller must verify or trust this data (e.g., if it comes from the kernel) */
r = device_set_syspath(device, path, false);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set syspath to '%s': %m", path);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set syspath to '%s': %m", path);
} else if (streq(key, "SUBSYSTEM")) {
r = device_set_subsystem(device, value);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set subsystem to '%s': %m", value);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem to '%s': %m", value);
} else if (streq(key, "DEVTYPE")) {
r = device_set_devtype(device, value);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set devtype to '%s': %m", value);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set devtype to '%s': %m", value);
} else if (streq(key, "DEVNAME")) {
r = device_set_devname(device, value);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set devname to '%s': %m", value);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set devname to '%s': %m", value);
} else if (streq(key, "USEC_INITIALIZED")) {
- r = device_set_usec_initialized(device, value);
+ usec_t t;
+
+ r = safe_atou64(value, &t);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set usec-initialized to '%s': %m", value);
+ return log_device_debug_errno(device, r, "sd-device: Failed to parse timestamp '%s': %m", value);
+
+ r = device_set_usec_initialized(device, t);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "sd-device: Failed to set usec-initialized to '%s': %m", value);
} else if (streq(key, "DRIVER")) {
r = device_set_driver(device, value);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set driver to '%s': %m", value);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set driver to '%s': %m", value);
} else if (streq(key, "IFINDEX")) {
r = device_set_ifindex(device, value);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set ifindex to '%s': %m", value);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set ifindex to '%s': %m", value);
} else if (streq(key, "DEVMODE")) {
r = device_set_devmode(device, value);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set devmode to '%s': %m", value);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set devmode to '%s': %m", value);
} else if (streq(key, "DEVUID")) {
r = device_set_devuid(device, value);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set devuid to '%s': %m", value);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set devuid to '%s': %m", value);
} else if (streq(key, "DEVGID")) {
r = device_set_devgid(device, value);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set devgid to '%s': %m", value);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set devgid to '%s': %m", value);
} else if (streq(key, "DEVLINKS")) {
const char *word, *state;
size_t l;
@@ -416,7 +254,7 @@ static int device_amend(sd_device *device, const char *key, const char *value) {
r = device_add_devlink(device, devlink);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not add devlink '%s': %m", devlink);
+ return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", devlink);
}
} else if (streq(key, "TAGS")) {
const char *word, *state;
@@ -425,17 +263,17 @@ static int device_amend(sd_device *device, const char *key, const char *value) {
FOREACH_WORD_SEPARATOR(word, l, value, ":", state) {
char tag[l + 1];
- (void)strncpy(tag, word, l);
+ (void) strncpy(tag, word, l);
tag[l] = '\0';
r = device_add_tag(device, tag);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not add tag '%s': %m", tag);
+ return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", tag);
}
} else {
r = device_add_property_internal(device, key, value);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not add property '%s=%s': %m", key, value);
+ return log_device_debug_errno(device, r, "sd-device: Failed to add property '%s=%s': %m", key, value);
}
return 0;
@@ -471,7 +309,7 @@ static int device_append(sd_device *device, char *key, const char **_major, cons
value = strchr(key, '=');
if (!value) {
- log_debug("sd-device: not a key-value pair: '%s'", key);
+ log_device_debug(device, "sd-device: Not a key-value pair: '%s'", key);
return -EINVAL;
}
@@ -488,6 +326,15 @@ static int device_append(sd_device *device, char *key, const char **_major, cons
action = device_action_from_string(value);
if (action == _DEVICE_ACTION_INVALID)
return -EINVAL;
+ /* FIXME: remove once we no longer flush previuos state for each action */
+ if (action == DEVICE_ACTION_BIND || action == DEVICE_ACTION_UNBIND) {
+ static bool warned;
+ if (!warned) {
+ log_device_debug(device, "sd-device: ignoring actions 'bind' and 'unbind'");
+ warned = true;
+ }
+ return -EINVAL;
+ }
} else if (streq(key, "SEQNUM")) {
r = safe_atou64(value, &seqnum);
if (r < 0)
@@ -527,7 +374,7 @@ static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum
assert(device);
if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) {
- log_debug("sd-device: device created from strv lacks devpath, subsystem, action or seqnum");
+ log_device_debug(device, "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum.");
return -EINVAL;
}
@@ -541,7 +388,7 @@ int device_new_from_strv(sd_device **ret, char **strv) {
char **key;
const char *major = NULL, *minor = NULL;
DeviceAction action = _DEVICE_ACTION_INVALID;
- uint64_t seqnum;
+ uint64_t seqnum = 0;
int r;
assert(ret);
@@ -560,7 +407,7 @@ int device_new_from_strv(sd_device **ret, char **strv) {
if (major) {
r = device_set_devnum(device, major, minor);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor);
}
r = device_verify(device, action, seqnum);
@@ -576,7 +423,7 @@ int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
const char *major = NULL, *minor = NULL;
DeviceAction action = _DEVICE_ACTION_INVALID;
- uint64_t seqnum;
+ uint64_t seqnum = 0;
unsigned i = 0;
int r;
@@ -595,7 +442,7 @@ int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) {
key = (char*)&nulstr[i];
end = memchr(key, '\0', len - i);
if (!end) {
- log_debug("sd-device: failed to parse nulstr");
+ log_device_debug(device, "sd-device: Failed to parse nulstr");
return -EINVAL;
}
i += end - key + 1;
@@ -608,7 +455,7 @@ int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) {
if (major) {
r = device_set_devnum(device, major, minor);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor);
}
r = device_verify(device, action, seqnum);
@@ -717,13 +564,16 @@ int device_get_watch_handle(sd_device *device, int *handle) {
int r;
assert(device);
- assert(handle);
r = device_read_db(device);
if (r < 0)
return r;
- *handle = device->watch_handle;
+ if (device->watch_handle < 0)
+ return -ENOENT;
+
+ if (handle)
+ *handle = device->watch_handle;
return 0;
}
@@ -843,6 +693,22 @@ int device_new_from_synthetic_event(sd_device **new_device, const char *syspath,
return 0;
}
+int device_new_from_stat_rdev(sd_device **ret, const struct stat *st) {
+ char type;
+
+ assert(ret);
+ assert(st);
+
+ if (S_ISBLK(st->st_mode))
+ type = 'b';
+ else if (S_ISCHR(st->st_mode))
+ type = 'c';
+ else
+ return -ENOTTY;
+
+ return sd_device_new_from_devnum(ret, type, st->st_rdev);
+}
+
int device_copy_properties(sd_device *device_dst, sd_device *device_src) {
const char *property, *value;
int r;
@@ -1055,8 +921,8 @@ int device_update_db(sd_device *device) {
goto fail;
}
- log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty",
- path, device->devpath);
+ log_device_debug(device, "sd-device: Created %s file '%s' for '%s'", has_info ? "db" : "empty",
+ path, device->devpath);
return 0;
@@ -1064,7 +930,7 @@ fail:
(void) unlink(path);
(void) unlink(path_tmp);
- return log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath);
+ return log_device_debug_errno(device, r, "sd-device: Failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath);
}
int device_delete_db(sd_device *device) {
@@ -1086,9 +952,3 @@ int device_delete_db(sd_device *device) {
return 0;
}
-
-int device_read_db_force(sd_device *device) {
- assert(device);
-
- return device_read_db_aux(device, true);
-}
diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h
index 800c9ba7bd..56558b38c6 100644
--- a/src/libsystemd/sd-device/device-private.h
+++ b/src/libsystemd/sd-device/device-private.h
@@ -1,15 +1,16 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <inttypes.h>
#include <stdbool.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include "sd-device.h"
int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len);
int device_new_from_strv(sd_device **ret, char **strv);
+int device_new_from_stat_rdev(sd_device **ret, const struct stat *st);
int device_get_id_filename(sd_device *device, const char **ret);
@@ -48,4 +49,7 @@ int device_new_from_synthetic_event(sd_device **new_device, const char *syspath,
int device_tag_index(sd_device *dev, sd_device *dev_old, bool add);
int device_update_db(sd_device *device);
int device_delete_db(sd_device *device);
-int device_read_db_force(sd_device *device);
+int device_read_db_internal(sd_device *device, bool force);
+static inline int device_read_db(sd_device *device) {
+ return device_read_db_internal(device, false);
+}
diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h
index 6dcd2645e6..94f6174bff 100644
--- a/src/libsystemd/sd-device/device-util.h
+++ b/src/libsystemd/sd-device/device-util.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
-#include "util.h"
-
#define FOREACH_DEVICE_PROPERTY(device, key, value) \
for (key = sd_device_get_property_first(device, &(value)); \
key; \
@@ -33,3 +30,28 @@
for (device = sd_device_enumerator_get_subsystem_first(enumerator); \
device; \
device = sd_device_enumerator_get_subsystem_next(enumerator))
+
+#define log_device_full(device, level, error, ...) \
+ ({ \
+ const char *_sysname = NULL; \
+ sd_device *_d = (device); \
+ int _level = (level), _error = (error); \
+ \
+ if (_d && _unlikely_(log_get_max_level() >= _level)) \
+ (void) sd_device_get_sysname(_d, &_sysname); \
+ log_object_internal(_level, _error, __FILE__, __LINE__, __func__, \
+ _sysname ? "DEVICE=" : NULL, _sysname, \
+ NULL, NULL, ##__VA_ARGS__); \
+ })
+
+#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_device_info(device, ...) log_device_full(device, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, 0, ##__VA_ARGS__)
+#define log_device_error(device, ...) log_device_full(device, LOG_ERR, 0, ##__VA_ARGS__)
+
+#define log_device_debug_errno(device, error, ...) log_device_full(device, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_device_info_errno(device, error, ...) log_device_full(device, LOG_INFO, error, ##__VA_ARGS__)
+#define log_device_notice_errno(device, error, ...) log_device_full(device, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_device_warning_errno(device, error, ...) log_device_full(device, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_device_error_errno(device, error, ...) log_device_full(device, LOG_ERR, error, ##__VA_ARGS__)
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index be29053f8c..db58615df5 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -2,6 +2,7 @@
#include <ctype.h>
#include <net/if.h>
+#include <sys/ioctl.h>
#include <sys/types.h>
#include "sd-device.h"
@@ -21,6 +22,7 @@
#include "set.h"
#include "socket-util.h"
#include "stat-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "strxcpyx.h"
@@ -31,51 +33,48 @@ int device_new_aux(sd_device **ret) {
assert(ret);
- device = new0(sd_device, 1);
+ device = new(sd_device, 1);
if (!device)
return -ENOMEM;
- device->n_ref = 1;
- device->watch_handle = -1;
+ *device = (sd_device) {
+ .n_ref = 1,
+ .watch_handle = -1,
+ .devmode = (mode_t) -1,
+ .devuid = (uid_t) -1,
+ .devgid = (gid_t) -1,
+ };
*ret = device;
-
return 0;
}
-_public_ sd_device *sd_device_ref(sd_device *device) {
- if (device)
- assert_se(++ device->n_ref >= 2);
+static sd_device *device_free(sd_device *device) {
+ assert(device);
- return device;
+ sd_device_unref(device->parent);
+ free(device->syspath);
+ free(device->sysname);
+ free(device->devtype);
+ free(device->devname);
+ free(device->subsystem);
+ free(device->driver_subsystem);
+ free(device->driver);
+ free(device->id_filename);
+ free(device->properties_strv);
+ free(device->properties_nulstr);
+
+ ordered_hashmap_free_free_free(device->properties);
+ ordered_hashmap_free_free_free(device->properties_db);
+ hashmap_free_free_free(device->sysattr_values);
+ set_free_free(device->sysattrs);
+ set_free_free(device->tags);
+ set_free_free(device->devlinks);
+
+ return mfree(device);
}
-_public_ sd_device *sd_device_unref(sd_device *device) {
- if (device && -- device->n_ref == 0) {
- sd_device_unref(device->parent);
- free(device->syspath);
- free(device->sysname);
- free(device->devtype);
- free(device->devname);
- free(device->subsystem);
- free(device->driver_subsystem);
- free(device->driver);
- free(device->id_filename);
- free(device->properties_strv);
- free(device->properties_nulstr);
-
- ordered_hashmap_free_free_free(device->properties);
- ordered_hashmap_free_free_free(device->properties_db);
- hashmap_free_free_free(device->sysattr_values);
- set_free_free(device->sysattrs);
- set_free_free(device->tags);
- set_free_free(device->devlinks);
-
- free(device);
- }
-
- return NULL;
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device, sd_device, device_free);
int device_add_property_aux(sd_device *device, const char *_key, const char *_value, bool db) {
OrderedHashmap **properties;
@@ -140,17 +139,17 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
assert(_syspath);
/* must be a subdirectory of /sys */
- if (!path_startswith(_syspath, "/sys/")) {
- log_debug("sd-device: syspath '%s' is not a subdirectory of /sys", _syspath);
- return -EINVAL;
- }
+ if (!path_startswith(_syspath, "/sys/"))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "sd-device: Syspath '%s' is not a subdirectory of /sys",
+ _syspath);
if (verify) {
r = chase_symlinks(_syspath, NULL, 0, &syspath);
if (r == -ENOENT)
return -ENODEV; /* the device does not exist (any more?) */
if (r < 0)
- return log_debug_errno(r, "sd-device: could not get target of '%s': %m", _syspath);
+ return log_debug_errno(r, "sd-device: Failed to get target of '%s': %m", _syspath);
if (!path_startswith(syspath, "/sys")) {
_cleanup_free_ char *real_sys = NULL, *new_syspath = NULL;
@@ -159,17 +158,17 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
/* /sys is a symlink to somewhere sysfs is mounted on? In that case, we convert the path to real sysfs to "/sys". */
r = chase_symlinks("/sys", NULL, 0, &real_sys);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not chase symlink /sys: %m");
+ return log_debug_errno(r, "sd-device: Failed to chase symlink /sys: %m");
p = path_startswith(syspath, real_sys);
- if (!p) {
- log_debug("sd-device: canonicalized path '%s' does not starts with sysfs mount point '%s'", syspath, real_sys);
- return -ENODEV;
- }
+ if (!p)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+ "sd-device: Canonicalized path '%s' does not starts with sysfs mount point '%s'",
+ syspath, real_sys);
new_syspath = strjoin("/sys/", p);
if (!new_syspath)
- return log_oom();
+ return -ENOMEM;
free_and_replace(syspath, new_syspath);
path_simplify(syspath, false);
@@ -500,8 +499,6 @@ int device_read_uevent_file(sd_device *device) {
if (device->uevent_loaded || device->sealed)
return 0;
- device->uevent_loaded = true;
-
r = sd_device_get_syspath(device, &syspath);
if (r < 0)
return r;
@@ -509,16 +506,18 @@ int device_read_uevent_file(sd_device *device) {
path = strjoina(syspath, "/uevent");
r = read_full_file(path, &uevent, &uevent_len);
- if (r == -EACCES)
+ if (r == -EACCES) {
/* empty uevent files may be write-only */
+ device->uevent_loaded = true;
return 0;
- else if (r == -ENOENT)
+ }
+ if (r == -ENOENT)
/* some devices may not have uevent files, see set_syspath() */
return 0;
- else if (r < 0) {
- log_debug_errno(r, "sd-device: failed to read uevent file '%s': %m", path);
- return r;
- }
+ if (r < 0)
+ return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file '%s': %m", path);
+
+ device->uevent_loaded = true;
for (i = 0; i < uevent_len; i++)
switch (state) {
@@ -537,7 +536,7 @@ int device_read_uevent_file(sd_device *device) {
state = PRE_VALUE;
} else if (strchr(NEWLINE, uevent[i])) {
uevent[i] = '\0';
- log_debug("sd-device: ignoring invalid uevent line '%s'", key);
+ log_device_debug(device, "sd-device: Invalid uevent line '%s', ignoring", key);
state = PRE_KEY;
}
@@ -554,20 +553,20 @@ int device_read_uevent_file(sd_device *device) {
r = handle_uevent_line(device, key, value, &major, &minor);
if (r < 0)
- log_debug_errno(r, "sd-device: failed to handle uevent entry '%s=%s': %m", key, value);
+ log_device_debug_errno(device, r, "sd-device: Failed to handle uevent entry '%s=%s', ignoring: %m", key, value);
state = PRE_KEY;
}
break;
default:
- assert_not_reached("invalid state when parsing uevent file");
+ assert_not_reached("Invalid state when parsing uevent file");
}
if (major) {
r = device_set_devnum(device, major, minor);
if (r < 0)
- log_debug_errno(r, "sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %m", major, minor, path);
+ log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, minor, path);
}
return 0;
@@ -577,13 +576,16 @@ _public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
int r;
assert_return(device, -EINVAL);
- assert_return(ifindex, -EINVAL);
r = device_read_uevent_file(device);
if (r < 0)
return r;
- *ifindex = device->ifindex;
+ if (device->ifindex <= 0)
+ return -ENOENT;
+
+ if (ifindex)
+ *ifindex = device->ifindex;
return 0;
}
@@ -596,16 +598,17 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
switch (id[0]) {
case 'b':
- case 'c':
- {
- char type;
- int maj, min;
+ case 'c': {
+ dev_t devt;
- r = sscanf(id, "%c%i:%i", &type, &maj, &min);
- if (r != 3)
+ if (isempty(id))
return -EINVAL;
- return sd_device_new_from_devnum(ret, type, makedev(maj, min));
+ r = parse_dev(id + 1, &devt);
+ if (r < 0)
+ return r;
+
+ return sd_device_new_from_devnum(ret, id[0], devt);
}
case 'n':
{
@@ -647,7 +650,7 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
char subsys[PATH_MAX];
char *sysname;
- (void)strscpy(subsys, sizeof(subsys), id + 1);
+ (void) strscpy(subsys, sizeof(subsys), id + 1);
sysname = strchr(subsys, ':');
if (!sysname)
return -EINVAL;
@@ -717,7 +720,7 @@ _public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
if (!child->parent_set) {
child->parent_set = true;
- (void)device_new_from_child(&child->parent, child);
+ (void) device_new_from_child(&child->parent, child);
}
if (!child->parent)
@@ -795,12 +798,12 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
else if (path_startswith(device->devpath, "/module/"))
r = device_set_subsystem(device, "module");
else if (!(drivers = strstr(syspath, "/drivers/")) &&
- (path_startswith(device->devpath, "/subsystem/") ||
- path_startswith(device->devpath, "/class/") ||
- path_startswith(device->devpath, "/bus/")))
+ PATH_STARTSWITH_SET(device->devpath, "/subsystem/",
+ "/class/",
+ "/bus/"))
r = device_set_subsystem(device, "subsystem");
if (r < 0 && r != -ENOENT)
- return log_debug_errno(r, "sd-device: could not set subsystem for %s: %m", device->devpath);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem for %s: %m", device->devpath);
device->subsystem_set = true;
} else if (!device->driver_subsystem_set)
@@ -823,7 +826,7 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
r = device_set_drivers_subsystem(device, subsys + 1);
}
if (r < 0 && r != -ENOENT)
- return log_debug_errno(r, "sd-device: could not set subsystem for driver %s: %m", device->devpath);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem for driver %s: %m", device->devpath);
}
device->driver_subsystem_set = true;
@@ -847,6 +850,9 @@ _public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
if (r < 0)
return r;
+ if (!device->devtype)
+ return -ENOENT;
+
*devtype = device->devtype;
return 0;
@@ -864,12 +870,12 @@ _public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const
const char *parent_subsystem = NULL;
const char *parent_devtype = NULL;
- (void)sd_device_get_subsystem(parent, &parent_subsystem);
+ (void) sd_device_get_subsystem(parent, &parent_subsystem);
if (streq_ptr(parent_subsystem, subsystem)) {
if (!devtype)
break;
- (void)sd_device_get_devtype(parent, &parent_devtype);
+ (void) sd_device_get_devtype(parent, &parent_devtype);
if (streq_ptr(parent_devtype, devtype))
break;
}
@@ -888,13 +894,16 @@ _public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
int r;
assert_return(device, -EINVAL);
- assert_return(devnum, -EINVAL);
r = device_read_uevent_file(device);
if (r < 0)
return r;
- *devnum = device->devnum;
+ if (major(device->devnum) <= 0)
+ return -ENOENT;
+
+ if (devnum)
+ *devnum = device->devnum;
return 0;
}
@@ -940,11 +949,11 @@ _public_ int sd_device_get_driver(sd_device *device, const char **ret) {
if (r >= 0) {
r = device_set_driver(device, driver);
if (r < 0)
- return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set driver for %s: %m", device->devpath);
} else if (r == -ENOENT)
device->driver_set = true;
else
- return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath);
+ return log_device_debug_errno(device, r, "sd-device: Failed to set driver for %s: %m", device->devpath);
}
if (!device->driver)
@@ -1061,6 +1070,9 @@ _public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
return r;
}
+ if (!device->sysnum)
+ return -ENOENT;
+
*ret = device->sysnum;
return 0;
@@ -1138,23 +1150,19 @@ static int device_add_property_internal_from_string(sd_device *device, const cha
return device_add_property_internal(device, key, value);
}
-int device_set_usec_initialized(sd_device *device, const char *initialized) {
- uint64_t usec_initialized;
+int device_set_usec_initialized(sd_device *device, usec_t when) {
+ char s[DECIMAL_STR_MAX(usec_t)];
int r;
assert(device);
- assert(initialized);
- r = safe_atou64(initialized, &usec_initialized);
- if (r < 0)
- return r;
+ xsprintf(s, USEC_FMT, when);
- r = device_add_property_internal(device, "USEC_INITIALIZED", initialized);
+ r = device_add_property_internal(device, "USEC_INITIALIZED", s);
if (r < 0)
return r;
- device->usec_initialized = usec_initialized;
-
+ device->usec_initialized = when;
return 0;
}
@@ -1185,12 +1193,19 @@ static int handle_db_line(sd_device *device, char key, const char *value) {
return r;
break;
- case 'I':
- r = device_set_usec_initialized(device, value);
+ case 'I': {
+ usec_t t;
+
+ r = safe_atou64(value, &t);
+ if (r < 0)
+ return r;
+
+ r = device_set_usec_initialized(device, t);
if (r < 0)
return r;
break;
+ }
case 'L':
r = safe_atoi(value, &device->devlink_priority);
if (r < 0)
@@ -1204,7 +1219,7 @@ static int handle_db_line(sd_device *device, char key, const char *value) {
break;
default:
- log_debug("device db: unknown key '%c'", key);
+ log_device_debug(device, "sd-device: Unknown key '%c' in device db, ignoring", key);
}
return 0;
@@ -1224,15 +1239,7 @@ int device_get_id_filename(sd_device *device, const char **ret) {
if (r < 0)
return r;
- r = sd_device_get_devnum(device, &devnum);
- if (r < 0)
- return r;
-
- r = sd_device_get_ifindex(device, &ifindex);
- if (r < 0)
- return r;
-
- if (major(devnum) > 0) {
+ if (sd_device_get_devnum(device, &devnum) >= 0) {
assert(subsystem);
/* use dev_t — b259:131072, c254:0 */
@@ -1241,9 +1248,9 @@ int device_get_id_filename(sd_device *device, const char **ret) {
major(devnum), minor(devnum));
if (r < 0)
return -ENOMEM;
- } else if (ifindex > 0) {
+ } else if (sd_device_get_ifindex(device, &ifindex) >= 0) {
/* use netdev ifindex — n3 */
- r = asprintf(&id, "n%u", ifindex);
+ r = asprintf(&id, "n%u", (unsigned) ifindex);
if (r < 0)
return -ENOMEM;
} else {
@@ -1280,7 +1287,7 @@ int device_get_id_filename(sd_device *device, const char **ret) {
return 0;
}
-int device_read_db_aux(sd_device *device, bool force) {
+int device_read_db_internal(sd_device *device, bool force) {
_cleanup_free_ char *db = NULL;
char *path;
const char *id, *value;
@@ -1297,11 +1304,11 @@ int device_read_db_aux(sd_device *device, bool force) {
INVALID_LINE,
} state = PRE_KEY;
+ assert(device);
+
if (device->db_loaded || (!force && device->sealed))
return 0;
- device->db_loaded = true;
-
r = device_get_id_filename(device, &id);
if (r < 0)
return r;
@@ -1313,12 +1320,14 @@ int device_read_db_aux(sd_device *device, bool force) {
if (r == -ENOENT)
return 0;
else
- return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path);
+ return log_device_debug_errno(device, r, "sd-device: Failed to read db '%s': %m", path);
}
/* devices with a database entry are initialized */
device->is_initialized = true;
+ device->db_loaded = true;
+
for (i = 0; i < db_len; i++) {
switch (state) {
case PRE_KEY:
@@ -1331,7 +1340,7 @@ int device_read_db_aux(sd_device *device, bool force) {
break;
case KEY:
if (db[i] != ':') {
- log_debug("sd-device: ignoring invalid db entry with key '%c'", key);
+ log_device_debug(device, "sd-device: Invalid db entry with key '%c', ignoring", key);
state = INVALID_LINE;
} else {
@@ -1357,37 +1366,30 @@ int device_read_db_aux(sd_device *device, bool force) {
db[i] = '\0';
r = handle_db_line(device, key, value);
if (r < 0)
- log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value);
+ log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m", key, value);
state = PRE_KEY;
}
break;
default:
- assert_not_reached("invalid state when parsing db");
+ assert_not_reached("Invalid state when parsing db");
}
}
return 0;
}
-static int device_read_db(sd_device *device) {
- return device_read_db_aux(device, false);
-}
-
-_public_ int sd_device_get_is_initialized(sd_device *device, int *initialized) {
+_public_ int sd_device_get_is_initialized(sd_device *device) {
int r;
assert_return(device, -EINVAL);
- assert_return(initialized, -EINVAL);
r = device_read_db(device);
if (r < 0)
return r;
- *initialized = device->is_initialized;
-
- return 0;
+ return device->is_initialized;
}
_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) {
@@ -1678,7 +1680,6 @@ _public_ int sd_device_get_property_value(sd_device *device, const char *key, co
assert_return(device, -EINVAL);
assert_return(key, -EINVAL);
- assert_return(_value, -EINVAL);
r = device_properties_prepare(device);
if (r < 0)
@@ -1688,7 +1689,8 @@ _public_ int sd_device_get_property_value(sd_device *device, const char *key, co
if (!value)
return -ENOENT;
- *_value = value;
+ if (_value)
+ *_value = value;
return 0;
}
@@ -1830,13 +1832,10 @@ static void device_remove_sysattr_value(sd_device *device, const char *_key) {
/* set the attribute and save it in the cache. If a NULL value is passed the
* attribute is cleared from the cache */
-_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *_value) {
- _cleanup_close_ int fd = -1;
+_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) {
_cleanup_free_ char *value = NULL;
- const char *syspath;
- char *path;
- size_t len = 0;
- ssize_t size;
+ const char *syspath, *path;
+ size_t len;
int r;
assert_return(device, -EINVAL);
@@ -1854,25 +1853,6 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
path = strjoina(syspath, "/", sysattr);
- fd = open(path, O_WRONLY | O_CLOEXEC | O_NOFOLLOW);
- if (fd < 0) {
- if (errno == ELOOP)
- return -EINVAL;
- if (errno == EISDIR)
- return -EISDIR;
-
- value = strdup("");
- if (!value)
- return -ENOMEM;
-
- r = device_add_sysattr_value(device, sysattr, value);
- if (r < 0)
- return r;
- value = NULL;
-
- return -ENXIO;
- }
-
len = strlen(_value);
/* drop trailing newlines */
@@ -1887,17 +1867,30 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
if (!value)
return -ENOMEM;
- size = write(fd, value, len);
- if (size < 0)
- return -errno;
+ r = write_string_file(path, value, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_NOFOLLOW);
+ if (r < 0) {
+ if (r == -ELOOP)
+ return -EINVAL;
+ if (r == -EISDIR)
+ return r;
- if ((size_t)size != len)
- return -EIO;
+ free(value);
+ value = strdup("");
+ if (!value)
+ return -ENOMEM;
+
+ r = device_add_sysattr_value(device, sysattr, value);
+ if (r < 0)
+ return r;
+
+ value = NULL;
+ return -ENXIO;
+ }
r = device_add_sysattr_value(device, sysattr, value);
if (r < 0)
return r;
- value = NULL;
+ value = NULL;
return 0;
}
diff --git a/src/libsystemd/sd-device/test-sd-device-monitor.c b/src/libsystemd/sd-device/test-sd-device-monitor.c
new file mode 100644
index 0000000000..9e5ca11fe9
--- /dev/null
+++ b/src/libsystemd/sd-device/test-sd-device-monitor.c
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "sd-device.h"
+#include "sd-event.h"
+
+#include "device-monitor-private.h"
+#include "device-private.h"
+#include "device-util.h"
+#include "macro.h"
+#include "string-util.h"
+#include "tests.h"
+#include "util.h"
+#include "virt.h"
+
+static int monitor_handler(sd_device_monitor *m, sd_device *d, void *userdata) {
+ const char *s, *syspath = userdata;
+
+ assert_se(sd_device_get_syspath(d, &s) >= 0);
+ assert_se(streq(s, syspath));
+
+ return sd_event_exit(sd_device_monitor_get_event(m), 0);
+}
+
+static int test_send_receive_one(sd_device *device, bool subsystem_filter, bool tag_filter, bool use_bpf) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
+ const char *syspath, *subsystem, *tag, *devtype = NULL;
+ int r;
+
+ log_device_info(device, "/* %s(subsystem_filter=%s, tag_filter=%s, use_bpf=%s) */", __func__,
+ true_false(subsystem_filter), true_false(tag_filter), true_false(use_bpf));
+
+ assert_se(sd_device_get_syspath(device, &syspath) >= 0);
+
+ assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
+ assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
+
+ assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
+ assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
+ assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
+
+ if (subsystem_filter) {
+ assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
+ (void) sd_device_get_devtype(device, &devtype);
+ assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, devtype) >= 0);
+ }
+
+ if (tag_filter)
+ FOREACH_DEVICE_TAG(device, tag)
+ assert_se(sd_device_monitor_filter_add_match_tag(monitor_client, tag) >= 0);
+
+ if ((subsystem_filter || tag_filter) && use_bpf)
+ assert_se(sd_device_monitor_filter_update(monitor_client) >= 0);
+
+ /* Do not use assert_se() here. */
+ r = device_monitor_send_device(monitor_server, monitor_client, device);
+ if (r < 0)
+ return log_error_errno(r, "Failed to send loopback device: %m");
+
+ assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 0);
+
+ return 0;
+}
+
+static void test_subsystem_filter(sd_device *device) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ const char *syspath, *subsystem, *p, *s;
+ sd_device *d;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(sd_device_get_syspath(device, &syspath) >= 0);
+ assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
+
+ assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
+ assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
+
+ assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
+ assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL) >= 0);
+ assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
+ assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ assert_se(sd_device_enumerator_add_match_subsystem(e, subsystem, false) >= 0);
+ FOREACH_DEVICE(e, d) {
+ assert_se(sd_device_get_syspath(d, &p) >= 0);
+ assert_se(sd_device_get_subsystem(d, &s) >= 0);
+
+ log_info("Sending device subsystem:%s syspath:%s", s, p);
+ assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
+ }
+
+ log_info("Sending device subsystem:%s syspath:%s", subsystem, syspath);
+ assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0);
+ assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 0);
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(sd_device_unrefp) sd_device *loopback = NULL, *sda = NULL;
+ int r;
+
+ test_setup_logging(LOG_INFO);
+
+ if (getuid() != 0)
+ return log_tests_skipped("not root");
+
+ assert_se(sd_device_new_from_syspath(&loopback, "/sys/class/net/lo") >= 0);
+ assert_se(device_add_property(loopback, "ACTION", "add") >= 0);
+ assert_se(device_add_property(loopback, "SEQNUM", "10") >= 0);
+
+ r = test_send_receive_one(loopback, false, false, false);
+ if (r < 0) {
+ assert_se(r == -EPERM && detect_container() > 0);
+ return log_tests_skipped("Running in container? Skipping remaining tests");
+ }
+
+ assert_se(test_send_receive_one(loopback, true, false, false) >= 0);
+ assert_se(test_send_receive_one(loopback, false, true, false) >= 0);
+ assert_se(test_send_receive_one(loopback, true, true, false) >= 0);
+ assert_se(test_send_receive_one(loopback, true, false, true) >= 0);
+ assert_se(test_send_receive_one(loopback, false, true, true) >= 0);
+ assert_se(test_send_receive_one(loopback, true, true, true) >= 0);
+
+ test_subsystem_filter(loopback);
+
+ r = sd_device_new_from_subsystem_sysname(&sda, "block", "sda");
+ if (r < 0) {
+ log_info_errno(r, "Failed to create sd_device for sda, skipping remaining tests: %m");
+ return 0;
+ }
+
+ assert_se(device_add_property(sda, "ACTION", "change") >= 0);
+ assert_se(device_add_property(sda, "SEQNUM", "11") >= 0);
+
+ assert_se(test_send_receive_one(sda, false, false, false) >= 0);
+ assert_se(test_send_receive_one(sda, true, false, false) >= 0);
+ assert_se(test_send_receive_one(sda, false, true, false) >= 0);
+ assert_se(test_send_receive_one(sda, true, true, false) >= 0);
+ assert_se(test_send_receive_one(sda, true, false, true) >= 0);
+ assert_se(test_send_receive_one(sda, false, true, true) >= 0);
+ assert_se(test_send_receive_one(sda, true, true, true) >= 0);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-device/test-sd-device-thread.c b/src/libsystemd/sd-device/test-sd-device-thread.c
new file mode 100644
index 0000000000..9f1c02373f
--- /dev/null
+++ b/src/libsystemd/sd-device/test-sd-device-thread.c
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sd-device.h"
+
+#include "device-util.h"
+#include "macro.h"
+
+static void* thread(void *p) {
+ sd_device **d = p;
+
+ assert_se(!(*d = sd_device_unref(*d)));
+
+ return NULL;
+}
+
+int main(int argc, char *argv[]) {
+ sd_device *loopback;
+ pthread_t t;
+ const char *key, *value;
+
+ assert_se(unsetenv("SYSTEMD_MEMPOOL") == 0);
+
+ assert_se(sd_device_new_from_syspath(&loopback, "/sys/class/net/lo") >= 0);
+
+ FOREACH_DEVICE_PROPERTY(loopback, key, value)
+ printf("%s=%s\n", key, value);
+
+ assert_se(pthread_create(&t, NULL, thread, &loopback) == 0);
+ assert_se(pthread_join(t, NULL) == 0);
+
+ assert_se(!loopback);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c
new file mode 100644
index 0000000000..e459b13b96
--- /dev/null
+++ b/src/libsystemd/sd-device/test-sd-device.c
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "device-enumerator-private.h"
+#include "device-private.h"
+#include "device-util.h"
+#include "hashmap.h"
+#include "string-util.h"
+#include "tests.h"
+#include "util.h"
+
+static void test_sd_device_one(sd_device *d) {
+ const char *syspath, *subsystem, *val;
+ dev_t devnum;
+ usec_t usec;
+ int i, r;
+
+ assert_se(sd_device_get_syspath(d, &syspath) >= 0);
+
+ r = sd_device_get_subsystem(d, &subsystem);
+ assert_se(r >= 0 || r == -ENOENT);
+
+ r = sd_device_get_devtype(d, &val);
+ assert_se(r >= 0 || r == -ENOENT);
+
+ r = sd_device_get_devnum(d, &devnum);
+ assert_se((r >= 0 && major(devnum) > 0) || r == -ENOENT);
+
+ r = sd_device_get_ifindex(d, &i);
+ assert_se((r >= 0 && i > 0) || r == -ENOENT);
+
+ r = sd_device_get_driver(d, &val);
+ assert_se(r >= 0 || r == -ENOENT);
+
+ assert_se(sd_device_get_devpath(d, &val) >= 0);
+
+ r = sd_device_get_devname(d, &val);
+ assert_se(r >= 0 || r == -ENOENT);
+
+ assert_se(sd_device_get_sysname(d, &val) >= 0);
+
+ r = sd_device_get_sysnum(d, &val);
+ assert_se(r >= 0 || r == -ENOENT);
+
+ i = sd_device_get_is_initialized(d);
+ assert_se(i >= 0);
+ if (i > 0) {
+ r = sd_device_get_usec_since_initialized(d, &usec);
+ assert_se((r >= 0 && usec > 0) || r == -ENODATA);
+ }
+
+ r = sd_device_get_sysattr_value(d, "name_assign_type", &val);
+ assert_se(r >= 0 || IN_SET(r, -ENOENT, -EINVAL));
+
+ r = sd_device_get_property_value(d, "ID_NET_DRIVER", &val);
+ assert_se(r >= 0 || r == -ENOENT);
+
+ log_info("syspath:%s subsystem:%s initialized:%s", syspath, strna(subsystem), yes_no(i));
+}
+
+static void test_sd_device_enumerator_devices(void) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
+ FOREACH_DEVICE(e, d)
+ test_sd_device_one(d);
+}
+
+static void test_sd_device_enumerator_subsystems(void) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
+ FOREACH_SUBSYSTEM(e, d)
+ test_sd_device_one(d);
+}
+
+static void test_sd_device_enumerator_filter_subsystem_one(const char *subsystem, Hashmap *h) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d, *t;
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ assert_se(sd_device_enumerator_add_match_subsystem(e, subsystem, true) >= 0);
+
+ FOREACH_DEVICE(e, d) {
+ const char *syspath;
+
+ assert_se(sd_device_get_syspath(d, &syspath) >= 0);
+ assert_se(t = hashmap_remove(h, syspath));
+ assert_se(!sd_device_unref(t));
+
+ log_debug("Removed subsystem:%s syspath:%s", subsystem, syspath);
+ }
+
+ assert_se(hashmap_isempty(h));
+}
+
+static void test_sd_device_enumerator_filter_subsystem(void) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ _cleanup_(hashmap_freep) Hashmap *subsystems;
+ sd_device *d;
+ Hashmap *h;
+ char *s;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(subsystems = hashmap_new(&string_hash_ops));
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+
+ FOREACH_DEVICE(e, d) {
+ const char *syspath, *subsystem;
+ int r;
+
+ assert_se(sd_device_get_syspath(d, &syspath) >= 0);
+
+ r = sd_device_get_subsystem(d, &subsystem);
+ assert_se(r >= 0 || r == -ENOENT);
+ if (r < 0)
+ continue;
+
+ h = hashmap_get(subsystems, subsystem);
+ if (!h) {
+ char *str;
+ assert_se(str = strdup(subsystem));
+ assert_se(h = hashmap_new(&string_hash_ops));
+ assert_se(hashmap_put(subsystems, str, h) >= 0);
+ }
+
+ assert_se(hashmap_put(h, syspath, d) >= 0);
+ assert_se(sd_device_ref(d));
+
+ log_debug("Added subsystem:%s syspath:%s", subsystem, syspath);
+ }
+
+ while ((h = hashmap_steal_first_key_and_value(subsystems, (void**) &s))) {
+ test_sd_device_enumerator_filter_subsystem_one(s, h);
+ hashmap_free(h);
+ free(s);
+ }
+}
+
+int main(int argc, char **argv) {
+ test_setup_logging(LOG_INFO);
+
+ test_sd_device_enumerator_devices();
+ test_sd_device_enumerator_subsystems();
+ test_sd_device_enumerator_filter_subsystem();
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-device/test-udev-device-thread.c b/src/libsystemd/sd-device/test-udev-device-thread.c
new file mode 100644
index 0000000000..2fc0f59185
--- /dev/null
+++ b/src/libsystemd/sd-device/test-udev-device-thread.c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libudev.h"
+
+#include "macro.h"
+
+static void* thread(void *p) {
+ struct udev_device **d = p;
+
+ assert_se(!(*d = udev_device_unref(*d)));
+
+ return NULL;
+}
+
+int main(int argc, char *argv[]) {
+ struct udev_device *loopback;
+ pthread_t t;
+
+ assert_se(unsetenv("SYSTEMD_MEMPOOL") == 0);
+
+ assert_se(loopback = udev_device_new_from_syspath(NULL, "/sys/class/net/lo"));
+
+ assert_se(udev_device_get_properties_list_entry(loopback));
+
+ assert_se(pthread_create(&t, NULL, thread, &loopback) == 0);
+ assert_se(pthread_join(t, NULL) == 0);
+
+ assert_se(!loopback);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-event/event-source.h b/src/libsystemd/sd-event/event-source.h
new file mode 100644
index 0000000000..99ab8fc169
--- /dev/null
+++ b/src/libsystemd/sd-event/event-source.h
@@ -0,0 +1,206 @@
+#pragma once
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <sys/wait.h>
+
+#include "sd-event.h"
+
+#include "fs-util.h"
+#include "hashmap.h"
+#include "list.h"
+#include "prioq.h"
+
+typedef enum EventSourceType {
+ SOURCE_IO,
+ SOURCE_TIME_REALTIME,
+ SOURCE_TIME_BOOTTIME,
+ SOURCE_TIME_MONOTONIC,
+ SOURCE_TIME_REALTIME_ALARM,
+ SOURCE_TIME_BOOTTIME_ALARM,
+ SOURCE_SIGNAL,
+ SOURCE_CHILD,
+ SOURCE_DEFER,
+ SOURCE_POST,
+ SOURCE_EXIT,
+ SOURCE_WATCHDOG,
+ SOURCE_INOTIFY,
+ _SOURCE_EVENT_SOURCE_TYPE_MAX,
+ _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1
+} EventSourceType;
+
+/* All objects we use in epoll events start with this value, so that
+ * we know how to dispatch it */
+typedef enum WakeupType {
+ WAKEUP_NONE,
+ WAKEUP_EVENT_SOURCE,
+ WAKEUP_CLOCK_DATA,
+ WAKEUP_SIGNAL_DATA,
+ WAKEUP_INOTIFY_DATA,
+ _WAKEUP_TYPE_MAX,
+ _WAKEUP_TYPE_INVALID = -1,
+} WakeupType;
+
+struct inode_data;
+
+struct sd_event_source {
+ WakeupType wakeup;
+
+ unsigned n_ref;
+
+ sd_event *event;
+ void *userdata;
+ sd_event_handler_t prepare;
+
+ char *description;
+
+ EventSourceType type:5;
+ signed int enabled:3;
+ bool pending:1;
+ bool dispatching:1;
+ bool floating:1;
+
+ int64_t priority;
+ unsigned pending_index;
+ unsigned prepare_index;
+ uint64_t pending_iteration;
+ uint64_t prepare_iteration;
+
+ sd_event_destroy_t destroy_callback;
+
+ LIST_FIELDS(sd_event_source, sources);
+
+ union {
+ struct {
+ sd_event_io_handler_t callback;
+ int fd;
+ uint32_t events;
+ uint32_t revents;
+ bool registered:1;
+ bool owned:1;
+ } io;
+ struct {
+ sd_event_time_handler_t callback;
+ usec_t next, accuracy;
+ unsigned earliest_index;
+ unsigned latest_index;
+ } time;
+ struct {
+ sd_event_signal_handler_t callback;
+ struct signalfd_siginfo siginfo;
+ int sig;
+ } signal;
+ struct {
+ sd_event_child_handler_t callback;
+ siginfo_t siginfo;
+ pid_t pid;
+ int options;
+ } child;
+ struct {
+ sd_event_handler_t callback;
+ } defer;
+ struct {
+ sd_event_handler_t callback;
+ } post;
+ struct {
+ sd_event_handler_t callback;
+ unsigned prioq_index;
+ } exit;
+ struct {
+ sd_event_inotify_handler_t callback;
+ uint32_t mask;
+ struct inode_data *inode_data;
+ LIST_FIELDS(sd_event_source, by_inode_data);
+ } inotify;
+ };
+};
+
+struct clock_data {
+ WakeupType wakeup;
+ int fd;
+
+ /* For all clocks we maintain two priority queues each, one
+ * ordered for the earliest times the events may be
+ * dispatched, and one ordered by the latest times they must
+ * have been dispatched. The range between the top entries in
+ * the two prioqs is the time window we can freely schedule
+ * wakeups in */
+
+ Prioq *earliest;
+ Prioq *latest;
+ usec_t next;
+
+ bool needs_rearm:1;
+};
+
+struct signal_data {
+ WakeupType wakeup;
+
+ /* For each priority we maintain one signal fd, so that we
+ * only have to dequeue a single event per priority at a
+ * time. */
+
+ int fd;
+ int64_t priority;
+ sigset_t sigset;
+ sd_event_source *current;
+};
+
+/* A structure listing all event sources currently watching a specific inode */
+struct inode_data {
+ /* The identifier for the inode, the combination of the .st_dev + .st_ino fields of the file */
+ ino_t ino;
+ dev_t dev;
+
+ /* An fd of the inode to watch. The fd is kept open until the next iteration of the loop, so that we can
+ * rearrange the priority still until then, as we need the original inode to change the priority as we need to
+ * add a watch descriptor to the right inotify for the priority which we can only do if we have a handle to the
+ * original inode. We keep a list of all inode_data objects with an open fd in the to_close list (see below) of
+ * the sd-event object, so that it is efficient to close everything, before entering the next event loop
+ * iteration. */
+ int fd;
+
+ /* The inotify "watch descriptor" */
+ int wd;
+
+ /* The combination of the mask of all inotify watches on this inode we manage. This is also the mask that has
+ * most recently been set on the watch descriptor. */
+ uint32_t combined_mask;
+
+ /* All event sources subscribed to this inode */
+ LIST_HEAD(sd_event_source, event_sources);
+
+ /* The inotify object we watch this inode with */
+ struct inotify_data *inotify_data;
+
+ /* A linked list of all inode data objects with fds to close (see above) */
+ LIST_FIELDS(struct inode_data, to_close);
+};
+
+/* A structure encapsulating an inotify fd */
+struct inotify_data {
+ WakeupType wakeup;
+
+ /* For each priority we maintain one inotify fd, so that we only have to dequeue a single event per priority at
+ * a time */
+
+ int fd;
+ int64_t priority;
+
+ Hashmap *inodes; /* The inode_data structures keyed by dev+ino */
+ Hashmap *wd; /* The inode_data structures keyed by the watch descriptor for each */
+
+ /* The buffer we read inotify events into */
+ union inotify_event_buffer buffer;
+ size_t buffer_filled; /* fill level of the buffer */
+
+ /* How many event sources are currently marked pending for this inotify. We won't read new events off the
+ * inotify fd as long as there are still pending events on the inotify (because we have no strategy of queuing
+ * the events locally if they can't be coalesced). */
+ unsigned n_pending;
+
+ /* A linked list of all inotify objects with data already read, that still need processing. We keep this list
+ * to make it efficient to figure out what inotify objects to process data on next. */
+ LIST_FIELDS(struct inotify_data, buffered);
+};
diff --git a/src/libsystemd/sd-event/event-util.c b/src/libsystemd/sd-event/event-util.c
new file mode 100644
index 0000000000..43e73d55e1
--- /dev/null
+++ b/src/libsystemd/sd-event/event-util.c
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+
+#include "event-source.h"
+#include "event-util.h"
+#include "log.h"
+#include "string-util.h"
+
+int event_reset_time(
+ sd_event *e,
+ sd_event_source **s,
+ clockid_t clock,
+ uint64_t usec,
+ uint64_t accuracy,
+ sd_event_time_handler_t callback,
+ void *userdata,
+ int64_t priority,
+ const char *description,
+ bool force_reset) {
+
+ bool created = false;
+ int enabled, r;
+ clockid_t c;
+
+ assert(e);
+ assert(s);
+
+ if (*s) {
+ if (!force_reset) {
+ r = sd_event_source_get_enabled(*s, &enabled);
+ if (r < 0)
+ return log_debug_errno(r, "sd-event: Failed to query whether event source \"%s\" is enabled or not: %m",
+ strna((*s)->description ?: description));
+
+ if (enabled != SD_EVENT_OFF)
+ return 0;
+ }
+
+ r = sd_event_source_get_time_clock(*s, &c);
+ if (r < 0)
+ return log_debug_errno(r, "sd-event: Failed to get clock id of event source \"%s\": %m", strna((*s)->description ?: description));
+
+ if (c != clock)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "sd-event: Current clock id %i of event source \"%s\" is different from specified one %i.",
+ (int)c,
+ strna((*s)->description ? : description),
+ (int)clock);
+
+ r = sd_event_source_set_time(*s, usec);
+ if (r < 0)
+ return log_debug_errno(r, "sd-event: Failed to set time for event source \"%s\": %m", strna((*s)->description ?: description));
+
+ r = sd_event_source_set_time_accuracy(*s, accuracy);
+ if (r < 0)
+ return log_debug_errno(r, "sd-event: Failed to set accuracy for event source \"%s\": %m", strna((*s)->description ?: description));
+
+ /* callback function is not updated, as we do not have sd_event_source_set_time_callback(). */
+
+ (void) sd_event_source_set_userdata(*s, userdata);
+
+ r = sd_event_source_set_enabled(*s, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_debug_errno(r, "sd-event: Failed to enable event source \"%s\": %m", strna((*s)->description ?: description));
+ } else {
+ r = sd_event_add_time(e, s, clock, usec, accuracy, callback, userdata);
+ if (r < 0)
+ return log_debug_errno(r, "sd-event: Failed to create timer event \"%s\": %m", strna(description));
+
+ created = true;
+ }
+
+ r = sd_event_source_set_priority(*s, priority);
+ if (r < 0)
+ return log_debug_errno(r, "sd-event: Failed to set priority for event source \"%s\": %m", strna((*s)->description ?: description));
+
+ if (description) {
+ r = sd_event_source_set_description(*s, description);
+ if (r < 0)
+ return log_debug_errno(r, "sd-event: Failed to set description for event source \"%s\": %m", description);
+ }
+
+ return created;
+}
+
+int event_source_disable(sd_event_source *s) {
+ if (!s)
+ return 0;
+
+ return sd_event_source_set_enabled(s, SD_EVENT_OFF);
+}
+
+int event_source_is_enabled(sd_event_source *s) {
+ if (!s)
+ return false;
+
+ return sd_event_source_get_enabled(s, NULL);
+}
diff --git a/src/libsystemd/sd-event/event-util.h b/src/libsystemd/sd-event/event-util.h
new file mode 100644
index 0000000000..00180955f9
--- /dev/null
+++ b/src/libsystemd/sd-event/event-util.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+#include "sd-event.h"
+
+int event_reset_time(sd_event *e, sd_event_source **s,
+ clockid_t clock, uint64_t usec, uint64_t accuracy,
+ sd_event_time_handler_t callback, void *userdata,
+ int64_t priority, const char *description, bool force_reset);
+int event_source_disable(sd_event_source *s);
+int event_source_is_enabled(sd_event_source *s);
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index d53b9a7026..0030ea5dbe 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <sys/epoll.h>
#include <sys/timerfd.h>
@@ -11,6 +9,7 @@
#include "sd-id128.h"
#include "alloc-util.h"
+#include "event-source.h"
#include "fd-util.h"
#include "fs-util.h"
#include "hashmap.h"
@@ -28,24 +27,6 @@
#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
-typedef enum EventSourceType {
- SOURCE_IO,
- SOURCE_TIME_REALTIME,
- SOURCE_TIME_BOOTTIME,
- SOURCE_TIME_MONOTONIC,
- SOURCE_TIME_REALTIME_ALARM,
- SOURCE_TIME_BOOTTIME_ALARM,
- SOURCE_SIGNAL,
- SOURCE_CHILD,
- SOURCE_DEFER,
- SOURCE_POST,
- SOURCE_EXIT,
- SOURCE_WATCHDOG,
- SOURCE_INOTIFY,
- _SOURCE_EVENT_SOURCE_TYPE_MAX,
- _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1
-} EventSourceType;
-
static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = {
[SOURCE_IO] = "io",
[SOURCE_TIME_REALTIME] = "realtime",
@@ -64,183 +45,8 @@ static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX]
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
-/* All objects we use in epoll events start with this value, so that
- * we know how to dispatch it */
-typedef enum WakeupType {
- WAKEUP_NONE,
- WAKEUP_EVENT_SOURCE,
- WAKEUP_CLOCK_DATA,
- WAKEUP_SIGNAL_DATA,
- WAKEUP_INOTIFY_DATA,
- _WAKEUP_TYPE_MAX,
- _WAKEUP_TYPE_INVALID = -1,
-} WakeupType;
-
#define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM)
-struct inode_data;
-
-struct sd_event_source {
- WakeupType wakeup;
-
- unsigned n_ref;
-
- sd_event *event;
- void *userdata;
- sd_event_handler_t prepare;
-
- char *description;
-
- EventSourceType type:5;
- int enabled:3;
- bool pending:1;
- bool dispatching:1;
- bool floating:1;
-
- int64_t priority;
- unsigned pending_index;
- unsigned prepare_index;
- uint64_t pending_iteration;
- uint64_t prepare_iteration;
-
- sd_event_destroy_t destroy_callback;
-
- LIST_FIELDS(sd_event_source, sources);
-
- union {
- struct {
- sd_event_io_handler_t callback;
- int fd;
- uint32_t events;
- uint32_t revents;
- bool registered:1;
- bool owned:1;
- } io;
- struct {
- sd_event_time_handler_t callback;
- usec_t next, accuracy;
- unsigned earliest_index;
- unsigned latest_index;
- } time;
- struct {
- sd_event_signal_handler_t callback;
- struct signalfd_siginfo siginfo;
- int sig;
- } signal;
- struct {
- sd_event_child_handler_t callback;
- siginfo_t siginfo;
- pid_t pid;
- int options;
- } child;
- struct {
- sd_event_handler_t callback;
- } defer;
- struct {
- sd_event_handler_t callback;
- } post;
- struct {
- sd_event_handler_t callback;
- unsigned prioq_index;
- } exit;
- struct {
- sd_event_inotify_handler_t callback;
- uint32_t mask;
- struct inode_data *inode_data;
- LIST_FIELDS(sd_event_source, by_inode_data);
- } inotify;
- };
-};
-
-struct clock_data {
- WakeupType wakeup;
- int fd;
-
- /* For all clocks we maintain two priority queues each, one
- * ordered for the earliest times the events may be
- * dispatched, and one ordered by the latest times they must
- * have been dispatched. The range between the top entries in
- * the two prioqs is the time window we can freely schedule
- * wakeups in */
-
- Prioq *earliest;
- Prioq *latest;
- usec_t next;
-
- bool needs_rearm:1;
-};
-
-struct signal_data {
- WakeupType wakeup;
-
- /* For each priority we maintain one signal fd, so that we
- * only have to dequeue a single event per priority at a
- * time. */
-
- int fd;
- int64_t priority;
- sigset_t sigset;
- sd_event_source *current;
-};
-
-/* A structure listing all event sources currently watching a specific inode */
-struct inode_data {
- /* The identifier for the inode, the combination of the .st_dev + .st_ino fields of the file */
- ino_t ino;
- dev_t dev;
-
- /* An fd of the inode to watch. The fd is kept open until the next iteration of the loop, so that we can
- * rearrange the priority still until then, as we need the original inode to change the priority as we need to
- * add a watch descriptor to the right inotify for the priority which we can only do if we have a handle to the
- * original inode. We keep a list of all inode_data objects with an open fd in the to_close list (see below) of
- * the sd-event object, so that it is efficient to close everything, before entering the next event loop
- * iteration. */
- int fd;
-
- /* The inotify "watch descriptor" */
- int wd;
-
- /* The combination of the mask of all inotify watches on this inode we manage. This is also the mask that has
- * most recently been set on the watch descriptor. */
- uint32_t combined_mask;
-
- /* All event sources subscribed to this inode */
- LIST_HEAD(sd_event_source, event_sources);
-
- /* The inotify object we watch this inode with */
- struct inotify_data *inotify_data;
-
- /* A linked list of all inode data objects with fds to close (see above) */
- LIST_FIELDS(struct inode_data, to_close);
-};
-
-/* A structure encapsulating an inotify fd */
-struct inotify_data {
- WakeupType wakeup;
-
- /* For each priority we maintain one inotify fd, so that we only have to dequeue a single event per priority at
- * a time */
-
- int fd;
- int64_t priority;
-
- Hashmap *inodes; /* The inode_data structures keyed by dev+ino */
- Hashmap *wd; /* The inode_data structures keyed by the watch descriptor for each */
-
- /* The buffer we read inotify events into */
- union inotify_event_buffer buffer;
- size_t buffer_filled; /* fill level of the buffer */
-
- /* How many event sources are currently marked pending for this inotify. We won't read new events off the
- * inotify fd as long as there are still pending events on the inotify (because we have no strategy of queuing
- * the events locally if they can't be coalesced). */
- unsigned n_pending;
-
- /* A linked list of all inotify objects with data already read, that still need processing. We keep this list
- * to make it efficient to figure out what inotify objects to process data on next. */
- LIST_FIELDS(struct inotify_data, buffered);
-};
-
struct sd_event {
unsigned n_ref;
@@ -316,6 +122,7 @@ static sd_event *event_resolve(sd_event *e) {
static int pending_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
+ int r;
assert(x->pending);
assert(y->pending);
@@ -327,22 +134,17 @@ static int pending_prioq_compare(const void *a, const void *b) {
return 1;
/* Lower priority values first */
- if (x->priority < y->priority)
- return -1;
- if (x->priority > y->priority)
- return 1;
+ r = CMP(x->priority, y->priority);
+ if (r != 0)
+ return r;
/* Older entries first */
- if (x->pending_iteration < y->pending_iteration)
- return -1;
- if (x->pending_iteration > y->pending_iteration)
- return 1;
-
- return 0;
+ return CMP(x->pending_iteration, y->pending_iteration);
}
static int prepare_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
+ int r;
assert(x->prepare);
assert(y->prepare);
@@ -356,18 +158,12 @@ static int prepare_prioq_compare(const void *a, const void *b) {
/* Move most recently prepared ones last, so that we can stop
* preparing as soon as we hit one that has already been
* prepared in the current iteration */
- if (x->prepare_iteration < y->prepare_iteration)
- return -1;
- if (x->prepare_iteration > y->prepare_iteration)
- return 1;
+ r = CMP(x->prepare_iteration, y->prepare_iteration);
+ if (r != 0)
+ return r;
/* Lower priority values first */
- if (x->priority < y->priority)
- return -1;
- if (x->priority > y->priority)
- return 1;
-
- return 0;
+ return CMP(x->priority, y->priority);
}
static int earliest_time_prioq_compare(const void *a, const void *b) {
@@ -389,12 +185,7 @@ static int earliest_time_prioq_compare(const void *a, const void *b) {
return 1;
/* Order by time */
- if (x->time.next < y->time.next)
- return -1;
- if (x->time.next > y->time.next)
- return 1;
-
- return 0;
+ return CMP(x->time.next, y->time.next);
}
static usec_t time_event_source_latest(const sd_event_source *s) {
@@ -420,12 +211,7 @@ static int latest_time_prioq_compare(const void *a, const void *b) {
return 1;
/* Order by time */
- if (time_event_source_latest(x) < time_event_source_latest(y))
- return -1;
- if (time_event_source_latest(x) > time_event_source_latest(y))
- return 1;
-
- return 0;
+ return CMP(time_event_source_latest(x), time_event_source_latest(y));
}
static int exit_prioq_compare(const void *a, const void *b) {
@@ -441,12 +227,7 @@ static int exit_prioq_compare(const void *a, const void *b) {
return 1;
/* Lower priority values first */
- if (x->priority < y->priority)
- return -1;
- if (x->priority > y->priority)
- return 1;
-
- return 0;
+ return CMP(x->priority, y->priority);
}
static void free_clock_data(struct clock_data *d) {
@@ -458,7 +239,7 @@ static void free_clock_data(struct clock_data *d) {
prioq_free(d->latest);
}
-static void event_free(sd_event *e) {
+static sd_event *event_free(sd_event *e) {
sd_event_source *s;
assert(e);
@@ -494,7 +275,8 @@ static void event_free(sd_event *e) {
hashmap_free(e->child_sources);
set_free(e->post_sources);
- free(e);
+
+ return mfree(e);
}
_public_ int sd_event_new(sd_event** ret) {
@@ -555,30 +337,7 @@ fail:
return r;
}
-_public_ sd_event* sd_event_ref(sd_event *e) {
-
- if (!e)
- return NULL;
-
- assert(e->n_ref >= 1);
- e->n_ref++;
-
- return e;
-}
-
-_public_ sd_event* sd_event_unref(sd_event *e) {
-
- if (!e)
- return NULL;
-
- assert(e->n_ref >= 1);
- e->n_ref--;
-
- if (e->n_ref <= 0)
- event_free(e);
-
- return NULL;
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event, sd_event, event_free);
static bool event_pid_changed(sd_event *e) {
assert(e);
@@ -974,7 +733,7 @@ static void source_disconnect(sd_event_source *s) {
* continued to being watched. That's because inotify doesn't really have an API for that: we
* can only change watch masks with access to the original inode either by fd or by path. But
* paths aren't stable, and keeping an O_PATH fd open all the time would mean wasting an fd
- * continously and keeping the mount busy which we can't really do. We could reconstruct the
+ * continuously and keeping the mount busy which we can't really do. We could reconstruct the
* original inode from /proc/self/fdinfo/$INOTIFY_FD (as all watch descriptors are listed
* there), but given the need for open_by_handle_at() which is privileged and not universally
* available this would be quite an incomplete solution. Hence we go the other way, leave the
@@ -1023,6 +782,7 @@ static void source_free(sd_event_source *s) {
free(s->description);
free(s);
}
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, source_free);
static int source_set_pending(sd_event_source *s, bool b) {
int r;
@@ -1116,7 +876,7 @@ _public_ int sd_event_add_io(
sd_event_io_handler_t callback,
void *userdata) {
- sd_event_source *s;
+ _cleanup_(source_freep) sd_event_source *s = NULL;
int r;
assert_return(e, -EINVAL);
@@ -1139,13 +899,12 @@ _public_ int sd_event_add_io(
s->enabled = SD_EVENT_ON;
r = source_io_register(s, s->enabled, events);
- if (r < 0) {
- source_free(s);
+ if (r < 0)
return r;
- }
if (ret)
*ret = s;
+ TAKE_PTR(s);
return 0;
}
@@ -1154,7 +913,7 @@ static void initialize_perturb(sd_event *e) {
sd_id128_t bootid = {};
/* When we sleep for longer, we try to realign the wakeup to
- the same time wihtin each minute/second/250ms, so that
+ the same time within each minute/second/250ms, so that
events all across the system can be coalesced into a single
CPU wakeup. However, let's take some system-specific
randomness for this value, so that in a network of systems
@@ -1220,7 +979,7 @@ _public_ int sd_event_add_time(
void *userdata) {
EventSourceType type;
- sd_event_source *s;
+ _cleanup_(source_freep) sd_event_source *s = NULL;
struct clock_data *d;
int r;
@@ -1272,20 +1031,17 @@ _public_ int sd_event_add_time(
r = prioq_put(d->earliest, s, &s->time.earliest_index);
if (r < 0)
- goto fail;
+ return r;
r = prioq_put(d->latest, s, &s->time.latest_index);
if (r < 0)
- goto fail;
+ return r;
if (ret)
*ret = s;
+ TAKE_PTR(s);
return 0;
-
-fail:
- source_free(s);
- return r;
}
static int signal_exit_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
@@ -1301,7 +1057,7 @@ _public_ int sd_event_add_signal(
sd_event_signal_handler_t callback,
void *userdata) {
- sd_event_source *s;
+ _cleanup_(source_freep) sd_event_source *s = NULL;
struct signal_data *d;
sigset_t ss;
int r;
@@ -1341,16 +1097,15 @@ _public_ int sd_event_add_signal(
e->signal_sources[sig] = s;
r = event_make_signal_data(e, sig, &d);
- if (r < 0) {
- source_free(s);
+ if (r < 0)
return r;
- }
/* Use the signal name as description for the event source by default */
(void) sd_event_source_set_description(s, signal_to_string(sig));
if (ret)
*ret = s;
+ TAKE_PTR(s);
return 0;
}
@@ -1363,7 +1118,7 @@ _public_ int sd_event_add_child(
sd_event_child_handler_t callback,
void *userdata) {
- sd_event_source *s;
+ _cleanup_(source_freep) sd_event_source *s = NULL;
int r;
assert_return(e, -EINVAL);
@@ -1393,17 +1148,14 @@ _public_ int sd_event_add_child(
s->enabled = SD_EVENT_ONESHOT;
r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
- if (r < 0) {
- source_free(s);
+ if (r < 0)
return r;
- }
e->n_enabled_child_sources++;
r = event_make_signal_data(e, SIGCHLD, NULL);
if (r < 0) {
e->n_enabled_child_sources--;
- source_free(s);
return r;
}
@@ -1411,6 +1163,7 @@ _public_ int sd_event_add_child(
if (ret)
*ret = s;
+ TAKE_PTR(s);
return 0;
}
@@ -1421,7 +1174,7 @@ _public_ int sd_event_add_defer(
sd_event_handler_t callback,
void *userdata) {
- sd_event_source *s;
+ _cleanup_(source_freep) sd_event_source *s = NULL;
int r;
assert_return(e, -EINVAL);
@@ -1439,13 +1192,12 @@ _public_ int sd_event_add_defer(
s->enabled = SD_EVENT_ONESHOT;
r = source_set_pending(s, true);
- if (r < 0) {
- source_free(s);
+ if (r < 0)
return r;
- }
if (ret)
*ret = s;
+ TAKE_PTR(s);
return 0;
}
@@ -1456,7 +1208,7 @@ _public_ int sd_event_add_post(
sd_event_handler_t callback,
void *userdata) {
- sd_event_source *s;
+ _cleanup_(source_freep) sd_event_source *s = NULL;
int r;
assert_return(e, -EINVAL);
@@ -1478,13 +1230,12 @@ _public_ int sd_event_add_post(
s->enabled = SD_EVENT_ON;
r = set_put(e->post_sources, s);
- if (r < 0) {
- source_free(s);
+ if (r < 0)
return r;
- }
if (ret)
*ret = s;
+ TAKE_PTR(s);
return 0;
}
@@ -1495,7 +1246,7 @@ _public_ int sd_event_add_exit(
sd_event_handler_t callback,
void *userdata) {
- sd_event_source *s;
+ _cleanup_(source_freep) sd_event_source *s = NULL;
int r;
assert_return(e, -EINVAL);
@@ -1518,13 +1269,12 @@ _public_ int sd_event_add_exit(
s->enabled = SD_EVENT_ONESHOT;
r = prioq_put(s->event->exit, s, &s->exit.prioq_index);
- if (r < 0) {
- source_free(s);
+ if (r < 0)
return r;
- }
if (ret)
*ret = s;
+ TAKE_PTR(s);
return 0;
}
@@ -1621,38 +1371,27 @@ static int event_make_inotify_data(
return 1;
}
-static int inode_data_compare(const void *a, const void *b) {
- const struct inode_data *x = a, *y = b;
+static int inode_data_compare(const struct inode_data *x, const struct inode_data *y) {
+ int r;
assert(x);
assert(y);
- if (x->dev < y->dev)
- return -1;
- if (x->dev > y->dev)
- return 1;
-
- if (x->ino < y->ino)
- return -1;
- if (x->ino > y->ino)
- return 1;
+ r = CMP(x->dev, y->dev);
+ if (r != 0)
+ return r;
- return 0;
+ return CMP(x->ino, y->ino);
}
-static void inode_data_hash_func(const void *p, struct siphash *state) {
- const struct inode_data *d = p;
-
- assert(p);
+static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) {
+ assert(d);
siphash24_compress(&d->dev, sizeof(d->dev), state);
siphash24_compress(&d->ino, sizeof(d->ino), state);
}
-const struct hash_ops inode_data_hash_ops = {
- .hash = inode_data_hash_func,
- .compare = inode_data_compare
-};
+DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare);
static void event_free_inode_data(
sd_event *e,
@@ -1779,7 +1518,7 @@ static uint32_t inode_data_determine_mask(struct inode_data *d) {
*
* Note that we add all sources to the mask here, regardless whether enabled, disabled or oneshot. That's
* because we cannot change the mask anymore after the event source was created once, since the kernel has no
- * API for that. Hence we need to subscribe to the maximum mask we ever might be interested in, and supress
+ * API for that. Hence we need to subscribe to the maximum mask we ever might be interested in, and suppress
* events we don't care for client-side. */
LIST_FOREACH(inotify.by_inode_data, s, d->event_sources) {
@@ -1841,11 +1580,10 @@ _public_ int sd_event_add_inotify(
sd_event_inotify_handler_t callback,
void *userdata) {
- bool rm_inotify = false, rm_inode = false;
struct inotify_data *inotify_data = NULL;
struct inode_data *inode_data = NULL;
_cleanup_close_ int fd = -1;
- sd_event_source *s;
+ _cleanup_(source_freep) sd_event_source *s = NULL;
struct stat st;
int r;
@@ -1883,13 +1621,13 @@ _public_ int sd_event_add_inotify(
/* Allocate an inotify object for this priority, and an inode object within it */
r = event_make_inotify_data(e, SD_EVENT_PRIORITY_NORMAL, &inotify_data);
if (r < 0)
- goto fail;
- rm_inotify = r > 0;
+ return r;
r = event_make_inode_data(e, inotify_data, st.st_dev, st.st_ino, &inode_data);
- if (r < 0)
- goto fail;
- rm_inode = r > 0;
+ if (r < 0) {
+ event_free_inotify_data(e, inotify_data);
+ return r;
+ }
/* Keep the O_PATH fd around until the first iteration of the loop, so that we can still change the priority of
* the event source, until then, for which we need the original inode. */
@@ -1902,72 +1640,45 @@ _public_ int sd_event_add_inotify(
LIST_PREPEND(inotify.by_inode_data, inode_data->event_sources, s);
s->inotify.inode_data = inode_data;
- rm_inode = rm_inotify = false;
-
/* Actually realize the watch now */
r = inode_data_realize_watch(e, inode_data);
if (r < 0)
- goto fail;
+ return r;
(void) sd_event_source_set_description(s, path);
if (ret)
*ret = s;
+ TAKE_PTR(s);
return 0;
-
-fail:
- source_free(s);
-
- if (rm_inode)
- event_free_inode_data(e, inode_data);
-
- if (rm_inotify)
- event_free_inotify_data(e, inotify_data);
-
- return r;
}
-_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) {
-
+static sd_event_source* event_source_free(sd_event_source *s) {
if (!s)
return NULL;
- assert(s->n_ref >= 1);
- s->n_ref++;
+ /* Here's a special hack: when we are called from a
+ * dispatch handler we won't free the event source
+ * immediately, but we will detach the fd from the
+ * epoll. This way it is safe for the caller to unref
+ * the event source and immediately close the fd, but
+ * we still retain a valid event source object after
+ * the callback. */
- return s;
-}
-
-_public_ sd_event_source* sd_event_source_unref(sd_event_source *s) {
-
- if (!s)
- return NULL;
-
- assert(s->n_ref >= 1);
- s->n_ref--;
-
- if (s->n_ref <= 0) {
- /* Here's a special hack: when we are called from a
- * dispatch handler we won't free the event source
- * immediately, but we will detach the fd from the
- * epoll. This way it is safe for the caller to unref
- * the event source and immediately close the fd, but
- * we still retain a valid event source object after
- * the callback. */
-
- if (s->dispatching) {
- if (s->type == SOURCE_IO)
- source_io_unregister(s);
+ if (s->dispatching) {
+ if (s->type == SOURCE_IO)
+ source_io_unregister(s);
- source_disconnect(s);
- } else
- source_free(s);
- }
+ source_disconnect(s);
+ } else
+ source_free(s);
return NULL;
}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event_source, sd_event_source, event_source_free);
+
_public_ int sd_event_source_set_description(sd_event_source *s, const char *description) {
assert_return(s, -EINVAL);
assert_return(!event_pid_changed(s->event), -ECHILD);
@@ -1978,9 +1689,11 @@ _public_ int sd_event_source_set_description(sd_event_source *s, const char *des
_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) {
assert_return(s, -EINVAL);
assert_return(description, -EINVAL);
- assert_return(s->description, -ENXIO);
assert_return(!event_pid_changed(s->event), -ECHILD);
+ if (!s->description)
+ return -ENXIO;
+
*description = s->description;
return 0;
}
@@ -2232,11 +1945,11 @@ fail:
_public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) {
assert_return(s, -EINVAL);
- assert_return(m, -EINVAL);
assert_return(!event_pid_changed(s->event), -ECHILD);
- *m = s->enabled;
- return 0;
+ if (m)
+ *m = s->enabled;
+ return s->enabled != SD_EVENT_OFF;
}
_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
@@ -3793,3 +3506,31 @@ _public_ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_d
return !!s->destroy_callback;
}
+
+_public_ int sd_event_source_get_floating(sd_event_source *s) {
+ assert_return(s, -EINVAL);
+
+ return s->floating;
+}
+
+_public_ int sd_event_source_set_floating(sd_event_source *s, int b) {
+ assert_return(s, -EINVAL);
+
+ if (s->floating == !!b)
+ return 0;
+
+ if (!s->event) /* Already disconnected */
+ return -ESTALE;
+
+ s->floating = b;
+
+ if (b) {
+ sd_event_source_ref(s);
+ sd_event_unref(s->event);
+ } else {
+ sd_event_ref(s->event);
+ sd_event_source_unref(s);
+ }
+
+ return 1;
+}
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index bde00cf719..6fd6d9f0b0 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <sys/wait.h>
@@ -8,7 +6,6 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "log.h"
#include "macro.h"
@@ -18,6 +15,8 @@
#include "signal-util.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "util.h"
static int prepare_handler(sd_event_source *s, void *userdata) {
@@ -483,9 +482,7 @@ static void test_inotify(unsigned n_create_events) {
}
int main(int argc, char *argv[]) {
-
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
+ test_setup_logging(LOG_DEBUG);
test_basic();
test_sd_event_now();
diff --git a/src/libsystemd/sd-hwdb/hwdb-internal.h b/src/libsystemd/sd-hwdb/hwdb-internal.h
index 1fc6d31aed..d82b8c1279 100644
--- a/src/libsystemd/sd-hwdb/hwdb-internal.h
+++ b/src/libsystemd/sd-hwdb/hwdb-internal.h
@@ -1,9 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <stdint.h>
#include "sparse-endian.h"
-#include "util.h"
#define HWDB_SIG { 'K', 'S', 'L', 'P', 'H', 'H', 'R', 'H' }
diff --git a/src/libsystemd/sd-hwdb/hwdb-util.c b/src/libsystemd/sd-hwdb/hwdb-util.c
new file mode 100644
index 0000000000..c5c329f2ac
--- /dev/null
+++ b/src/libsystemd/sd-hwdb/hwdb-util.c
@@ -0,0 +1,686 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "conf-files.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hwdb-internal.h"
+#include "hwdb-util.h"
+#include "label.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "strbuf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+
+static const char *default_hwdb_bin_dir = "/etc/udev";
+static const char * const conf_file_dirs[] = {
+ "/etc/udev/hwdb.d",
+ UDEVLIBEXECDIR "/hwdb.d",
+ NULL
+};
+
+/*
+ * Generic udev properties, key-value database based on modalias strings.
+ * Uses a Patricia/radix trie to index all matches for efficient lookup.
+ */
+
+/* in-memory trie objects */
+struct trie {
+ struct trie_node *root;
+ struct strbuf *strings;
+
+ size_t nodes_count;
+ size_t children_count;
+ size_t values_count;
+};
+
+struct trie_node {
+ /* prefix, common part for all children of this node */
+ size_t prefix_off;
+
+ /* sorted array of pointers to children nodes */
+ struct trie_child_entry *children;
+ uint8_t children_count;
+
+ /* sorted array of key-value pairs */
+ struct trie_value_entry *values;
+ size_t values_count;
+};
+
+/* children array item with char (0-255) index */
+struct trie_child_entry {
+ uint8_t c;
+ struct trie_node *child;
+};
+
+/* value array item with key-value pairs */
+struct trie_value_entry {
+ size_t key_off;
+ size_t value_off;
+ size_t filename_off;
+ uint32_t line_number;
+ uint16_t file_priority;
+};
+
+static int trie_children_cmp(const struct trie_child_entry *a, const struct trie_child_entry *b) {
+ return CMP(a->c, b->c);
+}
+
+static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) {
+ struct trie_child_entry *child;
+
+ /* extend array, add new entry, sort for bisection */
+ child = reallocarray(node->children, node->children_count + 1, sizeof(struct trie_child_entry));
+ if (!child)
+ return -ENOMEM;
+
+ node->children = child;
+ trie->children_count++;
+ node->children[node->children_count].c = c;
+ node->children[node->children_count].child = node_child;
+ node->children_count++;
+ typesafe_qsort(node->children, node->children_count, trie_children_cmp);
+ trie->nodes_count++;
+
+ return 0;
+}
+
+static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) {
+ struct trie_child_entry *child;
+ struct trie_child_entry search;
+
+ search.c = c;
+ child = typesafe_bsearch(&search, node->children, node->children_count, trie_children_cmp);
+ if (child)
+ return child->child;
+ return NULL;
+}
+
+static void trie_node_cleanup(struct trie_node *node) {
+ size_t i;
+
+ if (!node)
+ return;
+
+ for (i = 0; i < node->children_count; i++)
+ trie_node_cleanup(node->children[i].child);
+ free(node->children);
+ free(node->values);
+ free(node);
+}
+
+static void trie_free(struct trie *trie) {
+ if (!trie)
+ return;
+
+ trie_node_cleanup(trie->root);
+ strbuf_cleanup(trie->strings);
+ free(trie);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct trie*, trie_free);
+
+static int trie_values_cmp(const struct trie_value_entry *a, const struct trie_value_entry *b, struct trie *trie) {
+ return strcmp(trie->strings->buf + a->key_off,
+ trie->strings->buf + b->key_off);
+}
+
+static int trie_node_add_value(struct trie *trie, struct trie_node *node,
+ const char *key, const char *value,
+ const char *filename, uint16_t file_priority, uint32_t line_number, bool compat) {
+ ssize_t k, v, fn = 0;
+ struct trie_value_entry *val;
+
+ k = strbuf_add_string(trie->strings, key, strlen(key));
+ if (k < 0)
+ return k;
+ v = strbuf_add_string(trie->strings, value, strlen(value));
+ if (v < 0)
+ return v;
+
+ if (!compat) {
+ fn = strbuf_add_string(trie->strings, filename, strlen(filename));
+ if (fn < 0)
+ return fn;
+ }
+
+ if (node->values_count) {
+ struct trie_value_entry search = {
+ .key_off = k,
+ .value_off = v,
+ };
+
+ val = typesafe_bsearch_r(&search, node->values, node->values_count, trie_values_cmp, trie);
+ if (val) {
+ /* At this point we have 2 identical properties on the same match-string.
+ * Since we process files in order, we just replace the previous value. */
+ val->value_off = v;
+ val->filename_off = fn;
+ val->file_priority = file_priority;
+ val->line_number = line_number;
+ return 0;
+ }
+ }
+
+ /* extend array, add new entry, sort for bisection */
+ val = reallocarray(node->values, node->values_count + 1, sizeof(struct trie_value_entry));
+ if (!val)
+ return -ENOMEM;
+ trie->values_count++;
+ node->values = val;
+ node->values[node->values_count] = (struct trie_value_entry) {
+ .key_off = k,
+ .value_off = v,
+ .filename_off = fn,
+ .file_priority = file_priority,
+ .line_number = line_number,
+ };
+ node->values_count++;
+ typesafe_qsort_r(node->values, node->values_count, trie_values_cmp, trie);
+ return 0;
+}
+
+static int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
+ const char *key, const char *value,
+ const char *filename, uint16_t file_priority, uint32_t line_number, bool compat) {
+ size_t i = 0;
+ int r = 0;
+
+ for (;;) {
+ size_t p;
+ uint8_t c;
+ struct trie_node *child;
+
+ for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
+ _cleanup_free_ struct trie_node *new_child = NULL;
+ _cleanup_free_ char *s = NULL;
+ ssize_t off;
+
+ if (c == search[i + p])
+ continue;
+
+ /* split node */
+ new_child = new(struct trie_node, 1);
+ if (!new_child)
+ return -ENOMEM;
+
+ /* move values from parent to child */
+ *new_child = (struct trie_node) {
+ .prefix_off = node->prefix_off + p+1,
+ .children = node->children,
+ .children_count = node->children_count,
+ .values = node->values,
+ .values_count = node->values_count,
+ };
+
+ /* update parent; use strdup() because the source gets realloc()d */
+ s = strndup(trie->strings->buf + node->prefix_off, p);
+ if (!s)
+ return -ENOMEM;
+
+ off = strbuf_add_string(trie->strings, s, p);
+ if (off < 0)
+ return off;
+
+ *node = (struct trie_node) {
+ .prefix_off = off,
+ };
+ r = node_add_child(trie, node, new_child, c);
+ if (r < 0)
+ return r;
+
+ new_child = NULL; /* avoid cleanup */
+ break;
+ }
+ i += p;
+
+ c = search[i];
+ if (c == '\0')
+ return trie_node_add_value(trie, node, key, value, filename, file_priority, line_number, compat);
+
+ child = node_lookup(node, c);
+ if (!child) {
+ _cleanup_free_ struct trie_node *new_child = NULL;
+ ssize_t off;
+
+ /* new child */
+ new_child = new(struct trie_node, 1);
+ if (!new_child)
+ return -ENOMEM;
+
+ off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
+ if (off < 0)
+ return off;
+
+ *new_child = (struct trie_node) {
+ .prefix_off = off,
+ };
+
+ r = node_add_child(trie, node, new_child, c);
+ if (r < 0)
+ return r;
+
+ child = TAKE_PTR(new_child);
+ return trie_node_add_value(trie, child, key, value, filename, file_priority, line_number, compat);
+ }
+
+ node = child;
+ i++;
+ }
+}
+
+struct trie_f {
+ FILE *f;
+ struct trie *trie;
+ uint64_t strings_off;
+
+ uint64_t nodes_count;
+ uint64_t children_count;
+ uint64_t values_count;
+};
+
+/* calculate the storage space for the nodes, children arrays, value arrays */
+static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node, bool compat) {
+ uint64_t i;
+
+ for (i = 0; i < node->children_count; i++)
+ trie_store_nodes_size(trie, node->children[i].child, compat);
+
+ trie->strings_off += sizeof(struct trie_node_f);
+ for (i = 0; i < node->children_count; i++)
+ trie->strings_off += sizeof(struct trie_child_entry_f);
+ for (i = 0; i < node->values_count; i++)
+ trie->strings_off += compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f);
+}
+
+static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node, bool compat) {
+ uint64_t i;
+ struct trie_node_f n = {
+ .prefix_off = htole64(trie->strings_off + node->prefix_off),
+ .children_count = node->children_count,
+ .values_count = htole64(node->values_count),
+ };
+ _cleanup_free_ struct trie_child_entry_f *children = NULL;
+ int64_t node_off;
+
+ if (node->children_count) {
+ children = new(struct trie_child_entry_f, node->children_count);
+ if (!children)
+ return -ENOMEM;
+ }
+
+ /* post-order recursion */
+ for (i = 0; i < node->children_count; i++) {
+ int64_t child_off;
+
+ child_off = trie_store_nodes(trie, node->children[i].child, compat);
+ if (child_off < 0)
+ return child_off;
+
+ children[i] = (struct trie_child_entry_f) {
+ .c = node->children[i].c,
+ .child_off = htole64(child_off),
+ };
+ }
+
+ /* write node */
+ node_off = ftello(trie->f);
+ fwrite(&n, sizeof(struct trie_node_f), 1, trie->f);
+ trie->nodes_count++;
+
+ /* append children array */
+ if (node->children_count) {
+ fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
+ trie->children_count += node->children_count;
+ }
+
+ /* append values array */
+ for (i = 0; i < node->values_count; i++) {
+ struct trie_value_entry2_f v = {
+ .key_off = htole64(trie->strings_off + node->values[i].key_off),
+ .value_off = htole64(trie->strings_off + node->values[i].value_off),
+ .filename_off = htole64(trie->strings_off + node->values[i].filename_off),
+ .line_number = htole32(node->values[i].line_number),
+ .file_priority = htole16(node->values[i].file_priority),
+ };
+
+ fwrite(&v, compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f), 1, trie->f);
+ }
+ trie->values_count += node->values_count;
+
+ return node_off;
+}
+
+static int trie_store(struct trie *trie, const char *filename, bool compat) {
+ struct trie_f t = {
+ .trie = trie,
+ };
+ _cleanup_free_ char *filename_tmp = NULL;
+ int64_t pos;
+ int64_t root_off;
+ int64_t size;
+ struct trie_header_f h = {
+ .signature = HWDB_SIG,
+ .tool_version = htole64(atoi(PACKAGE_VERSION)),
+ .header_size = htole64(sizeof(struct trie_header_f)),
+ .node_size = htole64(sizeof(struct trie_node_f)),
+ .child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
+ .value_entry_size = htole64(compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f)),
+ };
+ int r;
+
+ /* calculate size of header, nodes, children entries, value entries */
+ t.strings_off = sizeof(struct trie_header_f);
+ trie_store_nodes_size(&t, trie->root, compat);
+
+ r = fopen_temporary(filename, &t.f, &filename_tmp);
+ if (r < 0)
+ return r;
+ fchmod(fileno(t.f), 0444);
+
+ /* write nodes */
+ if (fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET) < 0)
+ goto error_fclose;
+
+ root_off = trie_store_nodes(&t, trie->root, compat);
+ h.nodes_root_off = htole64(root_off);
+ pos = ftello(t.f);
+ h.nodes_len = htole64(pos - sizeof(struct trie_header_f));
+
+ /* write string buffer */
+ fwrite(trie->strings->buf, trie->strings->len, 1, t.f);
+ h.strings_len = htole64(trie->strings->len);
+
+ /* write header */
+ size = ftello(t.f);
+ h.file_size = htole64(size);
+ if (fseeko(t.f, 0, SEEK_SET) < 0)
+ goto error_fclose;
+ fwrite(&h, sizeof(struct trie_header_f), 1, t.f);
+
+ if (ferror(t.f))
+ goto error_fclose;
+ if (fflush(t.f) < 0)
+ goto error_fclose;
+ if (fsync(fileno(t.f)) < 0)
+ goto error_fclose;
+ if (rename(filename_tmp, filename) < 0)
+ goto error_fclose;
+
+ /* write succeeded */
+ fclose(t.f);
+
+ log_debug("=== trie on-disk ===");
+ log_debug("size: %8"PRIi64" bytes", size);
+ log_debug("header: %8zu bytes", sizeof(struct trie_header_f));
+ log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")",
+ t.nodes_count * sizeof(struct trie_node_f), t.nodes_count);
+ log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")",
+ t.children_count * sizeof(struct trie_child_entry_f), t.children_count);
+ log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")",
+ t.values_count * (compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f)), t.values_count);
+ log_debug("string store: %8zu bytes", trie->strings->len);
+ log_debug("strings start: %8"PRIu64, t.strings_off);
+ return 0;
+
+ error_fclose:
+ r = -errno;
+ fclose(t.f);
+ unlink(filename_tmp);
+ return r;
+}
+
+static int insert_data(struct trie *trie, char **match_list, char *line, const char *filename,
+ uint16_t file_priority, uint32_t line_number, bool compat) {
+ char *value, **entry;
+
+ assert(line[0] == ' ');
+
+ value = strchr(line, '=');
+ if (!value)
+ return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ "Key-value pair expected but got \"%s\", ignoring", line);
+
+ value[0] = '\0';
+ value++;
+
+ /* Replace multiple leading spaces by a single space */
+ while (isblank(line[0]) && isblank(line[1]))
+ line++;
+
+ if (isempty(line + 1) || isempty(value))
+ return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ "Empty %s in \"%s=%s\", ignoring",
+ isempty(line + 1) ? "key" : "value",
+ line, value);
+
+ STRV_FOREACH(entry, match_list)
+ trie_insert(trie, trie->root, *entry, line, value, filename, file_priority, line_number, compat);
+
+ return 0;
+}
+
+static int import_file(struct trie *trie, const char *filename, uint16_t file_priority, bool compat) {
+ enum {
+ HW_NONE,
+ HW_MATCH,
+ HW_DATA,
+ } state = HW_NONE;
+ _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");
+ if (!f)
+ return -errno;
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ size_t len;
+ char *pos;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ ++line_number;
+
+ /* comment line */
+ if (line[0] == '#')
+ continue;
+
+ /* strip trailing comment */
+ pos = strchr(line, '#');
+ if (pos)
+ pos[0] = '\0';
+
+ /* strip trailing whitespace */
+ len = strlen(line);
+ while (len > 0 && isspace(line[len-1]))
+ len--;
+ line[len] = '\0';
+
+ switch (state) {
+ case HW_NONE:
+ if (len == 0)
+ 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;
+ break;
+ }
+
+ /* start of record, first match */
+ state = HW_MATCH;
+
+ match = strdup(line);
+ if (!match)
+ return -ENOMEM;
+
+ err = strv_consume(&match_list, match);
+ if (err < 0)
+ return err;
+
+ break;
+
+ case HW_MATCH:
+ if (len == 0) {
+ log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ "Property expected, ignoring record with no properties");
+ r = -EINVAL;
+ state = HW_NONE;
+ strv_clear(match_list);
+ break;
+ }
+
+ if (line[0] != ' ') {
+ /* another match */
+ match = strdup(line);
+ if (!match)
+ return -ENOMEM;
+
+ err = strv_consume(&match_list, match);
+ if (err < 0)
+ return err;
+
+ break;
+ }
+
+ /* first data */
+ state = HW_DATA;
+ err = insert_data(trie, match_list, line, filename, file_priority, line_number, compat);
+ if (err < 0)
+ r = err;
+ break;
+
+ case HW_DATA:
+ if (len == 0) {
+ /* end of record */
+ state = HW_NONE;
+ strv_clear(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;
+ state = HW_NONE;
+ strv_clear(match_list);
+ break;
+ }
+
+ err = insert_data(trie, match_list, line, filename, file_priority, line_number, compat);
+ if (err < 0)
+ r = err;
+ break;
+ };
+ }
+
+ if (state == HW_MATCH)
+ log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ "Property expected, ignoring record with no properties");
+
+ return r;
+}
+
+int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool compat) {
+ _cleanup_free_ char *hwdb_bin = NULL;
+ _cleanup_(trie_freep) struct trie *trie = NULL;
+ _cleanup_strv_free_ char **files = NULL;
+ char **f;
+ uint16_t file_priority = 1;
+ int r = 0, err;
+
+ /* The argument 'compat' controls the format version of database. If false, then hwdb.bin will be created with
+ * additional information such that priority, line number, and filename of database source. If true, then hwdb.bin
+ * will be created without the information. systemd-hwdb command should set the argument false, and 'udevadm hwdb'
+ * command should set it true. */
+
+ trie = new0(struct trie, 1);
+ if (!trie)
+ return -ENOMEM;
+
+ /* string store */
+ trie->strings = strbuf_new();
+ if (!trie->strings)
+ return -ENOMEM;
+
+ /* index */
+ trie->root = new0(struct trie_node, 1);
+ if (!trie->root)
+ return -ENOMEM;
+
+ trie->nodes_count++;
+
+ err = conf_files_list_strv(&files, ".hwdb", root, 0, conf_file_dirs);
+ if (err < 0)
+ return log_error_errno(err, "Failed to enumerate hwdb files: %m");
+
+ STRV_FOREACH(f, files) {
+ log_debug("Reading file \"%s\"", *f);
+ err = import_file(trie, *f, file_priority++, compat);
+ if (err < 0 && strict)
+ r = err;
+ }
+
+ strbuf_complete(trie->strings);
+
+ log_debug("=== trie in-memory ===");
+ log_debug("nodes: %8zu bytes (%8zu)",
+ trie->nodes_count * sizeof(struct trie_node), trie->nodes_count);
+ log_debug("children arrays: %8zu bytes (%8zu)",
+ trie->children_count * sizeof(struct trie_child_entry), trie->children_count);
+ log_debug("values arrays: %8zu bytes (%8zu)",
+ trie->values_count * sizeof(struct trie_value_entry), trie->values_count);
+ log_debug("strings: %8zu bytes",
+ trie->strings->len);
+ log_debug("strings incoming: %8zu bytes (%8zu)",
+ trie->strings->in_len, trie->strings->in_count);
+ log_debug("strings dedup'ed: %8zu bytes (%8zu)",
+ trie->strings->dedup_len, trie->strings->dedup_count);
+
+ hwdb_bin = path_join(root, hwdb_bin_dir ?: default_hwdb_bin_dir, "hwdb.bin");
+ if (!hwdb_bin)
+ return -ENOMEM;
+
+ mkdir_parents_label(hwdb_bin, 0755);
+ err = trie_store(trie, hwdb_bin, compat);
+ if (err < 0)
+ return log_error_errno(err, "Failed to write database %s: %m", hwdb_bin);
+
+ err = label_fix(hwdb_bin, 0);
+ if (err < 0)
+ return err;
+
+ return r;
+}
+
+int hwdb_query(const char *modalias) {
+ _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
+ const char *key, *value;
+ int r;
+
+ assert(modalias);
+
+ r = sd_hwdb_new(&hwdb);
+ if (r < 0)
+ return r;
+
+ SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value)
+ printf("%s=%s\n", key, value);
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-hwdb/hwdb-util.h b/src/libsystemd/sd-hwdb/hwdb-util.h
index 2998249199..425b4b3e13 100644
--- a/src/libsystemd/sd-hwdb/hwdb-util.h
+++ b/src/libsystemd/sd-hwdb/hwdb-util.h
@@ -1,9 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <stdbool.h>
#include "sd-hwdb.h"
-#include "util.h"
-
bool hwdb_validate(sd_hwdb *hwdb);
+int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool compat);
+int hwdb_query(const char *modalias);
diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c
index 5c612d9a2f..b81786a64d 100644
--- a/src/libsystemd/sd-hwdb/sd-hwdb.c
+++ b/src/libsystemd/sd-hwdb/sd-hwdb.c
@@ -20,10 +20,10 @@
#include "hwdb-util.h"
#include "refcnt.h"
#include "string-util.h"
+#include "util.h"
struct sd_hwdb {
RefCount n_ref;
- int refcount;
FILE *f;
struct stat st;
@@ -148,7 +148,6 @@ static int hwdb_add_property(sd_hwdb *hwdb, const struct trie_value_entry_f *ent
if (old) {
/* On duplicates, we order by filename priority and line-number.
*
- *
* v2 of the format had 64 bits for the line number.
* v3 reuses top 32 bits of line_number to store the priority.
* We check the top bits — if they are zero we have v2 format.
@@ -327,25 +326,25 @@ _public_ int sd_hwdb_new(sd_hwdb **ret) {
else if (errno == ENOENT)
continue;
else
- return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
+ return log_debug_errno(errno, "Failed to open %s: %m", hwdb_bin_path);
}
if (!hwdb->f) {
- log_debug("hwdb.bin does not exist, please run systemd-hwdb update");
+ log_debug("hwdb.bin does not exist, please run 'systemd-hwdb update'");
return -ENOENT;
}
if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
- (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8)
- return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
+ (size_t) hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8)
+ return log_debug_errno(errno, "Failed to read %s: %m", hwdb_bin_path);
hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
if (hwdb->map == MAP_FAILED)
- return log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path);
+ return log_debug_errno(errno, "Failed to map %s: %m", hwdb_bin_path);
if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
- (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
- log_debug("error recognizing the format of %s", hwdb_bin_path);
+ (size_t) hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
+ log_debug("Failed to recognize the format of %s", hwdb_bin_path);
return -EINVAL;
}
@@ -361,25 +360,17 @@ _public_ int sd_hwdb_new(sd_hwdb **ret) {
return 0;
}
-_public_ sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb) {
- assert_return(hwdb, NULL);
-
- assert_se(REFCNT_INC(hwdb->n_ref) >= 2);
+static sd_hwdb *hwdb_free(sd_hwdb *hwdb) {
+ assert(hwdb);
- return hwdb;
+ if (hwdb->map)
+ munmap((void *)hwdb->map, hwdb->st.st_size);
+ safe_fclose(hwdb->f);
+ ordered_hashmap_free(hwdb->properties);
+ return mfree(hwdb);
}
-_public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) {
- if (hwdb && REFCNT_DEC(hwdb->n_ref) == 0) {
- if (hwdb->map)
- munmap((void *)hwdb->map, hwdb->st.st_size);
- safe_fclose(hwdb->f);
- ordered_hashmap_free(hwdb->properties);
- free(hwdb);
- }
-
- return NULL;
-}
+DEFINE_PUBLIC_ATOMIC_REF_UNREF_FUNC(sd_hwdb, sd_hwdb, hwdb_free)
bool hwdb_validate(sd_hwdb *hwdb) {
bool found = false;
diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c
index edee985e44..9ffd594ee6 100644
--- a/src/libsystemd/sd-id128/id128-util.c
+++ b/src/libsystemd/sd-id128/id128-util.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <errno.h>
#include <fcntl.h>
@@ -184,15 +182,12 @@ int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
return id128_write_fd(fd, f, id, do_sync);
}
-void id128_hash_func(const void *p, struct siphash *state) {
- siphash24_compress(p, 16, state);
+void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
+ siphash24_compress(p, sizeof(sd_id128_t), state);
}
-int id128_compare_func(const void *a, const void *b) {
+int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
return memcmp(a, b, 16);
}
-const struct hash_ops id128_hash_ops = {
- .hash = id128_hash_func,
- .compare = id128_compare_func,
-};
+DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
diff --git a/src/libsystemd/sd-id128/id128-util.h b/src/libsystemd/sd-id128/id128-util.h
index f0b4eca581..65f14ab252 100644
--- a/src/libsystemd/sd-id128/id128-util.h
+++ b/src/libsystemd/sd-id128/id128-util.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include <stdbool.h>
#include "sd-id128.h"
@@ -31,6 +28,6 @@ int id128_read(const char *p, Id128Format f, sd_id128_t *ret);
int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync);
int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
-void id128_hash_func(const void *p, struct siphash *state);
-int id128_compare_func(const void *a, const void *b) _pure_;
+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;
diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c
index b7123280f3..3593a71c02 100644
--- a/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libsystemd/sd-id128/sd-id128.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <errno.h>
#include <fcntl.h>
@@ -274,7 +272,9 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
assert_return(ret, -EINVAL);
- r = acquire_random_bytes(&t, sizeof t, true);
+ /* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be
+ * fine for UUIDS. */
+ r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND);
if (r < 0)
return r;
@@ -286,19 +286,15 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
return 0;
}
-_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
_cleanup_(khash_unrefp) khash *h = NULL;
- sd_id128_t m, result;
+ sd_id128_t result;
const void *p;
int r;
- assert_return(ret, -EINVAL);
-
- r = sd_id128_get_machine(&m);
- if (r < 0)
- return r;
+ assert(ret);
- r = khash_new_with_key(&h, "hmac(sha256)", &m, sizeof(m));
+ r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base));
if (r < 0)
return r;
@@ -316,3 +312,29 @@ _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *re
*ret = make_v4_uuid(result);
return 0;
}
+
+_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+ sd_id128_t id;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_id128_get_machine(&id);
+ if (r < 0)
+ return r;
+
+ return get_app_specific(id, app_id, ret);
+}
+
+_public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+ sd_id128_t id;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_id128_get_boot(&id);
+ if (r < 0)
+ return r;
+
+ return get_app_specific(id, app_id, ret);
+}
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index c2f7133e42..a904c6b544 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <errno.h>
#include <poll.h>
@@ -13,9 +11,9 @@
#include "alloc-util.h"
#include "cgroup-util.h"
#include "dirent-util.h"
+#include "env-file.h"
#include "escape.h"
#include "fd-util.h"
-#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "hostname-util.h"
@@ -260,8 +258,7 @@ static int file_of_uid(uid_t uid, char **p) {
}
_public_ int sd_uid_get_state(uid_t uid, char**state) {
- _cleanup_free_ char *p = NULL;
- char *s = NULL;
+ _cleanup_free_ char *p = NULL, *s = NULL;
int r;
assert_return(state, -EINVAL);
@@ -270,24 +267,17 @@ _public_ int sd_uid_get_state(uid_t uid, char**state) {
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE, "STATE", &s, NULL);
+ r = parse_env_file(NULL, p, "STATE", &s);
if (r == -ENOENT) {
- free(s);
- s = strdup("offline");
- if (!s)
- return -ENOMEM;
-
- }
- else if (r < 0) {
- free(s);
+ r = free_and_strdup(&s, "offline");
+ if (r < 0)
+ return r;
+ } else if (r < 0)
return r;
- }
- if (isempty(s)) {
- free(s);
+ else if (isempty(s))
return -EIO;
- }
- *state = s;
+ *state = TAKE_PTR(s);
return 0;
}
@@ -301,7 +291,7 @@ _public_ int sd_uid_get_display(uid_t uid, char **session) {
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE, "DISPLAY", &s, NULL);
+ r = parse_env_file(NULL, p, "DISPLAY", &s);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -356,7 +346,7 @@ _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat)
variable = require_active ? "ACTIVE_UID" : "UIDS";
- r = parse_env_file(NULL, p, NEWLINE, variable, &s, NULL);
+ r = parse_env_file(NULL, p, variable, &s);
if (r == -ENOENT)
return 0;
if (r < 0)
@@ -385,7 +375,7 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) {
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE, variable, &s, NULL);
+ r = parse_env_file(NULL, p, variable, &s);
if (r == -ENOENT || (r >= 0 && isempty(s))) {
if (array)
*array = NULL;
@@ -463,7 +453,7 @@ _public_ int sd_session_is_active(const char *session) {
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE, "ACTIVE", &s, NULL);
+ r = parse_env_file(NULL, p, "ACTIVE", &s);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -482,7 +472,7 @@ _public_ int sd_session_is_remote(const char *session) {
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE, "REMOTE", &s, NULL);
+ r = parse_env_file(NULL, p, "REMOTE", &s);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -503,7 +493,7 @@ _public_ int sd_session_get_state(const char *session, char **state) {
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE, "STATE", &s, NULL);
+ r = parse_env_file(NULL, p, "STATE", &s);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -526,7 +516,7 @@ _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE, "UID", &s, NULL);
+ r = parse_env_file(NULL, p, "UID", &s);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -548,7 +538,7 @@ static int session_get_string(const char *session, const char *field, char **val
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE, field, &s, NULL);
+ r = parse_env_file(NULL, p, field, &s);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -640,10 +630,9 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE,
+ r = parse_env_file(NULL, p,
"ACTIVE", &s,
- "ACTIVE_UID", &t,
- NULL);
+ "ACTIVE_UID", &t);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -678,10 +667,9 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE,
+ r = parse_env_file(NULL, p,
"SESSIONS", &s,
- "UIDS", &t,
- NULL);
+ "UIDS", &t);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -747,9 +735,8 @@ static int seat_get_can(const char *seat, const char *variable) {
if (r < 0)
return r;
- r = parse_env_file(NULL, p, NEWLINE,
- variable, &s,
- NULL);
+ r = parse_env_file(NULL, p,
+ variable, &s);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
@@ -892,20 +879,27 @@ _public_ int sd_machine_get_class(const char *machine, char **class) {
const char *p;
int r;
- assert_return(machine_name_is_valid(machine), -EINVAL);
assert_return(class, -EINVAL);
- p = strjoina("/run/systemd/machines/", machine);
- r = parse_env_file(NULL, p, NEWLINE, "CLASS", &c, NULL);
- if (r == -ENOENT)
- return -ENXIO;
- if (r < 0)
- return r;
- if (!c)
- return -EIO;
+ if (streq(machine, ".host")) {
+ c = strdup("host");
+ if (!c)
+ return -ENOMEM;
+ } else {
+ if (!machine_name_is_valid(machine))
+ return -EINVAL;
- *class = TAKE_PTR(c);
+ p = strjoina("/run/systemd/machines/", machine);
+ r = parse_env_file(NULL, p, "CLASS", &c);
+ if (r == -ENOENT)
+ return -ENXIO;
+ if (r < 0)
+ return r;
+ if (!c)
+ return -EIO;
+ }
+ *class = TAKE_PTR(c);
return 0;
}
@@ -920,7 +914,7 @@ _public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) {
assert_return(ifindices, -EINVAL);
p = strjoina("/run/systemd/machines/", machine);
- r = parse_env_file(NULL, p, NEWLINE, "NETIF", &netif, NULL);
+ r = parse_env_file(NULL, p, "NETIF", &netif);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c
index ccb1905a46..1789e547b8 100644
--- a/src/libsystemd/sd-login/test-login.c
+++ b/src/libsystemd/sd-login/test-login.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <poll.h>
#include <string.h>
diff --git a/src/libsystemd/sd-netlink/generic-netlink.c b/src/libsystemd/sd-netlink/generic-netlink.c
index 347bf4cbd5..3445757da9 100644
--- a/src/libsystemd/sd-netlink/generic-netlink.c
+++ b/src/libsystemd/sd-netlink/generic-netlink.c
@@ -12,6 +12,7 @@ typedef struct {
static const genl_family genl_families[] = {
[SD_GENL_ID_CTRL] = { .name = "", .version = 1 },
[SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 },
+ [SD_GENL_FOU] = { .name = "fou", .version = 1 },
};
int sd_genl_socket_open(sd_netlink **ret) {
diff --git a/src/libsystemd/sd-netlink/local-addresses.c b/src/libsystemd/sd-netlink/local-addresses.c
index 5467ba432f..5c37279bd2 100644
--- a/src/libsystemd/sd-netlink/local-addresses.c
+++ b/src/libsystemd/sd-netlink/local-addresses.c
@@ -7,8 +7,8 @@
#include "macro.h"
#include "netlink-util.h"
-static int address_compare(const void *_a, const void *_b) {
- const struct local_address *a = _a, *b = _b;
+static int address_compare(const struct local_address *a, const struct local_address *b) {
+ int r;
/* Order lowest scope first, IPv4 before IPv6, lowest interface index first */
@@ -17,20 +17,17 @@ static int address_compare(const void *_a, const void *_b) {
if (a->family == AF_INET6 && b->family == AF_INET)
return 1;
- if (a->scope < b->scope)
- return -1;
- if (a->scope > b->scope)
- return 1;
+ r = CMP(a->scope, b->scope);
+ if (r != 0)
+ return r;
- if (a->metric < b->metric)
- return -1;
- if (a->metric > b->metric)
- return 1;
+ r = CMP(a->metric, b->metric);
+ if (r != 0)
+ return r;
- if (a->ifindex < b->ifindex)
- return -1;
- if (a->ifindex > b->ifindex)
- return 1;
+ r = CMP(a->ifindex, b->ifindex);
+ if (r != 0)
+ return r;
return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family));
}
@@ -137,7 +134,7 @@ int local_addresses(sd_netlink *context, int ifindex, int af, struct local_addre
n_list++;
};
- qsort_safe(list, n_list, sizeof(struct local_address), address_compare);
+ typesafe_qsort(list, n_list, address_compare);
*ret = TAKE_PTR(list);
@@ -248,8 +245,7 @@ int local_gateways(sd_netlink *context, int ifindex, int af, struct local_addres
n_list++;
}
- if (n_list > 0)
- qsort(list, n_list, sizeof(struct local_address), address_compare);
+ typesafe_qsort(list, n_list, address_compare);
*ret = TAKE_PTR(list);
diff --git a/src/libsystemd/sd-netlink/local-addresses.h b/src/libsystemd/sd-netlink/local-addresses.h
index 4fc3477e47..e88c5e5d21 100644
--- a/src/libsystemd/sd-netlink/local-addresses.h
+++ b/src/libsystemd/sd-netlink/local-addresses.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
-***/
-
#include "sd-netlink.h"
#include "in-addr-util.h"
diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h
index dd4fa9d2af..98de4f0a83 100644
--- a/src/libsystemd/sd-netlink/netlink-internal.h
+++ b/src/libsystemd/sd-netlink/netlink-internal.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <linux/netlink.h>
#include "sd-netlink.h"
@@ -20,7 +19,6 @@
struct reply_callback {
sd_netlink_message_handler_t callback;
- void *userdata;
usec_t timeout;
uint64_t serial;
unsigned prioq_idx;
@@ -29,11 +27,34 @@ struct reply_callback {
struct match_callback {
sd_netlink_message_handler_t callback;
uint16_t type;
- void *userdata;
LIST_FIELDS(struct match_callback, match_callbacks);
};
+typedef enum NetlinkSlotType {
+ NETLINK_REPLY_CALLBACK,
+ NETLINK_MATCH_CALLBACK,
+ _NETLINK_SLOT_INVALID = -1,
+} NetlinkSlotType;
+
+struct sd_netlink_slot {
+ unsigned n_ref;
+ sd_netlink *netlink;
+ void *userdata;
+ sd_netlink_destroy_t destroy_callback;
+ NetlinkSlotType type:2;
+
+ bool floating:1;
+ char *description;
+
+ LIST_FIELDS(sd_netlink_slot, slots);
+
+ union {
+ struct reply_callback reply_callback;
+ struct match_callback match_callback;
+ };
+};
+
struct sd_netlink {
RefCount n_ref;
@@ -69,6 +90,8 @@ struct sd_netlink {
LIST_HEAD(struct match_callback, match_callbacks);
+ LIST_HEAD(sd_netlink_slot, slots);
+
pid_t original_pid;
sd_event_source *io_event_source;
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index 23907c8224..b0b25639f4 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -96,13 +96,7 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
return 0;
}
-sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m) {
- if (!m)
- return NULL;
-
- assert_se(REFCNT_INC(m->n_ref) >= 2);
- return m;
-}
+DEFINE_ATOMIC_REF_FUNC(sd_netlink_message, sd_netlink_message);
sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) {
sd_netlink_message *t;
@@ -568,6 +562,25 @@ static int netlink_message_read_internal(sd_netlink_message *m, unsigned short t
return RTA_PAYLOAD(rta);
}
+int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data) {
+ void *attr_data;
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ if (r < 0)
+ return r;
+
+ if ((size_t) r < size)
+ return -EIO;
+
+ if (data)
+ memcpy(data, attr_data, size);
+
+ return 0;
+}
+
int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) {
int r;
void *attr_data;
@@ -758,7 +771,7 @@ static int netlink_container_parse(sd_netlink_message *m,
struct netlink_container *container,
int count,
struct rtattr *rta,
- unsigned int rt_len) {
+ unsigned rt_len) {
_cleanup_free_ struct netlink_attribute *attributes = NULL;
attributes = new0(struct netlink_attribute, count);
diff --git a/src/libsystemd/sd-netlink/netlink-slot.c b/src/libsystemd/sd-netlink/netlink-slot.c
new file mode 100644
index 0000000000..2b8675dd32
--- /dev/null
+++ b/src/libsystemd/sd-netlink/netlink-slot.c
@@ -0,0 +1,202 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+
+#include "sd-netlink.h"
+
+#include "alloc-util.h"
+#include "netlink-internal.h"
+#include "netlink-slot.h"
+#include "string-util.h"
+
+int netlink_slot_allocate(
+ sd_netlink *nl,
+ bool floating,
+ NetlinkSlotType type,
+ size_t extra,
+ void *userdata,
+ const char *description,
+ sd_netlink_slot **ret) {
+
+ _cleanup_free_ sd_netlink_slot *slot = NULL;
+
+ assert(nl);
+ assert(ret);
+
+ slot = malloc0(offsetof(sd_netlink_slot, reply_callback) + extra);
+ if (!slot)
+ return -ENOMEM;
+
+ slot->n_ref = 1;
+ slot->netlink = nl;
+ slot->userdata = userdata;
+ slot->type = type;
+ slot->floating = floating;
+
+ if (description) {
+ slot->description = strdup(description);
+ if (!slot->description)
+ return -ENOMEM;
+ }
+
+ if (!floating)
+ sd_netlink_ref(nl);
+
+ LIST_PREPEND(slots, nl->slots, slot);
+
+ *ret = TAKE_PTR(slot);
+
+ return 0;
+}
+
+void netlink_slot_disconnect(sd_netlink_slot *slot, bool unref) {
+ sd_netlink *nl;
+
+ assert(slot);
+
+ nl = slot->netlink;
+ if (!nl)
+ return;
+
+ switch (slot->type) {
+
+ case NETLINK_REPLY_CALLBACK:
+ (void) hashmap_remove(nl->reply_callbacks, &slot->reply_callback.serial);
+
+ if (slot->reply_callback.timeout != 0)
+ prioq_remove(nl->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx);
+
+ break;
+ case NETLINK_MATCH_CALLBACK:
+ LIST_REMOVE(match_callbacks, nl->match_callbacks, &slot->match_callback);
+
+ switch (slot->match_callback.type) {
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+ (void) socket_broadcast_group_unref(nl, RTNLGRP_LINK);
+
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV4_IFADDR);
+ (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV6_IFADDR);
+
+ break;
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV4_ROUTE);
+ (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV6_ROUTE);
+
+ break;
+ }
+
+ break;
+ default:
+ assert_not_reached("Wut? Unknown slot type?");
+ }
+
+ slot->type = _NETLINK_SLOT_INVALID;
+ slot->netlink = NULL;
+ LIST_REMOVE(slots, nl->slots, slot);
+
+ if (!slot->floating)
+ sd_netlink_unref(nl);
+ else if (unref)
+ sd_netlink_slot_unref(slot);
+}
+
+static sd_netlink_slot* netlink_slot_free(sd_netlink_slot *slot) {
+ assert(slot);
+
+ netlink_slot_disconnect(slot, false);
+
+ if (slot->destroy_callback)
+ slot->destroy_callback(slot->userdata);
+
+ free(slot->description);
+ return mfree(slot);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_netlink_slot, sd_netlink_slot, netlink_slot_free);
+
+sd_netlink *sd_netlink_slot_get_netlink(sd_netlink_slot *slot) {
+ assert_return(slot, NULL);
+
+ return slot->netlink;
+}
+
+void *sd_netlink_slot_get_userdata(sd_netlink_slot *slot) {
+ assert_return(slot, NULL);
+
+ return slot->userdata;
+}
+
+void *sd_netlink_slot_set_userdata(sd_netlink_slot *slot, void *userdata) {
+ void *ret;
+
+ assert_return(slot, NULL);
+
+ ret = slot->userdata;
+ slot->userdata = userdata;
+
+ return ret;
+}
+
+int sd_netlink_slot_get_destroy_callback(sd_netlink_slot *slot, sd_netlink_destroy_t *callback) {
+ assert_return(slot, -EINVAL);
+
+ if (callback)
+ *callback = slot->destroy_callback;
+
+ return !!slot->destroy_callback;
+}
+
+int sd_netlink_slot_set_destroy_callback(sd_netlink_slot *slot, sd_netlink_destroy_t callback) {
+ assert_return(slot, -EINVAL);
+
+ slot->destroy_callback = callback;
+ return 0;
+}
+
+int sd_netlink_slot_get_floating(sd_netlink_slot *slot) {
+ assert_return(slot, -EINVAL);
+
+ return slot->floating;
+}
+
+int sd_netlink_slot_set_floating(sd_netlink_slot *slot, int b) {
+ assert_return(slot, -EINVAL);
+
+ if (slot->floating == !!b)
+ return 0;
+
+ if (!slot->netlink) /* Already disconnected */
+ return -ESTALE;
+
+ slot->floating = b;
+
+ if (b) {
+ sd_netlink_slot_ref(slot);
+ sd_netlink_unref(slot->netlink);
+ } else {
+ sd_netlink_ref(slot->netlink);
+ sd_netlink_slot_unref(slot);
+ }
+
+ return 1;
+}
+
+int sd_netlink_slot_get_description(sd_netlink_slot *slot, const char **description) {
+ assert_return(slot, -EINVAL);
+
+ if (description)
+ *description = slot->description;
+
+ return !!slot->description;
+}
+
+int sd_netlink_slot_set_description(sd_netlink_slot *slot, const char *description) {
+ assert_return(slot, -EINVAL);
+
+ return free_and_strdup(&slot->description, description);
+}
diff --git a/src/libsystemd/sd-netlink/netlink-slot.h b/src/libsystemd/sd-netlink/netlink-slot.h
new file mode 100644
index 0000000000..2641ec6b4a
--- /dev/null
+++ b/src/libsystemd/sd-netlink/netlink-slot.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-netlink.h"
+
+int netlink_slot_allocate(
+ sd_netlink *nl,
+ bool floating,
+ NetlinkSlotType type,
+ size_t extra,
+ void *userdata,
+ const char *description,
+ sd_netlink_slot **ret);
+void netlink_slot_disconnect(sd_netlink_slot *slot, bool unref);
diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c
index f103cbedea..432e8e8c06 100644
--- a/src/libsystemd/sd-netlink/netlink-socket.c
+++ b/src/libsystemd/sd-netlink/netlink-socket.c
@@ -9,6 +9,7 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "format-util.h"
+#include "io-util.h"
#include "missing.h"
#include "netlink-internal.h"
#include "netlink-types.h"
@@ -88,11 +89,11 @@ static int broadcast_groups_get(sd_netlink *nl) {
int socket_bind(sd_netlink *nl) {
socklen_t addrlen;
- int r, one = 1;
+ int r;
- r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one));
+ r = setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, true);
if (r < 0)
- return -errno;
+ return r;
addrlen = sizeof(nl->sockaddr);
@@ -334,8 +335,7 @@ int socket_read_message(sd_netlink *rtnl) {
len, sizeof(uint8_t)))
return -ENOMEM;
- iov.iov_base = rtnl->rbuffer;
- iov.iov_len = rtnl->rbuffer_allocated;
+ iov = IOVEC_MAKE(rtnl->rbuffer, rtnl->rbuffer_allocated);
/* read the pending message */
r = socket_recv_message(rtnl->fd, &iov, &group, false);
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index c93fe9cb4c..cd5cdcc6e5 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -16,7 +16,12 @@
#include <linux/if_link.h>
#include <linux/if_tunnel.h>
#include <linux/veth.h>
-#if HAVE_VXCAN_INFO_PEER
+
+#if HAVE_LINUX_FOU_H
+#include <linux/fou.h>
+#endif
+
+#if HAVE_LINUX_CAN_VXCAN_H
#include <linux/can/vxcan.h>
#endif
@@ -219,6 +224,10 @@ static const NLType rtnl_link_info_data_bond_types[] = {
[IFLA_BOND_AD_LACP_RATE] = { .type = NETLINK_TYPE_U8 },
[IFLA_BOND_AD_SELECT] = { .type = NETLINK_TYPE_U8 },
[IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED },
+ [IFLA_BOND_AD_ACTOR_SYS_PRIO] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BOND_AD_USER_PORT_KEY] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BOND_AD_ACTOR_SYSTEM] = { .type = NETLINK_TYPE_ETHER_ADDR },
+ [IFLA_BOND_TLB_DYNAMIC_LB] = { .type = NETLINK_TYPE_U8 },
};
static const NLType rtnl_link_info_data_iptun_types[] = {
@@ -257,6 +266,7 @@ static const NLType rtnl_link_info_data_ipgre_types[] = {
[IFLA_GRE_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 },
[IFLA_GRE_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 },
[IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_GRE_ERSPAN_INDEX] = { .type = NETLINK_TYPE_U32 },
};
static const NLType rtnl_link_info_data_ipvti_types[] = {
@@ -313,6 +323,7 @@ static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_VXLAN] = "vxlan",
[NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = "ipip",
[NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = "gre",
+ [NL_UNION_LINK_INFO_DATA_ERSPAN] = "erspan",
[NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = "gretap",
[NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = "ip6gre",
[NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = "ip6gretap",
@@ -352,6 +363,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
.types = rtnl_link_info_data_iptun_types },
[NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
.types = rtnl_link_info_data_ipgre_types },
+ [NL_UNION_LINK_INFO_DATA_ERSPAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
+ .types = rtnl_link_info_data_ipgre_types },
[NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
.types = rtnl_link_info_data_ipgre_types },
[NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
@@ -400,17 +413,40 @@ static const NLTypeSystem rtnl_link_info_type_system = {
};
static const struct NLType rtnl_prot_info_bridge_port_types[] = {
- [IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_MODE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_GUARD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_PROTECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_FAST_LEAVE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_LEARNING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_PROXYARP] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_MODE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_GUARD] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_PROTECT] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_FAST_LEAVE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_LEARNING] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_PROXYARP] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_ROOT_ID] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_BRIDGE_ID] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_DESIGNATED_PORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_DESIGNATED_COST] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_ID] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_NO] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_TOPOLOGY_CHANGE_ACK] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_CONFIG_PENDING] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_MESSAGE_AGE_TIMER] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BRPORT_FORWARD_DELAY_TIMER] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BRPORT_HOLD_TIMER] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BRPORT_FLUSH] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_PAD] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_MCAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_VLAN_TUNNEL] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_BCAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_ISOLATED] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_BACKUP_PORT] = { .type = NETLINK_TYPE_U32 },
};
static const NLTypeSystem rtnl_prot_info_type_systems[] = {
@@ -629,6 +665,10 @@ static const NLType rtnl_routing_policy_rule_types[] = {
[FRA_PAD] = { .type = NETLINK_TYPE_U32 },
[FRA_L3MDEV] = { .type = NETLINK_TYPE_U64 },
[FRA_UID_RANGE] = { .size = sizeof(struct fib_rule_uid_range) },
+ [FRA_PROTOCOL] = { .type = NETLINK_TYPE_U8 },
+ [FRA_IP_PROTO] = { .type = NETLINK_TYPE_U8 },
+ [FRA_SPORT_RANGE] = { .size = sizeof(struct fib_rule_port_range) },
+ [FRA_DPORT_RANGE] = { .size = sizeof(struct fib_rule_port_range) },
};
static const NLTypeSystem rtnl_routing_policy_rule_type_system = {
@@ -680,7 +720,7 @@ static const NLType genl_wireguard_peer_types[] = {
[WGPEER_A_PUBLIC_KEY] = { .size = WG_KEY_LEN },
[WGPEER_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
[WGPEER_A_PRESHARED_KEY] = { .size = WG_KEY_LEN },
- [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NETLINK_TYPE_U32 },
+ [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NETLINK_TYPE_U16 },
[WGPEER_A_ENDPOINT] = { /* either size of sockaddr_in or sockaddr_in6 depending on address family */ },
[WGPEER_A_ALLOWEDIPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_allowedip_type_system },
};
@@ -733,9 +773,34 @@ static const NLTypeSystem genl_ctrl_id_ctrl_type_system = {
.types = genl_ctrl_id_ctrl_cmds,
};
+static const NLType genl_fou_types[] = {
+ [FOU_ATTR_PORT] = { .type = NETLINK_TYPE_U16 },
+ [FOU_ATTR_AF] = { .type = NETLINK_TYPE_U8 },
+ [FOU_ATTR_IPPROTO] = { .type = NETLINK_TYPE_U8 },
+ [FOU_ATTR_TYPE] = { .type = NETLINK_TYPE_U8 },
+ [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
+};
+
+static const NLTypeSystem genl_fou_type_system = {
+ .count = ELEMENTSOF(genl_fou_types),
+ .types = genl_fou_types,
+};
+
+static const NLType genl_fou_cmds[] = {
+ [FOU_CMD_ADD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
+ [FOU_CMD_DEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
+ [FOU_CMD_GET] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
+};
+
+static const NLTypeSystem genl_fou_cmds_type_system = {
+ .count = ELEMENTSOF(genl_fou_cmds),
+ .types = genl_fou_cmds,
+};
+
static const NLType genl_families[] = {
- [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
+ [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
[SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system },
+ [SD_GENL_FOU] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_cmds_type_system},
};
const NLTypeSystem genl_family_type_system_root = {
diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h
index d411ffa6c2..3133e4863d 100644
--- a/src/libsystemd/sd-netlink/netlink-types.h
+++ b/src/libsystemd/sd-netlink/netlink-types.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "macro.h"
enum {
@@ -65,6 +64,7 @@ typedef enum NLUnionLinkInfoData {
NL_UNION_LINK_INFO_DATA_VXLAN,
NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL,
NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL,
+ NL_UNION_LINK_INFO_DATA_ERSPAN,
NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL,
NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL,
NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL,
diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h
index 7c35a2cfa7..d2723285a6 100644
--- a/src/libsystemd/sd-netlink/netlink-util.h
+++ b/src/libsystemd/sd-netlink/netlink-util.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-netlink.h"
#include "util.h"
@@ -39,3 +38,23 @@ int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias,
int rtnl_log_parse_error(int r);
int rtnl_log_create_error(int r);
+
+#define netlink_call_async(nl, ret_slot, message, callback, destroy_callback, userdata) \
+ ({ \
+ int (*_callback_)(sd_netlink *, sd_netlink_message *, typeof(userdata)) = callback; \
+ void (*_destroy_)(typeof(userdata)) = destroy_callback; \
+ sd_netlink_call_async(nl, ret_slot, message, \
+ (sd_netlink_message_handler_t) _callback_, \
+ (sd_netlink_destroy_t) _destroy_, \
+ userdata, 0, __func__); \
+ })
+
+#define netlink_add_match(nl, ret_slot, metch, callback, destroy_callback, userdata) \
+ ({ \
+ int (*_callback_)(sd_netlink *, sd_netlink_message *, typeof(userdata)) = callback; \
+ void (*_destroy_)(typeof(userdata)) = destroy_callback; \
+ sd_netlink_add_match(nl, ret_slot, match, \
+ (sd_netlink_message_handler_t) _callback_, \
+ (sd_netlink_destroy_t) _destroy_, \
+ userdata, __func__); \
+ })
diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c
index 4416e1720c..2d4d00e0eb 100644
--- a/src/libsystemd/sd-netlink/rtnl-message.c
+++ b/src/libsystemd/sd-netlink/rtnl-message.c
@@ -852,6 +852,32 @@ int sd_rtnl_message_routing_policy_rule_get_table(sd_netlink_message *m, unsigne
return 0;
}
+int sd_rtnl_message_routing_policy_rule_set_flags(sd_netlink_message *m, unsigned flags) {
+ struct rtmsg *routing_policy_rule;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+ routing_policy_rule = NLMSG_DATA(m->hdr);
+ routing_policy_rule->rtm_flags |= flags;
+
+ return 0;
+}
+
+int sd_rtnl_message_routing_policy_rule_get_flags(sd_netlink_message *m, unsigned *flags) {
+ struct rtmsg *routing_policy_rule;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
+
+ routing_policy_rule = NLMSG_DATA(m->hdr);
+ *flags = routing_policy_rule->rtm_flags;
+
+ return 0;
+}
+
int sd_rtnl_message_routing_policy_rule_set_rtm_type(sd_netlink_message *m, unsigned char type) {
struct rtmsg *routing_policy_rule;
diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c
index a177f220ab..d83952d0cc 100644
--- a/src/libsystemd/sd-netlink/sd-netlink.c
+++ b/src/libsystemd/sd-netlink/sd-netlink.c
@@ -11,9 +11,11 @@
#include "macro.h"
#include "missing.h"
#include "netlink-internal.h"
+#include "netlink-slot.h"
#include "netlink-util.h"
#include "process-util.h"
#include "socket-util.h"
+#include "string-util.h"
#include "util.h"
static int sd_netlink_new(sd_netlink **ret) {
@@ -21,17 +23,23 @@ static int sd_netlink_new(sd_netlink **ret) {
assert_return(ret, -EINVAL);
- rtnl = new0(sd_netlink, 1);
+ rtnl = new(sd_netlink, 1);
if (!rtnl)
return -ENOMEM;
- rtnl->n_ref = REFCNT_INIT;
- rtnl->fd = -1;
- rtnl->sockaddr.nl.nl_family = AF_NETLINK;
- rtnl->original_pid = getpid_cached();
- rtnl->protocol = -1;
+ *rtnl = (sd_netlink) {
+ .n_ref = REFCNT_INIT,
+ .fd = -1,
+ .sockaddr.nl.nl_family = AF_NETLINK,
+ .original_pid = getpid_cached(),
+ .protocol = -1,
- LIST_HEAD_INIT(rtnl->match_callbacks);
+ /* Change notification responses have sequence 0, so we must
+ * start our request sequence numbers at 1, or we may confuse our
+ * responses with notifications from the kernel */
+ .serial = 1,
+
+ };
/* We guarantee that the read buffer has at least space for
* a message header */
@@ -39,11 +47,6 @@ static int sd_netlink_new(sd_netlink **ret) {
sizeof(struct nlmsghdr), sizeof(uint8_t)))
return -ENOMEM;
- /* Change notification responses have sequence 0, so we must
- * start our request sequence numbers at 1, or we may confuse our
- * responses with notifications from the kernel */
- rtnl->serial = 1;
-
*ret = TAKE_PTR(rtnl);
return 0;
@@ -146,56 +149,41 @@ int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) {
return fd_inc_rcvbuf(rtnl->fd, size);
}
-sd_netlink *sd_netlink_ref(sd_netlink *rtnl) {
- assert_return(rtnl, NULL);
- assert_return(!rtnl_pid_changed(rtnl), NULL);
-
- if (rtnl)
- assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
-
- return rtnl;
-}
-
-sd_netlink *sd_netlink_unref(sd_netlink *rtnl) {
- if (!rtnl)
- return NULL;
-
- assert_return(!rtnl_pid_changed(rtnl), NULL);
-
- if (REFCNT_DEC(rtnl->n_ref) == 0) {
- struct match_callback *f;
- unsigned i;
+static sd_netlink *netlink_free(sd_netlink *rtnl) {
+ sd_netlink_slot *s;
+ unsigned i;
- for (i = 0; i < rtnl->rqueue_size; i++)
- sd_netlink_message_unref(rtnl->rqueue[i]);
- free(rtnl->rqueue);
-
- for (i = 0; i < rtnl->rqueue_partial_size; i++)
- sd_netlink_message_unref(rtnl->rqueue_partial[i]);
- free(rtnl->rqueue_partial);
+ assert(rtnl);
- free(rtnl->rbuffer);
+ for (i = 0; i < rtnl->rqueue_size; i++)
+ sd_netlink_message_unref(rtnl->rqueue[i]);
+ free(rtnl->rqueue);
- hashmap_free_free(rtnl->reply_callbacks);
- prioq_free(rtnl->reply_callbacks_prioq);
+ for (i = 0; i < rtnl->rqueue_partial_size; i++)
+ sd_netlink_message_unref(rtnl->rqueue_partial[i]);
+ free(rtnl->rqueue_partial);
- sd_event_source_unref(rtnl->io_event_source);
- sd_event_source_unref(rtnl->time_event_source);
- sd_event_unref(rtnl->event);
+ free(rtnl->rbuffer);
- while ((f = rtnl->match_callbacks)) {
- sd_netlink_remove_match(rtnl, f->type, f->callback, f->userdata);
- }
+ while ((s = rtnl->slots)) {
+ assert(s->floating);
+ netlink_slot_disconnect(s, true);
+ }
+ hashmap_free(rtnl->reply_callbacks);
+ prioq_free(rtnl->reply_callbacks_prioq);
- hashmap_free(rtnl->broadcast_group_refs);
+ sd_event_source_unref(rtnl->io_event_source);
+ sd_event_source_unref(rtnl->time_event_source);
+ sd_event_unref(rtnl->event);
- safe_close(rtnl->fd);
- free(rtnl);
- }
+ hashmap_free(rtnl->broadcast_group_refs);
- return NULL;
+ safe_close(rtnl->fd);
+ return mfree(rtnl);
}
+DEFINE_ATOMIC_REF_UNREF_FUNC(sd_netlink, sd_netlink, netlink_free);
+
static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) {
assert(rtnl);
assert(!rtnl_pid_changed(rtnl));
@@ -212,8 +200,8 @@ static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) {
}
int sd_netlink_send(sd_netlink *nl,
- sd_netlink_message *message,
- uint32_t *serial) {
+ sd_netlink_message *message,
+ uint32_t *serial) {
int r;
assert_return(nl, -EINVAL);
@@ -236,10 +224,10 @@ int sd_netlink_send(sd_netlink *nl,
int rtnl_rqueue_make_room(sd_netlink *rtnl) {
assert(rtnl);
- if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX) {
- log_debug("rtnl: exhausted the read queue size (%d)", RTNL_RQUEUE_MAX);
- return -ENOBUFS;
- }
+ if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
+ "rtnl: exhausted the read queue size (%d)",
+ RTNL_RQUEUE_MAX);
if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1))
return -ENOMEM;
@@ -250,10 +238,10 @@ int rtnl_rqueue_make_room(sd_netlink *rtnl) {
int rtnl_rqueue_partial_make_room(sd_netlink *rtnl) {
assert(rtnl);
- if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX) {
- log_debug("rtnl: exhausted the partial read queue size (%d)", RTNL_RQUEUE_MAX);
- return -ENOBUFS;
- }
+ if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
+ "rtnl: exhausted the partial read queue size (%d)",
+ RTNL_RQUEUE_MAX);
if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated,
rtnl->rqueue_partial_size + 1))
@@ -290,6 +278,7 @@ static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) {
static int process_timeout(sd_netlink *rtnl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
struct reply_callback *c;
+ sd_netlink_slot *slot;
usec_t n;
int r;
@@ -308,19 +297,27 @@ static int process_timeout(sd_netlink *rtnl) {
return r;
assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
+ c->timeout = 0;
hashmap_remove(rtnl->reply_callbacks, &c->serial);
- r = c->callback(rtnl, m, c->userdata);
+ slot = container_of(c, sd_netlink_slot, reply_callback);
+
+ r = c->callback(rtnl, m, slot->userdata);
if (r < 0)
- log_debug_errno(r, "sd-netlink: timedout callback failed: %m");
+ log_debug_errno(r, "sd-netlink: timedout callback %s%s%sfailed: %m",
+ slot->description ? "'" : "",
+ strempty(slot->description),
+ slot->description ? "' " : "");
- free(c);
+ if (slot->floating)
+ netlink_slot_disconnect(slot, true);
return 1;
}
static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
- _cleanup_free_ struct reply_callback *c = NULL;
+ struct reply_callback *c;
+ sd_netlink_slot *slot;
uint64_t serial;
uint16_t type;
int r;
@@ -333,25 +330,36 @@ static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
if (!c)
return 0;
- if (c->timeout != 0)
+ if (c->timeout != 0) {
prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
+ c->timeout = 0;
+ }
r = sd_netlink_message_get_type(m, &type);
if (r < 0)
- return 0;
+ return r;
if (type == NLMSG_DONE)
m = NULL;
- r = c->callback(rtnl, m, c->userdata);
+ slot = container_of(c, sd_netlink_slot, reply_callback);
+
+ r = c->callback(rtnl, m, slot->userdata);
if (r < 0)
- log_debug_errno(r, "sd-netlink: callback failed: %m");
+ log_debug_errno(r, "sd-netlink: reply callback %s%s%sfailed: %m",
+ slot->description ? "'" : "",
+ strempty(slot->description),
+ slot->description ? "' " : "");
+
+ if (slot->floating)
+ netlink_slot_disconnect(slot, true);
return 1;
}
static int process_match(sd_netlink *rtnl, sd_netlink_message *m) {
struct match_callback *c;
+ sd_netlink_slot *slot;
uint16_t type;
int r;
@@ -364,10 +372,15 @@ static int process_match(sd_netlink *rtnl, sd_netlink_message *m) {
LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
if (type == c->type) {
- r = c->callback(rtnl, m, c->userdata);
+ slot = container_of(c, sd_netlink_slot, match_callback);
+
+ r = c->callback(rtnl, m, slot->userdata);
if (r != 0) {
if (r < 0)
- log_debug_errno(r, "sd-netlink: match callback failed: %m");
+ log_debug_errno(r, "sd-netlink: match callback %s%s%sfailed: %m",
+ slot->description ? "'" : "",
+ strempty(slot->description),
+ slot->description ? "' " : "");
break;
}
@@ -506,22 +519,19 @@ static int timeout_compare(const void *a, const void *b) {
if (x->timeout == 0 && y->timeout != 0)
return 1;
- if (x->timeout < y->timeout)
- return -1;
-
- if (x->timeout > y->timeout)
- return 1;
-
- return 0;
+ return CMP(x->timeout, y->timeout);
}
-int sd_netlink_call_async(sd_netlink *nl,
- sd_netlink_message *m,
- sd_netlink_message_handler_t callback,
- void *userdata,
- uint64_t usec,
- uint32_t *serial) {
- struct reply_callback *c;
+int sd_netlink_call_async(
+ sd_netlink *nl,
+ sd_netlink_slot **ret_slot,
+ sd_netlink_message *m,
+ sd_netlink_message_handler_t callback,
+ sd_netlink_destroy_t destroy_callback,
+ void *userdata,
+ uint64_t usec,
+ const char *description) {
+ _cleanup_free_ sd_netlink_slot *slot = NULL;
uint32_t s;
int r, k;
@@ -540,60 +550,40 @@ int sd_netlink_call_async(sd_netlink *nl,
return r;
}
- c = new0(struct reply_callback, 1);
- if (!c)
- return -ENOMEM;
+ r = netlink_slot_allocate(nl, !ret_slot, NETLINK_REPLY_CALLBACK, sizeof(struct reply_callback), userdata, description, &slot);
+ if (r < 0)
+ return r;
- c->callback = callback;
- c->userdata = userdata;
- c->timeout = calc_elapse(usec);
+ slot->reply_callback.callback = callback;
+ slot->reply_callback.timeout = calc_elapse(usec);
k = sd_netlink_send(nl, m, &s);
- if (k < 0) {
- free(c);
+ if (k < 0)
return k;
- }
- c->serial = s;
+ slot->reply_callback.serial = s;
- r = hashmap_put(nl->reply_callbacks, &c->serial, c);
- if (r < 0) {
- free(c);
+ r = hashmap_put(nl->reply_callbacks, &slot->reply_callback.serial, &slot->reply_callback);
+ if (r < 0)
return r;
- }
- if (c->timeout != 0) {
- r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx);
- if (r > 0) {
- c->timeout = 0;
- sd_netlink_call_async_cancel(nl, c->serial);
+ if (slot->reply_callback.timeout != 0) {
+ r = prioq_put(nl->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx);
+ if (r < 0) {
+ (void) hashmap_remove(nl->reply_callbacks, &slot->reply_callback.serial);
return r;
}
}
- if (serial)
- *serial = s;
+ /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
+ slot->destroy_callback = destroy_callback;
- return k;
-}
+ if (ret_slot)
+ *ret_slot = slot;
-int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial) {
- struct reply_callback *c;
- uint64_t s = serial;
+ TAKE_PTR(slot);
- assert_return(nl, -EINVAL);
- assert_return(serial != 0, -EINVAL);
- assert_return(!rtnl_pid_changed(nl), -ECHILD);
-
- c = hashmap_remove(nl->reply_callbacks, &s);
- if (!c)
- return 0;
-
- if (c->timeout != 0)
- prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
-
- free(c);
- return 1;
+ return k;
}
int sd_netlink_call(sd_netlink *rtnl,
@@ -838,24 +828,27 @@ int sd_netlink_detach_event(sd_netlink *rtnl) {
return 0;
}
-int sd_netlink_add_match(sd_netlink *rtnl,
- uint16_t type,
- sd_netlink_message_handler_t callback,
- void *userdata) {
- _cleanup_free_ struct match_callback *c = NULL;
+int sd_netlink_add_match(
+ sd_netlink *rtnl,
+ sd_netlink_slot **ret_slot,
+ uint16_t type,
+ sd_netlink_message_handler_t callback,
+ sd_netlink_destroy_t destroy_callback,
+ void *userdata,
+ const char *description) {
+ _cleanup_free_ sd_netlink_slot *slot = NULL;
int r;
assert_return(rtnl, -EINVAL);
assert_return(callback, -EINVAL);
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
- c = new0(struct match_callback, 1);
- if (!c)
- return -ENOMEM;
+ r = netlink_slot_allocate(rtnl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback), userdata, description, &slot);
+ if (r < 0)
+ return r;
- c->callback = callback;
- c->type = type;
- c->userdata = userdata;
+ slot->match_callback.callback = callback;
+ slot->match_callback.type = type;
switch (type) {
case RTM_NEWLINK:
@@ -900,64 +893,15 @@ int sd_netlink_add_match(sd_netlink *rtnl,
return -EOPNOTSUPP;
}
- LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c);
-
- c = NULL;
-
- return 0;
-}
-
-int sd_netlink_remove_match(sd_netlink *rtnl,
- uint16_t type,
- sd_netlink_message_handler_t callback,
- void *userdata) {
- struct match_callback *c;
- int r;
+ LIST_PREPEND(match_callbacks, rtnl->match_callbacks, &slot->match_callback);
- assert_return(rtnl, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+ /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
+ slot->destroy_callback = destroy_callback;
- LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks)
- if (c->callback == callback && c->type == type && c->userdata == userdata) {
- LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c);
- free(c);
-
- switch (type) {
- case RTM_NEWLINK:
- case RTM_DELLINK:
- r = socket_broadcast_group_unref(rtnl, RTNLGRP_LINK);
- if (r < 0)
- return r;
-
- break;
- case RTM_NEWADDR:
- case RTM_DELADDR:
- r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_IFADDR);
- if (r < 0)
- return r;
-
- r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_IFADDR);
- if (r < 0)
- return r;
-
- break;
- case RTM_NEWROUTE:
- case RTM_DELROUTE:
- r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_ROUTE);
- if (r < 0)
- return r;
-
- r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_ROUTE);
- if (r < 0)
- return r;
- break;
- default:
- return -EOPNOTSUPP;
- }
+ if (ret_slot)
+ *ret_slot = slot;
- return 1;
- }
+ TAKE_PTR(slot);
return 0;
}
diff --git a/src/libsystemd/sd-netlink/test-local-addresses.c b/src/libsystemd/sd-netlink/test-local-addresses.c
index 921ce289d4..17114265d7 100644
--- a/src/libsystemd/sd-netlink/test-local-addresses.c
+++ b/src/libsystemd/sd-netlink/test-local-addresses.c
@@ -1,11 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "af-list.h"
#include "alloc-util.h"
#include "in-addr-util.h"
#include "local-addresses.h"
+#include "tests.h"
static void print_local_addresses(struct local_address *a, unsigned n) {
unsigned i;
@@ -22,9 +21,7 @@ int main(int argc, char *argv[]) {
struct local_address *a;
int n;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
a = NULL;
n = local_addresses(NULL, 0, AF_UNSPEC, &a);
diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c
index 03773fb936..b13fa224cc 100644
--- a/src/libsystemd/sd-netlink/test-netlink.c
+++ b/src/libsystemd/sd-netlink/test-netlink.c
@@ -5,6 +5,7 @@
#include "sd-netlink.h"
+#include "alloc-util.h"
#include "ether-addr-util.h"
#include "macro.h"
#include "missing.h"
@@ -177,8 +178,9 @@ static int link_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata)
assert_se(rtnl);
assert_se(m);
+ assert_se(userdata);
- log_info("got link info about %s", ifname);
+ log_info("%s: got link info about %s", __func__, ifname);
free(ifname);
assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &data) >= 0);
@@ -199,7 +201,7 @@ static void test_event_loop(int ifindex) {
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
- assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, NULL) >= 0);
+ assert_se(sd_netlink_call_async(rtnl, NULL, m, link_handler, NULL, ifname, 0, NULL) >= 0);
assert_se(sd_event_default(&event) >= 0);
@@ -212,25 +214,46 @@ static void test_event_loop(int ifindex) {
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
}
-static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- int *counter = userdata;
- int r;
+static void test_async_destroy(void *userdata) {
+}
- (*counter)--;
+static void test_async(int ifindex) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL;
+ _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL;
+ sd_netlink_destroy_t destroy_callback;
+ const char *description;
+ char *ifname;
- r = sd_netlink_message_get_errno(m);
+ ifname = strdup("lo");
+ assert_se(ifname);
- log_info_errno(r, "%d left in pipe. got reply: %m", *counter);
+ assert_se(sd_netlink_open(&rtnl) >= 0);
- assert_se(r >= 0);
+ assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
- return 1;
+ assert_se(sd_netlink_call_async(rtnl, &slot, m, link_handler, test_async_destroy, ifname, 0, "hogehoge") >= 0);
+
+ assert_se(sd_netlink_slot_get_netlink(slot) == rtnl);
+ assert_se(sd_netlink_slot_get_userdata(slot) == ifname);
+ assert_se(sd_netlink_slot_get_destroy_callback(slot, &destroy_callback) == 1);
+ assert_se(destroy_callback == test_async_destroy);
+ assert_se(sd_netlink_slot_get_floating(slot) == 0);
+ assert_se(sd_netlink_slot_get_description(slot, &description) == 1);
+ assert_se(streq(description, "hogehoge"));
+
+ assert_se(sd_netlink_wait(rtnl, 0) >= 0);
+ assert_se(sd_netlink_process(rtnl, &r) >= 0);
+
+ assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
}
-static void test_async(int ifindex) {
+static void test_slot_set(int ifindex) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL;
- uint32_t serial;
+ _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL;
+ sd_netlink_destroy_t destroy_callback;
+ const char *description;
char *ifname;
ifname = strdup("lo");
@@ -240,7 +263,23 @@ static void test_async(int ifindex) {
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
- assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, &serial) >= 0);
+ assert_se(sd_netlink_call_async(rtnl, &slot, m, link_handler, NULL, NULL, 0, NULL) >= 0);
+
+ assert_se(sd_netlink_slot_get_netlink(slot) == rtnl);
+ assert_se(!sd_netlink_slot_get_userdata(slot));
+ assert_se(!sd_netlink_slot_set_userdata(slot, ifname));
+ assert_se(sd_netlink_slot_get_userdata(slot) == ifname);
+ assert_se(sd_netlink_slot_get_destroy_callback(slot, NULL) == 0);
+ assert_se(sd_netlink_slot_set_destroy_callback(slot, test_async_destroy) >= 0);
+ assert_se(sd_netlink_slot_get_destroy_callback(slot, &destroy_callback) == 1);
+ assert_se(destroy_callback == test_async_destroy);
+ assert_se(sd_netlink_slot_get_floating(slot) == 0);
+ assert_se(sd_netlink_slot_set_floating(slot, 1) == 1);
+ assert_se(sd_netlink_slot_get_floating(slot) == 1);
+ assert_se(sd_netlink_slot_get_description(slot, NULL) == 0);
+ assert_se(sd_netlink_slot_set_description(slot, "hogehoge") >= 0);
+ assert_se(sd_netlink_slot_get_description(slot, &description) == 1);
+ assert_se(streq(description, "hogehoge"));
assert_se(sd_netlink_wait(rtnl, 0) >= 0);
assert_se(sd_netlink_process(rtnl, &r) >= 0);
@@ -248,6 +287,116 @@ static void test_async(int ifindex) {
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
}
+struct test_async_object {
+ unsigned n_ref;
+ char *ifname;
+};
+
+static struct test_async_object *test_async_object_free(struct test_async_object *t) {
+ assert(t);
+
+ free(t->ifname);
+ return mfree(t);
+}
+
+DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(struct test_async_object, test_async_object, test_async_object_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct test_async_object *, test_async_object_unref);
+
+static int link_handler2(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+ struct test_async_object *t = userdata;
+ const char *data;
+
+ assert_se(rtnl);
+ assert_se(m);
+ assert_se(userdata);
+
+ log_info("%s: got link info about %s", __func__, t->ifname);
+
+ assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &data) >= 0);
+ assert_se(streq(data, "lo"));
+
+ return 1;
+}
+
+static void test_async_object_destroy(void *userdata) {
+ struct test_async_object *t = userdata;
+
+ assert(userdata);
+
+ log_info("%s: n_ref=%u", __func__, t->n_ref);
+ test_async_object_unref(t);
+}
+
+static void test_async_destroy_callback(int ifindex) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL;
+ _cleanup_(test_async_object_unrefp) struct test_async_object *t = NULL;
+ _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL;
+ char *ifname;
+
+ assert_se(t = new(struct test_async_object, 1));
+ assert_se(ifname = strdup("lo"));
+ *t = (struct test_async_object) {
+ .n_ref = 1,
+ .ifname = ifname,
+ };
+
+ assert_se(sd_netlink_open(&rtnl) >= 0);
+
+ /* destroy callback is called after processing message */
+ assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
+ assert_se(sd_netlink_call_async(rtnl, NULL, m, link_handler2, test_async_object_destroy, t, 0, NULL) >= 0);
+
+ assert_se(t->n_ref == 1);
+ assert_se(test_async_object_ref(t));
+ assert_se(t->n_ref == 2);
+
+ assert_se(sd_netlink_wait(rtnl, 0) >= 0);
+ assert_se(sd_netlink_process(rtnl, &r) == 1);
+ assert_se(t->n_ref == 1);
+
+ assert_se(!sd_netlink_message_unref(m));
+
+ /* destroy callback is called when asynchronous call is cancelled, that is, slot is freed. */
+ assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
+ assert_se(sd_netlink_call_async(rtnl, &slot, m, link_handler2, test_async_object_destroy, t, 0, NULL) >= 0);
+
+ assert_se(t->n_ref == 1);
+ assert_se(test_async_object_ref(t));
+ assert_se(t->n_ref == 2);
+
+ assert_se(!(slot = sd_netlink_slot_unref(slot)));
+ assert_se(t->n_ref == 1);
+
+ assert_se(!sd_netlink_message_unref(m));
+
+ /* destroy callback is also called by sd_netlink_unref() */
+ assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
+ assert_se(sd_netlink_call_async(rtnl, NULL, m, link_handler2, test_async_object_destroy, t, 0, NULL) >= 0);
+
+ assert_se(t->n_ref == 1);
+ assert_se(test_async_object_ref(t));
+ assert_se(t->n_ref == 2);
+
+ assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
+ assert_se(t->n_ref == 1);
+}
+
+static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+ int *counter = userdata;
+ int r;
+
+ (*counter)--;
+
+ r = sd_netlink_message_get_errno(m);
+
+ log_info_errno(r, "%d left in pipe. got reply: %m", *counter);
+
+ assert_se(r >= 0);
+
+ return 1;
+}
+
static void test_pipe(int ifindex) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m1 = NULL, *m2 = NULL;
@@ -259,10 +408,10 @@ static void test_pipe(int ifindex) {
assert_se(sd_rtnl_message_new_link(rtnl, &m2, RTM_GETLINK, ifindex) >= 0);
counter++;
- assert_se(sd_netlink_call_async(rtnl, m1, pipe_handler, &counter, 0, NULL) >= 0);
+ assert_se(sd_netlink_call_async(rtnl, NULL, m1, pipe_handler, NULL, &counter, 0, NULL) >= 0);
counter++;
- assert_se(sd_netlink_call_async(rtnl, m2, pipe_handler, &counter, 0, NULL) >= 0);
+ assert_se(sd_netlink_call_async(rtnl, NULL, m2, pipe_handler, NULL, &counter, 0, NULL) >= 0);
while (counter > 0) {
assert_se(sd_netlink_wait(rtnl, 0) >= 0);
@@ -308,16 +457,17 @@ static void test_container(sd_netlink *rtnl) {
}
static void test_match(void) {
+ _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *s1 = NULL, *s2 = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
assert_se(sd_netlink_open(&rtnl) >= 0);
- assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0);
- assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0);
+ assert_se(sd_netlink_add_match(rtnl, &s1, RTM_NEWLINK, link_handler, NULL, NULL, NULL) >= 0);
+ assert_se(sd_netlink_add_match(rtnl, &s2, RTM_NEWLINK, link_handler, NULL, NULL, NULL) >= 0);
+ assert_se(sd_netlink_add_match(rtnl, NULL, RTM_NEWLINK, link_handler, NULL, NULL, NULL) >= 0);
- assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1);
- assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1);
- assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 0);
+ assert_se(!(s1 = sd_netlink_slot_unref(s1)));
+ assert_se(!(s2 = sd_netlink_slot_unref(s2)));
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
}
@@ -366,31 +516,26 @@ int main(void) {
uint16_t type;
test_match();
-
test_multiple();
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(rtnl);
test_route(rtnl);
-
test_message(rtnl);
-
test_container(rtnl);
if_loopback = (int) if_nametoindex("lo");
assert_se(if_loopback > 0);
test_async(if_loopback);
-
+ test_slot_set(if_loopback);
+ test_async_destroy_callback(if_loopback);
test_pipe(if_loopback);
-
test_event_loop(if_loopback);
-
test_link_configure(rtnl, if_loopback);
test_get_addresses(rtnl);
-
test_message_link_bridge(rtnl);
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, if_loopback) >= 0);
diff --git a/src/libsystemd/sd-network/network-util.c b/src/libsystemd/sd-network/network-util.c
index 256d4a27a5..df5ce86842 100644
--- a/src/libsystemd/sd-network/network-util.c
+++ b/src/libsystemd/sd-network/network-util.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "alloc-util.h"
#include "fd-util.h"
diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h
index 62260d09dd..e868bc2e27 100644
--- a/src/libsystemd/sd-network/network-util.h
+++ b/src/libsystemd/sd-network/network-util.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-network.h"
bool network_is_online(void);
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index 3b8ce935b0..d4b5e248cc 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -8,8 +8,8 @@
#include "sd-network.h"
#include "alloc-util.h"
+#include "env-file.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "macro.h"
#include "parse-util.h"
@@ -24,7 +24,7 @@ _public_ int sd_network_get_operational_state(char **state) {
assert_return(state, -EINVAL);
- r = parse_env_file(NULL, "/run/systemd/netif/state", NEWLINE, "OPER_STATE", &s, NULL);
+ r = parse_env_file(NULL, "/run/systemd/netif/state", "OPER_STATE", &s);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -44,7 +44,7 @@ static int network_get_strv(const char *key, char ***ret) {
assert_return(ret, -EINVAL);
- r = parse_env_file(NULL, "/run/systemd/netif/state", NEWLINE, key, &s, NULL);
+ r = parse_env_file(NULL, "/run/systemd/netif/state", key, &s);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -92,7 +92,7 @@ static int network_link_get_string(int ifindex, const char *field, char **ret) {
xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
- r = parse_env_file(NULL, path, NEWLINE, field, &s, NULL);
+ r = parse_env_file(NULL, path, field, &s);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -115,7 +115,7 @@ static int network_link_get_strv(int ifindex, const char *key, char ***ret) {
assert_return(ret, -EINVAL);
xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
- r = parse_env_file(NULL, path, NEWLINE, key, &s, NULL);
+ r = parse_env_file(NULL, path, key, &s);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -204,6 +204,25 @@ _public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret);
}
+_public_ int sd_network_link_get_dns_default_route(int ifindex) {
+ char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ assert_return(ifindex > 0, -EINVAL);
+
+ xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
+
+ r = parse_env_file(NULL, path, "DNS_DEFAULT_ROUTE", &s);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+ if (isempty(s))
+ return -ENODATA;
+ return parse_boolean(s);
+}
+
static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
_cleanup_free_ int *ifis = NULL;
@@ -216,7 +235,7 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
assert_return(ret, -EINVAL);
xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
- r = parse_env_file(NULL, path, NEWLINE, key, &s, NULL);
+ r = parse_env_file(NULL, path, key, &s);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c
index 2845f91e1b..9949c23802 100644
--- a/src/libsystemd/sd-path/sd-path.c
+++ b/src/libsystemd/sd-path/sd-path.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "sd-path.h"
@@ -77,7 +75,6 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) {
_cleanup_free_ char *b = NULL;
_cleanup_free_ const char *fn = NULL;
const char *c = NULL;
- char line[LINE_MAX];
size_t n;
int r;
@@ -105,9 +102,16 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) {
* xdg-user-dirs does upstream */
n = strlen(field);
- FOREACH_LINE(line, f, return -errno) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
char *l, *p, *e;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
l = strstrip(line);
if (!strneq(l, field, n))
diff --git a/src/libsystemd/sd-resolve/resolve-private.h b/src/libsystemd/sd-resolve/resolve-private.h
new file mode 100644
index 0000000000..a0feb36f7c
--- /dev/null
+++ b/src/libsystemd/sd-resolve/resolve-private.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-resolve.h"
+
+int resolve_getaddrinfo_with_destroy_callback(
+ sd_resolve *resolve, sd_resolve_query **q,
+ const char *node, const char *service, const struct addrinfo *hints,
+ sd_resolve_getaddrinfo_handler_t callback,
+ sd_resolve_destroy_t destroy_callback, void *userdata);
+int resolve_getnameinfo_with_destroy_callback(
+ sd_resolve *resolve, sd_resolve_query **q,
+ const struct sockaddr *sa, socklen_t salen, int flags, uint64_t get,
+ sd_resolve_getnameinfo_handler_t callback,
+ sd_resolve_destroy_t destroy_callback, void *userdata);
+
+#define resolve_getaddrinfo(resolve, ret_query, node, service, hints, callback, destroy_callback, userdata) \
+ ({ \
+ int (*_callback_)(sd_resolve_query*, int, const struct addrinfo*, typeof(userdata)) = callback; \
+ void (*_destroy_)(typeof(userdata)) = destroy_callback; \
+ resolve_getaddrinfo_with_destroy_callback( \
+ resolve, ret_query, \
+ node, service, hints, \
+ (sd_resolve_getaddrinfo_handler_t) _callback_, \
+ (sd_resolve_destroy_t) _destroy_, \
+ userdata); \
+ })
+
+#define resolve_getnameinfo(resolve, ret_query, sa, salen, flags, get, callback, destroy_callback, userdata) \
+ ({ \
+ int (*_callback_)(sd_resolve_query*, int, const char*, const char*, typeof(userdata)) = callback; \
+ void (*_destroy_)(typeof(userdata)) = destroy_callback; \
+ resolve_getaddrinfo_with_destroy_callback( \
+ resolve, ret_query, \
+ sa, salen, flags, get, \
+ (sd_resolve_getnameinfo_handler_t) _callback_, \
+ (sd_resolve_destroy_t) _destroy_, \
+ userdata); \
+ })
diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c
index a189f140f8..21d783b8f0 100644
--- a/src/libsystemd/sd-resolve/sd-resolve.c
+++ b/src/libsystemd/sd-resolve/sd-resolve.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include <errno.h>
#include <poll.h>
@@ -22,6 +20,7 @@
#include "io-util.h"
#include "list.h"
#include "missing.h"
+#include "resolve-private.h"
#include "socket-util.h"
#include "util.h"
#include "process-util.h"
@@ -96,6 +95,7 @@ struct sd_resolve_query {
};
void *userdata;
+ sd_resolve_destroy_t destroy_callback;
LIST_FIELDS(sd_resolve_query, queries);
};
@@ -202,12 +202,14 @@ static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *leng
if (*length + l > maxlength)
return NULL;
- s.ai_flags = ai->ai_flags;
- s.ai_family = ai->ai_family;
- s.ai_socktype = ai->ai_socktype;
- s.ai_protocol = ai->ai_protocol;
- s.ai_addrlen = ai->ai_addrlen;
- s.canonname_len = cnl;
+ s = (AddrInfoSerialization) {
+ .ai_flags = ai->ai_flags,
+ .ai_family = ai->ai_family,
+ .ai_socktype = ai->ai_socktype,
+ .ai_protocol = ai->ai_protocol,
+ .ai_addrlen = ai->ai_addrlen,
+ .canonname_len = cnl,
+ };
memcpy((uint8_t*) p, &s, sizeof(AddrInfoSerialization));
memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen);
@@ -226,15 +228,7 @@ static int send_addrinfo_reply(
int _errno,
int _h_errno) {
- AddrInfoResponse resp = {
- .header.type = RESPONSE_ADDRINFO,
- .header.id = id,
- .header.length = sizeof(AddrInfoResponse),
- .ret = ret,
- ._errno = _errno,
- ._h_errno = _h_errno,
- };
-
+ AddrInfoResponse resp = {};
union {
AddrInfoSerialization ais;
uint8_t space[BUFSIZE];
@@ -244,6 +238,15 @@ static int send_addrinfo_reply(
assert(out_fd >= 0);
+ resp = (AddrInfoResponse) {
+ .header.type = RESPONSE_ADDRINFO,
+ .header.id = id,
+ .header.length = sizeof(AddrInfoResponse),
+ .ret = ret,
+ ._errno = _errno,
+ ._h_errno = _h_errno,
+ };
+
if (ret == 0 && ai) {
void *p = &buffer;
struct addrinfo *k;
@@ -260,10 +263,13 @@ static int send_addrinfo_reply(
if (ai)
freeaddrinfo(ai);
- iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(AddrInfoResponse) };
- iov[1] = (struct iovec) { .iov_base = &buffer, .iov_len = resp.header.length - sizeof(AddrInfoResponse) };
+ iov[0] = IOVEC_MAKE(&resp, sizeof(AddrInfoResponse));
+ iov[1] = IOVEC_MAKE(&buffer, resp.header.length - sizeof(AddrInfoResponse));
- mh = (struct msghdr) { .msg_iov = iov, .msg_iovlen = ELEMENTSOF(iov) };
+ mh = (struct msghdr) {
+ .msg_iov = iov,
+ .msg_iovlen = ELEMENTSOF(iov)
+ };
if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0)
return -errno;
@@ -280,14 +286,7 @@ static int send_nameinfo_reply(
int _errno,
int _h_errno) {
- NameInfoResponse resp = {
- .header.type = RESPONSE_NAMEINFO,
- .header.id = id,
- .ret = ret,
- ._errno = _errno,
- ._h_errno = _h_errno,
- };
-
+ NameInfoResponse resp = {};
struct iovec iov[3];
struct msghdr mh;
size_t hl, sl;
@@ -297,15 +296,25 @@ static int send_nameinfo_reply(
sl = serv ? strlen(serv)+1 : 0;
hl = host ? strlen(host)+1 : 0;
- resp.header.length = sizeof(NameInfoResponse) + hl + sl;
- resp.hostlen = hl;
- resp.servlen = sl;
+ resp = (NameInfoResponse) {
+ .header.type = RESPONSE_NAMEINFO,
+ .header.id = id,
+ .header.length = sizeof(NameInfoResponse) + hl + sl,
+ .hostlen = hl,
+ .servlen = sl,
+ .ret = ret,
+ ._errno = _errno,
+ ._h_errno = _h_errno,
+ };
- iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(NameInfoResponse) };
- iov[1] = (struct iovec) { .iov_base = (void*) host, .iov_len = hl };
- iov[2] = (struct iovec) { .iov_base = (void*) serv, .iov_len = sl };
+ iov[0] = IOVEC_MAKE(&resp, sizeof(NameInfoResponse));
+ iov[1] = IOVEC_MAKE((void*) host, hl);
+ iov[2] = IOVEC_MAKE((void*) serv, sl);
- mh = (struct msghdr) { .msg_iov = iov, .msg_iovlen = ELEMENTSOF(iov) };
+ mh = (struct msghdr) {
+ .msg_iov = iov,
+ .msg_iovlen = ELEMENTSOF(iov)
+ };
if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0)
return -errno;
@@ -424,8 +433,7 @@ static int start_threads(sd_resolve *resolve, unsigned extra) {
unsigned n;
int r, k;
- if (sigfillset(&ss) < 0)
- return -errno;
+ assert_se(sigfillset(&ss) >= 0);
/* No signals in forked off threads please. We set the mask before forking, so that the threads never exist
* with a different mask than a fully blocked one */
@@ -502,7 +510,6 @@ _public_ int sd_resolve_new(sd_resolve **ret) {
}
_public_ int sd_resolve_default(sd_resolve **ret) {
-
static thread_local sd_resolve *default_resolve = NULL;
sd_resolve *e = NULL;
int r;
@@ -543,7 +550,7 @@ _public_ int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid) {
return -ENXIO;
}
-static void resolve_free(sd_resolve *resolve) {
+static sd_resolve *resolve_free(sd_resolve *resolve) {
PROTECT_ERRNO;
sd_resolve_query *q;
unsigned i;
@@ -582,30 +589,11 @@ static void resolve_free(sd_resolve *resolve) {
/* Close all communication channels */
close_many(resolve->fds, _FD_MAX);
- free(resolve);
-}
-
-_public_ sd_resolve* sd_resolve_ref(sd_resolve *resolve) {
- assert_return(resolve, NULL);
-
- assert(resolve->n_ref >= 1);
- resolve->n_ref++;
- return resolve;
+ return mfree(resolve);
}
-_public_ sd_resolve* sd_resolve_unref(sd_resolve *resolve) {
- if (!resolve)
- return NULL;
-
- assert(resolve->n_ref >= 1);
- resolve->n_ref--;
-
- if (resolve->n_ref <= 0)
- resolve_free(resolve);
-
- return NULL;
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_resolve, sd_resolve, resolve_free);
_public_ int sd_resolve_get_fd(sd_resolve *resolve) {
assert_return(resolve, -EINVAL);
@@ -683,8 +671,8 @@ static int complete_query(sd_resolve *resolve, sd_resolve_query *q) {
static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo **ret_ai) {
AddrInfoSerialization s;
- size_t l;
struct addrinfo *ai;
+ size_t l;
assert(p);
assert(*p);
@@ -700,15 +688,17 @@ static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo
if (*length < l)
return -EBADMSG;
- ai = new0(struct addrinfo, 1);
+ ai = new(struct addrinfo, 1);
if (!ai)
return -ENOMEM;
- ai->ai_flags = s.ai_flags;
- ai->ai_family = s.ai_family;
- ai->ai_socktype = s.ai_socktype;
- ai->ai_protocol = s.ai_protocol;
- ai->ai_addrlen = s.ai_addrlen;
+ *ai = (struct addrinfo) {
+ .ai_flags = s.ai_flags,
+ .ai_family = s.ai_family,
+ .ai_socktype = s.ai_socktype,
+ .ai_protocol = s.ai_protocol,
+ .ai_addrlen = s.ai_addrlen,
+ };
if (s.ai_addrlen > 0) {
ai->ai_addr = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization), s.ai_addrlen);
@@ -922,26 +912,29 @@ static int alloc_query(sd_resolve *resolve, bool floating, sd_resolve_query **_q
return 0;
}
-_public_ int sd_resolve_getaddrinfo(
+
+int resolve_getaddrinfo_with_destroy_callback(
sd_resolve *resolve,
- sd_resolve_query **_q,
+ sd_resolve_query **ret_query,
const char *node, const char *service,
const struct addrinfo *hints,
- sd_resolve_getaddrinfo_handler_t callback, void *userdata) {
+ sd_resolve_getaddrinfo_handler_t callback,
+ sd_resolve_destroy_t destroy_callback,
+ void *userdata) {
_cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL;
- AddrInfoRequest req;
+ size_t node_len, service_len;
+ AddrInfoRequest req = {};
struct iovec iov[3];
struct msghdr mh = {};
int r;
- size_t node_len, service_len;
assert_return(resolve, -EINVAL);
assert_return(node || service, -EINVAL);
assert_return(callback, -EINVAL);
assert_return(!resolve_pid_changed(resolve), -ECHILD);
- r = alloc_query(resolve, !_q, &q);
+ r = alloc_query(resolve, !ret_query, &q);
if (r < 0)
return r;
@@ -967,25 +960,38 @@ _public_ int sd_resolve_getaddrinfo(
.ai_protocol = hints ? hints->ai_protocol : 0,
};
- iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(AddrInfoRequest) };
+ iov[mh.msg_iovlen++] = IOVEC_MAKE(&req, sizeof(AddrInfoRequest));
if (node)
- iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) node, .iov_len = req.node_len };
+ iov[mh.msg_iovlen++] = IOVEC_MAKE((void*) node, req.node_len);
if (service)
- iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) service, .iov_len = req.service_len };
+ iov[mh.msg_iovlen++] = IOVEC_MAKE((void*) service, req.service_len);
mh.msg_iov = iov;
if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0)
return -errno;
resolve->n_outstanding++;
+ q->destroy_callback = destroy_callback;
+
+ if (ret_query)
+ *ret_query = q;
- if (_q)
- *_q = q;
TAKE_PTR(q);
return 0;
}
+_public_ int sd_resolve_getaddrinfo(
+ sd_resolve *resolve,
+ sd_resolve_query **ret_query,
+ const char *node, const char *service,
+ const struct addrinfo *hints,
+ sd_resolve_getaddrinfo_handler_t callback,
+ void *userdata) {
+
+ return resolve_getaddrinfo_with_destroy_callback(resolve, ret_query, node, service, hints, callback, NULL, userdata);
+}
+
static int getaddrinfo_done(sd_resolve_query* q) {
assert(q);
assert(q->done);
@@ -997,17 +1003,18 @@ static int getaddrinfo_done(sd_resolve_query* q) {
return q->getaddrinfo_handler(q, q->ret, q->addrinfo, q->userdata);
}
-_public_ int sd_resolve_getnameinfo(
+int resolve_getnameinfo_with_destroy_callback(
sd_resolve *resolve,
- sd_resolve_query**_q,
+ sd_resolve_query **ret_query,
const struct sockaddr *sa, socklen_t salen,
int flags,
uint64_t get,
sd_resolve_getnameinfo_handler_t callback,
+ sd_resolve_destroy_t destroy_callback,
void *userdata) {
_cleanup_(sd_resolve_query_unrefp) sd_resolve_query *q = NULL;
- NameInfoRequest req;
+ NameInfoRequest req = {};
struct iovec iov[2];
struct msghdr mh;
int r;
@@ -1020,7 +1027,7 @@ _public_ int sd_resolve_getnameinfo(
assert_return(callback, -EINVAL);
assert_return(!resolve_pid_changed(resolve), -ECHILD);
- r = alloc_query(resolve, !_q, &q);
+ r = alloc_query(resolve, !ret_query, &q);
if (r < 0)
return r;
@@ -1039,23 +1046,40 @@ _public_ int sd_resolve_getnameinfo(
.getserv = !!(get & SD_RESOLVE_GET_SERVICE),
};
- iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(NameInfoRequest) };
- iov[1] = (struct iovec) { .iov_base = (void*) sa, .iov_len = salen };
+ iov[0] = IOVEC_MAKE(&req, sizeof(NameInfoRequest));
+ iov[1] = IOVEC_MAKE((void*) sa, salen);
- mh = (struct msghdr) { .msg_iov = iov, .msg_iovlen = ELEMENTSOF(iov) };
+ mh = (struct msghdr) {
+ .msg_iov = iov,
+ .msg_iovlen = ELEMENTSOF(iov)
+ };
if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0)
return -errno;
- if (_q)
- *_q = q;
-
resolve->n_outstanding++;
+ q->destroy_callback = destroy_callback;
+
+ if (ret_query)
+ *ret_query = q;
+
TAKE_PTR(q);
return 0;
}
+_public_ int sd_resolve_getnameinfo(
+ sd_resolve *resolve,
+ sd_resolve_query **ret_query,
+ const struct sockaddr *sa, socklen_t salen,
+ int flags,
+ uint64_t get,
+ sd_resolve_getnameinfo_handler_t callback,
+ void *userdata) {
+
+ return resolve_getnameinfo_with_destroy_callback(resolve, ret_query, sa, salen, flags, get, callback, NULL, userdata);
+}
+
static int getnameinfo_done(sd_resolve_query *q) {
assert(q);
@@ -1068,15 +1092,6 @@ static int getnameinfo_done(sd_resolve_query *q) {
return q->getnameinfo_handler(q, q->ret, q->host, q->serv, q->userdata);
}
-_public_ sd_resolve_query* sd_resolve_query_ref(sd_resolve_query *q) {
- assert_return(q, NULL);
-
- assert(q->n_ref >= 1);
- q->n_ref++;
-
- return q;
-}
-
static void resolve_freeaddrinfo(struct addrinfo *ai) {
while (ai) {
struct addrinfo *next = ai->ai_next;
@@ -1116,30 +1131,23 @@ static void resolve_query_disconnect(sd_resolve_query *q) {
sd_resolve_unref(resolve);
}
-static void resolve_query_free(sd_resolve_query *q) {
+static sd_resolve_query *resolve_query_free(sd_resolve_query *q) {
assert(q);
resolve_query_disconnect(q);
+ if (q->destroy_callback)
+ q->destroy_callback(q->userdata);
+
resolve_freeaddrinfo(q->addrinfo);
free(q->host);
free(q->serv);
- free(q);
-}
-
-_public_ sd_resolve_query* sd_resolve_query_unref(sd_resolve_query* q) {
- if (!q)
- return NULL;
-
- assert(q->n_ref >= 1);
- q->n_ref--;
- if (q->n_ref <= 0)
- resolve_query_free(q);
-
- return NULL;
+ return mfree(q);
}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_resolve_query, sd_resolve_query, resolve_query_free);
+
_public_ int sd_resolve_query_is_done(sd_resolve_query *q) {
assert_return(q, -EINVAL);
assert_return(!resolve_pid_changed(q->resolve), -ECHILD);
@@ -1173,6 +1181,50 @@ _public_ sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q) {
return q->resolve;
}
+_public_ int sd_resolve_query_get_destroy_callback(sd_resolve_query *q, sd_resolve_destroy_t *destroy_callback) {
+ assert_return(q, -EINVAL);
+
+ if (destroy_callback)
+ *destroy_callback = q->destroy_callback;
+
+ return !!q->destroy_callback;
+}
+
+_public_ int sd_resolve_query_set_destroy_callback(sd_resolve_query *q, sd_resolve_destroy_t destroy_callback) {
+ assert_return(q, -EINVAL);
+
+ q->destroy_callback = destroy_callback;
+ return 0;
+}
+
+_public_ int sd_resolve_query_get_floating(sd_resolve_query *q) {
+ assert_return(q, -EINVAL);
+
+ return q->floating;
+}
+
+_public_ int sd_resolve_query_set_floating(sd_resolve_query *q, int b) {
+ assert_return(q, -EINVAL);
+
+ if (q->floating == !!b)
+ return 0;
+
+ if (!q->resolve) /* Already disconnected */
+ return -ESTALE;
+
+ q->floating = b;
+
+ if (b) {
+ sd_resolve_query_ref(q);
+ sd_resolve_unref(q->resolve);
+ } else {
+ sd_resolve_ref(q->resolve);
+ sd_resolve_query_unref(q);
+ }
+
+ return 1;
+}
+
static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
sd_resolve *resolve = userdata;
int r;
diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c
index 880d224a38..8de7adcd16 100644
--- a/src/libsystemd/sd-resolve/test-resolve.c
+++ b/src/libsystemd/sd-resolve/test-resolve.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2014 Daniel Buch
-***/
#include <arpa/inet.h>
#include <errno.h>
@@ -17,6 +14,7 @@
#include "macro.h"
#include "socket-util.h"
#include "string-util.h"
+#include "time-util.h"
#define TEST_TIMEOUT_USEC (20*USEC_PER_SEC)
@@ -94,7 +92,7 @@ int main(int argc, char *argv[]) {
if (r == 0)
break;
if (r == -ETIMEDOUT) {
- /* Let's catch time-outs here, so that we can run safely in a CI that has no reliable DNS. Note
+ /* Let's catch timeouts here, so that we can run safely in a CI that has no reliable DNS. Note
* that we invoke exit() directly here, as the stuck NSS call will not allow us to exit
* cleanly. */
diff --git a/src/libsystemd/sd-utf8/sd-utf8.c b/src/libsystemd/sd-utf8/sd-utf8.c
index 79909c9af1..78323cf7cb 100644
--- a/src/libsystemd/sd-utf8/sd-utf8.c
+++ b/src/libsystemd/sd-utf8/sd-utf8.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-***/
#include "sd-utf8.h"
diff --git a/src/libudev/libudev-device-internal.h b/src/libudev/libudev-device-internal.h
index 7a78deff79..8a6e5a48f6 100644
--- a/src/libudev/libudev-device-internal.h
+++ b/src/libudev/libudev-device-internal.h
@@ -1,11 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "libudev.h"
#include "sd-device.h"
-#include "libudev-private.h"
+#include "libudev-list-internal.h"
/**
* udev_device:
@@ -19,7 +18,7 @@ struct udev_device {
sd_device *device;
/* legacy */
- int refcount;
+ unsigned n_ref;
struct udev_device *parent;
bool parent_set;
@@ -37,4 +36,4 @@ struct udev_device {
bool sysattrs_read;
};
-struct udev_device *udev_device_new(struct udev *udev);
+struct udev_device *udev_device_new(struct udev *udev, sd_device *device);
diff --git a/src/libudev/libudev-device-private.c b/src/libudev/libudev-device-private.c
deleted file mode 100644
index 22b421299a..0000000000
--- a/src/libudev/libudev-device-private.c
+++ /dev/null
@@ -1,393 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#include "libudev.h"
-
-#include "device-private.h"
-#include "libudev-device-internal.h"
-#include "libudev-private.h"
-
-int udev_device_tag_index(struct udev_device *udev_device, struct udev_device *udev_device_old, bool add) {
- sd_device *device_old = NULL;
- int r;
-
- assert(udev_device);
-
- if (udev_device_old)
- device_old = udev_device_old->device;
-
- r = device_tag_index(udev_device->device, device_old, add);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int udev_device_update_db(struct udev_device *udev_device) {
- int r;
-
- assert(udev_device);
-
- r = device_update_db(udev_device->device);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int udev_device_delete_db(struct udev_device *udev_device) {
- int r;
-
- assert(udev_device);
-
- r = device_delete_db(udev_device->device);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int udev_device_get_ifindex(struct udev_device *udev_device) {
- int r, ifindex;
-
- assert(udev_device);
-
- r = sd_device_get_ifindex(udev_device->device, &ifindex);
- if (r < 0)
- return r;
-
- return ifindex;
-}
-
-const char *udev_device_get_devpath_old(struct udev_device *udev_device) {
- const char *devpath_old = NULL;
- int r;
-
- assert(udev_device);
-
- r = sd_device_get_property_value(udev_device->device, "DEVPATH_OLD", &devpath_old);
- if (r < 0 && r != -ENOENT) {
- errno = -r;
- return NULL;
- }
-
- return devpath_old;
-}
-
-mode_t udev_device_get_devnode_mode(struct udev_device *udev_device) {
- mode_t mode;
- int r;
-
- assert(udev_device);
-
- r = device_get_devnode_mode(udev_device->device, &mode);
- if (r < 0) {
- errno = -r;
- return 0;
- }
-
- return mode;
-}
-
-uid_t udev_device_get_devnode_uid(struct udev_device *udev_device) {
- uid_t uid;
- int r;
-
- assert(udev_device);
-
- r = device_get_devnode_uid(udev_device->device, &uid);
- if (r < 0) {
- errno = -r;
- return 0;
- }
-
- return uid;
-}
-
-gid_t udev_device_get_devnode_gid(struct udev_device *udev_device) {
- gid_t gid;
- int r;
-
- assert(udev_device);
-
- r = device_get_devnode_gid(udev_device->device, &gid);
- if (r < 0) {
- errno = -r;
- return 0;
- }
-
- return gid;
-}
-
-void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *udev_device_old) {
- assert(udev_device);
-
- device_ensure_usec_initialized(udev_device->device,
- udev_device_old ? udev_device_old->device : NULL);
-}
-
-char **udev_device_get_properties_envp(struct udev_device *udev_device) {
- char **envp;
- int r;
-
- assert(udev_device);
-
- r = device_get_properties_strv(udev_device->device, &envp);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
-
- return envp;
-}
-
-ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf) {
- const char *nulstr;
- size_t len;
- int r;
-
- assert(udev_device);
- assert(buf);
-
- r = device_get_properties_nulstr(udev_device->device, (const uint8_t **)&nulstr, &len);
- if (r < 0)
- return r;
-
- *buf = nulstr;
-
- return len;
-}
-
-int udev_device_get_devlink_priority(struct udev_device *udev_device) {
- int priority, r;
-
- assert(udev_device);
-
- r = device_get_devlink_priority(udev_device->device, &priority);
- if (r < 0)
- return r;
-
- return priority;
-}
-
-int udev_device_get_watch_handle(struct udev_device *udev_device) {
- int handle, r;
-
- assert(udev_device);
-
- r = device_get_watch_handle(udev_device->device, &handle);
- if (r < 0)
- return r;
-
- return handle;
-}
-
-void udev_device_set_is_initialized(struct udev_device *udev_device) {
- assert(udev_device);
-
- device_set_is_initialized(udev_device->device);
-}
-
-int udev_device_rename(struct udev_device *udev_device, const char *name) {
- int r;
-
- assert(udev_device);
-
- r = device_rename(udev_device->device, name);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-struct udev_device *udev_device_shallow_clone(struct udev_device *old_device) {
- struct udev_device *device;
- int r;
-
- assert(old_device);
-
- device = udev_device_new(old_device->udev);
- if (!device)
- return NULL;
-
- r = device_shallow_clone(old_device->device, &device->device);
- if (r < 0) {
- udev_device_unref(device);
- errno = -r;
- return NULL;
- }
-
- return device;
-}
-
-struct udev_device *udev_device_clone_with_db(struct udev_device *udev_device_old) {
- struct udev_device *udev_device;
- int r;
-
- assert(udev_device_old);
-
- udev_device = udev_device_new(udev_device_old->udev);
- if (!udev_device)
- return NULL;
-
- r = device_clone_with_db(udev_device_old->device, &udev_device->device);
- if (r < 0) {
- udev_device_unref(udev_device);
- errno = -r;
- return NULL;
- }
-
- return udev_device;
-}
-
-struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen) {
- struct udev_device *device;
- int r;
-
- device = udev_device_new(udev);
- if (!device)
- return NULL;
-
- r = device_new_from_nulstr(&device->device, (uint8_t*)nulstr, buflen);
- if (r < 0) {
- udev_device_unref(device);
- errno = -r;
- return NULL;
- }
-
- return device;
-}
-
-struct udev_device *udev_device_new_from_synthetic_event(struct udev *udev, const char *syspath, const char *action) {
- struct udev_device *device;
- int r;
-
- device = udev_device_new(udev);
- if (!device)
- return NULL;
-
- r = device_new_from_synthetic_event(&device->device, syspath, action);
- if (r < 0) {
- udev_device_unref(device);
- errno = -r;
- return NULL;
- }
-
- return device;
-}
-
-int udev_device_copy_properties(struct udev_device *udev_device_dst, struct udev_device *udev_device_src) {
- int r;
-
- assert(udev_device_dst);
- assert(udev_device_src);
-
- r = device_copy_properties(udev_device_dst->device, udev_device_src->device);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-const char *udev_device_get_id_filename(struct udev_device *udev_device) {
- const char *filename;
- int r;
-
- assert(udev_device);
-
- r = device_get_id_filename(udev_device->device, &filename);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
-
- return filename;
-}
-
-int udev_device_set_watch_handle(struct udev_device *udev_device, int handle) {
-
- assert(udev_device);
-
- device_set_watch_handle(udev_device->device, handle);
-
- return 0;
-}
-
-void udev_device_set_db_persist(struct udev_device *udev_device) {
- assert(udev_device);
-
- device_set_db_persist(udev_device->device);
-}
-
-int udev_device_set_devlink_priority(struct udev_device *udev_device, int priority) {
- assert(udev_device);
-
- device_set_devlink_priority(udev_device->device, priority);
-
- return 0;
-}
-
-int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink) {
- int r;
-
- assert(udev_device);
-
- r = device_add_devlink(udev_device->device, devlink);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int udev_device_add_property(struct udev_device *udev_device, const char *property, const char *value) {
- int r;
-
- assert(udev_device);
-
- r = device_add_property(udev_device->device, property, value);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int udev_device_add_tag(struct udev_device *udev_device, const char *tag) {
- int r;
-
- assert(udev_device);
-
- r = device_add_tag(udev_device->device, tag);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-void udev_device_remove_tag(struct udev_device *udev_device, const char *tag) {
- assert(udev_device);
-
- device_remove_tag(udev_device->device, tag);
-}
-
-void udev_device_cleanup_tags_list(struct udev_device *udev_device) {
- assert(udev_device);
-
- device_cleanup_tags(udev_device->device);
-}
-
-void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) {
- assert(udev_device);
-
- device_cleanup_devlinks(udev_device->device);
-}
-
-void udev_device_set_info_loaded(struct udev_device *udev_device) {
- assert(udev_device);
-
- device_seal(udev_device->device);
-}
-
-void udev_device_read_db(struct udev_device *udev_device) {
- assert(udev_device);
-
- device_read_db_force(udev_device->device);
-}
diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c
index 0f132c54b5..1e7d774ba9 100644
--- a/src/libudev/libudev-device.c
+++ b/src/libudev/libudev-device.c
@@ -23,8 +23,8 @@
#include "device-private.h"
#include "device-util.h"
#include "libudev-device-internal.h"
-#include "libudev-private.h"
#include "parse-util.h"
+#include "time-util.h"
/**
* SECTION:libudev-device
@@ -45,8 +45,7 @@
*
* Returns: the kernel event sequence number, or 0 if there is no sequence number available.
**/
-_public_ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device)
-{
+_public_ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) {
const char *seqnum;
unsigned long long ret;
int r;
@@ -56,16 +55,12 @@ _public_ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_
r = sd_device_get_property_value(udev_device->device, "SEQNUM", &seqnum);
if (r == -ENOENT)
return 0;
- else if (r < 0) {
- errno = -r;
- return 0;
- }
+ else if (r < 0)
+ return_with_errno(0, r);
r = safe_atollu(seqnum, &ret);
- if (r < 0) {
- errno = -r;
- return 0;
- }
+ if (r < 0)
+ return_with_errno(0, r);
return ret;
}
@@ -78,18 +73,17 @@ _public_ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_
*
* Returns: the dev_t number.
**/
-_public_ dev_t udev_device_get_devnum(struct udev_device *udev_device)
-{
+_public_ dev_t udev_device_get_devnum(struct udev_device *udev_device) {
dev_t devnum;
int r;
assert_return_errno(udev_device, makedev(0, 0), EINVAL);
r = sd_device_get_devnum(udev_device->device, &devnum);
- if (r < 0) {
- errno = -r;
+ if (r == -ENOENT)
return makedev(0, 0);
- }
+ if (r < 0)
+ return_with_errno(makedev(0, 0), r);
return devnum;
}
@@ -102,18 +96,15 @@ _public_ dev_t udev_device_get_devnum(struct udev_device *udev_device)
*
* Returns: the driver name string, or #NULL if there is no driver attached.
**/
-_public_ const char *udev_device_get_driver(struct udev_device *udev_device)
-{
+_public_ const char *udev_device_get_driver(struct udev_device *udev_device) {
const char *driver;
int r;
assert_return_errno(udev_device, NULL, EINVAL);
r = sd_device_get_driver(udev_device->device, &driver);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
return driver;
}
@@ -126,18 +117,17 @@ _public_ const char *udev_device_get_driver(struct udev_device *udev_device)
*
* Returns: the devtype name of the udev device, or #NULL if it cannot be determined
**/
-_public_ const char *udev_device_get_devtype(struct udev_device *udev_device)
-{
+_public_ const char *udev_device_get_devtype(struct udev_device *udev_device) {
const char *devtype;
int r;
assert_return_errno(udev_device, NULL, EINVAL);
r = sd_device_get_devtype(udev_device->device, &devtype);
- if (r < 0) {
- errno = -r;
+ if (r == -ENOENT)
return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
return devtype;
}
@@ -151,19 +141,15 @@ _public_ const char *udev_device_get_devtype(struct udev_device *udev_device)
*
* Returns: the subsystem name of the udev device, or #NULL if it cannot be determined
**/
-_public_ const char *udev_device_get_subsystem(struct udev_device *udev_device)
-{
+_public_ const char *udev_device_get_subsystem(struct udev_device *udev_device) {
const char *subsystem;
int r;
assert_return_errno(udev_device, NULL, EINVAL);
r = sd_device_get_subsystem(udev_device->device, &subsystem);
- if (r < 0) {
- errno = -r;
- return NULL;
- } else if (!subsystem)
- errno = ENODATA;
+ if (r < 0)
+ return_with_errno(NULL, r);
return subsystem;
}
@@ -177,38 +163,38 @@ _public_ const char *udev_device_get_subsystem(struct udev_device *udev_device)
*
* Returns: the property string, or #NULL if there is no such property.
**/
-_public_ const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key)
-{
- const char *value = NULL;
+_public_ const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) {
+ const char *value;
int r;
assert_return_errno(udev_device && key, NULL, EINVAL);
r = sd_device_get_property_value(udev_device->device, key, &value);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
return value;
}
-struct udev_device *udev_device_new(struct udev *udev) {
+struct udev_device *udev_device_new(struct udev *udev, sd_device *device) {
struct udev_device *udev_device;
- assert_return_errno(udev, NULL, EINVAL);
+ assert(device);
- udev_device = new0(struct udev_device, 1);
- if (!udev_device) {
- errno = ENOMEM;
- return NULL;
- }
- udev_device->refcount = 1;
- udev_device->udev = udev;
- udev_list_init(udev, &udev_device->properties, true);
- udev_list_init(udev, &udev_device->tags, true);
- udev_list_init(udev, &udev_device->sysattrs, true);
- udev_list_init(udev, &udev_device->devlinks, true);
+ udev_device = new(struct udev_device, 1);
+ if (!udev_device)
+ return_with_errno(NULL, ENOMEM);
+
+ *udev_device = (struct udev_device) {
+ .n_ref = 1,
+ .udev = udev,
+ .device = sd_device_ref(device),
+ };
+
+ udev_list_init(&udev_device->properties, true);
+ udev_list_init(&udev_device->tags, true);
+ udev_list_init(&udev_device->sysattrs, true);
+ udev_list_init(&udev_device->devlinks, true);
return udev_device;
}
@@ -228,21 +214,14 @@ struct udev_device *udev_device_new(struct udev *udev) {
* Returns: a new udev device, or #NULL, if it does not exist
**/
_public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) {
- struct udev_device *udev_device;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
int r;
- udev_device = udev_device_new(udev);
- if (!udev_device)
- return NULL;
-
- r = sd_device_new_from_syspath(&udev_device->device, syspath);
- if (r < 0) {
- errno = -r;
- udev_device_unref(udev_device);
- return NULL;
- }
+ r = sd_device_new_from_syspath(&device, syspath);
+ if (r < 0)
+ return_with_errno(NULL, r);
- return udev_device;
+ return udev_device_new(udev, device);
}
/**
@@ -261,23 +240,15 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con
*
* Returns: a new udev device, or #NULL, if it does not exist
**/
-_public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum)
-{
- struct udev_device *udev_device;
+_public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
int r;
- udev_device = udev_device_new(udev);
- if (!udev_device)
- return NULL;
-
- r = sd_device_new_from_devnum(&udev_device->device, type, devnum);
- if (r < 0) {
- errno = -r;
- udev_device_unref(udev_device);
- return NULL;
- }
+ r = sd_device_new_from_devnum(&device, type, devnum);
+ if (r < 0)
+ return_with_errno(NULL, r);
- return udev_device;
+ return udev_device_new(udev, device);
}
/**
@@ -298,23 +269,15 @@ _public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char
*
* Returns: a new udev device, or #NULL, if it does not exist
**/
-_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id)
-{
- struct udev_device *udev_device;
+_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
int r;
- udev_device = udev_device_new(udev);
- if (!udev_device)
- return NULL;
-
- r = sd_device_new_from_device_id(&udev_device->device, id);
- if (r < 0) {
- errno = -r;
- udev_device_unref(udev_device);
- return NULL;
- }
+ r = sd_device_new_from_device_id(&device, id);
+ if (r < 0)
+ return_with_errno(NULL, r);
- return udev_device;
+ return udev_device_new(udev, device);
}
/**
@@ -332,23 +295,15 @@ _public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, c
*
* Returns: a new udev device, or #NULL, if it does not exist
**/
-_public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname)
-{
- struct udev_device *udev_device;
+_public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
int r;
- udev_device = udev_device_new(udev);
- if (!udev_device)
- return NULL;
-
- r = sd_device_new_from_subsystem_sysname(&udev_device->device, subsystem, sysname);
- if (r < 0) {
- errno = -r;
- udev_device_unref(udev_device);
- return NULL;
- }
+ r = sd_device_new_from_subsystem_sysname(&device, subsystem, sysname);
+ if (r < 0)
+ return_with_errno(NULL, r);
- return udev_device;
+ return udev_device_new(udev, device);
}
/**
@@ -365,47 +320,28 @@ _public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev
*
* Returns: a new udev device, or #NULL, if it does not exist
**/
-_public_ struct udev_device *udev_device_new_from_environment(struct udev *udev)
-{
- struct udev_device *udev_device;
+_public_ struct udev_device *udev_device_new_from_environment(struct udev *udev) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
int r;
- udev_device = udev_device_new(udev);
- if (!udev_device)
- return NULL;
-
- r = device_new_from_strv(&udev_device->device, environ);
- if (r < 0) {
- errno = -r;
- udev_device_unref(udev_device);
- return NULL;
- }
+ r = device_new_from_strv(&device, environ);
+ if (r < 0)
+ return_with_errno(NULL, r);
- return udev_device;
+ return udev_device_new(udev, device);
}
-static struct udev_device *device_new_from_parent(struct udev_device *child)
-{
- struct udev_device *parent;
+static struct udev_device *device_new_from_parent(struct udev_device *child) {
+ sd_device *parent;
int r;
assert_return_errno(child, NULL, EINVAL);
- parent = udev_device_new(child->udev);
- if (!parent)
- return NULL;
-
- r = sd_device_get_parent(child->device, &parent->device);
- if (r < 0) {
- errno = -r;
- udev_device_unref(parent);
- return NULL;
- }
-
- /* the parent is unref'ed with the child, so take a ref from libudev as well */
- sd_device_ref(parent->device);
+ r = sd_device_get_parent(child->device, &parent);
+ if (r < 0)
+ return_with_errno(NULL, r);
- return parent;
+ return udev_device_new(child->udev, parent);
}
/**
@@ -426,8 +362,7 @@ static struct udev_device *device_new_from_parent(struct udev_device *child)
*
* Returns: a new udev device, or #NULL, if it no parent exist.
**/
-_public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_device)
-{
+_public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_device) {
assert_return_errno(udev_device, NULL, EINVAL);
if (!udev_device->parent_set) {
@@ -460,8 +395,7 @@ _public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_dev
*
* Returns: a new udev device, or #NULL if no matching parent exists.
**/
-_public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype)
-{
+_public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype) {
sd_device *parent;
int r;
@@ -472,20 +406,16 @@ _public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struc
/* first find the correct sd_device */
r = sd_device_get_parent_with_subsystem_devtype(udev_device->device, subsystem, devtype, &parent);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
/* then walk the chain of udev_device parents until the corresponding
one is found */
- while ((udev_device = udev_device_get_parent(udev_device))) {
+ while ((udev_device = udev_device_get_parent(udev_device)))
if (udev_device->device == parent)
return udev_device;
- }
- errno = ENOENT;
- return NULL;
+ return_with_errno(NULL, ENOENT);
}
/**
@@ -496,13 +426,26 @@ _public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struc
*
* Returns: the udev library context
**/
-_public_ struct udev *udev_device_get_udev(struct udev_device *udev_device)
-{
+_public_ struct udev *udev_device_get_udev(struct udev_device *udev_device) {
assert_return_errno(udev_device, NULL, EINVAL);
return udev_device->udev;
}
+static struct udev_device *udev_device_free(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ sd_device_unref(udev_device->device);
+ udev_device_unref(udev_device->parent);
+
+ udev_list_cleanup(&udev_device->properties);
+ udev_list_cleanup(&udev_device->sysattrs);
+ udev_list_cleanup(&udev_device->tags);
+ udev_list_cleanup(&udev_device->devlinks);
+
+ return mfree(udev_device);
+}
+
/**
* udev_device_ref:
* @udev_device: udev device
@@ -511,13 +454,6 @@ _public_ struct udev *udev_device_get_udev(struct udev_device *udev_device)
*
* Returns: the passed udev device
**/
-_public_ struct udev_device *udev_device_ref(struct udev_device *udev_device)
-{
- if (udev_device)
- udev_device->refcount++;
-
- return udev_device;
-}
/**
* udev_device_unref:
@@ -528,22 +464,7 @@ _public_ struct udev_device *udev_device_ref(struct udev_device *udev_device)
*
* Returns: #NULL
**/
-_public_ struct udev_device *udev_device_unref(struct udev_device *udev_device)
-{
- if (udev_device && (-- udev_device->refcount) == 0) {
- sd_device_unref(udev_device->device);
- udev_device_unref(udev_device->parent);
-
- udev_list_cleanup(&udev_device->properties);
- udev_list_cleanup(&udev_device->sysattrs);
- udev_list_cleanup(&udev_device->tags);
- udev_list_cleanup(&udev_device->devlinks);
-
- free(udev_device);
- }
-
- return NULL;
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(struct udev_device, udev_device, udev_device_free);
/**
* udev_device_get_devpath:
@@ -554,18 +475,15 @@ _public_ struct udev_device *udev_device_unref(struct udev_device *udev_device)
*
* Returns: the devpath of the udev device
**/
-_public_ const char *udev_device_get_devpath(struct udev_device *udev_device)
-{
+_public_ const char *udev_device_get_devpath(struct udev_device *udev_device) {
const char *devpath;
int r;
assert_return_errno(udev_device, NULL, EINVAL);
r = sd_device_get_devpath(udev_device->device, &devpath);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
return devpath;
}
@@ -579,18 +497,15 @@ _public_ const char *udev_device_get_devpath(struct udev_device *udev_device)
*
* Returns: the sys path of the udev device
**/
-_public_ const char *udev_device_get_syspath(struct udev_device *udev_device)
-{
+_public_ const char *udev_device_get_syspath(struct udev_device *udev_device) {
const char *syspath;
int r;
assert_return_errno(udev_device, NULL, EINVAL);
r = sd_device_get_syspath(udev_device->device, &syspath);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
return syspath;
}
@@ -603,18 +518,15 @@ _public_ const char *udev_device_get_syspath(struct udev_device *udev_device)
*
* Returns: the name string of the device
**/
-_public_ const char *udev_device_get_sysname(struct udev_device *udev_device)
-{
+_public_ const char *udev_device_get_sysname(struct udev_device *udev_device) {
const char *sysname;
int r;
assert_return_errno(udev_device, NULL, EINVAL);
r = sd_device_get_sysname(udev_device->device, &sysname);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
return sysname;
}
@@ -627,18 +539,17 @@ _public_ const char *udev_device_get_sysname(struct udev_device *udev_device)
*
* Returns: the trailing number string of the device name
**/
-_public_ const char *udev_device_get_sysnum(struct udev_device *udev_device)
-{
+_public_ const char *udev_device_get_sysnum(struct udev_device *udev_device) {
const char *sysnum;
int r;
assert_return_errno(udev_device, NULL, EINVAL);
r = sd_device_get_sysnum(udev_device->device, &sysnum);
- if (r < 0) {
- errno = -r;
+ if (r == -ENOENT)
return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
return sysnum;
}
@@ -652,18 +563,15 @@ _public_ const char *udev_device_get_sysnum(struct udev_device *udev_device)
*
* Returns: the device node file name of the udev device, or #NULL if no device node exists
**/
-_public_ const char *udev_device_get_devnode(struct udev_device *udev_device)
-{
+_public_ const char *udev_device_get_devnode(struct udev_device *udev_device) {
const char *devnode;
int r;
assert_return_errno(udev_device, NULL, EINVAL);
r = sd_device_get_devname(udev_device->device, &devnode);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
return devnode;
}
@@ -681,8 +589,7 @@ _public_ const char *udev_device_get_devnode(struct udev_device *udev_device)
*
* Returns: the first entry of the device node link list
**/
-_public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device)
-{
+_public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device) {
assert_return_errno(udev_device, NULL, EINVAL);
if (device_get_devlinks_generation(udev_device->device) != udev_device->devlinks_generation ||
@@ -692,7 +599,8 @@ _public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev
udev_list_cleanup(&udev_device->devlinks);
FOREACH_DEVICE_DEVLINK(udev_device->device, devlink)
- udev_list_entry_add(&udev_device->devlinks, devlink, NULL);
+ if (!udev_list_entry_add(&udev_device->devlinks, devlink, NULL))
+ return_with_errno(NULL, ENOMEM);
udev_device->devlinks_read = true;
udev_device->devlinks_generation = device_get_devlinks_generation(udev_device->device);
@@ -713,8 +621,7 @@ _public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev
*
* Returns: the first entry of the property list
**/
-_public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device)
-{
+_public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device) {
assert_return_errno(udev_device, NULL, EINVAL);
if (device_get_properties_generation(udev_device->device) != udev_device->properties_generation ||
@@ -724,7 +631,8 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud
udev_list_cleanup(&udev_device->properties);
FOREACH_DEVICE_PROPERTY(udev_device->device, key, value)
- udev_list_entry_add(&udev_device->properties, key, value);
+ if (!udev_list_entry_add(&udev_device->properties, key, value))
+ return_with_errno(NULL, ENOMEM);
udev_device->properties_read = true;
udev_device->properties_generation = device_get_properties_generation(udev_device->device);
@@ -744,16 +652,16 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud
* Returns: the kernel action value, or #NULL if there is no action value available.
**/
_public_ const char *udev_device_get_action(struct udev_device *udev_device) {
- const char *action = NULL;
+ const char *action;
int r;
assert_return_errno(udev_device, NULL, EINVAL);
r = sd_device_get_property_value(udev_device->device, "ACTION", &action);
- if (r < 0 && r != -ENOENT) {
- errno = -r;
+ if (r == -ENOENT)
return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
return action;
}
@@ -770,18 +678,15 @@ _public_ const char *udev_device_get_action(struct udev_device *udev_device) {
*
* Returns: the number of microseconds since the device was first seen.
**/
-_public_ unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device)
-{
+_public_ unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device) {
usec_t ts;
int r;
assert_return(udev_device, -EINVAL);
r = sd_device_get_usec_since_initialized(udev_device->device, &ts);
- if (r < 0) {
- errno = EINVAL;
- return 0;
- }
+ if (r < 0)
+ return_with_errno(0, r);
return ts;
}
@@ -796,18 +701,15 @@ _public_ unsigned long long int udev_device_get_usec_since_initialized(struct ud
*
* Returns: the content of a sys attribute file, or #NULL if there is no sys attribute value.
**/
-_public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr)
-{
+_public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) {
const char *value;
int r;
assert_return_errno(udev_device, NULL, EINVAL);
r = sd_device_get_sysattr_value(udev_device->device, sysattr, &value);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
return value;
}
@@ -822,8 +724,7 @@ _public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_devi
*
* Returns: Negative error code on failure or 0 on success.
**/
-_public_ int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value)
-{
+_public_ int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, const char *value) {
int r;
assert_return(udev_device, -EINVAL);
@@ -845,8 +746,7 @@ _public_ int udev_device_set_sysattr_value(struct udev_device *udev_device, cons
*
* Returns: the first entry of the property list
**/
-_public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device)
-{
+_public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device) {
assert_return_errno(udev_device, NULL, EINVAL);
if (!udev_device->sysattrs_read) {
@@ -855,7 +755,8 @@ _public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_
udev_list_cleanup(&udev_device->sysattrs);
FOREACH_DEVICE_SYSATTR(udev_device->device, sysattr)
- udev_list_entry_add(&udev_device->sysattrs, sysattr, NULL);
+ if (!udev_list_entry_add(&udev_device->sysattrs, sysattr, NULL))
+ return_with_errno(NULL, ENOMEM);
udev_device->sysattrs_read = true;
}
@@ -876,20 +777,16 @@ _public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_
*
* Returns: 1 if the device is set up. 0 otherwise.
**/
-_public_ int udev_device_get_is_initialized(struct udev_device *udev_device)
-{
- int r, initialized;
+_public_ int udev_device_get_is_initialized(struct udev_device *udev_device) {
+ int r;
assert_return(udev_device, -EINVAL);
- r = sd_device_get_is_initialized(udev_device->device, &initialized);
- if (r < 0) {
- errno = -r;
-
- return 0;
- }
+ r = sd_device_get_is_initialized(udev_device->device);
+ if (r < 0)
+ return_with_errno(0, r);
- return initialized;
+ return r;
}
/**
@@ -903,8 +800,7 @@ _public_ int udev_device_get_is_initialized(struct udev_device *udev_device)
*
* Returns: the first entry of the tag list
**/
-_public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device)
-{
+_public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device) {
assert_return_errno(udev_device, NULL, EINVAL);
if (device_get_tags_generation(udev_device->device) != udev_device->tags_generation ||
@@ -914,7 +810,8 @@ _public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_dev
udev_list_cleanup(&udev_device->tags);
FOREACH_DEVICE_TAG(udev_device->device, tag)
- udev_list_entry_add(&udev_device->tags, tag, NULL);
+ if (!udev_list_entry_add(&udev_device->tags, tag, NULL))
+ return_with_errno(NULL, ENOMEM);
udev_device->tags_read = true;
udev_device->tags_generation = device_get_tags_generation(udev_device->device);
@@ -932,9 +829,8 @@ _public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_dev
*
* Returns: 1 if the tag is found. 0 otherwise.
**/
-_public_ int udev_device_has_tag(struct udev_device *udev_device, const char *tag)
-{
+_public_ int udev_device_has_tag(struct udev_device *udev_device, const char *tag) {
assert_return(udev_device, 0);
- return sd_device_has_tag(udev_device->device, tag);
+ return sd_device_has_tag(udev_device->device, tag) > 0;
}
diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c
index c9d54dc72c..e54ee572c5 100644
--- a/src/libudev/libudev-enumerate.c
+++ b/src/libudev/libudev-enumerate.c
@@ -33,7 +33,7 @@
*/
struct udev_enumerate {
struct udev *udev;
- int refcount;
+ unsigned n_ref;
struct udev_list devices_list;
bool devices_uptodate:1;
@@ -49,35 +49,39 @@ struct udev_enumerate {
* Returns: an enumeration context.
**/
_public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) {
- _cleanup_free_ struct udev_enumerate *udev_enumerate = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ struct udev_enumerate *udev_enumerate;
int r;
- assert_return_errno(udev, NULL, EINVAL);
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return_with_errno(NULL, r);
- udev_enumerate = new0(struct udev_enumerate, 1);
- if (!udev_enumerate) {
- errno = ENOMEM;
- return NULL;
- }
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return_with_errno(NULL, r);
- r = sd_device_enumerator_new(&udev_enumerate->enumerator);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ udev_enumerate = new(struct udev_enumerate, 1);
+ if (!udev_enumerate)
+ return_with_errno(NULL, ENOMEM);
- r = sd_device_enumerator_allow_uninitialized(udev_enumerate->enumerator);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ *udev_enumerate = (struct udev_enumerate) {
+ .udev = udev,
+ .n_ref = 1,
+ .enumerator = TAKE_PTR(e),
+ };
+
+ udev_list_init(&udev_enumerate->devices_list, false);
- udev_enumerate->refcount = 1;
- udev_enumerate->udev = udev;
+ return udev_enumerate;
+}
- udev_list_init(udev, &udev_enumerate->devices_list, false);
+static struct udev_enumerate *udev_enumerate_free(struct udev_enumerate *udev_enumerate) {
+ assert(udev_enumerate);
- return TAKE_PTR(udev_enumerate);
+ udev_list_cleanup(&udev_enumerate->devices_list);
+ sd_device_enumerator_unref(udev_enumerate->enumerator);
+ return mfree(udev_enumerate);
}
/**
@@ -88,12 +92,6 @@ _public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) {
*
* Returns: the passed enumeration context
**/
-_public_ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) {
- if (udev_enumerate)
- udev_enumerate->refcount++;
-
- return udev_enumerate;
-}
/**
* udev_enumerate_unref:
@@ -104,15 +102,7 @@ _public_ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_e
*
* Returns: #NULL
**/
-_public_ struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) {
- if (udev_enumerate && (-- udev_enumerate->refcount) == 0) {
- udev_list_cleanup(&udev_enumerate->devices_list);
- sd_device_enumerator_unref(udev_enumerate->enumerator);
- free(udev_enumerate);
- }
-
- return NULL;
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(struct udev_enumerate, udev_enumerate, udev_enumerate_free);
/**
* udev_enumerate_get_udev:
@@ -151,12 +141,11 @@ _public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enume
int r;
r = sd_device_get_syspath(device, &syspath);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
- udev_list_entry_add(&udev_enumerate->devices_list, syspath, NULL);
+ if (!udev_list_entry_add(&udev_enumerate->devices_list, syspath, NULL))
+ return_with_errno(NULL, ENOMEM);
}
udev_enumerate->devices_uptodate = true;
@@ -164,7 +153,7 @@ _public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enume
e = udev_list_get_entry(&udev_enumerate->devices_list);
if (!e)
- errno = ENODATA;
+ return_with_errno(NULL, ENODATA);
return e;
}
diff --git a/src/libudev/libudev-hwdb.c b/src/libudev/libudev-hwdb.c
index 45322677e9..ed755e5d3c 100644
--- a/src/libudev/libudev-hwdb.c
+++ b/src/libudev/libudev-hwdb.c
@@ -1,10 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <errno.h>
+
#include "sd-hwdb.h"
#include "alloc-util.h"
#include "hwdb-util.h"
-#include "libudev-private.h"
+#include "libudev-list-internal.h"
/**
* SECTION:libudev-hwdb
@@ -19,17 +21,14 @@
* Opaque object representing the hardware database.
*/
struct udev_hwdb {
- struct udev *udev;
- int refcount;
-
+ unsigned n_ref;
sd_hwdb *hwdb;
-
struct udev_list properties_list;
};
/**
* udev_hwdb_new:
- * @udev: udev library context
+ * @udev: udev library context (unused)
*
* Create a hardware database context to query properties for devices.
*
@@ -40,28 +39,32 @@ _public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
struct udev_hwdb *hwdb;
int r;
- assert_return_errno(udev, NULL, EINVAL);
-
r = sd_hwdb_new(&hwdb_internal);
- if (r < 0) {
- errno = -r;
- return NULL;
- }
+ if (r < 0)
+ return_with_errno(NULL, r);
- hwdb = new0(struct udev_hwdb, 1);
- if (!hwdb) {
- errno = ENOMEM;
- return NULL;
- }
+ hwdb = new(struct udev_hwdb, 1);
+ if (!hwdb)
+ return_with_errno(NULL, ENOMEM);
- hwdb->refcount = 1;
- hwdb->hwdb = TAKE_PTR(hwdb_internal);
+ *hwdb = (struct udev_hwdb) {
+ .n_ref = 1,
+ .hwdb = TAKE_PTR(hwdb_internal),
+ };
- udev_list_init(udev, &hwdb->properties_list, true);
+ udev_list_init(&hwdb->properties_list, true);
return hwdb;
}
+static struct udev_hwdb *udev_hwdb_free(struct udev_hwdb *hwdb) {
+ assert(hwdb);
+
+ sd_hwdb_unref(hwdb->hwdb);
+ udev_list_cleanup(&hwdb->properties_list);
+ return mfree(hwdb);
+}
+
/**
* udev_hwdb_ref:
* @hwdb: context
@@ -70,12 +73,6 @@ _public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
*
* Returns: the passed enumeration context
**/
-_public_ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb) {
- if (!hwdb)
- return NULL;
- hwdb->refcount++;
- return hwdb;
-}
/**
* udev_hwdb_unref:
@@ -86,16 +83,7 @@ _public_ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb) {
*
* Returns: #NULL
**/
-_public_ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb) {
- if (!hwdb)
- return NULL;
- hwdb->refcount--;
- if (hwdb->refcount > 0)
- return NULL;
- sd_hwdb_unref(hwdb->hwdb);
- udev_list_cleanup(&hwdb->properties_list);
- return mfree(hwdb);
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(struct udev_hwdb, udev_hwdb, udev_hwdb_free);
/**
* udev_hwdb_get_properties_list_entry:
@@ -110,27 +98,22 @@ _public_ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb) {
*
* Returns: a udev_list_entry.
*/
-_public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags) {
+_public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned flags) {
const char *key, *value;
struct udev_list_entry *e;
- if (!hwdb || !modalias) {
- errno = EINVAL;
- return NULL;
- }
+ assert_return_errno(hwdb, NULL, EINVAL);
+ assert_return_errno(modalias, NULL, EINVAL);
udev_list_cleanup(&hwdb->properties_list);
- SD_HWDB_FOREACH_PROPERTY(hwdb->hwdb, modalias, key, value) {
- if (udev_list_entry_add(&hwdb->properties_list, key, value) == NULL) {
- errno = ENOMEM;
- return NULL;
- }
- }
+ SD_HWDB_FOREACH_PROPERTY(hwdb->hwdb, modalias, key, value)
+ if (!udev_list_entry_add(&hwdb->properties_list, key, value))
+ return_with_errno(NULL, ENOMEM);
e = udev_list_get_entry(&hwdb->properties_list);
if (!e)
- errno = ENODATA;
+ return_with_errno(NULL, ENODATA);
return e;
}
diff --git a/src/libudev/libudev-list-internal.h b/src/libudev/libudev-list-internal.h
new file mode 100644
index 0000000000..4e1632c78d
--- /dev/null
+++ b/src/libudev/libudev-list-internal.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "libudev.h"
+
+struct udev_list_node {
+ struct udev_list_node *next, *prev;
+};
+
+struct udev_list {
+ struct udev_list_node node;
+ struct udev_list_entry **entries;
+ unsigned entries_cur;
+ unsigned entries_max;
+ bool unique;
+};
+
+void udev_list_init(struct udev_list *list, bool unique);
+void udev_list_cleanup(struct udev_list *list);
+struct udev_list_entry *udev_list_get_entry(struct udev_list *list);
+struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value);
diff --git a/src/libudev/libudev-list.c b/src/libudev/libudev-list.c
index b7373efcda..5f6726c2d8 100644
--- a/src/libudev/libudev-list.c
+++ b/src/libudev/libudev-list.c
@@ -6,7 +6,8 @@
#include <string.h>
#include "alloc-util.h"
-#include "libudev-private.h"
+#include "libudev-list-internal.h"
+#include "util.h"
/**
* SECTION:libudev-list
@@ -30,34 +31,25 @@ struct udev_list_entry {
};
/* the list's head points to itself if empty */
-void udev_list_node_init(struct udev_list_node *list)
-{
+static void udev_list_node_init(struct udev_list_node *list) {
list->next = list;
list->prev = list;
}
-int udev_list_node_is_empty(struct udev_list_node *list)
-{
+static int udev_list_node_is_empty(struct udev_list_node *list) {
return list->next == list;
}
static void udev_list_node_insert_between(struct udev_list_node *new,
struct udev_list_node *prev,
- struct udev_list_node *next)
-{
+ struct udev_list_node *next) {
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
-void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list)
-{
- udev_list_node_insert_between(new, list->prev, list);
-}
-
-void udev_list_node_remove(struct udev_list_node *entry)
-{
+static void udev_list_node_remove(struct udev_list_node *entry) {
struct udev_list_node *prev = entry->prev;
struct udev_list_node *next = entry->next;
@@ -69,43 +61,37 @@ void udev_list_node_remove(struct udev_list_node *entry)
}
/* return list entry which embeds this node */
-static inline struct udev_list_entry *list_node_to_entry(struct udev_list_node *node)
-{
+static inline struct udev_list_entry *list_node_to_entry(struct udev_list_node *node) {
return container_of(node, struct udev_list_entry, node);
}
-void udev_list_init(struct udev *udev, struct udev_list *list, bool unique)
-{
+void udev_list_init(struct udev_list *list, bool unique) {
memzero(list, sizeof(struct udev_list));
- list->udev = udev;
list->unique = unique;
udev_list_node_init(&list->node);
}
/* insert entry into a list as the last element */
-static void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list)
-{
+static void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list) {
/* inserting before the list head make the node the last node in the list */
udev_list_node_insert_between(&new->node, list->node.prev, &list->node);
new->list = list;
}
/* insert entry into a list, before a given existing entry */
-static void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry)
-{
+static void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry) {
udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node);
new->list = entry->list;
}
/* binary search in sorted array */
-static int list_search(struct udev_list *list, const char *name)
-{
- unsigned int first, last;
+static int list_search(struct udev_list *list, const char *name) {
+ unsigned first, last;
first = 0;
last = list->entries_cur;
while (first < last) {
- unsigned int i;
+ unsigned i;
int cmp;
i = (first + last)/2;
@@ -165,7 +151,7 @@ struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *
/* allocate or enlarge sorted array if needed */
if (list->entries_cur >= list->entries_max) {
struct udev_list_entry **entries;
- unsigned int add;
+ unsigned add;
add = list->entries_max;
if (add < 1)
@@ -184,7 +170,7 @@ struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *
i = (-i)-1;
/* insert into sorted list */
- if ((unsigned int)i < list->entries_cur)
+ if ((unsigned)i < list->entries_cur)
udev_list_entry_insert_before(entry, list->entries[i]);
else
udev_list_entry_append(entry, list);
@@ -200,9 +186,8 @@ struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *
return entry;
}
-void udev_list_entry_delete(struct udev_list_entry *entry)
-{
- if (entry->list->entries != NULL) {
+static void udev_list_entry_delete(struct udev_list_entry *entry) {
+ if (entry->list->entries) {
int i;
struct udev_list *list = entry->list;
@@ -221,8 +206,12 @@ void udev_list_entry_delete(struct udev_list_entry *entry)
free(entry);
}
-void udev_list_cleanup(struct udev_list *list)
-{
+#define udev_list_entry_foreach_safe(entry, tmp, first) \
+ for (entry = first, tmp = udev_list_entry_get_next(entry); \
+ entry; \
+ entry = tmp, tmp = udev_list_entry_get_next(tmp))
+
+void udev_list_cleanup(struct udev_list *list) {
struct udev_list_entry *entry_loop;
struct udev_list_entry *entry_tmp;
@@ -233,8 +222,7 @@ void udev_list_cleanup(struct udev_list *list)
udev_list_entry_delete(entry_loop);
}
-struct udev_list_entry *udev_list_get_entry(struct udev_list *list)
-{
+struct udev_list_entry *udev_list_get_entry(struct udev_list *list) {
if (udev_list_node_is_empty(&list->node))
return NULL;
return list_node_to_entry(list->node.next);
@@ -248,11 +236,10 @@ struct udev_list_entry *udev_list_get_entry(struct udev_list *list)
*
* Returns: udev_list_entry, #NULL if no more entries are available.
*/
-_public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry)
-{
+_public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) {
struct udev_list_node *next;
- if (list_entry == NULL)
+ if (!list_entry)
return NULL;
next = list_entry->node.next;
/* empty list or no more entries */
@@ -270,11 +257,10 @@ _public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry
*
* Returns: udev_list_entry, #NULL if no matching entry is found.
*/
-_public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name)
-{
+_public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) {
int i;
- if (list_entry == NULL)
+ if (!list_entry)
return NULL;
if (!list_entry->list->unique)
@@ -294,9 +280,8 @@ _public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_en
*
* Returns: the name string of this entry.
*/
-_public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry)
-{
- if (list_entry == NULL)
+_public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) {
+ if (!list_entry)
return NULL;
return list_entry->name;
}
@@ -309,23 +294,8 @@ _public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry
*
* Returns: the value string of this entry.
*/
-_public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry)
-{
- if (list_entry == NULL)
+_public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) {
+ if (!list_entry)
return NULL;
return list_entry->value;
}
-
-int udev_list_entry_get_num(struct udev_list_entry *list_entry)
-{
- if (list_entry == NULL)
- return -EINVAL;
- return list_entry->num;
-}
-
-void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num)
-{
- if (list_entry == NULL)
- return;
- list_entry->num = num;
-}
diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c
index bfad50c891..70036f5136 100644
--- a/src/libudev/libudev-monitor.c
+++ b/src/libudev/libudev-monitor.c
@@ -1,26 +1,15 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
-#include <linux/filter.h>
-#include <linux/netlink.h>
#include <poll.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <unistd.h>
#include "libudev.h"
#include "alloc-util.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "format-util.h"
-#include "libudev-private.h"
-#include "missing.h"
-#include "mount-util.h"
-#include "socket-util.h"
+#include "device-monitor-private.h"
+#include "device-private.h"
+#include "device-util.h"
+#include "libudev-device-internal.h"
#include "string-util.h"
/**
@@ -37,178 +26,18 @@
*/
struct udev_monitor {
struct udev *udev;
- int refcount;
- int sock;
- union sockaddr_union snl;
- union sockaddr_union snl_trusted_sender;
- union sockaddr_union snl_destination;
- socklen_t addrlen;
- struct udev_list filter_subsystem_list;
- struct udev_list filter_tag_list;
- bool bound;
+ unsigned n_ref;
+ sd_device_monitor *monitor;
};
-enum udev_monitor_netlink_group {
- UDEV_MONITOR_NONE,
- UDEV_MONITOR_KERNEL,
- UDEV_MONITOR_UDEV,
-};
-
-#define UDEV_MONITOR_MAGIC 0xfeedcafe
-struct udev_monitor_netlink_header {
- /* "libudev" prefix to distinguish libudev and kernel messages */
- char prefix[8];
- /*
- * magic to protect against daemon <-> library message format mismatch
- * used in the kernel from socket filter rules; needs to be stored in network order
- */
- unsigned int magic;
- /* total length of header structure known to the sender */
- unsigned int header_size;
- /* properties string buffer */
- unsigned int properties_off;
- unsigned int properties_len;
- /*
- * hashes of primary device properties strings, to let libudev subscribers
- * use in-kernel socket filters; values need to be stored in network order
- */
- unsigned int filter_subsystem_hash;
- unsigned int filter_devtype_hash;
- unsigned int filter_tag_bloom_hi;
- unsigned int filter_tag_bloom_lo;
-};
-
-static struct udev_monitor *udev_monitor_new(struct udev *udev) {
- struct udev_monitor *udev_monitor;
-
- udev_monitor = new0(struct udev_monitor, 1);
- if (udev_monitor == NULL) {
- errno = ENOMEM;
- return NULL;
- }
- udev_monitor->refcount = 1;
- udev_monitor->udev = udev;
- udev_list_init(udev, &udev_monitor->filter_subsystem_list, false);
- udev_list_init(udev, &udev_monitor->filter_tag_list, true);
- return udev_monitor;
-}
-
-/* we consider udev running when /dev is on devtmpfs */
-static bool udev_has_devtmpfs(struct udev *udev) {
-
- _cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX], *e;
- int mount_id, r;
-
- r = path_get_mnt_id("/dev", &mount_id);
- if (r < 0) {
- if (r != -EOPNOTSUPP)
- log_debug_errno(r, "name_to_handle_at on /dev: %m");
-
- return false;
- }
-
- f = fopen("/proc/self/mountinfo", "re");
- if (!f)
- return false;
-
- FOREACH_LINE(line, f, return false) {
- int mid;
-
- if (sscanf(line, "%i", &mid) != 1)
- continue;
-
- if (mid != mount_id)
- continue;
-
- e = strstr(line, " - ");
- if (!e)
- continue;
-
- /* accept any name that starts with the currently expected type */
- if (startswith(e + 3, "devtmpfs"))
- return true;
- }
-
- return false;
-}
-
-static void monitor_set_nl_address(struct udev_monitor *udev_monitor) {
- union sockaddr_union snl;
- socklen_t addrlen;
- int r;
-
- assert(udev_monitor);
-
- /* get the address the kernel has assigned us
- * it is usually, but not necessarily the pid
- */
- addrlen = sizeof(struct sockaddr_nl);
- r = getsockname(udev_monitor->sock, &snl.sa, &addrlen);
- if (r >= 0)
- udev_monitor->snl.nl.nl_pid = snl.nl.nl_pid;
-}
-
-struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd) {
- struct udev_monitor *udev_monitor;
- unsigned int group;
-
- if (udev == NULL) {
- errno = EINVAL;
- return NULL;
- }
-
- if (name == NULL)
- group = UDEV_MONITOR_NONE;
- else if (streq(name, "udev")) {
- /*
- * We do not support subscribing to uevents if no instance of
- * udev is running. Uevents would otherwise broadcast the
- * processing data of the host into containers, which is not
- * desired.
- *
- * Containers will currently not get any udev uevents, until
- * a supporting infrastructure is available.
- *
- * We do not set a netlink multicast group here, so the socket
- * will not receive any messages.
- */
- if (access("/run/udev/control", F_OK) < 0 && !udev_has_devtmpfs(udev)) {
- log_debug("the udev service seems not to be active, disable the monitor");
- group = UDEV_MONITOR_NONE;
- } else
- group = UDEV_MONITOR_UDEV;
- } else if (streq(name, "kernel"))
- group = UDEV_MONITOR_KERNEL;
- else {
- errno = EINVAL;
- return NULL;
- }
-
- udev_monitor = udev_monitor_new(udev);
- if (udev_monitor == NULL)
- return NULL;
-
- if (fd < 0) {
- udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
- if (udev_monitor->sock < 0) {
- log_debug_errno(errno, "error getting socket: %m");
- return mfree(udev_monitor);
- }
- } else {
- udev_monitor->bound = true;
- udev_monitor->sock = fd;
- monitor_set_nl_address(udev_monitor);
- }
-
- udev_monitor->snl.nl.nl_family = AF_NETLINK;
- udev_monitor->snl.nl.nl_groups = group;
-
- /* default destination for sending */
- udev_monitor->snl_destination.nl.nl_family = AF_NETLINK;
- udev_monitor->snl_destination.nl.nl_groups = UDEV_MONITOR_UDEV;
-
- return udev_monitor;
+static MonitorNetlinkGroup monitor_netlink_group_from_string(const char *name) {
+ if (!name)
+ return MONITOR_GROUP_NONE;
+ if (streq(name, "udev"))
+ return MONITOR_GROUP_UDEV;
+ if (streq(name, "kernel"))
+ return MONITOR_GROUP_KERNEL;
+ return _MONITOR_NETLINK_GROUP_INVALID;
}
/**
@@ -234,30 +63,30 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c
* Returns: a new udev monitor, or #NULL, in case of an error
**/
_public_ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) {
- return udev_monitor_new_from_netlink_fd(udev, name, -1);
-}
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
+ struct udev_monitor *udev_monitor;
+ MonitorNetlinkGroup g;
+ int r;
-static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i,
- unsigned short code, unsigned int data)
-{
- struct sock_filter *ins = &inss[*i];
+ g = monitor_netlink_group_from_string(name);
+ if (g < 0)
+ return_with_errno(NULL, EINVAL);
- ins->code = code;
- ins->k = data;
- (*i)++;
-}
+ r = device_monitor_new_full(&m, g, -1);
+ if (r < 0)
+ return_with_errno(NULL, r);
+
+ udev_monitor = new(struct udev_monitor, 1);
+ if (!udev_monitor)
+ return_with_errno(NULL, ENOMEM);
+
+ *udev_monitor = (struct udev_monitor) {
+ .udev = udev,
+ .n_ref = 1,
+ .monitor = TAKE_PTR(m),
+ };
-static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i,
- unsigned short code, unsigned int data,
- unsigned short jt, unsigned short jf)
-{
- struct sock_filter *ins = &inss[*i];
-
- ins->code = code;
- ins->jt = jt;
- ins->jf = jf;
- ins->k = data;
- (*i)++;
+ return udev_monitor;
}
/**
@@ -269,109 +98,10 @@ static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i,
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
-{
- struct sock_filter ins[512];
- struct sock_fprog filter;
- unsigned int i;
- struct udev_list_entry *list_entry;
- int err;
-
- if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL &&
- udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL)
- return 0;
-
- memzero(ins, sizeof(ins));
- i = 0;
-
- /* load magic in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic));
- /* jump if magic matches */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0);
- /* wrong magic, pass packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
-
- if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL) {
- int tag_matches;
-
- /* count tag matches, to calculate end of tag match block */
- tag_matches = 0;
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list))
- tag_matches++;
-
- /* add all tags matches */
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) {
- uint64_t tag_bloom_bits = util_string_bloom64(udev_list_entry_get_name(list_entry));
- uint32_t tag_bloom_hi = tag_bloom_bits >> 32;
- uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff;
-
- /* load device bloom bits in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_hi));
- /* clear bits (tag bits & bloom bits) */
- bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi);
- /* jump to next tag if it does not match */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3);
-
- /* load device bloom bits in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_lo));
- /* clear bits (tag bits & bloom bits) */
- bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo);
- /* jump behind end of tag match block if tag matches */
- tag_matches--;
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0);
- }
-
- /* nothing matched, drop packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
- }
-
- /* add all subsystem matches */
- if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) != NULL) {
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) {
- unsigned int hash = util_string_hash32(udev_list_entry_get_name(list_entry));
-
- /* load device subsystem value in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem_hash));
- if (udev_list_entry_get_value(list_entry) == NULL) {
- /* jump if subsystem does not match */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
- } else {
- /* jump if subsystem does not match */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3);
-
- /* load device devtype value in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_devtype_hash));
- /* jump if value does not match */
- hash = util_string_hash32(udev_list_entry_get_value(list_entry));
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
- }
-
- /* matched, pass packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
-
- if (i+1 >= ELEMENTSOF(ins))
- return -E2BIG;
- }
-
- /* nothing matched, drop packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
- }
-
- /* matched, pass packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
-
- /* install filter */
- memzero(&filter, sizeof(filter));
- filter.len = i;
- filter.filter = ins;
- err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
- return err < 0 ? -errno : 0;
-}
+_public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor) {
+ assert_return(udev_monitor, -EINVAL);
-int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender)
-{
- udev_monitor->snl_trusted_sender.nl.nl_pid = sender->snl.nl.nl_pid;
- return 0;
+ return sd_device_monitor_filter_update(udev_monitor->monitor);
}
/**
@@ -382,31 +112,10 @@ int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
-{
- int err = 0;
- const int on = 1;
-
- udev_monitor_filter_update(udev_monitor);
-
- if (!udev_monitor->bound) {
- err = bind(udev_monitor->sock,
- &udev_monitor->snl.sa, sizeof(struct sockaddr_nl));
- if (err == 0)
- udev_monitor->bound = true;
- }
-
- if (err >= 0)
- monitor_set_nl_address(udev_monitor);
- else
- return log_debug_errno(errno, "bind failed: %m");
+_public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) {
+ assert_return(udev_monitor, -EINVAL);
- /* enable receiving of sender credentials */
- err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
- if (err < 0)
- log_debug_errno(errno, "setting SO_PASSCRED failed: %m");
-
- return 0;
+ return device_monitor_enable_receiving(udev_monitor->monitor);
}
/**
@@ -419,23 +128,17 @@ _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
*
* Returns: 0 on success, otherwise -1 on error.
*/
-_public_ int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
-{
- if (udev_monitor == NULL)
- return -EINVAL;
- if (setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)) < 0)
- return -errno;
-
- return 0;
+_public_ int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) {
+ assert_return(udev_monitor, -EINVAL);
+
+ return sd_device_monitor_set_receive_buffer_size(udev_monitor->monitor, (size_t) size);
}
-int udev_monitor_disconnect(struct udev_monitor *udev_monitor)
-{
- int err;
+static struct udev_monitor *udev_monitor_free(struct udev_monitor *udev_monitor) {
+ assert(udev_monitor);
- err = close(udev_monitor->sock);
- udev_monitor->sock = -1;
- return err < 0 ? -errno : 0;
+ sd_device_monitor_unref(udev_monitor->monitor);
+ return mfree(udev_monitor);
}
/**
@@ -446,13 +149,6 @@ int udev_monitor_disconnect(struct udev_monitor *udev_monitor)
*
* Returns: the passed udev monitor
**/
-_public_ struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
-{
- if (udev_monitor == NULL)
- return NULL;
- udev_monitor->refcount++;
- return udev_monitor;
-}
/**
* udev_monitor_unref:
@@ -464,19 +160,7 @@ _public_ struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor
*
* Returns: #NULL
**/
-_public_ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor)
-{
- if (udev_monitor == NULL)
- return NULL;
- udev_monitor->refcount--;
- if (udev_monitor->refcount > 0)
- return NULL;
- if (udev_monitor->sock >= 0)
- close(udev_monitor->sock);
- udev_list_cleanup(&udev_monitor->filter_subsystem_list);
- udev_list_cleanup(&udev_monitor->filter_tag_list);
- return mfree(udev_monitor);
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(struct udev_monitor, udev_monitor, udev_monitor_free);
/**
* udev_monitor_get_udev:
@@ -486,10 +170,9 @@ _public_ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monit
*
* Returns: the udev library context
**/
-_public_ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
-{
- if (udev_monitor == NULL)
- return NULL;
+_public_ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) {
+ assert_return(udev_monitor, NULL);
+
return udev_monitor->udev;
}
@@ -501,49 +184,45 @@ _public_ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
*
* Returns: the socket file descriptor
**/
-_public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
-{
- if (udev_monitor == NULL)
- return -EINVAL;
- return udev_monitor->sock;
+_public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor) {
+ assert_return(udev_monitor, -EINVAL);
+
+ return device_monitor_get_fd(udev_monitor->monitor);
}
-static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device)
-{
- struct udev_list_entry *list_entry;
-
- if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL)
- goto tag;
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) {
- const char *subsys = udev_list_entry_get_name(list_entry);
- const char *dsubsys = udev_device_get_subsystem(udev_device);
- const char *devtype;
- const char *ddevtype;
-
- if (!streq(dsubsys, subsys))
- continue;
-
- devtype = udev_list_entry_get_value(list_entry);
- if (devtype == NULL)
- goto tag;
- ddevtype = udev_device_get_devtype(udev_device);
- if (ddevtype == NULL)
- continue;
- if (streq(ddevtype, devtype))
- goto tag;
- }
- return 0;
+static int udev_monitor_receive_sd_device(struct udev_monitor *udev_monitor, sd_device **ret) {
+ struct pollfd pfd;
+ int r;
+
+ assert(udev_monitor);
+ assert(ret);
-tag:
- if (udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL)
- return 1;
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) {
- const char *tag = udev_list_entry_get_name(list_entry);
+ pfd = (struct pollfd) {
+ .fd = device_monitor_get_fd(udev_monitor->monitor),
+ .events = POLLIN,
+ };
- if (udev_device_has_tag(udev_device, tag))
- return 1;
+ for (;;) {
+ /* r == 0 means a device is received but it does not pass the current filter. */
+ r = device_monitor_receive_device(udev_monitor->monitor, ret);
+ if (r != 0)
+ return r;
+
+ for (;;) {
+ /* wait next message */
+ r = poll(&pfd, 1, 0);
+ if (r < 0) {
+ if (IN_SET(errno, EINTR, EAGAIN))
+ continue;
+
+ return -errno;
+ } else if (r == 0)
+ return -EAGAIN;
+
+ /* receive next message */
+ break;
+ }
}
- return 0;
}
/**
@@ -565,219 +244,17 @@ tag:
*
* Returns: a new udev device, or #NULL, in case of an error
**/
-_public_ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor)
-{
- struct udev_device *udev_device;
- struct msghdr smsg;
- struct iovec iov;
- char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
- struct cmsghdr *cmsg;
- union sockaddr_union snl;
- struct ucred *cred;
- union {
- struct udev_monitor_netlink_header nlh;
- char raw[8192];
- } buf;
- ssize_t buflen;
- ssize_t bufpos;
- bool is_initialized = false;
-
-retry:
- if (udev_monitor == NULL) {
- errno = EINVAL;
- return NULL;
- }
- iov.iov_base = &buf;
- iov.iov_len = sizeof(buf);
- memzero(&smsg, sizeof(struct msghdr));
- smsg.msg_iov = &iov;
- smsg.msg_iovlen = 1;
- smsg.msg_control = cred_msg;
- smsg.msg_controllen = sizeof(cred_msg);
- smsg.msg_name = &snl;
- smsg.msg_namelen = sizeof(snl);
-
- buflen = recvmsg(udev_monitor->sock, &smsg, 0);
- if (buflen < 0) {
- if (errno != EINTR)
- log_debug("unable to receive message");
- return NULL;
- }
-
- if (buflen < 32 || (smsg.msg_flags & MSG_TRUNC)) {
- log_debug("invalid message length");
- errno = EINVAL;
- return NULL;
- }
-
- if (snl.nl.nl_groups == 0) {
- /* unicast message, check if we trust the sender */
- if (udev_monitor->snl_trusted_sender.nl.nl_pid == 0 ||
- snl.nl.nl_pid != udev_monitor->snl_trusted_sender.nl.nl_pid) {
- log_debug("unicast netlink message ignored");
- errno = EAGAIN;
- return NULL;
- }
- } else if (snl.nl.nl_groups == UDEV_MONITOR_KERNEL) {
- if (snl.nl.nl_pid > 0) {
- log_debug("multicast kernel netlink message from PID %"PRIu32" ignored",
- snl.nl.nl_pid);
- errno = EAGAIN;
- return NULL;
- }
- }
-
- cmsg = CMSG_FIRSTHDR(&smsg);
- if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
- log_debug("no sender credentials received, message ignored");
- errno = EAGAIN;
- return NULL;
- }
-
- cred = (struct ucred *)CMSG_DATA(cmsg);
- if (cred->uid != 0) {
- log_debug("sender uid="UID_FMT", message ignored", cred->uid);
- errno = EAGAIN;
- return NULL;
- }
-
- if (memcmp(buf.raw, "libudev", 8) == 0) {
- /* udev message needs proper version magic */
- if (buf.nlh.magic != htobe32(UDEV_MONITOR_MAGIC)) {
- log_debug("unrecognized message signature (%x != %x)",
- buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC));
- errno = EAGAIN;
- return NULL;
- }
- if (buf.nlh.properties_off+32 > (size_t)buflen) {
- log_debug("message smaller than expected (%u > %zd)",
- buf.nlh.properties_off+32, buflen);
- errno = EAGAIN;
- return NULL;
- }
-
- bufpos = buf.nlh.properties_off;
-
- /* devices received from udev are always initialized */
- is_initialized = true;
- } else {
- /* kernel message with header */
- bufpos = strlen(buf.raw) + 1;
- if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) {
- log_debug("invalid message length");
- errno = EAGAIN;
- return NULL;
- }
-
- /* check message header */
- if (strstr(buf.raw, "@/") == NULL) {
- log_debug("unrecognized message header");
- errno = EAGAIN;
- return NULL;
- }
- }
-
- udev_device = udev_device_new_from_nulstr(udev_monitor->udev, &buf.raw[bufpos], buflen - bufpos);
- if (!udev_device) {
- log_debug_errno(errno, "could not create device: %m");
- return NULL;
- }
-
- if (is_initialized)
- udev_device_set_is_initialized(udev_device);
-
- /* skip device, if it does not pass the current filter */
- if (!passes_filter(udev_monitor, udev_device)) {
- struct pollfd pfd[1];
- int rc;
-
- udev_device_unref(udev_device);
-
- /* if something is queued, get next device */
- pfd[0].fd = udev_monitor->sock;
- pfd[0].events = POLLIN;
- rc = poll(pfd, 1, 0);
- if (rc > 0)
- goto retry;
-
- errno = EAGAIN;
- return NULL;
- }
-
- return udev_device;
-}
-
-int udev_monitor_send_device(struct udev_monitor *udev_monitor,
- struct udev_monitor *destination, struct udev_device *udev_device)
-{
- const char *buf, *val;
- ssize_t blen, count;
- struct udev_monitor_netlink_header nlh = {
- .prefix = "libudev",
- .magic = htobe32(UDEV_MONITOR_MAGIC),
- .header_size = sizeof nlh,
- };
- struct iovec iov[2] = {
- { .iov_base = &nlh, .iov_len = sizeof nlh },
- };
- struct msghdr smsg = {
- .msg_iov = iov,
- .msg_iovlen = 2,
- };
- struct udev_list_entry *list_entry;
- uint64_t tag_bloom_bits;
-
- blen = udev_device_get_properties_monitor_buf(udev_device, &buf);
- if (blen < 32) {
- log_debug("device buffer is too small to contain a valid device");
- return -EINVAL;
- }
+_public_ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ int r;
- /* fill in versioned header */
- val = udev_device_get_subsystem(udev_device);
- nlh.filter_subsystem_hash = htobe32(util_string_hash32(val));
-
- val = udev_device_get_devtype(udev_device);
- if (val != NULL)
- nlh.filter_devtype_hash = htobe32(util_string_hash32(val));
-
- /* add tag bloom filter */
- tag_bloom_bits = 0;
- udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))
- tag_bloom_bits |= util_string_bloom64(udev_list_entry_get_name(list_entry));
- if (tag_bloom_bits > 0) {
- nlh.filter_tag_bloom_hi = htobe32(tag_bloom_bits >> 32);
- nlh.filter_tag_bloom_lo = htobe32(tag_bloom_bits & 0xffffffff);
- }
+ assert_return(udev_monitor, NULL);
- /* add properties list */
- nlh.properties_off = iov[0].iov_len;
- nlh.properties_len = blen;
- iov[1].iov_base = (char *)buf;
- iov[1].iov_len = blen;
-
- /*
- * Use custom address for target, or the default one.
- *
- * If we send to a multicast group, we will get
- * ECONNREFUSED, which is expected.
- */
- if (destination)
- smsg.msg_name = &destination->snl;
- else
- smsg.msg_name = &udev_monitor->snl_destination;
- smsg.msg_namelen = sizeof(struct sockaddr_nl);
- count = sendmsg(udev_monitor->sock, &smsg, 0);
- if (count < 0) {
- if (!destination && errno == ECONNREFUSED) {
- log_debug("passed device to netlink monitor %p", udev_monitor);
- return 0;
- } else
- return -errno;
- }
+ r = udev_monitor_receive_sd_device(udev_monitor, &device);
+ if (r < 0)
+ return_with_errno(NULL, r);
- log_debug("passed %zi byte device to netlink monitor %p", count, udev_monitor);
- return count;
+ return udev_device_new(udev_monitor->udev, device);
}
/**
@@ -793,15 +270,10 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor,
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype)
-{
- if (udev_monitor == NULL)
- return -EINVAL;
- if (subsystem == NULL)
- return -EINVAL;
- if (udev_list_entry_add(&udev_monitor->filter_subsystem_list, subsystem, devtype) == NULL)
- return -ENOMEM;
- return 0;
+_public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) {
+ assert_return(udev_monitor, -EINVAL);
+
+ return sd_device_monitor_filter_add_match_subsystem_devtype(udev_monitor->monitor, subsystem, devtype);
}
/**
@@ -816,15 +288,10 @@ _public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag)
-{
- if (udev_monitor == NULL)
- return -EINVAL;
- if (tag == NULL)
- return -EINVAL;
- if (udev_list_entry_add(&udev_monitor->filter_tag_list, tag, NULL) == NULL)
- return -ENOMEM;
- return 0;
+_public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) {
+ assert_return(udev_monitor, -EINVAL);
+
+ return sd_device_monitor_filter_add_match_tag(udev_monitor->monitor, tag);
}
/**
@@ -835,13 +302,8 @@ _public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor
*
* Returns: 0 on success, otherwise a negative error value.
*/
-_public_ int udev_monitor_filter_remove(struct udev_monitor *udev_monitor)
-{
- static const struct sock_fprog filter = { 0, NULL };
-
- udev_list_cleanup(&udev_monitor->filter_subsystem_list);
- if (setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0)
- return -errno;
+_public_ int udev_monitor_filter_remove(struct udev_monitor *udev_monitor) {
+ assert_return(udev_monitor, -EINVAL);
- return 0;
+ return sd_device_monitor_filter_remove(udev_monitor->monitor);
}
diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h
deleted file mode 100644
index 384be98e79..0000000000
--- a/src/libudev/libudev-private.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-#pragma once
-
-
-#include <signal.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "libudev.h"
-
-#include "macro.h"
-#include "mkdir.h"
-#include "strxcpyx.h"
-#include "util.h"
-
-#define READ_END 0
-#define WRITE_END 1
-
-/* libudev.c */
-int udev_get_rules_path(struct udev *udev, char **path[], usec_t *ts_usec[]);
-
-/* libudev-device.c */
-struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen);
-struct udev_device *udev_device_new_from_synthetic_event(struct udev *udev, const char *syspath, const char *action);
-struct udev_device *udev_device_shallow_clone(struct udev_device *old_device);
-struct udev_device *udev_device_clone_with_db(struct udev_device *old_device);
-int udev_device_copy_properties(struct udev_device *dst, struct udev_device *src);
-mode_t udev_device_get_devnode_mode(struct udev_device *udev_device);
-uid_t udev_device_get_devnode_uid(struct udev_device *udev_device);
-gid_t udev_device_get_devnode_gid(struct udev_device *udev_device);
-int udev_device_rename(struct udev_device *udev_device, const char *new_name);
-int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink);
-void udev_device_cleanup_devlinks_list(struct udev_device *udev_device);
-int udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value);
-char **udev_device_get_properties_envp(struct udev_device *udev_device);
-ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf);
-const char *udev_device_get_devpath_old(struct udev_device *udev_device);
-const char *udev_device_get_id_filename(struct udev_device *udev_device);
-void udev_device_set_is_initialized(struct udev_device *udev_device);
-int udev_device_add_tag(struct udev_device *udev_device, const char *tag);
-void udev_device_remove_tag(struct udev_device *udev_device, const char *tag);
-void udev_device_cleanup_tags_list(struct udev_device *udev_device);
-usec_t udev_device_get_usec_initialized(struct udev_device *udev_device);
-void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *old_device);
-int udev_device_get_devlink_priority(struct udev_device *udev_device);
-int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio);
-int udev_device_get_watch_handle(struct udev_device *udev_device);
-int udev_device_set_watch_handle(struct udev_device *udev_device, int handle);
-int udev_device_get_ifindex(struct udev_device *udev_device);
-void udev_device_set_info_loaded(struct udev_device *device);
-bool udev_device_get_db_persist(struct udev_device *udev_device);
-void udev_device_set_db_persist(struct udev_device *udev_device);
-void udev_device_read_db(struct udev_device *udev_device);
-
-/* libudev-device-private.c */
-int udev_device_update_db(struct udev_device *udev_device);
-int udev_device_delete_db(struct udev_device *udev_device);
-int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add);
-
-/* libudev-monitor.c - netlink/unix socket communication */
-int udev_monitor_disconnect(struct udev_monitor *udev_monitor);
-int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender);
-int udev_monitor_send_device(struct udev_monitor *udev_monitor,
- struct udev_monitor *destination, struct udev_device *udev_device);
-struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd);
-
-/* libudev-list.c */
-struct udev_list_node {
- struct udev_list_node *next, *prev;
-};
-struct udev_list {
- struct udev *udev;
- struct udev_list_node node;
- struct udev_list_entry **entries;
- unsigned int entries_cur;
- unsigned int entries_max;
- bool unique;
-};
-void udev_list_node_init(struct udev_list_node *list);
-int udev_list_node_is_empty(struct udev_list_node *list);
-void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list);
-void udev_list_node_remove(struct udev_list_node *entry);
-#define udev_list_node_foreach(node, list) \
- for (node = (list)->next; \
- node != list; \
- node = (node)->next)
-#define udev_list_node_foreach_safe(node, tmp, list) \
- for (node = (list)->next, tmp = (node)->next; \
- node != list; \
- node = tmp, tmp = (tmp)->next)
-void udev_list_init(struct udev *udev, struct udev_list *list, bool unique);
-void udev_list_cleanup(struct udev_list *list);
-struct udev_list_entry *udev_list_get_entry(struct udev_list *list);
-struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value);
-void udev_list_entry_delete(struct udev_list_entry *entry);
-int udev_list_entry_get_num(struct udev_list_entry *list_entry);
-void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num);
-#define udev_list_entry_foreach_safe(entry, tmp, first) \
- for (entry = first, tmp = udev_list_entry_get_next(entry); \
- entry != NULL; \
- entry = tmp, tmp = udev_list_entry_get_next(tmp))
-
-/* libudev-queue.c */
-unsigned long long int udev_get_kernel_seqnum(struct udev *udev);
-int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum);
-ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size);
-ssize_t udev_queue_skip_devpath(FILE *queue_file);
-
-/* libudev-queue-private.c */
-struct udev_queue_export *udev_queue_export_new(struct udev *udev);
-struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export);
-void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export);
-int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device);
-int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device);
-
-/* libudev-util.c */
-#define UTIL_PATH_SIZE 1024
-#define UTIL_NAME_SIZE 512
-#define UTIL_LINE_SIZE 16384
-#define UDEV_ALLOWED_CHARS_INPUT "/ $%?,"
-int util_log_priority(const char *priority);
-size_t util_path_encode(const char *src, char *dest, size_t size);
-int util_replace_whitespace(const char *str, char *to, size_t len);
-int util_replace_chars(char *str, const char *white);
-unsigned int util_string_hash32(const char *key);
-uint64_t util_string_bloom64(const char *str);
-
-/* libudev-util-private.c */
-int util_resolve_subsys_kernel(struct udev *udev, const char *string, char *result, size_t maxsize, int read_value);
diff --git a/src/libudev/libudev-queue.c b/src/libudev/libudev-queue.c
index 14e8a67e08..4e055bbc37 100644
--- a/src/libudev/libudev-queue.c
+++ b/src/libudev/libudev-queue.c
@@ -9,10 +9,11 @@
#include <sys/inotify.h>
#include <unistd.h>
+#include "libudev.h"
+
#include "alloc-util.h"
#include "fd-util.h"
#include "io-util.h"
-#include "libudev-private.h"
/**
* SECTION:libudev-queue
@@ -28,7 +29,7 @@
*/
struct udev_queue {
struct udev *udev;
- int refcount;
+ unsigned n_ref;
int fd;
};
@@ -41,27 +42,29 @@ struct udev_queue {
*
* Returns: the udev queue context, or #NULL on error.
**/
-_public_ struct udev_queue *udev_queue_new(struct udev *udev)
-{
+_public_ struct udev_queue *udev_queue_new(struct udev *udev) {
struct udev_queue *udev_queue;
- if (udev == NULL) {
- errno = EINVAL;
- return NULL;
- }
+ udev_queue = new(struct udev_queue, 1);
+ if (!udev_queue)
+ return_with_errno(NULL, ENOMEM);
- udev_queue = new0(struct udev_queue, 1);
- if (udev_queue == NULL) {
- errno = ENOMEM;
- return NULL;
- }
+ *udev_queue = (struct udev_queue) {
+ .udev = udev,
+ .n_ref = 1,
+ .fd = -1,
+ };
- udev_queue->refcount = 1;
- udev_queue->udev = udev;
- udev_queue->fd = -1;
return udev_queue;
}
+static struct udev_queue *udev_queue_free(struct udev_queue *udev_queue) {
+ assert(udev_queue);
+
+ safe_close(udev_queue->fd);
+ return mfree(udev_queue);
+}
+
/**
* udev_queue_ref:
* @udev_queue: udev queue context
@@ -70,14 +73,6 @@ _public_ struct udev_queue *udev_queue_new(struct udev *udev)
*
* Returns: the same udev queue context.
**/
-_public_ struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue)
-{
- if (udev_queue == NULL)
- return NULL;
-
- udev_queue->refcount++;
- return udev_queue;
-}
/**
* udev_queue_unref:
@@ -88,19 +83,7 @@ _public_ struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue)
*
* Returns: #NULL
**/
-_public_ struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue)
-{
- if (udev_queue == NULL)
- return NULL;
-
- udev_queue->refcount--;
- if (udev_queue->refcount > 0)
- return NULL;
-
- safe_close(udev_queue->fd);
-
- return mfree(udev_queue);
-}
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(struct udev_queue, udev_queue, udev_queue_free);
/**
* udev_queue_get_udev:
@@ -110,12 +93,9 @@ _public_ struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue)
*
* Returns: the udev library context.
**/
-_public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
-{
- if (udev_queue == NULL) {
- errno = EINVAL;
- return NULL;
- }
+_public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue) {
+ assert_return_errno(udev_queue, NULL, EINVAL);
+
return udev_queue->udev;
}
@@ -127,8 +107,7 @@ _public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
*
* Returns: 0.
**/
-_public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
-{
+_public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) {
return 0;
}
@@ -140,8 +119,7 @@ _public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *
*
* Returns: 0.
**/
-_public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
-{
+_public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) {
return 0;
}
@@ -153,8 +131,7 @@ _public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *ud
*
* Returns: a flag indicating if udev is active.
**/
-_public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
-{
+_public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue) {
return access("/run/udev/control", F_OK) >= 0;
}
@@ -166,8 +143,7 @@ _public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
*
* Returns: a flag indicating if udev is currently handling events.
**/
-_public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
-{
+_public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue) {
return access("/run/udev/queue", F_OK) < 0;
}
@@ -183,8 +159,7 @@ _public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
* Returns: a flag indicating if udev is currently handling events.
**/
_public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
- unsigned long long int start, unsigned long long int end)
-{
+ unsigned long long int start, unsigned long long int end) {
return udev_queue_get_queue_is_empty(udev_queue);
}
@@ -198,8 +173,7 @@ _public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_
*
* Returns: a flag indicating if udev is currently handling events.
**/
-_public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
-{
+_public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) {
return udev_queue_get_queue_is_empty(udev_queue);
}
@@ -211,10 +185,8 @@ _public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, un
*
* Returns: NULL.
**/
-_public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
-{
- errno = ENODATA;
- return NULL;
+_public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) {
+ return_with_errno(NULL, ENODATA);
}
/**
@@ -224,8 +196,9 @@ _public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_qu
* Returns: a file descriptor to watch for a queue to become empty.
*/
_public_ int udev_queue_get_fd(struct udev_queue *udev_queue) {
- int fd;
- int r;
+ _cleanup_close_ int fd = -1;
+
+ assert_return(udev_queue, -EINVAL);
if (udev_queue->fd >= 0)
return udev_queue->fd;
@@ -234,15 +207,11 @@ _public_ int udev_queue_get_fd(struct udev_queue *udev_queue) {
if (fd < 0)
return -errno;
- r = inotify_add_watch(fd, "/run/udev" , IN_DELETE);
- if (r < 0) {
- r = -errno;
- close(fd);
- return r;
- }
+ if (inotify_add_watch(fd, "/run/udev" , IN_DELETE) < 0)
+ return -errno;
- udev_queue->fd = fd;
- return fd;
+ udev_queue->fd = TAKE_FD(fd);
+ return udev_queue->fd;
}
/**
@@ -254,7 +223,7 @@ _public_ int udev_queue_get_fd(struct udev_queue *udev_queue) {
_public_ int udev_queue_flush(struct udev_queue *udev_queue) {
int r;
- assert(udev_queue);
+ assert_return(udev_queue, -EINVAL);
if (udev_queue->fd < 0)
return -EINVAL;
diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c
index f80af9705e..f67ab40354 100644
--- a/src/libudev/libudev-util.c
+++ b/src/libudev/libudev-util.c
@@ -2,17 +2,13 @@
#include <ctype.h>
#include <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "libudev.h"
+#include "sd-device.h"
-#include "MurmurHash2.h"
#include "device-nodes.h"
-#include "libudev-private.h"
-#include "syslog-util.h"
+#include "libudev-util.h"
+#include "string-util.h"
+#include "strxcpyx.h"
#include "utf8.h"
/**
@@ -23,31 +19,28 @@
*/
/* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
-int util_resolve_subsys_kernel(struct udev *udev, const char *string,
- char *result, size_t maxsize, int read_value)
-{
- char temp[UTIL_PATH_SIZE];
- char *subsys;
- char *sysname;
- struct udev_device *dev;
- char *attr;
+int util_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value) {
+ char temp[UTIL_PATH_SIZE], *subsys, *sysname, *attr;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ const char *val;
+ int r;
if (string[0] != '[')
- return -1;
+ return -EINVAL;
strscpy(temp, sizeof(temp), string);
subsys = &temp[1];
sysname = strchr(subsys, '/');
- if (sysname == NULL)
- return -1;
+ if (!sysname)
+ return -EINVAL;
sysname[0] = '\0';
sysname = &sysname[1];
attr = strchr(sysname, ']');
- if (attr == NULL)
- return -1;
+ if (!attr)
+ return -EINVAL;
attr[0] = '\0';
attr = &attr[1];
if (attr[0] == '/')
@@ -55,56 +48,39 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
if (attr[0] == '\0')
attr = NULL;
- if (read_value && attr == NULL)
- return -1;
+ if (read_value && !attr)
+ return -EINVAL;
- dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname);
- if (dev == NULL)
- return -1;
+ r = sd_device_new_from_subsystem_sysname(&dev, subsys, sysname);
+ if (r < 0)
+ return r;
if (read_value) {
- const char *val;
-
- val = udev_device_get_sysattr_value(dev, attr);
- if (val != NULL)
- strscpy(result, maxsize, val);
- else
+ r = sd_device_get_sysattr_value(dev, attr, &val);
+ if (r < 0 && r != -ENOENT)
+ return r;
+ if (r == -ENOENT)
result[0] = '\0';
+ else
+ strscpy(result, maxsize, val);
log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
} else {
- size_t l;
- char *s;
-
- s = result;
- l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL);
- if (attr != NULL)
- strpcpyl(&s, l, "/", attr, NULL);
- log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
- }
- udev_device_unref(dev);
- return 0;
-}
-
-int util_log_priority(const char *priority)
-{
- char *endptr;
- int prio;
+ r = sd_device_get_syspath(dev, &val);
+ if (r < 0)
+ return r;
- prio = strtoul(priority, &endptr, 10);
- if (endptr[0] == '\0' || isspace(endptr[0])) {
- if (prio >= 0 && prio <= 7)
- return prio;
- else
- return -ERANGE;
+ strscpyl(result, maxsize, val, attr ? "/" : NULL, attr ?: NULL, NULL);
+ log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, strempty(attr), result);
}
-
- return log_level_from_string(priority);
+ return 0;
}
-size_t util_path_encode(const char *src, char *dest, size_t size)
-{
+size_t util_path_encode(const char *src, char *dest, size_t size) {
size_t i, j;
+ assert(src);
+ assert(dest);
+
for (i = 0, j = 0; src[i] != '\0'; i++) {
if (src[i] == '/') {
if (j+4 >= size) {
@@ -147,39 +123,41 @@ size_t util_path_encode(const char *src, char *dest, size_t size)
* Note this may be called with 'str' == 'to', i.e. to replace whitespace
* in-place in a buffer. This function can handle that situation.
*/
-int util_replace_whitespace(const char *str, char *to, size_t len)
-{
- size_t i, j;
+size_t util_replace_whitespace(const char *str, char *to, size_t len) {
+ bool is_space = false;
+ const char *p = str;
+ size_t j;
- /* strip trailing whitespace */
- len = strnlen(str, len);
- while (len && isspace(str[len-1]))
- len--;
+ assert(str);
+ assert(to);
- /* strip leading whitespace */
- i = 0;
- while ((i < len) && isspace(str[i]))
- i++;
+ p += strspn(p, WHITESPACE);
+
+ for (j = 0; j < len && *p != '\0'; p++) {
+ if (isspace(*p)) {
+ is_space = true;
+ continue;
+ }
+
+ if (is_space) {
+ if (j + 1 >= len)
+ break;
- j = 0;
- while (i < len) {
- /* substitute multiple whitespace with a single '_' */
- if (isspace(str[i])) {
- while (isspace(str[i]))
- i++;
to[j++] = '_';
+ is_space = false;
}
- to[j++] = str[i++];
+ to[j++] = *p;
}
+
to[j] = '\0';
return j;
}
/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
-int util_replace_chars(char *str, const char *white)
-{
- size_t i = 0;
- int replaced = 0;
+size_t util_replace_chars(char *str, const char *white) {
+ size_t i = 0, replaced = 0;
+
+ assert(str);
while (str[i] != '\0') {
int len;
@@ -203,7 +181,7 @@ int util_replace_chars(char *str, const char *white)
}
/* if space is allowed, replace whitespace with ordinary space */
- if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
+ if (isspace(str[i]) && white && strchr(white, ' ')) {
str[i] = ' ';
i++;
replaced++;
@@ -230,25 +208,6 @@ int util_replace_chars(char *str, const char *white)
*
* Returns: 0 if the entire string was copied, non-zero otherwise.
**/
-_public_ int udev_util_encode_string(const char *str, char *str_enc, size_t len)
-{
+_public_ int udev_util_encode_string(const char *str, char *str_enc, size_t len) {
return encode_devnode_name(str, str_enc, len);
}
-
-unsigned int util_string_hash32(const char *str)
-{
- return MurmurHash2(str, strlen(str), 0);
-}
-
-/* get a bunch of bit numbers out of the hash, and set the bits in our bit field */
-uint64_t util_string_bloom64(const char *str)
-{
- uint64_t bits = 0;
- unsigned int hash = util_string_hash32(str);
-
- bits |= 1LLU << (hash & 63);
- bits |= 1LLU << ((hash >> 6) & 63);
- bits |= 1LLU << ((hash >> 12) & 63);
- bits |= 1LLU << ((hash >> 18) & 63);
- return bits;
-}
diff --git a/src/libudev/libudev-util.h b/src/libudev/libudev-util.h
new file mode 100644
index 0000000000..32b626ebc9
--- /dev/null
+++ b/src/libudev/libudev-util.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "libudev.h"
+
+#include "macro.h"
+
+/* libudev-util.c */
+#define UTIL_PATH_SIZE 1024
+#define UTIL_NAME_SIZE 512
+#define UTIL_LINE_SIZE 16384
+#define UDEV_ALLOWED_CHARS_INPUT "/ $%?,"
+size_t util_path_encode(const char *src, char *dest, size_t size);
+size_t util_replace_whitespace(const char *str, char *to, size_t len);
+size_t util_replace_chars(char *str, const char *white);
+int util_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value);
+
+/* Cleanup functions */
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev*, udev_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_device*, udev_device_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_enumerate*, udev_enumerate_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_hwdb*, udev_hwdb_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_queue*, udev_queue_unref);
diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c
index 8af97fe16d..d11e7a9279 100644
--- a/src/libudev/libudev.c
+++ b/src/libudev/libudev.c
@@ -11,16 +11,12 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "libudev-private.h"
#include "missing.h"
#include "string-util.h"
/**
* SECTION:libudev
* @short_description: libudev context
- *
- * The context contains the default values read from the udev config file,
- * and is passed to all library operations.
*/
/**
@@ -29,10 +25,7 @@
* Opaque object representing the library context.
*/
struct udev {
- int refcount;
- void (*log_fn)(struct udev *udev,
- int priority, const char *file, int line, const char *fn,
- const char *format, va_list args);
+ unsigned n_ref;
void *userdata;
};
@@ -46,8 +39,8 @@ struct udev {
* Returns: stored userdata
**/
_public_ void *udev_get_userdata(struct udev *udev) {
- if (udev == NULL)
- return NULL;
+ assert_return(udev, NULL);
+
return udev->userdata;
}
@@ -59,8 +52,9 @@ _public_ void *udev_get_userdata(struct udev *udev) {
* Store custom @userdata in the library context.
**/
_public_ void udev_set_userdata(struct udev *udev, void *userdata) {
- if (udev == NULL)
+ if (!udev)
return;
+
udev->userdata = userdata;
}
@@ -77,12 +71,13 @@ _public_ void udev_set_userdata(struct udev *udev, void *userdata) {
_public_ struct udev *udev_new(void) {
struct udev *udev;
- udev = new0(struct udev, 1);
- if (!udev) {
- errno = ENOMEM;
- return NULL;
- }
- udev->refcount = 1;
+ udev = new(struct udev, 1);
+ if (!udev)
+ return_with_errno(NULL, ENOMEM);
+
+ *udev = (struct udev) {
+ .n_ref = 1,
+ };
return udev;
}
@@ -95,12 +90,7 @@ _public_ struct udev *udev_new(void) {
*
* Returns: the passed udev library context
**/
-_public_ struct udev *udev_ref(struct udev *udev) {
- if (udev == NULL)
- return NULL;
- udev->refcount++;
- return udev;
-}
+DEFINE_PUBLIC_TRIVIAL_REF_FUNC(struct udev, udev);
/**
* udev_unref:
@@ -112,11 +102,17 @@ _public_ struct udev *udev_ref(struct udev *udev) {
* Returns: the passed udev library context if it has still an active reference, or #NULL otherwise.
**/
_public_ struct udev *udev_unref(struct udev *udev) {
- if (udev == NULL)
+ if (!udev)
return NULL;
- udev->refcount--;
- if (udev->refcount > 0)
+
+ assert(udev->n_ref > 0);
+ udev->n_ref--;
+ if (udev->n_ref > 0)
+ /* This is different from our convetion, but let's keep backward
+ * compatibility. So, do not use DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC()
+ * macro to define this function. */
return udev;
+
return mfree(udev);
}
@@ -128,10 +124,11 @@ _public_ struct udev *udev_unref(struct udev *udev) {
* This function is deprecated.
*
**/
-_public_ void udev_set_log_fn(struct udev *udev,
- void (*log_fn)(struct udev *udev,
- int priority, const char *file, int line, const char *fn,
- const char *format, va_list args)) {
+_public_ void udev_set_log_fn(
+ struct udev *udev,
+ void (*log_fn)(struct udev *udev,
+ int priority, const char *file, int line, const char *fn,
+ const char *format, va_list args)) {
return;
}
diff --git a/src/libudev/libudev.h b/src/libudev/libudev.h
index fa4eaeb969..02c2e5e8ed 100644
--- a/src/libudev/libudev.h
+++ b/src/libudev/libudev.h
@@ -24,9 +24,9 @@ struct udev *udev_new(void);
void udev_set_log_fn(struct udev *udev,
void (*log_fn)(struct udev *udev,
int priority, const char *file, int line, const char *fn,
- const char *format, va_list args)) __attribute__ ((deprecated));
-int udev_get_log_priority(struct udev *udev) __attribute__ ((deprecated));
-void udev_set_log_priority(struct udev *udev, int priority) __attribute__ ((deprecated));
+ const char *format, va_list args)) __attribute__((__deprecated__));
+int udev_get_log_priority(struct udev *udev) __attribute__((__deprecated__));
+void udev_set_log_priority(struct udev *udev, int priority) __attribute__((__deprecated__));
void *udev_get_userdata(struct udev *udev);
void udev_set_userdata(struct udev *udev, void *userdata);
@@ -49,7 +49,7 @@ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry);
*/
#define udev_list_entry_foreach(list_entry, first_entry) \
for (list_entry = first_entry; \
- list_entry != NULL; \
+ list_entry; \
list_entry = udev_list_entry_get_next(list_entry))
/*
@@ -90,7 +90,7 @@ const char *udev_device_get_action(struct udev_device *udev_device);
unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device);
unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device);
const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr);
-int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value);
+int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, const char *value);
int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
/*
@@ -153,16 +153,16 @@ struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue);
struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue);
struct udev *udev_queue_get_udev(struct udev_queue *udev_queue);
struct udev_queue *udev_queue_new(struct udev *udev);
-unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) __attribute__ ((deprecated));
-unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) __attribute__ ((deprecated));
+unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) __attribute__((__deprecated__));
+ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) __attribute__((__deprecated__));
int udev_queue_get_udev_is_active(struct udev_queue *udev_queue);
int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue);
-int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) __attribute__ ((deprecated));
+int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) __attribute__((__deprecated__));
int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
- unsigned long long int start, unsigned long long int end) __attribute__ ((deprecated));
+ unsigned long long int start, unsigned long long int end) __attribute__((__deprecated__));
int udev_queue_get_fd(struct udev_queue *udev_queue);
int udev_queue_flush(struct udev_queue *udev_queue);
-struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) __attribute__ ((deprecated));
+struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) __attribute__((__deprecated__));
/*
* udev_hwdb
@@ -173,7 +173,7 @@ struct udev_hwdb;
struct udev_hwdb *udev_hwdb_new(struct udev *udev);
struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb);
struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb);
-struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags);
+struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned flags);
/*
* udev_util
diff --git a/src/libudev/meson.build b/src/libudev/meson.build
index 323c7e97d0..8d86c34189 100644
--- a/src/libudev/meson.build
+++ b/src/libudev/meson.build
@@ -1,17 +1,17 @@
# SPDX-License-Identifier: LGPL-2.1+
libudev_sources = files('''
- libudev-private.h
- libudev-device-internal.h
libudev.c
- libudev-list.c
- libudev-util.c
libudev-device.c
- libudev-device-private.c
+ libudev-device-internal.h
libudev-enumerate.c
+ libudev-hwdb.c
+ libudev-list.c
+ libudev-list-internal.h
libudev-monitor.c
libudev-queue.c
- libudev-hwdb.c
+ libudev-util.c
+ libudev-util.h
'''.split())
############################################################
diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c
index 598b931d03..6b6b32a591 100644
--- a/src/locale/keymap-util.c
+++ b/src/locale/keymap-util.c
@@ -5,7 +5,10 @@
#include <string.h>
#include <unistd.h>
+#include "bus-util.h"
#include "def.h"
+#include "env-file.h"
+#include "env-file-label.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio-label.h"
@@ -16,6 +19,7 @@
#include "mkdir.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
static bool startswith_comma(const char *s, const char *prefix) {
s = startswith(s, prefix);
@@ -68,10 +72,16 @@ static void context_free_locale(Context *c) {
c->locale[p] = mfree(c->locale[p]);
}
-void context_free(Context *c) {
+void context_clear(Context *c) {
context_free_locale(c);
context_free_x11(c);
context_free_vconsole(c);
+
+ sd_bus_message_unref(c->locale_cache);
+ sd_bus_message_unref(c->x11_cache);
+ sd_bus_message_unref(c->vc_cache);
+
+ bus_verify_polkit_async_registry_free(c->polkit_registry);
};
void locale_simplify(char *locale[_VARIABLE_LC_MAX]) {
@@ -87,11 +97,13 @@ int locale_read_data(Context *c, sd_bus_message *m) {
int r;
/* Do not try to re-read the file within single bus operation. */
- if (m && m == c->locale_cache)
- return 0;
+ if (m) {
+ if (m == c->locale_cache)
+ return 0;
- /* To suppress multiple call of stat(), store the message to cache here. */
- c->locale_cache = m;
+ sd_bus_message_unref(c->locale_cache);
+ c->locale_cache = sd_bus_message_ref(m);
+ }
r = stat("/etc/locale.conf", &st);
if (r < 0 && errno != ENOENT)
@@ -108,7 +120,7 @@ int locale_read_data(Context *c, sd_bus_message *m) {
c->locale_mtime = t;
context_free_locale(c);
- r = parse_env_file(NULL, "/etc/locale.conf", NEWLINE,
+ r = parse_env_file(NULL, "/etc/locale.conf",
"LANG", &c->locale[VARIABLE_LANG],
"LANGUAGE", &c->locale[VARIABLE_LANGUAGE],
"LC_CTYPE", &c->locale[VARIABLE_LC_CTYPE],
@@ -122,8 +134,7 @@ int locale_read_data(Context *c, sd_bus_message *m) {
"LC_ADDRESS", &c->locale[VARIABLE_LC_ADDRESS],
"LC_TELEPHONE", &c->locale[VARIABLE_LC_TELEPHONE],
"LC_MEASUREMENT", &c->locale[VARIABLE_LC_MEASUREMENT],
- "LC_IDENTIFICATION", &c->locale[VARIABLE_LC_IDENTIFICATION],
- NULL);
+ "LC_IDENTIFICATION", &c->locale[VARIABLE_LC_IDENTIFICATION]);
if (r < 0)
return r;
} else {
@@ -155,11 +166,13 @@ int vconsole_read_data(Context *c, sd_bus_message *m) {
int r;
/* Do not try to re-read the file within single bus operation. */
- if (m && m == c->vc_cache)
- return 0;
+ if (m) {
+ if (m == c->vc_cache)
+ return 0;
- /* To suppress multiple call of stat(), store the message to cache here. */
- c->vc_cache = m;
+ sd_bus_message_unref(c->vc_cache);
+ c->vc_cache = sd_bus_message_ref(m);
+ }
if (stat("/etc/vconsole.conf", &st) < 0) {
if (errno != ENOENT)
@@ -178,10 +191,9 @@ int vconsole_read_data(Context *c, sd_bus_message *m) {
c->vc_mtime = t;
context_free_vconsole(c);
- r = parse_env_file(NULL, "/etc/vconsole.conf", NEWLINE,
+ r = parse_env_file(NULL, "/etc/vconsole.conf",
"KEYMAP", &c->vc_keymap,
- "KEYMAP_TOGGLE", &c->vc_keymap_toggle,
- NULL);
+ "KEYMAP_TOGGLE", &c->vc_keymap_toggle);
if (r < 0)
return r;
@@ -191,17 +203,18 @@ int vconsole_read_data(Context *c, sd_bus_message *m) {
int x11_read_data(Context *c, sd_bus_message *m) {
_cleanup_fclose_ FILE *f = NULL;
bool in_section = false;
- char line[LINE_MAX];
struct stat st;
usec_t t;
int r;
/* Do not try to re-read the file within single bus operation. */
- if (m && m == c->x11_cache)
- return 0;
+ if (m) {
+ if (m == c->x11_cache)
+ return 0;
- /* To suppress multiple call of stat(), store the message to cache here. */
- c->x11_cache = m;
+ sd_bus_message_unref(c->x11_cache);
+ c->x11_cache = sd_bus_message_ref(m);
+ }
if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) < 0) {
if (errno != ENOENT)
@@ -224,12 +237,17 @@ int x11_read_data(Context *c, sd_bus_message *m) {
if (!f)
return -errno;
- while (fgets(line, sizeof(line), f)) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
char *l;
- char_array_0(line);
- l = strstrip(line);
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+ l = strstrip(line);
if (IN_SET(l[0], 0, '#'))
continue;
@@ -327,7 +345,7 @@ int vconsole_write_data(Context *c) {
struct stat st;
int r;
- r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
+ r = load_env_file(NULL, "/etc/vconsole.conf", &l);
if (r < 0 && r != -ENOENT)
return r;
@@ -460,19 +478,16 @@ static int read_next_mapping(const char* filename,
assert(a);
for (;;) {
- char line[LINE_MAX];
+ _cleanup_free_ char *line = NULL;
+ size_t length;
char *l, **b;
int r;
- size_t length;
-
- errno = 0;
- if (!fgets(line, sizeof(line), f)) {
- if (ferror(f))
- return errno > 0 ? -errno : -EIO;
-
- return 0;
- }
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
(*n)++;
@@ -495,6 +510,8 @@ static int read_next_mapping(const char* filename,
*a = b;
return 1;
}
+
+ return 0;
}
int vconsole_convert_to_x11(Context *c) {
diff --git a/src/locale/keymap-util.h b/src/locale/keymap-util.h
index c55c9f92a6..6eced84240 100644
--- a/src/locale/keymap-util.h
+++ b/src/locale/keymap-util.h
@@ -1,7 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
#include "sd-bus.h"
+#include "hashmap.h"
#include "locale-util.h"
#include "time-util.h"
@@ -21,6 +23,8 @@ typedef struct Context {
usec_t vc_mtime;
char *vc_keymap;
char *vc_keymap_toggle;
+
+ Hashmap *polkit_registry;
} Context;
int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap);
@@ -31,7 +35,7 @@ int locale_read_data(Context *c, sd_bus_message *m);
int vconsole_read_data(Context *c, sd_bus_message *m);
int x11_read_data(Context *c, sd_bus_message *m);
-void context_free(Context *c);
+void context_clear(Context *c);
int vconsole_convert_to_x11(Context *c);
int vconsole_write_data(Context *c);
int x11_convert_to_vconsole(Context *c);
diff --git a/src/locale/localectl.c b/src/locale/localectl.c
index b3ad2820d9..69f5667801 100644
--- a/src/locale/localectl.c
+++ b/src/locale/localectl.c
@@ -15,7 +15,10 @@
#include "fd-util.h"
#include "fileio.h"
#include "locale-util.h"
+#include "main-func.h"
#include "pager.h"
+#include "pretty-print.h"
+#include "proc-cmdline.h"
#include "set.h"
#include "spawn-polkit-agent.h"
#include "strv.h"
@@ -23,10 +26,10 @@
#include "verbs.h"
#include "virt.h"
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static bool arg_ask_password = true;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
-static char *arg_host = NULL;
+static const char *arg_host = NULL;
static bool arg_convert = true;
typedef struct StatusInfo {
@@ -47,34 +50,33 @@ static void status_info_clear(StatusInfo *info) {
}
static void print_overridden_variables(void) {
- int r;
- char *variables[_VARIABLE_LC_MAX] = {};
- LocaleVariable j;
+ _cleanup_(locale_variables_freep) char *variables[_VARIABLE_LC_MAX] = {};
bool print_warning = true;
+ LocaleVariable j;
+ int r;
- if (detect_container() > 0 || arg_host)
+ if (arg_transport != BUS_TRANSPORT_LOCAL)
return;
- r = parse_env_file(NULL, "/proc/cmdline", WHITESPACE,
- "locale.LANG", &variables[VARIABLE_LANG],
- "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
- "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "locale.LC_TIME", &variables[VARIABLE_LC_TIME],
- "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "locale.LC_NAME", &variables[VARIABLE_LC_NAME],
- "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL);
-
+ r = proc_cmdline_get_key_many(
+ PROC_CMDLINE_STRIP_RD_PREFIX,
+ "locale.LANG", &variables[VARIABLE_LANG],
+ "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
+ "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+ "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
+ "locale.LC_TIME", &variables[VARIABLE_LC_TIME],
+ "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
+ "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
+ "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
+ "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER],
+ "locale.LC_NAME", &variables[VARIABLE_LC_NAME],
+ "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
+ "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
+ "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
+ "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION]);
if (r < 0 && r != -ENOENT) {
log_warning_errno(r, "Failed to read /proc/cmdline: %m");
- goto finish;
+ return;
}
for (j = 0; j < _VARIABLE_LC_MAX; j++)
@@ -87,9 +89,6 @@ static void print_overridden_variables(void) {
} else
log_warning(" %s=%s", locale_variable_to_string(j), variables[j]);
}
- finish:
- for (j = 0; j < _VARIABLE_LC_MAX; j++)
- free(variables[j]);
}
static void print_status_info(StatusInfo *i) {
@@ -184,10 +183,8 @@ static int set_locale(int argc, char **argv, void *userdata) {
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, NULL);
- if (r < 0) {
- log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, -r));
return 0;
}
@@ -200,7 +197,7 @@ static int list_locales(int argc, char **argv, void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to read list of locales: %m");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
strv_print(l);
return 0;
@@ -229,9 +226,9 @@ static int set_vconsole_keymap(int argc, char **argv, void *userdata) {
NULL,
"ssbb", map, toggle_map, arg_convert, arg_ask_password);
if (r < 0)
- log_error("Failed to set keymap: %s", bus_error_message(&error, -r));
+ return log_error_errno(r, "Failed to set keymap: %s", bus_error_message(&error, -r));
- return r;
+ return 0;
}
static int list_vconsole_keymaps(int argc, char **argv, void *userdata) {
@@ -242,7 +239,7 @@ static int list_vconsole_keymaps(int argc, char **argv, void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to read list of keymaps: %m");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
strv_print(l);
@@ -273,15 +270,14 @@ static int set_x11_keymap(int argc, char **argv, void *userdata) {
"ssssbb", layout, model, variant, options,
arg_convert, arg_ask_password);
if (r < 0)
- log_error("Failed to set keymap: %s", bus_error_message(&error, -r));
+ return log_error_errno(r, "Failed to set keymap: %s", bus_error_message(&error, -r));
- return r;
+ return 0;
}
static int list_x11_keymaps(int argc, char **argv, void *userdata) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **list = NULL;
- char line[LINE_MAX];
enum {
NONE,
MODELS,
@@ -306,9 +302,16 @@ static int list_x11_keymaps(int argc, char **argv, void *userdata) {
else
assert_not_reached("Wrong parameter");
- FOREACH_LINE(line, f, break) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
char *l, *w;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read keyboard mapping list: %m");
+ if (r == 0)
+ break;
+
l = strstrip(line);
if (isempty(l))
@@ -360,21 +363,27 @@ static int list_x11_keymaps(int argc, char **argv, void *userdata) {
return log_oom();
}
- if (strv_isempty(list)) {
- log_error("Couldn't find any entries.");
- return -ENOENT;
- }
+ if (strv_isempty(list))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Couldn't find any entries.");
strv_sort(list);
strv_uniq(list);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
strv_print(list);
return 0;
}
static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("localectl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] COMMAND ...\n\n"
"Query or change system locale and keyboard settings.\n\n"
" -h --help Show this help\n"
@@ -397,7 +406,10 @@ static int help(void) {
" list-x11-keymap-variants [LAYOUT]\n"
" Show known X11 keyboard mapping variants\n"
" list-x11-keymap-options Show known X11 keyboard mapping options\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
@@ -446,7 +458,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_ASK_PASSWORD:
@@ -493,8 +505,8 @@ static int localectl_main(sd_bus *bus, int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, bus);
}
-int main(int argc, char*argv[]) {
- sd_bus *bus = NULL;
+static int run(int argc, char *argv[]) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -503,21 +515,13 @@ int main(int argc, char*argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
- if (r < 0) {
- log_error_errno(r, "Failed to create bus connection: %m");
- goto finish;
- }
-
- r = localectl_main(bus, argc, argv);
-
-finish:
- /* make sure we terminate the bus connection first, and then close the
- * pager, see issue #3543 for the details. */
- sd_bus_flush_close_unref(bus);
- pager_close();
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return localectl_main(bus, argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/locale/localed.c b/src/locale/localed.c
index b8f95b69a6..f851d35a08 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -19,31 +19,33 @@
#include "keymap-util.h"
#include "locale-util.h"
#include "macro.h"
+#include "main-func.h"
+#include "missing_capability.h"
#include "path-util.h"
#include "selinux-util.h"
+#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
-static Hashmap *polkit_registry = NULL;
-
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;
- unsigned c_set, c_unset, p;
+ size_t c_set, c_unset;
+ LocaleVariable p;
int r;
assert(bus);
l_unset = new0(char*, _VARIABLE_LC_MAX);
if (!l_unset)
- return -ENOMEM;
+ return log_oom();
l_set = new0(char*, _VARIABLE_LC_MAX);
if (!l_set)
- return -ENOMEM;
+ return log_oom();
for (p = 0, c_set = 0, c_unset = 0; p < _VARIABLE_LC_MAX; p++) {
const char *name;
@@ -56,8 +58,9 @@ static int locale_update_system_manager(Context *c, sd_bus *bus) {
else {
char *s;
- if (asprintf(&s, "%s=%s", name, c->locale[p]) < 0)
- return -ENOMEM;
+ s = strjoin(name, "=", c->locale[p]);
+ if (!s)
+ return log_oom();
l_set[c_unset++] = s;
}
@@ -70,19 +73,19 @@ static int locale_update_system_manager(Context *c, sd_bus *bus) {
"org.freedesktop.systemd1.Manager",
"UnsetAndSetEnvironment");
if (r < 0)
- return r;
+ return bus_log_create_error(r);
r = sd_bus_message_append_strv(m, l_unset);
if (r < 0)
- return r;
+ return bus_log_create_error(r);
r = sd_bus_message_append_strv(m, l_set);
if (r < 0)
- return r;
+ return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, NULL);
if (r < 0)
- log_error_errno(r, "Failed to update the manager environment, ignoring: %m");
+ return log_error_errno(r, "Failed to update the manager environment: %s", bus_error_message(&error, r));
return 0;
}
@@ -103,8 +106,9 @@ static int vconsole_reload(sd_bus *bus) {
"ss", "systemd-vconsole-setup.service", "replace");
if (r < 0)
- log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
- return r;
+ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
+
+ return 0;
}
static int vconsole_convert_to_x11_and_emit(Context *c, sd_bus_message *m) {
@@ -251,20 +255,13 @@ static int property_get_xkb(
return -EINVAL;
}
-static void locale_free(char ***l) {
- int p;
-
- for (p = 0; p < _VARIABLE_LC_MAX; p++)
- (*l)[p] = mfree((*l)[p]);
-}
-
static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- Context *c = userdata;
+ _cleanup_(locale_variables_freep) char *new_locale[_VARIABLE_LC_MAX] = {};
_cleanup_strv_free_ char **settings = NULL, **l = NULL;
- char *new_locale[_VARIABLE_LC_MAX] = {}, **i;
- _cleanup_(locale_free) _unused_ char **dummy = new_locale;
+ Context *c = userdata;
bool modified = false;
int interactive, p, r;
+ char **i;
assert(m);
assert(c);
@@ -365,7 +362,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
NULL,
interactive,
UID_INVALID,
- &polkit_registry,
+ &c->polkit_registry,
error);
if (r < 0)
return r;
@@ -436,7 +433,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
NULL,
interactive,
UID_INVALID,
- &polkit_registry,
+ &c->polkit_registry,
error);
if (r < 0)
return r;
@@ -456,9 +453,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
log_info("Changed virtual console keymap to '%s' toggle '%s'",
strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
- r = vconsole_reload(sd_bus_message_get_bus(m));
- if (r < 0)
- log_error_errno(r, "Failed to request keymap reload: %m");
+ (void) vconsole_reload(sd_bus_message_get_bus(m));
(void) sd_bus_emit_properties_changed(
sd_bus_message_get_bus(m),
@@ -631,7 +626,7 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
NULL,
interactive,
UID_INVALID,
- &polkit_registry,
+ &c->polkit_registry,
error);
if (r < 0)
return r;
@@ -715,8 +710,8 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
return 0;
}
-int main(int argc, char *argv[]) {
- _cleanup_(context_free) Context context = {
+static int run(int argc, char *argv[]) {
+ _cleanup_(context_clear) Context context = {
.locale_mtime = USEC_INFINITY,
.vc_mtime = USEC_INFINITY,
.x11_mtime = USEC_INFINITY,
@@ -725,37 +720,39 @@ int main(int argc, char *argv[]) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
mac_selinux_init();
- if (argc != 1) {
- log_error("This program takes no arguments.");
- r = -EINVAL;
- goto finish;
- }
+ if (argc != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
r = sd_event_default(&event);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate event loop: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
- sd_event_set_watchdog(event, true);
+ (void) sd_event_set_watchdog(event, true);
+
+ r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to install SIGINT handler: %m");
+
+ r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to install SIGTERM handler: %m");
r = connect_bus(&context, event, &bus);
if (r < 0)
- goto finish;
+ return r;
r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
if (r < 0)
- log_error_errno(r, "Failed to run event loop: %m");
+ return log_error_errno(r, "Failed to run event loop: %m");
-finish:
- bus_verify_polkit_async_registry_free(polkit_registry);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/locale/test-keymap-util.c b/src/locale/test-keymap-util.c
index e20731b253..2f82891d60 100644
--- a/src/locale/test-keymap-util.c
+++ b/src/locale/test-keymap-util.c
@@ -65,7 +65,7 @@ static void test_find_legacy_keymap(void) {
}
static void test_vconsole_convert_to_x11(void) {
- _cleanup_(context_free) Context c = {};
+ _cleanup_(context_clear) Context c = {};
log_info("/*** %s ***/", __func__);
@@ -119,7 +119,7 @@ static void test_vconsole_convert_to_x11(void) {
}
static void test_x11_convert_to_vconsole(void) {
- _cleanup_(context_free) Context c = {};
+ _cleanup_(context_clear) Context c = {};
int r;
log_info("/*** %s ***/", __func__);
diff --git a/src/login/70-uaccess.rules b/src/login/70-uaccess.rules.m4
index 3515d292ac..d55e5bf5ce 100644
--- a/src/login/70-uaccess.rules
+++ b/src/login/70-uaccess.rules.m4
@@ -46,6 +46,10 @@ SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess"
# DRI video devices
SUBSYSTEM=="drm", KERNEL=="card*", TAG+="uaccess"
+m4_ifdef(`DEV_KVM_UACCESS',``
+# KVM
+SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess"''
+)m4_dnl
# smart-card readers
ENV{ID_SMARTCARD_READER}=="?*", TAG+="uaccess"
diff --git a/src/login/71-seat.rules.in b/src/login/71-seat.rules.in
index 0db46adc84..1f30900392 100644
--- a/src/login/71-seat.rules.in
+++ b/src/login/71-seat.rules.in
@@ -12,7 +12,7 @@ ACTION=="remove", GOTO="seat_end"
TAG=="uaccess", SUBSYSTEM!="sound", TAG+="seat"
SUBSYSTEM=="sound", KERNEL=="card*", TAG+="seat"
SUBSYSTEM=="input", KERNEL=="input*", TAG+="seat"
-SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat", TAG+="master-of-seat"
+SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat"
SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", TAG+="master-of-seat"
SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat"
diff --git a/src/login/inhibit.c b/src/login/inhibit.c
index b0bb8de57e..f574d429f4 100644
--- a/src/login/inhibit.c
+++ b/src/login/inhibit.c
@@ -12,8 +12,11 @@
#include "bus-error.h"
#include "bus-util.h"
#include "fd-util.h"
+#include "format-table.h"
#include "format-util.h"
+#include "main-func.h"
#include "pager.h"
+#include "pretty-print.h"
#include "process-util.h"
#include "signal-util.h"
#include "strv.h"
@@ -24,7 +27,8 @@ static const char* arg_what = "idle:sleep:shutdown";
static const char* arg_who = NULL;
static const char* arg_why = "Unknown reason";
static const char* arg_mode = NULL;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
+static bool arg_legend = true;
static enum {
ACTION_INHIBIT,
@@ -59,14 +63,13 @@ static int inhibit(sd_bus *bus, sd_bus_error *error) {
return r;
}
-static int print_inhibitors(sd_bus *bus, sd_bus_error *error) {
+static int print_inhibitors(sd_bus *bus) {
+ _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 *what, *who, *why, *mode;
- unsigned int uid, pid;
- unsigned n = 0;
+ _cleanup_(table_unrefp) Table *table = NULL;
int r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
r = sd_bus_call_method(
bus,
@@ -74,53 +77,93 @@ static int print_inhibitors(sd_bus *bus, sd_bus_error *error) {
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"ListInhibitors",
- error,
+ &error,
&reply,
"");
if (r < 0)
- return r;
+ return log_error_errno(r, "Could not get active inhibitors: %s", bus_error_message(&error, r));
+
+ table = table_new("who", "uid", "user", "pid", "comm", "what", "why", "mode");
+ if (!table)
+ return log_oom();
+
+ /* If there's not enough space, shorten the "WHY" column, as it's little more than an explaining comment. */
+ (void) table_set_weight(table, TABLE_HEADER_CELL(6), 20);
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
if (r < 0)
return bus_log_parse_error(r);
- while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
+ for (;;) {
_cleanup_free_ char *comm = NULL, *u = NULL;
+ const char *what, *who, *why, *mode;
+ uint32_t uid, pid;
+
+ r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ if (r == 0)
+ break;
if (arg_mode && !streq(mode, arg_mode))
continue;
- get_process_comm(pid, &comm);
+ (void) get_process_comm(pid, &comm);
u = uid_to_name(uid);
- printf(" Who: %s (UID "UID_FMT"/%s, PID "PID_FMT"/%s)\n"
- " What: %s\n"
- " Why: %s\n"
- " Mode: %s\n\n",
- who, uid, strna(u), pid, strna(comm),
- what,
- why,
- mode);
-
- n++;
+ r = table_add_many(table,
+ TABLE_STRING, who,
+ TABLE_UINT32, uid,
+ TABLE_STRING, strna(u),
+ TABLE_UINT32, pid,
+ TABLE_STRING, strna(comm),
+ TABLE_STRING, what,
+ TABLE_STRING, why,
+ TABLE_STRING, mode);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add table row: %m");
}
- 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);
- printf("%u inhibitors listed.\n", n);
+ if (table_get_rows(table) > 1) {
+ r = table_set_sort(table, (size_t) 1, (size_t) 0, (size_t) 5, (size_t) 6, (size_t) -1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to sort table: %m");
+
+ table_set_header(table, arg_legend);
+
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to show table: %m");
+ }
+
+ if (arg_legend) {
+ if (table_get_rows(table) > 1)
+ printf("\n%zu inhibitors listed.\n", table_get_rows(table) - 1);
+ else
+ printf("No inhibitors.\n");
+ }
+
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-inhibit", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Execute a process while inhibiting shutdown/sleep/idle.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
+ " --no-legend Do not show the headers and footers\n"
" --what=WHAT Operations to inhibit, colon separated list of:\n"
" shutdown, sleep, idle, handle-power-key,\n"
" handle-suspend-key, handle-hibernate-key,\n"
@@ -129,7 +172,12 @@ static void help(void) {
" --why=STRING A descriptive string why is being inhibited\n"
" --mode=MODE One of block or delay\n"
" --list List active inhibitors\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -142,6 +190,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_MODE,
ARG_LIST,
ARG_NO_PAGER,
+ ARG_NO_LEGEND,
};
static const struct option options[] = {
@@ -153,6 +202,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "mode", required_argument, NULL, ARG_MODE },
{ "list", no_argument, NULL, ARG_LIST },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{}
};
@@ -166,8 +216,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -193,7 +242,11 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
+ break;
+
+ case ARG_NO_LEGEND:
+ arg_legend = false;
break;
case '?':
@@ -206,16 +259,14 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_action == ACTION_INHIBIT && optind == argc)
arg_action = ACTION_LIST;
- else if (arg_action == ACTION_INHIBIT && optind >= argc) {
- log_error("Missing command line to execute.");
- return -EINVAL;
- }
+ else if (arg_action == ACTION_INHIBIT && optind >= argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Missing command line to execute.");
return 1;
}
-int main(int argc, char *argv[]) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+static int run(int argc, char *argv[]) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
@@ -223,27 +274,18 @@ int main(int argc, char *argv[]) {
log_open();
r = parse_argv(argc, argv);
- if (r < 0)
- return EXIT_FAILURE;
- if (r == 0)
- return EXIT_SUCCESS;
+ if (r <= 0)
+ return r;
r = sd_bus_default_system(&bus);
- if (r < 0) {
- log_error_errno(r, "Failed to connect to bus: %m");
- return EXIT_FAILURE;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to bus: %m");
- if (arg_action == ACTION_LIST) {
+ if (arg_action == ACTION_LIST)
+ return print_inhibitors(bus);
- r = print_inhibitors(bus, &error);
- pager_close();
- if (r < 0) {
- log_error("Failed to list inhibitors: %s", bus_error_message(&error, -r));
- return EXIT_FAILURE;
- }
-
- } else {
+ else {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_close_ int fd = -1;
_cleanup_free_ char *w = NULL;
pid_t pid;
@@ -258,14 +300,12 @@ int main(int argc, char *argv[]) {
arg_mode = "block";
fd = inhibit(bus, &error);
- if (fd < 0) {
- log_error("Failed to inhibit: %s", bus_error_message(&error, fd));
- return EXIT_FAILURE;
- }
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to inhibit: %s", bus_error_message(&error, fd));
- r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
- return EXIT_FAILURE;
+ return r;
if (r == 0) {
/* Child */
execvp(argv[optind], argv + optind);
@@ -274,9 +314,8 @@ int main(int argc, char *argv[]) {
_exit(EXIT_FAILURE);
}
- r = wait_for_terminate_and_check(argv[optind], pid, WAIT_LOG);
- return r < 0 ? EXIT_FAILURE : r;
+ return wait_for_terminate_and_check(argv[optind], pid, WAIT_LOG);
}
-
- return EXIT_SUCCESS;
}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index be55fdbfd8..ab1b56201a 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -18,9 +18,12 @@
#include "log.h"
#include "logs-show.h"
#include "macro.h"
+#include "main-func.h"
#include "pager.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "sigbus.h"
#include "signal-util.h"
#include "spawn-polkit-agent.h"
@@ -37,7 +40,7 @@ static char **arg_property = NULL;
static bool arg_all = false;
static bool arg_value = false;
static bool arg_full = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static const char *arg_kill_who = NULL;
static int arg_signal = SIGTERM;
@@ -47,6 +50,8 @@ static bool arg_ask_password = true;
static unsigned arg_lines = 10;
static OutputMode arg_output = OUTPUT_SHORT;
+STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
+
static OutputFlags get_output_flags(void) {
return
@@ -96,7 +101,10 @@ static int show_table(Table *table, const char *word) {
table_set_header(table, arg_legend);
- r = table_print(table, NULL);
+ if (OUTPUT_MODE_IS_JSON(arg_output))
+ r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO);
+ else
+ r = table_print(table, NULL);
if (r < 0)
return log_error_errno(r, "Failed to show table: %m");
}
@@ -121,7 +129,7 @@ static int list_sessions(int argc, char *argv[], void *userdata) {
assert(bus);
assert(argv);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
r = sd_bus_call_method(
bus,
@@ -138,7 +146,7 @@ static int list_sessions(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- table = table_new("SESSION", "UID", "USER", "SEAT", "TTY");
+ table = table_new("session", "uid", "user", "seat", "tty");
if (!table)
return log_oom();
@@ -202,7 +210,7 @@ static int list_users(int argc, char *argv[], void *userdata) {
assert(bus);
assert(argv);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
r = sd_bus_call_method(
bus,
@@ -219,7 +227,7 @@ static int list_users(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- table = table_new("UID", "USER");
+ table = table_new("uid", "user");
if (!table)
return log_oom();
@@ -259,7 +267,7 @@ static int list_seats(int argc, char *argv[], void *userdata) {
assert(bus);
assert(argv);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
r = sd_bus_call_method(
bus,
@@ -276,7 +284,7 @@ static int list_seats(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- table = table_new("SEAT");
+ table = table_new("seat");
if (!table)
return log_oom();
@@ -346,7 +354,7 @@ typedef struct SessionStatusInfo {
uid_t uid;
const char *name;
struct dual_timestamp timestamp;
- unsigned int vtnr;
+ unsigned vtnr;
const char *seat;
const char *tty;
const char *display;
@@ -723,15 +731,7 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line)
return 0;
}
-#define property(name, fmt, ...) \
- do { \
- if (value) \
- printf(fmt "\n", __VA_ARGS__); \
- else \
- printf("%s=" fmt "\n", name, __VA_ARGS__); \
- } while (0)
-
-static int print_property(const char *name, sd_bus_message *m, bool value, bool all) {
+static int print_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) {
char type;
const char *contents;
int r;
@@ -755,7 +755,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
return bus_log_parse_error(r);
if (all || !isempty(s))
- property(name, "%s", s);
+ bus_print_property_value(name, expected_value, value, "%s", s);
return 1;
@@ -766,12 +766,12 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
if (r < 0)
return bus_log_parse_error(r);
- if (!uid_is_valid(uid)) {
- log_error("Invalid user ID: " UID_FMT, uid);
- return -EINVAL;
- }
+ if (!uid_is_valid(uid))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid user ID: " UID_FMT,
+ uid);
- property(name, UID_FMT, uid);
+ bus_print_property_value(name, expected_value, value, UID_FMT, uid);
return 1;
}
break;
@@ -843,7 +843,7 @@ static int show_session(int argc, char *argv[], void *userdata) {
properties = !strstr(argv[0], "status");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (argc <= 1) {
const char *session, *p = "/org/freedesktop/login1/session/self";
@@ -856,10 +856,9 @@ static int show_session(int argc, char *argv[], void *userdata) {
session = getenv("XDG_SESSION_ID");
if (session) {
r = get_session_path(bus, session, &error, &path);
- if (r < 0) {
- log_error("Failed to get session path: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get session path: %s", bus_error_message(&error, r));
+
p = path;
}
@@ -868,10 +867,8 @@ static int show_session(int argc, char *argv[], void *userdata) {
for (i = 1; i < argc; i++) {
r = get_session_path(bus, argv[i], &error, &path);
- if (r < 0) {
- log_error("Failed to get session path: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get session path: %s", bus_error_message(&error, r));
if (properties)
r = show_properties(bus, path, &new_line);
@@ -895,7 +892,7 @@ static int show_user(int argc, char *argv[], void *userdata) {
properties = !strstr(argv[0], "status");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (argc <= 1) {
/* If not argument is specified inspect the manager
@@ -912,7 +909,7 @@ static int show_user(int argc, char *argv[], void *userdata) {
const char *path = NULL;
uid_t uid;
- r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
+ r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
@@ -924,10 +921,8 @@ static int show_user(int argc, char *argv[], void *userdata) {
"GetUser",
&error, &reply,
"u", (uint32_t) uid);
- if (r < 0) {
- log_error("Failed to get user: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get user: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "o", &path);
if (r < 0)
@@ -955,7 +950,7 @@ static int show_seat(int argc, char *argv[], void *userdata) {
properties = !strstr(argv[0], "status");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (argc <= 1) {
/* If not argument is specified inspect the manager
@@ -979,10 +974,8 @@ static int show_seat(int argc, char *argv[], void *userdata) {
"GetSeat",
&error, &reply,
"s", argv[i]);
- if (r < 0) {
- log_error("Failed to get seat: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get seat: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "o", &path);
if (r < 0)
@@ -1036,10 +1029,8 @@ static int activate(int argc, char *argv[], void *userdata) {
"ActivateSession",
&error, NULL,
"s", argv[i]);
- if (r < 0) {
- log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, -r));
}
return 0;
@@ -1068,10 +1059,8 @@ static int kill_session(int argc, char *argv[], void *userdata) {
"KillSession",
&error, NULL,
"ssi", argv[i], arg_kill_who, arg_signal);
- if (r < 0) {
- log_error("Could not kill session: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not kill session: %s", bus_error_message(&error, -r));
}
return 0;
@@ -1108,7 +1097,7 @@ static int enable_linger(int argc, char *argv[], void *userdata) {
if (isempty(argv[i]))
uid = UID_INVALID;
else {
- r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
+ r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
}
@@ -1121,10 +1110,8 @@ static int enable_linger(int argc, char *argv[], void *userdata) {
"SetUserLinger",
&error, NULL,
"ubb", (uint32_t) uid, b, true);
- if (r < 0) {
- log_error("Could not enable linger: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not enable linger: %s", bus_error_message(&error, -r));
}
return 0;
@@ -1143,7 +1130,7 @@ static int terminate_user(int argc, char *argv[], void *userdata) {
for (i = 1; i < argc; i++) {
uid_t uid;
- r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
+ r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
@@ -1155,10 +1142,8 @@ static int terminate_user(int argc, char *argv[], void *userdata) {
"TerminateUser",
&error, NULL,
"u", (uint32_t) uid);
- if (r < 0) {
- log_error("Could not terminate user: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not terminate user: %s", bus_error_message(&error, -r));
}
return 0;
@@ -1180,7 +1165,7 @@ static int kill_user(int argc, char *argv[], void *userdata) {
for (i = 1; i < argc; i++) {
uid_t uid;
- r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
+ r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
@@ -1192,10 +1177,8 @@ static int kill_user(int argc, char *argv[], void *userdata) {
"KillUser",
&error, NULL,
"ui", (uint32_t) uid, arg_signal);
- if (r < 0) {
- log_error("Could not kill user: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not kill user: %s", bus_error_message(&error, -r));
}
return 0;
@@ -1222,10 +1205,8 @@ static int attach(int argc, char *argv[], void *userdata) {
&error, NULL,
"ssb", argv[1], argv[i], true);
- if (r < 0) {
- log_error("Could not attach device: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not attach device: %s", bus_error_message(&error, -r));
}
return 0;
@@ -1250,9 +1231,9 @@ static int flush_devices(int argc, char *argv[], void *userdata) {
&error, NULL,
"b", true);
if (r < 0)
- log_error("Could not flush devices: %s", bus_error_message(&error, -r));
+ return log_error_errno(r, "Could not flush devices: %s", bus_error_message(&error, -r));
- return r;
+ return 0;
}
static int lock_sessions(int argc, char *argv[], void *userdata) {
@@ -1274,9 +1255,9 @@ static int lock_sessions(int argc, char *argv[], void *userdata) {
&error, NULL,
NULL);
if (r < 0)
- log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
+ return log_error_errno(r, "Could not lock sessions: %s", bus_error_message(&error, -r));
- return r;
+ return 0;
}
static int terminate_seat(int argc, char *argv[], void *userdata) {
@@ -1299,16 +1280,22 @@ static int terminate_seat(int argc, char *argv[], void *userdata) {
"TerminateSeat",
&error, NULL,
"s", argv[i]);
- if (r < 0) {
- log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not terminate seat: %s", bus_error_message(&error, -r));
}
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("loginctl", "1", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Send control commands to or query the login manager.\n\n"
@@ -1329,7 +1316,8 @@ static int help(int argc, char *argv[], void *userdata) {
" -o --output=STRING Change journal output mode (short, short-precise,\n"
" short-iso, short-iso-precise, short-full,\n"
" short-monotonic, short-unix, verbose, export,\n"
- " json, json-pretty, json-sse, cat)\n"
+ " json, json-pretty, json-sse, json-seq, cat,\n"
+ " with-unit)\n"
"Session Commands:\n"
" list-sessions List sessions\n"
" session-status [ID...] Show session status\n"
@@ -1356,7 +1344,10 @@ static int help(int argc, char *argv[], void *userdata) {
" attach NAME DEVICE... Attach one or more devices to a seat\n"
" flush-devices Flush all device associations\n"
" terminate-seat NAME... Terminate all sessions on one or more seats\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
@@ -1401,8 +1392,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help(0, NULL, NULL);
- return 0;
+ return help(0, NULL, NULL);
case ARG_VERSION:
return version();
@@ -1432,10 +1422,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'n':
- if (safe_atou(optarg, &arg_lines) < 0) {
- log_error("Failed to parse lines '%s'", optarg);
- return -EINVAL;
- }
+ if (safe_atou(optarg, &arg_lines) < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse lines '%s'", optarg);
break;
case 'o':
@@ -1445,14 +1434,17 @@ static int parse_argv(int argc, char *argv[]) {
}
arg_output = output_mode_from_string(optarg);
- if (arg_output < 0) {
- log_error("Unknown output '%s'.", optarg);
- return -EINVAL;
- }
+ if (arg_output < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown output '%s'.", optarg);
+
+ if (OUTPUT_MODE_IS_JSON(arg_output))
+ arg_legend = false;
+
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
@@ -1474,10 +1466,9 @@ static int parse_argv(int argc, char *argv[]) {
}
arg_signal = signal_from_string(optarg);
- if (arg_signal < 0) {
- log_error("Failed to parse signal string %s.", optarg);
- return -EINVAL;
- }
+ if (arg_signal < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse signal string %s.", optarg);
break;
case 'H':
@@ -1533,37 +1524,30 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
return dispatch_verb(argc, argv, verbs, bus);
}
-int main(int argc, char *argv[]) {
- sd_bus *bus = NULL;
+static int run(int argc, char *argv[]) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
+
+ /* The journal merging logic potentially needs a lot of fds. */
+ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
sigbus_install();
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
- if (r < 0) {
- log_error_errno(r, "Failed to create bus connection: %m");
- goto finish;
- }
-
- sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
-
- r = loginctl_main(argc, argv, bus);
-
-finish:
- /* make sure we terminate the bus connection first, and then close the
- * pager, see issue #3543 for the details. */
- sd_bus_flush_close_unref(bus);
- pager_close();
- polkit_agent_close();
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
- strv_free(arg_property);
+ (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return loginctl_main(argc, argv, bus);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/login/logind-acl.c b/src/login/logind-acl.c
index cafeb8822f..d2f4e60f98 100644
--- a/src/login/logind-acl.c
+++ b/src/login/logind-acl.c
@@ -3,8 +3,11 @@
#include <errno.h>
#include <string.h>
+#include "sd-device.h"
+
#include "acl-util.h"
#include "alloc-util.h"
+#include "device-util.h"
#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
@@ -12,7 +15,6 @@
#include "logind-acl.h"
#include "set.h"
#include "string-util.h"
-#include "udev-util.h"
#include "util.h"
static int flush_acl(acl_t acl) {
@@ -157,30 +159,27 @@ finish:
return r;
}
-int devnode_acl_all(struct udev *udev,
- const char *seat,
+int devnode_acl_all(const char *seat,
bool flush,
bool del, uid_t old_uid,
bool add, uid_t new_uid) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
_cleanup_set_free_free_ Set *nodes = NULL;
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *dent;
+ sd_device *d;
Iterator i;
char *n;
int r;
- assert(udev);
-
nodes = set_new(&path_hash_ops);
if (!nodes)
return -ENOMEM;
- e = udev_enumerate_new(udev);
- if (!e)
- return -ENOMEM;
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
if (isempty(seat))
seat = "seat0";
@@ -190,45 +189,25 @@ int devnode_acl_all(struct udev *udev,
* could add the seat name as second match tag, but this would
* be hardly optimizable in libudev, and hence checking the
* second tag manually in our loop is a good solution. */
- r = udev_enumerate_add_match_tag(e, "uaccess");
+ r = sd_device_enumerator_add_match_tag(e, "uaccess");
if (r < 0)
return r;
- r = udev_enumerate_add_match_is_initialized(e);
- if (r < 0)
- return r;
-
- r = udev_enumerate_scan_devices(e);
- if (r < 0)
- return r;
-
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+ FOREACH_DEVICE(e, d) {
const char *node, *sn;
- d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!d)
- return -ENOMEM;
-
- sn = udev_device_get_property_value(d, "ID_SEAT");
- if (isempty(sn))
+ if (sd_device_get_property_value(d, "ID_SEAT", &sn) < 0 || isempty(sn))
sn = "seat0";
if (!streq(seat, sn))
continue;
- node = udev_device_get_devnode(d);
/* In case people mistag devices with nodes, we need to ignore this */
- if (!node)
+ if (sd_device_get_devname(d, &node) < 0)
continue;
- n = strdup(node);
- if (!n)
- return -ENOMEM;
-
- log_debug("Found udev node %s for seat %s", n, seat);
- r = set_consume(nodes, n);
+ log_device_debug(d, "Found udev node %s for seat %s", node, seat);
+ r = set_put_strdup(nodes, node);
if (r < 0)
return r;
}
diff --git a/src/login/logind-acl.h b/src/login/logind-acl.h
index 79700670cc..00e286d76d 100644
--- a/src/login/logind-acl.h
+++ b/src/login/logind-acl.h
@@ -4,8 +4,6 @@
#include <stdbool.h>
#include <sys/types.h>
-#include "libudev.h"
-
#if HAVE_ACL
int devnode_acl(const char *path,
@@ -13,8 +11,7 @@ int devnode_acl(const char *path,
bool del, uid_t old_uid,
bool add, uid_t new_uid);
-int devnode_acl_all(struct udev *udev,
- const char *seat,
+int devnode_acl_all(const char *seat,
bool flush,
bool del, uid_t old_uid,
bool add, uid_t new_uid);
@@ -27,8 +24,7 @@ static inline int devnode_acl(const char *path,
return 0;
}
-static inline int devnode_acl_all(struct udev *udev,
- const char *seat,
+static inline int devnode_acl_all(const char *seat,
bool flush,
bool del, uid_t old_uid,
bool add, uid_t new_uid) {
diff --git a/src/login/logind-action.c b/src/login/logind-action.c
index 08e41af81a..e4e6c90191 100644
--- a/src/login/logind-action.c
+++ b/src/login/logind-action.c
@@ -108,6 +108,14 @@ int manager_handle_action(
else
supported = true;
+ if (!supported && IN_SET(handle, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP, HANDLE_SUSPEND_THEN_HIBERNATE)) {
+ supported = can_sleep("suspend") > 0;
+ if (supported) {
+ log_notice("Operation '%s' requested but not supported, using regular suspend instead.", handle_action_to_string(handle));
+ handle = HANDLE_SUSPEND;
+ }
+ }
+
if (!supported) {
log_warning("Requested operation not supported, ignoring.");
return -EOPNOTSUPP;
@@ -129,7 +137,7 @@ int manager_handle_action(
manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) {
_cleanup_free_ char *comm = NULL, *u = NULL;
- get_process_comm(offending->pid, &comm);
+ (void) get_process_comm(offending->pid, &comm);
u = uid_to_name(offending->uid);
/* If this is just a recheck of the lid switch then don't warn about anything */
@@ -152,10 +160,8 @@ int manager_handle_action(
log_info("%s", message_table[handle]);
r = bus_manager_shutdown_or_sleep_now_or_later(m, target, inhibit_operation, &error);
- if (r < 0) {
- log_error("Failed to execute operation: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
return 1;
}
diff --git a/src/login/logind-button.c b/src/login/logind-button.c
index 0defa6b9ba..daffbf0668 100644
--- a/src/login/logind-button.c
+++ b/src/login/logind-button.c
@@ -12,6 +12,7 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "logind-button.h"
+#include "missing_input.h"
#include "string-util.h"
#include "util.h"
@@ -329,10 +330,10 @@ int button_open(Button *b) {
r = button_suitable(b);
if (r < 0)
return log_warning_errno(r, "Failed to determine whether input device is relevant to us: %m");
- if (r == 0) {
- log_debug("Device %s does not expose keys or switches relevant to us, ignoring.", p);
- return -EADDRNOTAVAIL;
- }
+ if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL),
+ "Device %s does not expose keys or switches relevant to us, ignoring.",
+ p);
if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
r = log_error_errno(errno, "Failed to get input name: %m");
diff --git a/src/login/logind-button.h b/src/login/logind-button.h
index 740cf0b786..d009851cf4 100644
--- a/src/login/logind-button.h
+++ b/src/login/logind-button.h
@@ -20,7 +20,7 @@ struct Button {
};
Button* button_new(Manager *m, const char *name);
-void button_free(Button*b);
+void button_free(Button *b);
int button_open(Button *b);
int button_set_seat(Button *b, const char *sn);
int button_check_switches(Button *b);
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index dbae4bf5af..60831bb1c3 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -5,19 +5,25 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <linux/vt.h>
+#if ENABLE_UTMP
+#include <utmpx.h>
+#endif
+
+#include "sd-device.h"
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
#include "cgroup-util.h"
#include "conf-parser.h"
+#include "device-util.h"
#include "fd-util.h"
#include "logind.h"
#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
#include "strv.h"
#include "terminal-util.h"
-#include "udev-util.h"
#include "user-util.h"
void manager_reset_config(Manager *m) {
@@ -27,6 +33,8 @@ void manager_reset_config(Manager *m) {
m->reserve_vt = 6;
m->remove_ipc = true;
m->inhibit_delay_max = 5 * USEC_PER_SEC;
+ m->user_stop_delay = 10 * USEC_PER_SEC;
+
m->handle_power_key = HANDLE_POWEROFF;
m->handle_suspend_key = HANDLE_SUSPEND;
m->handle_hibernate_key = HANDLE_HIBERNATE;
@@ -88,15 +96,16 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev
int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
Seat *s;
+ int r;
assert(m);
assert(id);
s = hashmap_get(m->seats, id);
if (!s) {
- s = seat_new(m, id);
- if (!s)
- return -ENOMEM;
+ r = seat_new(&s, m, id);
+ if (r < 0)
+ return r;
}
if (_seat)
@@ -107,15 +116,16 @@ int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
int manager_add_session(Manager *m, const char *id, Session **_session) {
Session *s;
+ int r;
assert(m);
assert(id);
s = hashmap_get(m->sessions, id);
if (!s) {
- s = session_new(m, id);
- if (!s)
- return -ENOMEM;
+ r = session_new(&s, m, id);
+ if (r < 0)
+ return r;
}
if (_session)
@@ -124,7 +134,14 @@ int manager_add_session(Manager *m, const char *id, Session **_session) {
return 0;
}
-int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
+int manager_add_user(
+ Manager *m,
+ uid_t uid,
+ gid_t gid,
+ const char *name,
+ const char *home,
+ User **_user) {
+
User *u;
int r;
@@ -133,7 +150,7 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **
u = hashmap_get(m->users, UID_TO_PTR(uid));
if (!u) {
- r = user_new(&u, m, uid, gid, name);
+ r = user_new(&u, m, uid, gid, name, home);
if (r < 0)
return r;
}
@@ -144,7 +161,12 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **
return 0;
}
-int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
+int manager_add_user_by_name(
+ Manager *m,
+ const char *name,
+ User **_user) {
+
+ const char *home = NULL;
uid_t uid;
gid_t gid;
int r;
@@ -152,11 +174,11 @@ int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
assert(m);
assert(name);
- r = get_user_creds(&name, &uid, &gid, NULL, NULL);
+ r = get_user_creds(&name, &uid, &gid, &home, NULL, 0);
if (r < 0)
return r;
- return manager_add_user(m, uid, gid, name, _user);
+ return manager_add_user(m, uid, gid, name, home, _user);
}
int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
@@ -169,7 +191,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
if (!p)
return errno > 0 ? -errno : -ENOENT;
- return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
+ return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, _user);
}
int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
@@ -215,15 +237,22 @@ int manager_add_button(Manager *m, const char *name, Button **_button) {
return 0;
}
-int manager_process_seat_device(Manager *m, struct udev_device *d) {
+int manager_process_seat_device(Manager *m, sd_device *d) {
+ const char *action;
Device *device;
int r;
assert(m);
- if (streq_ptr(udev_device_get_action(d), "remove")) {
+ if (sd_device_get_property_value(d, "ACTION", &action) >= 0 &&
+ streq(action, "remove")) {
+ const char *syspath;
+
+ r = sd_device_get_syspath(d, &syspath);
+ if (r < 0)
+ return 0;
- device = hashmap_get(m->devices, udev_device_get_syspath(d));
+ device = hashmap_get(m->devices, syspath);
if (!device)
return 0;
@@ -231,27 +260,30 @@ int manager_process_seat_device(Manager *m, struct udev_device *d) {
device_free(device);
} else {
- const char *sn;
- Seat *seat = NULL;
+ const char *sn, *syspath;
bool master;
+ Seat *seat;
- sn = udev_device_get_property_value(d, "ID_SEAT");
- if (isempty(sn))
+ if (sd_device_get_property_value(d, "ID_SEAT", &sn) < 0 || isempty(sn))
sn = "seat0";
if (!seat_name_is_valid(sn)) {
- log_warning("Device with invalid seat name %s found, ignoring.", sn);
+ log_device_warning(d, "Device with invalid seat name %s found, ignoring.", sn);
return 0;
}
seat = hashmap_get(m->seats, sn);
- master = udev_device_has_tag(d, "master-of-seat");
+ master = sd_device_has_tag(d, "master-of-seat") > 0;
/* Ignore non-master devices for unknown seats */
if (!master && !seat)
return 0;
- r = manager_add_device(m, udev_device_get_syspath(d), master, &device);
+ r = sd_device_get_syspath(d, &syspath);
+ if (r < 0)
+ return r;
+
+ r = manager_add_device(m, syspath, master, &device);
if (r < 0)
return r;
@@ -272,16 +304,21 @@ int manager_process_seat_device(Manager *m, struct udev_device *d) {
return 0;
}
-int manager_process_button_device(Manager *m, struct udev_device *d) {
+int manager_process_button_device(Manager *m, sd_device *d) {
+ const char *action, *sysname;
Button *b;
-
int r;
assert(m);
- if (streq_ptr(udev_device_get_action(d), "remove")) {
+ r = sd_device_get_sysname(d, &sysname);
+ if (r < 0)
+ return r;
+
+ if (sd_device_get_property_value(d, "ACTION", &action) >= 0 &&
+ streq(action, "remove")) {
- b = hashmap_get(m->buttons, udev_device_get_sysname(d));
+ b = hashmap_get(m->buttons, sysname);
if (!b)
return 0;
@@ -290,12 +327,11 @@ int manager_process_button_device(Manager *m, struct udev_device *d) {
} else {
const char *sn;
- r = manager_add_button(m, udev_device_get_sysname(d), &b);
+ r = manager_add_button(m, sysname, &b);
if (r < 0)
return r;
- sn = udev_device_get_property_value(d, "ID_SEAT");
- if (isempty(sn))
+ if (sd_device_get_property_value(d, "ID_SEAT", &sn) < 0 || isempty(sn))
sn = "seat0";
button_set_seat(b, sn);
@@ -319,13 +355,16 @@ int manager_get_session_by_pid(Manager *m, pid_t pid, Session **ret) {
if (!pid_is_valid(pid))
return -EINVAL;
- r = cg_pid_get_unit(pid, &unit);
- if (r < 0)
- goto not_found;
+ s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(pid));
+ if (!s) {
+ r = cg_pid_get_unit(pid, &unit);
+ if (r < 0)
+ goto not_found;
- s = hashmap_get(m->session_units, unit);
- if (!s)
- goto not_found;
+ s = hashmap_get(m->session_units, unit);
+ if (!s)
+ goto not_found;
+ }
if (ret)
*ret = s;
@@ -459,7 +498,7 @@ int config_parse_n_autovts(
return 0;
}
-static int vt_is_busy(unsigned int vtnr) {
+static int vt_is_busy(unsigned vtnr) {
struct vt_stat vt_stat;
int r = 0;
_cleanup_close_ int fd;
@@ -487,9 +526,9 @@ static int vt_is_busy(unsigned int vtnr) {
return r;
}
-int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
+int manager_spawn_autovt(Manager *m, unsigned vtnr) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)];
+ char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned)];
int r;
assert(m);
@@ -522,9 +561,20 @@ int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
NULL,
"ss", name, "fail");
if (r < 0)
- log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to start %s: %s", name, bus_error_message(&error, r));
- return r;
+ return 0;
+}
+
+bool manager_is_lid_closed(Manager *m) {
+ Iterator i;
+ Button *b;
+
+ HASHMAP_FOREACH(b, m->buttons, i)
+ if (b->lid_closed)
+ return true;
+
+ return false;
}
static bool manager_is_docked(Manager *m) {
@@ -539,82 +589,60 @@ static bool manager_is_docked(Manager *m) {
}
static int manager_count_external_displays(Manager *m) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
- int r;
- int n = 0;
-
- e = udev_enumerate_new(m->udev);
- if (!e)
- return -ENOMEM;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d;
+ int r, n = 0;
- r = udev_enumerate_add_match_subsystem(e, "drm");
+ r = sd_device_enumerator_new(&e);
if (r < 0)
return r;
- r = udev_enumerate_scan_devices(e);
+ r = sd_device_enumerator_allow_uninitialized(e);
if (r < 0)
return r;
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
- struct udev_device *p;
- const char *status, *enabled, *dash, *nn, *i;
- bool external = false;
+ r = sd_device_enumerator_add_match_subsystem(e, "drm", true);
+ if (r < 0)
+ return r;
- d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
- if (!d)
- return -ENOMEM;
+ FOREACH_DEVICE(e, d) {
+ const char *status, *enabled, *dash, *nn, *subsys;
+ sd_device *p;
- p = udev_device_get_parent(d);
- if (!p)
+ if (sd_device_get_parent(d, &p) < 0)
continue;
/* If the parent shares the same subsystem as the
* device we are looking at then it is a connector,
* which is what we are interested in. */
- if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
+ if (sd_device_get_subsystem(p, &subsys) < 0 || !streq(subsys, "drm"))
continue;
- nn = udev_device_get_sysname(d);
- if (!nn)
+ if (sd_device_get_sysname(d, &nn) < 0)
continue;
- /* Ignore internal displays: the type is encoded in
- * the sysfs name, as the second dash separated item
- * (the first is the card name, the last the connector
- * number). We implement a whitelist of external
- * displays here, rather than a whitelist, to ensure
- * we don't block suspends too eagerly. */
+ /* Ignore internal displays: the type is encoded in the sysfs name, as the second dash separated item
+ * (the first is the card name, the last the connector number). We implement a blacklist of external
+ * displays here, rather than a whitelist of internal ones, to ensure we don't block suspends too
+ * eagerly. */
dash = strchr(nn, '-');
if (!dash)
continue;
dash++;
- FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-"
- "Composite-", "SVIDEO-", "Component-",
- "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") {
-
- if (startswith(dash, i)) {
- external = true;
- break;
- }
- }
- if (!external)
+ if (!STARTSWITH_SET(dash,
+ "VGA-", "DVI-I-", "DVI-D-", "DVI-A-"
+ "Composite-", "SVIDEO-", "Component-",
+ "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-"))
continue;
/* Ignore ports that are not enabled */
- enabled = udev_device_get_sysattr_value(d, "enabled");
- if (!enabled)
- continue;
- if (!streq_ptr(enabled, "enabled"))
+ if (sd_device_get_sysattr_value(d, "enabled", &enabled) < 0 || !streq(enabled, "enabled"))
continue;
/* We count any connector which is not explicitly
* "disconnected" as connected. */
- status = udev_device_get_sysattr_value(d, "status");
- if (!streq_ptr(status, "disconnected"))
+ if (sd_device_get_sysattr_value(d, "status", &status) < 0 || !streq(status, "disconnected"))
n++;
}
@@ -646,15 +674,13 @@ bool manager_is_docked_or_external_displays(Manager *m) {
bool manager_is_on_external_power(void) {
int r;
- /* For now we only check for AC power, but 'external power' can apply
- * to anything that isn't an internal battery */
+ /* For now we only check for AC power, but 'external power' can apply to anything that isn't an internal
+ * battery */
r = on_ac_power();
if (r < 0)
log_warning_errno(r, "Failed to read AC power status: %m");
- else if (r > 0)
- return true;
- return false;
+ return r != 0; /* Treat failure as 'on AC' */
}
bool manager_all_buttons_ignored(Manager *m) {
@@ -676,3 +702,142 @@ bool manager_all_buttons_ignored(Manager *m) {
return true;
}
+
+int manager_read_utmp(Manager *m) {
+#if ENABLE_UTMP
+ int r;
+
+ assert(m);
+
+ if (utmpxname(_PATH_UTMPX) < 0)
+ return log_error_errno(errno, "Failed to set utmp path to " _PATH_UTMPX ": %m");
+
+ setutxent();
+
+ for (;;) {
+ _cleanup_free_ char *t = NULL;
+ struct utmpx *u;
+ const char *c;
+ Session *s;
+
+ errno = 0;
+ u = getutxent();
+ if (!u) {
+ if (errno != 0)
+ log_warning_errno(errno, "Failed to read " _PATH_UTMPX ", ignoring: %m");
+ r = 0;
+ break;
+ }
+
+ if (u->ut_type != USER_PROCESS)
+ continue;
+
+ if (!pid_is_valid(u->ut_pid))
+ continue;
+
+ t = strndup(u->ut_line, sizeof(u->ut_line));
+ if (!t) {
+ r = log_oom();
+ break;
+ }
+
+ c = path_startswith(t, "/dev/");
+ if (c) {
+ r = free_and_strdup(&t, c);
+ if (r < 0) {
+ log_oom();
+ break;
+ }
+ }
+
+ if (isempty(t))
+ continue;
+
+ s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(u->ut_pid));
+ if (!s)
+ continue;
+
+ if (s->tty_validity == TTY_FROM_UTMP && !streq_ptr(s->tty, t)) {
+ /* This may happen on multiplexed SSH connection (i.e. 'SSH connection sharing'). In
+ * this case PAM and utmp sessions don't match. In such a case let's invalidate the TTY
+ * information and never acquire it again. */
+
+ s->tty = mfree(s->tty);
+ s->tty_validity = TTY_UTMP_INCONSISTENT;
+ log_debug("Session '%s' has inconsistent TTY information, dropping TTY information.", s->id);
+ continue;
+ }
+
+ /* Never override what we figured out once */
+ if (s->tty || s->tty_validity >= 0)
+ continue;
+
+ s->tty = TAKE_PTR(t);
+ s->tty_validity = TTY_FROM_UTMP;
+ log_debug("Acquired TTY information '%s' from utmp for session '%s'.", s->tty, s->id);
+ }
+
+ endutxent();
+ return r;
+#else
+ return 0;
+#endif
+}
+
+#if ENABLE_UTMP
+static int manager_dispatch_utmp(sd_event_source *s, const struct inotify_event *event, void *userdata) {
+ Manager *m = userdata;
+
+ assert(m);
+
+ /* If there's indication the file itself might have been removed or became otherwise unavailable, then let's
+ * reestablish the watch on whatever there's now. */
+ if ((event->mask & (IN_ATTRIB|IN_DELETE_SELF|IN_MOVE_SELF|IN_Q_OVERFLOW|IN_UNMOUNT)) != 0)
+ manager_connect_utmp(m);
+
+ (void) manager_read_utmp(m);
+ return 0;
+}
+#endif
+
+void manager_connect_utmp(Manager *m) {
+#if ENABLE_UTMP
+ sd_event_source *s = NULL;
+ int r;
+
+ assert(m);
+
+ /* Watch utmp for changes via inotify. We do this to deal with tools such as ssh, which will register the PAM
+ * session early, and acquire a TTY only much later for the connection. Thus during PAM the TTY won't be known
+ * yet. ssh will register itself with utmp when it finally acquired the TTY. Hence, let's make use of this, and
+ * watch utmp for the TTY asynchronously. We use the PAM session's leader PID as key, to find the right entry.
+ *
+ * Yes, relying on utmp is pretty ugly, but it's good enough for informational purposes, as well as idle
+ * detection (which, for tty sessions, relies on the TTY used) */
+
+ r = sd_event_add_inotify(m->event, &s, _PATH_UTMPX, IN_MODIFY|IN_MOVE_SELF|IN_DELETE_SELF|IN_ATTRIB, manager_dispatch_utmp, m);
+ if (r < 0)
+ log_full_errno(r == -ENOENT ? LOG_DEBUG: LOG_WARNING, r, "Failed to create inotify watch on " _PATH_UTMPX ", ignoring: %m");
+ else {
+ r = sd_event_source_set_priority(s, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ log_warning_errno(r, "Failed to adjust utmp event source priority, ignoring: %m");
+
+ (void) sd_event_source_set_description(s, "utmp");
+ }
+
+ sd_event_source_unref(m->utmp_event_source);
+ m->utmp_event_source = s;
+#endif
+}
+
+void manager_reconnect_utmp(Manager *m) {
+#if ENABLE_UTMP
+ assert(m);
+
+ if (m->utmp_event_source)
+ return;
+
+ manager_connect_utmp(m);
+#endif
+}
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 13298cc855..bd9f5ac4d6 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -5,6 +5,7 @@
#include <string.h>
#include <unistd.h>
+#include "sd-device.h"
#include "sd-messages.h"
#include "alloc-util.h"
@@ -13,24 +14,27 @@
#include "bus-error.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 "escape.h"
#include "fd-util.h"
#include "fileio-label.h"
+#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "logind.h"
+#include "missing_capability.h"
#include "mkdir.h"
#include "path-util.h"
#include "process-util.h"
-#include "cgroup-util.h"
#include "selinux-util.h"
#include "sleep-config.h"
#include "special.h"
#include "strv.h"
#include "terminal-util.h"
-#include "udev-util.h"
+#include "tmpfile-util.h"
#include "unit-name.h"
#include "user-util.h"
#include "utmp-wtmp.h"
@@ -273,6 +277,8 @@ static int property_get_scheduled_shutdown(
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction);
static BUS_DEFINE_PROPERTY_GET(property_get_docked, "b", Manager, manager_is_docked_or_external_displays);
+static BUS_DEFINE_PROPERTY_GET(property_get_lid_closed, "b", Manager, manager_is_lid_closed);
+static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_on_external_power, "b", manager_is_on_external_power);
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_compat_user_tasks_max, "t", CGROUP_LIMIT_MAX);
static BUS_DEFINE_PROPERTY_GET_REF(property_get_hashmap_size, "t", Hashmap *, (uint64_t) hashmap_size);
@@ -772,6 +778,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
} while (hashmap_get(m->sessions, id));
}
+ /* If we are not watching utmp aleady, try again */
+ manager_reconnect_utmp(m);
+
r = manager_add_user_by_uid(m, uid, &user);
if (r < 0)
goto fail;
@@ -781,9 +790,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
goto fail;
session_set_user(session, user);
+ session_set_leader(session, leader);
- session->leader = leader;
- session->audit_id = audit_id;
session->type = t;
session->class = c;
session->remote = remote;
@@ -795,6 +803,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
r = -ENOMEM;
goto fail;
}
+
+ session->tty_validity = TTY_FROM_PAM;
}
if (!isempty(display)) {
@@ -845,9 +855,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
r = sd_bus_message_enter_container(message, 'a', "(sv)");
if (r < 0)
- return r;
+ goto fail;
- r = session_start(session, message);
+ r = session_start(session, message, error);
if (r < 0)
goto fail;
@@ -1189,46 +1199,46 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
return sd_bus_reply_method_return(message, NULL);
}
-static int trigger_device(Manager *m, struct udev_device *d) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- struct udev_list_entry *first, *item;
+static int trigger_device(Manager *m, sd_device *d) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
int r;
assert(m);
- e = udev_enumerate_new(m->udev);
- if (!e)
- return -ENOMEM;
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
if (d) {
- r = udev_enumerate_add_match_parent(e, d);
+ r = sd_device_enumerator_add_match_parent(e, d);
if (r < 0)
return r;
}
- r = udev_enumerate_scan_devices(e);
- if (r < 0)
- return r;
-
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
+ FOREACH_DEVICE(e, d) {
_cleanup_free_ char *t = NULL;
const char *p;
- p = udev_list_entry_get_name(item);
+ r = sd_device_get_syspath(d, &p);
+ if (r < 0)
+ return r;
t = strappend(p, "/uevent");
if (!t)
return -ENOMEM;
- (void) write_string_file(t, "change", 0);
+ (void) write_string_file(t, "change", WRITE_STRING_FILE_DISABLE_BUFFER);
}
return 0;
}
static int attach_device(Manager *m, const char *seat, const char *sysfs) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_free_ char *rule = NULL, *file = NULL;
const char *id_for_seat;
int r;
@@ -1237,15 +1247,14 @@ static int attach_device(Manager *m, const char *seat, const char *sysfs) {
assert(seat);
assert(sysfs);
- d = udev_device_new_from_syspath(m->udev, sysfs);
- if (!d)
- return -ENODEV;
+ r = sd_device_new_from_syspath(&d, sysfs);
+ if (r < 0)
+ return r;
- if (!udev_device_has_tag(d, "seat"))
+ if (sd_device_has_tag(d, "seat") <= 0)
return -ENODEV;
- id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT");
- if (!id_for_seat)
+ if (sd_device_get_property_value(d, "ID_FOR_SEAT", &id_for_seat) < 0)
return -ENODEV;
if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0)
@@ -1254,7 +1263,7 @@ static int attach_device(Manager *m, const char *seat, const char *sysfs) {
if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0)
return -ENOMEM;
- mkdir_p_label("/etc/udev/rules.d", 0755);
+ (void) mkdir_p_label("/etc/udev/rules.d", 0755);
r = write_string_file_atomic_label(file, rule);
if (r < 0)
return r;
@@ -1654,10 +1663,10 @@ int bus_manager_shutdown_or_sleep_now_or_later(
if (r < 0)
return r;
- if (!streq(load_state, "loaded")) {
- log_notice("Unit %s is %s, refusing operation.", unit_name, load_state);
- return -EACCES;
- }
+ if (!streq(load_state, "loaded"))
+ return log_notice_errno(SYNTHETIC_ERRNO(EACCES),
+ "Unit %s is %s, refusing operation.",
+ unit_name, load_state);
/* Tell everybody to prepare for shutdown/sleep */
(void) send_prepare_for(m, w, true);
@@ -2155,6 +2164,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
if (cancelled && m->enable_wall_messages) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ _cleanup_free_ char *username = NULL;
const char *tty = NULL;
uid_t uid = 0;
int r;
@@ -2165,8 +2175,9 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
(void) sd_bus_creds_get_tty(creds, &tty);
}
+ username = uid_to_name(uid);
utmp_wall("The system shutdown has been cancelled",
- uid_to_name(uid), tty, logind_wall_tty_filter, m);
+ username, tty, logind_wall_tty_filter, m);
}
return sd_bus_reply_method_return(message, "b", cancelled);
@@ -2257,11 +2268,13 @@ static int method_can_shutdown_or_sleep(
if (r < 0)
return r;
- if (r > 0 && !result)
- result = "yes";
- else if (challenge && (!result || streq(result, "yes")))
- result = "challenge";
- else
+ if (r > 0) {
+ if (!result)
+ result = "yes";
+ } else if (challenge) {
+ if (!result || streq(result, "yes"))
+ result = "challenge";
+ } else
result = "no";
}
@@ -2448,7 +2461,7 @@ static int method_can_reboot_to_firmware_setup(
r = efi_reboot_to_firmware_supported();
if (r < 0) {
if (r != -EOPNOTSUPP)
- log_warning_errno(errno, "Failed to determine whether reboot to firmware is supported: %m");
+ log_warning_errno(r, "Failed to determine whether reboot to firmware is supported: %m");
return sd_bus_reply_method_return(message, "s", "na");
}
@@ -2637,13 +2650,14 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, 0),
SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("UserStopDelayUSec", "t", NULL, offsetof(Manager, user_stop_delay), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -2657,6 +2671,8 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0),
SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0),
SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0),
+ SD_BUS_PROPERTY("LidClosed", "b", property_get_lid_closed, 0, 0),
+ SD_BUS_PROPERTY("OnExternalPower", "b", property_get_on_external_power, 0, 0),
SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectorySize", "t", NULL, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -2724,24 +2740,20 @@ const sd_bus_vtable manager_vtable[] = {
};
static int session_jobs_reply(Session *s, const char *unit, const char *result) {
- int r = 0;
-
assert(s);
assert(unit);
if (!s->started)
- return r;
+ return 0;
- if (streq(result, "done"))
- r = session_send_create_reply(s, NULL);
- else {
+ if (result && !streq(result, "done")) {
_cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
- sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
- r = session_send_create_reply(s, &e);
+ sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit '%s' failed with '%s'", unit, result);
+ return session_send_create_reply(s, &e);
}
- return r;
+ return session_send_create_reply(s, NULL);
}
int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -2774,30 +2786,29 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
}
session = hashmap_get(m->session_units, unit);
- if (session && streq_ptr(path, session->scope_job)) {
- session->scope_job = mfree(session->scope_job);
- session_jobs_reply(session, unit, result);
+ if (session) {
+ if (streq_ptr(path, session->scope_job)) {
+ session->scope_job = mfree(session->scope_job);
+ (void) session_jobs_reply(session, unit, result);
+
+ session_save(session);
+ user_save(session->user);
+ }
- session_save(session);
- user_save(session->user);
session_add_to_gc_queue(session);
}
user = hashmap_get(m->user_units, unit);
- if (user &&
- (streq_ptr(path, user->service_job) ||
- streq_ptr(path, user->slice_job))) {
-
- if (streq_ptr(path, user->service_job))
+ if (user) {
+ if (streq_ptr(path, user->service_job)) {
user->service_job = mfree(user->service_job);
- if (streq_ptr(path, user->slice_job))
- user->slice_job = mfree(user->slice_job);
+ LIST_FOREACH(sessions_by_user, session, user->sessions)
+ (void) session_jobs_reply(session, unit, NULL /* don't propagate user service failures to the client */);
- LIST_FOREACH(sessions_by_user, session, user->sessions)
- session_jobs_reply(session, unit, result);
+ user_save(user);
+ }
- user_save(user);
user_add_to_gc_queue(user);
}
@@ -2929,13 +2940,15 @@ int manager_start_scope(
pid_t pid,
const char *slice,
const char *description,
- const char *after,
- const char *after2,
+ char **wants,
+ char **after,
+ const char *requires_mounts_for,
sd_bus_message *more_properties,
sd_bus_error *error,
char **job) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ char **i;
int r;
assert(manager);
@@ -2973,14 +2986,20 @@ int manager_start_scope(
return r;
}
- if (!isempty(after)) {
- r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after);
+ STRV_FOREACH(i, wants) {
+ r = sd_bus_message_append(m, "(sv)", "Wants", "as", 1, *i);
if (r < 0)
return r;
}
- if (!isempty(after2)) {
- r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2);
+ STRV_FOREACH(i, after) {
+ r = sd_bus_message_append(m, "(sv)", "After", "as", 1, *i);
+ if (r < 0)
+ return r;
+ }
+
+ if (!empty_or_root(requires_mounts_for)) {
+ r = sd_bus_message_append(m, "(sv)", "RequiresMountsFor", "as", 1, requires_mounts_for);
if (r < 0)
return r;
}
@@ -3077,7 +3096,8 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
return strdup_job(reply, job);
}
-int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) {
+int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *ret_error) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *path = NULL;
int r;
@@ -3094,17 +3114,16 @@ int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *err
path,
"org.freedesktop.systemd1.Scope",
"Abandon",
- error,
+ &error,
NULL,
NULL);
if (r < 0) {
- if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
- sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED) ||
- sd_bus_error_has_name(error, BUS_ERROR_SCOPE_NOT_RUNNING)) {
- sd_bus_error_free(error);
+ if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
+ sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED) ||
+ sd_bus_error_has_name(&error, BUS_ERROR_SCOPE_NOT_RUNNING))
return 0;
- }
+ sd_bus_error_move(ret_error, &error);
return r;
}
@@ -3126,7 +3145,7 @@ int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo
"ssi", unit, who == KILL_LEADER ? "main" : "all", signo);
}
-int manager_unit_is_active(Manager *manager, const char *unit) {
+int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *ret_error) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL;
@@ -3162,17 +3181,18 @@ int manager_unit_is_active(Manager *manager, const char *unit) {
sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
return false;
+ sd_bus_error_move(ret_error, &error);
return r;
}
r = sd_bus_message_read(reply, "s", &state);
if (r < 0)
- return -EINVAL;
+ return r;
- return !streq(state, "inactive") && !streq(state, "failed");
+ return !STR_IN_SET(state, "inactive", "failed");
}
-int manager_job_is_active(Manager *manager, const char *path) {
+int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *ret_error) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
@@ -3197,6 +3217,7 @@ int manager_job_is_active(Manager *manager, const char *path) {
if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
return false;
+ sd_bus_error_move(ret_error, &error);
return r;
}
diff --git a/src/login/logind-device.c b/src/login/logind-device.c
index 9b5b3e8798..e724365b13 100644
--- a/src/login/logind-device.c
+++ b/src/login/logind-device.c
@@ -99,6 +99,8 @@ void device_attach(Device *d, Seat *s) {
}
}
- if (!had_master && d->master)
+ if (!had_master && d->master && s->started) {
+ seat_save(s);
seat_send_changed(s, "CanGraphical", NULL);
+ }
}
diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf
index c85339dcd3..8829ce7d85 100644
--- a/src/login/logind-gperf.gperf
+++ b/src/login/logind-gperf.gperf
@@ -23,6 +23,7 @@ Login.KillUserProcesses, config_parse_bool, 0, offse
Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users)
Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users)
Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max)
+Login.UserStopDelaySec, config_parse_sec, 0, offsetof(Manager, user_stop_delay)
Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key)
Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key)
Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key)
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index b1f45baaca..415c26b147 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -6,6 +6,7 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "env-file.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -15,6 +16,7 @@
#include "parse-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
@@ -193,15 +195,14 @@ int inhibitor_load(Inhibitor *i) {
char *cc;
int r;
- r = parse_env_file(NULL, i->state_file, NEWLINE,
+ r = parse_env_file(NULL, i->state_file,
"WHAT", &what,
"UID", &uid,
"PID", &pid,
"WHO", &who,
"WHY", &why,
"MODE", &mode,
- "FIFO", &i->fifo_path,
- NULL);
+ "FIFO", &i->fifo_path);
if (r < 0)
return r;
diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c
index c4d9b067c6..6ee5a1c95d 100644
--- a/src/login/logind-seat-dbus.c
+++ b/src/login/logind-seat-dbus.c
@@ -9,6 +9,7 @@
#include "bus-util.h"
#include "logind-seat.h"
#include "logind.h"
+#include "missing_capability.h"
#include "strv.h"
#include "user-util.h"
#include "util.h"
@@ -183,7 +184,7 @@ static int method_activate_session(sd_bus_message *message, void *userdata, sd_b
static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Seat *s = userdata;
- unsigned int to;
+ unsigned to;
int r;
assert(message);
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index 63253db5bf..c758ffd5fa 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -19,35 +19,45 @@
#include "stdio-util.h"
#include "string-util.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "util.h"
-Seat *seat_new(Manager *m, const char *id) {
- Seat *s;
+int seat_new(Seat** ret, Manager *m, const char *id) {
+ _cleanup_(seat_freep) Seat *s = NULL;
+ int r;
+ assert(ret);
assert(m);
assert(id);
- s = new0(Seat, 1);
+ if (!seat_name_is_valid(id))
+ return -EINVAL;
+
+ s = new(Seat, 1);
if (!s)
- return NULL;
+ return -ENOMEM;
+
+ *s = (Seat) {
+ .manager = m,
+ };
s->state_file = strappend("/run/systemd/seats/", id);
if (!s->state_file)
- return mfree(s);
+ return -ENOMEM;
s->id = basename(s->state_file);
- s->manager = m;
- if (hashmap_put(m->seats, s->id, s) < 0) {
- free(s->state_file);
- return mfree(s);
- }
+ r = hashmap_put(m->seats, s->id, s);
+ if (r < 0)
+ return r;
- return s;
+ *ret = TAKE_PTR(s);
+ return 0;
}
-void seat_free(Seat *s) {
- assert(s);
+Seat* seat_free(Seat *s) {
+ if (!s)
+ return NULL;
if (s->in_gc_queue)
LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
@@ -64,7 +74,8 @@ void seat_free(Seat *s) {
free(s->positions);
free(s->state_file);
- free(s);
+
+ return mfree(s);
}
int seat_save(Seat *s) {
@@ -156,8 +167,8 @@ int seat_load(Seat *s) {
return 0;
}
-static int vt_allocate(unsigned int vtnr) {
- char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)];
+static int vt_allocate(unsigned vtnr) {
+ char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned)];
_cleanup_close_ int fd = -1;
assert(vtnr >= 1);
@@ -165,7 +176,7 @@ static int vt_allocate(unsigned int vtnr) {
xsprintf(p, "/dev/tty%u", vtnr);
fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
- return -errno;
+ return fd;
return 0;
}
@@ -189,10 +200,8 @@ int seat_preallocate_vts(Seat *s) {
int q;
q = vt_allocate(i);
- if (q < 0) {
- log_error_errno(q, "Failed to preallocate VT %u: %m", i);
- r = q;
- }
+ if (q < 0)
+ r = log_error_errno(q, "Failed to preallocate VT %u: %m", i);
}
return r;
@@ -203,16 +212,15 @@ int seat_apply_acls(Seat *s, Session *old_active) {
assert(s);
- r = devnode_acl_all(s->manager->udev,
- s->id,
+ 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);
if (r < 0)
- log_error_errno(r, "Failed to apply ACLs: %m");
+ return log_error_errno(r, "Failed to apply ACLs: %m");
- return r;
+ return 0;
}
int seat_set_active(Seat *s, Session *session) {
@@ -232,7 +240,7 @@ int seat_set_active(Seat *s, Session *session) {
session_send_changed(old_active, "Active", NULL);
}
- seat_apply_acls(s, old_active);
+ (void) seat_apply_acls(s, old_active);
if (session && session->started) {
session_send_changed(session, "Active", NULL);
@@ -258,7 +266,7 @@ int seat_set_active(Seat *s, Session *session) {
return 0;
}
-int seat_switch_to(Seat *s, unsigned int num) {
+int seat_switch_to(Seat *s, unsigned num) {
/* Public session positions skip 0 (there is only F1-F12). Maybe it
* will get reassigned in the future, so return error for now. */
if (num == 0)
@@ -276,7 +284,7 @@ int seat_switch_to(Seat *s, unsigned int num) {
}
int seat_switch_to_next(Seat *s) {
- unsigned int start, i;
+ unsigned start, i;
if (s->position_count == 0)
return -EINVAL;
@@ -297,7 +305,7 @@ int seat_switch_to_next(Seat *s) {
}
int seat_switch_to_previous(Seat *s) {
- unsigned int start, i;
+ unsigned start, i;
if (s->position_count == 0)
return -EINVAL;
@@ -317,7 +325,7 @@ int seat_switch_to_previous(Seat *s) {
return -EINVAL;
}
-int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
+int seat_active_vt_changed(Seat *s, unsigned vtnr) {
Session *i, *new_active = NULL;
int r;
@@ -356,8 +364,7 @@ int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
int seat_read_active_vt(Seat *s) {
char t[64];
ssize_t k;
- unsigned int vtnr;
- int r;
+ int vtnr;
assert(s);
@@ -376,17 +383,9 @@ int seat_read_active_vt(Seat *s) {
t[k] = 0;
truncate_nl(t);
- if (!startswith(t, "tty")) {
- log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
- return -EIO;
- }
-
- r = safe_atou(t+3, &vtnr);
- if (r < 0)
- return log_error_errno(r, "Failed to parse VT number \"%s\": %m", t+3);
-
- if (!vtnr) {
- log_error("VT number invalid: %s", t+3);
+ vtnr = vtnr_from_tty(t);
+ if (vtnr < 0) {
+ log_error_errno(vtnr, "Hm, /sys/class/tty/tty0/active is badly formatted: %m");
return -EIO;
}
@@ -421,7 +420,7 @@ int seat_start(Seat *s) {
}
int seat_stop(Seat *s, bool force) {
- int r = 0;
+ int r;
assert(s);
@@ -431,9 +430,9 @@ int seat_stop(Seat *s, bool force) {
"SEAT_ID=%s", s->id,
LOG_MESSAGE("Removed seat %s.", s->id));
- seat_stop_sessions(s, force);
+ r = seat_stop_sessions(s, force);
- unlink(s->state_file);
+ (void) unlink(s->state_file);
seat_add_to_gc_queue(s);
if (s->started)
@@ -461,7 +460,7 @@ int seat_stop_sessions(Seat *s, bool force) {
void seat_evict_position(Seat *s, Session *session) {
Session *iter;
- unsigned int pos = session->position;
+ unsigned pos = session->position;
session->position = 0;
@@ -483,7 +482,7 @@ void seat_evict_position(Seat *s, Session *session) {
}
}
-void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
+void seat_claim_position(Seat *s, Session *session, unsigned pos) {
/* with VTs, the position is always the same as the VTnr */
if (seat_has_vts(s))
pos = session->vtnr;
@@ -499,7 +498,7 @@ void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
}
static void seat_assign_position(Seat *s, Session *session) {
- unsigned int pos;
+ unsigned pos;
if (session->position > 0)
return;
diff --git a/src/login/logind-seat.h b/src/login/logind-seat.h
index 70878bbe52..6236f1360b 100644
--- a/src/login/logind-seat.h
+++ b/src/login/logind-seat.h
@@ -27,25 +27,27 @@ struct Seat {
LIST_FIELDS(Seat, gc_queue);
};
-Seat *seat_new(Manager *m, const char *id);
-void seat_free(Seat *s);
+int seat_new(Seat **ret, Manager *m, const char *id);
+Seat* seat_free(Seat *s);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Seat *, seat_free);
int seat_save(Seat *s);
int seat_load(Seat *s);
int seat_apply_acls(Seat *s, Session *old_active);
int seat_set_active(Seat *s, Session *session);
-int seat_switch_to(Seat *s, unsigned int num);
+int seat_switch_to(Seat *s, unsigned num);
int seat_switch_to_next(Seat *s);
int seat_switch_to_previous(Seat *s);
-int seat_active_vt_changed(Seat *s, unsigned int vtnr);
+int seat_active_vt_changed(Seat *s, unsigned vtnr);
int seat_read_active_vt(Seat *s);
int seat_preallocate_vts(Seat *s);
int seat_attach_session(Seat *s, Session *session);
void seat_complete_switch(Seat *s);
void seat_evict_position(Seat *s, Session *session);
-void seat_claim_position(Seat *s, Session *session, unsigned int pos);
+void seat_claim_position(Seat *s, Session *session, unsigned pos);
bool seat_has_vts(Seat *s);
bool seat_is_seat0(Seat *s);
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index 25c4981dc0..df5bfba982 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -11,7 +11,9 @@
#include "logind-session-device.h"
#include "logind-session.h"
#include "logind.h"
+#include "missing_capability.h"
#include "signal-util.h"
+#include "stat-util.h"
#include "strv.h"
#include "util.h"
@@ -380,6 +382,9 @@ static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_er
if (r < 0)
return r;
+ if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
+
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
@@ -427,6 +432,9 @@ static int method_release_device(sd_bus_message *message, void *userdata, sd_bus
if (r < 0)
return r;
+ if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
+
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
@@ -455,6 +463,9 @@ static int method_pause_device_complete(sd_bus_message *message, void *userdata,
if (r < 0)
return r;
+ if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
+
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
@@ -689,6 +700,15 @@ int session_send_lock_all(Manager *m, bool lock) {
return r;
}
+static bool session_ready(Session *s) {
+ assert(s);
+
+ /* Returns true when the session is ready, i.e. all jobs we enqueued for it are done (regardless if successful or not) */
+
+ return !s->scope_job &&
+ !s->user->service_job;
+}
+
int session_send_create_reply(Session *s, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
_cleanup_close_ int fifo_fd = -1;
@@ -696,19 +716,16 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
assert(s);
- /* This is called after the session scope and the user service
- * were successfully created, and finishes where
+ /* This is called after the session scope and the user service were successfully created, and finishes where
* bus_manager_create_session() left off. */
if (!s->create_message)
return 0;
- if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job))
+ if (!sd_bus_error_is_set(error) && !session_ready(s))
return 0;
- c = s->create_message;
- s->create_message = NULL;
-
+ c = TAKE_PTR(s->create_message);
if (error)
return sd_bus_reply_method_error(c, error);
@@ -716,8 +733,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
if (fifo_fd < 0)
return fifo_fd;
- /* Update the session state file before we notify the client
- * about the result. */
+ /* Update the session state file before we notify the client about the result. */
session_save(s);
p = session_bus_path(s);
diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c
index c2dfe9f974..f358524ebc 100644
--- a/src/login/logind-session-device.c
+++ b/src/login/logind-session-device.c
@@ -6,7 +6,7 @@
#include <sys/ioctl.h>
#include <sys/types.h>
-#include "libudev.h"
+#include "sd-device.h"
#include "alloc-util.h"
#include "bus-util.h"
@@ -175,10 +175,9 @@ static int session_device_start(SessionDevice *sd) {
switch (sd->type) {
case DEVICE_TYPE_DRM:
- if (sd->fd < 0) {
- log_error("Failed to re-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
- return -EBADF;
- }
+ if (sd->fd < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADF),
+ "Failed to re-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
/* Device is kept open. Simply call drmSetMaster() and hope there is no-one else. In case it fails, we
* keep the device paused. Maybe at some point we have a drmStealMaster(). */
@@ -247,18 +246,18 @@ static void session_device_stop(SessionDevice *sd) {
sd->active = false;
}
-static DeviceType detect_device_type(struct udev_device *dev) {
+static DeviceType detect_device_type(sd_device *dev) {
const char *sysname, *subsystem;
- DeviceType type;
+ DeviceType type = DEVICE_TYPE_UNKNOWN;
- sysname = udev_device_get_sysname(dev);
- subsystem = udev_device_get_subsystem(dev);
- type = DEVICE_TYPE_UNKNOWN;
+ if (sd_device_get_sysname(dev, &sysname) < 0 ||
+ sd_device_get_subsystem(dev, &subsystem) < 0)
+ return type;
- if (streq_ptr(subsystem, "drm")) {
+ if (streq(subsystem, "drm")) {
if (startswith(sysname, "card"))
type = DEVICE_TYPE_DRM;
- } else if (streq_ptr(subsystem, "input")) {
+ } else if (streq(subsystem, "input")) {
if (startswith(sysname, "event"))
type = DEVICE_TYPE_EVDEV;
}
@@ -267,42 +266,38 @@ static DeviceType detect_device_type(struct udev_device *dev) {
}
static int session_device_verify(SessionDevice *sd) {
- struct udev_device *dev, *p = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *p = NULL;
const char *sp, *node;
+ sd_device *dev;
int r;
- dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev);
- if (!dev)
- return -ENODEV;
+ r = sd_device_new_from_devnum(&p, 'c', sd->dev);
+ if (r < 0)
+ return r;
- sp = udev_device_get_syspath(dev);
- node = udev_device_get_devnode(dev);
- if (!node) {
- r = -EINVAL;
- goto err_dev;
- }
+ dev = p;
+
+ if (sd_device_get_syspath(dev, &sp) < 0 ||
+ sd_device_get_devname(dev, &node) < 0)
+ return -EINVAL;
/* detect device type so we can find the correct sysfs parent */
sd->type = detect_device_type(dev);
- if (sd->type == DEVICE_TYPE_UNKNOWN) {
- r = -ENODEV;
- goto err_dev;
- } else if (sd->type == DEVICE_TYPE_EVDEV) {
+ if (sd->type == DEVICE_TYPE_UNKNOWN)
+ return -ENODEV;
+
+ else if (sd->type == DEVICE_TYPE_EVDEV) {
/* for evdev devices we need the parent node as device */
- p = dev;
- dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL);
- if (!dev) {
- r = -ENODEV;
- goto err_dev;
- }
- sp = udev_device_get_syspath(dev);
- } else if (sd->type != DEVICE_TYPE_DRM) {
+ if (sd_device_get_parent_with_subsystem_devtype(p, "input", NULL, &dev) < 0)
+ return -ENODEV;
+ if (sd_device_get_syspath(dev, &sp) < 0)
+ return -ENODEV;
+
+ } else if (sd->type != DEVICE_TYPE_DRM)
/* Prevent opening unsupported devices. Especially devices of
* subsystem "input" must be opened via the evdev node as
* we require EVIOCREVOKE. */
- r = -ENODEV;
- goto err_dev;
- }
+ return -ENODEV;
/* search for an existing seat device and return it if available */
sd->device = hashmap_get(sd->session->manager->devices, sp);
@@ -312,31 +307,22 @@ static int session_device_verify(SessionDevice *sd) {
* logind-manager handle the new device. */
r = manager_process_seat_device(sd->session->manager, dev);
if (r < 0)
- goto err_dev;
+ return r;
/* if it's still not available, then the device is invalid */
sd->device = hashmap_get(sd->session->manager->devices, sp);
- if (!sd->device) {
- r = -ENODEV;
- goto err_dev;
- }
+ if (!sd->device)
+ return -ENODEV;
}
- if (sd->device->seat != sd->session->seat) {
- r = -EPERM;
- goto err_dev;
- }
+ if (sd->device->seat != sd->session->seat)
+ return -EPERM;
sd->node = strdup(node);
- if (!sd->node) {
- r = -ENOMEM;
- goto err_dev;
- }
+ if (!sd->node)
+ return -ENOMEM;
- r = 0;
-err_dev:
- udev_device_unref(p ? : dev);
- return r;
+ return 0;
}
int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out) {
@@ -481,7 +467,7 @@ void session_device_pause_all(Session *s) {
}
}
-unsigned int session_device_try_pause_all(Session *s) {
+unsigned session_device_try_pause_all(Session *s) {
unsigned num_pending = 0;
SessionDevice *sd;
Iterator i;
diff --git a/src/login/logind-session-device.h b/src/login/logind-session-device.h
index f42c0218c0..6c20403d12 100644
--- a/src/login/logind-session-device.h
+++ b/src/login/logind-session-device.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
typedef enum DeviceType DeviceType;
typedef struct SessionDevice SessionDevice;
@@ -34,7 +33,7 @@ void session_device_complete_pause(SessionDevice *sd);
void session_device_resume_all(Session *s);
void session_device_pause_all(Session *s);
-unsigned int session_device_try_pause_all(Session *s);
+unsigned session_device_try_pause_all(Session *s);
int session_device_save(SessionDevice *sd);
void session_device_attach_fd(SessionDevice *sd, int fd, bool active);
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 69d5a10319..4b4dd4c060 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -5,6 +5,7 @@
#include <linux/kd.h>
#include <linux/vt.h>
#include <signal.h>
+#include <stdio_ext.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
@@ -15,6 +16,7 @@
#include "audit-util.h"
#include "bus-error.h"
#include "bus-util.h"
+#include "env-file.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -24,57 +26,66 @@
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
+#include "serialize.h"
#include "string-table.h"
+#include "strv.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
-#include "process-util.h"
#define RELEASE_USEC (20*USEC_PER_SEC)
static void session_remove_fifo(Session *s);
+static void session_restore_vt(Session *s);
-Session* session_new(Manager *m, const char *id) {
- Session *s;
+int session_new(Session **ret, Manager *m, const char *id) {
+ _cleanup_(session_freep) Session *s = NULL;
+ int r;
+ assert(ret);
assert(m);
assert(id);
- assert(session_id_valid(id));
- s = new0(Session, 1);
+ if (!session_id_valid(id))
+ return -EINVAL;
+
+ s = new(Session, 1);
if (!s)
- return NULL;
+ return -ENOMEM;
+
+ *s = (Session) {
+ .manager = m,
+ .fifo_fd = -1,
+ .vtfd = -1,
+ .audit_id = AUDIT_SESSION_INVALID,
+ .tty_validity = _TTY_VALIDITY_INVALID,
+ };
s->state_file = strappend("/run/systemd/sessions/", id);
if (!s->state_file)
- return mfree(s);
-
- s->devices = hashmap_new(&devt_hash_ops);
- if (!s->devices) {
- free(s->state_file);
- return mfree(s);
- }
+ return -ENOMEM;
s->id = basename(s->state_file);
- if (hashmap_put(m->sessions, s->id, s) < 0) {
- hashmap_free(s->devices);
- free(s->state_file);
- return mfree(s);
- }
+ s->devices = hashmap_new(&devt_hash_ops);
+ if (!s->devices)
+ return -ENOMEM;
- s->manager = m;
- s->fifo_fd = -1;
- s->vtfd = -1;
- s->audit_id = AUDIT_SESSION_INVALID;
+ r = hashmap_put(m->sessions, s->id, s);
+ if (r < 0)
+ return r;
- return s;
+ *ret = TAKE_PTR(s);
+ return 0;
}
-void session_free(Session *s) {
+Session* session_free(Session *s) {
SessionDevice *sd;
- assert(s);
+ if (!s)
+ return NULL;
if (s->in_gc_queue)
LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s);
@@ -95,6 +106,8 @@ void session_free(Session *s) {
if (s->user->display == s)
s->user->display = NULL;
+
+ user_update_last_session_timer(s->user);
}
if (s->seat) {
@@ -112,6 +125,9 @@ void session_free(Session *s) {
free(s->scope);
}
+ if (pid_is_valid(s->leader))
+ (void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s);
+
free(s->scope_job);
sd_bus_message_unref(s->create_message);
@@ -126,7 +142,8 @@ void session_free(Session *s) {
hashmap_remove(s->manager->sessions, s->id);
free(s->state_file);
- free(s);
+
+ return mfree(s);
}
void session_set_user(Session *s, User *u) {
@@ -135,6 +152,32 @@ void session_set_user(Session *s, User *u) {
s->user = u;
LIST_PREPEND(sessions_by_user, u->sessions, s);
+
+ user_update_last_session_timer(u);
+}
+
+int session_set_leader(Session *s, pid_t pid) {
+ int r;
+
+ assert(s);
+
+ if (!pid_is_valid(pid))
+ return -EINVAL;
+
+ if (s->leader == pid)
+ return 0;
+
+ r = hashmap_put(s->manager->sessions_by_leader, PID_TO_PTR(pid), s);
+ if (r < 0)
+ return r;
+
+ if (pid_is_valid(s->leader))
+ (void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s);
+
+ s->leader = pid;
+ (void) audit_session_from_pid(pid, &s->audit_id);
+
+ return 1;
}
static void session_save_devices(Session *s, FILE *f) {
@@ -170,20 +213,21 @@ int session_save(Session *s) {
if (r < 0)
goto fail;
- assert(s->user);
-
- fchmod(fileno(f), 0644);
+ (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+ (void) fchmod(fileno(f), 0644);
fprintf(f,
"# This is private data. Do not parse.\n"
"UID="UID_FMT"\n"
"USER=%s\n"
"ACTIVE=%i\n"
+ "IS_DISPLAY=%i\n"
"STATE=%s\n"
"REMOTE=%i\n",
s->user->uid,
s->user->name,
session_is_active(s),
+ s->user->display == s,
session_state_to_string(session_get_state(s)),
s->remote);
@@ -207,6 +251,9 @@ int session_save(Session *s) {
if (s->tty)
fprintf(f, "TTY=%s\n", s->tty);
+ if (s->tty_validity >= 0)
+ fprintf(f, "TTY_VALIDITY=%s\n", tty_validity_to_string(s->tty_validity));
+
if (s->display)
fprintf(f, "DISPLAY=%s\n", s->display);
@@ -343,6 +390,7 @@ static int session_load_devices(Session *s, const char *devices) {
int session_load(Session *s) {
_cleanup_free_ char *remote = NULL,
*seat = NULL,
+ *tty_validity = NULL,
*vtnr = NULL,
*state = NULL,
*position = NULL,
@@ -354,19 +402,21 @@ int session_load(Session *s) {
*monotonic = NULL,
*controller = NULL,
*active = NULL,
- *devices = NULL;
+ *devices = NULL,
+ *is_display = NULL;
int k, r;
assert(s);
- r = parse_env_file(NULL, s->state_file, NEWLINE,
+ r = parse_env_file(NULL, s->state_file,
"REMOTE", &remote,
"SCOPE", &s->scope,
"SCOPE_JOB", &s->scope_job,
"FIFO", &s->fifo_path,
"SEAT", &seat,
"TTY", &s->tty,
+ "TTY_VALIDITY", &tty_validity,
"DISPLAY", &s->display,
"REMOTE_HOST", &s->remote_host,
"REMOTE_USER", &s->remote_user,
@@ -384,7 +434,7 @@ int session_load(Session *s) {
"CONTROLLER", &controller,
"ACTIVE", &active,
"DEVICES", &devices,
- NULL);
+ "IS_DISPLAY", &is_display);
if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", s->state_file);
@@ -393,10 +443,10 @@ int session_load(Session *s) {
uid_t u;
User *user;
- if (!uid) {
- log_error("UID not specified for session %s", s->id);
- return -ENOENT;
- }
+ if (!uid)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+ "UID not specified for session %s",
+ s->id);
r = parse_uid(uid, &u);
if (r < 0) {
@@ -405,10 +455,10 @@ int session_load(Session *s) {
}
user = hashmap_get(s->manager->users, UID_TO_PTR(u));
- if (!user) {
- log_error("User of session %s not known.", s->id);
- return -ENOENT;
- }
+ if (!user)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+ "User of session %s not known.",
+ s->id);
session_set_user(s, user);
}
@@ -436,15 +486,33 @@ int session_load(Session *s) {
s->vtnr = 0;
if (position && s->seat) {
- unsigned int npos;
+ unsigned npos;
safe_atou(position, &npos);
seat_claim_position(s->seat, s, npos);
}
+ if (tty_validity) {
+ TTYValidity v;
+
+ v = tty_validity_from_string(tty_validity);
+ if (v < 0)
+ log_debug("Failed to parse TTY validity: %s", tty_validity);
+ else
+ s->tty_validity = v;
+ }
+
if (leader) {
- if (parse_pid(leader, &s->leader) >= 0)
- (void) audit_session_from_pid(s->leader, &s->audit_id);
+ pid_t pid;
+
+ r = parse_pid(leader, &pid);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse leader PID of session: %s", leader);
+ else {
+ r = session_set_leader(s, pid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set session leader PID, ignoring: %m");
+ }
}
if (type) {
@@ -481,9 +549,9 @@ int session_load(Session *s) {
}
if (realtime)
- timestamp_deserialize(realtime, &s->timestamp.realtime);
+ (void) deserialize_usec(realtime, &s->timestamp.realtime);
if (monotonic)
- timestamp_deserialize(monotonic, &s->timestamp.monotonic);
+ (void) deserialize_usec(monotonic, &s->timestamp.monotonic);
if (active) {
k = parse_boolean(active);
@@ -491,6 +559,18 @@ int session_load(Session *s) {
s->was_active = k;
}
+ if (is_display) {
+ /* Note that when enumerating users are loaded before sessions, hence the display session to use is
+ * something we have to store along with the session and not the user, as in that case we couldn't
+ * apply it at the time we load the user. */
+
+ k = parse_boolean(is_display);
+ if (k < 0)
+ log_warning_errno(k, "Failed to parse IS_DISPLAY session property: %m");
+ else if (k > 0)
+ s->user->display = s;
+ }
+
if (controller) {
if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) {
session_set_controller(s, controller, false, false);
@@ -503,7 +583,7 @@ int session_load(Session *s) {
}
int session_activate(Session *s) {
- unsigned int num_pending;
+ unsigned num_pending;
assert(s);
assert(s->user);
@@ -516,7 +596,7 @@ int session_activate(Session *s) {
/* on seats with VTs, we let VTs manage session-switching */
if (seat_has_vts(s->seat)) {
- if (!s->vtnr)
+ if (s->vtnr == 0)
return -EOPNOTSUPP;
return chvt(s->vtnr);
@@ -539,17 +619,18 @@ int session_activate(Session *s) {
return 0;
}
-static int session_start_scope(Session *s, sd_bus_message *properties) {
+static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_error *error) {
int r;
assert(s);
assert(s->user);
if (!s->scope) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char *scope, *job = NULL;
+ _cleanup_free_ char *scope = NULL;
const char *description;
+ s->scope_job = mfree(s->scope_job);
+
scope = strjoin("session-", s->id, ".scope");
if (!scope)
return log_oom();
@@ -562,21 +643,16 @@ static int session_start_scope(Session *s, sd_bus_message *properties) {
s->leader,
s->user->slice,
description,
- "systemd-logind.service",
- "systemd-user-sessions.service",
+ STRV_MAKE(s->user->runtime_dir_service, s->user->service), /* These two have StopWhenUnneeded= set, hence add a dep towards them */
+ STRV_MAKE("systemd-logind.service", "systemd-user-sessions.service", s->user->runtime_dir_service, s->user->service), /* And order us after some more */
+ s->user->home,
properties,
- &error,
- &job);
- if (r < 0) {
- log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r));
- free(scope);
- return r;
- } else {
- s->scope = scope;
+ error,
+ &s->scope_job);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(error, r));
- free(s->scope_job);
- s->scope_job = job;
- }
+ s->scope = TAKE_PTR(scope);
}
if (s->scope)
@@ -585,7 +661,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties) {
return 0;
}
-int session_start(Session *s, sd_bus_message *properties) {
+int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
int r;
assert(s);
@@ -593,6 +669,9 @@ int session_start(Session *s, sd_bus_message *properties) {
if (!s->user)
return -ESTALE;
+ if (s->stopping)
+ return -EINVAL;
+
if (s->started)
return 0;
@@ -600,8 +679,7 @@ int session_start(Session *s, sd_bus_message *properties) {
if (r < 0)
return r;
- /* Create cgroup */
- r = session_start_scope(s, properties);
+ r = session_start_scope(s, properties, error);
if (r < 0)
return r;
@@ -652,21 +730,24 @@ static int session_stop_scope(Session *s, bool force) {
* that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log
* when killing any processes left after this point. */
r = manager_abandon_scope(s->manager, s->scope, &error);
- if (r < 0)
+ if (r < 0) {
log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r));
+ sd_bus_error_free(&error);
+ }
+
+ 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)) {
- char *job = NULL;
- r = manager_stop_unit(s->manager, s->scope, &error, &job);
- if (r < 0)
- return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r));
+ r = manager_stop_unit(s->manager, s->scope, &error, &s->scope_job);
+ if (r < 0) {
+ if (force)
+ return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r));
- free(s->scope_job);
- s->scope_job = job;
+ log_warning_errno(r, "Failed to stop session scope, ignoring: %s", bus_error_message(&error, r));
+ }
} else {
- s->scope_job = mfree(s->scope_job);
/* With no killing, this session is allowed to persist in "closing" state indefinitely.
* Therefore session stop and session removal may be two distinct events.
@@ -686,8 +767,17 @@ int session_stop(Session *s, bool force) {
assert(s);
+ /* This is called whenever we begin with tearing down a session record. It's called in four cases: explicit API
+ * request via the bus (either directly for the session object or for the seat or user object this session
+ * belongs to; 'force' is true), or due to automatic GC (i.e. scope vanished; 'force' is false), or because the
+ * session FIFO saw an EOF ('force' is false), or because the release timer hit ('force' is false). */
+
if (!s->user)
return -ESTALE;
+ if (!s->started)
+ return 0;
+ if (s->stopping)
+ return 0;
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
@@ -779,7 +869,7 @@ int session_release(Session *s) {
return sd_event_add_time(s->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + RELEASE_USEC, 0,
+ usec_add(now(CLOCK_MONOTONIC), RELEASE_USEC), 0,
release_timeout_callback, s);
}
@@ -858,7 +948,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) {
/* For sessions with a leader but no explicitly configured
* tty, let's check the controlling tty of the leader */
- if (s->leader > 0) {
+ if (pid_is_valid(s->leader)) {
r = get_process_ctty_atime(s->leader, &atime);
if (r >= 0)
goto found_atime;
@@ -942,7 +1032,8 @@ int session_create_fifo(Session *s) {
if (r < 0)
return r;
- if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
+ s->fifo_path = strjoin("/run/systemd/sessions/", s->id, ".ref");
+ if (!s->fifo_path)
return -ENOMEM;
if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
@@ -954,7 +1045,6 @@ int session_create_fifo(Session *s) {
s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (s->fifo_fd < 0)
return -errno;
-
}
if (!s->fifo_event_source) {
@@ -984,12 +1074,14 @@ static void session_remove_fifo(Session *s) {
s->fifo_fd = safe_close(s->fifo_fd);
if (s->fifo_path) {
- unlink(s->fifo_path);
+ (void) unlink(s->fifo_path);
s->fifo_path = mfree(s->fifo_path);
}
}
bool session_may_gc(Session *s, bool drop_not_started) {
+ int r;
+
assert(s);
if (drop_not_started && !s->started)
@@ -1003,11 +1095,25 @@ bool session_may_gc(Session *s, bool drop_not_started) {
return false;
}
- if (s->scope_job && manager_job_is_active(s->manager, s->scope_job))
- return false;
+ if (s->scope_job) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ r = manager_job_is_active(s->manager, s->scope_job, &error);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine whether job '%s' is pending, ignoring: %s", s->scope_job, bus_error_message(&error, r));
+ if (r != 0)
+ return false;
+ }
+
+ if (s->scope) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- if (s->scope && manager_unit_is_active(s->manager, s->scope))
- return false;
+ r = manager_unit_is_active(s->manager, s->scope, &error);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", s->scope, bus_error_message(&error, r));
+ if (r != 0)
+ return false;
+ }
return true;
}
@@ -1120,35 +1226,55 @@ error:
return r;
}
-void session_restore_vt(Session *s) {
-
- static const struct vt_mode mode = {
- .mode = VT_AUTO,
- };
-
- int vt, old_fd;
-
- /* We need to get a fresh handle to the virtual terminal,
- * since the old file-descriptor is potentially in a hung-up
- * state after the controlling process exited; we do a
- * little dance to avoid having the terminal be available
- * for reuse before we've cleaned it up.
- */
- old_fd = TAKE_FD(s->vtfd);
+static void session_restore_vt(Session *s) {
+ pid_t pid;
+ int r;
- vt = session_open_vt(s);
- safe_close(old_fd);
+ if (s->vtnr < 1)
+ return;
- if (vt < 0)
+ if (s->vtfd < 0)
return;
- (void) ioctl(vt, KDSETMODE, KD_TEXT);
+ /* The virtual terminal can potentially be entering in hung-up state at any time
+ * depending on when the controlling process exits.
+ *
+ * If the controlling process exits while we're restoring the virtual terminal,
+ * the VT will enter in hung-up state and we'll fail at restoring it. To prevent
+ * this case, we kick off the current controlling process (if any) in a child
+ * process so logind doesn't play around with tty ownership.
+ *
+ * If the controlling process already exited, getting a fresh handle to the
+ * virtual terminal reset the hung-up state. */
+ r = safe_fork("(logind)", FORK_REOPEN_LOG|FORK_CLOSE_ALL_FDS|FORK_RESET_SIGNALS|FORK_WAIT|FORK_LOG, &pid);
+ if (r == 0) {
+ char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)];
+ int vt;
+
+ /* We must be a session leader in order to become the controlling process. */
+ pid = setsid();
+ if (pid < 0) {
+ log_error_errno(errno, "Failed to become session leader: %m");
+ _exit(EXIT_FAILURE);
+ }
- (void) vt_reset_keyboard(vt);
+ sprintf(path, "/dev/tty%u", s->vtnr);
+ vt = acquire_terminal(path, ACQUIRE_TERMINAL_FORCE, USEC_INFINITY);
+ if (vt < 0) {
+ log_error_errno(vt, "Cannot acquire VT %s of session %s: %m", path, s->id);
+ _exit(EXIT_FAILURE);
+ }
- (void) ioctl(vt, VT_SETMODE, &mode);
- (void) fchown(vt, 0, (gid_t) -1);
+ r = vt_restore(vt);
+ if (r < 0)
+ log_warning_errno(r, "Failed to restore VT, ignoring: %m");
+
+ /* Give up and release the controlling terminal. */
+ safe_close(vt);
+ _exit(EXIT_SUCCESS);
+ }
+ /* Close the fd in any cases. */
s->vtfd = safe_close(s->vtfd);
}
@@ -1172,9 +1298,9 @@ void session_leave_vt(Session *s) {
return;
session_device_pause_all(s);
- r = ioctl(s->vtfd, VT_RELDISP, 1);
+ r = vt_release(s->vtfd, false);
if (r < 0)
- log_debug_errno(errno, "Cannot release VT of session %s: %m", s->id);
+ log_debug_errno(r, "Cannot release VT of session %s: %m", s->id);
}
bool session_is_controller(Session *s, const char *sender) {
@@ -1312,3 +1438,11 @@ static const char* const kill_who_table[_KILL_WHO_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
+
+static const char* const tty_validity_table[_TTY_VALIDITY_MAX] = {
+ [TTY_FROM_PAM] = "from-pam",
+ [TTY_FROM_UTMP] = "from-utmp",
+ [TTY_UTMP_INCONSISTENT] = "utmp-inconsistent",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(tty_validity, TTYValidity);
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index 29ca399daf..f3c17a8d91 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -46,11 +46,19 @@ enum KillWho {
_KILL_WHO_INVALID = -1
};
+typedef enum TTYValidity {
+ TTY_FROM_PAM,
+ TTY_FROM_UTMP,
+ TTY_UTMP_INCONSISTENT, /* may happen on ssh sessions with multiplexed TTYs */
+ _TTY_VALIDITY_MAX,
+ _TTY_VALIDITY_INVALID = -1,
+} TTYValidity;
+
struct Session {
Manager *manager;
const char *id;
- unsigned int position;
+ unsigned position;
SessionType type;
SessionClass class;
@@ -60,8 +68,9 @@ struct Session {
dual_timestamp timestamp;
- char *tty;
char *display;
+ char *tty;
+ TTYValidity tty_validity;
bool remote;
char *remote_user;
@@ -73,7 +82,7 @@ struct Session {
char *scope_job;
Seat *seat;
- unsigned int vtnr;
+ unsigned vtnr;
int vtfd;
pid_t leader;
@@ -97,6 +106,7 @@ struct Session {
sd_bus_message *create_message;
+ /* Set up when a client requested to release the session via the bus */
sd_event_source *timer_event_source;
char *controller;
@@ -109,9 +119,13 @@ struct Session {
LIST_FIELDS(Session, gc_queue);
};
-Session *session_new(Manager *m, const char *id);
-void session_free(Session *s);
+int session_new(Session **ret, Manager *m, const char *id);
+Session* session_free(Session *s);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Session *, session_free);
+
void session_set_user(Session *s, User *u);
+int session_set_leader(Session *s, pid_t pid);
bool session_may_gc(Session *s, bool drop_not_started);
void session_add_to_gc_queue(Session *s);
int session_activate(Session *s);
@@ -121,7 +135,7 @@ void 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);
-int session_start(Session *s, sd_bus_message *properties);
+int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error);
int session_stop(Session *s, bool force);
int session_finalize(Session *s);
int session_release(Session *s);
@@ -155,8 +169,10 @@ SessionClass session_class_from_string(const char *s) _pure_;
const char *kill_who_to_string(KillWho k) _const_;
KillWho kill_who_from_string(const char *s) _pure_;
+const char* tty_validity_to_string(TTYValidity t) _const_;
+TTYValidity tty_validity_from_string(const char *s) _pure_;
+
int session_prepare_vt(Session *s);
-void session_restore_vt(Session *s);
void session_leave_vt(Session *s);
bool session_is_controller(Session *s, const char *sender);
diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c
index c662a26b9f..fcaeba13f6 100644
--- a/src/login/logind-user-dbus.c
+++ b/src/login/logind-user-dbus.c
@@ -8,6 +8,7 @@
#include "format-util.h"
#include "logind-user.h"
#include "logind.h"
+#include "missing_capability.h"
#include "signal-util.h"
#include "strv.h"
#include "user-util.h"
@@ -109,7 +110,7 @@ static int property_get_idle_since_hint(
assert(reply);
assert(u);
- user_get_idle_hint(u, &t);
+ (void) user_get_idle_hint(u, &t);
k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
return sd_bus_message_append(reply, "t", k);
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 8c4cd54a29..ae27bfb662 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -11,6 +11,7 @@
#include "bus-util.h"
#include "cgroup-util.h"
#include "clean-ipc.h"
+#include "env-file.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -23,41 +24,57 @@
#include "parse-util.h"
#include "path-util.h"
#include "rm-rf.h"
+#include "serialize.h"
#include "special.h"
#include "stdio-util.h"
#include "string-table.h"
+#include "strv.h"
+#include "tmpfile-util.h"
#include "unit-name.h"
#include "user-util.h"
#include "util.h"
-int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
+int user_new(User **ret,
+ Manager *m,
+ uid_t uid,
+ gid_t gid,
+ const char *name,
+ const char *home) {
+
_cleanup_(user_freep) User *u = NULL;
char lu[DECIMAL_STR_MAX(uid_t) + 1];
int r;
- assert(out);
+ assert(ret);
assert(m);
assert(name);
- u = new0(User, 1);
+ u = new(User, 1);
if (!u)
return -ENOMEM;
- u->manager = m;
- u->uid = uid;
- u->gid = gid;
- xsprintf(lu, UID_FMT, uid);
+ *u = (User) {
+ .manager = m,
+ .uid = uid,
+ .gid = gid,
+ .last_session_timestamp = USEC_INFINITY,
+ };
u->name = strdup(name);
if (!u->name)
return -ENOMEM;
+ u->home = strdup(home);
+ if (!u->home)
+ return -ENOMEM;
+
if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
return -ENOMEM;
if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0)
return -ENOMEM;
+ xsprintf(lu, UID_FMT, uid);
r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice);
if (r < 0)
return r;
@@ -66,6 +83,10 @@ int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
if (r < 0)
return r;
+ r = unit_name_build("user-runtime-dir", lu, ".service", &u->runtime_dir_service);
+ if (r < 0)
+ return r;
+
r = hashmap_put(m->users, UID_TO_PTR(uid), u);
if (r < 0)
return r;
@@ -78,8 +99,11 @@ int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
if (r < 0)
return r;
- *out = TAKE_PTR(u);
+ r = hashmap_put(m->user_units, u->runtime_dir_service, u);
+ if (r < 0)
+ return r;
+ *ret = TAKE_PTR(u);
return 0;
}
@@ -96,19 +120,25 @@ User *user_free(User *u) {
if (u->service)
hashmap_remove_value(u->manager->user_units, u->service, u);
+ if (u->runtime_dir_service)
+ hashmap_remove_value(u->manager->user_units, u->runtime_dir_service, 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);
- u->slice_job = mfree(u->slice_job);
+ (void) sd_event_source_unref(u->timer_event_source);
+
u->service_job = mfree(u->service_job);
u->service = mfree(u->service);
+ u->runtime_dir_service = mfree(u->runtime_dir_service);
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);
return mfree(u);
}
@@ -135,9 +165,11 @@ static int user_save_internal(User *u) {
fprintf(f,
"# This is private data. Do not parse.\n"
"NAME=%s\n"
- "STATE=%s\n",
+ "STATE=%s\n" /* friendly user-facing state */
+ "STOPPING=%s\n", /* low-level state */
u->name,
- user_state_to_string(user_get_state(u)));
+ user_state_to_string(user_get_state(u)),
+ yes_no(u->stopping));
/* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */
if (u->runtime_path)
@@ -146,9 +178,6 @@ static int user_save_internal(User *u) {
if (u->service_job)
fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
- if (u->slice_job)
- fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
-
if (u->display)
fprintf(f, "DISPLAY=%s\n", u->display->id);
@@ -159,6 +188,10 @@ static int user_save_internal(User *u) {
u->timestamp.realtime,
u->timestamp.monotonic);
+ if (u->last_session_timestamp != USEC_INFINITY)
+ fprintf(f, "LAST_SESSION_TIMESTAMP=" USEC_FMT "\n",
+ u->last_session_timestamp);
+
if (u->sessions) {
Session *i;
bool first;
@@ -272,103 +305,82 @@ int user_save(User *u) {
if (!u->started)
return 0;
- return user_save_internal (u);
+ return user_save_internal(u);
}
int user_load(User *u) {
- _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
- Session *s = NULL;
+ _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL, *last_session_timestamp = NULL;
int r;
assert(u);
- r = parse_env_file(NULL, u->state_file, NEWLINE,
- "SERVICE_JOB", &u->service_job,
- "SLICE_JOB", &u->slice_job,
- "DISPLAY", &display,
- "REALTIME", &realtime,
- "MONOTONIC", &monotonic,
- NULL);
- if (r < 0) {
- if (r == -ENOENT)
- return 0;
-
+ r = parse_env_file(NULL, u->state_file,
+ "SERVICE_JOB", &u->service_job,
+ "STOPPING", &stopping,
+ "REALTIME", &realtime,
+ "MONOTONIC", &monotonic,
+ "LAST_SESSION_TIMESTAMP", &last_session_timestamp);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", u->state_file);
- }
-
- if (display)
- s = hashmap_get(u->manager->sessions, display);
- if (s && s->display && display_is_local(s->display))
- u->display = s;
+ if (stopping) {
+ r = parse_boolean(stopping);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse 'STOPPING' boolean: %s", stopping);
+ else
+ u->stopping = r;
+ }
if (realtime)
- timestamp_deserialize(realtime, &u->timestamp.realtime);
+ (void) deserialize_usec(realtime, &u->timestamp.realtime);
if (monotonic)
- timestamp_deserialize(monotonic, &u->timestamp.monotonic);
+ (void) deserialize_usec(monotonic, &u->timestamp.monotonic);
+ if (last_session_timestamp)
+ (void) deserialize_usec(last_session_timestamp, &u->last_session_timestamp);
- return r;
+ return 0;
}
-static int user_start_service(User *u) {
+static void user_start_service(User *u) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char *job;
int r;
assert(u);
+ /* Start the service containing the "systemd --user" instance (user@.service). Note that we don't explicitly
+ * start the per-user slice or the systemd-runtime-dir@.service instance, as those are pulled in both by
+ * user@.service and the session scopes as dependencies. */
+
u->service_job = mfree(u->service_job);
- r = manager_start_unit(
- u->manager,
- u->service,
- &error,
- &job);
+ r = manager_start_unit(u->manager, u->service, &error, &u->service_job);
if (r < 0)
- /* we don't fail due to this, let's try to continue */
- log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r));
- else
- u->service_job = job;
-
- return 0;
+ log_warning_errno(r, "Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r));
}
int user_start(User *u) {
- int r;
-
assert(u);
if (u->started && !u->stopping)
return 0;
- /*
- * If u->stopping is set, the user is marked for removal and the slice
- * and service stop-jobs are queued. We have to clear that flag before
- * queing the start-jobs again. If they succeed, the user object can be
- * re-used just fine (pid1 takes care of job-ordering and proper
- * restart), but if they fail, we want to force another user_stop() so
- * possibly pending units are stopped.
- * Note that we don't clear u->started, as we have no clue what state
- * the user is in on failure here. Hence, we pretend the user is
- * running so it will be properly taken down by GC. However, we clearly
- * return an error from user_start() in that case, so no further
- * reference to the user is taken.
- */
+ /* If u->stopping is set, the user is marked for removal and service stop-jobs are queued. We have to clear
+ * that flag before queing the start-jobs again. If they succeed, the user object can be re-used just fine
+ * (pid1 takes care of job-ordering and proper restart), but if they fail, we want to force another user_stop()
+ * so possibly pending units are stopped. */
u->stopping = false;
if (!u->started)
log_debug("Starting services for new user %s.", u->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. */
+ /* 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);
- /* Spawn user systemd */
- r = user_start_service(u);
- if (r < 0)
- return r;
+ /* Start user@UID.service */
+ user_start_service(u);
if (!u->started) {
if (!dual_timestamp_is_set(&u->timestamp))
@@ -383,68 +395,50 @@ int user_start(User *u) {
return 0;
}
-static int user_stop_slice(User *u) {
+static void user_stop_service(User *u) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char *job;
int r;
assert(u);
+ assert(u->service);
- r = manager_stop_unit(u->manager, u->slice, &error, &job);
- if (r < 0) {
- log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
- return r;
- }
-
- free(u->slice_job);
- u->slice_job = job;
+ /* The reverse of user_start_service(). Note that we only stop user@UID.service here, and let StopWhenUnneeded=
+ * deal with the slice and the user-runtime-dir@.service instance. */
- return r;
-}
-
-static int user_stop_service(User *u) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char *job;
- int r;
-
- assert(u);
-
- r = manager_stop_unit(u->manager, u->service, &error, &job);
- if (r < 0) {
- log_error("Failed to stop user service: %s", bus_error_message(&error, r));
- return r;
- }
+ u->service_job = mfree(u->service_job);
- free_and_replace(u->service_job, job);
- return r;
+ r = manager_stop_unit(u->manager, u->service, &error, &u->service_job);
+ if (r < 0)
+ log_warning_errno(r, "Failed to stop user service '%s', ignoring: %s", u->service, bus_error_message(&error, r));
}
int user_stop(User *u, bool force) {
Session *s;
- int r = 0, k;
+ int r = 0;
assert(u);
- /* Stop jobs have already been queued */
- if (u->stopping) {
+ /* This is called whenever we begin with tearing down a user record. It's called in two cases: explicit API
+ * request to do so via the bus (in which case 'force' is true) and automatically due to GC, if there's no
+ * session left pinning it (in which case 'force' is false). Note that this just initiates tearing down of the
+ * user, the User object will remain in memory until user_finalize() is called, see below. */
+
+ if (!u->started)
+ return 0;
+
+ if (u->stopping) { /* Stop jobs have already been queued */
user_save(u);
- return r;
+ return 0;
}
LIST_FOREACH(sessions_by_user, s, u->sessions) {
+ int k;
+
k = session_stop(s, force);
if (k < 0)
r = k;
}
- /* Kill systemd */
- k = user_stop_service(u);
- if (k < 0)
- r = k;
-
- /* Kill cgroup */
- k = user_stop_slice(u);
- if (k < 0)
- r = k;
+ user_stop_service(u);
u->stopping = true;
@@ -459,6 +453,9 @@ int user_finalize(User *u) {
assert(u);
+ /* Called when the user is really ready to be freed, i.e. when all unit stop jobs and suchlike for it are
+ * 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);
@@ -480,7 +477,7 @@ int user_finalize(User *u) {
r = k;
}
- unlink(u->state_file);
+ (void) unlink(u->state_file);
user_add_to_gc_queue(u);
if (u->started) {
@@ -536,11 +533,40 @@ int user_check_linger_file(User *u) {
return -ENOMEM;
p = strjoina("/var/lib/systemd/linger/", cc);
+ if (access(p, F_OK) < 0) {
+ if (errno != ENOENT)
+ return -errno;
+
+ return false;
+ }
- return access(p, F_OK) >= 0;
+ return true;
+}
+
+static bool user_unit_active(User *u) {
+ const char *i;
+ int r;
+
+ assert(u->service);
+ assert(u->runtime_dir_service);
+ assert(u->slice);
+
+ FOREACH_STRING(i, u->service, u->runtime_dir_service, u->slice) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ r = manager_unit_is_active(u->manager, i, &error);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", u->service, bus_error_message(&error, r));
+ if (r != 0)
+ return true;
+ }
+
+ return false;
}
bool user_may_gc(User *u, bool drop_not_started) {
+ int r;
+
assert(u);
if (drop_not_started && !u->started)
@@ -549,15 +575,36 @@ bool user_may_gc(User *u, bool drop_not_started) {
if (u->sessions)
return false;
- if (user_check_linger_file(u) > 0)
- return false;
+ if (u->last_session_timestamp != USEC_INFINITY) {
+ /* All sessions have been closed. Let's see if we shall leave the user record around for a bit */
- if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
- return false;
+ if (u->manager->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))
+ return false; /* Leave it around for a bit longer. */
+ }
- if (u->service_job && manager_job_is_active(u->manager, u->service_job))
+ /* Is this a user that shall stay around forever ("linger")? Before we say "no" to GC'ing for lingering users, let's check
+ * if any of the three units that we maintain for this user is still around. If none of them is,
+ * there's no need to keep this user around even if lingering is enabled. */
+ if (user_check_linger_file(u) > 0 && user_unit_active(u))
return false;
+ /* Check if our job is still pending */
+ if (u->service_job) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ r = manager_job_is_active(u->manager, u->service_job, &error);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine whether job '%s' is pending, ignoring: %s", u->service_job, bus_error_message(&error, r));
+ if (r != 0)
+ return false;
+ }
+
+ /* Note that we don't care if the three units we manage for each user object are up or not, as we are managing
+ * their state rather than tracking it. */
+
return true;
}
@@ -579,7 +626,7 @@ UserState user_get_state(User *u) {
if (u->stopping)
return USER_CLOSING;
- if (!u->started || u->slice_job || u->service_job)
+ if (!u->started || u->service_job)
return USER_OPENING;
if (u->sessions) {
@@ -598,7 +645,7 @@ UserState user_get_state(User *u) {
return all_closing ? USER_CLOSING : USER_ONLINE;
}
- if (user_check_linger_file(u) > 0)
+ if (user_check_linger_file(u) > 0 && user_unit_active(u))
return USER_LINGERING;
return USER_CLOSING;
@@ -611,11 +658,10 @@ int user_kill(User *u, int signo) {
}
static bool elect_display_filter(Session *s) {
- /* Return true if the session is a candidate for the user’s ‘primary
- * session’ or ‘display’. */
+ /* Return true if the session is a candidate for the user’s ‘primary session’ or ‘display’. */
assert(s);
- return (s->class == SESSION_USER && !s->stopping);
+ return s->class == SESSION_USER && s->started && !s->stopping;
}
static int elect_display_compare(Session *s1, Session *s2) {
@@ -661,9 +707,8 @@ void user_elect_display(User *u) {
assert(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. */
+ /* 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);
LIST_FOREACH(sessions_by_user, s, u->sessions) {
@@ -679,6 +724,59 @@ void user_elect_display(User *u) {
}
}
+static int user_stop_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
+ User *u = userdata;
+
+ assert(u);
+ user_add_to_gc_queue(u);
+
+ return 0;
+}
+
+void user_update_last_session_timer(User *u) {
+ int r;
+
+ assert(u);
+
+ if (u->sessions) {
+ /* There are sessions, turn off the timer */
+ u->last_session_timestamp = USEC_INFINITY;
+ u->timer_event_source = sd_event_source_unref(u->timer_event_source);
+ return;
+ }
+
+ if (u->last_session_timestamp != USEC_INFINITY)
+ return; /* Timer already started */
+
+ u->last_session_timestamp = now(CLOCK_MONOTONIC);
+
+ assert(!u->timer_event_source);
+
+ if (u->manager->user_stop_delay == 0 || u->manager->user_stop_delay == USEC_INFINITY)
+ return;
+
+ if (sd_event_get_state(u->manager->event) == SD_EVENT_FINISHED) {
+ log_debug("Not allocating user stop timeout, since we are already exiting.");
+ return;
+ }
+
+ 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,
+ user_stop_timeout_callback, u);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enqueue user stop event source, ignoring: %m");
+
+ if (DEBUG_LOGGING) {
+ 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));
+ }
+}
+
static const char* const user_state_table[_USER_STATE_MAX] = {
[USER_OFFLINE] = "offline",
[USER_OPENING] = "opening",
@@ -711,17 +809,19 @@ int config_parse_tmpfs_size(
assert(data);
/* First, try to parse as percentage */
- r = parse_percent(rvalue);
- if (r > 0 && r < 100)
- *sz = physical_memory_scale(r, 100U);
+ r = parse_permille(rvalue);
+ if (r > 0 && r < 1000)
+ *sz = physical_memory_scale(r, 1000U);
else {
uint64_t k;
/* If the passed argument was not a percentage, or out of range, parse as byte size */
r = parse_size(rvalue, 1024, &k);
- if (r < 0 || k <= 0 || (uint64_t) (size_t) k != k) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
+ if (r >= 0 && (k <= 0 || (uint64_t) (size_t) k != k))
+ r = -ERANGE;
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue);
return 0;
}
@@ -751,7 +851,7 @@ int config_parse_compat_user_tasks_max(
log_syntax(unit, LOG_NOTICE, filename, line, 0,
"Support for option %s= has been removed.",
lvalue);
- log_info("Hint: try creating /etc/systemd/system/user-.slice/50-limits.conf with:\n"
+ log_info("Hint: try creating /etc/systemd/system/user-.slice.d/50-limits.conf with:\n"
" [Slice]\n"
" TasksMax=%s",
rvalue);
diff --git a/src/login/logind-user.h b/src/login/logind-user.h
index eba2325284..c41973e27d 100644
--- a/src/login/logind-user.h
+++ b/src/login/logind-user.h
@@ -23,27 +23,34 @@ struct User {
uid_t uid;
gid_t gid;
char *name;
+ char *home;
char *state_file;
char *runtime_path;
- char *slice;
- char *service;
+
+ char *slice; /* user-UID.slice */
+ char *service; /* user@UID.service */
+ char *runtime_dir_service; /* user-runtime-dir@UID.service */
char *service_job;
- char *slice_job;
Session *display;
- dual_timestamp timestamp;
+ dual_timestamp timestamp; /* When this User object was 'started' the first time */
+ usec_t last_session_timestamp; /* When the number of sessions of this user went from 1 to 0 the last time */
+
+ /* Set up when the last session of the user logs out */
+ sd_event_source *timer_event_source;
bool in_gc_queue:1;
- bool started:1;
- bool stopping:1;
+
+ bool started:1; /* Whenever the user being started, has been started or is being stopped again. */
+ bool stopping:1; /* Whenever the user is being stopped or has been stopped. */
LIST_HEAD(Session, sessions);
LIST_FIELDS(User, gc_queue);
};
-int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name);
+int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name, const char *home);
User *user_free(User *u);
DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free);
@@ -60,6 +67,7 @@ int user_load(User *u);
int user_kill(User *u, int signo);
int user_check_linger_file(User *u);
void user_elect_display(User *u);
+void user_update_last_session_timer(User *u);
extern const sd_bus_vtable user_vtable[];
int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c
index 71ebdfcfb1..3a5a333496 100644
--- a/src/login/logind-utmp.c
+++ b/src/login/logind-utmp.c
@@ -24,7 +24,7 @@
_const_ static usec_t when_wall(usec_t n, usec_t elapse) {
usec_t left;
- unsigned int i;
+ unsigned i;
static const int wall_timers[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
25, 40, 55, 70, 100, 130, 150, 180,
@@ -61,7 +61,7 @@ bool logind_wall_tty_filter(const char *tty, void *userdata) {
static int warn_wall(Manager *m, usec_t n) {
char date[FORMAT_TIMESTAMP_MAX] = {};
- _cleanup_free_ char *l = NULL;
+ _cleanup_free_ char *l = NULL, *username = NULL;
usec_t left;
int r;
@@ -83,8 +83,8 @@ static int warn_wall(Manager *m, usec_t n) {
return 0;
}
- utmp_wall(l, uid_to_name(m->scheduled_shutdown_uid),
- m->scheduled_shutdown_tty, logind_wall_tty_filter, m);
+ username = uid_to_name(m->scheduled_shutdown_uid);
+ utmp_wall(l, username, m->scheduled_shutdown_tty, logind_wall_tty_filter, m);
return 1;
}
diff --git a/src/login/logind.c b/src/login/logind.c
index 52fcee933c..8d85de9b43 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -5,25 +5,27 @@
#include <string.h>
#include <unistd.h>
-#include "libudev.h"
#include "sd-daemon.h"
+#include "sd-device.h"
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
#include "cgroup-util.h"
#include "def.h"
+#include "device-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
#include "logind.h"
+#include "main-func.h"
#include "parse-util.h"
#include "process-util.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "strv.h"
-#include "udev-util.h"
+#include "terminal-util.h"
static Manager* manager_unref(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
@@ -34,18 +36,21 @@ static int manager_new(Manager **ret) {
assert(ret);
- m = new0(Manager, 1);
+ m = new(Manager, 1);
if (!m)
return -ENOMEM;
- m->console_active_fd = -1;
- m->reserve_vt_fd = -1;
+ *m = (Manager) {
+ .console_active_fd = -1,
+ .reserve_vt_fd = -1,
+ };
m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
m->devices = hashmap_new(&string_hash_ops);
m->seats = hashmap_new(&string_hash_ops);
m->sessions = hashmap_new(&string_hash_ops);
+ m->sessions_by_leader = hashmap_new(NULL);
m->users = hashmap_new(NULL);
m->inhibitors = hashmap_new(&string_hash_ops);
m->buttons = hashmap_new(&string_hash_ops);
@@ -53,13 +58,9 @@ static int manager_new(Manager **ret) {
m->user_units = hashmap_new(&string_hash_ops);
m->session_units = hashmap_new(&string_hash_ops);
- if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units)
+ if (!m->devices || !m->seats || !m->sessions || !m->sessions_by_leader || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units)
return -ENOMEM;
- m->udev = udev_new();
- if (!m->udev)
- return -errno;
-
r = sd_event_default(&m->event);
if (r < 0)
return r;
@@ -112,6 +113,7 @@ static Manager* manager_unref(Manager *m) {
hashmap_free(m->devices);
hashmap_free(m->seats);
hashmap_free(m->sessions);
+ hashmap_free(m->sessions_by_leader);
hashmap_free(m->users);
hashmap_free(m->inhibitors);
hashmap_free(m->buttons);
@@ -126,20 +128,18 @@ static Manager* manager_unref(Manager *m) {
sd_event_source_unref(m->wall_message_timeout_source);
sd_event_source_unref(m->console_active_event_source);
- sd_event_source_unref(m->udev_seat_event_source);
- sd_event_source_unref(m->udev_device_event_source);
- sd_event_source_unref(m->udev_vcsa_event_source);
- sd_event_source_unref(m->udev_button_event_source);
sd_event_source_unref(m->lid_switch_ignore_event_source);
- safe_close(m->console_active_fd);
+#if ENABLE_UTMP
+ sd_event_source_unref(m->utmp_event_source);
+#endif
- udev_monitor_unref(m->udev_seat_monitor);
- udev_monitor_unref(m->udev_device_monitor);
- udev_monitor_unref(m->udev_vcsa_monitor);
- udev_monitor_unref(m->udev_button_monitor);
+ safe_close(m->console_active_fd);
- udev_unref(m->udev);
+ sd_device_monitor_unref(m->device_seat_monitor);
+ sd_device_monitor_unref(m->device_monitor);
+ sd_device_monitor_unref(m->device_vcsa_monitor);
+ sd_device_monitor_unref(m->device_button_monitor);
if (m->unlink_nologin)
(void) unlink_or_warn("/run/nologin");
@@ -163,8 +163,8 @@ static Manager* manager_unref(Manager *m) {
}
static int manager_enumerate_devices(Manager *m) {
- struct udev_list_entry *item = NULL, *first = NULL;
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d;
int r;
assert(m);
@@ -172,31 +172,17 @@ static int manager_enumerate_devices(Manager *m) {
/* Loads devices from udev and creates seats for them as
* necessary */
- e = udev_enumerate_new(m->udev);
- if (!e)
- return -ENOMEM;
-
- r = udev_enumerate_add_match_tag(e, "master-of-seat");
- if (r < 0)
- return r;
-
- r = udev_enumerate_add_match_is_initialized(e);
+ r = sd_device_enumerator_new(&e);
if (r < 0)
return r;
- r = udev_enumerate_scan_devices(e);
+ r = sd_device_enumerator_add_match_tag(e, "master-of-seat");
if (r < 0)
return r;
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+ FOREACH_DEVICE(e, d) {
int k;
- d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
- if (!d)
- return -ENOMEM;
-
k = manager_process_seat_device(m, d);
if (k < 0)
r = k;
@@ -206,8 +192,8 @@ static int manager_enumerate_devices(Manager *m) {
}
static int manager_enumerate_buttons(Manager *m) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d;
int r;
assert(m);
@@ -217,35 +203,21 @@ static int manager_enumerate_buttons(Manager *m) {
if (manager_all_buttons_ignored(m))
return 0;
- e = udev_enumerate_new(m->udev);
- if (!e)
- return -ENOMEM;
-
- r = udev_enumerate_add_match_subsystem(e, "input");
+ r = sd_device_enumerator_new(&e);
if (r < 0)
return r;
- r = udev_enumerate_add_match_tag(e, "power-switch");
+ r = sd_device_enumerator_add_match_subsystem(e, "input", true);
if (r < 0)
return r;
- r = udev_enumerate_add_match_is_initialized(e);
+ r = sd_device_enumerator_add_match_tag(e, "power-switch");
if (r < 0)
return r;
- r = udev_enumerate_scan_devices(e);
- if (r < 0)
- return r;
-
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+ FOREACH_DEVICE(e, d) {
int k;
- d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
- if (!d)
- return -ENOMEM;
-
k = manager_process_button_device(m, d);
if (k < 0)
r = k;
@@ -373,7 +345,7 @@ static int manager_enumerate_users(Manager *m) {
static int parse_fdname(const char *fdname, char **session_id, dev_t *dev) {
_cleanup_strv_free_ char **parts = NULL;
_cleanup_free_ char *id = NULL;
- unsigned int major, minor;
+ unsigned major, minor;
int r;
parts = strv_split(fdname, "-");
@@ -565,67 +537,52 @@ static int manager_enumerate_inhibitors(Manager *m) {
return r;
}
-static int manager_dispatch_seat_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+static int manager_dispatch_seat_udev(sd_device_monitor *monitor, sd_device *device, void *userdata) {
Manager *m = userdata;
assert(m);
+ assert(device);
- d = udev_monitor_receive_device(m->udev_seat_monitor);
- if (!d)
- return -ENOMEM;
-
- manager_process_seat_device(m, d);
+ manager_process_seat_device(m, device);
return 0;
}
-static int manager_dispatch_device_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+static int manager_dispatch_device_udev(sd_device_monitor *monitor, sd_device *device, void *userdata) {
Manager *m = userdata;
assert(m);
+ assert(device);
- d = udev_monitor_receive_device(m->udev_device_monitor);
- if (!d)
- return -ENOMEM;
-
- manager_process_seat_device(m, d);
+ manager_process_seat_device(m, device);
return 0;
}
-static int manager_dispatch_vcsa_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+static int manager_dispatch_vcsa_udev(sd_device_monitor *monitor, sd_device *device, void *userdata) {
Manager *m = userdata;
- const char *name;
+ const char *name, *action;
assert(m);
-
- d = udev_monitor_receive_device(m->udev_vcsa_monitor);
- if (!d)
- return -ENOMEM;
-
- name = udev_device_get_sysname(d);
+ assert(device);
/* Whenever a VCSA device is removed try to reallocate our
* VTs, to make sure our auto VTs never go away. */
- if (name && startswith(name, "vcsa") && streq_ptr(udev_device_get_action(d), "remove"))
+ if (sd_device_get_sysname(device, &name) >= 0 &&
+ startswith(name, "vcsa") &&
+ sd_device_get_property_value(device, "ACTION", &action) >= 0 &&
+ streq(action, "remove"))
seat_preallocate_vts(m->seat0);
return 0;
}
-static int manager_dispatch_button_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+static int manager_dispatch_button_udev(sd_device_monitor *monitor, sd_device *device, void *userdata) {
Manager *m = userdata;
assert(m);
+ assert(device);
- d = udev_monitor_receive_device(m->udev_button_monitor);
- if (!d)
- return -ENOMEM;
-
- manager_process_button_device(m, d);
+ manager_process_button_device(m, device);
return 0;
}
@@ -791,7 +748,29 @@ static int manager_vt_switch(sd_event_source *src, const struct signalfd_siginfo
active = m->seat0->active;
if (!active || active->vtnr < 1) {
- log_warning("Received VT_PROCESS signal without a registered session on that VT.");
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ /* We are requested to acknowledge the VT-switch signal by the kernel but
+ * there's no registered sessions for the current VT. Normally this
+ * shouldn't happen but something wrong might have happened when we tried
+ * to release the VT. Better be safe than sorry, and try to release the VT
+ * one more time otherwise the user will be locked with the current VT. */
+
+ log_warning("Received VT_PROCESS signal without a registered session, restoring VT.");
+
+ /* At this point we only have the kernel mapping for referring to the
+ * current VT. */
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
+ if (fd < 0) {
+ log_warning_errno(fd, "Failed to open, ignoring: %m");
+ return 0;
+ }
+
+ r = vt_release(fd, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to release VT, ignoring: %m");
+
return 0;
}
@@ -815,28 +794,28 @@ static int manager_connect_console(Manager *m) {
assert(m);
assert(m->console_active_fd < 0);
- /* On certain architectures (S390 and Xen, and containers),
- /dev/tty0 does not exist, so don't fail if we can't open
- it. */
+ /* On certain systems (such as S390, Xen, and containers) /dev/tty0 does not exist (as there is no VC), so
+ * don't fail if we can't open it. */
+
if (access("/dev/tty0", F_OK) < 0)
return 0;
m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (m->console_active_fd < 0) {
- /* On some systems the device node /dev/tty0 may exist
- * even though /sys/class/tty/tty0 does not. */
- if (errno == ENOENT)
+ /* On some systems /dev/tty0 may exist even though /sys/class/tty/tty0 does not. These are broken, but
+ * common. Let's complain but continue anyway. */
+ if (errno == ENOENT) {
+ log_warning_errno(errno, "System has /dev/tty0 but not /sys/class/tty/tty0/active which is broken, ignoring: %m");
return 0;
+ }
return log_error_errno(errno, "Failed to open /sys/class/tty/tty0/active: %m");
}
r = sd_event_add_io(m->event, &m->console_active_event_source, m->console_active_fd, 0, manager_dispatch_console, m);
- if (r < 0) {
- log_error("Failed to watch foreground console");
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch foreground console: %m");
/*
* SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used
@@ -845,17 +824,17 @@ static int manager_connect_console(Manager *m) {
* release events immediately.
*/
- if (SIGRTMIN + 1 > SIGRTMAX) {
- log_error("Not enough real-time signals available: %u-%u", SIGRTMIN, SIGRTMAX);
- return -EINVAL;
- }
+ if (SIGRTMIN + 1 > SIGRTMAX)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not enough real-time signals available: %u-%u",
+ SIGRTMIN, SIGRTMAX);
assert_se(ignore_signals(SIGRTMIN + 1, -1) >= 0);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN, -1) >= 0);
r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to subscribe to signal: %m");
return 0;
}
@@ -864,92 +843,100 @@ static int manager_connect_udev(Manager *m) {
int r;
assert(m);
- assert(!m->udev_seat_monitor);
- assert(!m->udev_device_monitor);
- assert(!m->udev_vcsa_monitor);
- assert(!m->udev_button_monitor);
+ assert(!m->device_seat_monitor);
+ assert(!m->device_monitor);
+ assert(!m->device_vcsa_monitor);
+ assert(!m->device_button_monitor);
- m->udev_seat_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
- if (!m->udev_seat_monitor)
- return -ENOMEM;
+ r = sd_device_monitor_new(&m->device_seat_monitor);
+ if (r < 0)
+ return r;
- r = udev_monitor_filter_add_match_tag(m->udev_seat_monitor, "master-of-seat");
+ r = sd_device_monitor_filter_add_match_tag(m->device_seat_monitor, "master-of-seat");
if (r < 0)
return r;
- r = udev_monitor_enable_receiving(m->udev_seat_monitor);
+ r = sd_device_monitor_attach_event(m->device_seat_monitor, m->event);
if (r < 0)
return r;
- r = sd_event_add_io(m->event, &m->udev_seat_event_source, udev_monitor_get_fd(m->udev_seat_monitor), EPOLLIN, manager_dispatch_seat_udev, m);
+ r = sd_device_monitor_start(m->device_seat_monitor, manager_dispatch_seat_udev, m);
if (r < 0)
return r;
- m->udev_device_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
- if (!m->udev_device_monitor)
- return -ENOMEM;
+ (void) sd_event_source_set_description(sd_device_monitor_get_event_source(m->device_seat_monitor), "logind-seat-monitor");
- r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "input", NULL);
+ r = sd_device_monitor_new(&m->device_monitor);
if (r < 0)
return r;
- r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "graphics", NULL);
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "input", NULL);
if (r < 0)
return r;
- r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "drm", NULL);
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "graphics", NULL);
if (r < 0)
return r;
- r = udev_monitor_enable_receiving(m->udev_device_monitor);
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "drm", NULL);
if (r < 0)
return r;
- r = sd_event_add_io(m->event, &m->udev_device_event_source, udev_monitor_get_fd(m->udev_device_monitor), EPOLLIN, manager_dispatch_device_udev, m);
+ r = sd_device_monitor_attach_event(m->device_monitor, m->event);
if (r < 0)
return r;
+ r = sd_device_monitor_start(m->device_monitor, manager_dispatch_device_udev, m);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(sd_device_monitor_get_event_source(m->device_monitor), "logind-device-monitor");
+
/* Don't watch keys if nobody cares */
if (!manager_all_buttons_ignored(m)) {
- m->udev_button_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
- if (!m->udev_button_monitor)
- return -ENOMEM;
+ r = sd_device_monitor_new(&m->device_button_monitor);
+ if (r < 0)
+ return r;
- r = udev_monitor_filter_add_match_tag(m->udev_button_monitor, "power-switch");
+ r = sd_device_monitor_filter_add_match_tag(m->device_button_monitor, "power-switch");
if (r < 0)
return r;
- r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_button_monitor, "input", NULL);
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_button_monitor, "input", NULL);
if (r < 0)
return r;
- r = udev_monitor_enable_receiving(m->udev_button_monitor);
+ r = sd_device_monitor_attach_event(m->device_button_monitor, m->event);
if (r < 0)
return r;
- r = sd_event_add_io(m->event, &m->udev_button_event_source, udev_monitor_get_fd(m->udev_button_monitor), EPOLLIN, manager_dispatch_button_udev, m);
+ r = sd_device_monitor_start(m->device_button_monitor, manager_dispatch_button_udev, m);
if (r < 0)
return r;
+
+ (void) sd_event_source_set_description(sd_device_monitor_get_event_source(m->device_button_monitor), "logind-button-monitor");
}
/* Don't bother watching VCSA devices, if nobody cares */
if (m->n_autovts > 0 && m->console_active_fd >= 0) {
- m->udev_vcsa_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
- if (!m->udev_vcsa_monitor)
- return -ENOMEM;
+ r = sd_device_monitor_new(&m->device_vcsa_monitor);
+ if (r < 0)
+ return r;
- r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_vcsa_monitor, "vc", NULL);
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_vcsa_monitor, "vc", NULL);
if (r < 0)
return r;
- r = udev_monitor_enable_receiving(m->udev_vcsa_monitor);
+ r = sd_device_monitor_attach_event(m->device_vcsa_monitor, m->event);
if (r < 0)
return r;
- r = sd_event_add_io(m->event, &m->udev_vcsa_event_source, udev_monitor_get_fd(m->udev_vcsa_monitor), EPOLLIN, manager_dispatch_vcsa_udev, m);
+ r = sd_device_monitor_start(m->device_vcsa_monitor, manager_dispatch_vcsa_udev, m);
if (r < 0)
return r;
+
+ (void) sd_event_source_set_description(sd_device_monitor_get_event_source(m->device_vcsa_monitor), "logind-vcsa-monitor");
}
return 0;
@@ -979,13 +966,13 @@ static void manager_gc(Manager *m, bool drop_not_started) {
/* First, if we are not closing yet, initiate stopping */
if (session_may_gc(session, drop_not_started) &&
session_get_state(session) != SESSION_CLOSING)
- session_stop(session, false);
+ (void) session_stop(session, false);
/* Normally, this should make the session referenced
* again, if it doesn't then let's get rid of it
* immediately */
if (session_may_gc(session, drop_not_started)) {
- session_finalize(session);
+ (void) session_finalize(session);
session_free(session);
}
}
@@ -996,11 +983,11 @@ static void manager_gc(Manager *m, bool drop_not_started) {
/* First step: queue stop jobs */
if (user_may_gc(user, drop_not_started))
- user_stop(user, false);
+ (void) user_stop(user, false);
/* Second step: finalize user */
if (user_may_gc(user, drop_not_started)) {
- user_finalize(user);
+ (void) user_finalize(user);
user_free(user);
}
}
@@ -1095,6 +1082,9 @@ static int manager_startup(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to register SIGHUP handler: %m");
+ /* Connect to utmp */
+ manager_connect_utmp(m);
+
/* Connect to console */
r = manager_connect_console(m);
if (r < 0)
@@ -1150,15 +1140,18 @@ static int manager_startup(Manager *m) {
/* Reserve the special reserved VT */
manager_reserve_vt(m);
+ /* Read in utmp if it exists */
+ manager_read_utmp(m);
+
/* And start everything */
HASHMAP_FOREACH(seat, m->seats, i)
- seat_start(seat);
+ (void) seat_start(seat);
HASHMAP_FOREACH(user, m->users, i)
- user_start(user);
+ (void) user_start(user);
HASHMAP_FOREACH(session, m->sessions, i)
- session_start(session, NULL);
+ (void) session_start(session, NULL, NULL);
HASHMAP_FOREACH(inhibitor, m->inhibitors, i)
inhibitor_start(inhibitor);
@@ -1197,28 +1190,23 @@ static int manager_run(Manager *m) {
}
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_(manager_unrefp) Manager *m = NULL;
int r;
- log_set_target(LOG_TARGET_AUTO);
log_set_facility(LOG_AUTH);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
if (argc != 1) {
log_error("This program takes no arguments.");
- r = -EINVAL;
- goto finish;
+ return -EINVAL;
}
r = mac_selinux_init();
- if (r < 0) {
- log_error_errno(r, "Could not initialize labelling: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not initialize labelling: %m");
/* Always create the directories people can create inotify watches in. Note that some applications might check
* for the existence of /run/systemd/seats/ to determine whether logind is available, so please always make
@@ -1230,21 +1218,16 @@ int main(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGHUP, SIGTERM, SIGINT, -1) >= 0);
r = manager_new(&m);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate manager object: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate manager object: %m");
(void) manager_parse_config_file(m);
r = manager_startup(m);
- if (r < 0) {
- log_error_errno(r, "Failed to fully start up daemon: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to fully start up daemon: %m");
log_debug("systemd-logind running as pid "PID_FMT, getpid_cached());
-
(void) sd_notify(false,
"READY=1\n"
"STATUS=Processing requests...");
@@ -1252,11 +1235,11 @@ int main(int argc, char *argv[]) {
r = manager_run(m);
log_debug("systemd-logind stopped as pid "PID_FMT, getpid_cached());
-
(void) sd_notify(false,
"STOPPING=1\n"
"STATUS=Shutting down...");
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/login/logind.h b/src/login/logind.h
index a6ebc9e152..7b774f49e0 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -3,8 +3,8 @@
#include <stdbool.h>
-#include "libudev.h"
#include "sd-bus.h"
+#include "sd-device.h"
#include "sd-event.h"
#include "conf-parser.h"
@@ -26,6 +26,7 @@ struct Manager {
Hashmap *devices;
Hashmap *seats;
Hashmap *sessions;
+ Hashmap *sessions_by_leader;
Hashmap *users;
Hashmap *inhibitors;
Hashmap *buttons;
@@ -34,14 +35,13 @@ struct Manager {
LIST_HEAD(Session, session_gc_queue);
LIST_HEAD(User, user_gc_queue);
- struct udev *udev;
- struct udev_monitor *udev_seat_monitor, *udev_device_monitor, *udev_vcsa_monitor, *udev_button_monitor;
+ sd_device_monitor *device_seat_monitor, *device_monitor, *device_vcsa_monitor, *device_button_monitor;
sd_event_source *console_active_event_source;
- sd_event_source *udev_seat_event_source;
- sd_event_source *udev_device_event_source;
- sd_event_source *udev_vcsa_event_source;
- sd_event_source *udev_button_event_source;
+
+#if ENABLE_UTMP
+ sd_event_source *utmp_event_source;
+#endif
int console_active_fd;
@@ -62,6 +62,7 @@ struct Manager {
Hashmap *user_units;
usec_t inhibit_delay_max;
+ usec_t user_stop_delay;
/* If an action is currently being executed or is delayed,
* this is != 0 and encodes what is being done */
@@ -128,15 +129,15 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev
int manager_add_button(Manager *m, const char *name, Button **_button);
int manager_add_seat(Manager *m, const char *id, Seat **_seat);
int manager_add_session(Manager *m, const char *id, Session **_session);
-int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user);
+int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, const char *home, User **_user);
int manager_add_user_by_name(Manager *m, const char *name, User **_user);
int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user);
int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor);
-int manager_process_seat_device(Manager *m, struct udev_device *d);
-int manager_process_button_device(Manager *m, struct udev_device *d);
+int manager_process_seat_device(Manager *m, sd_device *d);
+int manager_process_button_device(Manager *m, sd_device *d);
-int manager_spawn_autovt(Manager *m, unsigned int vtnr);
+int manager_spawn_autovt(Manager *m, unsigned vtnr);
bool manager_shall_kill(Manager *m, const char *user);
@@ -145,10 +146,15 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t);
int manager_get_user_by_pid(Manager *m, pid_t pid, User **user);
int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session);
+bool manager_is_lid_closed(Manager *m);
bool manager_is_docked_or_external_displays(Manager *m);
bool manager_is_on_external_power(void);
bool manager_all_buttons_ignored(Manager *m);
+int manager_read_utmp(Manager *m);
+void manager_connect_utmp(Manager *m);
+void manager_reconnect_utmp(Manager *m);
+
extern const sd_bus_vtable manager_vtable[];
int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
@@ -161,13 +167,13 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name
int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
-int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_message *more_properties, sd_bus_error *error, char **job);
+int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, char **wants, char **after, const char *requires_mounts_for, sd_bus_message *more_properties, sd_bus_error *error, char **job);
int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error);
int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error);
-int manager_unit_is_active(Manager *manager, const char *unit);
-int manager_job_is_active(Manager *manager, const char *path);
+int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *error);
+int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *error);
/* gperf lookup function */
const struct ConfigPerfItem* logind_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
diff --git a/src/login/meson.build b/src/login/meson.build
index 4326a452c6..1cc75fd1cf 100644
--- a/src/login/meson.build
+++ b/src/login/meson.build
@@ -58,7 +58,6 @@ loginctl_sources = files('''
user_runtime_dir_sources = files('''
user-runtime-dir.c
- logind.h
'''.split())
if conf.get('ENABLE_LOGIND') == 1
@@ -81,10 +80,6 @@ if conf.get('ENABLE_LOGIND') == 1
install_data('70-power-switch.rules', install_dir : udevrulesdir)
- if conf.get('HAVE_ACL') == 1
- install_data('70-uaccess.rules', install_dir : udevrulesdir)
- endif
-
seat_rules = configure_file(
input : '71-seat.rules.in',
output : '71-seat.rules',
@@ -93,6 +88,15 @@ if conf.get('ENABLE_LOGIND') == 1
install_dir : udevrulesdir)
custom_target(
+ '70-uaccess.rules',
+ input : '70-uaccess.rules.m4',
+ output: '70-uaccess.rules',
+ command : [meson_apply_m4, config_h, '@INPUT@'],
+ capture : true,
+ install : conf.get('HAVE_ACL') == 1,
+ install_dir : udevrulesdir)
+
+ custom_target(
'73-seat-late.rules',
input : '73-seat-late.rules.m4',
output: '73-seat-late.rules',
diff --git a/src/login/org.freedesktop.login1.policy b/src/login/org.freedesktop.login1.policy
index f1d1f956d3..5ee62ab5b4 100644
--- a/src/login/org.freedesktop.login1.policy
+++ b/src/login/org.freedesktop.login1.policy
@@ -343,8 +343,9 @@
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
- <allow_active>auth_admin_keep</allow_active>
+ <allow_active>yes</allow_active>
</defaults>
+ <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.reboot</annotate>
</action>
<action id="org.freedesktop.login1.set-wall-message">
diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c
index 1fbf6ba585..c7d9dcf4e2 100644
--- a/src/login/pam_systemd.c
+++ b/src/login/pam_systemd.c
@@ -16,6 +16,7 @@
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-util.h"
+#include "cgroup-util.h"
#include "def.h"
#include "fd-util.h"
#include "fileio.h"
@@ -24,19 +25,19 @@
#include "login-util.h"
#include "macro.h"
#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
#include "socket-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "util.h"
-#include "path-util.h"
-#include "cgroup-util.h"
static int parse_argv(
pam_handle_t *handle,
int argc, const char **argv,
const char **class,
const char **type,
+ const char **desktop,
bool *debug) {
unsigned i;
@@ -53,6 +54,10 @@ static int parse_argv(
if (type)
*type = argv[i] + 5;
+ } else if (startswith(argv[i], "desktop=")) {
+ if (desktop)
+ *desktop = argv[i] + 8;
+
} else if (streq(argv[i], "debug")) {
if (debug)
*debug = true;
@@ -109,14 +114,37 @@ static int get_user_data(
return PAM_SUCCESS;
}
+static int socket_from_display(const char *display, char **path) {
+ size_t k;
+ char *f, *c;
+
+ assert(display);
+ assert(path);
+
+ if (!display_is_local(display))
+ return -EINVAL;
+
+ k = strspn(display+1, "0123456789");
+
+ f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
+ if (!f)
+ return -ENOMEM;
+
+ c = stpcpy(f, "/tmp/.X11-unix/X");
+ memcpy(c, display+1, k);
+ c[k] = 0;
+
+ *path = f;
+
+ return 0;
+}
+
static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
- union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- };
+ union sockaddr_union sa = {};
_cleanup_free_ char *p = NULL, *tty = NULL;
_cleanup_close_ int fd = -1;
struct ucred ucred;
- int v, r;
+ int v, r, salen;
assert(display);
assert(vtnr);
@@ -130,13 +158,15 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
r = socket_from_display(display, &p);
if (r < 0)
return r;
- strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
+ salen = sockaddr_un_set_path(&sa.un, p);
+ if (salen < 0)
+ return salen;
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
if (fd < 0)
return -errno;
- if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+ if (connect(fd, &sa.sa, salen) < 0)
return -errno;
r = getpeercred(fd, &ucred);
@@ -160,40 +190,6 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
return 0;
}
-static int export_legacy_dbus_address(
- pam_handle_t *handle,
- uid_t uid,
- const char *runtime) {
-
- _cleanup_free_ char *s = NULL;
- int r = PAM_BUF_ERR;
-
- /* FIXME: We *really* should move the access() check into the
- * daemons that spawn dbus-daemon, instead of forcing
- * DBUS_SESSION_BUS_ADDRESS= here. */
-
- s = strjoin(runtime, "/bus");
- if (!s)
- goto error;
-
- if (access(s, F_OK) < 0)
- return PAM_SUCCESS;
-
- s = mfree(s);
- if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
- goto error;
-
- r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0);
- if (r != PAM_SUCCESS)
- goto error;
-
- 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) {
uint64_t val;
int r;
@@ -208,9 +204,9 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
return r;
}
} else {
- r = parse_percent(limit);
+ r = parse_permille(limit);
if (r >= 0) {
- r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
+ 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(-r));
return r;
@@ -231,8 +227,7 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
return 0;
}
-static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit)
-{
+static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
uint64_t val;
int r;
@@ -257,23 +252,92 @@ static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, con
uint64_t val;
int r;
- if (!isempty(limit)) {
- 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(-r));
- return r;
- }
- } else if (streq(field, "CPUWeight"))
- pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit);
- else
- pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit);
- }
+ if (isempty(limit))
+ return 0;
+
+ 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(-r));
+ return r;
+ }
+ } else if (streq(field, "CPUWeight"))
+ pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit);
+ else
+ pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit);
return 0;
}
+static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) {
+ const char *v;
+
+ assert(handle);
+ assert(key);
+
+ /* Looks for an environment variable, preferrably in the environment block associated with the specified PAM
+ * handle, falling back to the process' block instead. */
+
+ v = pam_getenv(handle, key);
+ if (!isempty(v))
+ return v;
+
+ v = getenv(key);
+ if (!isempty(v))
+ return v;
+
+ return fallback;
+}
+
+static int update_environment(pam_handle_t *handle, const char *key, const char *value) {
+ int r;
+
+ assert(handle);
+ assert(key);
+
+ /* Updates the environment, but only if there's actually a value set. Also, log about errors */
+
+ if (isempty(value))
+ return PAM_SUCCESS;
+
+ r = pam_misc_setenv(handle, key, value, 0);
+ if (r != PAM_SUCCESS)
+ pam_syslog(handle, LOG_ERR, "Failed to set environment variable %s.", key);
+
+ return r;
+}
+
+static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) {
+ struct stat st;
+
+ assert(path);
+
+ /* Just some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually set
+ * up properly for us. */
+
+ if (lstat(path, &st) < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror(errno));
+ goto fail;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not actually a directory.", path);
+ goto fail;
+ }
+
+ if (st.st_uid != uid) {
+ pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not owned by UID " UID_FMT ", as it should.", path, uid);
+ goto fail;
+ }
+
+ return true;
+
+fail:
+ pam_syslog(handle, LOG_WARNING, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order.");
+ return false;
+}
+
_public_ PAM_EXTERN int pam_sm_open_session(
pam_handle_t *handle,
int flags,
@@ -288,7 +352,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
*remote_user = NULL, *remote_host = NULL,
*seat = NULL,
*type = NULL, *class = NULL,
- *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL,
+ *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;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int session_fd = -1, existing, r;
@@ -307,6 +371,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
argc, argv,
&class_pam,
&type_pam,
+ &desktop_pam,
&debug) < 0)
return PAM_SESSION_ERR;
@@ -332,16 +397,14 @@ _public_ PAM_EXTERN int pam_sm_open_session(
if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0)
return PAM_BUF_ERR;
- r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
- if (r != PAM_SUCCESS) {
- pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
- return r;
+ if (validate_runtime_directory(handle, rt, pw->pw_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.");
+ return r;
+ }
}
- r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
- if (r != PAM_SUCCESS)
- return r;
-
return PAM_SUCCESS;
}
@@ -352,55 +415,41 @@ _public_ PAM_EXTERN int pam_sm_open_session(
pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
- seat = pam_getenv(handle, "XDG_SEAT");
- if (isempty(seat))
- seat = getenv("XDG_SEAT");
-
- cvtnr = pam_getenv(handle, "XDG_VTNR");
- if (isempty(cvtnr))
- cvtnr = getenv("XDG_VTNR");
-
- type = pam_getenv(handle, "XDG_SESSION_TYPE");
- if (isempty(type))
- type = getenv("XDG_SESSION_TYPE");
- if (isempty(type))
- type = type_pam;
-
- class = pam_getenv(handle, "XDG_SESSION_CLASS");
- if (isempty(class))
- class = getenv("XDG_SESSION_CLASS");
- if (isempty(class))
- class = class_pam;
-
- desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP");
- if (isempty(desktop))
- desktop = getenv("XDG_SESSION_DESKTOP");
+ seat = getenv_harder(handle, "XDG_SEAT", NULL);
+ cvtnr = getenv_harder(handle, "XDG_VTNR", NULL);
+ type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam);
+ class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
+ desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
tty = strempty(tty);
if (strchr(tty, ':')) {
- /* A tty with a colon is usually an X11 display,
- * placed there to show up in utmp. We rearrange
- * things and don't pretend that an X display was a
- * tty. */
-
+ /* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things
+ * and don't pretend that an X display was a tty. */
if (isempty(display))
display = tty;
tty = NULL;
+
} else if (streq(tty, "cron")) {
- /* cron has been setting PAM_TTY to "cron" for a very
- * long time and it probably shouldn't stop doing that
- * for compatibility reasons. */
+ /* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but
+ * probably because it wants to set it to something as pam_time/pam_access/… require PAM_TTY to be set
+ * (as they otherwise even try to update it!) — but cron doesn't actually allocate a TTY for its forked
+ * off processes.) */
type = "unspecified";
class = "background";
tty = NULL;
+
} else if (streq(tty, "ssh")) {
- /* ssh has been setting PAM_TTY to "ssh" for a very
- * long time and probably shouldn't stop doing that
- * for compatibility reasons. */
+ /* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further
+ * details look for "PAM_TTY_KLUDGE" in the openssh sources). */
type ="tty";
class = "user";
- tty = NULL;
+ tty = NULL; /* This one is particularly sad, as this means that ssh sessions — even though usually
+ * associated with a pty — won't be tracked by their tty in logind. This is because ssh
+ * does the PAM session registration early for new connections, and registers a pty only
+ * much later (this is because it doesn't know yet if it needs one at all, as whether to
+ * register a pty or not is negotiated much later in the protocol). */
+
} else
/* Chop off leading /dev prefix that some clients specify, but others do not. */
tty = skip_dev_prefix(tty);
@@ -411,13 +460,14 @@ _public_ PAM_EXTERN int pam_sm_open_session(
if (!isempty(display) && !vtnr) {
if (isempty(seat))
- get_seat_from_display(display, &seat, &vtnr);
+ (void) get_seat_from_display(display, &seat, &vtnr);
else if (streq(seat, "seat0"))
- get_seat_from_display(display, NULL, &vtnr);
+ (void) get_seat_from_display(display, NULL, &vtnr);
}
if (seat && !streq(seat, "seat0") && vtnr != 0) {
- pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat);
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat);
vtnr = 0;
}
@@ -470,7 +520,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
r = sd_bus_message_append(m, "uusssssussbss",
(uint32_t) pw->pw_uid,
- (uint32_t) getpid_cached(),
+ 0,
service,
type,
class,
@@ -518,7 +568,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
- pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r));
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r));
return PAM_SUCCESS;
} else {
pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
@@ -546,11 +597,9 @@ _public_ PAM_EXTERN int pam_sm_open_session(
"id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
id, object_path, runtime_path, session_fd, seat, vtnr, original_uid);
- r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
- if (r != PAM_SUCCESS) {
- pam_syslog(handle, LOG_ERR, "Failed to set session id.");
+ r = update_environment(handle, "XDG_SESSION_ID", id);
+ if (r != PAM_SUCCESS)
return r;
- }
if (original_uid == pw->pw_uid) {
/* Don't set $XDG_RUNTIME_DIR if the user we now
@@ -559,34 +608,40 @@ _public_ PAM_EXTERN int pam_sm_open_session(
* in privileged apps clobbering the runtime directory
* unnecessarily. */
- r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
- if (r != PAM_SUCCESS) {
- pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
- return r;
+ if (validate_runtime_directory(handle, runtime_path, pw->pw_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);
- if (r != PAM_SUCCESS)
- return r;
}
- if (!isempty(seat)) {
- r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
- if (r != PAM_SUCCESS) {
- pam_syslog(handle, LOG_ERR, "Failed to set seat.");
- return r;
- }
- }
+ /* Most likely we got the session/type/class from environment variables, but might have gotten the data
+ * somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this
+ * data is inherited into the session processes, and programs can rely on them to be initialized. */
+
+ r = update_environment(handle, "XDG_SESSION_TYPE", type);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = update_environment(handle, "XDG_SESSION_CLASS", class);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = update_environment(handle, "XDG_SESSION_DESKTOP", desktop);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = update_environment(handle, "XDG_SEAT", seat);
+ if (r != PAM_SUCCESS)
+ return r;
if (vtnr > 0) {
char buf[DECIMAL_STR_MAX(vtnr)];
sprintf(buf, "%u", vtnr);
- r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
- if (r != PAM_SUCCESS) {
- pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
+ r = update_environment(handle, "XDG_VTNR", buf);
+ if (r != PAM_SUCCESS)
return r;
- }
}
r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
@@ -628,7 +683,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
/* Only release session if it wasn't pre-existing when we
* tried to create it */
- pam_get_data(handle, "systemd.existing", &existing);
+ (void) pam_get_data(handle, "systemd.existing", &existing);
id = pam_getenv(handle, "XDG_SESSION_ID");
if (id && !existing) {
diff --git a/src/login/sysfs-show.c b/src/login/sysfs-show.c
index d5c347be0c..a41d83b897 100644
--- a/src/login/sysfs-show.c
+++ b/src/login/sysfs-show.c
@@ -3,31 +3,33 @@
#include <errno.h>
#include <string.h>
-#include "libudev.h"
+#include "sd-device.h"
#include "alloc-util.h"
+#include "device-enumerator-private.h"
#include "locale-util.h"
#include "path-util.h"
#include "string-util.h"
#include "sysfs-show.h"
#include "terminal-util.h"
-#include "udev-util.h"
#include "util.h"
static int show_sysfs_one(
- struct udev *udev,
const char *seat,
- struct udev_list_entry **item,
+ sd_device **dev_list,
+ size_t *i_dev,
+ size_t n_dev,
const char *sub,
const char *prefix,
unsigned n_columns,
OutputFlags flags) {
size_t max_width;
+ int r;
- assert(udev);
assert(seat);
- assert(item);
+ assert(dev_list);
+ assert(i_dev);
assert(prefix);
if (flags & OUTPUT_FULL_WIDTH)
@@ -37,73 +39,58 @@ static int show_sysfs_one(
else
max_width = n_columns;
- while (*item) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
- struct udev_list_entry *next, *lookahead;
- const char *sn, *name, *sysfs, *subsystem, *sysname;
+ while (*i_dev < n_dev) {
+ const char *sysfs, *sn, *name = NULL, *subsystem, *sysname;
_cleanup_free_ char *k = NULL, *l = NULL;
+ size_t lookahead;
bool is_master;
- sysfs = udev_list_entry_get_name(*item);
- if (!path_startswith(sysfs, sub))
+ if (sd_device_get_syspath(dev_list[*i_dev], &sysfs) < 0 ||
+ !path_startswith(sysfs, sub))
return 0;
- d = udev_device_new_from_syspath(udev, sysfs);
- if (!d) {
- *item = udev_list_entry_get_next(*item);
- continue;
- }
-
- sn = udev_device_get_property_value(d, "ID_SEAT");
- if (isempty(sn))
+ if (sd_device_get_property_value(dev_list[*i_dev], "ID_SEAT", &sn) < 0 || isempty(sn))
sn = "seat0";
/* Explicitly also check for tag 'seat' here */
- if (!streq(seat, sn) || !udev_device_has_tag(d, "seat")) {
- *item = udev_list_entry_get_next(*item);
+ if (!streq(seat, sn) ||
+ sd_device_has_tag(dev_list[*i_dev], "seat") <= 0 ||
+ sd_device_get_subsystem(dev_list[*i_dev], &subsystem) < 0 ||
+ sd_device_get_sysname(dev_list[*i_dev], &sysname) < 0) {
+ (*i_dev)++;
continue;
}
- is_master = udev_device_has_tag(d, "master-of-seat");
+ is_master = sd_device_has_tag(dev_list[*i_dev], "master-of-seat") > 0;
- name = udev_device_get_sysattr_value(d, "name");
- if (!name)
- name = udev_device_get_sysattr_value(d, "id");
- subsystem = udev_device_get_subsystem(d);
- sysname = udev_device_get_sysname(d);
+ if (sd_device_get_sysattr_value(dev_list[*i_dev], "name", &name) < 0)
+ (void) sd_device_get_sysattr_value(dev_list[*i_dev], "id", &name);
/* Look if there's more coming after this */
- lookahead = next = udev_list_entry_get_next(*item);
- while (lookahead) {
+ for (lookahead = *i_dev + 1; lookahead < n_dev; lookahead++) {
const char *lookahead_sysfs;
- lookahead_sysfs = udev_list_entry_get_name(lookahead);
+ if (sd_device_get_syspath(dev_list[lookahead], &lookahead_sysfs) < 0)
+ continue;
if (path_startswith(lookahead_sysfs, sub) &&
!path_startswith(lookahead_sysfs, sysfs)) {
- _cleanup_(udev_device_unrefp) struct udev_device *lookahead_d = NULL;
+ const char *lookahead_sn;
- lookahead_d = udev_device_new_from_syspath(udev, lookahead_sysfs);
- if (lookahead_d) {
- const char *lookahead_sn;
+ if (sd_device_get_property_value(dev_list[lookahead], "ID_SEAT", &lookahead_sn) < 0 ||
+ isempty(lookahead_sn))
+ lookahead_sn = "seat0";
- lookahead_sn = udev_device_get_property_value(d, "ID_SEAT");
- if (isempty(lookahead_sn))
- lookahead_sn = "seat0";
-
- if (streq(seat, lookahead_sn) && udev_device_has_tag(lookahead_d, "seat"))
- break;
- }
+ if (streq(seat, lookahead_sn) && sd_device_has_tag(dev_list[lookahead], "seat") > 0)
+ break;
}
-
- lookahead = udev_list_entry_get_next(lookahead);
}
k = ellipsize(sysfs, max_width, 20);
if (!k)
return -ENOMEM;
- printf("%s%s%s\n", prefix, special_glyph(lookahead ? TREE_BRANCH : TREE_RIGHT), k);
+ printf("%s%s%s\n", prefix, special_glyph(lookahead < n_dev ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT), k);
if (asprintf(&l,
"%s%s:%s%s%s%s",
@@ -117,29 +104,31 @@ static int show_sysfs_one(
if (!k)
return -ENOMEM;
- printf("%s%s%s\n", prefix, lookahead ? special_glyph(TREE_VERTICAL) : " ", k);
+ printf("%s%s%s\n", prefix, lookahead < n_dev ? special_glyph(SPECIAL_GLYPH_TREE_VERTICAL) : " ", k);
- *item = next;
- if (*item) {
+ if (++(*i_dev) < n_dev) {
_cleanup_free_ char *p = NULL;
- p = strappend(prefix, lookahead ? special_glyph(TREE_VERTICAL) : " ");
+ p = strappend(prefix, lookahead < n_dev ? special_glyph(SPECIAL_GLYPH_TREE_VERTICAL) : " ");
if (!p)
return -ENOMEM;
- show_sysfs_one(udev, seat, item, sysfs, p,
- n_columns == (unsigned) -1 || n_columns < 2 ? n_columns : n_columns - 2,
- flags);
+ r = show_sysfs_one(seat, dev_list, i_dev, n_dev, sysfs, p,
+ n_columns == (unsigned) -1 || n_columns < 2 ? n_columns : n_columns - 2,
+ flags);
+ if (r < 0)
+ return r;
}
+
}
return 0;
}
int show_sysfs(const char *seat, const char *prefix, unsigned n_columns, OutputFlags flags) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
- struct udev_list_entry *first = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ size_t n_dev = 0, i = 0;
+ sd_device **dev_list;
int r;
if (n_columns <= 0)
@@ -150,34 +139,28 @@ int show_sysfs(const char *seat, const char *prefix, unsigned n_columns, OutputF
if (isempty(seat))
seat = "seat0";
- udev = udev_new();
- if (!udev)
- return -ENOMEM;
-
- e = udev_enumerate_new(udev);
- if (!e)
- return -ENOMEM;
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
- if (!streq(seat, "seat0"))
- r = udev_enumerate_add_match_tag(e, seat);
- else
- r = udev_enumerate_add_match_tag(e, "seat");
+ r = sd_device_enumerator_allow_uninitialized(e);
if (r < 0)
return r;
- r = udev_enumerate_add_match_is_initialized(e);
+ r = sd_device_enumerator_add_match_tag(e, streq(seat, "seat0") ? "seat" : seat);
if (r < 0)
return r;
- r = udev_enumerate_scan_devices(e);
+ r = device_enumerator_scan_devices(e);
if (r < 0)
return r;
- first = udev_enumerate_get_list_entry(e);
- if (first)
- show_sysfs_one(udev, seat, &first, "/", prefix, n_columns, flags);
+ dev_list = device_enumerator_get_devices(e, &n_dev);
+
+ if (dev_list && n_dev > 0)
+ show_sysfs_one(seat, dev_list, &i, n_dev, "/", prefix, n_columns, flags);
else
- printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), "(none)");
+ printf("%s%s%s\n", prefix, special_glyph(SPECIAL_GLYPH_TREE_RIGHT), "(none)");
- return r;
+ return 0;
}
diff --git a/src/login/test-inhibit.c b/src/login/test-inhibit.c
index 75c9303ff3..a891b0af6f 100644
--- a/src/login/test-inhibit.c
+++ b/src/login/test-inhibit.c
@@ -65,7 +65,7 @@ static void print_inhibitors(sd_bus *bus) {
printf("%u inhibitors\n", n);
}
-int main(int argc, char*argv[]) {
+int main(int argc, char *argv[]) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int fd1, fd2;
int r;
diff --git a/src/login/user-runtime-dir.c b/src/login/user-runtime-dir.c
index 1bb26c99e4..5e58e4baad 100644
--- a/src/login/user-runtime-dir.c
+++ b/src/login/user-runtime-dir.c
@@ -3,34 +3,45 @@
#include <stdint.h>
#include <sys/mount.h>
+#include "sd-bus.h"
+
+#include "bus-error.h"
#include "fs-util.h"
#include "label.h"
-#include "logind.h"
+#include "main-func.h"
#include "mkdir.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "rm-rf.h"
+#include "selinux-util.h"
#include "smack-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
-static int gather_configuration(size_t *runtime_dir_size) {
- Manager m = {};
+static int acquire_runtime_dir_size(uint64_t *ret) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
- manager_reset_config(&m);
+ r = sd_bus_default_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to system bus: %m");
- r = manager_parse_config_file(&m);
+ r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', ret);
if (r < 0)
- log_warning_errno(r, "Failed to parse logind.conf: %m");
+ return log_error_errno(r, "Failed to acquire runtime directory size: %s", bus_error_message(&error, r));
- *runtime_dir_size = m.runtime_dir_size;
return 0;
}
-static int user_mkdir_runtime_path(const char *runtime_path, uid_t uid, gid_t gid, size_t runtime_dir_size) {
+static int user_mkdir_runtime_path(
+ const char *runtime_path,
+ uid_t uid,
+ gid_t gid,
+ uint64_t runtime_dir_size) {
+
int r;
assert(runtime_path);
@@ -48,10 +59,10 @@ static int user_mkdir_runtime_path(const char *runtime_path, uid_t uid, gid_t gi
char options[sizeof("mode=0700,uid=,gid=,size=,smackfsroot=*")
+ DECIMAL_STR_MAX(uid_t)
+ DECIMAL_STR_MAX(gid_t)
- + DECIMAL_STR_MAX(size_t)];
+ + DECIMAL_STR_MAX(uint64_t)];
xsprintf(options,
- "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu%s",
+ "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 "%s",
uid, gid, runtime_dir_size,
mac_smack_use() ? ",smackfsroot=*" : "");
@@ -95,76 +106,93 @@ static int user_remove_runtime_path(const char *runtime_path) {
r = rm_rf(runtime_path, 0);
if (r < 0)
- log_error_errno(r, "Failed to remove runtime directory %s (before unmounting): %m", runtime_path);
+ log_debug_errno(r, "Failed to remove runtime directory %s (before unmounting), ignoring: %m", runtime_path);
- /* Ignore cases where the directory isn't mounted, as that's
- * quite possible, if we lacked the permissions to mount
- * something */
+ /* Ignore cases where the directory isn't mounted, as that's quite possible, if we lacked the permissions to
+ * mount something */
r = umount2(runtime_path, MNT_DETACH);
if (r < 0 && !IN_SET(errno, EINVAL, ENOENT))
- log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", runtime_path);
+ log_debug_errno(errno, "Failed to unmount user runtime directory %s, ignoring: %m", runtime_path);
r = rm_rf(runtime_path, REMOVE_ROOT);
- if (r < 0)
- log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", runtime_path);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", runtime_path);
- return r;
+ return 0;
}
-static int do_mount(const char *runtime_path, uid_t uid, gid_t gid) {
- size_t runtime_dir_size;
+static int do_mount(const char *user) {
+ char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
+ uint64_t runtime_dir_size;
+ uid_t uid;
+ gid_t gid;
+ int r;
- assert_se(gather_configuration(&runtime_dir_size) == 0);
+ r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r,
+ r == -ESRCH ? "No such user \"%s\"" :
+ r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group"
+ : "Failed to look up user \"%s\": %m",
+ user);
+
+ r = acquire_runtime_dir_size(&runtime_dir_size);
+ if (r < 0)
+ return r;
+
+ xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
log_debug("Will mount %s owned by "UID_FMT":"GID_FMT, runtime_path, uid, gid);
return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size);
}
-static int do_umount(const char *runtime_path) {
+static int do_umount(const char *user) {
+ char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
+ uid_t uid;
+ int r;
+
+ /* The user may be already removed. So, first try to parse the string by parse_uid(),
+ * and if it fails, fallback to get_user_creds().*/
+ if (parse_uid(user, &uid) < 0) {
+ r = get_user_creds(&user, &uid, NULL, NULL, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r,
+ r == -ESRCH ? "No such user \"%s\"" :
+ r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group"
+ : "Failed to look up user \"%s\": %m",
+ user);
+ }
+
+ xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
+
log_debug("Will remove %s", runtime_path);
return user_remove_runtime_path(runtime_path);
}
-int main(int argc, char *argv[]) {
- const char *user;
- uid_t uid;
- gid_t gid;
- char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
+static int run(int argc, char *argv[]) {
int r;
log_parse_environment();
log_open();
- if (argc != 3) {
- log_error("This program takes two arguments.");
- return EXIT_FAILURE;
- }
- if (!STR_IN_SET(argv[1], "start", "stop")) {
- log_error("First argument must be either \"start\" or \"stop\".");
- return EXIT_FAILURE;
- }
-
- umask(0022);
+ if (argc != 3)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "This program takes two arguments.");
+ if (!STR_IN_SET(argv[1], "start", "stop"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "First argument must be either \"start\" or \"stop\".");
- user = argv[2];
- r = get_user_creds(&user, &uid, &gid, NULL, NULL);
- if (r < 0) {
- log_error_errno(r,
- r == -ESRCH ? "No such user \"%s\"" :
- r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group"
- : "Failed to look up user \"%s\": %m",
- user);
- return EXIT_FAILURE;
- }
+ r = mac_selinux_init();
+ if (r < 0)
+ return log_error_errno(r, "Could not initialize labelling: %m\n");
- xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
+ umask(0022);
if (streq(argv[1], "start"))
- r = do_mount(runtime_path, uid, gid);
- else if (streq(argv[1], "stop"))
- r = do_umount(runtime_path);
- else
- assert_not_reached("Unknown verb!");
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return do_mount(argv[2]);
+ if (streq(argv[1], "stop"))
+ return do_umount(argv[2]);
+ assert_not_reached("Unknown verb!");
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/machine-id-setup/machine-id-setup-main.c b/src/machine-id-setup/machine-id-setup-main.c
index 966fd8e404..1b575d7725 100644
--- a/src/machine-id-setup/machine-id-setup-main.c
+++ b/src/machine-id-setup/machine-id-setup-main.c
@@ -5,17 +5,29 @@
#include <stdio.h>
#include <stdlib.h>
+#include "alloc-util.h"
#include "id128-util.h"
#include "log.h"
#include "machine-id-setup.h"
+#include "main-func.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "util.h"
static char *arg_root = NULL;
static bool arg_commit = false;
static bool arg_print = false;
-static void help(void) {
+STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-machine-id-setup", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"Initialize /etc/machine-id from a random source.\n\n"
" -h --help Show this help\n"
@@ -23,7 +35,12 @@ static void help(void) {
" --root=ROOT Filesystem root\n"
" --commit Commit transient ID\n"
" --print Print used machine ID\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -54,8 +71,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -81,15 +97,14 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind < argc) {
- log_error("Extraneous arguments");
- return -EINVAL;
- }
+ if (optind < argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Extraneous arguments");
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
char buf[SD_ID128_STRING_MAX];
sd_id128_t id;
int r;
@@ -99,31 +114,29 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
if (arg_commit) {
const char *etc_machine_id;
r = machine_id_commit(arg_root);
if (r < 0)
- goto finish;
+ return r;
etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
r = id128_read(etc_machine_id, ID128_PLAIN, &id);
- if (r < 0) {
- log_error_errno(r, "Failed to read machine ID back: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read machine ID back: %m");
} else {
r = machine_id_setup(arg_root, SD_ID128_NULL, &id);
if (r < 0)
- goto finish;
+ return r;
}
if (arg_print)
puts(sd_id128_to_string(id, buf));
-finish:
- free(arg_root);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 89df274544..7e7f0d51bf 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -15,6 +15,7 @@
#include "io-util.h"
#include "loop-util.h"
#include "machine-image.h"
+#include "missing_capability.h"
#include "mount-util.h"
#include "process-util.h"
#include "raw-clone.h"
@@ -169,7 +170,7 @@ int bus_image_method_clone(
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
- r = safe_fork("(imgclone)", FORK_RESET_SIGNALS, &child);
+ r = safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS, &child);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
if (r == 0) {
@@ -382,7 +383,7 @@ static int image_flush_cache(sd_event_source *s, void *userdata) {
assert(s);
assert(m);
- hashmap_clear_with_destructor(m->image_cache, image_unref);
+ hashmap_clear(m->image_cache);
return 0;
}
@@ -412,7 +413,7 @@ int image_object_find(sd_bus *bus, const char *path, const char *interface, void
return 1;
}
- r = hashmap_ensure_allocated(&m->image_cache, &string_hash_ops);
+ r = hashmap_ensure_allocated(&m->image_cache, &image_hash_ops);
if (r < 0)
return r;
@@ -461,7 +462,7 @@ char *image_bus_path(const char *name) {
}
int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
- _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
+ _cleanup_hashmap_free_ Hashmap *images = NULL;
_cleanup_strv_free_ char **l = NULL;
Image *image;
Iterator i;
@@ -471,7 +472,7 @@ int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char **
assert(path);
assert(nodes);
- images = hashmap_new(&string_hash_ops);
+ images = hashmap_new(&image_hash_ops);
if (!images)
return -ENOMEM;
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index 7f41465ccd..48270b3709 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -17,15 +17,17 @@
#include "bus-label.h"
#include "bus-util.h"
#include "copy.h"
+#include "env-file.h"
#include "env-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "in-addr-util.h"
+#include "io-util.h"
#include "local-addresses.h"
#include "machine-dbus.h"
#include "machine.h"
+#include "missing_capability.h"
#include "mkdir.h"
#include "os-util.h"
#include "path-util.h"
@@ -33,6 +35,7 @@
#include "signal-util.h"
#include "strv.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "user-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
@@ -207,7 +210,8 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
return -errno;
- r = safe_fork("(sd-addr)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ r = namespace_fork("(sd-addrns)", "(sd-addr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ -1, -1, netns_fd, -1, -1, &child);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
if (r == 0) {
@@ -217,10 +221,6 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
pair[0] = safe_close(pair[0]);
- r = namespace_enter(-1, -1, netns_fd, -1, -1);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
if (n < 0)
_exit(EXIT_FAILURE);
@@ -253,8 +253,8 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
.msg_iovlen = 2,
};
- iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
- iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
+ iov[0] = IOVEC_MAKE(&family, sizeof(family));
+ iov[1] = IOVEC_MAKE(&in_addr, sizeof(in_addr));
n = recvmsg(pair[0], &mh, 0);
if (n < 0)
@@ -294,7 +294,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
return r;
}
- r = wait_for_terminate_and_check("(sd-addr)", child, 0);
+ r = wait_for_terminate_and_check("(sd-addrns)", child, 0);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (r != EXIT_SUCCESS)
@@ -333,19 +333,21 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
break;
case MACHINE_CONTAINER: {
- _cleanup_close_ int mntns_fd = -1, root_fd = -1;
+ _cleanup_close_ int mntns_fd = -1, root_fd = -1, pidns_fd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_fclose_ FILE *f = NULL;
pid_t child;
- r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
+ r = namespace_open(m->leader, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
if (r < 0)
return r;
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
return -errno;
- r = safe_fork("(sd-osrel)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ r = namespace_fork("(sd-osrelns)", "(sd-osrel)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ pidns_fd, mntns_fd, -1, -1, root_fd,
+ &child);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
if (r == 0) {
@@ -353,10 +355,6 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
pair[0] = safe_close(pair[0]);
- r = namespace_enter(-1, mntns_fd, -1, -1, root_fd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
r = open_os_release(NULL, NULL, &fd);
if (r == -ENOENT)
_exit(EXIT_NOT_FOUND);
@@ -372,17 +370,17 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
pair[1] = safe_close(pair[1]);
- f = fdopen(pair[0], "re");
+ f = fdopen(pair[0], "r");
if (!f)
return -errno;
pair[0] = -1;
- r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
+ r = load_env_file_pairs(f, "/etc/os-release", &l);
if (r < 0)
return r;
- r = wait_for_terminate_and_check("(sd-osrel)", child, 0);
+ r = wait_for_terminate_and_check("(sd-osrelns)", child, 0);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (r == EXIT_NOT_FOUND)
@@ -605,7 +603,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (strv_isempty(args)) {
args = strv_free(args);
- args = strv_new(path, NULL);
+ args = strv_new(path);
if (!args)
return -ENOMEM;
}
@@ -1058,7 +1056,7 @@ finish:
}
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname;
+ const char *src, *dest, *host_path, *container_path, *host_basename, *container_basename, *container_dirname;
_cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE;
_cleanup_close_ int hostfd = -1;
@@ -1119,16 +1117,14 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
}
host_basename = basename(host_path);
- t = strdupa(host_path);
- host_dirname = dirname(t);
container_basename = basename(container_path);
t = strdupa(container_path);
container_dirname = dirname(t);
- hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
+ hostfd = open_parent(host_path, O_CLOEXEC, 0);
if (hostfd < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to open host directory %s: %m", host_dirname);
+ return sd_bus_error_set_errnof(error, hostfd, "Failed to open host directory %s: %m", host_path);
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
@@ -1241,7 +1237,8 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- r = safe_fork("(sd-openroot)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ r = namespace_fork("(sd-openrootns)", "(sd-openroot)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ -1, mntns_fd, -1, -1, root_fd, &child);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
if (r == 0) {
@@ -1249,10 +1246,6 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
pair[0] = safe_close(pair[0]);
- r = namespace_enter(-1, mntns_fd, -1, -1, root_fd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (dfd < 0)
_exit(EXIT_FAILURE);
@@ -1267,7 +1260,7 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-openroot)", child, 0);
+ r = wait_for_terminate_and_check("(sd-openrootns)", child, 0);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (r != EXIT_SUCCESS)
diff --git a/src/machine/machine.c b/src/machine/machine.c
index b645f614c4..4f89ac026d 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -10,6 +10,7 @@
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
+#include "env-file.h"
#include "escape.h"
#include "extract-word.h"
#include "fd-util.h"
@@ -21,10 +22,12 @@
#include "mkdir.h"
#include "parse-util.h"
#include "process-util.h"
+#include "serialize.h"
#include "special.h"
#include "stdio-util.h"
#include "string-table.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "unit-name.h"
#include "user-util.h"
#include "util.h"
@@ -69,8 +72,9 @@ fail:
return mfree(m);
}
-void machine_free(Machine *m) {
- assert(m);
+Machine* machine_free(Machine *m) {
+ if (!m)
+ return NULL;
while (m->operations)
operation_free(m->operations);
@@ -97,7 +101,7 @@ void machine_free(Machine *m) {
free(m->service);
free(m->root_directory);
free(m->netif);
- free(m);
+ return mfree(m);
}
int machine_save(Machine *m) {
@@ -183,7 +187,7 @@ int machine_save(Machine *m) {
m->timestamp.monotonic);
if (m->n_netif > 0) {
- unsigned i;
+ size_t i;
fputs("NETIF=", f);
@@ -250,7 +254,7 @@ int machine_load(Machine *m) {
if (!m->state_file)
return 0;
- r = parse_env_file(NULL, m->state_file, NEWLINE,
+ r = parse_env_file(NULL, m->state_file,
"SCOPE", &m->unit,
"SCOPE_JOB", &m->scope_job,
"SERVICE", &m->service,
@@ -260,8 +264,7 @@ int machine_load(Machine *m) {
"CLASS", &class,
"REALTIME", &realtime,
"MONOTONIC", &monotonic,
- "NETIF", &netif,
- NULL);
+ "NETIF", &netif);
if (r < 0) {
if (r == -ENOENT)
return 0;
@@ -284,9 +287,9 @@ int machine_load(Machine *m) {
}
if (realtime)
- timestamp_deserialize(realtime, &m->timestamp.realtime);
+ (void) deserialize_usec(realtime, &m->timestamp.realtime);
if (monotonic)
- timestamp_deserialize(monotonic, &m->timestamp.monotonic);
+ (void) deserialize_usec(monotonic, &m->timestamp.monotonic);
if (netif) {
size_t allocated = 0, nr = 0;
@@ -328,14 +331,13 @@ int machine_load(Machine *m) {
}
static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
- int r = 0;
-
assert(m);
assert(m->class != MACHINE_HOST);
if (!m->unit) {
- _cleanup_free_ char *escaped = NULL;
- char *scope, *description, *job = NULL;
+ _cleanup_free_ char *escaped = NULL, *scope = NULL;
+ char *description, *job = NULL;
+ int r;
escaped = unit_name_escape(m->name);
if (!escaped)
@@ -348,22 +350,17 @@ static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_er
description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
- if (r < 0) {
- log_error("Failed to start machine scope: %s", bus_error_message(error, r));
- free(scope);
- return r;
- } else {
- m->unit = scope;
-
- free(m->scope_job);
- m->scope_job = job;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to start machine scope: %s", bus_error_message(error, r));
+
+ m->unit = TAKE_PTR(scope);
+ free_and_replace(m->scope_job, job);
}
if (m->unit)
hashmap_put(m->manager->machine_units, m->unit, m);
- return r;
+ return 0;
}
int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
@@ -401,6 +398,7 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
machine_save(m);
machine_send_signal(m, true);
+ (void) manager_enqueue_nscd_cache_flush(m->manager);
return 0;
}
@@ -408,7 +406,7 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
static int machine_stop_scope(Machine *m) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *job = NULL;
- int r;
+ int r, q;
assert(m);
assert(m->class != MACHINE_HOST);
@@ -418,14 +416,16 @@ static int machine_stop_scope(Machine *m) {
r = manager_stop_unit(m->manager, m->unit, &error, &job);
if (r < 0) {
- log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
- return r;
- }
+ log_error_errno(r, "Failed to stop machine scope: %s", bus_error_message(&error, r));
+ sd_bus_error_free(&error);
+ } else
+ free_and_replace(m->scope_job, job);
- free(m->scope_job);
- m->scope_job = job;
+ q = manager_unref_unit(m->manager, m->unit, &error);
+ if (q < 0)
+ log_warning_errno(q, "Failed to drop reference to machine scope, ignoring: %s", bus_error_message(&error, r));
- return 0;
+ return r;
}
int machine_stop(Machine *m) {
@@ -440,6 +440,7 @@ int machine_stop(Machine *m) {
m->stopping = true;
machine_save(m);
+ (void) manager_enqueue_nscd_cache_flush(m->manager);
return r;
}
@@ -593,7 +594,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
uid_t uid_base, uid_shift, uid_range;
gid_t gid_base, gid_shift, gid_range;
_cleanup_fclose_ FILE *f = NULL;
- int k;
+ int k, r;
assert(m);
assert(ret);
@@ -642,7 +643,10 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
return -ENXIO;
/* If there's more than one line, then we don't support this mapping. */
- if (fgetc(f) != EOF)
+ r = safe_fgetc(f, NULL);
+ if (r < 0)
+ return r;
+ if (r != 0) /* Insist on EOF */
return -ENXIO;
fclose(f);
@@ -663,7 +667,10 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
}
/* If there's more than one line, then we don't support this file. */
- if (fgetc(f) != EOF)
+ r = safe_fgetc(f, NULL);
+ if (r < 0)
+ return r;
+ if (r != 0) /* Insist on EOF */
return -ENXIO;
/* If the UID and GID mapping doesn't match, we don't support this mapping. */
diff --git a/src/machine/machine.h b/src/machine/machine.h
index 946838f86f..31527d029b 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -57,7 +57,7 @@ struct Machine {
sd_bus_message *create_message;
int *netif;
- unsigned n_netif;
+ size_t n_netif;
LIST_HEAD(Operation, operations);
@@ -65,7 +65,7 @@ struct Machine {
};
Machine* machine_new(Manager *manager, MachineClass class, const char *name);
-void machine_free(Machine *m);
+Machine* machine_free(Machine *m);
bool machine_may_gc(Machine *m, bool drop_not_started);
void machine_add_to_gc_queue(Machine *m);
int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index d656681daf..44e6c76035 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -5,6 +5,7 @@
#include <fcntl.h>
#include <getopt.h>
#include <locale.h>
+#include <math.h>
#include <net/if.h>
#include <netinet/in.h>
#include <string.h>
@@ -22,6 +23,7 @@
#include "cgroup-show.h"
#include "cgroup-util.h"
#include "copy.h"
+#include "def.h"
#include "env-util.h"
#include "fd-util.h"
#include "format-table.h"
@@ -31,12 +33,15 @@
#include "log.h"
#include "logs-show.h"
#include "macro.h"
+#include "main-func.h"
#include "mkdir.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "process-util.h"
#include "ptyfwd.h"
+#include "rlimit-util.h"
#include "sigbus.h"
#include "signal-util.h"
#include "spawn-polkit-agent.h"
@@ -55,12 +60,12 @@ static char **arg_property = NULL;
static bool arg_all = false;
static bool arg_value = false;
static bool arg_full = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static const char *arg_kill_who = NULL;
static int arg_signal = SIGTERM;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
-static char *arg_host = NULL;
+static const char *arg_host = NULL;
static bool arg_read_only = false;
static bool arg_mkdir = false;
static bool arg_quiet = false;
@@ -74,6 +79,9 @@ static const char *arg_uid = NULL;
static char **arg_setenv = NULL;
static int arg_addrs = 1;
+STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_setenv, strv_freep);
+
static OutputFlags get_output_flags(void) {
return
arg_all * OUTPUT_SHOW_ALL |
@@ -229,7 +237,7 @@ static int call_get_addresses(sd_bus *bus, const char *name, int ifi, const char
if (truncate) {
- if (!strextend(&addresses, special_glyph(ELLIPSIS), NULL))
+ if (!strextend(&addresses, special_glyph(SPECIAL_GLYPH_ELLIPSIS), NULL))
return -ENOMEM;
}
@@ -251,7 +259,10 @@ static int show_table(Table *table, const char *word) {
table_set_header(table, arg_legend);
- r = table_print(table, NULL);
+ if (OUTPUT_MODE_IS_JSON(arg_output))
+ r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO);
+ else
+ r = table_print(table, NULL);
if (r < 0)
return log_error_errno(r, "Failed to show table: %m");
}
@@ -276,7 +287,7 @@ static int list_machines(int argc, char *argv[], void *userdata) {
assert(bus);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
r = sd_bus_call_method(bus,
"org.freedesktop.machine1",
@@ -289,7 +300,7 @@ static int list_machines(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Could not get machines: %s", bus_error_message(&error, r));
- table = table_new("MACHINE", "CLASS", "SERVICE", "OS", "VERSION", "ADDRESSES");
+ table = table_new("machine", "class", "service", "os", "version", "addresses");
if (!table)
return log_oom();
@@ -324,7 +335,7 @@ static int list_machines(int argc, char *argv[], void *userdata) {
name,
0,
"",
- "",
+ " ",
arg_addrs,
&addresses);
@@ -356,7 +367,7 @@ static int list_images(int argc, char *argv[], void *userdata) {
assert(bus);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
r = sd_bus_call_method(bus,
"org.freedesktop.machine1",
@@ -369,7 +380,7 @@ static int list_images(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Could not get images: %s", bus_error_message(&error, r));
- table = table_new("NAME", "TYPE", "RO", "USAGE", "CREATED", "MODIFIED");
+ table = table_new("name", "type", "ro", "usage", "created", "modified");
if (!table)
return log_oom();
@@ -467,20 +478,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
return 0;
}
-static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2, int n_addr) {
- _cleanup_free_ char *s = NULL;
- int r;
-
- r = call_get_addresses(bus, name, ifi, prefix, prefix2, n_addr, &s);
- if (r < 0)
- return r;
-
- if (r > 0)
- fputs(s, stdout);
-
- return r;
-}
-
static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) {
_cleanup_free_ char *pretty = NULL;
int r;
@@ -540,7 +537,7 @@ typedef struct MachineStatusInfo {
pid_t leader;
struct dual_timestamp timestamp;
int *netif;
- unsigned n_netif;
+ size_t n_netif;
} MachineStatusInfo;
static void machine_status_info_clear(MachineStatusInfo *info) {
@@ -553,6 +550,7 @@ static void machine_status_info_clear(MachineStatusInfo *info) {
static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX];
char since2[FORMAT_TIMESTAMP_MAX];
+ _cleanup_free_ char *addresses = NULL;
const char *s1, *s2;
int ifi = -1;
@@ -600,7 +598,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
printf("\t Root: %s\n", i->root_directory);
if (i->n_netif > 0) {
- unsigned c;
+ size_t c;
fputs("\t Iface:", stdout);
@@ -622,11 +620,12 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
fputc('\n', stdout);
}
- if (print_addresses(bus, i->name, ifi,
- "\t Address: ",
- "\n\t ",
- ALL_IP_ADDRESSES) > 0)
+ if (call_get_addresses(bus, i->name, ifi,
+ "\t Address: ", "\n\t ", ALL_IP_ADDRESSES,
+ &addresses) > 0) {
+ fputs(addresses, stdout);
fputc('\n', stdout);
+ }
print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: ");
@@ -751,7 +750,7 @@ static int show_machine(int argc, char *argv[], void *userdata) {
properties = !strstr(argv[0], "status");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (properties && argc <= 1) {
@@ -773,10 +772,8 @@ static int show_machine(int argc, char *argv[], void *userdata) {
&error,
&reply,
"s", argv[i]);
- if (r < 0) {
- log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not get path to machine: %s", bus_error_message(&error, -r));
r = sd_bus_message_read(reply, "o", &path);
if (r < 0)
@@ -1091,7 +1088,7 @@ static int show_image(int argc, char *argv[], void *userdata) {
properties = !strstr(argv[0], "status");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (argc <= 1) {
@@ -1118,10 +1115,8 @@ static int show_image(int argc, char *argv[], void *userdata) {
&error,
&reply,
"s", argv[i]);
- if (r < 0) {
- log_error("Could not get path to image: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not get path to image: %s", bus_error_message(&error, -r));
r = sd_bus_message_read(reply, "o", &path);
if (r < 0)
@@ -1158,10 +1153,8 @@ static int kill_machine(int argc, char *argv[], void *userdata) {
&error,
NULL,
"ssi", argv[i], arg_kill_who, arg_signal);
- if (r < 0) {
- log_error("Could not kill machine: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not kill machine: %s", bus_error_message(&error, -r));
}
return 0;
@@ -1200,10 +1193,8 @@ static int terminate_machine(int argc, char *argv[], void *userdata) {
&error,
NULL,
"s", argv[i]);
- if (r < 0) {
- log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not terminate machine: %s", bus_error_message(&error, -r));
}
return 0;
@@ -1285,10 +1276,8 @@ static int bind_mount(int argc, char *argv[], void *userdata) {
argv[3],
arg_read_only,
arg_mkdir);
- if (r < 0) {
- log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind mount: %s", bus_error_message(&error, -r));
return 0;
}
@@ -1335,8 +1324,8 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT
log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
}
- sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
- sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ (void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+ (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
r = pty_forward_new(event, master, flags, forward);
if (r < 0)
@@ -1459,10 +1448,8 @@ static int login_machine(int argc, char *argv[], void *userdata) {
&error,
&reply,
"s", machine);
- if (r < 0) {
- log_error("Failed to get login PTY: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get login PTY: %s", bus_error_message(&error, -r));
r = sd_bus_message_read(reply, "hs", &master, NULL);
if (r < 0)
@@ -1550,10 +1537,8 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0) {
- log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get shell PTY: %s", bus_error_message(&error, -r));
r = sd_bus_message_read(reply, "hs", &master, NULL);
if (r < 0)
@@ -1615,10 +1600,8 @@ static int rename_image(int argc, char *argv[], void *userdata) {
&error,
NULL,
"ss", argv[1], argv[2]);
- if (r < 0) {
- log_error("Could not rename image: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not rename image: %s", bus_error_message(&error, -r));
return 0;
}
@@ -1681,10 +1664,8 @@ static int read_only_image(int argc, char *argv[], void *userdata) {
&error,
NULL,
"sb", argv[1], b);
- if (r < 0) {
- log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, -r));
return 0;
}
@@ -1721,10 +1702,9 @@ static int make_service_name(const char *name, char **ret) {
assert(name);
assert(ret);
- if (!machine_name_is_valid(name)) {
- log_error("Invalid machine name %s.", name);
- return -EINVAL;
- }
+ if (!machine_name_is_valid(name))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid machine name %s.", name);
r = unit_name_build("systemd-nspawn", name, ".service", ret);
if (r < 0)
@@ -1773,10 +1753,8 @@ static int start_machine(int argc, char *argv[], void *userdata) {
&error,
&reply,
"ss", unit, "fail");
- if (r < 0) {
- log_error("Failed to start unit: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to start unit: %s", bus_error_message(&error, -r));
r = sd_bus_message_read(reply, "o", &object);
if (r < 0)
@@ -1855,10 +1833,8 @@ static int enable_machine(int argc, char *argv[], void *userdata) {
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0) {
- log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable or disable unit: %s", bus_error_message(&error, -r));
if (streq(argv[0], "enable")) {
r = sd_bus_message_read(reply, "b", NULL);
@@ -1993,12 +1969,10 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
return log_error_errno(r, "Failed to request match: %m");
r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0) {
- log_error("Failed to transfer image: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to transfer image: %s", bus_error_message(&error, -r));
- r = sd_bus_message_read(reply, "uo", &id, NULL);
+ r = sd_bus_message_read(reply, "uo", &id, &path);
if (r < 0)
return bus_log_parse_error(r);
@@ -2007,8 +1981,8 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
if (!arg_quiet)
log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
- sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
- sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
+ (void) sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
+ (void) sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
r = sd_event_loop(event);
if (r < 0)
@@ -2017,28 +1991,38 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
return -r;
}
+static const char *nullify_dash(const char *p) {
+ if (isempty(p))
+ return NULL;
+
+ if (streq(p, "-"))
+ return NULL;
+
+ return p;
+}
+
static int import_tar(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- _cleanup_free_ char *ll = NULL;
- _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *ll = NULL, *fn = NULL;
const char *local = NULL, *path = NULL;
+ _cleanup_close_ int fd = -1;
sd_bus *bus = userdata;
int r;
assert(bus);
if (argc >= 2)
- path = argv[1];
- if (isempty(path) || streq(path, "-"))
- path = NULL;
+ path = nullify_dash(argv[1]);
if (argc >= 3)
- local = argv[2];
- else if (path)
- local = basename(path);
- if (isempty(local) || streq(local, "-"))
- local = NULL;
+ local = nullify_dash(argv[2]);
+ else if (path) {
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return log_error_errno(r, "Cannot extract container name from filename: %m");
+ local = fn;
+ }
if (!local) {
log_error("Need either path or local name.");
return -EINVAL;
@@ -2086,26 +2070,26 @@ static int import_tar(int argc, char *argv[], void *userdata) {
static int import_raw(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- _cleanup_free_ char *ll = NULL;
- _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *ll = NULL, *fn = NULL;
const char *local = NULL, *path = NULL;
+ _cleanup_close_ int fd = -1;
sd_bus *bus = userdata;
int r;
assert(bus);
if (argc >= 2)
- path = argv[1];
- if (isempty(path) || streq(path, "-"))
- path = NULL;
+ path = nullify_dash(argv[1]);
if (argc >= 3)
- local = argv[2];
- else if (path)
- local = basename(path);
- if (isempty(local) || streq(local, "-"))
- local = NULL;
+ local = nullify_dash(argv[2]);
+ else if (path) {
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return log_error_errno(r, "Cannot extract container name from filename: %m");
+ local = fn;
+ }
if (!local) {
log_error("Need either path or local name.");
return -EINVAL;
@@ -2151,6 +2135,67 @@ static int import_raw(int argc, char *argv[], void *userdata) {
return transfer_image_common(bus, m);
}
+static int import_fs(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ const char *local = NULL, *path = NULL;
+ _cleanup_free_ char *fn = NULL;
+ _cleanup_close_ int fd = -1;
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ if (argc >= 2)
+ path = nullify_dash(argv[1]);
+
+ if (argc >= 3)
+ local = nullify_dash(argv[2]);
+ else if (path) {
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return log_error_errno(r, "Cannot extract container name from filename: %m");
+
+ local = fn;
+ }
+ if (!local) {
+ log_error("Need either path or local name.");
+ return -EINVAL;
+ }
+
+ if (!machine_name_is_valid(local)) {
+ log_error("Local name %s is not a suitable machine name.", local);
+ return -EINVAL;
+ }
+
+ if (path) {
+ fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open directory '%s': %m", path);
+ }
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.import1",
+ "/org/freedesktop/import1",
+ "org.freedesktop.import1.Manager",
+ "ImportFileSystem");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(
+ m,
+ "hsbb",
+ fd >= 0 ? fd : STDIN_FILENO,
+ local,
+ arg_force,
+ arg_read_only);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return transfer_image_common(bus, m);
+}
+
static void determine_compression_from_filename(const char *p) {
if (arg_format)
return;
@@ -2402,10 +2447,8 @@ typedef struct TransferInfo {
double progress;
} TransferInfo;
-static int compare_transfer_info(const void *a, const void *b) {
- const TransferInfo *x = a, *y = b;
-
- return strcmp(x->local, y->local);
+static int compare_transfer_info(const TransferInfo *a, const TransferInfo *b) {
+ return strcmp(a->local, b->local);
}
static int list_transfers(int argc, char *argv[], void *userdata) {
@@ -2420,7 +2463,7 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
double progress;
int r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
r = sd_bus_call_method(bus,
"org.freedesktop.import1",
@@ -2430,10 +2473,8 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
&error,
&reply,
NULL);
- if (r < 0) {
- log_error("Could not get transfers: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not get transfers: %s", bus_error_message(&error, -r));
r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
if (r < 0)
@@ -2475,7 +2516,7 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
+ typesafe_qsort(transfers, n_transfers, compare_transfer_info);
if (arg_legend && n_transfers > 0)
printf("%-*s %-*s %-*s %-*s %-*s\n",
@@ -2486,12 +2527,21 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
(int) max_remote, "REMOTE");
for (j = 0; j < n_transfers; j++)
- printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
- (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
- (int) 6, (unsigned) (transfers[j].progress * 100),
- (int) max_type, transfers[j].type,
- (int) max_local, transfers[j].local,
- (int) max_remote, transfers[j].remote);
+
+ if (transfers[j].progress < 0)
+ printf("%*" PRIu32 " %*s %-*s %-*s %-*s\n",
+ (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
+ (int) 7, "n/a",
+ (int) max_type, transfers[j].type,
+ (int) max_local, transfers[j].local,
+ (int) max_remote, transfers[j].remote);
+ else
+ printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
+ (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
+ (int) 6, (unsigned) (transfers[j].progress * 100),
+ (int) max_type, transfers[j].type,
+ (int) max_local, transfers[j].local,
+ (int) max_remote, transfers[j].remote);
if (arg_legend) {
if (n_transfers > 0)
@@ -2528,10 +2578,8 @@ static int cancel_transfer(int argc, char *argv[], void *userdata) {
&error,
NULL,
"u", id);
- if (r < 0) {
- log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not cancel transfer: %s", bus_error_message(&error, -r));
}
return 0;
@@ -2637,7 +2685,14 @@ static int clean_images(int argc, char *argv[], void *userdata) {
}
static int help(int argc, char *argv[], void *userdata) {
- (void) pager_open(arg_no_pager, false);
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ (void) pager_open(arg_pager_flags);
+
+ r = terminal_urlify_man("machinectl", "1", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Send control commands to or query the virtual machine and container\n"
@@ -2665,7 +2720,8 @@ static int help(int argc, char *argv[], void *userdata) {
" -o --output=STRING Change journal output mode (short, short-precise,\n"
" short-iso, short-iso-precise, short-full,\n"
" short-monotonic, short-unix, verbose, export,\n"
- " json, json-pretty, json-sse, cat)\n"
+ " json, json-pretty, json-sse, json-seq, cat,\n"
+ " with-unit)\n"
" --verify=MODE Verification mode for downloaded images (no,\n"
" checksum, signature)\n"
" --force Download image even if already exists\n\n"
@@ -2703,11 +2759,15 @@ static int help(int argc, char *argv[], void *userdata) {
" pull-raw URL [NAME] Download a RAW container or VM image\n"
" import-tar FILE [NAME] Import a local TAR container image\n"
" import-raw FILE [NAME] Import a local RAW container or VM image\n"
+ " import-fs DIRECTORY [NAME] Import a local directory container image\n"
" export-tar NAME [FILE] Export a TAR container image locally\n"
" export-raw NAME [FILE] Export a RAW container or VM image locally\n"
" list-transfers Show list of downloads in progress\n"
" cancel-transfer Cancel a download\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
@@ -2849,10 +2909,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'n':
- if (safe_atou(optarg, &arg_lines) < 0) {
- log_error("Failed to parse lines '%s'", optarg);
- return -EINVAL;
- }
+ if (safe_atou(optarg, &arg_lines) < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse lines '%s'", optarg);
break;
case 'o':
@@ -2862,14 +2921,16 @@ static int parse_argv(int argc, char *argv[]) {
}
arg_output = output_mode_from_string(optarg);
- if (arg_output < 0) {
- log_error("Unknown output '%s'.", optarg);
- return -EINVAL;
- }
+ if (arg_output < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown output '%s'.", optarg);
+
+ if (OUTPUT_MODE_IS_JSON(arg_output))
+ arg_legend = false;
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
@@ -2887,10 +2948,9 @@ static int parse_argv(int argc, char *argv[]) {
}
arg_signal = signal_from_string(optarg);
- if (arg_signal < 0) {
- log_error("Failed to parse signal string %s.", optarg);
- return -EINVAL;
- }
+ if (arg_signal < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse signal string %s.", optarg);
break;
case ARG_NO_ASK_PASSWORD:
@@ -2926,10 +2986,9 @@ static int parse_argv(int argc, char *argv[]) {
}
arg_verify = import_verify_from_string(optarg);
- if (arg_verify < 0) {
- log_error("Failed to parse --verify= setting: %s", optarg);
- return -EINVAL;
- }
+ if (arg_verify < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse --verify= setting: %s", optarg);
break;
case ARG_FORCE:
@@ -2937,10 +2996,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_FORMAT:
- if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
- log_error("Unknown format: %s", optarg);
- return -EINVAL;
- }
+ if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown format: %s", optarg);
arg_format = optarg;
break;
@@ -2950,10 +3008,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'E':
- if (!env_assignment_is_valid(optarg)) {
- log_error("Environment assignment invalid: %s", optarg);
- return -EINVAL;
- }
+ if (!env_assignment_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Environment assignment invalid: %s", optarg);
r = strv_extend(&arg_setenv, optarg);
if (r < 0)
@@ -2963,13 +3020,12 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_NUMBER_IPS:
if (streq(optarg, "all"))
arg_addrs = ALL_IP_ADDRESSES;
- else if (safe_atoi(optarg, &arg_addrs) < 0) {
- log_error("Invalid number of IPs");
- return -EINVAL;
- } else if (arg_addrs < 0) {
- log_error("Number of IPs cannot be negative");
- return -EINVAL;
- }
+ else if (safe_atoi(optarg, &arg_addrs) < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid number of IPs");
+ else if (arg_addrs < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Number of IPs cannot be negative");
break;
case '?':
@@ -3028,6 +3084,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "disable", 2, VERB_ANY, 0, enable_machine },
{ "import-tar", 2, 3, 0, import_tar },
{ "import-raw", 2, 3, 0, import_raw },
+ { "import-fs", 2, 3, 0, import_fs },
{ "export-tar", 2, 3, 0, export_tar },
{ "export-raw", 2, 3, 0, export_raw },
{ "pull-tar", 2, 3, 0, pull_tar },
@@ -3042,38 +3099,30 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
return dispatch_verb(argc, argv, verbs, bus);
}
-int main(int argc, char*argv[]) {
- sd_bus *bus = NULL;
+static int run(int argc, char *argv[]) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
+
+ /* The journal merging logic potentially needs a lot of fds. */
+ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
sigbus_install();
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
- if (r < 0) {
- log_error_errno(r, "Failed to create bus connection: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
- r = machinectl_main(argc, argv, bus);
-
-finish:
- /* make sure we terminate the bus connection first, and then close the
- * pager, see issue #3543 for the details. */
- sd_bus_flush_close_unref(bus);
- pager_close();
- polkit_agent_close();
-
- strv_free(arg_property);
- strv_free(arg_setenv);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return machinectl_main(argc, argv, bus);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/machine/machined-core.c b/src/machine/machined-core.c
new file mode 100644
index 0000000000..6a404805ea
--- /dev/null
+++ b/src/machine/machined-core.c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "machined.h"
+#include "nscd-flush.h"
+#include "strv.h"
+
+static int on_nscd_cache_flush_event(sd_event_source *s, void *userdata) {
+ /* Let's ask glibc's nscd daemon to flush its caches. We request this for the three database machines may show
+ * up in: the hosts database (for resolvable machine names) and the user and group databases (for the user ns
+ * ranges). */
+
+ (void) nscd_flush_cache(STRV_MAKE("passwd", "group", "hosts"));
+ return 0;
+}
+
+int manager_enqueue_nscd_cache_flush(Manager *m) {
+ int r;
+
+ assert(m);
+
+ if (!m->nscd_cache_flush_event) {
+ r = sd_event_add_defer(m->event, &m->nscd_cache_flush_event, on_nscd_cache_flush_event, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate NSCD cache flush event: %m");
+
+ sd_event_source_set_description(m->nscd_cache_flush_event, "nscd-cache-flush");
+ }
+
+ r = sd_event_source_set_enabled(m->nscd_cache_flush_event, SD_EVENT_ONESHOT);
+ if (r < 0) {
+ m->nscd_cache_flush_event = sd_event_source_unref(m->nscd_cache_flush_event);
+ return log_error_errno(r, "Failed to enable NSCD cache flush event: %m");
+ }
+
+ return 0;
+}
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 32c0b04283..fea9cc2633 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -21,10 +21,12 @@
#include "machine-image.h"
#include "machine-pool.h"
#include "machined.h"
+#include "missing_capability.h"
#include "path-util.h"
#include "process-util.h"
#include "stdio-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "unit-name.h"
#include "user-util.h"
@@ -41,15 +43,10 @@ static int property_get_pool_usage(
_cleanup_close_ int fd = -1;
uint64_t usage = (uint64_t) -1;
- struct stat st;
assert(bus);
assert(reply);
- /* We try to read the quota info from /var/lib/machines, as
- * well as the usage of the loopback file
- * /var/lib/machines.raw, and pick the larger value. */
-
fd = open("/var/lib/machines", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (fd >= 0) {
BtrfsQuotaInfo q;
@@ -58,11 +55,6 @@ static int property_get_pool_usage(
usage = q.referenced;
}
- if (stat("/var/lib/machines.raw", &st) >= 0) {
- if (usage == (uint64_t) -1 || st.st_blocks * 512ULL > usage)
- usage = st.st_blocks * 512ULL;
- }
-
return sd_bus_message_append(reply, "t", usage);
}
@@ -77,15 +69,10 @@ static int property_get_pool_limit(
_cleanup_close_ int fd = -1;
uint64_t size = (uint64_t) -1;
- struct stat st;
assert(bus);
assert(reply);
- /* We try to read the quota limit from /var/lib/machines, as
- * well as the size of the loopback file
- * /var/lib/machines.raw, and pick the smaller value. */
-
fd = open("/var/lib/machines", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (fd >= 0) {
BtrfsQuotaInfo q;
@@ -94,11 +81,6 @@ static int property_get_pool_limit(
size = q.referenced_max;
}
- if (stat("/var/lib/machines.raw", &st) >= 0) {
- if (size == (uint64_t) -1 || (uint64_t) st.st_size < size)
- size = st.st_size;
- }
-
return sd_bus_message_append(reply, "t", size);
}
@@ -483,7 +465,7 @@ static int method_get_machine_os_release(sd_bus_message *message, void *userdata
static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
+ _cleanup_hashmap_free_ Hashmap *images = NULL;
Manager *m = userdata;
Image *image;
Iterator i;
@@ -492,7 +474,7 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er
assert(message);
assert(m);
- images = hashmap_new(&string_hash_ops);
+ images = hashmap_new(&image_hash_ops);
if (!images)
return -ENOMEM;
@@ -633,7 +615,7 @@ static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
if (lseek(operation->extra_fd, 0, SEEK_SET) == (off_t) -1)
return -errno;
- f = fdopen(operation->extra_fd, "re");
+ f = fdopen(operation->extra_fd, "r");
if (!f)
return -errno;
@@ -655,8 +637,8 @@ static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
if (success) /* The resulting temporary file could not be updated, ignore it. */
return ret;
- r = read_nul_string(f, &name);
- if (r < 0 || isempty(name)) /* Same here... */
+ r = read_nul_string(f, LONG_LINE_MAX, &name);
+ if (r <= 0) /* Same here... */
return ret;
return sd_bus_error_set_errnof(error, ret, "Failed to remove image %s: %m", name);
@@ -678,10 +660,10 @@ static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
_cleanup_free_ char *name = NULL;
uint64_t size;
- r = read_nul_string(f, &name);
+ r = read_nul_string(f, LONG_LINE_MAX, &name);
if (r < 0)
return r;
- if (isempty(name)) /* reached the end */
+ if (r == 0) /* reached the end */
break;
errno = 0;
@@ -760,7 +742,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
if (r == 0) {
- _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
+ _cleanup_hashmap_free_ Hashmap *images = NULL;
bool success = true;
Image *image;
Iterator i;
@@ -768,7 +750,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
- images = hashmap_new(&string_hash_ops);
+ images = hashmap_new(&image_hash_ops);
if (!images) {
r = -ENOMEM;
goto child_fail;
@@ -877,19 +859,10 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
return 1; /* Will call us back */
/* Set up the machine directory if necessary */
- r = setup_machine_directory(limit, error);
+ r = setup_machine_directory(error);
if (r < 0)
return r;
- /* Resize the backing loopback device, if there is one, except if we asked to drop any limit */
- if (limit != (uint64_t) -1) {
- r = btrfs_resize_loopback("/var/lib/machines", limit, false);
- if (r == -ENOTTY)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
- if (r < 0 && r != -ENODEV) /* ignore ENODEV, as that's what is returned if the file system is not on loopback */
- return sd_bus_error_set_errnof(error, r, "Failed to adjust loopback limit: %m");
- }
-
(void) btrfs_qgroup_set_limit("/var/lib/machines", 0, limit);
r = btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, limit);
@@ -1363,18 +1336,15 @@ int manager_start_scope(
return r;
}
- r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "(sv)", "Delegate", "b", 1);
+ r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)(sv)",
+ "PIDs", "au", 1, pid,
+ "Delegate", "b", 1,
+ "CollectMode", "s", "inactive-or-failed",
+ "AddRef", "b", 1,
+ "TasksMax", "t", UINT64_C(16384));
if (r < 0)
return r;
- r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", UINT64_C(16384));
- if (r < 0)
- return bus_log_create_error(r);
-
if (more_properties) {
r = sd_bus_message_copy(m, more_properties, true);
if (r < 0)
@@ -1411,6 +1381,26 @@ int manager_start_scope(
return 1;
}
+int manager_unref_unit(
+ Manager *m,
+ const char *unit,
+ sd_bus_error *error) {
+
+ assert(m);
+ assert(unit);
+
+ return sd_bus_call_method(
+ m->bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "UnrefUnit",
+ error,
+ NULL,
+ "s",
+ unit);
+}
+
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
diff --git a/src/machine/machined.c b/src/machine/machined.c
index a6f0d847d5..9f23e369a4 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -17,6 +17,7 @@
#include "label.h"
#include "machine-image.h"
#include "machined.h"
+#include "main-func.h"
#include "process-util.h"
#include "signal-util.h"
#include "special.h"
@@ -24,6 +25,8 @@
static Manager* manager_unref(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(machine_hash_ops, char, string_hash_func, string_compare_func, Machine, machine_free);
+
static int manager_new(Manager **ret) {
_cleanup_(manager_unrefp) Manager *m = NULL;
int r;
@@ -34,7 +37,7 @@ static int manager_new(Manager **ret) {
if (!m)
return -ENOMEM;
- m->machines = hashmap_new(&string_hash_ops);
+ m->machines = hashmap_new(&machine_hash_ops);
m->machine_units = hashmap_new(&string_hash_ops);
m->machine_leaders = hashmap_new(NULL);
@@ -60,8 +63,6 @@ static int manager_new(Manager **ret) {
}
static Manager* manager_unref(Manager *m) {
- Machine *machine;
-
if (!m)
return NULL;
@@ -70,16 +71,13 @@ static Manager* manager_unref(Manager *m) {
assert(m->n_operations == 0);
- while ((machine = hashmap_first(m->machines)))
- machine_free(machine);
-
- hashmap_free(m->machines);
+ hashmap_free(m->machines); /* This will free all machines, so that the machine_units/machine_leaders is empty */
hashmap_free(m->machine_units);
hashmap_free(m->machine_leaders);
-
- hashmap_free_with_destructor(m->image_cache, image_unref);
+ hashmap_free(m->image_cache);
sd_event_source_unref(m->image_cache_defer_event);
+ sd_event_source_unref(m->nscd_cache_flush_event);
bus_verify_polkit_async_registry_free(m->polkit_registry);
@@ -345,21 +343,18 @@ static int manager_run(Manager *m) {
check_idle, m);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_(manager_unrefp) Manager *m = NULL;
int r;
- log_set_target(LOG_TARGET_AUTO);
log_set_facility(LOG_AUTH);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
if (argc != 1) {
log_error("This program takes no arguments.");
- r = -EINVAL;
- goto finish;
+ return -EINVAL;
}
/* Always create the directories people can create inotify watches in. Note that some applications might check
@@ -370,19 +365,14 @@ int main(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, -1) >= 0);
r = manager_new(&m);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate manager object: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate manager object: %m");
r = manager_startup(m);
- if (r < 0) {
- log_error_errno(r, "Failed to fully start up daemon: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to fully start up daemon: %m");
log_debug("systemd-machined running as pid "PID_FMT, getpid_cached());
-
(void) sd_notify(false,
"READY=1\n"
"STATUS=Processing requests...");
@@ -390,11 +380,11 @@ int main(int argc, char *argv[]) {
r = manager_run(m);
log_debug("systemd-machined stopped as pid "PID_FMT, getpid_cached());
-
(void) sd_notify(false,
"STOPPING=1\n"
"STATUS=Shutting down...");
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/machine/machined.h b/src/machine/machined.h
index 3197c1aade..2298a652c4 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -35,6 +35,8 @@ struct Manager {
LIST_HEAD(Operation, operations);
unsigned n_operations;
+
+ sd_event_source *nscd_cache_flush_event;
};
int manager_add_machine(Manager *m, const char *name, Machine **_machine);
@@ -50,5 +52,8 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, sd_bus_message *more_properties, sd_bus_error *error, char **job);
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error);
+int manager_unref_unit(Manager *m, const char *unit, sd_bus_error *error);
int manager_unit_is_active(Manager *manager, const char *unit);
int manager_job_is_active(Manager *manager, const char *path);
+
+int manager_enqueue_nscd_cache_flush(Manager *m);
diff --git a/src/machine/meson.build b/src/machine/meson.build
index d515877138..bc670714b4 100644
--- a/src/machine/meson.build
+++ b/src/machine/meson.build
@@ -9,6 +9,7 @@ libmachine_core_sources = files('''
machine.c
machine.h
machined-dbus.c
+ machined-core.c
machine-dbus.c
machine-dbus.h
image-dbus.c
diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c
index c49a6b7a76..36bda3433f 100644
--- a/src/modules-load/modules-load.c
+++ b/src/modules-load/modules-load.c
@@ -12,16 +12,19 @@
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
+#include "main-func.h"
#include "module-util.h"
+#include "pretty-print.h"
#include "proc-cmdline.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
static char **arg_proc_cmdline_modules = NULL;
-
static const char conf_file_dirs[] = CONF_PATHS_NULSTR("modules-load.d");
+STATIC_DESTRUCTOR_REGISTER(arg_proc_cmdline_modules, strv_freep);
+
static void systemd_kmod_log(void *data, int priority, const char *file, int line,
const char *fn, const char *format, va_list args) {
@@ -59,65 +62,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
return 0;
}
-static int load_module(struct kmod_ctx *ctx, const char *m) {
- const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
- struct kmod_list *itr;
- _cleanup_(kmod_module_unref_listp) struct kmod_list *modlist = NULL;
- int r = 0;
-
- log_debug("load: %s", m);
-
- r = kmod_module_new_from_lookup(ctx, m, &modlist);
- if (r < 0)
- return log_error_errno(r, "Failed to lookup alias '%s': %m", m);
-
- if (!modlist) {
- log_error("Failed to find module '%s'", m);
- return -ENOENT;
- }
-
- kmod_list_foreach(itr, modlist) {
- _cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
- int state, err;
-
- mod = kmod_module_get_module(itr);
- state = kmod_module_get_initstate(mod);
-
- switch (state) {
- case KMOD_MODULE_BUILTIN:
- log_info("Module '%s' is builtin", kmod_module_get_name(mod));
- break;
-
- case KMOD_MODULE_LIVE:
- log_debug("Module '%s' is already loaded", kmod_module_get_name(mod));
- break;
-
- default:
- err = kmod_module_probe_insert_module(mod, probe_flags,
- NULL, NULL, NULL, NULL);
-
- if (err == 0)
- log_info("Inserted module '%s'", kmod_module_get_name(mod));
- else if (err == KMOD_PROBE_APPLY_BLACKLIST)
- log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
- else {
- assert(err < 0);
-
- log_full_errno(err == ENODEV ? LOG_NOTICE :
- err == ENOENT ? LOG_WARNING :
- LOG_ERR,
- err,
- "Failed to insert '%s': %m",
- kmod_module_get_name(mod));
- if (!IN_SET(err, ENODEV, ENOENT))
- r = err;
- }
- }
- }
-
- return r;
-}
-
static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) {
_cleanup_fclose_ FILE *f = NULL;
int r;
@@ -130,41 +74,53 @@ static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent
if (ignore_enoent && r == -ENOENT)
return 0;
- return log_error_errno(r, "Failed to open %s, ignoring: %m", path);
+ return log_error_errno(r, "Failed to open %s: %m", path);
}
log_debug("apply: %s", path);
for (;;) {
- char line[LINE_MAX], *l;
+ _cleanup_free_ char *line = NULL;
+ char *l;
int k;
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- break;
-
- return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
- }
+ k = read_line(f, LONG_LINE_MAX, &line);
+ if (k < 0)
+ return log_error_errno(k, "Failed to read file '%s': %m", path);
+ if (k == 0)
+ break;
l = strstrip(line);
- if (!*l)
+ if (isempty(l))
continue;
- if (strchr(COMMENTS "\n", *l))
+ if (strchr(COMMENTS, *l))
continue;
- k = load_module(ctx, l);
- if (k < 0 && r == 0)
+ k = module_load_and_warn(ctx, l, true);
+ if (k < 0 && r >= 0)
r = k;
}
return r;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-modules-load.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
"Loads statically configured kernel modules.\n\n"
" -h --help Show this help\n"
- " --version Show package version\n",
- program_invocation_short_name);
+ " --version Show package version\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -189,8 +145,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -205,17 +160,15 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char *argv[]) {
- int r, k;
+static int run(int argc, char *argv[]) {
_cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
+ int r, k;
r = parse_argv(argc, argv);
if (r <= 0)
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
@@ -226,7 +179,7 @@ int main(int argc, char *argv[]) {
ctx = kmod_new(NULL, NULL);
if (!ctx) {
log_error("Failed to allocate memory for kmod.");
- goto finish;
+ return -ENOMEM;
}
kmod_load_resources(ctx);
@@ -248,7 +201,7 @@ int main(int argc, char *argv[]) {
char **fn, **i;
STRV_FOREACH(i, arg_proc_cmdline_modules) {
- k = load_module(ctx, *i);
+ k = module_load_and_warn(ctx, *i, true);
if (k < 0 && r == 0)
r = k;
}
@@ -258,7 +211,7 @@ int main(int argc, char *argv[]) {
log_error_errno(k, "Failed to enumerate modules-load.d files: %m");
if (r == 0)
r = k;
- goto finish;
+ return r;
}
STRV_FOREACH(fn, files) {
@@ -268,8 +221,7 @@ int main(int argc, char *argv[]) {
}
}
-finish:
- strv_free(arg_proc_cmdline_modules);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c
index 64c1efdb1c..bbbc91c38e 100644
--- a/src/mount/mount-tool.c
+++ b/src/mount/mount-tool.c
@@ -2,26 +2,29 @@
#include <getopt.h>
-#include "libudev.h"
#include "sd-bus.h"
+#include "sd-device.h"
#include "bus-error.h"
#include "bus-unit-util.h"
#include "bus-util.h"
+#include "device-util.h"
#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "fstab-util.h"
+#include "main-func.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "spawn-polkit-agent.h"
#include "stat-util.h"
#include "strv.h"
-#include "udev-util.h"
#include "unit-def.h"
#include "unit-name.h"
#include "user-util.h"
@@ -36,7 +39,7 @@ enum {
} arg_action = ACTION_DEFAULT;
static bool arg_no_block = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static bool arg_ask_password = true;
static bool arg_quiet = false;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
@@ -58,7 +61,22 @@ static gid_t arg_gid = GID_INVALID;
static bool arg_fsck = true;
static bool arg_aggressive_gc = false;
-static void help(void) {
+STATIC_DESTRUCTOR_REGISTER(arg_mount_what, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_mount_where, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_mount_type, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_mount_options, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_automount_property, strv_freep);
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-mount", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("systemd-mount [OPTIONS...] WHAT [WHERE]\n"
"systemd-mount [OPTIONS...] --list\n"
"%s [OPTIONS...] %sWHAT|WHERE...\n\n"
@@ -86,9 +104,14 @@ static void help(void) {
" --bind-device Bind automount unit to device\n"
" --list List mountable block devices\n"
" -u --umount Unmount mount points\n"
- " -G --collect Unload unit after it stopped, even when failed\n",
- program_invocation_short_name,
- streq(program_invocation_short_name, "systemd-umount") ? "" : "--umount ");
+ " -G --collect Unload unit after it stopped, even when failed\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , streq(program_invocation_short_name, "systemd-umount") ? "" : "--umount "
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -155,8 +178,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -166,7 +188,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_ASK_PASSWORD:
@@ -212,7 +234,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_OWNER: {
const char *user = optarg;
- r = get_user_creds(&user, &arg_uid, &arg_gid, NULL, NULL);
+ r = get_user_creds(&user, &arg_uid, &arg_gid, NULL, NULL, 0);
if (r < 0)
return log_error_errno(r,
r == -EBADMSG ? "UID or GID of user %s are invalid."
@@ -288,46 +310,39 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
- log_error("Execution in user context is not supported on non-local systems.");
- return -EINVAL;
- }
+ if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Execution in user context is not supported on non-local systems.");
if (arg_action == ACTION_LIST) {
- if (optind < argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
+ if (optind < argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many arguments.");
- if (arg_transport != BUS_TRANSPORT_LOCAL) {
- log_error("Listing devices only supported locally.");
- return -EOPNOTSUPP;
- }
+ if (arg_transport != BUS_TRANSPORT_LOCAL)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Listing devices only supported locally.");
} else if (arg_action == ACTION_UMOUNT) {
- if (optind >= argc) {
- log_error("At least one argument required.");
- return -EINVAL;
- }
+ if (optind >= argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "At least one argument required.");
if (arg_transport != BUS_TRANSPORT_LOCAL) {
int i;
for (i = optind; i < argc; i++)
- if (!path_is_absolute(argv[i]) ) {
- log_error("Only absolute path is supported: %s", argv[i]);
- return -EINVAL;
- }
+ if (!path_is_absolute(argv[i]) )
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Only absolute path is supported: %s", argv[i]);
}
} else {
- if (optind >= argc) {
- log_error("At least one argument required.");
- return -EINVAL;
- }
+ if (optind >= argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "At least one argument required.");
- if (argc > optind+2) {
- log_error("At most two arguments required.");
- return -EINVAL;
- }
+ if (argc > optind+2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "At most two arguments required.");
if (arg_mount_type && (fstype_is_api_vfs(arg_mount_type) || fstype_is_network(arg_mount_type))) {
arg_mount_what = strdup(argv[optind]);
@@ -351,10 +366,9 @@ static int parse_argv(int argc, char *argv[]) {
path_simplify(arg_mount_what, false);
- if (!path_is_absolute(arg_mount_what)) {
- log_error("Only absolute path is supported: %s", arg_mount_what);
- return -EINVAL;
- }
+ if (!path_is_absolute(arg_mount_what))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Only absolute path is supported: %s", arg_mount_what);
}
if (argc > optind+1) {
@@ -369,18 +383,16 @@ static int parse_argv(int argc, char *argv[]) {
path_simplify(arg_mount_where, false);
- if (!path_is_absolute(arg_mount_where)) {
- log_error("Only absolute path is supported: %s", arg_mount_where);
- return -EINVAL;
- }
+ if (!path_is_absolute(arg_mount_where))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Only absolute path is supported: %s", arg_mount_where);
}
} else
arg_discover = true;
- if (arg_discover && arg_transport != BUS_TRANSPORT_LOCAL) {
- log_error("Automatic mount location discovery is only supported locally.");
- return -EOPNOTSUPP;
- }
+ if (arg_discover && arg_transport != BUS_TRANSPORT_LOCAL)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Automatic mount location discovery is only supported locally.");
}
return 1;
@@ -888,15 +900,13 @@ static int stop_mounts(
int r;
- if (path_equal(where, "/")) {
- log_error("Refusing to operate on root directory: %s", where);
- return -EINVAL;
- }
+ if (path_equal(where, "/"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to operate on root directory: %s", where);
- if (!path_is_normalized(where)) {
- log_error("Path contains non-normalized components: %s", where);
- return -EINVAL;
- }
+ if (!path_is_normalized(where))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Path contains non-normalized components: %s", where);
r = stop_mount(bus, where, ".mount");
if (r < 0)
@@ -910,8 +920,7 @@ static int stop_mounts(
}
static int umount_by_device(sd_bus *bus, const char *what) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_strv_free_ char **list = NULL;
struct stat st;
const char *v;
@@ -928,22 +937,20 @@ static int umount_by_device(sd_bus *bus, const char *what) {
return -ENOTBLK;
}
- udev = udev_new();
- if (!udev)
- return log_oom();
+ r = sd_device_new_from_devnum(&d, 'b', st.st_rdev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get device from device number: %m");
- d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
- if (!d)
- return log_oom();
+ r = sd_device_get_property_value(d, "ID_FS_USAGE", &v);
+ if (r < 0)
+ return log_device_error_errno(d, r, "Failed to get device property: %m");
- v = udev_device_get_property_value(d, "ID_FS_USAGE");
- if (!streq_ptr(v, "filesystem")) {
- log_error("%s does not contain a known file system.", what);
+ if (!streq(v, "filesystem")) {
+ log_device_error(d, "%s does not contain a known file system.", what);
return -EINVAL;
}
- v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE");
- if (!isempty(v))
+ if (sd_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE", &v) >= 0)
r2 = stop_mounts(bus, v);
r = find_mount_points(what, &list);
@@ -1031,7 +1038,7 @@ static int action_umount(
return r2;
}
-static int acquire_mount_type(struct udev_device *d) {
+static int acquire_mount_type(sd_device *d) {
const char *v;
assert(d);
@@ -1039,8 +1046,7 @@ static int acquire_mount_type(struct udev_device *d) {
if (arg_mount_type)
return 0;
- v = udev_device_get_property_value(d, "ID_FS_TYPE");
- if (isempty(v))
+ if (sd_device_get_property_value(d, "ID_FS_TYPE", &v) < 0)
return 0;
arg_mount_type = strdup(v);
@@ -1051,14 +1057,15 @@ static int acquire_mount_type(struct udev_device *d) {
return 1;
}
-static int acquire_mount_options(struct udev_device *d) {
+static int acquire_mount_options(sd_device *d) {
const char *v;
+ assert(d);
+
if (arg_mount_options)
return 0;
- v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_OPTIONS");
- if (isempty(v))
+ if (sd_device_get_property_value(d, "SYSTEMD_MOUNT_OPTIONS", &v) < 0)
return 0;
arg_mount_options = strdup(v);
@@ -1069,38 +1076,41 @@ static int acquire_mount_options(struct udev_device *d) {
return 1;
}
-static const char *get_model(struct udev_device *d) {
+static const char *get_model(sd_device *d) {
const char *model;
assert(d);
- model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
- if (model)
+ if (sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model) >= 0)
+ return model;
+
+ if (sd_device_get_property_value(d, "ID_MODEL", &model) >= 0)
return model;
- return udev_device_get_property_value(d, "ID_MODEL");
+ return NULL;
}
-static const char* get_label(struct udev_device *d) {
+static const char* get_label(sd_device *d) {
const char *label;
assert(d);
- label = udev_device_get_property_value(d, "ID_FS_LABEL");
- if (label)
+ if (sd_device_get_property_value(d, "ID_FS_LABEL", &label) >= 0)
return label;
- return udev_device_get_property_value(d, "ID_PART_ENTRY_NAME");
+ if (sd_device_get_property_value(d, "ID_PART_ENTRY_NAME", &label) >= 0)
+ return label;
+
+ return NULL;
}
-static int acquire_mount_where(struct udev_device *d) {
+static int acquire_mount_where(sd_device *d) {
const char *v;
if (arg_mount_where)
return 0;
- v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE");
- if (isempty(v)) {
+ if (sd_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE", &v) < 0) {
_cleanup_free_ char *escaped = NULL;
const char *name;
@@ -1110,8 +1120,7 @@ static int acquire_mount_where(struct udev_device *d) {
if (!name) {
const char *dn;
- dn = udev_device_get_devnode(d);
- if (!dn)
+ if (sd_device_get_devname(d, &dn) < 0)
return 0;
name = basename(dn);
@@ -1144,13 +1153,14 @@ static int acquire_mount_where_for_loop_dev(const char *loop_dev) {
r = find_mount_points(loop_dev, &list);
if (r < 0)
return r;
- else if (r == 0) {
- log_error("Can't find mount point of %s. It is expected that %s is already mounted on a place.", loop_dev, loop_dev);
- return -EINVAL;
- } else if (r >= 2) {
- log_error("%s is mounted on %d places. It is expected that %s is mounted on a place.", loop_dev, r, loop_dev);
- return -EINVAL;
- }
+ else if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Can't find mount point of %s. It is expected that %s is already mounted on a place.",
+ loop_dev, loop_dev);
+ else if (r >= 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s is mounted on %d places. It is expected that %s is mounted on a place.",
+ loop_dev, r, loop_dev);
arg_mount_where = strdup(list[0]);
if (!arg_mount_where)
@@ -1160,7 +1170,7 @@ static int acquire_mount_where_for_loop_dev(const char *loop_dev) {
return 1;
}
-static int acquire_description(struct udev_device *d) {
+static int acquire_description(sd_device *d) {
const char *model, *label;
if (arg_description)
@@ -1170,7 +1180,7 @@ static int acquire_description(struct udev_device *d) {
label = get_label(d);
if (!label)
- label = udev_device_get_property_value(d, "ID_PART_ENTRY_NUMBER");
+ (void) sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &label);
if (model && label)
arg_description = strjoin(model, " ", label);
@@ -1188,7 +1198,7 @@ static int acquire_description(struct udev_device *d) {
return 1;
}
-static int acquire_removable(struct udev_device *d) {
+static int acquire_removable(sd_device *d) {
const char *v;
/* Shortcut this if there's no reason to check it */
@@ -1196,15 +1206,13 @@ static int acquire_removable(struct udev_device *d) {
return 0;
for (;;) {
- v = udev_device_get_sysattr_value(d, "removable");
- if (v)
+ if (sd_device_get_sysattr_value(d, "removable", &v) > 0)
break;
- d = udev_device_get_parent(d);
- if (!d)
+ if (sd_device_get_parent(d, &d) < 0)
return 0;
- if (!streq_ptr(udev_device_get_subsystem(d), "block"))
+ if (sd_device_get_subsystem(d, &v) < 0 || !streq(v, "block"))
return 0;
}
@@ -1232,8 +1240,7 @@ static int acquire_removable(struct udev_device *d) {
}
static int discover_loop_backing_file(void) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_free_ char *loop_dev = NULL;
struct stat st;
const char *v;
@@ -1273,17 +1280,12 @@ static int discover_loop_backing_file(void) {
return -EINVAL;
}
- udev = udev_new();
- if (!udev)
- return log_oom();
-
- d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
- if (!d)
- return log_oom();
+ r = sd_device_new_from_devnum(&d, 'b', st.st_rdev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get device from device number: %m");
- v = udev_device_get_property_value(d, "ID_FS_USAGE");
- if (!streq_ptr(v, "filesystem")) {
- log_error("%s does not contain a known file system.", arg_mount_what);
+ if (sd_device_get_property_value(d, "ID_FS_USAGE", &v) < 0 || !streq(v, "filesystem")) {
+ log_device_error(d, "%s does not contain a known file system.", arg_mount_what);
return -EINVAL;
}
@@ -1307,8 +1309,7 @@ static int discover_loop_backing_file(void) {
}
static int discover_device(void) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
struct stat st;
const char *v;
int r;
@@ -1324,16 +1325,11 @@ static int discover_device(void) {
return -EINVAL;
}
- udev = udev_new();
- if (!udev)
- return log_oom();
-
- d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
- if (!d)
- return log_oom();
+ r = sd_device_new_from_devnum(&d, 'b', st.st_rdev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get device from device number: %m");
- v = udev_device_get_property_value(d, "ID_FS_USAGE");
- if (!streq_ptr(v, "filesystem")) {
+ if (sd_device_get_property_value(d, "ID_FS_USAGE", &v) < 0 || !streq(v, "filesystem")) {
log_error("%s does not contain a known file system.", arg_mount_what);
return -EINVAL;
}
@@ -1376,17 +1372,15 @@ struct item {
char* columns[_COLUMN_MAX];
};
-static int compare_item(const void *a, const void *b) {
- const struct item *x = a, *y = b;
-
- if (x->columns[COLUMN_NODE] == y->columns[COLUMN_NODE])
+static int compare_item(const struct item *a, const struct item *b) {
+ if (a->columns[COLUMN_NODE] == b->columns[COLUMN_NODE])
return 0;
- if (!x->columns[COLUMN_NODE])
+ if (!a->columns[COLUMN_NODE])
return 1;
- if (!y->columns[COLUMN_NODE])
+ if (!b->columns[COLUMN_NODE])
return -1;
- return path_compare(x->columns[COLUMN_NODE], y->columns[COLUMN_NODE]);
+ return path_compare(a->columns[COLUMN_NODE], b->columns[COLUMN_NODE]);
}
static int list_devices(void) {
@@ -1401,49 +1395,32 @@ static int list_devices(void) {
[COLUMN_UUID] = "UUID"
};
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
size_t n_allocated = 0, n = 0, i;
size_t column_width[_COLUMN_MAX];
struct item *items = NULL;
+ sd_device *d;
unsigned c;
int r;
for (c = 0; c < _COLUMN_MAX; c++)
column_width[c] = strlen(titles[c]);
- udev = udev_new();
- if (!udev)
- return log_oom();
-
- e = udev_enumerate_new(udev);
- if (!e)
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
return log_oom();
- r = udev_enumerate_add_match_subsystem(e, "block");
+ r = sd_device_enumerator_add_match_subsystem(e, "block", true);
if (r < 0)
return log_error_errno(r, "Failed to add block match: %m");
- r = udev_enumerate_add_match_property(e, "ID_FS_USAGE", "filesystem");
+ r = sd_device_enumerator_add_match_property(e, "ID_FS_USAGE", "filesystem");
if (r < 0)
return log_error_errno(r, "Failed to add property match: %m");
- r = udev_enumerate_scan_devices(e);
- if (r < 0)
- return log_error_errno(r, "Failed to scan devices: %m");
-
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *d;
+ FOREACH_DEVICE(e, d) {
struct item *j;
- d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!d) {
- r = log_oom();
- goto finish;
- }
-
if (!GREEDY_REALLOC0(items, n_allocated, n+1)) {
r = log_oom();
goto finish;
@@ -1458,11 +1435,11 @@ static int list_devices(void) {
switch (c) {
case COLUMN_NODE:
- x = udev_device_get_devnode(d);
+ (void) sd_device_get_devname(d, &x);
break;
case COLUMN_PATH:
- x = udev_device_get_property_value(d, "ID_PATH");
+ (void) sd_device_get_property_value(d, "ID_PATH", &x);
break;
case COLUMN_MODEL:
@@ -1470,11 +1447,11 @@ static int list_devices(void) {
break;
case COLUMN_WWN:
- x = udev_device_get_property_value(d, "ID_WWN");
+ (void) sd_device_get_property_value(d, "ID_WWN", &x);
break;
case COLUMN_FSTYPE:
- x = udev_device_get_property_value(d, "ID_FS_TYPE");
+ (void) sd_device_get_property_value(d, "ID_FS_TYPE", &x);
break;
case COLUMN_LABEL:
@@ -1482,7 +1459,7 @@ static int list_devices(void) {
break;
case COLUMN_UUID:
- x = udev_device_get_property_value(d, "ID_FS_UUID");
+ (void) sd_device_get_property_value(d, "ID_FS_UUID", &x);
break;
}
@@ -1506,9 +1483,9 @@ static int list_devices(void) {
goto finish;
}
- qsort_safe(items, n, sizeof(struct item), compare_item);
+ typesafe_qsort(items, n, compare_item);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
fputs(ansi_underline(), stdout);
for (c = 0; c < _COLUMN_MAX; c++) {
@@ -1541,8 +1518,8 @@ finish:
return r;
}
-int main(int argc, char* argv[]) {
- sd_bus *bus = NULL;
+static int run(int argc, char* argv[]) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
log_parse_environment();
@@ -1550,52 +1527,42 @@ int main(int argc, char* argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
- if (arg_action == ACTION_LIST) {
- r = list_devices();
- goto finish;
- }
+ if (arg_action == ACTION_LIST)
+ return list_devices();
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
- if (r < 0) {
- log_error_errno(r, "Failed to create bus connection: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
- if (arg_action == ACTION_UMOUNT) {
- r = action_umount(bus, argc, argv);
- goto finish;
- }
+ if (arg_action == ACTION_UMOUNT)
+ return action_umount(bus, argc, argv);
if (!path_is_normalized(arg_mount_what)) {
log_error("Path contains non-normalized components: %s", arg_mount_what);
- r = -EINVAL;
- goto finish;
+ return -EINVAL;
}
if (arg_discover) {
r = discover_device();
if (r < 0)
- goto finish;
+ return r;
}
if (!arg_mount_where) {
log_error("Can't figure out where to mount %s.", arg_mount_what);
- r = -EINVAL;
- goto finish;
+ return -EINVAL;
}
if (path_equal(arg_mount_where, "/")) {
log_error("Refusing to operate on root directory.");
- r = -EINVAL;
- goto finish;
+ return -EINVAL;
}
if (!path_is_normalized(arg_mount_where)) {
log_error("Path contains non-normalized components: %s", arg_mount_where);
- r = -EINVAL;
- goto finish;
+ return -EINVAL;
}
if (streq_ptr(arg_mount_type, "auto"))
@@ -1629,8 +1596,7 @@ int main(int argc, char* argv[]) {
!fstype_can_uid_gid(arg_mount_type)) {
log_error("File system type %s is not known to support uid=/gid=, refusing.",
arg_mount_type);
- r = -EOPNOTSUPP;
- goto finish;
+ return -EOPNOTSUPP;
}
switch (arg_action) {
@@ -1648,19 +1614,7 @@ int main(int argc, char* argv[]) {
assert_not_reached("Unexpected action.");
}
-finish:
- /* make sure we terminate the bus connection first, and then close the
- * pager, see issue #3543 for the details. */
- bus = sd_bus_flush_close_unref(bus);
- pager_close();
-
- free(arg_mount_what);
- free(arg_mount_where);
- free(arg_mount_type);
- free(arg_mount_options);
- free(arg_description);
- strv_free(arg_property);
- strv_free(arg_automount_property);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/network/fuzz-netdev-parser.c b/src/network/fuzz-netdev-parser.c
new file mode 100644
index 0000000000..37f7cf8ccd
--- /dev/null
+++ b/src/network/fuzz-netdev-parser.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "fuzz.h"
+#include "networkd-manager.h"
+#include "tmpfile-util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(manager_freep) Manager *manager = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(unlink_tempfilep) char netdev_config[] = "/tmp/fuzz-networkd.XXXXXX";
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ assert_se(fmkostemp_safe(netdev_config, "r+", &f) == 0);
+ if (size != 0)
+ assert_se(fwrite(data, size, 1, f) == 1);
+
+ rewind(f);
+ assert_se(manager_new(&manager) >= 0);
+ (void) netdev_load_one(manager, netdev_config);
+ return 0;
+}
diff --git a/src/network/fuzz-network-parser.c b/src/network/fuzz-network-parser.c
new file mode 100644
index 0000000000..4b79500805
--- /dev/null
+++ b/src/network/fuzz-network-parser.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "fuzz.h"
+#include "networkd-manager.h"
+#include "tmpfile-util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(manager_freep) Manager *manager = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(unlink_tempfilep) char network_config[] = "/tmp/fuzz-networkd.XXXXXX";
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ assert_se(fmkostemp_safe(network_config, "r+", &f) == 0);
+ if (size != 0)
+ assert_se(fwrite(data, size, 1, f) == 1);
+
+ rewind(f);
+ assert_se(manager_new(&manager) >= 0);
+ (void) network_load_one(manager, network_config);
+ return 0;
+}
diff --git a/src/network/meson.build b/src/network/meson.build
index 8f5544ea04..8fe4854424 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -35,6 +35,8 @@ sources = files('''
netdev/wireguard.h
netdev/netdevsim.c
netdev/netdevsim.h
+ netdev/fou-tunnel.c
+ netdev/fou-tunnel.h
networkd-address-label.c
networkd-address-label.h
networkd-address-pool.c
@@ -62,6 +64,8 @@ sources = files('''
networkd-manager.h
networkd-ndisc.c
networkd-ndisc.h
+ networkd-neighbor.c
+ networkd-neighbor.h
networkd-radv.c
networkd-radv.h
networkd-network-bus.c
@@ -131,6 +135,27 @@ if conf.get('ENABLE_NETWORKD') == 1
install_dir : polkitpkladir)
endif
+ install_data('networkd.conf',
+ install_dir : pkgsysconfdir)
+
+ fuzzers += [
+ [['src/network/fuzz-netdev-parser.c',
+ 'src/fuzz/fuzz.h'],
+ [libnetworkd_core,
+ libudev_static,
+ libsystemd_network,
+ libshared],
+ [threads]],
+
+ [['src/network/fuzz-network-parser.c',
+ 'src/fuzz/fuzz.h'],
+ [libnetworkd_core,
+ libudev_static,
+ libsystemd_network,
+ libshared],
+ [threads]]
+ ]
+
tests += [
[['src/network/test-networkd-conf.c'],
[libnetworkd_core,
diff --git a/src/network/netdev/bond.c b/src/network/netdev/bond.c
index 5840a966ab..550a7f8914 100644
--- a/src/network/netdev/bond.c
+++ b/src/network/netdev/bond.c
@@ -7,6 +7,7 @@
#include "alloc-util.h"
#include "conf-parser.h"
+#include "ether-addr-util.h"
#include "extract-word.h"
#include "missing.h"
#include "netdev/bond.h"
@@ -284,10 +285,34 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlin
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_MIN_LINKS attribute: %m");
}
+ if (b->ad_actor_sys_prio != 0) {
+ r = sd_netlink_message_append_u16(m, IFLA_BOND_AD_ACTOR_SYS_PRIO, b->ad_actor_sys_prio);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_ACTOR_SYS_PRIO attribute: %m");
+ }
+
+ if (b->ad_user_port_key != 0) {
+ r = sd_netlink_message_append_u16(m, IFLA_BOND_AD_USER_PORT_KEY, b->ad_user_port_key);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_USER_PORT_KEY attribute: %m");
+ }
+
+ if (b->ad_actor_system) {
+ r = sd_netlink_message_append_ether_addr(m, IFLA_BOND_AD_ACTOR_SYSTEM, b->ad_actor_system);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_ACTOR_SYSTEM attribute: %m");
+ }
+
r = sd_netlink_message_append_u8(m, IFLA_BOND_ALL_SLAVES_ACTIVE, b->all_slaves_active);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ALL_SLAVES_ACTIVE attribute: %m");
+ if (b->tlb_dynamic_lb >= 0) {
+ r = sd_netlink_message_append_u8(m, IFLA_BOND_TLB_DYNAMIC_LB, b->tlb_dynamic_lb);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_TLB_DYNAMIC_LB attribute: %m");
+ }
+
if (b->arp_interval > 0) {
if (b->n_arp_ip_targets > 0) {
@@ -357,10 +382,8 @@ int config_parse_arp_ip_target_address(const char *unit,
return 0;
}
- LIST_PREPEND(arp_ip_target, b->arp_ip_targets, buffer);
+ LIST_PREPEND(arp_ip_target, b->arp_ip_targets, TAKE_PTR(buffer));
b->n_arp_ip_targets++;
-
- buffer = NULL;
}
if (b->n_arp_ip_targets > NETDEV_BOND_ARP_TARGETS_MAX)
@@ -371,6 +394,115 @@ int config_parse_arp_ip_target_address(const char *unit,
return 0;
}
+int config_parse_ad_actor_sys_prio(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Bond *b = userdata;
+ uint16_t v;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = safe_atou16(rvalue, &v);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse actor system priority '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ if (v == 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse actor system priority '%s'. Range is [1,65535], ignoring.", rvalue);
+ return 0;
+ }
+
+ b->ad_actor_sys_prio = v;
+
+ return 0;
+}
+
+int config_parse_ad_user_port_key(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Bond *b = userdata;
+ uint16_t v;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = safe_atou16(rvalue, &v);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse user port key '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ if (v > 1023) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse user port key '%s'. Range is [0,1023], ignoring.", rvalue);
+ return 0;
+ }
+
+ b->ad_user_port_key = v;
+
+ return 0;
+}
+
+int config_parse_ad_actor_system(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) {
+ Bond *b = userdata;
+ _cleanup_free_ struct ether_addr *n = NULL;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ n = new0(struct ether_addr, 1);
+ if (!n)
+ return log_oom();
+
+ r = ether_addr_from_string(rvalue, n);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Not a valid MAC address %s. Ignoring assignment: %m", rvalue);
+ return 0;
+ }
+
+ if (ether_addr_is_null(n) || (n->ether_addr_octet[0] & 0x01)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address %s, can not be null or multicast. Ignoring assignment.", rvalue);
+ return 0;
+ }
+
+ free_and_replace(b->ad_actor_system, n);
+
+ return 0;
+}
+
static void bond_done(NetDev *netdev) {
ArpIpTarget *t = NULL, *n = NULL;
Bond *b;
@@ -381,6 +513,8 @@ static void bond_done(NetDev *netdev) {
assert(b);
+ free(b->ad_actor_system);
+
LIST_FOREACH_SAFE(arp_ip_target, t, n, b->arp_ip_targets)
free(t);
@@ -406,6 +540,7 @@ static void bond_init(NetDev *netdev) {
b->primary_reselect = _NETDEV_BOND_PRIMARY_RESELECT_INVALID;
b->all_slaves_active = false;
+ b->tlb_dynamic_lb = -1;
b->resend_igmp = RESEND_IGMP_DEFAULT;
b->packets_per_slave = PACKETS_PER_SLAVE_DEFAULT;
diff --git a/src/network/netdev/bond.h b/src/network/netdev/bond.h
index fd68a18985..31b922b032 100644
--- a/src/network/netdev/bond.h
+++ b/src/network/netdev/bond.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "in-addr-util.h"
#include "list.h"
@@ -100,6 +99,8 @@ typedef struct Bond {
BondArpAllTargets arp_all_targets;
BondPrimaryReselect primary_reselect;
+ int tlb_dynamic_lb;
+
bool all_slaves_active;
unsigned resend_igmp;
@@ -107,6 +108,10 @@ typedef struct Bond {
unsigned num_grat_arp;
unsigned min_links;
+ uint16_t ad_actor_sys_prio;
+ uint16_t ad_user_port_key;
+ struct ether_addr *ad_actor_system;
+
usec_t miimon;
usec_t updelay;
usec_t downdelay;
@@ -144,12 +149,15 @@ BondArpAllTargets bond_arp_all_targets_from_string(const char *d) _pure_;
const char *bond_primary_reselect_to_string(BondPrimaryReselect d) _const_;
BondPrimaryReselect bond_primary_reselect_from_string(const char *d) _pure_;
-int config_parse_bond_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bond_xmit_hash_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bond_lacp_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);
-int config_parse_bond_ad_select(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bond_fail_over_mac(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bond_arp_validate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bond_arp_all_targets(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bond_primary_reselect(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_arp_ip_target_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_bond_mode);
+CONFIG_PARSER_PROTOTYPE(config_parse_bond_xmit_hash_policy);
+CONFIG_PARSER_PROTOTYPE(config_parse_bond_lacp_rate);
+CONFIG_PARSER_PROTOTYPE(config_parse_bond_ad_select);
+CONFIG_PARSER_PROTOTYPE(config_parse_bond_fail_over_mac);
+CONFIG_PARSER_PROTOTYPE(config_parse_bond_arp_validate);
+CONFIG_PARSER_PROTOTYPE(config_parse_bond_arp_all_targets);
+CONFIG_PARSER_PROTOTYPE(config_parse_bond_primary_reselect);
+CONFIG_PARSER_PROTOTYPE(config_parse_arp_ip_target_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_ad_actor_sys_prio);
+CONFIG_PARSER_PROTOTYPE(config_parse_ad_user_port_key);
+CONFIG_PARSER_PROTOTYPE(config_parse_ad_actor_system);
diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c
index be5914a2fa..0c804adb2e 100644
--- a/src/network/netdev/bridge.c
+++ b/src/network/netdev/bridge.c
@@ -9,8 +9,7 @@
#include "vlan-util.h"
/* callback for brige netdev's parameter set */
-static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(netdev_unrefp) NetDev *netdev = userdata;
+static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
int r;
assert(netdev);
@@ -129,7 +128,8 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
- r = sd_netlink_call_async(netdev->manager->rtnl, req, netdev_bridge_set_handler, netdev, 0, NULL);
+ r = netlink_call_async(netdev->manager->rtnl, NULL, req, netdev_bridge_set_handler,
+ netdev_destroy_callback, netdev);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
diff --git a/src/network/netdev/bridge.h b/src/network/netdev/bridge.h
index 4854e46953..3edc93a767 100644
--- a/src/network/netdev/bridge.h
+++ b/src/network/netdev/bridge.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "netdev/netdev.h"
typedef struct Bridge {
diff --git a/src/network/netdev/dummy.h b/src/network/netdev/dummy.h
index f4679a640d..93e0651f7d 100644
--- a/src/network/netdev/dummy.h
+++ b/src/network/netdev/dummy.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "netdev/netdev.h"
typedef struct Dummy {
diff --git a/src/network/netdev/fou-tunnel.c b/src/network/netdev/fou-tunnel.c
new file mode 100644
index 0000000000..65dad384e2
--- /dev/null
+++ b/src/network/netdev/fou-tunnel.c
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <linux/ip.h>
+
+#include "conf-parser.h"
+#include "missing.h"
+#include "netdev/fou-tunnel.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "parse-util.h"
+#include "sd-netlink.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "util.h"
+
+static const char* const fou_encap_type_table[_NETDEV_FOO_OVER_UDP_ENCAP_MAX] = {
+ [NETDEV_FOO_OVER_UDP_ENCAP_DIRECT] = "FooOverUDP",
+ [NETDEV_FOO_OVER_UDP_ENCAP_GUE] = "GenericUDPEncapsulation",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(fou_encap_type, FooOverUDPEncapType);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_fou_encap_type, fou_encap_type, FooOverUDPEncapType,
+ "Failed to parse Encapsulation=");
+
+static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **ret) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ FouTunnel *t;
+ int r;
+
+ assert(netdev);
+
+ t = FOU(netdev);
+
+ assert(t);
+
+ r = sd_genl_message_new(netdev->manager->genl, SD_GENL_FOU, FOU_CMD_ADD, &m);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
+
+ r = sd_netlink_message_append_u16(m, FOU_ATTR_PORT, htobe16(t->port));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PORT attribute: %m");
+
+ r = sd_netlink_message_append_u8(m, FOU_ATTR_TYPE, FOU_ENCAP_GUE);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_TYPE attribute: %m");
+
+ r = sd_netlink_message_append_u8(m, FOU_ATTR_AF, AF_INET);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_AF attribute: %m");
+
+ r = sd_netlink_message_append_u8(m, FOU_ATTR_IPPROTO, t->fou_protocol);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_IPPROTO attribute: %m");
+
+ *ret = m;
+ m = NULL;
+
+ return 0;
+}
+
+static int netdev_fou_tunnel_create(NetDev *netdev) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ uint32_t serial;
+ FouTunnel *t;
+ int r;
+
+ assert(netdev);
+
+ t = FOU(netdev);
+
+ assert(t);
+
+ r = netdev_fill_fou_tunnel_message(netdev, &m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_send(netdev->manager->genl, m, &serial);
+ if (r < 0 && r != -EADDRINUSE)
+ return log_netdev_error_errno(netdev, r, "Failed to add FooOverUDP tunnel: %m");
+
+ return 0;
+}
+
+static int netdev_fou_tunnel_verify(NetDev *netdev, const char *filename) {
+ FouTunnel *t;
+
+ assert(netdev);
+ assert(filename);
+
+ t = FOU(netdev);
+
+ assert(t);
+
+ if (t->fou_encap_type == NETDEV_FOO_OVER_UDP_ENCAP_DIRECT && t->fou_protocol <= 0) {
+ log_netdev_error(netdev, "FooOverUDP protocol not configured in %s. Rejecting configuration.", filename);
+ return -EINVAL;
+ }
+
+ if (t->fou_encap_type == NETDEV_FOO_OVER_UDP_ENCAP_GUE && t->fou_protocol > 0) {
+ log_netdev_error(netdev, "FooOverUDP GUE can't be set with protocol configured in %s. Rejecting configuration.", filename);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void fou_tunnel_init(NetDev *netdev) {
+ FouTunnel *t;
+
+ assert(netdev);
+
+ t = FOU(netdev);
+
+ assert(t);
+
+ t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT;
+}
+
+const NetDevVTable foutnl_vtable = {
+ .object_size = sizeof(FouTunnel),
+ .init = fou_tunnel_init,
+ .sections = "Match\0NetDev\0FooOverUDP\0",
+ .create = netdev_fou_tunnel_create,
+ .create_type = NETDEV_CREATE_INDEPENDENT,
+ .config_verify = netdev_fou_tunnel_verify,
+};
diff --git a/src/network/netdev/fou-tunnel.h b/src/network/netdev/fou-tunnel.h
new file mode 100644
index 0000000000..b8abed19bd
--- /dev/null
+++ b/src/network/netdev/fou-tunnel.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if HAVE_LINUX_FOU_H
+#include <linux/fou.h>
+#endif
+
+#include "in-addr-util.h"
+#include "missing_fou.h"
+#include "netdev/netdev.h"
+
+typedef enum FooOverUDPEncapType {
+ NETDEV_FOO_OVER_UDP_ENCAP_UNSPEC = FOU_ENCAP_UNSPEC,
+ NETDEV_FOO_OVER_UDP_ENCAP_DIRECT = FOU_ENCAP_DIRECT,
+ NETDEV_FOO_OVER_UDP_ENCAP_GUE = FOU_ENCAP_GUE,
+ _NETDEV_FOO_OVER_UDP_ENCAP_MAX,
+ _NETDEV_FOO_OVER_UDP_ENCAP_INVALID = -1,
+} FooOverUDPEncapType;
+
+typedef struct FouTunnel {
+ NetDev meta;
+
+ uint8_t fou_protocol;
+
+ uint16_t port;
+
+ FooOverUDPEncapType fou_encap_type;
+} FouTunnel;
+
+DEFINE_NETDEV_CAST(FOU, FouTunnel);
+extern const NetDevVTable foutnl_vtable;
+
+const char *fou_encap_type_to_string(FooOverUDPEncapType d) _const_;
+FooOverUDPEncapType fou_encap_type_from_string(const char *d) _pure_;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_fou_encap_type);
diff --git a/src/network/netdev/geneve.c b/src/network/netdev/geneve.c
index 18b6b359f3..089bbfea22 100644
--- a/src/network/netdev/geneve.c
+++ b/src/network/netdev/geneve.c
@@ -2,12 +2,14 @@
#include <net/if.h>
+#include "sd-netlink.h"
+
#include "alloc-util.h"
#include "conf-parser.h"
#include "extract-word.h"
#include "geneve.h"
+#include "netlink-util.h"
#include "parse-util.h"
-#include "sd-netlink.h"
#include "string-util.h"
#include "strv.h"
#include "missing.h"
@@ -17,10 +19,10 @@
#define DEFAULT_GENEVE_DESTINATION_PORT 6081
/* callback for geneve netdev's created without a backing Link */
-static int geneve_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(netdev_unrefp) NetDev *netdev = userdata;
+static int geneve_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
int r;
+ assert(netdev);
assert(netdev->state != _NETDEV_STATE_INVALID);
r = sd_netlink_message_get_errno(m);
@@ -135,12 +137,12 @@ static int netdev_geneve_create(NetDev *netdev) {
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
- r = sd_netlink_call_async(netdev->manager->rtnl, m, geneve_netdev_create_handler, netdev, 0, NULL);
+ r = netlink_call_async(netdev->manager->rtnl, NULL, m, geneve_netdev_create_handler,
+ netdev_destroy_callback, netdev);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
netdev_ref(netdev);
-
netdev->state = NETDEV_STATE_CREATING;
log_netdev_debug(netdev, "Creating");
diff --git a/src/network/netdev/geneve.h b/src/network/netdev/geneve.h
index dab9a4a553..c201981e02 100644
--- a/src/network/netdev/geneve.h
+++ b/src/network/netdev/geneve.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
typedef struct Geneve Geneve;
#include "in-addr-util.h"
@@ -34,35 +33,6 @@ struct Geneve {
DEFINE_NETDEV_CAST(GENEVE, Geneve);
extern const NetDevVTable geneve_vtable;
-int config_parse_geneve_vni(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata);
-
-int config_parse_geneve_address(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata);
-
-int config_parse_geneve_flow_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);
+CONFIG_PARSER_PROTOTYPE(config_parse_geneve_vni);
+CONFIG_PARSER_PROTOTYPE(config_parse_geneve_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_geneve_flow_label);
diff --git a/src/network/netdev/ipvlan.h b/src/network/netdev/ipvlan.h
index 8b698ebd1a..fb426d37e5 100644
--- a/src/network/netdev/ipvlan.h
+++ b/src/network/netdev/ipvlan.h
@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <linux/if_link.h>
-#include "missing.h"
+#include "missing_if_link.h"
#include "netdev/netdev.h"
typedef enum IPVlanMode {
@@ -39,5 +38,5 @@ IPVlanMode ipvlan_mode_from_string(const char *d) _pure_;
const char *ipvlan_flags_to_string(IPVlanFlags d) _const_;
IPVlanFlags ipvlan_flags_from_string(const char *d) _pure_;
-int config_parse_ipvlan_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_ipvlan_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_mode);
+CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_flags);
diff --git a/src/network/netdev/macvlan.h b/src/network/netdev/macvlan.h
index dcc0a3fd35..b473f1e19f 100644
--- a/src/network/netdev/macvlan.h
+++ b/src/network/netdev/macvlan.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
typedef struct MacVlan MacVlan;
#include "netdev/netdev.h"
@@ -29,4 +28,4 @@ extern const NetDevVTable macvtap_vtable;
const char *macvlan_mode_to_string(MacVlanMode d) _const_;
MacVlanMode macvlan_mode_from_string(const char *d) _pure_;
-int config_parse_macvlan_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_macvlan_mode);
diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf
index dfcac9adbf..f7ca98fa46 100644
--- a/src/network/netdev/netdev-gperf.gperf
+++ b/src/network/netdev/netdev-gperf.gperf
@@ -19,6 +19,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "netdev/netdev.h"
#include "netdev/vxcan.h"
#include "netdev/wireguard.h"
+#include "netdev/fou-tunnel.h"
#include "vlan-util.h"
%}
struct ConfigPerfItem;
@@ -65,6 +66,17 @@ Tunnel.CopyDSCP, config_parse_bool, 0,
Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent)
Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote)
+Tunnel.FooOverUDP, config_parse_bool, 0, offsetof(Tunnel, fou_tunnel)
+Tunnel.FOUDestinationPort, config_parse_ip_port, 0, offsetof(Tunnel, fou_destination_port)
+Tunnel.FOUSourcePort, config_parse_ip_port, 0, offsetof(Tunnel, encap_src_port)
+Tunnel.Encapsulation, config_parse_fou_encap_type, 0, offsetof(Tunnel, fou_encap_type)
+Tunnel.IPv6RapidDeploymentPrefix, config_parse_6rd_prefix, 0, 0
+Tunnel.ERSPANIndex, config_parse_uint32, 0, offsetof(Tunnel, erspan_index)
+Tunnel.SerializeTunneledPackets, config_parse_tristate, 0, offsetof(Tunnel, erspan_sequence)
+Tunnel.ISATAP, config_parse_tristate, 0, offsetof(Tunnel, isatap)
+FooOverUDP.Protocol, config_parse_uint8, 0, offsetof(FouTunnel, fou_protocol)
+FooOverUDP.Encapsulation, config_parse_fou_encap_type, 0, offsetof(FouTunnel, fou_encap_type)
+FooOverUDP.Port, config_parse_ip_port, 0, offsetof(FouTunnel, port)
Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer)
@@ -100,7 +112,9 @@ GENEVE.TOS, config_parse_uint8, 0,
GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl)
GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum)
GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx)
+GENEVE.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx)
GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx)
+GENEVE.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx)
GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port)
GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0
Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
@@ -126,13 +140,17 @@ Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0,
Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp)
Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave)
Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp)
-Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active)
+Bond.AllSlavesActive, config_parse_bool, 0, offsetof(Bond, all_slaves_active)
+Bond.DynamicTransmitLoadBalancing, config_parse_tristate, 0, offsetof(Bond, tlb_dynamic_lb)
Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links)
Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval)
Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval)
+Bond.AdActorSystemPriority, config_parse_ad_actor_sys_prio, 0, offsetof(Bond, ad_actor_sys_prio)
+Bond.AdUserPortKey, config_parse_ad_user_port_key, 0, offsetof(Bond, ad_user_port_key)
+Bond.AdActorSystem, config_parse_ad_actor_system, 0, offsetof(Bond, ad_actor_system)
Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time)
Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age)
Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time)
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index 82ce88402f..f0e9d00246 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -16,6 +16,7 @@
#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "strv.h"
#include "netdev/bridge.h"
#include "netdev/bond.h"
@@ -33,6 +34,7 @@
#include "netdev/vxcan.h"
#include "netdev/wireguard.h"
#include "netdev/netdevsim.h"
+#include "netdev/fou-tunnel.h"
const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_BRIDGE] = &bridge_vtable,
@@ -61,6 +63,8 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_VXCAN] = &vxcan_vtable,
[NETDEV_KIND_WIREGUARD] = &wireguard_vtable,
[NETDEV_KIND_NETDEVSIM] = &netdevsim_vtable,
+ [NETDEV_KIND_FOU] = &foutnl_vtable,
+ [NETDEV_KIND_ERSPAN] = &erspan_vtable,
};
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
@@ -90,45 +94,74 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_VXCAN] = "vxcan",
[NETDEV_KIND_WIREGUARD] = "wireguard",
[NETDEV_KIND_NETDEVSIM] = "netdevsim",
+ [NETDEV_KIND_FOU] = "fou",
+ [NETDEV_KIND_ERSPAN] = "erspan",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
-static void netdev_cancel_callbacks(NetDev *netdev) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- netdev_join_callback *callback;
+int config_parse_netdev_kind(
+ 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) {
+
+ NetDevKind k, *kind = data;
+
+ assert(rvalue);
+ assert(data);
+
+ k = netdev_kind_from_string(rvalue);
+ if (k < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse netdev kind, ignoring assignment: %s", rvalue);
+ return 0;
+ }
- if (!netdev || !netdev->manager)
- return;
+ if (*kind != _NETDEV_KIND_INVALID && *kind != k) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Specified netdev kind is different from the previous value '%s', ignoring assignment: %s",
+ netdev_kind_to_string(*kind), rvalue);
+ return 0;
+ }
- rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m);
+ *kind = k;
- while ((callback = netdev->callbacks)) {
- if (m) {
- assert(callback->link);
- assert(callback->callback);
- assert(netdev->manager);
- assert(netdev->manager->rtnl);
+ return 0;
+}
- callback->callback(netdev->manager->rtnl, m, callback->link);
- }
+static void netdev_callbacks_clear(NetDev *netdev) {
+ netdev_join_callback *callback;
+ if (!netdev)
+ return;
+
+ while ((callback = netdev->callbacks)) {
LIST_REMOVE(callbacks, netdev->callbacks, callback);
link_unref(callback->link);
free(callback);
}
}
-static void netdev_free(NetDev *netdev) {
- if (!netdev)
- return;
-
- netdev_cancel_callbacks(netdev);
-
+static void netdev_detach_from_manager(NetDev *netdev) {
if (netdev->ifname && netdev->manager)
hashmap_remove(netdev->manager->netdevs, netdev->ifname);
+ netdev->manager = NULL;
+}
+
+static NetDev *netdev_free(NetDev *netdev) {
+ assert(netdev);
+
+ netdev_callbacks_clear(netdev);
+
+ netdev_detach_from_manager(netdev);
+
free(netdev->filename);
free(netdev->description);
@@ -153,22 +186,10 @@ static void netdev_free(NetDev *netdev) {
NETDEV_VTABLE(netdev)->done)
NETDEV_VTABLE(netdev)->done(netdev);
- free(netdev);
+ return mfree(netdev);
}
-NetDev *netdev_unref(NetDev *netdev) {
- if (netdev && (-- netdev->n_ref <= 0))
- netdev_free(netdev);
-
- return NULL;
-}
-
-NetDev *netdev_ref(NetDev *netdev) {
- if (netdev)
- assert_se(++ netdev->n_ref >= 2);
-
- return netdev;
-}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(NetDev, netdev, netdev_free);
void netdev_drop(NetDev *netdev) {
if (!netdev || netdev->state == NETDEV_STATE_LINGER)
@@ -178,7 +199,9 @@ void netdev_drop(NetDev *netdev) {
log_netdev_debug(netdev, "netdev removed");
- netdev_cancel_callbacks(netdev);
+ netdev_callbacks_clear(netdev);
+
+ netdev_detach_from_manager(netdev);
netdev_unref(netdev);
@@ -206,12 +229,12 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret) {
static int netdev_enter_failed(NetDev *netdev) {
netdev->state = NETDEV_STATE_FAILED;
- netdev_cancel_callbacks(netdev);
+ netdev_callbacks_clear(netdev);
return 0;
}
-static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_handler_t callback) {
+static int netdev_enslave_ready(NetDev *netdev, Link* link, link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
@@ -238,7 +261,8 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_h
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_MASTER attribute: %m");
- r = sd_netlink_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
+ r = netlink_call_async(netdev->manager->rtnl, NULL, req, callback,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
@@ -282,10 +306,10 @@ static int netdev_enter_ready(NetDev *netdev) {
}
/* callback for netdev's created without a backing Link */
-static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(netdev_unrefp) NetDev *netdev = userdata;
+static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
int r;
+ assert(netdev);
assert(netdev->state != _NETDEV_STATE_INVALID);
r = sd_netlink_message_get_errno(m);
@@ -303,7 +327,7 @@ static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *
return 1;
}
-int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) {
+static int netdev_enslave(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) {
int r;
assert(netdev);
@@ -325,13 +349,14 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t call
/* the netdev is not yet read, save this request for when it is */
netdev_join_callback *cb;
- cb = new0(netdev_join_callback, 1);
+ cb = new(netdev_join_callback, 1);
if (!cb)
return log_oom();
- cb->callback = callback;
- cb->link = link;
- link_ref(link);
+ *cb = (netdev_join_callback) {
+ .callback = callback,
+ .link = link_ref(link),
+ };
LIST_PREPEND(callbacks, netdev->callbacks, cb);
@@ -478,8 +503,7 @@ int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
return 0;
}
-static int netdev_create(NetDev *netdev, Link *link,
- sd_netlink_message_handler_t callback) {
+static int netdev_create(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) {
int r;
assert(netdev);
@@ -546,13 +570,15 @@ static int netdev_create(NetDev *netdev, Link *link,
return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
if (link) {
- r = sd_netlink_call_async(netdev->manager->rtnl, m, callback, link, 0, NULL);
+ r = netlink_call_async(netdev->manager->rtnl, NULL, m, callback,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
link_ref(link);
} else {
- r = sd_netlink_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL);
+ r = netlink_call_async(netdev->manager->rtnl, NULL, m, netdev_create_handler,
+ netdev_destroy_callback, netdev);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
@@ -568,7 +594,7 @@ static int netdev_create(NetDev *netdev, Link *link,
}
/* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
-int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) {
+int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) {
int r;
assert(netdev);
@@ -596,7 +622,7 @@ int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callbac
return 0;
}
-static int netdev_load_one(Manager *manager, const char *filename) {
+int netdev_load_one(Manager *manager, const char *filename) {
_cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL;
_cleanup_fclose_ FILE *file = NULL;
const char *dropin_dirname;
@@ -619,13 +645,15 @@ static int netdev_load_one(Manager *manager, const char *filename) {
return 0;
}
- netdev_raw = new0(NetDev, 1);
+ netdev_raw = new(NetDev, 1);
if (!netdev_raw)
return log_oom();
- netdev_raw->n_ref = 1;
- netdev_raw->kind = _NETDEV_KIND_INVALID;
- netdev_raw->state = _NETDEV_STATE_INVALID; /* an invalid state means done() of the implementation won't be called on destruction */
+ *netdev_raw = (NetDev) {
+ .n_ref = 1,
+ .kind = _NETDEV_KIND_INVALID,
+ .state = _NETDEV_STATE_INVALID, /* an invalid state means done() of the implementation won't be called on destruction */
+ };
dropin_dirname = strjoina(basename(filename), ".d");
r = config_parse_many(filename, network_dirs, dropin_dirname,
@@ -669,10 +697,10 @@ static int netdev_load_one(Manager *manager, const char *filename) {
if (NETDEV_VTABLE(netdev)->init)
NETDEV_VTABLE(netdev)->init(netdev);
- r = config_parse(NULL, filename, file,
- NETDEV_VTABLE(netdev)->sections,
- config_item_perf_lookup, network_netdev_gperf_lookup,
- CONFIG_PARSE_WARN, netdev);
+ r = config_parse_many(filename, network_dirs, dropin_dirname,
+ NETDEV_VTABLE(netdev)->sections,
+ config_item_perf_lookup, network_netdev_gperf_lookup,
+ CONFIG_PARSE_WARN, netdev);
if (r < 0)
return r;
@@ -693,6 +721,10 @@ static int netdev_load_one(Manager *manager, const char *filename) {
return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname);
}
+ r = hashmap_ensure_allocated(&netdev->manager->netdevs, &string_hash_ops);
+ if (r < 0)
+ return r;
+
r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
if (r < 0)
return r;
@@ -758,14 +790,12 @@ static int netdev_load_one(Manager *manager, const char *filename) {
int netdev_load(Manager *manager) {
_cleanup_strv_free_ char **files = NULL;
- NetDev *netdev;
char **f;
int r;
assert(manager);
- while ((netdev = hashmap_first(manager->netdevs)))
- netdev_unref(netdev);
+ hashmap_clear_with_destructor(manager->netdevs, netdev_unref);
r = conf_files_list_strv(&files, ".netdev", NULL, 0, network_dirs);
if (r < 0)
diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h
index 8df26c0ac3..bfe1094181 100644
--- a/src/network/netdev/netdev.h
+++ b/src/network/netdev/netdev.h
@@ -1,17 +1,17 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-netlink.h"
+#include "conf-parser.h"
#include "list.h"
+#include "../networkd-link.h"
#include "time-util.h"
typedef struct netdev_join_callback netdev_join_callback;
-typedef struct Link Link;
struct netdev_join_callback {
- sd_netlink_message_handler_t callback;
+ link_netlink_message_handler_t callback;
Link *link;
LIST_FIELDS(netdev_join_callback, callbacks);
@@ -44,6 +44,8 @@ typedef enum NetDevKind {
NETDEV_KIND_VXCAN,
NETDEV_KIND_WIREGUARD,
NETDEV_KIND_NETDEVSIM,
+ NETDEV_KIND_FOU,
+ NETDEV_KIND_ERSPAN,
_NETDEV_KIND_MAX,
_NETDEV_KIND_INVALID = -1
} NetDevKind;
@@ -72,7 +74,7 @@ typedef struct Condition Condition;
typedef struct NetDev {
Manager *manager;
- int n_ref;
+ unsigned n_ref;
char *filename;
@@ -146,23 +148,23 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
#define NETDEV(n) (&(n)->meta)
int netdev_load(Manager *manager);
+int netdev_load_one(Manager *manager, const char *filename);
void netdev_drop(NetDev *netdev);
NetDev *netdev_unref(NetDev *netdev);
NetDev *netdev_ref(NetDev *netdev);
-
+DEFINE_TRIVIAL_DESTRUCTOR(netdev_destroy_callback, NetDev, netdev_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev*, netdev_unref);
int netdev_get(Manager *manager, const char *name, NetDev **ret);
int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink);
-int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback);
int netdev_get_mac(const char *ifname, struct ether_addr **ret);
-int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t cb);
+int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t cb);
const char *netdev_kind_to_string(NetDevKind d) _const_;
NetDevKind netdev_kind_from_string(const char *d) _pure_;
-int config_parse_netdev_kind(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);
+CONFIG_PARSER_PROTOTYPE(config_parse_netdev_kind);
/* gperf */
const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
diff --git a/src/network/netdev/netdevsim.h b/src/network/netdev/netdevsim.h
index 7287360f15..d3ed0c0d4f 100644
--- a/src/network/netdev/netdevsim.h
+++ b/src/network/netdev/netdevsim.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
typedef struct NetDevSim NetDevSim;
#include "netdev/netdev.h"
diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c
index c15ca74717..684edddb5f 100644
--- a/src/network/netdev/tunnel.c
+++ b/src/network/netdev/tunnel.c
@@ -6,6 +6,10 @@
#include <linux/if_tunnel.h>
#include <linux/ip6_tunnel.h>
+#if HAVE_LINUX_FOU_H
+#include <linux/fou.h>
+#endif
+
#include "sd-netlink.h"
#include "conf-parser.h"
@@ -61,6 +65,21 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlin
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m");
+ if (t->fou_tunnel) {
+
+ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_TYPE, t->fou_encap_type);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_TYPE attribute: %m");
+
+ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_SPORT, htobe16(t->encap_src_port));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_SPORT attribute: %m");
+
+ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_DPORT, htobe16(t->fou_destination_port));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_DPORT attribute: %m");
+ }
+
return r;
}
@@ -95,6 +114,29 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m");
+ if (t->sixrd_prefixlen > 0) {
+ r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_6RD_PREFIX, &t->sixrd_prefix);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_6RD_PREFIX attribute: %m");
+
+ /* u16 is deliberate here, even though we're passing a netmask that can never be >128. The kernel is
+ * expecting to receive the prefixlen as a u16.
+ */
+ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_6RD_PREFIXLEN, t->sixrd_prefixlen);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_6RD_PREFIXLEN attribute: %m");
+ }
+
+ if (t->isatap >= 0) {
+ uint16_t flags = 0;
+
+ SET_FLAG(flags, SIT_ISATAP, t->isatap);
+
+ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_FLAGS, flags);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLAGS attribute: %m");
+ }
+
return r;
}
@@ -142,6 +184,77 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink
return r;
}
+static int netdev_erspan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+ uint32_t ikey = 0;
+ uint32_t okey = 0;
+ uint16_t iflags = 0;
+ uint16_t oflags = 0;
+ Tunnel *t;
+ int r;
+
+ assert(netdev);
+
+ t = ERSPAN(netdev);
+
+ assert(t);
+ assert(IN_SET(t->family, AF_INET, AF_UNSPEC));
+ assert(m);
+
+ r = sd_netlink_message_append_u32(m, IFLA_GRE_ERSPAN_INDEX, t->erspan_index);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ERSPAN_INDEX attribute: %m");
+
+ if (t->key != 0) {
+ ikey = okey = htobe32(t->key);
+ iflags |= GRE_KEY;
+ oflags |= GRE_KEY;
+ }
+
+ if (t->ikey != 0) {
+ ikey = htobe32(t->ikey);
+ iflags |= GRE_KEY;
+ }
+
+ if (t->okey != 0) {
+ okey = htobe32(t->okey);
+ oflags |= GRE_KEY;
+ }
+
+ if (t->erspan_sequence > 0) {
+ iflags |= GRE_SEQ;
+ oflags |= GRE_SEQ;
+ } else if (t->erspan_sequence == 0) {
+ iflags &= ~GRE_SEQ;
+ oflags &= ~GRE_SEQ;
+ }
+
+ r = sd_netlink_message_append_u32(m, IFLA_GRE_IKEY, ikey);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_IKEY attribute: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_GRE_OKEY, okey);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_OKEY attribute: %m");
+
+ r = sd_netlink_message_append_u16(m, IFLA_GRE_IFLAGS, iflags);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_IFLAGS attribute: %m");
+
+ r = sd_netlink_message_append_u16(m, IFLA_GRE_OFLAGS, oflags);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_OFLAGS, attribute: %m");
+
+ r = sd_netlink_message_append_in_addr(m, IFLA_GRE_LOCAL, &t->local.in);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m");
+
+ r = sd_netlink_message_append_in_addr(m, IFLA_GRE_REMOTE, &t->remote.in);
+ if (r < 0)
+ log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m");
+
+ return r;
+}
+
static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
Tunnel *t;
int r;
@@ -384,6 +497,9 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
case NETDEV_KIND_IP6TNL:
t = IP6TNL(netdev);
break;
+ case NETDEV_KIND_ERSPAN:
+ t = ERSPAN(netdev);
+ break;
default:
assert_not_reached("Invalid tunnel kind");
}
@@ -396,17 +512,17 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
return -EINVAL;
}
- if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_GRE, NETDEV_KIND_GRETAP) &&
+ if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE, NETDEV_KIND_GRETAP, NETDEV_KIND_ERSPAN) &&
(t->family != AF_INET || in_addr_is_null(t->family, &t->local))) {
log_netdev_error(netdev,
- "vti/ipip/gre/gretap tunnel without a local IPv4 address configured in %s. Ignoring", filename);
+ "vti/ipip/sit/gre/gretap/erspan tunnel without a local IPv4 address configured in %s. Ignoring", filename);
return -EINVAL;
}
- if (IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL, NETDEV_KIND_IP6GRE) &&
+ if (IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL, NETDEV_KIND_IP6GRE, NETDEV_KIND_IP6GRETAP) &&
(t->family != AF_INET6 || in_addr_is_null(t->family, &t->local))) {
log_netdev_error(netdev,
- "vti6/ip6tnl/ip6gre tunnel without a local IPv6 address configured in %s. Ignoring", filename);
+ "vti6/ip6tnl/ip6gre/ip6gretap tunnel without a local IPv6 address configured in %s. Ignoring", filename);
return -EINVAL;
}
@@ -417,6 +533,16 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
return -EINVAL;
}
+ if (t->fou_tunnel && t->fou_destination_port <= 0) {
+ log_netdev_error(netdev, "FooOverUDP missing port configured in %s. Ignoring", filename);
+ return -EINVAL;
+ }
+
+ if (netdev->kind == NETDEV_KIND_ERSPAN && (t->erspan_index >= (1 << 20) || t->erspan_index == 0)) {
+ log_netdev_error(netdev, "Invalid erspan index %d. Ignoring", t->erspan_index);
+ return -EINVAL;
+ }
+
return 0;
}
@@ -593,6 +719,42 @@ int config_parse_encap_limit(const char* unit,
return 0;
}
+int config_parse_6rd_prefix(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Tunnel *t = userdata;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ union in_addr_union p;
+ uint8_t l;
+ int r;
+
+ r = in_addr_prefix_from_string(rvalue, AF_INET6, &p, &l);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 6rd prefix \"%s\", ignoring: %m", rvalue);
+ return 0;
+ }
+ if (l == 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "6rd prefix length of \"%s\" must be greater than zero, ignoring", rvalue);
+ return 0;
+ }
+
+ t->sixrd_prefix = p.in6;
+ t->sixrd_prefixlen = l;
+
+ return 0;
+}
+
static void ipip_init(NetDev *n) {
Tunnel *t = IPIP(n);
@@ -600,6 +762,7 @@ static void ipip_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
+ t->fou_encap_type = FOU_ENCAP_DIRECT;
}
static void sit_init(NetDev *n) {
@@ -609,6 +772,7 @@ static void sit_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
+ t->isatap = -1;
}
static void vti_init(NetDev *n) {
@@ -656,6 +820,18 @@ static void ip6gre_init(NetDev *n) {
t->ttl = DEFAULT_TNL_HOP_LIMIT;
}
+static void erspan_init(NetDev *n) {
+ Tunnel *t;
+
+ assert(n);
+
+ t = ERSPAN(n);
+
+ assert(t);
+
+ t->erspan_sequence = -1;
+}
+
static void ip6tnl_init(NetDev *n) {
Tunnel *t = IP6TNL(n);
@@ -749,3 +925,12 @@ const NetDevVTable ip6tnl_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
};
+
+const NetDevVTable erspan_vtable = {
+ .object_size = sizeof(Tunnel),
+ .init = erspan_init,
+ .sections = "Match\0NetDev\0Tunnel\0",
+ .fill_message_create = netdev_erspan_fill_message_create,
+ .create_type = NETDEV_CREATE_INDEPENDENT,
+ .config_verify = netdev_tunnel_verify,
+};
diff --git a/src/network/netdev/tunnel.h b/src/network/netdev/tunnel.h
index b54b58ddee..8f511dd1f6 100644
--- a/src/network/netdev/tunnel.h
+++ b/src/network/netdev/tunnel.h
@@ -1,10 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "in-addr-util.h"
+#include "conf-parser.h"
#include "netdev/netdev.h"
+#include "netdev/fou-tunnel.h"
typedef enum Ip6TnlMode {
NETDEV_IP6_TNL_MODE_IP6IP6,
@@ -28,6 +29,8 @@ typedef struct Tunnel {
int family;
int ipv6_flowlabel;
int allow_localremote;
+ int erspan_sequence;
+ int isatap;
unsigned ttl;
unsigned tos;
@@ -36,15 +39,24 @@ typedef struct Tunnel {
uint32_t key;
uint32_t ikey;
uint32_t okey;
+ uint32_t erspan_index;
union in_addr_union local;
union in_addr_union remote;
Ip6TnlMode ip6tnl_mode;
+ FooOverUDPEncapType fou_encap_type;
bool pmtudisc;
bool copy_dscp;
bool independent;
+ bool fou_tunnel;
+
+ uint16_t encap_src_port;
+ uint16_t fou_destination_port;
+
+ struct in6_addr sixrd_prefix;
+ uint8_t sixrd_prefixlen;
} Tunnel;
DEFINE_NETDEV_CAST(IPIP, Tunnel);
@@ -56,6 +68,7 @@ DEFINE_NETDEV_CAST(SIT, Tunnel);
DEFINE_NETDEV_CAST(VTI, Tunnel);
DEFINE_NETDEV_CAST(VTI6, Tunnel);
DEFINE_NETDEV_CAST(IP6TNL, Tunnel);
+DEFINE_NETDEV_CAST(ERSPAN, Tunnel);
extern const NetDevVTable ipip_vtable;
extern const NetDevVTable sit_vtable;
extern const NetDevVTable vti_vtable;
@@ -65,40 +78,14 @@ extern const NetDevVTable gretap_vtable;
extern const NetDevVTable ip6gre_vtable;
extern const NetDevVTable ip6gretap_vtable;
extern const NetDevVTable ip6tnl_vtable;
+extern const NetDevVTable erspan_vtable;
const char *ip6tnl_mode_to_string(Ip6TnlMode d) _const_;
Ip6TnlMode ip6tnl_mode_from_string(const char *d) _pure_;
-int config_parse_ip6tnl_mode(const char *unit, const char *filename,
- unsigned line, const char *section,
- unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data,
- void *userdata);
-
-int config_parse_tunnel_address(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata);
-
-int config_parse_ipv6_flowlabel(const char *unit, const char *filename,
- unsigned line, const char *section,
- unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data,
- void *userdata);
-
-int config_parse_encap_limit(const char *unit, const char *filename,
- unsigned line, const char *section,
- unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data,
- void *userdata);
-int config_parse_tunnel_key(const char *unit, const char *filename,
- unsigned line, const char *section,
- unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data,
- void *userdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_ip6tnl_mode);
+CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_flowlabel);
+CONFIG_PARSER_PROTOTYPE(config_parse_encap_limit);
+CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_key);
+CONFIG_PARSER_PROTOTYPE(config_parse_6rd_prefix);
diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c
index 21fb7ab067..951138d257 100644
--- a/src/network/netdev/tuntap.c
+++ b/src/network/netdev/tuntap.c
@@ -60,12 +60,11 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) {
assert(netdev);
assert(ifr);
- fd = open(TUN_DEV, O_RDWR);
+ fd = open(TUN_DEV, O_RDWR|O_CLOEXEC);
if (fd < 0)
return log_netdev_error_errno(netdev, -errno, "Failed to open tun dev: %m");
- r = ioctl(fd, TUNSETIFF, ifr);
- if (r < 0)
+ if (ioctl(fd, TUNSETIFF, ifr) < 0)
return log_netdev_error_errno(netdev, -errno, "TUNSETIFF failed on tun dev: %m");
if (netdev->kind == NETDEV_KIND_TAP)
@@ -76,34 +75,29 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) {
assert(t);
if (t->user_name) {
-
user = t->user_name;
- r = get_user_creds(&user, &uid, NULL, NULL, NULL);
+ r = get_user_creds(&user, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name);
- r = ioctl(fd, TUNSETOWNER, uid);
- if (r < 0)
+ if (ioctl(fd, TUNSETOWNER, uid) < 0)
return log_netdev_error_errno(netdev, -errno, "TUNSETOWNER failed on tun dev: %m");
}
if (t->group_name) {
-
group = t->group_name;
- r = get_group_creds(&group, &gid);
+ r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name);
- r = ioctl(fd, TUNSETGROUP, gid);
- if (r < 0)
+ if (ioctl(fd, TUNSETGROUP, gid) < 0)
return log_netdev_error_errno(netdev, -errno, "TUNSETGROUP failed on tun dev: %m");
}
- r = ioctl(fd, TUNSETPERSIST, 1);
- if (r < 0)
+ if (ioctl(fd, TUNSETPERSIST, 1) < 0)
return log_netdev_error_errno(netdev, -errno, "TUNSETPERSIST failed on tun dev: %m");
return 0;
diff --git a/src/network/netdev/tuntap.h b/src/network/netdev/tuntap.h
index 6e8c66a000..9d9f9922b5 100644
--- a/src/network/netdev/tuntap.h
+++ b/src/network/netdev/tuntap.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
typedef struct TunTap TunTap;
#include "netdev/netdev.h"
diff --git a/src/network/netdev/vcan.c b/src/network/netdev/vcan.c
index f06ad0fb42..574d1cad31 100644
--- a/src/network/netdev/vcan.c
+++ b/src/network/netdev/vcan.c
@@ -4,5 +4,6 @@
const NetDevVTable vcan_vtable = {
.object_size = sizeof(VCan),
+ .sections = "Match\0NetDev\0",
.create_type = NETDEV_CREATE_INDEPENDENT,
};
diff --git a/src/network/netdev/vcan.h b/src/network/netdev/vcan.h
index d2540645c8..6f62686d08 100644
--- a/src/network/netdev/vcan.h
+++ b/src/network/netdev/vcan.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
typedef struct VCan VCan;
#include <linux/can/netlink.h>
diff --git a/src/network/netdev/veth.h b/src/network/netdev/veth.h
index 29cddab573..0bb9947bbd 100644
--- a/src/network/netdev/veth.h
+++ b/src/network/netdev/veth.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
typedef struct Veth Veth;
#include "netdev/netdev.h"
diff --git a/src/network/netdev/vlan.h b/src/network/netdev/vlan.h
index f3629f903b..b815e03dc5 100644
--- a/src/network/netdev/vlan.h
+++ b/src/network/netdev/vlan.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
typedef struct VLan VLan;
#include "netdev/netdev.h"
diff --git a/src/network/netdev/vrf.c b/src/network/netdev/vrf.c
index f341061344..5efab537cc 100644
--- a/src/network/netdev/vrf.c
+++ b/src/network/netdev/vrf.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2016 Andreas Rammhold <andreas@rammhold.de>
-***/
#include <net/if.h>
@@ -30,7 +27,7 @@ static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink
const NetDevVTable vrf_vtable = {
.object_size = sizeof(Vrf),
- .sections = "NetDev\0VRF\0",
+ .sections = "Match\0NetDev\0VRF\0",
.fill_message_create = netdev_vrf_fill_message_create,
.create_type = NETDEV_CREATE_MASTER,
};
diff --git a/src/network/netdev/vrf.h b/src/network/netdev/vrf.h
index d1ac6f6976..05b3937856 100644
--- a/src/network/netdev/vrf.h
+++ b/src/network/netdev/vrf.h
@@ -1,10 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- Copyright © 2016 Andreas Rammhold <andreas@rammhold.de>
-***/
-
typedef struct Vrf Vrf;
#include "netdev/netdev.h"
diff --git a/src/network/netdev/vxcan.c b/src/network/netdev/vxcan.c
index f8139b52c3..e8ea70a1ed 100644
--- a/src/network/netdev/vxcan.c
+++ b/src/network/netdev/vxcan.c
@@ -1,7 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include "netdev/vxcan.h"
+#if HAVE_LINUX_CAN_VXCAN_H
+#include <linux/can/vxcan.h>
+#endif
+
#include "missing.h"
+#include "netdev/vxcan.h"
static int netdev_vxcan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
VxCan *v;
diff --git a/src/network/netdev/vxcan.h b/src/network/netdev/vxcan.h
index 84a39ee3cb..b5de19740f 100644
--- a/src/network/netdev/vxcan.h
+++ b/src/network/netdev/vxcan.h
@@ -1,13 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
typedef struct VxCan VxCan;
-#if HAVE_VXCAN_INFO_PEER
-#include <linux/can/vxcan.h>
-#endif
-
#include "netdev/netdev.h"
struct VxCan {
diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c
index e23dd13648..4cb2eca3d2 100644
--- a/src/network/netdev/vxlan.c
+++ b/src/network/netdev/vxlan.c
@@ -42,7 +42,6 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m");
-
}
if (!in_addr_is_null(v->local_family, &v->local)) {
@@ -54,20 +53,19 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LOCAL attribute: %m");
-
}
r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LINK, link->ifindex);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LINK attribute: %m");
- if (v->ttl) {
+ if (v->ttl != 0) {
r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TTL attribute: %m");
}
- if (v->tos) {
+ if (v->tos != 0) {
r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TOS, v->tos);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TOS attribute: %m");
@@ -93,13 +91,13 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_L3MISS attribute: %m");
- if (v->fdb_ageing) {
+ if (v->fdb_ageing != 0) {
r = sd_netlink_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_AGEING attribute: %m");
}
- if (v->max_fdb) {
+ if (v->max_fdb != 0) {
r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LIMIT, v->max_fdb);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LIMIT attribute: %m");
@@ -129,7 +127,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT attribute: %m");
- if (v->port_range.low || v->port_range.high) {
+ if (v->port_range.low != 0 || v->port_range.high != 0) {
struct ifla_vxlan_port_range port_range;
port_range.low = htobe16(v->port_range.low);
@@ -214,9 +212,8 @@ int config_parse_port_range(const char *unit,
const char *rvalue,
void *data,
void *userdata) {
- _cleanup_free_ char *word = NULL;
VxLan *v = userdata;
- unsigned low, high;
+ uint16_t low, high;
int r;
assert(filename);
@@ -224,30 +221,10 @@ int config_parse_port_range(const char *unit,
assert(rvalue);
assert(data);
- r = extract_first_word(&rvalue, &word, NULL, 0);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract VXLAN port range, ignoring: %s", rvalue);
- return 0;
- }
-
- if (r == 0)
- return 0;
-
- r = parse_range(word, &low, &high);
+ r = parse_ip_port_range(rvalue, &low, &high);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN port range '%s'", word);
- return 0;
- }
-
- if (low <= 0 || low > 65535 || high <= 0 || high > 65535) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse VXLAN port range '%s'. Port should be greater than 0 and less than 65535.", word);
- return 0;
- }
-
- if (high < low) {
log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse VXLAN port range '%s'. Port range %u .. %u not valid", word, low, high);
+ "Failed to parse VXLAN port range '%s'. Port should be greater than 0 and less than 65535.", rvalue);
return 0;
}
diff --git a/src/network/netdev/vxlan.h b/src/network/netdev/vxlan.h
index e5eb20f187..3b273e97bd 100644
--- a/src/network/netdev/vxlan.h
+++ b/src/network/netdev/vxlan.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
typedef struct VxLan VxLan;
#include "in-addr-util.h"
@@ -48,34 +47,6 @@ struct VxLan {
DEFINE_NETDEV_CAST(VXLAN, VxLan);
extern const NetDevVTable vxlan_vtable;
-int config_parse_vxlan_address(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata);
-int config_parse_port_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);
-
-int config_parse_flow_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);
+CONFIG_PARSER_PROTOTYPE(config_parse_vxlan_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_port_range);
+CONFIG_PARSER_PROTOTYPE(config_parse_flow_label);
diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c
index fb91997f7a..167cf65046 100644
--- a/src/network/netdev/wireguard.c
+++ b/src/network/netdev/wireguard.c
@@ -1,22 +1,24 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
/***
- Copyright © 2016-2017 Jörg Thalheim <joerg@thalheim.io>
Copyright © 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
***/
#include <sys/ioctl.h>
#include <net/if.h>
+#include "sd-resolve.h"
+
#include "alloc-util.h"
-#include "parse-util.h"
#include "fd-util.h"
-#include "strv.h"
#include "hexdecoct.h"
-#include "string-util.h"
-#include "wireguard.h"
#include "networkd-link.h"
-#include "networkd-util.h"
#include "networkd-manager.h"
+#include "networkd-util.h"
+#include "parse-util.h"
+#include "resolve-private.h"
+#include "string-util.h"
+#include "strv.h"
+#include "wireguard.h"
#include "wireguard-netlink.h"
static void resolve_endpoints(NetDev *netdev);
@@ -29,10 +31,13 @@ static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) {
if (w->last_peer_section == section && w->peers)
return w->peers;
- peer = new0(WireguardPeer, 1);
+ peer = new(WireguardPeer, 1);
if (!peer)
return NULL;
- peer->flags = WGPEER_F_REPLACE_ALLOWEDIPS;
+
+ *peer = (WireguardPeer) {
+ .flags = WGPEER_F_REPLACE_ALLOWEDIPS,
+ };
LIST_PREPEND(peers, w->peers, peer);
w->last_peer_section = section;
@@ -42,7 +47,7 @@ static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) {
static int set_wireguard_interface(NetDev *netdev) {
int r;
- unsigned int i, j;
+ unsigned i, j;
WireguardPeer *peer, *peer_start;
WireguardIPmask *mask, *mask_start = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
@@ -108,7 +113,7 @@ static int set_wireguard_interface(NetDev *netdev) {
if (r < 0)
break;
- r = sd_netlink_message_append_u32(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
+ r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
if (r < 0)
break;
@@ -196,12 +201,19 @@ static int set_wireguard_interface(NetDev *netdev) {
static WireguardEndpoint* wireguard_endpoint_free(WireguardEndpoint *e) {
if (!e)
return NULL;
- netdev_unref(e->netdev);
e->host = mfree(e->host);
e->port = mfree(e->port);
return mfree(e);
}
+static void wireguard_endpoint_destroy_callback(WireguardEndpoint *e) {
+ assert(e);
+ assert(e->netdev);
+
+ netdev_unref(e->netdev);
+ wireguard_endpoint_free(e);
+}
+
DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint*, wireguard_endpoint_free);
static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
@@ -212,8 +224,11 @@ static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
w = WIREGUARD(netdev);
assert(w);
- w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source);
+ if (!netdev->manager)
+ /* The netdev is detached. */
+ return 0;
+ assert(!w->unresolved_endpoints);
w->unresolved_endpoints = TAKE_PTR(w->failed_endpoints);
resolve_endpoints(netdev);
@@ -232,28 +247,30 @@ static int exponential_backoff_milliseconds(unsigned n_retries) {
static int wireguard_resolve_handler(sd_resolve_query *q,
int ret,
const struct addrinfo *ai,
- void *userdata) {
+ WireguardEndpoint *e) {
+ _cleanup_(netdev_unrefp) NetDev *netdev_will_unrefed = NULL;
NetDev *netdev;
Wireguard *w;
- _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *e;
int r;
- assert(userdata);
- e = userdata;
- netdev = e->netdev;
+ assert(e);
+ assert(e->netdev);
- assert(netdev);
+ netdev = e->netdev;
w = WIREGUARD(netdev);
assert(w);
- w->resolve_query = sd_resolve_query_unref(w->resolve_query);
+ if (!netdev->manager)
+ /* The netdev is detached. */
+ return 0;
if (ret != 0) {
log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", e->host, e->port, gai_strerror(ret));
LIST_PREPEND(endpoints, w->failed_endpoints, e);
- e = NULL;
+ (void) sd_resolve_query_set_destroy_callback(q, NULL); /* Avoid freeing endpoint by destroy callback. */
+ netdev_will_unrefed = netdev; /* But netdev needs to be unrefed. */
} else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
- (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
+ (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen);
else
log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e->host, e->port);
@@ -265,51 +282,69 @@ static int wireguard_resolve_handler(sd_resolve_query *q,
set_wireguard_interface(netdev);
if (w->failed_endpoints) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+
w->n_retries++;
r = sd_event_add_time(netdev->manager->event,
- &w->resolve_retry_event_source,
+ &s,
CLOCK_MONOTONIC,
now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries),
0,
on_resolve_retry,
netdev);
- if (r < 0)
+ if (r < 0) {
log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
+ return 0;
+ }
+
+ r = sd_event_source_set_destroy_callback(s, (sd_event_destroy_t) netdev_destroy_callback);
+ if (r < 0) {
+ log_netdev_warning_errno(netdev, r, "Failed to set destroy callback to event source: %m");
+ return 0;
+ }
+
+ (void) sd_event_source_set_floating(s, true);
+ netdev_ref(netdev);
}
return 0;
}
static void resolve_endpoints(NetDev *netdev) {
- int r = 0;
- Wireguard *w;
- WireguardEndpoint *endpoint;
static const struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP
};
+ WireguardEndpoint *endpoint;
+ Wireguard *w;
+ int r = 0;
assert(netdev);
w = WIREGUARD(netdev);
assert(w);
LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) {
- r = sd_resolve_getaddrinfo(netdev->manager->resolve,
- &w->resolve_query,
- endpoint->host,
- endpoint->port,
- &hints,
- wireguard_resolve_handler,
- endpoint);
+ r = resolve_getaddrinfo(netdev->manager->resolve,
+ NULL,
+ endpoint->host,
+ endpoint->port,
+ &hints,
+ wireguard_resolve_handler,
+ wireguard_endpoint_destroy_callback,
+ endpoint);
if (r == -ENOBUFS)
break;
+ if (r < 0) {
+ log_netdev_error_errno(netdev, r, "Failed to create resolver: %m");
+ continue;
+ }
- LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint);
+ /* Avoid freeing netdev. It will be unrefed by the destroy callback. */
+ netdev_ref(netdev);
- if (r < 0)
- log_netdev_error_errno(netdev, r, "Failed create resolver: %m");
+ LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint);
}
}
@@ -532,12 +567,15 @@ int config_parse_wireguard_allowed_ips(const char *unit,
return 0;
}
- ipmask = new0(WireguardIPmask, 1);
+ ipmask = new(WireguardIPmask, 1);
if (!ipmask)
return log_oom();
- ipmask->family = family;
- ipmask->ip.in6 = addr.in6;
- ipmask->cidr = prefixlen;
+
+ *ipmask = (WireguardIPmask) {
+ .family = family,
+ .ip.in6 = addr.in6,
+ .cidr = prefixlen,
+ };
LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
}
@@ -573,10 +611,6 @@ int config_parse_wireguard_endpoint(const char *unit,
if (!peer)
return log_oom();
- endpoint = new0(WireguardEndpoint, 1);
- if (!endpoint)
- return log_oom();
-
if (rvalue[0] == '[') {
begin = &rvalue[1];
end = strchr(rvalue, ']');
@@ -610,12 +644,17 @@ int config_parse_wireguard_endpoint(const char *unit,
if (!port)
return log_oom();
- endpoint->peer = TAKE_PTR(peer);
- endpoint->host = TAKE_PTR(host);
- endpoint->port = TAKE_PTR(port);
- endpoint->netdev = netdev_ref(data);
- LIST_PREPEND(endpoints, w->unresolved_endpoints, endpoint);
- endpoint = NULL;
+ endpoint = new(WireguardEndpoint, 1);
+ if (!endpoint)
+ return log_oom();
+
+ *endpoint = (WireguardEndpoint) {
+ .peer = TAKE_PTR(peer),
+ .host = TAKE_PTR(host),
+ .port = TAKE_PTR(port),
+ .netdev = data,
+ };
+ LIST_PREPEND(endpoints, w->unresolved_endpoints, TAKE_PTR(endpoint));
return 0;
}
@@ -674,11 +713,11 @@ static void wireguard_done(NetDev *netdev) {
Wireguard *w;
WireguardPeer *peer;
WireguardIPmask *mask;
+ WireguardEndpoint *e;
assert(netdev);
w = WIREGUARD(netdev);
- assert(!w->unresolved_endpoints);
- w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source);
+ assert(w);
while ((peer = w->peers)) {
LIST_REMOVE(peers, w->peers, peer);
@@ -688,6 +727,16 @@ static void wireguard_done(NetDev *netdev) {
}
free(peer);
}
+
+ while ((e = w->unresolved_endpoints)) {
+ LIST_REMOVE(endpoints, w->unresolved_endpoints, e);
+ wireguard_endpoint_free(e);
+ }
+
+ while ((e = w->failed_endpoints)) {
+ LIST_REMOVE(endpoints, w->failed_endpoints, e);
+ wireguard_endpoint_free(e);
+ }
}
const NetDevVTable wireguard_vtable = {
diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h
index 6fdbf09a7e..143e93cdc5 100644
--- a/src/network/netdev/wireguard.h
+++ b/src/network/netdev/wireguard.h
@@ -1,16 +1,11 @@
#pragma once
-/***
- Copyright © 2016 Jörg Thalheim <joerg@thalheim.io>
-***/
-
typedef struct Wireguard Wireguard;
+#include "in-addr-util.h"
#include "netdev.h"
-#include "sd-resolve.h"
-#include "wireguard-netlink.h"
#include "socket-util.h"
-#include "in-addr-util.h"
+#include "wireguard-netlink.h"
#ifndef IFNAMSIZ
#define IFNAMSIZ 16
@@ -51,33 +46,28 @@ struct Wireguard {
NetDev meta;
unsigned last_peer_section;
- char interface[IFNAMSIZ];
uint32_t flags;
- uint8_t public_key[WG_KEY_LEN];
uint8_t private_key[WG_KEY_LEN];
uint32_t fwmark;
uint16_t port;
LIST_HEAD(WireguardPeer, peers);
- size_t allocation_size;
- sd_event_source *resolve_retry_event_source;
LIST_HEAD(WireguardEndpoint, unresolved_endpoints);
LIST_HEAD(WireguardEndpoint, failed_endpoints);
unsigned n_retries;
- sd_resolve_query *resolve_query;
};
DEFINE_NETDEV_CAST(WIREGUARD, Wireguard);
extern const NetDevVTable wireguard_vtable;
-int config_parse_wireguard_allowed_ips(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_wireguard_endpoint(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_wireguard_listen_port(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_allowed_ips);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_endpoint);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_listen_port);
-int config_parse_wireguard_public_key(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_wireguard_private_key(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_wireguard_preshared_key(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_wireguard_keepalive(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_public_key);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_preshared_key);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_keepalive);
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 3ab06f28e9..b14b81cc85 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -20,9 +20,11 @@
#include "local-addresses.h"
#include "locale-util.h"
#include "macro.h"
+#include "main-func.h"
#include "netlink-util.h"
#include "pager.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "socket-util.h"
#include "sparse-endian.h"
#include "stdio-util.h"
@@ -34,21 +36,18 @@
#include "util.h"
#include "verbs.h"
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static bool arg_all = false;
static char *link_get_type_string(unsigned short iftype, sd_device *d) {
- const char *t;
+ const char *t, *devtype;
char *p;
- if (d) {
- const char *devtype = NULL;
-
- (void) sd_device_get_devtype(d, &devtype);
- if (!isempty(devtype))
- return strdup(devtype);
- }
+ if (d &&
+ sd_device_get_devtype(d, &devtype) >= 0 &&
+ !isempty(devtype))
+ return strdup(devtype);
t = arphrd_to_name(iftype);
if (!t)
@@ -104,10 +103,8 @@ typedef struct LinkInfo {
bool has_mtu:1;
} LinkInfo;
-static int link_info_compare(const void *a, const void *b) {
- const LinkInfo *x = a, *y = b;
-
- return x->ifindex - y->ifindex;
+static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
+ return CMP(a->ifindex, b->ifindex);
}
static int decode_link(sd_netlink_message *m, LinkInfo *info) {
@@ -190,7 +187,7 @@ static int acquire_link_info_strv(sd_netlink *rtnl, char **l, LinkInfo **ret) {
c++;
}
- qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
+ typesafe_qsort(links, c, link_info_compare);
*ret = TAKE_PTR(links);
@@ -230,7 +227,7 @@ static int acquire_link_info_all(sd_netlink *rtnl, LinkInfo **ret) {
c++;
}
- qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
+ typesafe_qsort(links, c, link_info_compare);
*ret = TAKE_PTR(links);
@@ -253,7 +250,7 @@ static int list_links(int argc, char *argv[], void *userdata) {
if (c < 0)
return c;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (arg_legend)
printf("%3s %-16s %-18s %-11s %-10s\n",
@@ -770,12 +767,10 @@ static int link_status_one(
(void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
(void) sd_device_get_property_value(d, "ID_PATH", &path);
- r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor);
- if (r < 0)
+ if (sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor) < 0)
(void) sd_device_get_property_value(d, "ID_VENDOR", &vendor);
- r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model);
- if (r < 0)
+ if (sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model) < 0)
(void) sd_device_get_property_value(d, "ID_MODEL", &model);
}
@@ -786,7 +781,7 @@ static int link_status_one(
(void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
(void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
- printf("%s%s%s %i: %s\n", on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, info->ifindex, info->name);
+ printf("%s%s%s %i: %s\n", on_color_operational, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), off_color_operational, info->ifindex, info->name);
printf(" Link File: %s\n"
" Network File: %s\n"
@@ -854,7 +849,7 @@ static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
printf("%s%s%s State: %s%s%s\n",
- on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational,
+ on_color_operational, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), off_color_operational,
on_color_operational, strna(operational_state), off_color_operational);
(void) dump_addresses(rtnl, " Address: ", 0);
@@ -881,7 +876,7 @@ static int link_status(int argc, char *argv[], void *userdata) {
_cleanup_free_ LinkInfo *links = NULL;
int r, c, i;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
r = sd_netlink_open(&rtnl);
if (r < 0)
@@ -977,7 +972,7 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
if (c < 0)
return c;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (arg_legend)
printf("%-16s %-17s %-16s %-11s %-17s %-16s\n",
@@ -1067,7 +1062,14 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("networkctl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"Query and control the networking subsystem.\n\n"
" -h --help Show this help\n"
@@ -1080,7 +1082,12 @@ static void help(void) {
" status [LINK...] Show link status\n"
" lldp [LINK...] Show LLDP neighbors\n"
" label Show current address label entries in the kernel\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -1110,14 +1117,13 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
@@ -1159,7 +1165,7 @@ static void warn_networkd_missing(void) {
fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n");
}
-int main(int argc, char* argv[]) {
+static int run(int argc, char* argv[]) {
int r;
log_parse_environment();
@@ -1167,14 +1173,11 @@ int main(int argc, char* argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
warn_networkd_missing();
- r = networkctl_main(argc, argv);
-
-finish:
- pager_close();
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return networkctl_main(argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/network/networkd-address-label.c b/src/network/networkd-address-label.c
index 4c55442739..94a12f8bfe 100644
--- a/src/network/networkd-address-label.c
+++ b/src/network/networkd-address-label.c
@@ -11,18 +11,6 @@
#include "parse-util.h"
#include "socket-util.h"
-int address_label_new(AddressLabel **ret) {
- _cleanup_(address_label_freep) AddressLabel *addrlabel = NULL;
-
- addrlabel = new0(AddressLabel, 1);
- if (!addrlabel)
- return -ENOMEM;
-
- *ret = TAKE_PTR(addrlabel);
-
- return 0;
-}
-
void address_label_free(AddressLabel *label) {
if (!label)
return;
@@ -50,40 +38,77 @@ static int address_label_new_static(Network *network, const char *filename, unsi
assert(ret);
assert(!!filename == (section_line > 0));
- r = network_config_section_new(filename, section_line, &n);
- if (r < 0)
- return r;
+ if (filename) {
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
- label = hashmap_get(network->address_labels_by_section, n);
- if (label) {
- *ret = TAKE_PTR(label);
+ label = hashmap_get(network->address_labels_by_section, n);
+ if (label) {
+ *ret = TAKE_PTR(label);
- return 0;
+ return 0;
+ }
}
- r = address_label_new(&label);
- if (r < 0)
- return r;
-
- label->section = TAKE_PTR(n);
+ label = new(AddressLabel, 1);
+ if (!label)
+ return -ENOMEM;
- r = hashmap_put(network->address_labels_by_section, label->section, label);
- if (r < 0)
- return r;
+ *label = (AddressLabel) {
+ .network = network,
+ };
- label->network = network;
LIST_APPEND(labels, network->address_labels, label);
network->n_address_labels++;
+ if (filename) {
+ label->section = TAKE_PTR(n);
+
+ r = hashmap_ensure_allocated(&network->address_labels_by_section, &network_config_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(network->address_labels_by_section, label->section, label);
+ if (r < 0)
+ return r;
+ }
+
*ret = TAKE_PTR(label);
return 0;
}
+static int address_label_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(rtnl);
+ assert(m);
+ assert(link);
+ assert(link->ifname);
+ assert(link->address_label_messages > 0);
+
+ link->address_label_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_warning_errno(link, r, "could not set address label: %m");
+ else if (r >= 0)
+ manager_rtnl_process_address(rtnl, m, link->manager);
+
+ if (link->address_label_messages == 0)
+ log_link_debug(link, "Addresses label set");
+
+ return 1;
+}
+
int address_label_configure(
AddressLabel *label,
Link *link,
- sd_netlink_message_handler_t callback,
+ link_netlink_message_handler_t callback,
bool update) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
@@ -112,7 +137,9 @@ int address_label_configure(
if (r < 0)
return log_error_errno(r, "Could not append IFA_ADDRESS attribute: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req,
+ callback ?: address_label_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_error_errno(r, "Could not send rtnetlink message: %m");
diff --git a/src/network/networkd-address-label.h b/src/network/networkd-address-label.h
index 77687edcba..6922cb0faf 100644
--- a/src/network/networkd-address-label.h
+++ b/src/network/networkd-address-label.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <inttypes.h>
#include <stdbool.h>
@@ -19,7 +18,6 @@ typedef struct NetworkConfigSection NetworkConfigSection;
struct AddressLabel {
Network *network;
- Link *link;
NetworkConfigSection *section;
unsigned char prefixlen;
@@ -30,12 +28,11 @@ struct AddressLabel {
LIST_FIELDS(AddressLabel, labels);
};
-int address_label_new(AddressLabel **ret);
void address_label_free(AddressLabel *label);
DEFINE_TRIVIAL_CLEANUP_FUNC(AddressLabel*, address_label_free);
-int address_label_configure(AddressLabel *address, Link *link, sd_netlink_message_handler_t callback, bool update);
+int address_label_configure(AddressLabel *address, Link *link, link_netlink_message_handler_t callback, bool update);
CONFIG_PARSER_PROTOTYPE(config_parse_address_label);
CONFIG_PARSER_PROTOTYPE(config_parse_address_label_prefix);
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c
index 2e35f77093..1650515064 100644
--- a/src/network/networkd-address-pool.c
+++ b/src/network/networkd-address-pool.c
@@ -19,14 +19,16 @@ int address_pool_new(
assert(ret);
assert(u);
- p = new0(AddressPool, 1);
+ p = new(AddressPool, 1);
if (!p)
return -ENOMEM;
- p->manager = m;
- p->family = family;
- p->prefixlen = prefixlen;
- p->in_addr = *u;
+ *p = (AddressPool) {
+ .manager = m,
+ .family = family,
+ .prefixlen = prefixlen,
+ .in_addr = *u,
+ };
LIST_PREPEND(address_pools, m->address_pools, p);
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 5bddf88e36..9f0a22b827 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -12,6 +12,7 @@
#include "set.h"
#include "socket-util.h"
#include "string-util.h"
+#include "strv.h"
#include "utf8.h"
#include "util.h"
@@ -21,14 +22,16 @@
int address_new(Address **ret) {
_cleanup_(address_freep) Address *address = NULL;
- address = new0(Address, 1);
+ address = new(Address, 1);
if (!address)
return -ENOMEM;
- address->family = AF_UNSPEC;
- address->scope = RT_SCOPE_UNIVERSE;
- address->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME;
- address->cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME;
+ *address = (Address) {
+ .family = AF_UNSPEC,
+ .scope = RT_SCOPE_UNIVERSE,
+ .cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME,
+ .cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME,
+ };
*ret = TAKE_PTR(address);
@@ -64,18 +67,22 @@ int address_new_static(Network *network, const char *filename, unsigned section_
if (r < 0)
return r;
+ address->network = network;
+ LIST_APPEND(addresses, network->static_addresses, address);
+ network->n_static_addresses++;
+
if (filename) {
address->section = TAKE_PTR(n);
+ r = hashmap_ensure_allocated(&network->addresses_by_section, &network_config_hash_ops);
+ if (r < 0)
+ return r;
+
r = hashmap_put(network->addresses_by_section, address->section, address);
if (r < 0)
return r;
}
- address->network = network;
- LIST_APPEND(addresses, network->static_addresses, address);
- network->n_static_addresses++;
-
*ret = TAKE_PTR(address);
return 0;
@@ -90,10 +97,8 @@ void address_free(Address *address) {
assert(address->network->n_static_addresses > 0);
address->network->n_static_addresses--;
- if (address->section) {
+ if (address->section)
hashmap_remove(address->network->addresses_by_section, address->section);
- network_config_section_free(address->section);
- }
}
if (address->link) {
@@ -104,12 +109,12 @@ void address_free(Address *address) {
memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr));
}
+ network_config_section_free(address->section);
+ free(address->label);
free(address);
}
-static void address_hash_func(const void *b, struct siphash *state) {
- const Address *a = b;
-
+static void address_hash_func(const Address *a, struct siphash *state) {
assert(a);
siphash24_compress(&a->family, sizeof(a->family), state);
@@ -142,21 +147,19 @@ static void address_hash_func(const void *b, struct siphash *state) {
}
}
-static int address_compare_func(const void *c1, const void *c2) {
- const Address *a1 = c1, *a2 = c2;
+static int address_compare_func(const Address *a1, const Address *a2) {
+ int r;
- if (a1->family < a2->family)
- return -1;
- if (a1->family > a2->family)
- return 1;
+ r = CMP(a1->family, a2->family);
+ if (r != 0)
+ return r;
switch (a1->family) {
/* use the same notion of equality as the kernel does */
case AF_INET:
- if (a1->prefixlen < a2->prefixlen)
- return -1;
- if (a1->prefixlen > a2->prefixlen)
- return 1;
+ r = CMP(a1->prefixlen, a2->prefixlen);
+ if (r != 0)
+ return r;
/* compare the peer prefixes */
if (a1->prefixlen != 0) {
@@ -174,10 +177,9 @@ static int address_compare_func(const void *c1, const void *c2) {
else
b2 = be32toh(a2->in_addr.in.s_addr) >> (32 - a1->prefixlen);
- if (b1 < b2)
- return -1;
- if (b1 > b2)
- return 1;
+ r = CMP(b1, b2);
+ if (r != 0)
+ return r;
}
_fallthrough_;
@@ -189,10 +191,7 @@ static int address_compare_func(const void *c1, const void *c2) {
}
}
-static const struct hash_ops address_hash_ops = {
- .hash = address_hash_func,
- .compare = address_compare_func
-};
+DEFINE_PRIVATE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func);
bool address_equal(Address *a1, Address *a2) {
if (a1 == a2)
@@ -423,12 +422,30 @@ int address_get(Link *link,
return -ENOENT;
}
+static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(m);
+ assert(link);
+ assert(link->ifname);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EADDRNOTAVAIL)
+ log_link_warning_errno(link, r, "Could not drop address: %m");
+
+ return 1;
+}
+
int address_remove(
Address *address,
Link *link,
- sd_netlink_message_handler_t callback) {
+ link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ _cleanup_free_ char *b = NULL;
int r;
assert(address);
@@ -438,6 +455,11 @@ int address_remove(
assert(link->manager);
assert(link->manager->rtnl);
+ if (DEBUG_LOGGING) {
+ if (in_addr_to_string(address->family, &address->in_addr, &b) >= 0)
+ log_link_debug(link, "Removing address %s", b);
+ }
+
r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR,
link->ifindex, address->family);
if (r < 0)
@@ -454,7 +476,9 @@ int address_remove(
if (r < 0)
return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req,
+ callback ?: address_remove_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_error_errno(r, "Could not send rtnetlink message: %m");
@@ -527,7 +551,7 @@ static int address_acquire(Link *link, Address *original, Address **ret) {
int address_configure(
Address *address,
Link *link,
- sd_netlink_message_handler_t callback,
+ link_netlink_message_handler_t callback,
bool update) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
@@ -539,6 +563,7 @@ int address_configure(
assert(link->ifindex > 0);
assert(link->manager);
assert(link->manager->rtnl);
+ assert(callback);
/* If this is a new address, then refuse adding more than the limit */
if (address_get(link, address->family, &address->in_addr, address->prefixlen, NULL) <= 0 &&
@@ -632,7 +657,8 @@ int address_configure(
if (r < 0)
return r;
- r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
+ link_netlink_destroy_callback, link);
if (r < 0) {
address_release(address);
return log_error_errno(r, "Could not send rtnetlink message: %m");
@@ -705,8 +731,8 @@ int config_parse_address(const char *unit,
Network *network = userdata;
_cleanup_(address_freep) Address *n = NULL;
- const char *address, *e;
union in_addr_union buffer;
+ unsigned char prefixlen;
int r, f;
assert(filename);
@@ -726,44 +752,19 @@ int config_parse_address(const char *unit,
return r;
/* Address=address/prefixlen */
-
- /* prefixlen */
- e = strchr(rvalue, '/');
- if (e) {
- unsigned i;
-
- r = safe_atou(e + 1, &i);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is invalid, ignoring assignment: %s", e + 1);
- return 0;
- }
-
- n->prefixlen = (unsigned char) i;
-
- address = strndupa(rvalue, e - rvalue);
- } else
- address = rvalue;
-
- r = in_addr_from_string_auto(address, &f, &buffer);
+ r = in_addr_default_prefix_from_string_auto(rvalue, &f, &buffer, &prefixlen);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Address is invalid, ignoring assignment: %s", address);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid address '%s', ignoring assignment: %m", rvalue);
return 0;
}
- if (!e && f == AF_INET) {
- r = in4_addr_default_prefixlen(&buffer.in, &n->prefixlen);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length not specified, and a default one cannot be deduced for '%s', ignoring assignment", address);
- return 0;
- }
- }
-
if (n->family != AF_UNSPEC && f != n->family) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Address is incompatible, ignoring assignment: %s", address);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Address is incompatible, ignoring assignment: %s", rvalue);
return 0;
}
n->family = f;
+ n->prefixlen = prefixlen;
if (streq(lvalue, "Address"))
n->in_addr = buffer;
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index 63e8f5d1ec..4714c07d4d 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <inttypes.h>
#include <stdbool.h>
@@ -55,8 +54,8 @@ int address_add(Link *link, int family, const union in_addr_union *in_addr, unsi
int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
int address_update(Address *address, unsigned char flags, unsigned char scope, const struct ifa_cacheinfo *cinfo);
int address_drop(Address *address);
-int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback, bool update);
-int address_remove(Address *address, Link *link, sd_netlink_message_handler_t callback);
+int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update);
+int address_remove(Address *address, Link *link, link_netlink_message_handler_t callback);
bool address_equal(Address *a1, Address *a2);
bool address_is_ready(const Address *a);
diff --git a/src/network/networkd-brvlan.c b/src/network/networkd-brvlan.c
index 8e8a618e21..1d18e29b7c 100644
--- a/src/network/networkd-brvlan.c
+++ b/src/network/networkd-brvlan.c
@@ -135,8 +135,7 @@ static int append_vlan_info_data(Link *const link, sd_netlink_message *req, uint
return cnt;
}
-static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- Link *link = userdata;
+static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -195,10 +194,13 @@ int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32
return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m");
/* send message to the kernel */
- r = sd_netlink_call_async(rtnl, req, set_brvlan_handler, link, 0, NULL);
+ r = netlink_call_async(rtnl, NULL, req, set_brvlan_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+ link_ref(link);
+
return 0;
}
diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c
index 05bbc0b3e9..334ffc3c57 100644
--- a/src/network/networkd-conf.c
+++ b/src/network/networkd-conf.c
@@ -31,7 +31,74 @@ static const char* const duid_type_table[_DUID_TYPE_MAX] = {
[DUID_TYPE_UUID] = "uuid",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_duid_type, duid_type, DUIDType, "Failed to parse DUID type");
+
+int config_parse_duid_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_ char *type_string = NULL;
+ const char *p = rvalue;
+ DUID *duid = data;
+ DUIDType type;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(duid);
+
+ r = extract_first_word(&p, &type_string, ":", 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r == 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Failed to extract DUID type from '%s', ignoring.", rvalue);
+ return 0;
+ }
+
+ type = duid_type_from_string(type_string);
+ if (type < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Failed to parse DUID type '%s', ignoring.", type_string);
+ return 0;
+ }
+
+ if (!isempty(p)) {
+ usec_t u;
+
+ if (type != DUID_TYPE_LLT) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ r = parse_timestamp(p, &u);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse timestamp, ignoring: %s", p);
+ return 0;
+ }
+
+ duid->llt_time = u;
+ }
+
+ duid->type = type;
+
+ return 0;
+}
int config_parse_duid_rawdata(
const char *unit,
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 34c1f63568..980d49e4ff 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -4,7 +4,6 @@
#include <linux/if.h>
#include "alloc-util.h"
-#include "dhcp-lease-internal.h"
#include "hostname-util.h"
#include "parse-util.h"
#include "netdev/vrf.h"
@@ -15,9 +14,7 @@
#include "string-util.h"
#include "sysctl-util.h"
-static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
- void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -81,15 +78,20 @@ static int link_set_dhcp_routes(Link *link) {
return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
- if (n < 0)
+ if (n == -ENODATA)
+ log_link_debug_errno(link, n, "DHCP: No routes received from DHCP server: %m");
+ else if (n < 0)
log_link_debug_errno(link, n, "DHCP error: could not get routes: %m");
for (i = 0; i < n; i++) {
- if (static_routes[i]->option == SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE)
+ switch (sd_dhcp_route_get_option(static_routes[i])) {
+ case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
classless_route = true;
-
- if (static_routes[i]->option == SD_DHCP_OPTION_STATIC_ROUTE)
+ break;
+ case SD_DHCP_OPTION_STATIC_ROUTE:
static_route = true;
+ break;
+ }
}
for (i = 0; i < n; i++) {
@@ -97,7 +99,8 @@ static int link_set_dhcp_routes(Link *link) {
/* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
the DHCP client MUST ignore the Static Routes option. */
- if (classless_route && static_routes[i]->option == SD_DHCP_OPTION_STATIC_ROUTE)
+ if (classless_route &&
+ sd_dhcp_route_get_option(static_routes[i]) != SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE)
continue;
r = route_new(&route);
@@ -122,7 +125,7 @@ static int link_set_dhcp_routes(Link *link) {
r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
if (r == -ENODATA)
- log_link_info_errno(link, r, "DHCP: No routes received from DHCP server: %m");
+ log_link_info_errno(link, r, "DHCP: No gateway received from DHCP server: %m");
else if (r < 0)
log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
@@ -211,8 +214,7 @@ static int dhcp_lease_lost(Link *link) {
assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0);
assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0);
- route_remove(route, link,
- link_route_remove_handler);
+ route_remove(route, link, NULL);
}
}
}
@@ -232,8 +234,7 @@ static int dhcp_lease_lost(Link *link) {
route_gw->dst_prefixlen = 32;
route_gw->scope = RT_SCOPE_LINK;
- route_remove(route_gw, link,
- link_route_remove_handler);
+ route_remove(route_gw, link, NULL);
}
r = route_new(&route);
@@ -241,8 +242,7 @@ static int dhcp_lease_lost(Link *link) {
route->family = AF_INET;
route->gw.in = gateway;
- route_remove(route, link,
- link_route_remove_handler);
+ route_remove(route, link, NULL);
}
}
@@ -256,7 +256,7 @@ static int dhcp_lease_lost(Link *link) {
address->in_addr.in = addr;
address->prefixlen = prefixlen;
- address_remove(address, link, link_address_remove_handler);
+ address_remove(address, link, NULL);
}
}
@@ -298,9 +298,7 @@ static int dhcp_lease_lost(Link *link) {
return 0;
}
-static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
- void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -501,10 +499,8 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
if (!link->network->dhcp_critical) {
r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
- if (r < 0) {
- log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
}
r = dhcp4_update_address(link, &address, &netmask, lifetime);
@@ -597,7 +593,14 @@ static int dhcp4_set_hostname(Link *link) {
hn = hostname;
}
- return sd_dhcp_client_set_hostname(link->dhcp_client, hn);
+ r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
+ if (r == -EINVAL && hostname)
+ /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
+ log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
+ else if (r < 0)
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set hostname: %m");
+
+ return 0;
}
static bool promote_secondaries_enabled(const char *ifname) {
@@ -653,6 +656,65 @@ int dhcp4_set_promote_secondaries(Link *link) {
return 0;
}
+int dhcp4_set_client_identifier(Link *link) {
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(link->dhcp_client);
+
+ switch (link->network->dhcp_client_identifier) {
+ case DHCP_CLIENT_ID_DUID: {
+ /* If configured, apply user specified DUID and IAID */
+ const DUID *duid = link_get_duid(link);
+
+ if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
+ r = sd_dhcp_client_set_iaid_duid_llt(link->dhcp_client,
+ link->network->iaid_set,
+ link->network->iaid,
+ duid->llt_time);
+ else
+ r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
+ link->network->iaid_set,
+ link->network->iaid,
+ duid->type,
+ duid->raw_data_len > 0 ? duid->raw_data : NULL,
+ duid->raw_data_len);
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set IAID+DUID: %m");
+ break;
+ }
+ case DHCP_CLIENT_ID_DUID_ONLY: {
+ /* If configured, apply user specified DUID */
+ const DUID *duid = link_get_duid(link);
+
+ if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
+ r = sd_dhcp_client_set_duid_llt(link->dhcp_client,
+ duid->llt_time);
+ else
+ r = sd_dhcp_client_set_duid(link->dhcp_client,
+ duid->type,
+ duid->raw_data_len > 0 ? duid->raw_data : NULL,
+ duid->raw_data_len);
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set DUID: %m");
+ break;
+ }
+ case DHCP_CLIENT_ID_MAC:
+ r = sd_dhcp_client_set_client_id(link->dhcp_client,
+ ARPHRD_ETHER,
+ (const uint8_t *) &link->mac,
+ sizeof(link->mac));
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set client ID: %m");
+ break;
+ default:
+ assert_not_reached("Unknown client identifier type.");
+ }
+
+ return 0;
+}
+
int dhcp4_configure(Link *link) {
int r;
@@ -662,44 +724,46 @@ int dhcp4_configure(Link *link) {
if (!link->dhcp_client) {
r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
+ if (r == -ENOMEM)
+ return log_oom();
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to create DHCP4 client: %m");
}
r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to attach event: %m");
r = sd_dhcp_client_set_mac(link->dhcp_client,
(const uint8_t *) &link->mac,
sizeof (link->mac), ARPHRD_ETHER);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set MAC address: %m");
r = sd_dhcp_client_set_ifindex(link->dhcp_client, link->ifindex);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set ifindex: %m");
r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set callback: %m");
r = sd_dhcp_client_set_request_broadcast(link->dhcp_client,
link->network->dhcp_broadcast);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for broadcast: %m");
if (link->mtu) {
r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set MTU: %m");
}
if (link->network->dhcp_use_mtu) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
SD_DHCP_OPTION_INTERFACE_MTU);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for MTU: %m");
}
/* NOTE: even if this variable is called "use", it also "sends" PRL
@@ -711,23 +775,24 @@ int dhcp4_configure(Link *link) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
SD_DHCP_OPTION_STATIC_ROUTE);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for static route: %m");
+
r = sd_dhcp_client_set_request_option(link->dhcp_client,
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for classless static route: %m");
}
if (link->network->dhcp_use_ntp) {
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for NTP server: %m");
}
if (link->network->dhcp_use_timezone) {
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for timezone: %m");
}
r = dhcp4_set_hostname(link);
@@ -738,58 +803,20 @@ int dhcp4_configure(Link *link) {
r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
link->network->dhcp_vendor_class_identifier);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set vendor class identifier: %m");
}
if (link->network->dhcp_user_class) {
r = sd_dhcp_client_set_user_class(link->dhcp_client, (const char **) link->network->dhcp_user_class);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set user class: %m");
}
if (link->network->dhcp_client_port) {
r = sd_dhcp_client_set_client_port(link->dhcp_client, link->network->dhcp_client_port);
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set listen port: %m");
}
- switch (link->network->dhcp_client_identifier) {
- case DHCP_CLIENT_ID_DUID: {
- /* If configured, apply user specified DUID and/or IAID */
- const DUID *duid = link_duid(link);
-
- r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
- link->network->iaid,
- duid->type,
- duid->raw_data_len > 0 ? duid->raw_data : NULL,
- duid->raw_data_len);
- if (r < 0)
- return r;
- break;
- }
- case DHCP_CLIENT_ID_DUID_ONLY: {
- /* If configured, apply user specified DUID */
- const DUID *duid = link_duid(link);
-
- r = sd_dhcp_client_set_duid(link->dhcp_client,
- duid->type,
- duid->raw_data_len > 0 ? duid->raw_data : NULL,
- duid->raw_data_len);
- if (r < 0)
- return r;
- break;
- }
- case DHCP_CLIENT_ID_MAC:
- r = sd_dhcp_client_set_client_id(link->dhcp_client,
- ARPHRD_ETHER,
- (const uint8_t *) &link->mac,
- sizeof(link->mac));
- if (r < 0)
- return r;
- break;
- default:
- assert_not_reached("Unknown client identifier type.");
- }
-
- return 0;
+ return dhcp4_set_client_identifier(link);
}
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index 0fcd2cab3e..ed6b9df72b 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -20,20 +20,13 @@
static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
-static bool dhcp6_verify_link(Link *link) {
- if (!link->network) {
- log_link_info(link, "Link is not managed by us");
+static bool dhcp6_get_prefix_delegation(Link *link) {
+ if (!link->network)
return false;
- }
- if (!IN_SET(link->network->router_prefix_delegation,
- RADV_PREFIX_DELEGATION_DHCP6,
- RADV_PREFIX_DELEGATION_BOTH)) {
- log_link_debug(link, "Link does not request DHCPv6 prefix delegation");
- return false;
- }
-
- return true;
+ return IN_SET(link->network->router_prefix_delegation,
+ RADV_PREFIX_DELEGATION_DHCP6,
+ RADV_PREFIX_DELEGATION_BOTH);
}
static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
@@ -50,7 +43,7 @@ static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
if (l == dhcp6_link)
continue;
- if (!dhcp6_verify_link(l))
+ if (!dhcp6_get_prefix_delegation(l))
continue;
return true;
@@ -103,12 +96,74 @@ static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
return sd_radv_start(radv);
}
-static Network *dhcp6_reset_pd_prefix_network(Link *link) {
+static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+ int r;
+
assert(link);
- assert(link->manager);
- assert(link->manager->networks);
- return link->manager->networks;
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0)
+ log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m");
+
+ return 1;
+}
+
+int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
+ int r;
+ sd_dhcp6_lease *lease;
+ union in_addr_union pd_prefix;
+ uint8_t pd_prefix_len;
+ uint32_t lifetime_preferred, lifetime_valid;
+
+ r = sd_dhcp6_client_get_lease(client, &lease);
+ if (r < 0)
+ return r;
+
+ sd_dhcp6_lease_reset_pd_prefix_iter(lease);
+
+ while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
+ &lifetime_preferred,
+ &lifetime_valid) >= 0) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_free_ Route *route = NULL;
+
+ if (pd_prefix_len > 64)
+ continue;
+
+ (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
+
+ if (pd_prefix_len < 64) {
+ r = route_new(&route);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Cannot create unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
+ strnull(buf),
+ pd_prefix_len);
+ continue;
+ }
+
+ route_add(link, AF_INET6, &pd_prefix, pd_prefix_len,
+ 0, 0, 0, &route);
+ route_update(route, NULL, 0, NULL, NULL, 0, 0,
+ RTN_UNREACHABLE);
+
+ r = route_remove(route, link, dhcp6_route_remove_handler);
+ if (r < 0) {
+ (void) in_addr_to_string(AF_INET6,
+ &pd_prefix, &buf);
+
+ log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
+ strnull(buf),
+ pd_prefix_len);
+
+ continue;
+ }
+
+ log_link_debug(link, "Removing unreachable route %s/%u",
+ strnull(buf), pd_prefix_len);
+ }
+ }
+
+ return 0;
}
static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
@@ -119,8 +174,9 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
Link *link;
Manager *manager = dhcp6_link->manager;
union in_addr_union prefix;
- uint8_t n_prefixes, n_used = 0;
+ uint64_t n_prefixes, n_used = 0;
_cleanup_free_ char *buf = NULL;
+ _cleanup_free_ char *assigned_buf = NULL;
int r;
assert(manager);
@@ -132,17 +188,17 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
if (r < 0)
return r;
- n_prefixes = 1 << (64 - pd_prefix_len);
+ n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
(void) in_addr_to_string(AF_INET6, &prefix, &buf);
- log_link_debug(dhcp6_link, "Assigning up to %u prefixes from %s/%u",
+ log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
n_prefixes, strnull(buf), pd_prefix_len);
while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
Link *assigned_link;
if (n_used == n_prefixes) {
- log_link_debug(dhcp6_link, "Assigned %u/%u prefixes from %s/%u",
+ log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
n_used, n_prefixes, strnull(buf), pd_prefix_len);
return -EAGAIN;
@@ -151,67 +207,58 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
if (link == dhcp6_link)
continue;
- if (!dhcp6_verify_link(link))
+ if (!dhcp6_get_prefix_delegation(link))
continue;
assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
if (assigned_link != NULL && assigned_link != link)
continue;
+ (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
lifetime_preferred, lifetime_valid);
if (r < 0) {
- log_link_error_errno(link, r, "Unable to %s prefix %s/%u for link: %m",
+ log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m",
assigned_link ? "update": "assign",
+ strnull(assigned_buf),
strnull(buf), pd_prefix_len);
if (assigned_link == NULL)
continue;
} else
- log_link_debug(link, "Assigned prefix %u/%u %s/64 to link",
- n_used + 1, n_prefixes, strnull(buf));
+ log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link",
+ n_used + 1, n_prefixes,
+ strnull(assigned_buf),
+ strnull(buf), pd_prefix_len);
n_used++;
- r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len);
+ r = in_addr_prefix_next(AF_INET6, &prefix, 64);
if (r < 0 && n_used < n_prefixes)
return r;
}
- if (n_used < n_prefixes) {
- Route *route;
- int n = n_used;
-
- r = route_new(&route);
- if (r < 0)
- return r;
-
- route->family = AF_INET6;
+ return 0;
+}
- while (n < n_prefixes) {
- route_update(route, &prefix, pd_prefix_len, NULL, NULL,
- 0, 0, RTN_UNREACHABLE);
+static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+ int r;
- r = route_configure(route, dhcp6_link, NULL);
- if (r < 0) {
- route_free(route);
- return r;
- }
+ assert(link);
- r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len);
- if (r < 0)
- return r;
- }
- }
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST)
+ log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m");
- return n_used;
+ return 1;
}
+
static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
int r;
sd_dhcp6_lease *lease;
- struct in6_addr pd_prefix;
+ union in_addr_union pd_prefix;
uint8_t pd_prefix_len;
uint32_t lifetime_preferred, lifetime_valid;
_cleanup_free_ char *buf = NULL;
@@ -221,22 +268,61 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
if (r < 0)
return r;
- (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf);
-
- dhcp6_reset_pd_prefix_network(link);
sd_dhcp6_lease_reset_pd_prefix_iter(lease);
- while (sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len,
+ while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
&lifetime_preferred,
&lifetime_valid) >= 0) {
if (pd_prefix_len > 64) {
+ (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
strnull(buf), pd_prefix_len);
continue;
}
- r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix,
+ if (pd_prefix_len < 48) {
+ (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
+ log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
+ strnull(buf), pd_prefix_len);
+ }
+
+ if (pd_prefix_len < 64) {
+ Route *route = NULL;
+
+ (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
+
+ r = route_new(&route);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Cannot create unreachable route for DHCPv6 delegated subnet %s/%u: %m",
+ strnull(buf),
+ pd_prefix_len);
+ continue;
+ }
+
+ route_add(link, AF_INET6, &pd_prefix, pd_prefix_len,
+ 0, 0, 0, &route);
+ route_update(route, NULL, 0, NULL, NULL, 0, 0,
+ RTN_UNREACHABLE);
+
+ r = route_configure(route, link, dhcp6_route_handler);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
+ strnull(buf),
+ pd_prefix_len);
+ route_free(route);
+ continue;
+ }
+
+ route_free(route);
+
+ log_link_debug(link, "Configuring unreachable route for %s/%u",
+ strnull(buf), pd_prefix_len);
+
+ } else
+ log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
+
+ r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6,
pd_prefix_len,
lifetime_preferred,
lifetime_valid);
@@ -250,9 +336,71 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
return 0;
}
-static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
- void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+int dhcp6_request_prefix_delegation(Link *link) {
+ Link *l;
+ Iterator i;
+
+ assert_return(link, -EINVAL);
+ assert_return(link->manager, -EOPNOTSUPP);
+
+ if (dhcp6_get_prefix_delegation(link) <= 0)
+ return 0;
+
+ log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
+
+ HASHMAP_FOREACH(l, link->manager->links, i) {
+ int r, enabled;
+
+ if (l == link)
+ continue;
+
+ if (!l->dhcp6_client)
+ continue;
+
+ r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
+ if (r < 0) {
+ log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link");
+ continue;
+ }
+
+ if (enabled == 0) {
+ r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
+ if (r < 0) {
+ log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link");
+ continue;
+ }
+ }
+
+ r = sd_dhcp6_client_is_running(l->dhcp6_client);
+ if (r <= 0)
+ continue;
+
+ if (enabled != 0) {
+ log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
+ (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l);
+
+ continue;
+ }
+
+ r = sd_dhcp6_client_stop(l->dhcp6_client);
+ if (r < 0) {
+ log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link");
+ continue;
+ }
+
+ r = sd_dhcp6_client_start(l->dhcp6_client);
+ if (r < 0) {
+ log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link");
+ continue;
+ }
+
+ log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
+ }
+
+ return 0;
+}
+
+static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -354,6 +502,7 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
log_link_warning(link, "DHCPv6 lease lost");
+ (void) dhcp6_lease_pd_prefix_lost(client, link);
(void) manager_dhcp6_prefix_remove_all(link->manager, link);
link->dhcp6_configured = false;
@@ -393,11 +542,12 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
}
int dhcp6_request_address(Link *link, int ir) {
- int r, inf_req;
+ int r, inf_req, pd;
bool running;
assert(link);
assert(link->dhcp6_client);
+ assert(link->network);
assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
r = sd_dhcp6_client_is_running(link->dhcp6_client);
@@ -406,6 +556,21 @@ int dhcp6_request_address(Link *link, int ir) {
else
running = r;
+ r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
+ if (r < 0)
+ return r;
+
+ if (pd && ir && link->network->dhcp6_force_pd_other_information) {
+ log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
+
+ r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
+ false);
+ if (r < 0 )
+ return r;
+
+ ir = false;
+ }
+
if (running) {
r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
if (r < 0)
@@ -453,11 +618,18 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
hn = hostname;
}
- return sd_dhcp6_client_set_fqdn(client, hn);
+ r = sd_dhcp6_client_set_fqdn(client, hn);
+ if (r == -EINVAL && hostname)
+ /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
+ log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
+ else if (r < 0)
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
+
+ return 0;
}
int dhcp6_configure(Link *link) {
- sd_dhcp6_client *client = NULL;
+ _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
const DUID *duid;
int r;
@@ -468,60 +640,63 @@ int dhcp6_configure(Link *link) {
return 0;
r = sd_dhcp6_client_new(&client);
+ if (r == -ENOMEM)
+ return log_oom();
if (r < 0)
- return r;
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
r = sd_dhcp6_client_attach_event(client, NULL, 0);
if (r < 0)
- goto error;
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
r = sd_dhcp6_client_set_mac(client,
(const uint8_t *) &link->mac,
sizeof (link->mac), ARPHRD_ETHER);
if (r < 0)
- goto error;
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
- r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
- if (r < 0)
- goto error;
+ if (link->network->iaid_set) {
+ r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
+ }
- duid = link_duid(link);
- r = sd_dhcp6_client_set_duid(client,
- duid->type,
- duid->raw_data_len > 0 ? duid->raw_data : NULL,
- duid->raw_data_len);
+ duid = link_get_duid(link);
+ if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
+ r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
+ else
+ r = sd_dhcp6_client_set_duid(client,
+ duid->type,
+ duid->raw_data_len > 0 ? duid->raw_data : NULL,
+ duid->raw_data_len);
if (r < 0)
- goto error;
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
r = dhcp6_set_hostname(client, link);
if (r < 0)
- goto error;
+ return r;
r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
if (r < 0)
- goto error;
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
if (link->network->rapid_commit) {
r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
if (r < 0)
- goto error;
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
}
r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
if (r < 0)
- goto error;
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
if (dhcp6_enable_prefix_delegation(link)) {
r = sd_dhcp6_client_set_prefix_delegation(client, true);
if (r < 0)
- goto error;
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
}
- link->dhcp6_client = client;
+ link->dhcp6_client = TAKE_PTR(client);
return 0;
-
-error:
- sd_dhcp6_client_unref(client);
- return r;
}
diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c
index 11f70723a9..324f6a5a1a 100644
--- a/src/network/networkd-fdb.c
+++ b/src/network/networkd-fdb.c
@@ -20,18 +20,26 @@
/* create a new FDB entry or get an existing one. */
int fdb_entry_new_static(
Network *network,
- unsigned section,
+ const char *filename,
+ unsigned section_line,
FdbEntry **ret) {
+ _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
- struct ether_addr *mac_addr = NULL;
+ _cleanup_free_ struct ether_addr *mac_addr = NULL;
+ int r;
assert(network);
assert(ret);
+ assert(!!filename == (section_line > 0));
/* search entry in hashmap first. */
- if (section) {
- fdb_entry = hashmap_get(network->fdb_entries_by_section, UINT_TO_PTR(section));
+ if (filename) {
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ fdb_entry = hashmap_get(network->fdb_entries_by_section, n);
if (fdb_entry) {
*ret = TAKE_PTR(fdb_entry);
@@ -48,24 +56,29 @@ int fdb_entry_new_static(
return -ENOMEM;
/* allocate space for and FDB entry. */
- fdb_entry = new0(FdbEntry, 1);
- if (!fdb_entry) {
- /* free previously allocated space for mac_addr. */
- free(mac_addr);
+ fdb_entry = new(FdbEntry, 1);
+ if (!fdb_entry)
return -ENOMEM;
- }
/* init FDB structure. */
- fdb_entry->network = network;
- fdb_entry->mac_addr = mac_addr;
+ *fdb_entry = (FdbEntry) {
+ .network = network,
+ .mac_addr = TAKE_PTR(mac_addr),
+ };
LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry);
network->n_static_fdb_entries++;
- if (section) {
- fdb_entry->section = section;
- hashmap_put(network->fdb_entries_by_section,
- UINT_TO_PTR(fdb_entry->section), fdb_entry);
+ if (filename) {
+ fdb_entry->section = TAKE_PTR(n);
+
+ r = hashmap_ensure_allocated(&network->fdb_entries_by_section, &network_config_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(network->fdb_entries_by_section, fdb_entry->section, fdb_entry);
+ if (r < 0)
+ return r;
}
/* return allocated FDB structure. */
@@ -74,8 +87,7 @@ int fdb_entry_new_static(
return 0;
}
-static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- Link *link = userdata;
+static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -134,10 +146,13 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
}
/* send message to the kernel to update its internal static MAC table. */
- r = sd_netlink_call_async(rtnl, req, set_fdb_handler, link, 0, NULL);
+ r = netlink_call_async(rtnl, NULL, req, set_fdb_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+ link_ref(link);
+
return 0;
}
@@ -148,16 +163,15 @@ void fdb_entry_free(FdbEntry *fdb_entry) {
if (fdb_entry->network) {
LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry);
-
assert(fdb_entry->network->n_static_fdb_entries > 0);
fdb_entry->network->n_static_fdb_entries--;
if (fdb_entry->section)
- hashmap_remove(fdb_entry->network->fdb_entries_by_section, UINT_TO_PTR(fdb_entry->section));
+ hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section);
}
+ network_config_section_free(fdb_entry->section);
free(fdb_entry->mac_addr);
-
free(fdb_entry);
}
@@ -184,7 +198,7 @@ int config_parse_fdb_hwaddr(
assert(rvalue);
assert(data);
- r = fdb_entry_new_static(network, section_line, &fdb_entry);
+ r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
if (r < 0)
return log_oom();
@@ -197,7 +211,7 @@ int config_parse_fdb_hwaddr(
&fdb_entry->mac_addr->ether_addr_octet[4],
&fdb_entry->mac_addr->ether_addr_octet[5]);
- if (ETHER_ADDR_LEN != r) {
+ if (r != ETHER_ADDR_LEN) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
return 0;
}
@@ -230,7 +244,7 @@ int config_parse_fdb_vlan_id(
assert(rvalue);
assert(data);
- r = fdb_entry_new_static(network, section_line, &fdb_entry);
+ r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
if (r < 0)
return log_oom();
diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h
index ac0d131a99..55fc3a2170 100644
--- a/src/network/networkd-fdb.h
+++ b/src/network/networkd-fdb.h
@@ -12,10 +12,11 @@
typedef struct Network Network;
typedef struct FdbEntry FdbEntry;
typedef struct Link Link;
+typedef struct NetworkConfigSection NetworkConfigSection;
struct FdbEntry {
Network *network;
- unsigned section;
+ NetworkConfigSection *section;
struct ether_addr *mac_addr;
uint16_t vlan_id;
@@ -23,7 +24,7 @@ struct FdbEntry {
LIST_FIELDS(FdbEntry, static_fdb_entries);
};
-int fdb_entry_new_static(Network *network, unsigned section, FdbEntry **ret);
+int fdb_entry_new_static(Network *network, const char *filename, unsigned section_line, FdbEntry **ret);
void fdb_entry_free(FdbEntry *fdb_entry);
int fdb_entry_configure(Link *link, FdbEntry *fdb_entry);
diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf
index 54161446bb..cc88fc0add 100644
--- a/src/network/networkd-gperf.gperf
+++ b/src/network/networkd-gperf.gperf
@@ -18,5 +18,5 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid.type)
+DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid)
DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid)
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index a710240f94..3562e90535 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -26,37 +26,32 @@ static int ipv4ll_address_lost(Link *link) {
log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr));
r = address_new(&address);
- if (r < 0) {
- log_link_error_errno(link, r, "Could not allocate address: %m");
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate address: %m");
address->family = AF_INET;
address->in_addr.in = addr;
address->prefixlen = 16;
address->scope = RT_SCOPE_LINK;
- address_remove(address, link, link_address_remove_handler);
+ address_remove(address, link, NULL);
r = route_new(&route);
- if (r < 0) {
- log_link_error_errno(link, r, "Could not allocate route: %m");
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
route->family = AF_INET;
route->scope = RT_SCOPE_LINK;
route->priority = IPV4LL_ROUTE_METRIC;
- route_remove(route, link, link_route_remove_handler);
+ route_remove(route, link, NULL);
link_check_ready(link);
return 0;
}
-static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -76,8 +71,7 @@ static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *u
return 1;
}
-static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -206,13 +200,11 @@ int ipv4ll_configure(Link *link) {
return r;
}
- if (link->udev_device) {
- r = net_get_unique_predictable_data(link->udev_device, &seed);
- if (r >= 0) {
- r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
- if (r < 0)
- return r;
- }
+ if (link->sd_device &&
+ net_get_unique_predictable_data(link->sd_device, &seed) >= 0) {
+ r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
+ if (r < 0)
+ return r;
}
r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c
index 3ebbacabaa..f594b27f7f 100644
--- a/src/network/networkd-ipv6-proxy-ndp.c
+++ b/src/network/networkd-ipv6-proxy-ndp.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2017 Florian Klink <flokli@flokli.de>
-***/
#include <netinet/ether.h>
#include <linux/if.h>
@@ -46,7 +43,7 @@ static int ipv6_proxy_ndp_set(Link *link) {
v = ipv6_proxy_ndp_is_needed(link);
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/proxy_ndp");
- r = write_string_file(p, one_zero(v), WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ r = write_string_file(p, one_zero(v), WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure proxy NDP for interface: %m");
@@ -60,17 +57,18 @@ int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **re
assert(ret);
/* allocate space for IPv6ProxyNDPAddress entry */
- ipv6_proxy_ndp_address = new0(IPv6ProxyNDPAddress, 1);
+ ipv6_proxy_ndp_address = new(IPv6ProxyNDPAddress, 1);
if (!ipv6_proxy_ndp_address)
return -ENOMEM;
- ipv6_proxy_ndp_address->network = network;
+ *ipv6_proxy_ndp_address = (IPv6ProxyNDPAddress) {
+ .network = network,
+ };
LIST_PREPEND(ipv6_proxy_ndp_addresses, network->ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address);
network->n_ipv6_proxy_ndp_addresses++;
- *ret = ipv6_proxy_ndp_address;
- ipv6_proxy_ndp_address = NULL;
+ *ret = TAKE_PTR(ipv6_proxy_ndp_address);
return 0;
}
@@ -91,16 +89,16 @@ void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) {
}
int config_parse_ipv6_proxy_ndp_address(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+ 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_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL;
@@ -137,8 +135,7 @@ int config_parse_ipv6_proxy_ndp_address(
return 0;
}
-static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- Link *link = userdata;
+static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -176,10 +173,13 @@ int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy
if (r < 0)
return rtnl_log_create_error(r);
- r = sd_netlink_call_async(rtnl, req, set_ipv6_proxy_ndp_address_handler, link, 0, NULL);
+ r = netlink_call_async(rtnl, NULL, req, set_ipv6_proxy_ndp_address_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+ link_ref(link);
+
return 0;
}
diff --git a/src/network/networkd-ipv6-proxy-ndp.h b/src/network/networkd-ipv6-proxy-ndp.h
index d06273be39..546e8561f1 100644
--- a/src/network/networkd-ipv6-proxy-ndp.h
+++ b/src/network/networkd-ipv6-proxy-ndp.h
@@ -1,10 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- Copyright © 2017 Florian Klink <flokli@flokli.de>
-***/
-
#include "conf-parser.h"
#include "list.h"
#include "macro.h"
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 4afcf843bd..e2851df31a 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -8,25 +8,37 @@
#include "alloc-util.h"
#include "bus-util.h"
+#include "dhcp-identifier.h"
#include "dhcp-lease-internal.h"
+#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
+#include "missing_network.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
+#include "networkd-neighbor.h"
#include "networkd-radv.h"
#include "networkd-routing-policy-rule.h"
#include "set.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "string-table.h"
-#include "udev-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
#include "util.h"
#include "virt.h"
+DUID* link_get_duid(Link *link) {
+ if (link->network->duid.type != _DUID_TYPE_INVALID)
+ return &link->network->duid;
+ else
+ return &link->manager->duid;
+}
+
static bool link_dhcp6_enabled(Link *link) {
assert(link);
@@ -262,7 +274,7 @@ static int link_enable_ipv6(Link *link) {
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/disable_ipv6");
- r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m",
enable_disable(!disabled), link->ifname);
@@ -399,6 +411,8 @@ static int link_update_flags(Link *link, sd_netlink_message *m) {
return 0;
}
+DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
+
static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
_cleanup_(link_unrefp) Link *link = NULL;
uint16_t type;
@@ -413,7 +427,7 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
/* check for link kind */
r = sd_netlink_message_enter_container(message, IFLA_LINKINFO);
if (r == 0) {
- (void)sd_netlink_message_read_string(message, IFLA_INFO_KIND, &kind);
+ (void) sd_netlink_message_read_string(message, IFLA_INFO_KIND, &kind);
r = sd_netlink_message_exit_container(message);
if (r < 0)
return r;
@@ -439,16 +453,19 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
if (r < 0)
return r;
- link = new0(Link, 1);
+ link = new(Link, 1);
if (!link)
return -ENOMEM;
- link->n_ref = 1;
- link->manager = manager;
- link->state = LINK_STATE_PENDING;
- link->rtnl_extended_attrs = true;
- link->ifindex = ifindex;
- link->iftype = iftype;
+ *link = (Link) {
+ .n_ref = 1,
+ .manager = manager,
+ .state = LINK_STATE_PENDING,
+ .rtnl_extended_attrs = true,
+ .ifindex = ifindex,
+ .iftype = iftype,
+ };
+
link->ifname = strdup(ifname);
if (!link->ifname)
return -ENOMEM;
@@ -459,6 +476,10 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
return -ENOMEM;
}
+ r = sd_netlink_message_read_u32(message, IFLA_MASTER, (uint32_t *)&link->master_ifindex);
+ if (r < 0)
+ log_link_debug_errno(link, r, "New device has no master, continuing without");
+
r = sd_netlink_message_read_ether_addr(message, IFLA_ADDRESS, &link->mac);
if (r < 0)
log_link_debug_errno(link, r, "MAC address not found for new device, continuing without");
@@ -489,22 +510,41 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
return 0;
}
-static void link_free(Link *link) {
+static void link_detach_from_manager(Link *link) {
+ if (!link || !link->manager)
+ return;
+
+ hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
+ set_remove(link->manager->links_requesting_uuid, link);
+ link_clean(link);
+
+ link->manager = NULL;
+}
+
+static Link *link_free(Link *link) {
Address *address;
Link *carrier;
+ Route *route;
Iterator i;
- if (!link)
- return;
+ assert(link);
- while (!set_isempty(link->addresses))
- address_free(set_first(link->addresses));
+ while ((route = set_first(link->routes)))
+ route_free(route);
- while (!set_isempty(link->addresses_foreign))
- address_free(set_first(link->addresses_foreign));
+ while ((route = set_first(link->routes_foreign)))
+ route_free(route);
- link->addresses = set_free(link->addresses);
+ link->routes = set_free(link->routes);
+ link->routes_foreign = set_free(link->routes_foreign);
+ while ((address = set_first(link->addresses)))
+ address_free(address);
+
+ while ((address = set_first(link->addresses_foreign)))
+ address_free(address);
+
+ link->addresses = set_free(link->addresses);
link->addresses_foreign = set_free(link->addresses_foreign);
while ((address = link->pool_addresses)) {
@@ -530,17 +570,16 @@ static void link_free(Link *link) {
sd_ndisc_unref(link->ndisc);
sd_radv_unref(link->radv);
- if (link->manager)
- hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
+ link_detach_from_manager(link);
free(link->ifname);
free(link->kind);
- (void)unlink(link->state_file);
+ (void) unlink(link->state_file);
free(link->state_file);
- udev_device_unref(link->udev_device);
+ sd_device_unref(link->sd_device);
HASHMAP_FOREACH (carrier, link->bound_to_links, i)
hashmap_remove(link->bound_to_links, INT_TO_PTR(carrier->ifindex));
@@ -550,35 +589,10 @@ static void link_free(Link *link) {
hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex));
hashmap_free(link->bound_by_links);
- free(link);
+ return mfree(link);
}
-Link *link_unref(Link *link) {
- if (!link)
- return NULL;
-
- assert(link->n_ref > 0);
-
- link->n_ref--;
-
- if (link->n_ref > 0)
- return NULL;
-
- link_free(link);
-
- return NULL;
-}
-
-Link *link_ref(Link *link) {
- if (!link)
- return NULL;
-
- assert(link->n_ref > 0);
-
- link->n_ref++;
-
- return link;
-}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Link, link, link_free);
int link_get(Manager *m, int ifindex, Link **ret) {
Link *link;
@@ -706,7 +720,7 @@ static void link_enter_configured(Link *link) {
assert(link);
assert(link->network);
- if (link->state != LINK_STATE_SETTING_ROUTES)
+ if (link->state != LINK_STATE_CONFIGURING)
return;
log_link_info(link, "Configured");
@@ -728,6 +742,12 @@ void link_check_ready(Link *link) {
if (!link->network)
return;
+ if (!link->addresses_configured)
+ return;
+
+ if (!link->neighbors_configured)
+ return;
+
if (!link->static_routes_configured)
return;
@@ -776,13 +796,14 @@ static int link_set_routing_policy_rule(Link *link) {
LIST_FOREACH(rules, rule, link->network->rules) {
r = routing_policy_rule_get(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to,
- rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif, &rrule);
- if (r == 1) {
+ rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif,
+ rule->protocol, &rule->sport, &rule->dport, &rrule);
+ if (r == 0) {
(void) routing_policy_rule_make_local(link->manager, rrule);
continue;
}
- r = routing_policy_rule_configure(rule, link, link_routing_policy_rule_handler, false);
+ r = routing_policy_rule_configure(rule, link, NULL, false);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set routing policy rules: %m");
link_enter_failed(link);
@@ -802,14 +823,13 @@ static int link_set_routing_policy_rule(Link *link) {
return 0;
}
-static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
+ assert(link);
assert(link->route_messages > 0);
- assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES,
- LINK_STATE_SETTING_ROUTES, LINK_STATE_FAILED,
- LINK_STATE_LINGER));
+ assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
+ LINK_STATE_FAILED, LINK_STATE_LINGER));
link->route_messages--;
@@ -829,28 +849,41 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata
return 1;
}
-static int link_enter_set_routes(Link *link) {
+static int link_request_set_routes(Link *link) {
+ enum {
+ PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */
+ PHASE_GATEWAY, /* Second phase: Routes with a gateway */
+ _PHASE_MAX
+ } phase;
Route *rt;
int r;
assert(link);
assert(link->network);
- assert(link->state == LINK_STATE_SETTING_ADDRESSES);
+ assert(link->addresses_configured);
+ assert(link->address_messages == 0);
+ assert(link->state != _LINK_STATE_INVALID);
+
+ link_set_state(link, LINK_STATE_CONFIGURING);
(void) link_set_routing_policy_rule(link);
- link_set_state(link, LINK_STATE_SETTING_ROUTES);
+ /* First add the routes that enable us to talk to gateways, then add in the others that need a gateway. */
+ for (phase = 0; phase < _PHASE_MAX; phase++)
+ LIST_FOREACH(routes, rt, link->network->static_routes) {
- LIST_FOREACH(routes, rt, link->network->static_routes) {
- r = route_configure(rt, link, route_handler);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not set routes: %m");
- link_enter_failed(link);
- return r;
- }
+ if (in_addr_is_null(rt->family, &rt->gw) != (phase == PHASE_NON_GATEWAY))
+ continue;
- link->route_messages++;
- }
+ r = route_configure(rt, link, route_handler);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not set routes: %m");
+ link_enter_failed(link);
+ return r;
+ }
+
+ link->route_messages++;
+ }
if (link->route_messages == 0) {
link->static_routes_configured = true;
@@ -861,26 +894,35 @@ static int link_enter_set_routes(Link *link) {
return 0;
}
-int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int link_request_set_neighbors(Link *link) {
+ Neighbor *neighbor;
int r;
- assert(m);
assert(link);
- assert(link->ifname);
+ assert(link->network);
+ assert(link->state != _LINK_STATE_INVALID);
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
+ link_set_state(link, LINK_STATE_CONFIGURING);
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -ESRCH)
- log_link_warning_errno(link, r, "Could not drop route: %m");
+ LIST_FOREACH(neighbors, neighbor, link->network->neighbors) {
+ r = neighbor_configure(neighbor, link, NULL);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not set neighbor: %m");
+ link_enter_failed(link);
+ return r;
+ }
+ }
- return 1;
+ if (link->neighbor_messages == 0) {
+ link->neighbors_configured = true;
+ link_check_ready(link);
+ } else
+ log_link_debug(link, "Setting neighbors");
+
+ return 0;
}
-static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(rtnl);
@@ -888,7 +930,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
assert(link);
assert(link->ifname);
assert(link->address_messages > 0);
- assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES,
+ assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
LINK_STATE_FAILED, LINK_STATE_LINGER));
link->address_messages--;
@@ -904,39 +946,13 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
if (link->address_messages == 0) {
log_link_debug(link, "Addresses set");
- link_enter_set_routes(link);
+ link->addresses_configured = true;
+ link_request_set_routes(link);
}
return 1;
}
-static int address_label_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
- int r;
-
- assert(rtnl);
- assert(m);
- assert(link);
- assert(link->ifname);
- assert(link->address_label_messages > 0);
-
- link->address_label_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_warning_errno(link, r, "could not set address label: %m");
- else if (r >= 0)
- manager_rtnl_process_address(rtnl, m, link->manager);
-
- if (link->address_label_messages == 0)
- log_link_debug(link, "Addresses label set");
-
- return 1;
-}
-
static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
_cleanup_free_ struct in_addr *addresses = NULL;
size_t n_addresses = 0, n_allocated = 0;
@@ -1048,7 +1064,7 @@ static int link_set_bridge_fdb(Link *link) {
return 0;
}
-static int link_enter_set_addresses(Link *link) {
+static int link_request_set_addresses(Link *link) {
AddressLabel *label;
Address *ad;
int r;
@@ -1061,7 +1077,9 @@ static int link_enter_set_addresses(Link *link) {
if (r < 0)
return r;
- link_set_state(link, LINK_STATE_SETTING_ADDRESSES);
+ link_set_state(link, LINK_STATE_CONFIGURING);
+
+ link_request_set_neighbors(link);
LIST_FOREACH(addresses, ad, link->network->static_addresses) {
r = address_configure(ad, link, address_handler, false);
@@ -1075,7 +1093,7 @@ static int link_enter_set_addresses(Link *link) {
}
LIST_FOREACH(labels, label, link->network->address_labels) {
- r = address_label_configure(label, link, address_label_handler, false);
+ r = address_label_configure(label, link, NULL, false);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set address label: %m");
link_enter_failed(link);
@@ -1087,7 +1105,7 @@ static int link_enter_set_addresses(Link *link) {
/* now that we can figure out a default address for the dhcp server,
start it */
- if (link_dhcp4_server_enabled(link)) {
+ if (link_dhcp4_server_enabled(link) && (link->flags & IFF_UP)) {
Address *address;
Link *uplink = NULL;
bool acquired_uplink = false;
@@ -1166,10 +1184,8 @@ static int link_enter_set_addresses(Link *link) {
}
r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m");
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m");
if (link->network->dhcp_server_emit_timezone) {
_cleanup_free_ char *buffer = NULL;
@@ -1204,32 +1220,15 @@ static int link_enter_set_addresses(Link *link) {
log_link_debug(link, "Offering DHCPv4 leases");
}
- if (link->address_messages == 0)
- link_enter_set_routes(link);
- else
+ if (link->address_messages == 0) {
+ link->addresses_configured = true;
+ link_request_set_routes(link);
+ } else
log_link_debug(link, "Setting addresses");
return 0;
}
-int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
- int r;
-
- assert(m);
- assert(link);
- assert(link->ifname);
-
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EADDRNOTAVAIL)
- log_link_warning_errno(link, r, "Could not drop address: %m");
-
- return 1;
-}
-
static int link_set_bridge_vlan(Link *link) {
int r = 0;
@@ -1249,43 +1248,53 @@ static int link_set_proxy_arp(Link *link) {
p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/proxy_arp");
- r = write_string_file(p, one_zero(link->network->proxy_arp), WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ r = write_string_file(p, one_zero(link->network->proxy_arp), WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m");
return 0;
}
-static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
+ assert(link);
+
log_link_debug(link, "Set link");
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not join netdev: %m");
link_enter_failed(link);
- return 1;
}
- return 0;
+ return 1;
}
-static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int link_configure_after_setting_mtu(Link *link);
+
+static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(m);
assert(link);
assert(link->ifname);
+ link->setting_mtu = false;
+
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
r = sd_netlink_message_get_errno(m);
- if (r < 0)
+ if (r < 0) {
log_link_warning_errno(link, r, "Could not set MTU: %m");
+ return 1;
+ }
+
+ log_link_debug(link, "Setting MTU done.");
+
+ if (link->state == LINK_STATE_PENDING)
+ (void) link_configure_after_setting_mtu(link);
return 1;
}
@@ -1298,6 +1307,9 @@ int link_set_mtu(Link *link, uint32_t mtu) {
assert(link->manager);
assert(link->manager->rtnl);
+ if (link->mtu == mtu || link->setting_mtu)
+ return 0;
+
log_link_debug(link, "Setting MTU: %" PRIu32, mtu);
r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
@@ -1305,40 +1317,35 @@ int link_set_mtu(Link *link, uint32_t mtu) {
return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
/* If IPv6 not configured (no static IPv6 address and IPv6LL autoconfiguration is disabled)
- for this interface, or if it is a bridge slave, then disable IPv6 else enable it. */
+ * for this interface, or if it is a bridge slave, then disable IPv6 else enable it. */
(void) link_enable_ipv6(link);
/* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes
- on the interface. Bump up MTU bytes to IPV6_MTU_MIN. */
- if (link_ipv6_enabled(link) && link->network->mtu < IPV6_MIN_MTU) {
+ * on the interface. Bump up MTU bytes to IPV6_MTU_MIN. */
+ if (link_ipv6_enabled(link) && mtu < IPV6_MIN_MTU) {
log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as "
"IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes: %m");
- link->network->mtu = IPV6_MIN_MTU;
+ mtu = IPV6_MIN_MTU;
}
- r = sd_netlink_message_append_u32(req, IFLA_MTU, link->network->mtu);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set MTU: %m");
-
r = sd_netlink_message_append_u32(req, IFLA_MTU, mtu);
if (r < 0)
return log_link_error_errno(link, r, "Could not append MTU: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, set_mtu_handler, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req, set_mtu_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
- link->setting_mtu = true;
-
link_ref(link);
+ link->setting_mtu = true;
return 0;
}
-static int set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(m);
@@ -1397,7 +1404,8 @@ static int link_set_flags(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not set link flags: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, set_flags_handler, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req, set_flags_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -1454,7 +1462,12 @@ static int link_set_bridge(Link *link) {
r = sd_netlink_message_append_u8(req, IFLA_BRPORT_UNICAST_FLOOD, link->network->unicast_flood);
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m");
+ }
+ if (link->network->multicast_to_unicast >= 0) {
+ r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_TO_UCAST, link->network->multicast_to_unicast);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MCAST_TO_UCAST attribute: %m");
}
if (link->network->cost != 0) {
@@ -1462,6 +1475,7 @@ static int link_set_bridge(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m");
}
+
if (link->network->priority != LINK_BRIDGE_PORT_PRIORITY_INVALID) {
r = sd_netlink_message_append_u16(req, IFLA_BRPORT_PRIORITY, link->network->priority);
if (r < 0)
@@ -1472,7 +1486,8 @@ static int link_set_bridge(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, link_set_handler, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req, link_set_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -1524,7 +1539,8 @@ static int link_bond_set(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, set_flags_handler, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req, set_flags_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -1628,18 +1644,6 @@ static int link_acquire_ipv6_conf(Link *link) {
assert(link);
- if (link_dhcp6_enabled(link)) {
- assert(link->dhcp6_client);
- assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
-
- /* start DHCPv6 client in stateless mode */
- 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");
- }
-
if (link_ipv6_accept_ra_enabled(link)) {
assert(link->ndisc);
@@ -1661,6 +1665,8 @@ static int link_acquire_ipv6_conf(Link *link) {
return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
}
+ (void) dhcp6_request_prefix_delegation(link);
+
return 0;
}
@@ -1700,11 +1706,6 @@ static int link_acquire_conf(Link *link) {
assert(link);
- if (link->setting_mtu) {
- link->setting_mtu = false;
- return 0;
- }
-
r = link_acquire_ipv4_conf(link);
if (r < 0)
return r;
@@ -1738,8 +1739,7 @@ bool link_has_carrier(Link *link) {
return false;
}
-static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -1749,8 +1749,7 @@ static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
r = sd_netlink_message_get_errno(m);
if (r < 0)
- /* we warn but don't fail the link, as it may be
- brought up later */
+ /* we warn but don't fail the link, as it may be brought up later */
log_link_warning_errno(link, r, "Could not bring up interface: %m");
return 1;
@@ -1832,7 +1831,8 @@ int link_up(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req, link_up_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -1857,7 +1857,8 @@ static int link_up_can(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not set link flags: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req, link_up_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -1946,7 +1947,8 @@ static int link_set_can(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Failed to close netlink container: %m");
- r = sd_netlink_call_async(link->manager->rtnl, m, link_set_handler, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, m, link_set_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -1965,8 +1967,7 @@ static int link_set_can(Link *link) {
return r;
}
-static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -1978,10 +1979,8 @@ static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *user
if (r < 0)
log_link_warning_errno(link, r, "Could not bring down interface: %m");
- if (streq_ptr(link->kind, "can")) {
- link_ref(link);
+ if (streq_ptr(link->kind, "can"))
link_set_can(link);
- }
return 1;
}
@@ -2005,7 +2004,8 @@ int link_down(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not set link flags: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, link_down_handler, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req, link_down_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
@@ -2257,7 +2257,10 @@ void link_drop(Link *link) {
log_link_debug(link, "Link removed");
- (void)unlink(link->state_file);
+ (void) unlink(link->state_file);
+
+ link_detach_from_manager(link);
+
link_unref(link);
return;
@@ -2306,11 +2309,10 @@ static int link_joined(Link *link) {
if (!link_has_carrier(link) && !link->network->configure_without_carrier)
return 0;
- return link_enter_set_addresses(link);
+ return link_request_set_addresses(link);
}
-static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -2344,7 +2346,7 @@ static int link_enter_join_netdev(Link *link) {
assert(link->network);
assert(link->state == LINK_STATE_PENDING);
- link_set_state(link, LINK_STATE_ENSLAVING);
+ link_set_state(link, LINK_STATE_CONFIGURING);
link_dirty(link);
@@ -2355,6 +2357,10 @@ static int link_enter_join_netdev(Link *link) {
return link_joined(link);
if (link->network->bond) {
+ if (link->network->bond->state == NETDEV_STATE_READY &&
+ link->network->bond->ifindex == link->master_ifindex)
+ return link_joined(link);
+
log_struct(LOG_DEBUG,
LOG_LINK_INTERFACE(link),
LOG_NETDEV_INTERFACE(link->network->bond),
@@ -2452,7 +2458,7 @@ static int link_set_ipv4_forward(Link *link) {
* primarily to keep IPv4 and IPv6 packet forwarding behaviour
* somewhat in sync (see below). */
- r = write_string_file("/proc/sys/net/ipv4/ip_forward", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ r = write_string_file("/proc/sys/net/ipv4/ip_forward", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
@@ -2474,7 +2480,7 @@ static int link_set_ipv6_forward(Link *link) {
* same behaviour there and also propagate the setting from
* one to all, to keep things simple (see above). */
- r = write_string_file("/proc/sys/net/ipv6/conf/all/forwarding", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ r = write_string_file("/proc/sys/net/ipv6/conf/all/forwarding", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m");
@@ -2494,7 +2500,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) {
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/use_tempaddr");
xsprintf(buf, "%u", (unsigned) link->network->ipv6_privacy_extensions);
- r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m");
@@ -2518,7 +2524,7 @@ static int link_set_ipv6_accept_ra(Link *link) {
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra");
/* We handle router advertisements ourselves, tell the kernel to GTFO */
- r = write_string_file(p, "0", WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ r = write_string_file(p, "0", WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m");
@@ -2546,7 +2552,7 @@ static int link_set_ipv6_dad_transmits(Link *link) {
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/dad_transmits");
xsprintf(buf, "%i", link->network->ipv6_dad_transmits);
- r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface: %m");
@@ -2574,7 +2580,7 @@ static int link_set_ipv6_hop_limit(Link *link) {
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/hop_limit");
xsprintf(buf, "%i", link->network->ipv6_hop_limit);
- r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE);
+ r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m");
@@ -2600,13 +2606,45 @@ static int link_set_ipv6_mtu(Link *link) {
xsprintf(buf, "%" PRIu32, link->network->ipv6_mtu);
- r = write_string_file(p, buf, 0);
+ r = write_string_file(p, buf, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 MTU for interface: %m");
return 0;
}
+static bool link_is_static_address_configured(Link *link, Address *address) {
+ Address *net_address;
+
+ assert(link);
+ assert(address);
+
+ if (!link->network)
+ return false;
+
+ LIST_FOREACH(addresses, net_address, link->network->static_addresses)
+ if (address_equal(net_address, address))
+ return true;
+
+ return false;
+}
+
+static bool link_is_static_route_configured(Link *link, Route *route) {
+ Route *net_route;
+
+ assert(link);
+ assert(route);
+
+ if (!link->network)
+ return false;
+
+ LIST_FOREACH(routes, net_route, link->network->static_routes)
+ if (route_equal(net_route, route))
+ return true;
+
+ return false;
+}
+
static int link_drop_foreign_config(Link *link) {
Address *address;
Route *route;
@@ -2618,9 +2656,15 @@ static int link_drop_foreign_config(Link *link) {
if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1)
continue;
- r = address_remove(address, link, link_address_remove_handler);
- if (r < 0)
- return r;
+ if (link_is_static_address_configured(link, address)) {
+ r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to add address: %m");
+ } else {
+ r = address_remove(address, link, NULL);
+ if (r < 0)
+ return r;
+ }
}
SET_FOREACH(route, link->routes_foreign, i) {
@@ -2628,9 +2672,15 @@ static int link_drop_foreign_config(Link *link) {
if (route->protocol == RTPROT_KERNEL)
continue;
- r = route_remove(route, link, link_route_remove_handler);
- if (r < 0)
- return r;
+ if (link_is_static_route_configured(link, route)) {
+ r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL);
+ if (r < 0)
+ return r;
+ } else {
+ r = route_remove(route, link, NULL);
+ if (r < 0)
+ return r;
+ }
}
return 0;
@@ -2647,7 +2697,7 @@ static int link_drop_config(Link *link) {
if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1)
continue;
- r = address_remove(address, link, link_address_remove_handler);
+ r = address_remove(address, link, NULL);
if (r < 0)
return r;
@@ -2666,7 +2716,7 @@ static int link_drop_config(Link *link) {
if (route->protocol == RTPROT_KERNEL)
continue;
- r = route_remove(route, link, link_route_remove_handler);
+ r = route_remove(route, link, NULL);
if (r < 0)
return r;
}
@@ -2868,6 +2918,19 @@ static int link_configure(Link *link) {
return r;
}
+ return link_configure_after_setting_mtu(link);
+}
+
+static int link_configure_after_setting_mtu(Link *link) {
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(link->state == LINK_STATE_PENDING);
+
+ if (link->setting_mtu)
+ return 0;
+
if (link_has_carrier(link) || link->network->configure_without_carrier) {
r = link_acquire_conf(link);
if (r < 0)
@@ -2877,9 +2940,136 @@ static int link_configure(Link *link) {
return link_enter_join_netdev(link);
}
-static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
- void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int duid_set_uuid(DUID *duid, sd_id128_t uuid) {
+ assert(duid);
+
+ if (duid->raw_data_len > 0)
+ return 0;
+
+ if (duid->type != DUID_TYPE_UUID)
+ return -EINVAL;
+
+ memcpy(&duid->raw_data, &uuid, sizeof(sd_id128_t));
+ duid->raw_data_len = sizeof(sd_id128_t);
+
+ return 1;
+}
+
+int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ Manager *manager = userdata;
+ const sd_bus_error *e;
+ const void *a;
+ size_t sz;
+ DUID *duid;
+ Link *link;
+ int r;
+
+ assert(m);
+ assert(manager);
+
+ e = sd_bus_message_get_error(m);
+ if (e) {
+ log_error_errno(sd_bus_error_get_errno(e),
+ "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s",
+ e->message);
+ goto configure;
+ }
+
+ r = sd_bus_message_read_array(m, 'y', &a, &sz);
+ if (r < 0)
+ goto configure;
+
+ if (sz != sizeof(sd_id128_t)) {
+ log_error("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
+ goto configure;
+ }
+
+ memcpy(&manager->product_uuid, a, sz);
+ while ((duid = set_steal_first(manager->duids_requesting_uuid)))
+ (void) duid_set_uuid(duid, manager->product_uuid);
+
+ manager->duids_requesting_uuid = set_free(manager->duids_requesting_uuid);
+
+configure:
+ while ((link = set_steal_first(manager->links_requesting_uuid))) {
+ r = link_configure(link);
+ if (r < 0)
+ log_link_error_errno(link, r, "Failed to configure link: %m");
+ }
+
+ manager->links_requesting_uuid = set_free(manager->links_requesting_uuid);
+
+ /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
+ * even if the method fails. */
+ manager->has_product_uuid = true;
+
+ return 1;
+}
+
+static bool link_requires_uuid(Link *link) {
+ const DUID *duid;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->network);
+
+ duid = link_get_duid(link);
+ if (duid->type != DUID_TYPE_UUID || duid->raw_data_len != 0)
+ return false;
+
+ if (link_dhcp4_enabled(link) && IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY))
+ return true;
+
+ if (link_dhcp6_enabled(link) || link_ipv6_accept_ra_enabled(link))
+ return true;
+
+ return false;
+}
+
+static int link_configure_duid(Link *link) {
+ Manager *m;
+ DUID *duid;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->network);
+
+ m = link->manager;
+ duid = link_get_duid(link);
+
+ if (!link_requires_uuid(link))
+ return 1;
+
+ if (m->has_product_uuid) {
+ (void) duid_set_uuid(duid, m->product_uuid);
+ return 1;
+ }
+
+ if (!m->links_requesting_uuid) {
+ r = manager_request_product_uuid(m, link);
+ if (r < 0) {
+ if (r == -ENOMEM)
+ return r;
+
+ log_link_warning_errno(link, r,
+ "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
+ return 1;
+ }
+ } else {
+ r = set_put(m->links_requesting_uuid, link);
+ if (r < 0)
+ return log_oom();
+
+ r = set_put(m->duids_requesting_uuid, duid);
+ if (r < 0)
+ return log_oom();
+ }
+
+ return 0;
+}
+
+static int link_initialized_and_synced(Link *link) {
Network *network;
int r;
@@ -2901,7 +3091,7 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
return r;
if (!link->network) {
- r = network_get(link->manager, link->udev_device, link->ifname,
+ r = network_get(link->manager, link->sd_device, link->ifname,
&link->mac, &network);
if (r == -ENOENT) {
link_enter_unmanaged(link);
@@ -2932,6 +3122,12 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
if (r < 0)
return r;
+ /* link_configure_duid() returns 0 if it requests product UUID. In that case,
+ * link_configure() is called later asynchronously. */
+ r = link_configure_duid(link);
+ if (r <= 0)
+ return r;
+
r = link_configure(link);
if (r < 0)
return r;
@@ -2939,7 +3135,12 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
return 1;
}
-int link_initialized(Link *link, struct udev_device *device) {
+static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ (void) link_initialized_and_synced(link);
+ return 1;
+}
+
+int link_initialized(Link *link, sd_device *device) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
@@ -2951,12 +3152,12 @@ int link_initialized(Link *link, struct udev_device *device) {
if (link->state != LINK_STATE_PENDING)
return 0;
- if (link->udev_device)
+ if (link->sd_device)
return 0;
log_link_debug(link, "udev initialized link");
- link->udev_device = udev_device_ref(device);
+ link->sd_device = sd_device_ref(device);
/* udev has initialized the link, but we don't know if we have yet
* processed the NEWLINK messages with the latest state. Do a GETLINK,
@@ -2968,8 +3169,8 @@ int link_initialized(Link *link, struct udev_device *device) {
if (r < 0)
return r;
- r = sd_netlink_call_async(link->manager->rtnl, req,
- link_initialized_and_synced, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req, link_initialized_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return r;
@@ -2991,13 +3192,12 @@ static int link_load(Link *link) {
assert(link);
- r = parse_env_file(NULL, link->state_file, NEWLINE,
+ r = parse_env_file(NULL, link->state_file,
"NETWORK_FILE", &network_file,
"ADDRESSES", &addresses,
"ROUTES", &routes,
"DHCP4_ADDRESS", &dhcp4_address,
- "IPV4LL_ADDRESS", &ipv4ll_address,
- NULL);
+ "IPV4LL_ADDRESS", &ipv4ll_address);
if (r < 0 && r != -ENOENT)
return log_link_error_errno(link, r, "Failed to read %s: %m", link->state_file);
@@ -3169,9 +3369,9 @@ ipv4ll_address_fail:
}
int link_add(Manager *m, sd_netlink_message *message, Link **ret) {
- Link *link;
- _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
char ifindex_str[2 + DECIMAL_STR_MAX(int)];
+ Link *link;
int r;
assert(m);
@@ -3194,13 +3394,18 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) {
if (detect_container() <= 0) {
/* not in a container, udev will be around */
sprintf(ifindex_str, "n%d", link->ifindex);
- device = udev_device_new_from_device_id(m->udev, ifindex_str);
- if (!device) {
- r = log_link_warning_errno(link, errno, "Could not find udev device: %m");
+ r = sd_device_new_from_device_id(&device, ifindex_str);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not find device: %m");
goto failed;
}
- if (udev_device_get_is_initialized(device) <= 0) {
+ r = sd_device_get_is_initialized(device);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not determine whether the device is initialized or not: %m");
+ goto failed;
+ }
+ if (r == 0) {
/* not yet ready */
log_link_debug(link, "link pending udev initialization...");
return 0;
@@ -3210,10 +3415,7 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) {
if (r < 0)
goto failed;
} else {
- /* we are calling a callback directly, so must take a ref */
- link_ref(link);
-
- r = link_initialized_and_synced(m->rtnl, NULL, link);
+ r = link_initialized_and_synced(link);
if (r < 0)
goto failed;
}
@@ -3257,7 +3459,7 @@ static int link_carrier_gained(Link *link) {
return r;
}
- r = link_enter_set_addresses(link);
+ r = link_request_set_addresses(link);
if (r < 0)
return r;
}
@@ -3275,8 +3477,8 @@ static int link_carrier_lost(Link *link) {
assert(link);
/* Some devices reset itself while setting the MTU. This causes the DHCP client fall into a loop.
- setting_mtu keep track whether the device got reset because of setting MTU and does not drop the
- configuration and stop the clients as well. */
+ * setting_mtu keep track whether the device got reset because of setting MTU and does not drop the
+ * configuration and stop the clients as well. */
if (link->setting_mtu)
return 0;
@@ -3339,9 +3541,8 @@ int link_update(Link *link, sd_netlink_message *m) {
assert(m);
if (link->state == LINK_STATE_LINGER) {
- link_ref(link);
log_link_info(link, "Link readded");
- link_set_state(link, LINK_STATE_ENSLAVING);
+ link_set_state(link, LINK_STATE_CONFIGURING);
r = link_new_carrier_maps(link);
if (r < 0)
@@ -3377,10 +3578,8 @@ int link_update(Link *link, sd_netlink_message *m) {
if (link->dhcp_client) {
r = sd_dhcp_client_set_mtu(link->dhcp_client,
link->mtu);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not update MTU in DHCP client: %m");
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MTU in DHCP client: %m");
}
if (link->radv) {
@@ -3423,45 +3622,13 @@ int link_update(Link *link, sd_netlink_message *m) {
if (r < 0)
return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m");
- switch (link->network->dhcp_client_identifier) {
- case DHCP_CLIENT_ID_DUID: {
- const DUID *duid = link_duid(link);
-
- r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
- link->network->iaid,
- duid->type,
- duid->raw_data_len > 0 ? duid->raw_data : NULL,
- duid->raw_data_len);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not update DUID/IAID in DHCP client: %m");
- break;
- }
- case DHCP_CLIENT_ID_DUID_ONLY: {
- const DUID *duid = link_duid(link);
-
- r = sd_dhcp_client_set_duid(link->dhcp_client,
- duid->type,
- duid->raw_data_len > 0 ? duid->raw_data : NULL,
- duid->raw_data_len);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not update DUID in DHCP client: %m");
- break;
- }
- case DHCP_CLIENT_ID_MAC:
- r = sd_dhcp_client_set_client_id(link->dhcp_client,
- ARPHRD_ETHER,
- (const uint8_t *)&link->mac,
- sizeof(link->mac));
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not update MAC client id in DHCP client: %m");
- break;
- default:
- assert_not_reached("Unknown client identifier type.");
- }
+ r = dhcp4_set_client_identifier(link);
+ if (r < 0)
+ return r;
}
if (link->dhcp6_client) {
- const DUID* duid = link_duid(link);
+ const DUID* duid = link_get_duid(link);
r = sd_dhcp6_client_set_mac(link->dhcp6_client,
(const uint8_t *) &link->mac,
@@ -3470,10 +3637,12 @@ int link_update(Link *link, sd_netlink_message *m) {
if (r < 0)
return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m");
- r = sd_dhcp6_client_set_iaid(link->dhcp6_client,
- link->network->iaid);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m");
+ if (link->network->iaid_set) {
+ r = sd_dhcp6_client_set_iaid(link->dhcp6_client,
+ link->network->iaid);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m");
+ }
r = sd_dhcp6_client_set_duid(link->dhcp6_client,
duid->type,
@@ -3760,6 +3929,8 @@ int link_save(Link *link) {
resolve_support_to_string(link->network->llmnr));
fprintf(f, "MDNS=%s\n",
resolve_support_to_string(link->network->mdns));
+ if (link->network->dns_default_route >= 0)
+ fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(link->network->dns_default_route));
if (link->network->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
fprintf(f, "DNS_OVER_TLS=%s\n",
@@ -3898,15 +4069,12 @@ void link_clean(Link *link) {
assert(link);
assert(link->manager);
- set_remove(link->manager->dirty_links, link);
- link_unref(link);
+ link_unref(set_remove(link->manager->dirty_links, link));
}
static const char* const link_state_table[_LINK_STATE_MAX] = {
[LINK_STATE_PENDING] = "pending",
- [LINK_STATE_ENSLAVING] = "configuring",
- [LINK_STATE_SETTING_ADDRESSES] = "configuring",
- [LINK_STATE_SETTING_ROUTES] = "configuring",
+ [LINK_STATE_CONFIGURING] = "configuring",
[LINK_STATE_CONFIGURED] = "configured",
[LINK_STATE_UNMANAGED] = "unmanaged",
[LINK_STATE_FAILED] = "failed",
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 7e22dfd504..00e68fdfaa 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -1,10 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <endian.h>
#include "sd-bus.h"
+#include "sd-device.h"
#include "sd-dhcp-client.h"
#include "sd-dhcp-server.h"
#include "sd-dhcp6-client.h"
@@ -19,9 +19,7 @@
typedef enum LinkState {
LINK_STATE_PENDING,
- LINK_STATE_ENSLAVING,
- LINK_STATE_SETTING_ADDRESSES,
- LINK_STATE_SETTING_ROUTES,
+ LINK_STATE_CONFIGURING,
LINK_STATE_CONFIGURED,
LINK_STATE_UNMANAGED,
LINK_STATE_FAILED,
@@ -44,13 +42,15 @@ typedef enum LinkOperationalState {
typedef struct Manager Manager;
typedef struct Network Network;
typedef struct Address Address;
+typedef struct DUID DUID;
typedef struct Link {
Manager *manager;
- int n_ref;
+ unsigned n_ref;
int ifindex;
+ int master_ifindex;
char *ifname;
char *kind;
unsigned short iftype;
@@ -58,7 +58,7 @@ typedef struct Link {
struct ether_addr mac;
struct in6_addr ipv6ll_address;
uint32_t mtu;
- struct udev_device *udev_device;
+ sd_device *sd_device;
unsigned flags;
uint8_t kernel_operstate;
@@ -70,6 +70,7 @@ typedef struct Link {
unsigned address_messages;
unsigned address_label_messages;
+ unsigned neighbor_messages;
unsigned route_messages;
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
@@ -80,6 +81,8 @@ typedef struct Link {
Set *routes;
Set *routes_foreign;
+ bool addresses_configured;
+
sd_dhcp_client *dhcp_client;
sd_dhcp_lease *dhcp_lease;
char *lease_file;
@@ -95,6 +98,8 @@ typedef struct Link {
bool ipv4ll_address:1;
bool ipv4ll_route:1;
+ bool neighbors_configured;
+
bool static_routes_configured;
bool routing_policy_rules_configured;
bool setting_mtu;
@@ -124,8 +129,15 @@ typedef struct Link {
Hashmap *bound_to_links;
} Link;
+typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*);
+
+DUID *link_get_duid(Link *link);
+int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
+
Link *link_unref(Link *link);
Link *link_ref(Link *link);
+DEFINE_TRIVIAL_DESTRUCTOR(link_netlink_destroy_callback, Link, link_unref);
+
int link_get(Manager *m, int ifindex, Link **ret);
int link_add(Manager *manager, sd_netlink_message *message, Link **ret);
void link_drop(Link *link);
@@ -133,11 +145,8 @@ void link_drop(Link *link);
int link_up(Link *link);
int link_down(Link *link);
-int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
-int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
-
void link_enter_failed(Link *link);
-int link_initialized(Link *link, struct udev_device *device);
+int link_initialized(Link *link, sd_device *device);
void link_check_ready(Link *link);
@@ -157,9 +166,12 @@ int link_set_mtu(Link *link, uint32_t mtu);
int ipv4ll_configure(Link *link);
int dhcp4_configure(Link *link);
+int dhcp4_set_client_identifier(Link *link);
int dhcp4_set_promote_secondaries(Link *link);
+int dhcp6_request_prefix_delegation(Link *link);
int dhcp6_configure(Link *link);
int dhcp6_request_address(Link *link, int ir);
+int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;
@@ -173,8 +185,6 @@ int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
int link_send_changed(Link *link, const char *property, ...) _sentinel_;
-DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
-
/* Macros which append INTERFACE= to the message */
#define log_link_full(link, level, error, ...) \
diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c
index 51fadc8125..8fd6365e68 100644
--- a/src/network/networkd-lldp-tx.c
+++ b/src/network/networkd-lldp-tx.c
@@ -5,9 +5,10 @@
#include <string.h>
#include "alloc-util.h"
+#include "env-file.h"
#include "fd-util.h"
-#include "fileio.h"
#include "hostname-util.h"
+#include "missing_network.h"
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "parse-util.h"
@@ -244,7 +245,7 @@ static int link_send_lldp(Link *link) {
return r;
(void) gethostname_strict(&hostname);
- (void) parse_env_file(NULL, "/etc/machine-info", NEWLINE, "PRETTY_HOSTNAME", &pretty_hostname, NULL);
+ (void) parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &pretty_hostname);
assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC);
ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC);
diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h
index 860065cc6d..6842804a7e 100644
--- a/src/network/networkd-lldp-tx.h
+++ b/src/network/networkd-lldp-tx.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include "conf-parser.h"
#include "networkd-link.h"
typedef enum LLDPEmit {
@@ -14,4 +15,4 @@ typedef enum LLDPEmit {
int link_lldp_emit_start(Link *link);
void link_lldp_emit_stop(Link *link);
-int config_parse_lldp_emit(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);
+CONFIG_PARSER_PROTOTYPE(config_parse_lldp_emit);
diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c
index 6e2c79bce1..8c527837f8 100644
--- a/src/network/networkd-manager-bus.c
+++ b/src/network/networkd-manager-bus.c
@@ -3,6 +3,7 @@
#include "alloc-util.h"
#include "bus-util.h"
#include "networkd-manager.h"
+#include "strv.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 1c4866077b..81c81f18af 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -12,17 +12,18 @@
#include "bus-util.h"
#include "conf-parser.h"
#include "def.h"
+#include "device-util.h"
#include "dns-domain.h"
#include "fd-util.h"
#include "fileio.h"
-#include "libudev-private.h"
#include "local-addresses.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "ordered-set.h"
#include "path-util.h"
#include "set.h"
-#include "udev-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
#include "virt.h"
/* use 8 MB for receive socket kernel queue. */
@@ -114,6 +115,8 @@ static int on_connected(sd_bus_message *message, void *userdata, sd_bus_error *r
(void) manager_set_hostname(m, m->dynamic_hostname);
if (m->dynamic_timezone)
(void) manager_set_timezone(m, m->dynamic_timezone);
+ if (m->links_requesting_uuid)
+ (void) manager_request_product_uuid(m, NULL);
return 0;
}
@@ -150,7 +153,7 @@ int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to add network enumerator: %m");
- r = bus_request_name_async_may_reload_dbus(m->bus, NULL, "org.freedesktop.network1", 0, NULL);
+ r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.network1", 0, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to request name: %m");
@@ -160,7 +163,7 @@ int manager_connect_bus(Manager *m) {
r = sd_bus_match_signal_async(
m->bus,
- &m->connected_slot,
+ NULL,
"org.freedesktop.DBus.Local",
NULL,
"org.freedesktop.DBus.Local",
@@ -171,7 +174,7 @@ int manager_connect_bus(Manager *m) {
r = sd_bus_match_signal_async(
m->bus,
- &m->prepare_for_sleep_slot,
+ NULL,
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
@@ -183,45 +186,40 @@ int manager_connect_bus(Manager *m) {
return 0;
}
-static int manager_udev_process_link(Manager *m, struct udev_device *device) {
+static int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata) {
+ Manager *m = userdata;
+ const char *action;
Link *link = NULL;
int r, ifindex;
assert(m);
assert(device);
- if (!streq_ptr(udev_device_get_action(device), "add"))
+ r = sd_device_get_property_value(device, "ACTION", &action);
+ if (r < 0) {
+ log_device_debug_errno(device, r, "Failed to get 'ACTION' property, ignoring device: %m");
return 0;
+ }
- ifindex = udev_device_get_ifindex(device);
- if (ifindex <= 0) {
- log_debug("Ignoring udev ADD event for device with invalid ifindex");
+ if (!STR_IN_SET(action, "add", "change")) {
+ log_device_debug(device, "Ignoring udev %s event for device.", action);
return 0;
}
- r = link_get(m, ifindex, &link);
- if (r == -ENODEV)
+ r = sd_device_get_ifindex(device, &ifindex);
+ if (r < 0) {
+ log_device_debug_errno(device, r, "Ignoring udev ADD event for device without ifindex or with invalid ifindex: %m");
return 0;
- else if (r < 0)
- return r;
-
- r = link_initialized(link, device);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
- Manager *m = userdata;
- struct udev_monitor *monitor = m->udev_monitor;
- _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
+ }
- device = udev_monitor_receive_device(monitor);
- if (!device)
- return -ENOMEM;
+ r = link_get(m, ifindex, &link);
+ if (r < 0) {
+ if (r != -ENODEV)
+ log_debug_errno(r, "Failed to get link from ifindex %i, ignoring: %m", ifindex);
+ return 0;
+ }
- (void) manager_udev_process_link(m, device);
+ (void) link_initialized(link, device);
return 0;
}
@@ -235,35 +233,21 @@ static int manager_connect_udev(Manager *m) {
if (detect_container() > 0)
return 0;
- m->udev = udev_new();
- if (!m->udev)
- return -ENOMEM;
-
- m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
- if (!m->udev_monitor)
- return -ENOMEM;
-
- r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL);
+ r = sd_device_monitor_new(&m->device_monitor);
if (r < 0)
- return log_error_errno(r, "Could not add udev monitor filter: %m");
+ return log_error_errno(r, "Failed to initialize device monitor: %m");
- r = udev_monitor_enable_receiving(m->udev_monitor);
- if (r < 0) {
- log_error("Could not enable udev monitor");
- return r;
- }
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "net", NULL);
+ if (r < 0)
+ return log_error_errno(r, "Could not add device monitor filter: %m");
- r = sd_event_add_io(m->event,
- &m->udev_event_source,
- udev_monitor_get_fd(m->udev_monitor),
- EPOLLIN, manager_dispatch_link_udev,
- m);
+ r = sd_device_monitor_attach_event(m->device_monitor, m->event);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to attach event to device monitor: %m");
- r = sd_event_source_set_description(m->udev_event_source, "networkd-udev");
+ r = sd_device_monitor_start(m->device_monitor, manager_udev_process_link, m);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to start device monitor: %m");
return 0;
}
@@ -714,11 +698,12 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
}
int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
- uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0;
+ uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0, protocol = 0;
+ struct fib_rule_port_range sport = {}, dport = {};
union in_addr_union to = {}, from = {};
RoutingPolicyRule *rule = NULL;
uint32_t fwmark = 0, table = 0;
- char *iif = NULL, *oif = NULL;
+ const char *iif = NULL, *oif = NULL;
Manager *m = userdata;
uint16_t type;
int family;
@@ -834,24 +819,42 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
return 0;
}
- r = sd_netlink_message_read_string(message, FRA_IIFNAME, (const char **) &iif);
+ r = sd_netlink_message_read_string(message, FRA_IIFNAME, &iif);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_IIFNAME attribute, ignoring: %m");
return 0;
}
- r = sd_netlink_message_read_string(message, FRA_OIFNAME, (const char **) &oif);
+ r = sd_netlink_message_read_string(message, FRA_OIFNAME, &oif);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_OIFNAME attribute, ignoring: %m");
return 0;
}
- (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule);
+ r = sd_netlink_message_read_u8(message, FRA_IP_PROTO, &protocol);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_IP_PROTO attribute, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(sport), (void *) &sport);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_SPORT_RANGE attribute, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read(message, FRA_DPORT_RANGE, sizeof(dport), (void *) &dport);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_DPORT_RANGE attribute, ignoring: %m");
+ return 0;
+ }
+
+ (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, &sport, &dport, &rule);
switch (type) {
case RTM_NEWRULE:
if (!rule) {
- r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule);
+ r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, &sport, &dport, &rule);
if (r < 0) {
log_warning_errno(r, "Could not add rule, ignoring: %m");
return 0;
@@ -930,35 +933,35 @@ static int manager_connect_rtnl(Manager *m) {
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWLINK, &manager_rtnl_process_link, NULL, m, "network-rtnl_process_link");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, &manager_rtnl_process_link, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELLINK, &manager_rtnl_process_link, NULL, m, "network-rtnl_process_link");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, &manager_rtnl_process_address, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &manager_rtnl_process_address, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_NEWROUTE, &manager_rtnl_process_route, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWROUTE, &manager_rtnl_process_route, NULL, m, "network-rtnl_process_route");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_DELROUTE, &manager_rtnl_process_route, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELROUTE, &manager_rtnl_process_route, NULL, m, "network-rtnl_process_route");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_NEWRULE, &manager_rtnl_process_rule, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWRULE, &manager_rtnl_process_rule, NULL, m, "network-rtnl_process_rule");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_DELRULE, &manager_rtnl_process_rule, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELRULE, &manager_rtnl_process_rule, NULL, m, "network-rtnl_process_rule");
if (r < 0)
return r;
@@ -1215,62 +1218,56 @@ static int manager_dirty_handler(sd_event_source *s, void *userdata) {
Manager *m = userdata;
Link *link;
Iterator i;
- int r;
assert(m);
if (m->dirty)
manager_save(m);
- SET_FOREACH(link, m->dirty_links, i) {
- r = link_save(link);
- if (r >= 0)
+ SET_FOREACH(link, m->dirty_links, i)
+ if (link_save(link) >= 0)
link_clean(link);
- }
return 1;
}
Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
assert_return(m, NULL);
- assert_return(m->dhcp6_prefixes, NULL);
assert_return(addr, NULL);
return hashmap_get(m->dhcp6_prefixes, addr);
}
-static int dhcp6_route_add_callback(sd_netlink *nl, sd_netlink_message *m,
- void *userdata) {
- Link *l = userdata;
+static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
int r;
- union in_addr_union prefix;
- _cleanup_free_ char *buf = NULL;
+
+ assert(link);
r = sd_netlink_message_get_errno(m);
- if (r != 0) {
- log_link_debug_errno(l, r, "Received error adding DHCPv6 Prefix Delegation route: %m");
- return 0;
- }
+ if (r < 0 && r != -EEXIST)
+ log_link_debug_errno(link, r, "Received error adding DHCPv6 Prefix Delegation route: %m");
- r = sd_netlink_message_read_in6_addr(m, RTA_DST, &prefix.in6);
- if (r < 0) {
- log_link_debug_errno(l, r, "Could not read IPv6 address from DHCPv6 Prefix Delegation while adding route: %m");
- return 0;
- }
+ return 0;
+}
- (void) in_addr_to_string(AF_INET6, &prefix, &buf);
- log_link_debug(l, "Added DHCPv6 Prefix Deleagtion route %s/64",
- strnull(buf));
+static void dhcp6_prefixes_hash_func(const struct in6_addr *addr, struct siphash *state) {
+ assert(addr);
- return 0;
+ siphash24_compress(addr, sizeof(*addr), state);
}
+static int dhcp6_prefixes_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
+ return memcmp(a, b, sizeof(*a));
+}
+
+DEFINE_PRIVATE_HASH_OPS(dhcp6_prefixes_hash_ops, struct in6_addr, dhcp6_prefixes_hash_func, dhcp6_prefixes_compare_func);
+
int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
- int r;
+ _cleanup_free_ char *buf = NULL;
Route *route;
+ int r;
assert_return(m, -EINVAL);
- assert_return(m->dhcp6_prefixes, -ENODATA);
assert_return(addr, -EINVAL);
r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64,
@@ -1278,46 +1275,39 @@ int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
if (r < 0)
return r;
- r = route_configure(route, link, dhcp6_route_add_callback);
+ r = route_configure(route, link, dhcp6_route_add_handler);
+ if (r < 0)
+ return r;
+
+ (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
+ log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
+
+ r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &dhcp6_prefixes_hash_ops);
if (r < 0)
return r;
return hashmap_put(m->dhcp6_prefixes, addr, link);
}
-static int dhcp6_route_remove_callback(sd_netlink *nl, sd_netlink_message *m,
- void *userdata) {
- Link *l = userdata;
+static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
int r;
- union in_addr_union prefix;
- _cleanup_free_ char *buf = NULL;
- r = sd_netlink_message_get_errno(m);
- if (r != 0) {
- log_link_debug_errno(l, r, "Received error on DHCPv6 Prefix Delegation route removal: %m");
- return 0;
- }
+ assert(link);
- r = sd_netlink_message_read_in6_addr(m, RTA_DST, &prefix.in6);
- if (r < 0) {
- log_link_debug_errno(l, r, "Could not read IPv6 address from DHCPv6 Prefix Delegation while removing route: %m");
- return 0;
- }
-
- (void) in_addr_to_string(AF_INET6, &prefix, &buf);
- log_link_debug(l, "Removed DHCPv6 Prefix Delegation route %s/64",
- strnull(buf));
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0)
+ log_link_debug_errno(link, r, "Received error on DHCPv6 Prefix Delegation route removal: %m");
- return 0;
+ return 1;
}
-int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
+static int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
+ _cleanup_free_ char *buf = NULL;
+ Route *route;
Link *l;
int r;
- Route *route;
assert_return(m, -EINVAL);
- assert_return(m->dhcp6_prefixes, -ENODATA);
assert_return(addr, -EINVAL);
l = hashmap_remove(m->dhcp6_prefixes, addr);
@@ -1327,16 +1317,23 @@ int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
(void) sd_radv_remove_prefix(l->radv, addr, 64);
r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64,
0, 0, 0, &route);
- if (r >= 0)
- (void) route_remove(route, l, dhcp6_route_remove_callback);
+ if (r < 0)
+ return r;
+
+ r = route_remove(route, l, dhcp6_route_remove_handler);
+ if (r < 0)
+ return r;
+
+ (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
+ log_link_debug(l, "Removing prefix route %s/64", strnull(buf));
return 0;
}
int manager_dhcp6_prefix_remove_all(Manager *m, Link *link) {
+ struct in6_addr *addr;
Iterator i;
Link *l;
- struct in6_addr *addr;
assert_return(m, -EINVAL);
assert_return(link, -EINVAL);
@@ -1351,26 +1348,7 @@ int manager_dhcp6_prefix_remove_all(Manager *m, Link *link) {
return 0;
}
-static void dhcp6_prefixes_hash_func(const void *p, struct siphash *state) {
- const struct in6_addr *addr = p;
-
- assert(p);
-
- siphash24_compress(addr, sizeof(*addr), state);
-}
-
-static int dhcp6_prefixes_compare_func(const void *_a, const void *_b) {
- const struct in6_addr *a = _a, *b = _b;
-
- return memcmp(a, b, sizeof(*a));
-}
-
-static const struct hash_ops dhcp6_prefixes_hash_ops = {
- .hash = dhcp6_prefixes_hash_func,
- .compare = dhcp6_prefixes_compare_func,
-};
-
-int manager_new(Manager **ret, sd_event *event) {
+int manager_new(Manager **ret) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
@@ -1382,7 +1360,13 @@ int manager_new(Manager **ret, sd_event *event) {
if (!m->state_file)
return -ENOMEM;
- m->event = sd_event_ref(event);
+ r = sd_event_default(&m->event);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_set_watchdog(m->event, true);
+ (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
+ (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
r = sd_event_add_post(m->event, NULL, manager_dirty_handler, m);
if (r < 0)
@@ -1400,10 +1384,6 @@ int manager_new(Manager **ret, sd_event *event) {
if (r < 0)
return r;
- m->netdevs = hashmap_new(&string_hash_ops);
- if (!m->netdevs)
- return -ENOMEM;
-
LIST_HEAD_INIT(m->networks);
r = sd_resolve_default(&m->resolve);
@@ -1418,10 +1398,6 @@ int manager_new(Manager **ret, sd_event *event) {
if (r < 0)
return r;
- m->dhcp6_prefixes = hashmap_new(&dhcp6_prefixes_hash_ops);
- if (!m->dhcp6_prefixes)
- return -ENOMEM;
-
m->duid.type = DUID_TYPE_EN;
(void) routing_policy_load_rules(m->state_file, &m->rules_saved);
@@ -1432,53 +1408,55 @@ int manager_new(Manager **ret, sd_event *event) {
}
void manager_free(Manager *m) {
+ AddressPool *pool;
Network *network;
- NetDev *netdev;
Link *link;
- AddressPool *pool;
if (!m)
return;
free(m->state_file);
+ sd_netlink_unref(m->rtnl);
+ sd_netlink_unref(m->genl);
+ sd_resolve_unref(m->resolve);
+
while ((network = m->networks))
network_free(network);
while ((link = hashmap_first(m->dhcp6_prefixes)))
- link_unref(link);
+ manager_dhcp6_prefix_remove_all(m, link);
hashmap_free(m->dhcp6_prefixes);
- while ((link = hashmap_first(m->links)))
+ while ((link = hashmap_steal_first(m->links))) {
+ if (link->dhcp6_client)
+ (void) dhcp6_lease_pd_prefix_lost(link->dhcp6_client, link);
link_unref(link);
- hashmap_free(m->links);
+ }
+
+ m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
+ m->links = hashmap_free(m->links);
+ m->links_requesting_uuid = set_free(m->links_requesting_uuid);
+ set_free(m->duids_requesting_uuid);
hashmap_free(m->networks_by_name);
- while ((netdev = hashmap_first(m->netdevs)))
- netdev_unref(netdev);
- hashmap_free(m->netdevs);
+ m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
while ((pool = m->address_pools))
address_pool_free(pool);
- set_free(m->rules);
- set_free(m->rules_foreign);
-
+ /* routing_policy_rule_free() access m->rules and m->rules_foreign.
+ * So, it is necessary to set NULL after the sets are freed. */
+ m->rules = set_free_with_destructor(m->rules, routing_policy_rule_free);
+ m->rules_foreign = set_free_with_destructor(m->rules_foreign, routing_policy_rule_free);
set_free_with_destructor(m->rules_saved, routing_policy_rule_free);
- sd_netlink_unref(m->rtnl);
sd_event_unref(m->event);
- sd_resolve_unref(m->resolve);
-
- sd_event_source_unref(m->udev_event_source);
- udev_monitor_unref(m->udev_monitor);
- udev_unref(m->udev);
+ sd_device_monitor_unref(m->device_monitor);
sd_bus_unref(m->bus);
- sd_bus_slot_unref(m->prepare_for_sleep_slot);
- sd_bus_slot_unref(m->connected_slot);
free(m->dynamic_timezone);
free(m->dynamic_hostname);
@@ -1757,7 +1735,7 @@ int manager_set_hostname(Manager *m, const char *hostname) {
return log_oom();
if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
- log_info("Not connected to system bus, not setting hostname.");
+ log_debug("Not connected to system bus, setting hostname later.");
return 0;
}
@@ -1805,7 +1783,7 @@ int manager_set_timezone(Manager *m, const char *tz) {
return log_oom();
if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
- log_info("Not connected to system bus, not setting timezone.");
+ log_debug("Not connected to system bus, setting timezone later.");
return 0;
}
@@ -1826,3 +1804,57 @@ int manager_set_timezone(Manager *m, const char *tz) {
return 0;
}
+
+int manager_request_product_uuid(Manager *m, Link *link) {
+ int r;
+
+ assert(m);
+
+ if (m->has_product_uuid)
+ return 0;
+
+ log_debug("Requesting product UUID");
+
+ if (link) {
+ DUID *duid;
+
+ assert_se(duid = link_get_duid(link));
+
+ r = set_ensure_allocated(&m->links_requesting_uuid, NULL);
+ if (r < 0)
+ return log_oom();
+
+ r = set_ensure_allocated(&m->duids_requesting_uuid, NULL);
+ if (r < 0)
+ return log_oom();
+
+ r = set_put(m->links_requesting_uuid, link);
+ if (r < 0)
+ return log_oom();
+
+ r = set_put(m->duids_requesting_uuid, duid);
+ if (r < 0)
+ return log_oom();
+ }
+
+ if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
+ log_debug("Not connected to system bus, requesting product UUID later.");
+ return 0;
+ }
+
+ r = sd_bus_call_method_async(
+ m->bus,
+ NULL,
+ "org.freedesktop.hostname1",
+ "/org/freedesktop/hostname1",
+ "org.freedesktop.hostname1",
+ "GetProductUUID",
+ get_product_uuid_handler,
+ m,
+ "b",
+ false);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to get product UUID: %m");
+
+ return 0;
+}
diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h
index fbb7f5413a..289ca96216 100644
--- a/src/network/networkd-manager.h
+++ b/src/network/networkd-manager.h
@@ -1,14 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <arpa/inet.h>
#include "sd-bus.h"
+#include "sd-device.h"
#include "sd-event.h"
+#include "sd-id128.h"
#include "sd-netlink.h"
#include "sd-resolve.h"
-#include "udev.h"
#include "dhcp-identifier.h"
#include "hashmap.h"
@@ -27,11 +27,7 @@ struct Manager {
sd_event *event;
sd_resolve *resolve;
sd_bus *bus;
- sd_bus_slot *prepare_for_sleep_slot;
- sd_bus_slot *connected_slot;
- struct udev *udev;
- struct udev_monitor *udev_monitor;
- sd_event_source *udev_event_source;
+ sd_device_monitor *device_monitor;
bool enumerating:1;
bool dirty:1;
@@ -51,6 +47,11 @@ struct Manager {
usec_t network_dirs_ts_usec;
DUID duid;
+ sd_id128_t product_uuid;
+ bool has_product_uuid;
+ Set *links_requesting_uuid;
+ Set *duids_requesting_uuid;
+
char* dynamic_hostname;
char* dynamic_timezone;
@@ -59,16 +60,9 @@ struct Manager {
Set *rules_saved;
};
-static inline const DUID* link_duid(const Link *link) {
- if (link->network->duid.type != _DUID_TYPE_INVALID)
- return &link->network->duid;
- else
- return &link->manager->duid;
-}
-
extern const sd_bus_vtable manager_vtable[];
-int manager_new(Manager **ret, sd_event *event);
+int manager_new(Manager **ret);
void manager_free(Manager *m);
int manager_connect_bus(Manager *m);
@@ -95,10 +89,10 @@ Link* manager_find_uplink(Manager *m, Link *exclude);
int manager_set_hostname(Manager *m, const char *hostname);
int manager_set_timezone(Manager *m, const char *timezone);
+int manager_request_product_uuid(Manager *m, Link *link);
Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
-int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr);
int manager_dhcp6_prefix_remove_all(Manager *m, Link *link);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index 8284e3a299..80bfd2cba1 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -10,13 +10,13 @@
#include "networkd-ndisc.h"
#include "networkd-route.h"
+#include "strv.h"
#define NDISC_DNSSL_MAX 64U
#define NDISC_RDNSS_MAX 64U
#define NDISC_PREFIX_LFT_MIN 7200U
-static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int ndisc_netlink_message_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -36,7 +36,7 @@ static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *
return 1;
}
-static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
+static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
struct in6_addr gateway;
uint16_t lifetime;
@@ -51,70 +51,55 @@ static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
assert(rt);
r = sd_ndisc_router_get_lifetime(rt, &lifetime);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
+
if (lifetime == 0) /* not a default router */
- return;
+ return 0;
r = sd_ndisc_router_get_address(rt, &gateway);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
- SET_FOREACH(address, link->addresses, i) {
- if (!memcmp(&gateway, &address->in_addr.in6,
- sizeof(address->in_addr.in6))) {
+ SET_FOREACH(address, link->addresses, i)
+ if (!memcmp(&gateway, &address->in_addr.in6, sizeof(address->in_addr.in6))) {
char buffer[INET6_ADDRSTRLEN];
log_link_debug(link, "No NDisc route added, gateway %s matches local address",
inet_ntop(AF_INET6,
&address->in_addr.in6,
buffer, sizeof(buffer)));
- return;
+ return 0;
}
- }
- SET_FOREACH(address, link->addresses_foreign, i) {
- if (!memcmp(&gateway, &address->in_addr.in6,
- sizeof(address->in_addr.in6))) {
+ SET_FOREACH(address, link->addresses_foreign, i)
+ if (!memcmp(&gateway, &address->in_addr.in6, sizeof(address->in_addr.in6))) {
char buffer[INET6_ADDRSTRLEN];
log_link_debug(link, "No NDisc route added, gateway %s matches local address",
inet_ntop(AF_INET6,
&address->in_addr.in6,
buffer, sizeof(buffer)));
- return;
+ return 0;
}
- }
r = sd_ndisc_router_get_preference(rt, &preference);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
r = sd_ndisc_router_get_mtu(rt, &mtu);
if (r == -ENODATA)
mtu = 0;
- else if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get default router MTU from RA: %m");
- return;
- }
+ else if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get default router MTU from RA: %m");
r = route_new(&route);
- if (r < 0) {
- log_link_error_errno(link, r, "Could not allocate route: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
route->family = AF_INET6;
route->table = link->network->ipv6_accept_ra_route_table;
@@ -125,17 +110,19 @@ static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
route->lifetime = time_now + lifetime * USEC_PER_SEC;
route->mtu = mtu;
- r = route_configure(route, link, ndisc_netlink_handler);
+ r = route_configure(route, link, ndisc_netlink_message_handler);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set default route: %m");
link_enter_failed(link);
- return;
+ return r;
}
link->ndisc_messages++;
+
+ return 0;
}
-static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
+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;
@@ -147,45 +134,33 @@ static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *
assert(rt);
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
- if (r < 0) {
- log_link_error_errno(link, r, "Failed to get prefix length: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix length: %m");
r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid);
- if (r < 0) {
- log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m");
r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred);
- if (r < 0) {
- log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m");
/* The preferred lifetime is never greater than the valid lifetime */
if (lifetime_preferred > lifetime_valid)
- return;
+ return 0;
r = address_new(&address);
- if (r < 0) {
- log_link_error_errno(link, r, "Could not allocate address: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate address: %m");
address->family = AF_INET6;
r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6);
- if (r < 0) {
- log_link_error_errno(link, r, "Failed to get prefix address: %m");
- return;
- }
+ 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);
@@ -218,22 +193,24 @@ static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *
} else if (lifetime_valid > 0)
address->cinfo.ifa_valid = lifetime_valid;
else
- return; /* see RFC4862 section 5.5.3.d */
+ return 0; /* see RFC4862 section 5.5.3.d */
if (address->cinfo.ifa_valid == 0)
- return;
+ return 0;
- r = address_configure(address, link, ndisc_netlink_handler, true);
+ r = address_configure(address, link, ndisc_netlink_message_handler, true);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
link_enter_failed(link);
- return;
+ return r;
}
link->ndisc_messages++;
+
+ return 0;
}
-static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
+static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
usec_t time_now;
uint32_t lifetime;
@@ -244,28 +221,20 @@ static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt)
assert(rt);
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
- if (r < 0) {
- log_link_error_errno(link, r, "Failed to get prefix length: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix length: %m");
r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime);
- if (r < 0) {
- log_link_error_errno(link, r, "Failed to get prefix lifetime: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix lifetime: %m");
r = route_new(&route);
- if (r < 0) {
- log_link_error_errno(link, r, "Could not allocate route: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
route->family = AF_INET6;
route->table = link->network->ipv6_accept_ra_route_table;
@@ -276,22 +245,22 @@ static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt)
route->lifetime = time_now + lifetime * USEC_PER_SEC;
r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6);
- if (r < 0) {
- log_link_error_errno(link, r, "Failed to get prefix address: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix address: %m");
- r = route_configure(route, link, ndisc_netlink_handler);
+ r = route_configure(route, link, ndisc_netlink_message_handler);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set prefix route: %m");
link_enter_failed(link);
- return;
+ return r;
}
link->ndisc_messages++;
+
+ return 0;
}
-static void ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
+static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
struct in6_addr gateway;
uint32_t lifetime;
@@ -302,42 +271,31 @@ static void ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
assert(link);
r = sd_ndisc_router_route_get_lifetime(rt, &lifetime);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
+
if (lifetime == 0)
- return;
+ return 0;
r = sd_ndisc_router_get_address(rt, &gateway);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
r = sd_ndisc_router_route_get_preference(rt, &preference);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
r = route_new(&route);
- if (r < 0) {
- log_link_error_errno(link, r, "Could not allocate route: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
route->family = AF_INET6;
route->table = link->network->ipv6_accept_ra_route_table;
@@ -348,39 +306,32 @@ static void ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
route->lifetime = time_now + lifetime * USEC_PER_SEC;
r = sd_ndisc_router_route_get_address(rt, &route->dst.in6);
- if (r < 0) {
- log_link_error_errno(link, r, "Failed to get route address: %m");
- return;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get route address: %m");
- r = route_configure(route, link, ndisc_netlink_handler);
+ r = route_configure(route, link, ndisc_netlink_message_handler);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set additional route: %m");
link_enter_failed(link);
- return;
+ return r;
}
link->ndisc_messages++;
-}
-static void ndisc_rdnss_hash_func(const void *p, struct siphash *state) {
- const NDiscRDNSS *x = p;
+ return 0;
+}
+static void ndisc_rdnss_hash_func(const NDiscRDNSS *x, struct siphash *state) {
siphash24_compress(&x->address, sizeof(x->address), state);
}
-static int ndisc_rdnss_compare_func(const void *_a, const void *_b) {
- const NDiscRDNSS *a = _a, *b = _b;
-
+static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) {
return memcmp(&a->address, &b->address, sizeof(a->address));
}
-static const struct hash_ops ndisc_rdnss_hash_ops = {
- .hash = ndisc_rdnss_hash_func,
- .compare = ndisc_rdnss_compare_func
-};
+DEFINE_PRIVATE_HASH_OPS(ndisc_rdnss_hash_ops, NDiscRDNSS, ndisc_rdnss_hash_func, ndisc_rdnss_compare_func);
-static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
+static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
uint32_t lifetime;
const struct in6_addr *a;
usec_t time_now;
@@ -390,27 +341,22 @@ static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
assert(rt);
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
- return;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
- if (n < 0) {
- log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
- return;
- }
+ if (n < 0)
+ return log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
for (i = 0; i < n; i++) {
+ _cleanup_free_ NDiscRDNSS *x = NULL;
NDiscRDNSS d = {
- .address = a[i]
- }, *x;
+ .address = a[i],
+ }, *y;
if (lifetime == 0) {
(void) set_remove(link->ndisc_rdnss, &d);
@@ -418,9 +364,9 @@ static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
continue;
}
- x = set_get(link->ndisc_rdnss, &d);
- if (x) {
- x->valid_until = time_now + lifetime * USEC_PER_SEC;
+ y = set_get(link->ndisc_rdnss, &d);
+ if (y) {
+ y->valid_until = time_now + lifetime * USEC_PER_SEC;
continue;
}
@@ -432,48 +378,40 @@ static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
}
r = set_ensure_allocated(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops);
- if (r < 0) {
- log_oom();
- return;
- }
+ if (r < 0)
+ return log_oom();
- x = new0(NDiscRDNSS, 1);
- if (!x) {
- log_oom();
- return;
- }
+ x = new(NDiscRDNSS, 1);
+ if (!x)
+ return log_oom();
- x->address = a[i];
- x->valid_until = time_now + lifetime * USEC_PER_SEC;
+ *x = (NDiscRDNSS) {
+ .address = a[i],
+ .valid_until = time_now + lifetime * USEC_PER_SEC,
+ };
r = set_put(link->ndisc_rdnss, x);
- if (r < 0) {
- free(x);
- log_oom();
- return;
- }
+ if (r < 0)
+ return log_oom();
+
+ TAKE_PTR(x);
assert(r > 0);
link_dirty(link);
}
-}
-static void ndisc_dnssl_hash_func(const void *p, struct siphash *state) {
- const NDiscDNSSL *x = p;
+ return 0;
+}
+static void ndisc_dnssl_hash_func(const NDiscDNSSL *x, struct siphash *state) {
siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state);
}
-static int ndisc_dnssl_compare_func(const void *_a, const void *_b) {
- const NDiscDNSSL *a = _a, *b = _b;
-
+static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) {
return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
}
-static const struct hash_ops ndisc_dnssl_hash_ops = {
- .hash = ndisc_dnssl_hash_func,
- .compare = ndisc_dnssl_compare_func
-};
+DEFINE_PRIVATE_HASH_OPS(ndisc_dnssl_hash_ops, NDiscDNSSL, ndisc_dnssl_hash_func, ndisc_dnssl_compare_func);
static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
_cleanup_strv_free_ char **l = NULL;
@@ -589,25 +527,25 @@ static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
}
if (flags & ND_OPT_PI_FLAG_ONLINK)
- ndisc_router_process_onlink_prefix(link, rt);
+ (void) ndisc_router_process_onlink_prefix(link, rt);
if (flags & ND_OPT_PI_FLAG_AUTO)
- ndisc_router_process_autonomous_prefix(link, rt);
+ (void) ndisc_router_process_autonomous_prefix(link, rt);
break;
}
case SD_NDISC_OPTION_ROUTE_INFORMATION:
- ndisc_router_process_route(link, rt);
+ (void) ndisc_router_process_route(link, rt);
break;
case SD_NDISC_OPTION_RDNSS:
if (link->network->ipv6_accept_ra_use_dns)
- ndisc_router_process_rdnss(link, rt);
+ (void) ndisc_router_process_rdnss(link, rt);
break;
case SD_NDISC_OPTION_DNSSL:
if (link->network->ipv6_accept_ra_use_dns)
- ndisc_router_process_dnssl(link, rt);
+ (void) ndisc_router_process_dnssl(link, rt);
break;
}
@@ -615,9 +553,9 @@ static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
}
}
-static void ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
+static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
uint64_t flags;
- int r;
+ int r = 0;
assert(link);
assert(link->network);
@@ -625,22 +563,24 @@ static void ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
assert(rt);
r = sd_ndisc_router_get_flags(rt, &flags);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to get RA flags: %m");
- return;
- }
+ 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)) {
/* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
if (r < 0 && r != -EBUSY)
log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
- else
+ else {
log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
+ r = 0;
+ }
}
ndisc_router_process_default(link, rt);
ndisc_router_process_options(link, rt);
+
+ return r;
}
static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) {
@@ -654,7 +594,7 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *r
switch (event) {
case SD_NDISC_EVENT_ROUTER:
- ndisc_router_handler(link, rt);
+ (void) ndisc_router_handler(link, rt);
break;
case SD_NDISC_EVENT_TIMEOUT:
diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h
index 351fc695c6..762f42d43e 100644
--- a/src/network/networkd-ndisc.h
+++ b/src/network/networkd-ndisc.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "networkd-link.h"
typedef struct NDiscRDNSS {
diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c
new file mode 100644
index 0000000000..254a60bdc3
--- /dev/null
+++ b/src/network/networkd-neighbor.c
@@ -0,0 +1,237 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "sd-netlink.h"
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "ether-addr-util.h"
+#include "hashmap.h"
+#include "in-addr-util.h"
+#include "netlink-util.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "networkd-neighbor.h"
+
+void neighbor_free(Neighbor *neighbor) {
+ if (!neighbor)
+ return;
+
+ if (neighbor->network) {
+ LIST_REMOVE(neighbors, neighbor->network->neighbors, neighbor);
+ assert(neighbor->network->n_neighbors > 0);
+ neighbor->network->n_neighbors--;
+
+ if (neighbor->section) {
+ hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
+ network_config_section_free(neighbor->section);
+ }
+ }
+
+ free(neighbor);
+}
+
+static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) {
+ _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
+ int r;
+
+ assert(network);
+ assert(ret);
+ assert(!!filename == (section_line > 0));
+
+ if (filename) {
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ neighbor = hashmap_get(network->neighbors_by_section, n);
+ if (neighbor) {
+ *ret = TAKE_PTR(neighbor);
+
+ return 0;
+ }
+ }
+
+ neighbor = new(Neighbor, 1);
+ if (!neighbor)
+ return -ENOMEM;
+
+ *neighbor = (Neighbor) {
+ .network = network,
+ .family = AF_UNSPEC,
+ };
+
+ LIST_APPEND(neighbors, network->neighbors, neighbor);
+ network->n_neighbors++;
+
+ if (filename) {
+ neighbor->section = TAKE_PTR(n);
+
+ r = hashmap_ensure_allocated(&network->neighbors_by_section, &network_config_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(neighbor);
+
+ return 0;
+}
+
+static int neighbor_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(link);
+ assert(link->neighbor_messages > 0);
+
+ link->neighbor_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_warning_errno(link, r, "Could not set neighbor: %m");
+
+ if (link->neighbor_messages == 0) {
+ log_link_debug(link, "Neighbors set");
+ link->neighbors_configured = true;
+ link_check_ready(link);
+ }
+
+ return 1;
+}
+
+int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ int r;
+
+ assert(neighbor);
+ assert(link);
+ assert(link->ifindex > 0);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+
+ if (neighbor->family == AF_UNSPEC)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Neighbor without Address= configured");
+ if (!neighbor->mac_configured)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Neighbor without MACAddress= configured");
+
+ r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH,
+ link->ifindex, neighbor->family);
+ if (r < 0)
+ return log_error_errno(r, "Could not allocate RTM_NEWNEIGH message: %m");
+
+ r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT);
+ if (r < 0)
+ return log_error_errno(r, "Could not set state: %m");
+
+ r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE);
+ if (r < 0)
+ return log_error_errno(r, "Could not set flags: %m");
+
+ r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, &neighbor->mac);
+ if (r < 0)
+ return log_error_errno(r, "Could not append NDA_LLADDR attribute: %m");
+
+ switch (neighbor->family) {
+ case AF_INET6:
+ r = sd_netlink_message_append_in6_addr(req, NDA_DST, &neighbor->in_addr.in6);
+ if (r < 0)
+ return log_error_errno(r, "Could not append NDA_DST attribute: %m");
+ break;
+ case AF_INET:
+ r = sd_netlink_message_append_in_addr(req, NDA_DST, &neighbor->in_addr.in);
+ if (r < 0)
+ return log_error_errno(r, "Could not append NDA_DST attribute: %m");
+ break;
+ default:
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Neighbor with invalid address family");
+ }
+
+ r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_handler,
+ link_netlink_destroy_callback, link);
+ if (r < 0)
+ return log_error_errno(r, "Could not send rtnetlink message: %m");
+
+ link->neighbor_messages++;
+ link_ref(link);
+
+ return 0;
+}
+
+int config_parse_neighbor_address(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ _cleanup_(neighbor_freep) Neighbor *n = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = neighbor_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor Address is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(n);
+
+ return 0;
+}
+
+int config_parse_neighbor_hwaddr(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_(neighbor_freep) Neighbor *n = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = neighbor_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = ether_addr_from_string(rvalue, &n->mac);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor MACAddress is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ n->mac_configured = true;
+ TAKE_PTR(n);
+
+ return 0;
+}
diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h
new file mode 100644
index 0000000000..094bf7977e
--- /dev/null
+++ b/src/network/networkd-neighbor.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-netlink.h"
+
+#include "conf-parser.h"
+#include "ether-addr-util.h"
+#include "in-addr-util.h"
+#include "list.h"
+#include "macro.h"
+
+typedef struct Neighbor Neighbor;
+
+#include "networkd-link.h"
+#include "networkd-network.h"
+
+struct Neighbor {
+ Network *network;
+ Link *link;
+ NetworkConfigSection *section;
+
+ int family;
+ union in_addr_union in_addr;
+ bool mac_configured;
+ struct ether_addr mac;
+
+ LIST_FIELDS(Neighbor, neighbors);
+};
+
+void neighbor_free(Neighbor *neighbor);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Neighbor*, neighbor_free);
+
+int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_hwaddr);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 6ad5257f79..5d8aede593 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -38,8 +38,8 @@ Link.AllMulticast, config_parse_tristate,
Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
Link.RequiredForOnline, config_parse_bool, 0, offsetof(Network, required_for_online)
Network.Description, config_parse_string, 0, offsetof(Network, description)
-Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge)
-Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
+Network.Bridge, config_parse_netdev, 0, 0
+Network.Bond, config_parse_netdev, 0, 0
Network.VLAN, config_parse_netdev, 0, 0
Network.MACVLAN, config_parse_netdev, 0, 0
Network.MACVTAP, config_parse_netdev, 0, 0
@@ -58,6 +58,7 @@ Network.Address, config_parse_address,
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)
@@ -72,7 +73,7 @@ Network.IPv6AcceptRouterAdvertisements, config_parse_tristate,
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, 0
+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)
@@ -93,6 +94,8 @@ Address.AutoJoin, config_parse_address_flags,
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.MACAddress, config_parse_neighbor_hwaddr, 0, 0
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
@@ -101,6 +104,10 @@ RoutingPolicyRule.From, config_parse_routing_policy_rule_prefix,
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
Route.Gateway, config_parse_gateway, 0, 0
Route.Destination, config_parse_destination, 0, 0
Route.Source, config_parse_destination, 0, 0
@@ -130,14 +137,15 @@ DHCP.RequestBroadcast, config_parse_bool,
DHCP.CriticalConnection, config_parse_bool, 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.type)
+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_dhcp_route_table, 0, 0
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
-DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
+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)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.RouteTable, config_parse_uint32, 0, offsetof(Network, ipv6_accept_ra_route_table)
@@ -158,6 +166,7 @@ Bridge.HairPin, config_parse_tristate,
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.MulticastToUnicast, config_parse_tristate, 0, offsetof(Network, multicast_to_unicast)
Bridge.Priority, config_parse_bridge_port_priority, 0, offsetof(Network, priority)
BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0
BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 429aac5e6c..ccc1c3ce89 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -10,6 +10,7 @@
#include "fd-util.h"
#include "hostname-util.h"
#include "in-addr-util.h"
+#include "missing_network.h"
#include "network-internal.h"
#include "networkd-manager.h"
#include "networkd-network.h"
@@ -21,28 +22,22 @@
#include "strv.h"
#include "util.h"
-static void network_config_hash_func(const void *p, struct siphash *state) {
- const NetworkConfigSection *c = p;
-
+static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) {
siphash24_compress(c->filename, strlen(c->filename), state);
siphash24_compress(&c->line, sizeof(c->line), state);
}
-static int network_config_compare_func(const void *a, const void *b) {
- const NetworkConfigSection *x = a, *y = b;
+static int network_config_compare_func(const NetworkConfigSection *x, const NetworkConfigSection *y) {
int r;
r = strcmp(x->filename, y->filename);
if (r != 0)
return r;
- return y->line - x->line;
+ return CMP(x->line, y->line);
}
-const struct hash_ops network_config_hash_ops = {
- .hash = network_config_hash_func,
- .compare = network_config_compare_func,
-};
+DEFINE_HASH_OPS(network_config_hash_ops, NetworkConfigSection, network_config_hash_func, network_config_compare_func);
int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) {
NetworkConfigSection *cs;
@@ -80,8 +75,7 @@ void network_apply_anonymize_if_set(Network *network) {
network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
/* RFC 7844 3.10:
SHOULD NOT use the Vendor Class Identifier option */
- /* NOTE: it was not initiallized to any value in network_load_one. */
- network->dhcp_vendor_class_identifier = false;
+ network->dhcp_vendor_class_identifier = mfree(network->dhcp_vendor_class_identifier);
/* RFC7844 section 3.6.:
The client intending to protect its privacy SHOULD only request a
minimal number of options in the PRL and SHOULD also randomly shuffle
@@ -103,7 +97,7 @@ void network_apply_anonymize_if_set(Network *network) {
network->dhcp_use_timezone = false;
}
-static int network_load_one(Manager *manager, const char *filename) {
+int network_load_one(Manager *manager, const char *filename) {
_cleanup_(network_freep) Network *network = NULL;
_cleanup_fclose_ FILE *file = NULL;
char *d;
@@ -128,47 +122,73 @@ static int network_load_one(Manager *manager, const char *filename) {
return 0;
}
- network = new0(Network, 1);
+ network = new(Network, 1);
if (!network)
return log_oom();
- network->manager = manager;
-
- LIST_HEAD_INIT(network->static_addresses);
- LIST_HEAD_INIT(network->static_routes);
- LIST_HEAD_INIT(network->static_fdb_entries);
- LIST_HEAD_INIT(network->ipv6_proxy_ndp_addresses);
- LIST_HEAD_INIT(network->address_labels);
- LIST_HEAD_INIT(network->static_prefixes);
- LIST_HEAD_INIT(network->rules);
-
- network->stacked_netdevs = hashmap_new(&string_hash_ops);
- if (!network->stacked_netdevs)
- return log_oom();
-
- network->addresses_by_section = hashmap_new(&network_config_hash_ops);
- if (!network->addresses_by_section)
- return log_oom();
-
- network->routes_by_section = hashmap_new(&network_config_hash_ops);
- if (!network->routes_by_section)
- return log_oom();
-
- network->fdb_entries_by_section = hashmap_new(NULL);
- if (!network->fdb_entries_by_section)
- return log_oom();
-
- network->address_labels_by_section = hashmap_new(&network_config_hash_ops);
- if (!network->address_labels_by_section)
- log_oom();
-
- network->prefixes_by_section = hashmap_new(&network_config_hash_ops);
- if (!network->prefixes_by_section)
- return log_oom();
-
- network->rules_by_section = hashmap_new(&network_config_hash_ops);
- if (!network->rules_by_section)
- return log_oom();
+ *network = (Network) {
+ .manager = manager,
+
+ .required_for_online = true,
+ .dhcp = ADDRESS_FAMILY_NO,
+ .dhcp_use_ntp = true,
+ .dhcp_use_dns = true,
+ .dhcp_use_hostname = true,
+ .dhcp_use_routes = true,
+ /* NOTE: this var might be overwriten by network_apply_anonymize_if_set */
+ .dhcp_send_hostname = true,
+ /* To enable/disable RFC7844 Anonymity Profiles */
+ .dhcp_anonymize = false,
+ .dhcp_route_metric = DHCP_ROUTE_METRIC,
+ /* NOTE: this var might be overwrite by network_apply_anonymize_if_set */
+ .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
+ .dhcp_route_table = RT_TABLE_MAIN,
+ .dhcp_route_table_set = false,
+ /* NOTE: from man: UseMTU=... Defaults to false*/
+ .dhcp_use_mtu = false,
+ /* NOTE: from man: UseTimezone=... Defaults to "no".*/
+ .dhcp_use_timezone = false,
+ .rapid_commit = true,
+
+ .dhcp_server_emit_dns = true,
+ .dhcp_server_emit_ntp = true,
+ .dhcp_server_emit_router = true,
+ .dhcp_server_emit_timezone = true,
+
+ .router_emit_dns = true,
+ .router_emit_domains = true,
+
+ .use_bpdu = -1,
+ .hairpin = -1,
+ .fast_leave = -1,
+ .allow_port_to_be_root = -1,
+ .unicast_flood = -1,
+ .multicast_to_unicast = -1,
+ .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID,
+
+ .lldp_mode = LLDP_MODE_ROUTERS_ONLY,
+
+ .dns_default_route = -1,
+ .llmnr = RESOLVE_SUPPORT_YES,
+ .mdns = RESOLVE_SUPPORT_NO,
+ .dnssec_mode = _DNSSEC_MODE_INVALID,
+ .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
+
+ .link_local = ADDRESS_FAMILY_IPV6,
+
+ .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
+ .ipv6_accept_ra = -1,
+ .ipv6_dad_transmits = -1,
+ .ipv6_hop_limit = -1,
+ .ipv6_proxy_ndp = -1,
+ .duid.type = _DUID_TYPE_INVALID,
+ .proxy_arp = -1,
+ .arp = -1,
+ .multicast = -1,
+ .allmulticast = -1,
+ .ipv6_accept_ra_use_dns = true,
+ .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
+ };
network->filename = strdup(filename);
if (!network->filename)
@@ -182,73 +202,8 @@ static int network_load_one(Manager *manager, const char *filename) {
if (!d)
return -EINVAL;
- assert(streq(d, ".network"));
-
*d = '\0';
- network->required_for_online = true;
- network->dhcp = ADDRESS_FAMILY_NO;
- network->dhcp_use_ntp = true;
- network->dhcp_use_dns = true;
- network->dhcp_use_hostname = true;
- network->dhcp_use_routes = true;
- /* NOTE: this var might be overwriten by network_apply_anonymize_if_set */
- network->dhcp_send_hostname = true;
- /* To enable/disable RFC7844 Anonymity Profiles */
- network->dhcp_anonymize = false;
- network->dhcp_route_metric = DHCP_ROUTE_METRIC;
- /* NOTE: this var might be overwrite by network_apply_anonymize_if_set */
- network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
- network->dhcp_route_table = RT_TABLE_MAIN;
- network->dhcp_route_table_set = false;
- /* NOTE: the following vars were not set to any default,
- * even if they are commented in the man?
- * These vars might be overwriten by network_apply_anonymize_if_set */
- network->dhcp_vendor_class_identifier = false;
- /* NOTE: from man: UseMTU=... Defaults to false*/
- network->dhcp_use_mtu = false;
- /* NOTE: from man: UseTimezone=... Defaults to "no".*/
- network->dhcp_use_timezone = false;
- network->rapid_commit = true;
-
- network->dhcp_server_emit_dns = true;
- network->dhcp_server_emit_ntp = true;
- network->dhcp_server_emit_router = true;
- network->dhcp_server_emit_timezone = true;
-
- network->router_emit_dns = true;
- network->router_emit_domains = true;
-
- network->use_bpdu = -1;
- network->hairpin = -1;
- network->fast_leave = -1;
- network->allow_port_to_be_root = -1;
- network->unicast_flood = -1;
- network->priority = LINK_BRIDGE_PORT_PRIORITY_INVALID;
-
- network->lldp_mode = LLDP_MODE_ROUTERS_ONLY;
-
- network->llmnr = RESOLVE_SUPPORT_YES;
- network->mdns = RESOLVE_SUPPORT_NO;
- network->dnssec_mode = _DNSSEC_MODE_INVALID;
- network->dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID;
-
- network->link_local = ADDRESS_FAMILY_IPV6;
-
- network->ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
- network->ipv6_accept_ra = -1;
- network->ipv6_dad_transmits = -1;
- network->ipv6_hop_limit = -1;
- network->ipv6_proxy_ndp = -1;
- network->duid.type = _DUID_TYPE_INVALID;
- network->proxy_arp = -1;
- network->arp = -1;
- network->multicast = -1;
- network->allmulticast = -1;
- network->ipv6_accept_ra_use_dns = true;
- network->ipv6_accept_ra_route_table = RT_TABLE_MAIN;
- network->ipv6_mtu = 0;
-
dropin_dirname = strjoina(network->name, ".network.d");
r = config_parse_many(filename, network_dirs, dropin_dirname,
@@ -256,6 +211,7 @@ static int network_load_one(Manager *manager, const char *filename) {
"Link\0"
"Network\0"
"Address\0"
+ "Neighbor\0"
"IPv6AddressLabel\0"
"RoutingPolicyRule\0"
"Route\0"
@@ -281,6 +237,12 @@ static int network_load_one(Manager *manager, const char *filename) {
if (network->ip_masquerade)
network->ip_forward |= ADDRESS_FAMILY_IPV4;
+ if (network->mtu > 0 && network->dhcp_use_mtu) {
+ log_warning("MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set in %s. "
+ "Disabling UseMTU=.", filename);
+ network->dhcp_use_mtu = false;
+ }
+
LIST_PREPEND(networks, manager->networks, network);
r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
@@ -340,12 +302,11 @@ void network_free(Network *network) {
IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
RoutingPolicyRule *rule;
FdbEntry *fdb_entry;
+ Neighbor *neighbor;
AddressLabel *label;
Prefix *prefix;
Address *address;
- NetDev *netdev;
Route *route;
- Iterator i;
if (!network)
return;
@@ -371,15 +332,14 @@ void network_free(Network *network) {
strv_free(network->route_domains);
strv_free(network->bind_carrier);
+ strv_free(network->router_search_domains);
+ free(network->router_dns);
+
netdev_unref(network->bridge);
netdev_unref(network->bond);
netdev_unref(network->vrf);
- HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
- hashmap_remove(network->stacked_netdevs, netdev->ifname);
- netdev_unref(netdev);
- }
- hashmap_free(network->stacked_netdevs);
+ hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref);
while ((route = network->static_routes))
route_free(route);
@@ -393,6 +353,9 @@ void network_free(Network *network) {
while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
+ while ((neighbor = network->neighbors))
+ neighbor_free(neighbor);
+
while ((label = network->address_labels))
address_label_free(label);
@@ -405,6 +368,7 @@ void network_free(Network *network) {
hashmap_free(network->addresses_by_section);
hashmap_free(network->routes_by_section);
hashmap_free(network->fdb_entries_by_section);
+ hashmap_free(network->neighbors_by_section);
hashmap_free(network->address_labels_by_section);
hashmap_free(network->prefixes_by_section);
hashmap_free(network->rules_by_section);
@@ -413,8 +377,11 @@ void network_free(Network *network) {
if (network->manager->networks)
LIST_REMOVE(networks, network->manager->networks, network);
- if (network->manager->networks_by_name)
+ if (network->manager->networks_by_name && network->name)
hashmap_remove(network->manager->networks_by_name, network->name);
+
+ if (network->manager->duids_requesting_uuid)
+ set_remove(network->manager->duids_requesting_uuid, &network->duid);
}
free(network->name);
@@ -450,26 +417,25 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret) {
return 0;
}
-int network_get(Manager *manager, struct udev_device *device,
+int network_get(Manager *manager, sd_device *device,
const char *ifname, const struct ether_addr *address,
Network **ret) {
- Network *network;
- struct udev_device *parent;
const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL;
+ sd_device *parent;
+ Network *network;
assert(manager);
assert(ret);
if (device) {
- path = udev_device_get_property_value(device, "ID_PATH");
+ (void) sd_device_get_property_value(device, "ID_PATH", &path);
- parent = udev_device_get_parent(device);
- if (parent)
- parent_driver = udev_device_get_driver(parent);
+ if (sd_device_get_parent(device, &parent) >= 0)
+ (void) sd_device_get_driver(parent, &parent_driver);
- driver = udev_device_get_property_value(device, "ID_NET_DRIVER");
+ (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &driver);
- devtype = udev_device_get_devtype(device);
+ (void) sd_device_get_devtype(device, &devtype);
}
LIST_FOREACH(networks, network, manager->networks) {
@@ -484,8 +450,7 @@ int network_get(Manager *manager, struct udev_device *device,
const char *attr;
uint8_t name_assign_type = NET_NAME_UNKNOWN;
- attr = udev_device_get_sysattr_value(device, "name_assign_type");
- if (attr)
+ if (sd_device_get_sysattr_value(device, "name_assign_type", &attr) >= 0)
(void) safe_atou8(attr, &name_assign_type);
if (name_assign_type == NET_NAME_ENUM)
@@ -605,14 +570,17 @@ int config_parse_netdev(const char *unit,
switch (kind) {
case NETDEV_KIND_BRIDGE:
+ network->bridge = netdev_unref(network->bridge);
network->bridge = netdev;
break;
case NETDEV_KIND_BOND:
+ network->bond = netdev_unref(network->bond);
network->bond = netdev;
break;
case NETDEV_KIND_VRF:
+ network->vrf = netdev_unref(network->vrf);
network->vrf = netdev;
break;
@@ -622,6 +590,10 @@ int config_parse_netdev(const char *unit,
case NETDEV_KIND_IPVLAN:
case NETDEV_KIND_VXLAN:
case NETDEV_KIND_VCAN:
+ r = hashmap_ensure_allocated(&network->stacked_netdevs, &string_hash_ops);
+ if (r < 0)
+ return log_oom();
+
r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add NetDev '%s' to network: %m", rvalue);
@@ -686,9 +658,8 @@ int config_parse_domains(
* routing domain, unconditionally. */
is_route = true;
domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */
-
} else {
- r = dns_name_normalize(domain, &normalized);
+ r = dns_name_normalize(domain, 0, &normalized);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain);
continue;
@@ -702,16 +673,12 @@ int config_parse_domains(
}
}
- if (is_route) {
+ if (is_route)
r = strv_extend(&n->route_domains, domain);
- if (r < 0)
- return log_oom();
-
- } else {
+ else
r = strv_extend(&n->search_domains, domain);
- if (r < 0)
- return log_oom();
- }
+ if (r < 0)
+ return log_oom();
}
strv_uniq(n->route_domains);
@@ -760,6 +727,10 @@ int config_parse_tunnel(const char *unit,
return 0;
}
+ r = hashmap_ensure_allocated(&network->stacked_netdevs, &string_hash_ops);
+ if (r < 0)
+ return log_oom();
+
r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue);
@@ -966,7 +937,8 @@ int config_parse_hostname(
void *data,
void *userdata) {
- char **hostname = data, *hn = NULL;
+ _cleanup_free_ char *hn = NULL;
+ char **hostname = data;
int r;
assert(filename);
@@ -979,13 +951,20 @@ int config_parse_hostname(
if (!hostname_is_valid(hn, false)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue);
- free(hn);
return 0;
}
- free(*hostname);
- *hostname = hostname_cleanup(hn);
- return 0;
+ r = dns_name_is_valid(hn);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
+ return 0;
+ }
+ if (r == 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ return free_and_replace(*hostname, hn);
}
int config_parse_timezone(
@@ -1000,7 +979,8 @@ int config_parse_timezone(
void *data,
void *userdata) {
- char **datap = data, *tz = NULL;
+ _cleanup_free_ char *tz = NULL;
+ char **datap = data;
int r;
assert(filename);
@@ -1013,14 +993,10 @@ int config_parse_timezone(
if (!timezone_is_valid(tz, LOG_ERR)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue);
- free(tz);
return 0;
}
- free(*datap);
- *datap = tz;
-
- return 0;
+ return free_and_replace(*datap, tz);
}
int config_parse_dhcp_server_dns(
@@ -1146,8 +1122,7 @@ int config_parse_radv_search_domains(
assert(rvalue);
for (;;) {
- _cleanup_free_ char *w = NULL;
- _cleanup_free_ char *idna = NULL;
+ _cleanup_free_ char *w = NULL, *idna = NULL;
r = extract_first_word(&p, &w, NULL, 0);
if (r == -ENOMEM)
@@ -1160,11 +1135,15 @@ int config_parse_radv_search_domains(
break;
r = dns_name_apply_idna(w, &idna);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
+ continue;
+ }
if (r > 0) {
r = strv_push(&n->router_search_domains, idna);
if (r >= 0)
idna = NULL;
- } else if (r == 0) {
+ } else {
r = strv_push(&n->router_search_domains, w);
if (r >= 0)
w = NULL;
@@ -1490,3 +1469,35 @@ static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);
+
+int config_parse_iaid(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Network *network = data;
+ uint32_t iaid;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(network);
+
+ r = safe_atou32(rvalue, &iaid);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Unable to read IAID, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ network->iaid = iaid;
+ network->iaid_set = true;
+
+ return 0;
+}
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 2d46d393ac..f6e62cdd79 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -1,9 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-bus.h"
-#include "udev.h"
+#include "sd-device.h"
#include "condition.h"
#include "conf-parser.h"
@@ -16,6 +15,7 @@
#include "networkd-fdb.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-lldp-tx.h"
+#include "networkd-neighbor.h"
#include "networkd-radv.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h"
@@ -71,6 +71,7 @@ typedef struct DUID {
uint8_t raw_data_len;
uint8_t raw_data[MAX_DUID_LEN];
+ usec_t llt_time;
} DUID;
typedef enum RADVPrefixDelegation {
@@ -78,6 +79,8 @@ typedef enum RADVPrefixDelegation {
RADV_PREFIX_DELEGATION_STATIC,
RADV_PREFIX_DELEGATION_DHCP6,
RADV_PREFIX_DELEGATION_BOTH,
+ _RADV_PREFIX_DELEGATION_MAX,
+ _RADV_PREFIX_DELEGATION_INVALID = -1,
} RADVPrefixDelegation;
typedef struct NetworkConfigSection {
@@ -87,8 +90,8 @@ typedef struct NetworkConfigSection {
int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s);
void network_config_section_free(NetworkConfigSection *network);
-
DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free);
+extern const struct hash_ops network_config_hash_ops;
typedef struct Manager Manager;
@@ -171,6 +174,9 @@ struct Network {
struct in6_addr *router_dns;
unsigned n_router_dns;
char **router_search_domains;
+ bool dhcp6_force_pd_other_information; /* Start DHCPv6 PD also when 'O'
+ RA flag is set, see RFC 7084,
+ WPD-4 */
/* Bridge Support */
int use_bpdu;
@@ -178,6 +184,7 @@ struct Network {
int fast_leave;
int allow_port_to_be_root;
int unicast_flood;
+ int multicast_to_unicast;
uint32_t cost;
uint16_t priority;
@@ -220,6 +227,8 @@ struct Network {
uint32_t iaid;
DUID duid;
+ bool iaid_set;
+
bool required_for_online; /* Is this network required to be considered online? */
LLDPMode lldp_mode; /* LLDP reception */
@@ -229,6 +238,7 @@ struct Network {
LIST_HEAD(Route, static_routes);
LIST_HEAD(FdbEntry, static_fdb_entries);
LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
+ LIST_HEAD(Neighbor, neighbors);
LIST_HEAD(AddressLabel, address_labels);
LIST_HEAD(Prefix, static_prefixes);
LIST_HEAD(RoutingPolicyRule, rules);
@@ -237,6 +247,7 @@ struct Network {
unsigned n_static_routes;
unsigned n_static_fdb_entries;
unsigned n_ipv6_proxy_ndp_addresses;
+ unsigned n_neighbors;
unsigned n_address_labels;
unsigned n_static_prefixes;
unsigned n_rules;
@@ -244,21 +255,25 @@ struct Network {
Hashmap *addresses_by_section;
Hashmap *routes_by_section;
Hashmap *fdb_entries_by_section;
+ Hashmap *neighbors_by_section;
Hashmap *address_labels_by_section;
Hashmap *prefixes_by_section;
Hashmap *rules_by_section;
+ /* All kinds of DNS configuration */
struct in_addr_data *dns;
unsigned n_dns;
-
- char **search_domains, **route_domains, **ntp, **bind_carrier;
-
+ char **search_domains, **route_domains;
+ int dns_default_route;
ResolveSupport llmnr;
ResolveSupport mdns;
DnssecMode dnssec_mode;
DnsOverTlsMode dns_over_tls_mode;
Set *dnssec_negative_trust_anchors;
+ char **ntp;
+ char **bind_carrier;
+
LIST_FIELDS(Network, networks);
};
@@ -267,9 +282,10 @@ void network_free(Network *network);
DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free);
int network_load(Manager *manager);
+int network_load_one(Manager *manager, const char *filename);
int network_get_by_name(Manager *manager, const char *name, Network **ret);
-int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret);
+int network_get(Manager *manager, sd_device *device, const char *ifname, const struct ether_addr *mac, Network **ret);
int network_apply(Network *network, Link *link);
void network_apply_anonymize_if_set(Network *network);
@@ -295,6 +311,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_lldp_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_route_table);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
+CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
/* Legacy IPv4LL support */
CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll);
@@ -313,3 +330,6 @@ DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
const char* lldp_mode_to_string(LLDPMode m) _const_;
LLDPMode lldp_mode_from_string(const char *s) _pure_;
+
+const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_;
+RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index 04a657a87f..92cead052a 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -12,6 +12,20 @@
#include "parse-util.h"
#include "sd-radv.h"
#include "string-util.h"
+#include "string-table.h"
+#include "strv.h"
+
+static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
+ [RADV_PREFIX_DELEGATION_NONE] = "no",
+ [RADV_PREFIX_DELEGATION_STATIC] = "static",
+ [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
+ [RADV_PREFIX_DELEGATION_BOTH] = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
+ radv_prefix_delegation,
+ RADVPrefixDelegation,
+ RADV_PREFIX_DELEGATION_BOTH);
int config_parse_router_prefix_delegation(
const char *unit,
@@ -26,7 +40,7 @@ int config_parse_router_prefix_delegation(
void *userdata) {
Network *network = userdata;
- int d;
+ RADVPrefixDelegation d;
assert(filename);
assert(section);
@@ -34,21 +48,14 @@ int config_parse_router_prefix_delegation(
assert(rvalue);
assert(data);
- if (streq(rvalue, "static"))
- network->router_prefix_delegation = RADV_PREFIX_DELEGATION_STATIC;
- else if (streq(rvalue, "dhcpv6"))
- network->router_prefix_delegation = RADV_PREFIX_DELEGATION_DHCP6;
- else {
- d = parse_boolean(rvalue);
- if (d > 0)
- network->router_prefix_delegation = RADV_PREFIX_DELEGATION_BOTH;
- else
- network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
-
- if (d < 0)
- log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router prefix delegation '%s' is invalid, ignoring assignment: %m", rvalue);
+ d = radv_prefix_delegation_from_string(rvalue);
+ if (d < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid router prefix delegation '%s', ignoring assignment.", rvalue);
+ return 0;
}
+ network->router_prefix_delegation = d;
+
return 0;
}
@@ -96,6 +103,7 @@ void prefix_free(Prefix *prefix) {
prefix->section);
}
+ network_config_section_free(prefix->section);
prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix);
free(prefix);
@@ -145,18 +153,21 @@ int prefix_new_static(Network *network, const char *filename,
if (r < 0)
return r;
+ prefix->network = network;
+ LIST_APPEND(prefixes, network->static_prefixes, prefix);
+ network->n_static_prefixes++;
+
if (filename) {
prefix->section = TAKE_PTR(n);
- r = hashmap_put(network->prefixes_by_section, prefix->section,
- prefix);
+ r = hashmap_ensure_allocated(&network->prefixes_by_section, &network_config_hash_ops);
if (r < 0)
return r;
- }
- prefix->network = network;
- LIST_APPEND(prefixes, network->static_prefixes, prefix);
- network->n_static_prefixes++;
+ r = hashmap_put(network->prefixes_by_section, prefix->section, prefix);
+ if (r < 0)
+ return r;
+ }
*ret = TAKE_PTR(prefix);
@@ -483,9 +494,16 @@ int radv_configure(Link *link) {
if (IN_SET(link->network->router_prefix_delegation,
RADV_PREFIX_DELEGATION_STATIC,
RADV_PREFIX_DELEGATION_BOTH)) {
+
LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
- if (r != -EEXIST && r < 0)
+ if (r == -EEXIST)
+ continue;
+ if (r == -ENOEXEC) {
+ log_link_warning_errno(link, r, "[IPv6Prefix] section configured without Prefix= setting, ignoring section.");
+ continue;
+ }
+ if (r < 0)
return r;
}
}
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index b335fdb1bb..5553a7e3bd 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -5,6 +5,7 @@
#include "alloc-util.h"
#include "conf-parser.h"
#include "in-addr-util.h"
+#include "missing_network.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "networkd-route.h"
@@ -46,17 +47,19 @@ static unsigned routes_max(void) {
int route_new(Route **ret) {
_cleanup_(route_freep) Route *route = NULL;
- route = new0(Route, 1);
+ route = new(Route, 1);
if (!route)
return -ENOMEM;
- route->family = AF_UNSPEC;
- route->scope = RT_SCOPE_UNIVERSE;
- route->protocol = RTPROT_UNSPEC;
- route->type = RTN_UNICAST;
- route->table = RT_TABLE_MAIN;
- route->lifetime = USEC_INFINITY;
- route->quickack = -1;
+ *route = (Route) {
+ .family = AF_UNSPEC,
+ .scope = RT_SCOPE_UNIVERSE,
+ .protocol = RTPROT_UNSPEC,
+ .type = RTN_UNICAST,
+ .table = RT_TABLE_MAIN,
+ .lifetime = USEC_INFINITY,
+ .quickack = -1,
+ };
*ret = TAKE_PTR(route);
@@ -93,19 +96,22 @@ int route_new_static(Network *network, const char *filename, unsigned section_li
return r;
route->protocol = RTPROT_STATIC;
+ route->network = network;
+ LIST_PREPEND(routes, network->static_routes, route);
+ network->n_static_routes++;
if (filename) {
route->section = TAKE_PTR(n);
+ r = hashmap_ensure_allocated(&network->routes_by_section, &network_config_hash_ops);
+ if (r < 0)
+ return r;
+
r = hashmap_put(network->routes_by_section, route->section, route);
if (r < 0)
return r;
}
- route->network = network;
- LIST_PREPEND(routes, network->static_routes, route);
- network->n_static_routes++;
-
*ret = TAKE_PTR(route);
return 0;
@@ -137,9 +143,7 @@ void route_free(Route *route) {
free(route);
}
-static void route_hash_func(const void *b, struct siphash *state) {
- const Route *route = b;
-
+static void route_hash_func(const Route *route, struct siphash *state) {
assert(route);
siphash24_compress(&route->family, sizeof(route->family), state);
@@ -162,36 +166,31 @@ static void route_hash_func(const void *b, struct siphash *state) {
}
}
-static int route_compare_func(const void *_a, const void *_b) {
- const Route *a = _a, *b = _b;
+static int route_compare_func(const Route *a, const Route *b) {
+ int r;
- if (a->family < b->family)
- return -1;
- if (a->family > b->family)
- return 1;
+ r = CMP(a->family, b->family);
+ if (r != 0)
+ return r;
switch (a->family) {
case AF_INET:
case AF_INET6:
- if (a->dst_prefixlen < b->dst_prefixlen)
- return -1;
- if (a->dst_prefixlen > b->dst_prefixlen)
- return 1;
-
- if (a->tos < b->tos)
- return -1;
- if (a->tos > b->tos)
- return 1;
-
- if (a->priority < b->priority)
- return -1;
- if (a->priority > b->priority)
- return 1;
-
- if (a->table < b->table)
- return -1;
- if (a->table > b->table)
- return 1;
+ r = CMP(a->dst_prefixlen, b->dst_prefixlen);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->tos, b->tos);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->priority, b->priority);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->table, b->table);
+ if (r != 0)
+ return r;
return memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
default:
@@ -200,10 +199,17 @@ static int route_compare_func(const void *_a, const void *_b) {
}
}
-static const struct hash_ops route_hash_ops = {
- .hash = route_hash_func,
- .compare = route_compare_func
-};
+DEFINE_PRIVATE_HASH_OPS(route_hash_ops, Route, route_hash_func, route_compare_func);
+
+bool route_equal(Route *r1, Route *r2) {
+ if (r1 == r2)
+ return true;
+
+ if (!r1 || !r2)
+ return false;
+
+ return route_compare_func(r1, r2) == 0;
+}
int route_get(Link *link,
int family,
@@ -358,17 +364,34 @@ void route_update(Route *route,
assert(route);
assert(src || src_prefixlen == 0);
- route->src = src ? *src : (union in_addr_union) {};
+ route->src = src ? *src : IN_ADDR_NULL;
route->src_prefixlen = src_prefixlen;
- route->gw = gw ? *gw : (union in_addr_union) {};
- route->prefsrc = prefsrc ? *prefsrc : (union in_addr_union) {};
+ route->gw = gw ? *gw : IN_ADDR_NULL;
+ route->prefsrc = prefsrc ? *prefsrc : IN_ADDR_NULL;
route->scope = scope;
route->protocol = protocol;
route->type = type;
}
+static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(m);
+ assert(link);
+ assert(link->ifname);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -ESRCH)
+ log_link_warning_errno(link, r, "Could not drop route: %m");
+
+ return 1;
+}
+
int route_remove(Route *route, Link *link,
- sd_netlink_message_handler_t callback) {
+ link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
@@ -437,13 +460,15 @@ int route_remove(Route *route, Link *link,
if (r < 0)
return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
- if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE)) {
+ if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
if (r < 0)
return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
}
- r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req,
+ callback ?: route_remove_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_error_errno(r, "Could not send rtnetlink message: %m");
@@ -452,32 +477,13 @@ int route_remove(Route *route, Link *link,
return 0;
}
-static int route_expire_callback(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- Link *link = userdata;
- int r;
-
- assert(rtnl);
- assert(m);
- assert(link);
- assert(link->ifname);
-
- 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_warning_errno(link, r, "could not remove route: %m");
-
- return 1;
-}
-
int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
Route *route = userdata;
int r;
assert(route);
- r = route_remove(route, route->link, route_expire_callback);
+ r = route_remove(route, route->link, NULL);
if (r < 0)
log_warning_errno(r, "Could not remove route: %m");
else
@@ -489,7 +495,7 @@ int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
int route_configure(
Route *route,
Link *link,
- sd_netlink_message_handler_t callback) {
+ link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
@@ -501,6 +507,7 @@ int route_configure(
assert(link->manager->rtnl);
assert(link->ifindex > 0);
assert(IN_SET(route->family, AF_INET, AF_INET6));
+ assert(callback);
if (route_get(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL) <= 0 &&
set_size(link->routes) >= routes_max())
@@ -604,7 +611,7 @@ int route_configure(
if (r < 0)
return log_error_errno(r, "Could not set route type: %m");
- if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE)) {
+ if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
if (r < 0)
return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
@@ -642,7 +649,8 @@ int route_configure(
if (r < 0)
return log_error_errno(r, "Could not append RTA_METRICS attribute: %m");
- r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_error_errno(r, "Could not send rtnetlink message: %m");
@@ -684,8 +692,7 @@ int config_parse_gateway(
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
- union in_addr_union buffer;
- int r, f;
+ int r;
assert(filename);
assert(section);
@@ -703,14 +710,12 @@ int config_parse_gateway(
if (r < 0)
return r;
- r = in_addr_from_string_auto(rvalue, &f, &buffer);
+ r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
return 0;
}
- n->family = f;
- n->gw = buffer;
TAKE_PTR(n);
return 0;
@@ -730,8 +735,7 @@ int config_parse_preferred_src(
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
- union in_addr_union buffer;
- int r, f;
+ int r;
assert(filename);
assert(section);
@@ -743,15 +747,13 @@ int config_parse_preferred_src(
if (r < 0)
return r;
- r = in_addr_from_string_auto(rvalue, &f, &buffer);
+ r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Preferred source is invalid, ignoring assignment: %s", rvalue);
return 0;
}
- n->family = f;
- n->prefsrc = buffer;
TAKE_PTR(n);
return 0;
@@ -771,8 +773,8 @@ int config_parse_destination(
Network *network = userdata;
_cleanup_(route_freep) Route *n = NULL;
- union in_addr_union buffer;
- unsigned char prefixlen;
+ union in_addr_union *buffer;
+ unsigned char *prefixlen;
int r;
assert(filename);
@@ -785,29 +787,23 @@ int config_parse_destination(
if (r < 0)
return r;
- r = in_addr_prefix_from_string(rvalue, AF_INET, &buffer, &prefixlen);
- if (r < 0) {
- r = in_addr_prefix_from_string(rvalue, AF_INET6, &buffer, &prefixlen);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Route %s= prefix is invalid, ignoring assignment: %s",
- lvalue, rvalue);
- return 0;
- }
-
- n->family = AF_INET6;
- } else
- n->family = AF_INET;
-
if (streq(lvalue, "Destination")) {
- n->dst = buffer;
- n->dst_prefixlen = prefixlen;
+ buffer = &n->dst;
+ prefixlen = &n->dst_prefixlen;
} else if (streq(lvalue, "Source")) {
- n->src = buffer;
- n->src_prefixlen = prefixlen;
+ buffer = &n->src;
+ prefixlen = &n->src_prefixlen;
} else
assert_not_reached(lvalue);
+ r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Route %s= prefix is invalid, ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
TAKE_PTR(n);
return 0;
}
@@ -1066,6 +1062,8 @@ int config_parse_route_type(
n->type = RTN_UNREACHABLE;
else if (streq(rvalue, "prohibit"))
n->type = RTN_PROHIBIT;
+ else if (streq(rvalue, "throw"))
+ n->type = RTN_THROW;
else {
log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
return 0;
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index 75da0e3a71..4eddf5194f 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "conf-parser.h"
typedef struct Route Route;
@@ -46,13 +45,14 @@ struct Route {
int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret);
int route_new(Route **ret);
void route_free(Route *route);
-int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback);
-int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback);
+int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback);
+int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback);
int route_get(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, uint32_t table, Route **ret);
int route_add(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, uint32_t table, Route **ret);
int route_add_foreign(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, uint32_t table, Route **ret);
void route_update(Route *route, const union in_addr_union *src, unsigned char src_prefixlen, const union in_addr_union *gw, const union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol, unsigned char type);
+bool route_equal(Route *r1, Route *r2);
int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata);
diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c
index 650ea0af52..2dc78622ce 100644
--- a/src/network/networkd-routing-policy-rule.c
+++ b/src/network/networkd-routing-policy-rule.c
@@ -6,22 +6,26 @@
#include "alloc-util.h"
#include "conf-parser.h"
#include "fileio.h"
+#include "ip-protocol-list.h"
#include "networkd-routing-policy-rule.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "parse-util.h"
#include "socket-util.h"
#include "string-util.h"
+#include "strv.h"
int routing_policy_rule_new(RoutingPolicyRule **ret) {
RoutingPolicyRule *rule;
- rule = new0(RoutingPolicyRule, 1);
+ rule = new(RoutingPolicyRule, 1);
if (!rule)
return -ENOMEM;
- rule->family = AF_INET;
- rule->table = RT_TABLE_MAIN;
+ *rule = (RoutingPolicyRule) {
+ .family = AF_INET,
+ .table = RT_TABLE_MAIN,
+ };
*ret = rule;
return 0;
@@ -37,11 +41,8 @@ void routing_policy_rule_free(RoutingPolicyRule *rule) {
assert(rule->network->n_rules > 0);
rule->network->n_rules--;
- if (rule->section) {
+ if (rule->section)
hashmap_remove(rule->network->rules_by_section, rule->section);
- network_config_section_free(rule->section);
- }
-
}
if (rule->manager) {
@@ -49,14 +50,13 @@ void routing_policy_rule_free(RoutingPolicyRule *rule) {
set_remove(rule->manager->rules_foreign, rule);
}
+ network_config_section_free(rule->section);
free(rule->iif);
free(rule->oif);
free(rule);
}
-static void routing_policy_rule_hash_func(const void *b, struct siphash *state) {
- const RoutingPolicyRule *rule = b;
-
+static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash *state) {
assert(rule);
siphash24_compress(&rule->family, sizeof(rule->family), state);
@@ -75,11 +75,15 @@ static void routing_policy_rule_hash_func(const void *b, struct siphash *state)
siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state);
siphash24_compress(&rule->table, sizeof(rule->table), 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);
+
if (rule->iif)
- siphash24_compress(&rule->iif, strlen(rule->iif), state);
+ siphash24_compress(rule->iif, strlen(rule->iif), state);
if (rule->oif)
- siphash24_compress(&rule->oif, strlen(rule->oif), state);
+ siphash24_compress(rule->oif, strlen(rule->oif), state);
break;
default:
@@ -88,42 +92,35 @@ static void routing_policy_rule_hash_func(const void *b, struct siphash *state)
}
}
-static int routing_policy_rule_compare_func(const void *_a, const void *_b) {
- const RoutingPolicyRule *a = _a, *b = _b;
+static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const RoutingPolicyRule *b) {
int r;
- if (a->family < b->family)
- return -1;
- if (a->family > b->family)
- return 1;
+ r = CMP(a->family, b->family);
+ if (r != 0)
+ return r;
switch (a->family) {
case AF_INET:
case AF_INET6:
- if (a->from_prefixlen < b->from_prefixlen)
- return -1;
- if (a->from_prefixlen > b->from_prefixlen)
- return 1;
-
- if (a->to_prefixlen < b->to_prefixlen)
- return -1;
- if (a->to_prefixlen > b->to_prefixlen)
- return 1;
-
- if (a->tos < b->tos)
- return -1;
- if (a->tos > b->tos)
- return 1;
-
- if (a->fwmask < b->fwmark)
- return -1;
- if (a->fwmask > b->fwmark)
- return 1;
-
- if (a->table < b->table)
- return -1;
- if (a->table > b->table)
- return 1;
+ r = CMP(a->from_prefixlen, b->from_prefixlen);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->to_prefixlen, b->to_prefixlen);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->tos, b->tos);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->fwmask, b->fwmask);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->table, b->table);
+ if (r != 0)
+ return r;
r = strcmp_ptr(a->iif, b->iif);
if (!r)
@@ -133,6 +130,18 @@ static int routing_policy_rule_compare_func(const void *_a, const void *_b) {
if (!r)
return r;
+ r = CMP(a->protocol, b->protocol);
+ if (r != 0)
+ return r;
+
+ r = memcmp(&a->sport, &b->sport, sizeof(a->sport));
+ if (r != 0)
+ return r;
+
+ r = memcmp(&a->dport, &b->dport, sizeof(a->dport));
+ if (r != 0)
+ return r;
+
r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family));
if (r != 0)
return r;
@@ -145,10 +154,7 @@ static int routing_policy_rule_compare_func(const void *_a, const void *_b) {
}
}
-const struct hash_ops routing_policy_rule_hash_ops = {
- .hash = routing_policy_rule_hash_func,
- .compare = routing_policy_rule_compare_func
-};
+DEFINE_PRIVATE_HASH_OPS(routing_policy_rule_hash_ops, RoutingPolicyRule, routing_policy_rule_hash_func, routing_policy_rule_compare_func);
int routing_policy_rule_get(Manager *m,
int family,
@@ -159,8 +165,11 @@ int routing_policy_rule_get(Manager *m,
uint8_t tos,
uint32_t fwmark,
uint32_t table,
- char *iif,
- char *oif,
+ const char *iif,
+ const char *oif,
+ uint8_t protocol,
+ struct fib_rule_port_range *sport,
+ struct fib_rule_port_range *dport,
RoutingPolicyRule **ret) {
RoutingPolicyRule rule, *existing;
@@ -176,26 +185,25 @@ int routing_policy_rule_get(Manager *m,
.tos = tos,
.fwmark = fwmark,
.table = table,
- .iif = iif,
- .oif = oif
+ .iif = (char*) iif,
+ .oif = (char*) oif,
+ .protocol = protocol,
+ .sport = *sport,
+ .dport = *dport,
};
- if (m->rules) {
- existing = set_get(m->rules, &rule);
- if (existing) {
- if (ret)
- *ret = existing;
- return 1;
- }
+ existing = set_get(m->rules, &rule);
+ if (existing) {
+ if (ret)
+ *ret = existing;
+ return 1;
}
- if (m->rules_foreign) {
- existing = set_get(m->rules_foreign, &rule);
- if (existing) {
- if (ret)
- *ret = existing;
- return 1;
- }
+ existing = set_get(m->rules_foreign, &rule);
+ if (existing) {
+ if (ret)
+ *ret = existing;
+ return 0;
}
return -ENOENT;
@@ -229,15 +237,31 @@ static int routing_policy_rule_add_internal(Manager *m,
uint8_t tos,
uint32_t fwmark,
uint32_t table,
- char *iif,
- char *oif,
+ const char *_iif,
+ const char *_oif,
+ uint8_t protocol,
+ const struct fib_rule_port_range *sport,
+ const struct fib_rule_port_range *dport,
RoutingPolicyRule **ret) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
+ _cleanup_free_ char *iif = NULL, *oif = NULL;
int r;
assert_return(rules, -EINVAL);
+ if (_iif) {
+ iif = strdup(_iif);
+ if (!iif)
+ return -ENOMEM;
+ }
+
+ if (_oif) {
+ oif = strdup(_oif);
+ if (!oif)
+ return -ENOMEM;
+ }
+
r = routing_policy_rule_new(&rule);
if (r < 0)
return r;
@@ -253,6 +277,9 @@ static int routing_policy_rule_add_internal(Manager *m,
rule->table = table;
rule->iif = iif;
rule->oif = oif;
+ rule->protocol = protocol;
+ rule->sport = *sport;
+ rule->dport = *dport;
r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops);
if (r < 0)
@@ -266,6 +293,7 @@ static int routing_policy_rule_add_internal(Manager *m,
*ret = rule;
rule = NULL;
+ iif = oif = NULL;
return 0;
}
@@ -279,11 +307,14 @@ int routing_policy_rule_add(Manager *m,
uint8_t tos,
uint32_t fwmark,
uint32_t table,
- char *iif,
- char *oif,
+ const char *iif,
+ const char *oif,
+ uint8_t protocol,
+ const struct fib_rule_port_range *sport,
+ const struct fib_rule_port_range *dport,
RoutingPolicyRule **ret) {
- return routing_policy_rule_add_internal(m, &m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret);
+ return routing_policy_rule_add_internal(m, &m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, sport, dport, ret);
}
int routing_policy_rule_add_foreign(Manager *m,
@@ -295,14 +326,16 @@ int routing_policy_rule_add_foreign(Manager *m,
uint8_t tos,
uint32_t fwmark,
uint32_t table,
- char *iif,
- char *oif,
+ const char *iif,
+ const char *oif,
+ uint8_t protocol,
+ const struct fib_rule_port_range *sport,
+ const struct fib_rule_port_range *dport,
RoutingPolicyRule **ret) {
- return routing_policy_rule_add_internal(m, &m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret);
+ return routing_policy_rule_add_internal(m, &m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, sport, dport, ret);
}
-static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(m);
@@ -321,7 +354,7 @@ static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_messa
return 1;
}
-int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, sd_netlink_message_handler_t callback) {
+int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
@@ -364,7 +397,9 @@ int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *lin
return log_error_errno(r, "Could not set destination prefix length: %m");
}
- r = sd_netlink_call_async(link->manager->rtnl, m, callback, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, m,
+ callback ?: routing_policy_rule_remove_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_error_errno(r, "Could not send rtnetlink message: %m");
@@ -382,39 +417,45 @@ static int routing_policy_rule_new_static(Network *network, const char *filename
assert(ret);
assert(!!filename == (section_line > 0));
- r = network_config_section_new(filename, section_line, &n);
- if (r < 0)
- return r;
+ if (filename) {
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
- rule = hashmap_get(network->rules_by_section, n);
- if (rule) {
- *ret = TAKE_PTR(rule);
+ rule = hashmap_get(network->rules_by_section, n);
+ if (rule) {
+ *ret = TAKE_PTR(rule);
- return 0;
+ return 0;
+ }
}
r = routing_policy_rule_new(&rule);
if (r < 0)
return r;
- rule->section = n;
rule->network = network;
- n = NULL;
-
- r = hashmap_put(network->rules_by_section, rule->section, rule);
- if (r < 0)
- return r;
-
LIST_APPEND(rules, network->rules, rule);
network->n_rules++;
+ if (filename) {
+ rule->section = TAKE_PTR(n);
+
+ r = hashmap_ensure_allocated(&network->rules_by_section, &network_config_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(network->rules_by_section, rule->section, rule);
+ if (r < 0)
+ return r;
+ }
+
*ret = TAKE_PTR(rule);
return 0;
}
-int link_routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
- _cleanup_(link_unrefp) Link *link = userdata;
+static int routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(rtnl);
@@ -441,7 +482,7 @@ int link_routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, vo
return 1;
}
-int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, sd_netlink_message_handler_t callback, bool update) {
+int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback, bool update) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
@@ -531,18 +572,42 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, sd_netlin
return log_error_errno(r, "Could not append FRA_OIFNAME attribute: %m");
}
+ r = sd_netlink_message_append_u8(m, FRA_IP_PROTO, rule->protocol);
+ if (r < 0)
+ return log_error_errno(r, "Could not append FRA_IP_PROTO attribute: %m");
+
+ if (rule->sport.start != 0 || rule->sport.end != 0) {
+ r = sd_netlink_message_append_data(m, FRA_SPORT_RANGE, &rule->sport, sizeof(rule->sport));
+ if (r < 0)
+ return log_error_errno(r, "Could not append FRA_SPORT_RANGE attribute: %m");
+ }
+
+ if (rule->dport.start != 0 || rule->dport.end != 0) {
+ r = sd_netlink_message_append_data(m, FRA_DPORT_RANGE, &rule->dport, sizeof(rule->dport));
+ if (r < 0)
+ return log_error_errno(r, "Could not append FRA_DPORT_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_error_errno(r, "Could not append FIB_RULE_INVERT attribute: %m");
+ }
+
rule->link = link;
- r = sd_netlink_call_async(link->manager->rtnl, m, callback, link, 0, NULL);
+ r = netlink_call_async(link->manager->rtnl, NULL, m,
+ callback ?: routing_policy_rule_handler,
+ link_netlink_destroy_callback, link);
if (r < 0)
return log_error_errno(r, "Could not send rtnetlink message: %m");
link_ref(link);
r = routing_policy_rule_add(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to,
- rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif, NULL);
+ rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif, rule->protocol, &rule->sport, &rule->dport, NULL);
if (r < 0)
- return log_error_errno(r, "Could not add rule : %m");
+ return log_error_errno(r, "Could not add rule: %m");
return 0;
}
@@ -737,8 +802,8 @@ int config_parse_routing_policy_rule_prefix(
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
Network *network = userdata;
- union in_addr_union buffer;
- uint8_t prefixlen;
+ union in_addr_union *buffer;
+ uint8_t *prefixlen;
int r;
assert(filename);
@@ -751,24 +816,18 @@ int config_parse_routing_policy_rule_prefix(
if (r < 0)
return r;
- r = in_addr_prefix_from_string(rvalue, AF_INET, &buffer, &prefixlen);
- if (r < 0) {
- r = in_addr_prefix_from_string(rvalue, AF_INET6, &buffer, &prefixlen);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue);
- return 0;
- }
-
- n->family = AF_INET6;
- } else
- n->family = AF_INET;
-
if (streq(lvalue, "To")) {
- n->to = buffer;
- n->to_prefixlen = prefixlen;
+ buffer = &n->to;
+ prefixlen = &n->to_prefixlen;
} else {
- n->from = buffer;
- n->from_prefixlen = prefixlen;
+ buffer = &n->from;
+ prefixlen = &n->from_prefixlen;
+ }
+
+ r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue);
+ return 0;
}
n = NULL;
@@ -822,6 +881,129 @@ int config_parse_routing_policy_rule_device(
return 0;
}
+int config_parse_routing_policy_rule_port_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_freep) RoutingPolicyRule *n = NULL;
+ Network *network = userdata;
+ uint16_t low, high;
+ 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_port_range(rvalue, &low, &high);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse routing policy rule port range '%s'", rvalue);
+ return 0;
+ }
+
+ if (streq(lvalue, "SourcePort")) {
+ n->sport.start = low;
+ n->sport.end = high;
+ } else {
+ n->dport.start = low;
+ n->dport.end = high;
+ }
+
+ n = NULL;
+
+ return 0;
+}
+
+int config_parse_routing_policy_rule_ip_protocol(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_(routing_policy_rule_freep) 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_protocol(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IP protocol '%s' for routing policy rule, ignoring: %m", rvalue);
+ return 0;
+ }
+
+ n->protocol = r;
+
+ n = NULL;
+
+ return 0;
+}
+
+int config_parse_routing_policy_rule_invert(
+ 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_freep) 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_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule invert, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ n->invert_rule = r;
+
+ 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;
@@ -904,6 +1086,27 @@ int routing_policy_serialize_rules(Set *rules, FILE *f) {
space = true;
}
+ if (rule->protocol != 0) {
+ fprintf(f, "%sprotocol=%hhu",
+ space ? " " : "",
+ rule->protocol);
+ space = true;
+ }
+
+ if (rule->sport.start != 0 || rule->sport.end != 0) {
+ fprintf(f, "%ssourcesport=%"PRIu16"-%"PRIu16,
+ space ? " " : "",
+ rule->sport.start, rule->sport.end);
+ space = true;
+ }
+
+ if (rule->dport.start != 0 || rule->dport.end != 0) {
+ fprintf(f, "%sdestinationport=%"PRIu16"-%"PRIu16,
+ space ? " " : "",
+ rule->dport.start, rule->dport.end);
+ space = true;
+ }
+
fprintf(f, "%stable=%"PRIu32 "\n",
space ? " " : "",
rule->table);
@@ -915,6 +1118,7 @@ int routing_policy_serialize_rules(Set *rules, FILE *f) {
int routing_policy_load_rules(const char *state_file, Set **rules) {
_cleanup_strv_free_ char **l = NULL;
_cleanup_free_ char *data = NULL;
+ uint16_t low = 0, high = 0;
const char *p;
char **i;
int r;
@@ -947,8 +1151,6 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
for (;;) {
_cleanup_free_ char *word = NULL, *a = NULL, *b = NULL;
- union in_addr_union buffer;
- uint8_t prefixlen;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
@@ -961,26 +1163,23 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
continue;
if (STR_IN_SET(a, "from", "to")) {
-
- r = in_addr_prefix_from_string(b, AF_INET, &buffer, &prefixlen);
- if (r < 0) {
- r = in_addr_prefix_from_string(b, AF_INET6, &buffer, &prefixlen);
- if (r < 0) {
- log_error_errno(r, "RPDB rule prefix is invalid, ignoring assignment: %s", b);
- continue;
- }
-
- rule->family = AF_INET6;
- } else
- rule->family = AF_INET;
+ union in_addr_union *buffer;
+ uint8_t *prefixlen;
if (streq(a, "to")) {
- rule->to = buffer;
- rule->to_prefixlen = prefixlen;
+ buffer = &rule->to;
+ prefixlen = &rule->to_prefixlen;
} else {
- rule->from = buffer;
- rule->from_prefixlen = prefixlen;
+ buffer = &rule->from;
+ prefixlen = &rule->from_prefixlen;
}
+
+ r = in_addr_prefix_from_string_auto(b, &rule->family, buffer, prefixlen);
+ if (r < 0) {
+ log_error_errno(r, "RPDB rule prefix is invalid, ignoring assignment: %s", b);
+ continue;
+ }
+
} else if (streq(a, "tos")) {
r = safe_atou8(b, &rule->tos);
if (r < 0) {
@@ -1009,6 +1208,33 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
if (free_and_strdup(&rule->oif, b) < 0)
return log_oom();
+ } else if (streq(a, "protocol")) {
+ r = safe_atou8(b, &rule->protocol);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse RPDB rule protocol, ignoring: %s", b);
+ 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);
+ 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);
+ continue;
+ }
+
+ rule->dport.start = low;
+ rule->dport.end = high;
}
}
@@ -1036,7 +1262,7 @@ void routing_policy_rule_purge(Manager *m, Link *link) {
existing = set_get(m->rules_foreign, rule);
if (existing) {
- r = routing_policy_rule_remove(rule, link, routing_policy_rule_remove_handler);
+ r = routing_policy_rule_remove(rule, link, NULL);
if (r < 0) {
log_warning_errno(r, "Could not remove routing policy rules: %m");
continue;
diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h
index f4a0d5138f..b35126e2cf 100644
--- a/src/network/networkd-routing-policy-rule.h
+++ b/src/network/networkd-routing-policy-rule.h
@@ -1,12 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <inttypes.h>
+#include <linux/fib_rules.h>
#include <stdbool.h>
#include "in-addr-util.h"
#include "conf-parser.h"
+#include "missing_fib_rules.h"
typedef struct RoutingPolicyRule RoutingPolicyRule;
@@ -24,7 +25,10 @@ struct RoutingPolicyRule {
Link *link;
NetworkConfigSection *section;
+ bool invert_rule;
+
uint8_t tos;
+ uint8_t protocol;
uint32_t table;
uint32_t fwmark;
@@ -41,6 +45,9 @@ struct RoutingPolicyRule {
union in_addr_union to;
union in_addr_union from;
+ struct fib_rule_port_range sport;
+ struct fib_rule_port_range dport;
+
LIST_FIELDS(RoutingPolicyRule, rules);
};
@@ -49,17 +56,18 @@ void routing_policy_rule_free(RoutingPolicyRule *rule);
DEFINE_TRIVIAL_CLEANUP_FUNC(RoutingPolicyRule*, routing_policy_rule_free);
-int routing_policy_rule_configure(RoutingPolicyRule *address, Link *link, sd_netlink_message_handler_t callback, bool update);
-int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, sd_netlink_message_handler_t callback);
-int link_routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
-int link_routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata);
+int routing_policy_rule_configure(RoutingPolicyRule *address, Link *link, link_netlink_message_handler_t callback, bool update);
+int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, link_netlink_message_handler_t callback);
int routing_policy_rule_add(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen,
- uint8_t tos, uint32_t fwmark, uint32_t table, char *iif, char *oif, RoutingPolicyRule **ret);
+ uint8_t tos, uint32_t fwmark, uint32_t table, const char *iif, const char *oif, uint8_t protocol, const struct fib_rule_port_range *sport,
+ const struct fib_rule_port_range *dport, RoutingPolicyRule **ret);
int routing_policy_rule_add_foreign(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen,
- uint8_t tos, uint32_t fwmark, uint32_t table, char *iif, char *oif, RoutingPolicyRule **ret);
+ uint8_t tos, uint32_t fwmark, uint32_t table, const char *iif, const char *oif, uint8_t protocol, const struct fib_rule_port_range *sport,
+ const struct fib_rule_port_range *dport, RoutingPolicyRule **ret);
int routing_policy_rule_get(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen, uint8_t tos,
- uint32_t fwmark, uint32_t table, char *iif, char *oif, RoutingPolicyRule **ret);
+ uint32_t fwmark, uint32_t table, const char *iif, const char *oif, uint8_t protocol, struct fib_rule_port_range *sport,
+ struct fib_rule_port_range *dport, RoutingPolicyRule **ret);
int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule);
int routing_policy_serialize_rules(Set *rules, FILE *f);
int routing_policy_load_rules(const char *state_file, Set **rules);
@@ -71,3 +79,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_fwmark_mask);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_priority);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_device);
+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);
diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h
index 0fb41caaf6..435fdb7ca8 100644
--- a/src/network/networkd-util.h
+++ b/src/network/networkd-util.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "conf-parser.h"
#include "macro.h"
diff --git a/src/network/networkd.c b/src/network/networkd.c
index b59b308141..fcecafe083 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -4,36 +4,32 @@
#include "sd-event.h"
#include "capability-util.h"
+#include "daemon-util.h"
+#include "main-func.h"
+#include "mkdir.h"
#include "networkd-conf.h"
#include "networkd-manager.h"
#include "signal-util.h"
#include "user-util.h"
-int main(int argc, char *argv[]) {
- sd_event *event = NULL;
+static int run(int argc, char *argv[]) {
+ _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
const char *user = "systemd-network";
uid_t uid;
gid_t gid;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
- if (argc != 1) {
- log_error("This program takes no arguments.");
- r = -EINVAL;
- goto out;
- }
+ if (argc != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
- r = get_user_creds(&user, &uid, &gid, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "Cannot resolve user name %s: %m", user);
- goto out;
- }
+ r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Cannot resolve user name %s: %m", user);
/* Create runtime directory. This is not necessary when networkd is
* started with "RuntimeDirectory=systemd/netif", or after
@@ -51,7 +47,7 @@ int main(int argc, char *argv[]) {
(1ULL << CAP_NET_BROADCAST) |
(1ULL << CAP_NET_RAW));
if (r < 0)
- goto out;
+ return log_error_errno(r, "Failed to drop privileges: %m");
}
/* Always create the directories people can create inotify watches in.
@@ -71,83 +67,51 @@ int main(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
- r = sd_event_default(&event);
+ r = manager_new(&m);
if (r < 0)
- goto out;
-
- sd_event_set_watchdog(event, true);
- sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
- sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
-
- r = manager_new(&m, event);
- if (r < 0) {
- log_error_errno(r, "Could not create manager: %m");
- goto out;
- }
+ return log_error_errno(r, "Could not create manager: %m");
r = manager_connect_bus(m);
- if (r < 0) {
- log_error_errno(r, "Could not connect to bus: %m");
- goto out;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not connect to bus: %m");
r = manager_parse_config_file(m);
if (r < 0)
log_warning_errno(r, "Failed to parse configuration file: %m");
r = manager_load_config(m);
- if (r < 0) {
- log_error_errno(r, "Could not load configuration files: %m");
- goto out;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not load configuration files: %m");
r = manager_rtnl_enumerate_links(m);
- if (r < 0) {
- log_error_errno(r, "Could not enumerate links: %m");
- goto out;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not enumerate links: %m");
r = manager_rtnl_enumerate_addresses(m);
- if (r < 0) {
- log_error_errno(r, "Could not enumerate addresses: %m");
- goto out;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not enumerate addresses: %m");
r = manager_rtnl_enumerate_routes(m);
- if (r < 0) {
- log_error_errno(r, "Could not enumerate routes: %m");
- goto out;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not enumerate routes: %m");
r = manager_rtnl_enumerate_rules(m);
- if (r < 0) {
- log_error_errno(r, "Could not enumerate rules: %m");
- goto out;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not enumerate rules: %m");
r = manager_start(m);
- if (r < 0) {
- log_error_errno(r, "Could not start manager: %m");
- goto out;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not start manager: %m");
log_info("Enumeration completed");
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing requests...");
-
- r = sd_event_loop(event);
- if (r < 0) {
- log_error_errno(r, "Event loop failed: %m");
- goto out;
- }
-out:
- sd_notify(false,
- "STOPPING=1\n"
- "STATUS=Shutting down...");
+ notify_message = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
- sd_event_unref(event);
+ r = sd_event_loop(m->event);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/network/networkd.conf b/src/network/networkd.conf
new file mode 100644
index 0000000000..8dc5676166
--- /dev/null
+++ b/src/network/networkd.conf
@@ -0,0 +1,16 @@
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
+#
+# See networkd.conf(5) for details
+
+[DHCP]
+#DUIDType=vendor
+#DUIDRawData=
diff --git a/src/network/systemd-networkd.pkla b/src/network/systemd-networkd.pkla
index fb257d933b..4d1bb4585e 100644
--- a/src/network/systemd-networkd.pkla
+++ b/src/network/systemd-networkd.pkla
@@ -1,4 +1,4 @@
[Allow systemd-networkd to set timezone and transient hostname]
Identity=unix-user:systemd-network
-Action=org.freedesktop.hostname1.set-hostname;org.freedesktop.timedate1.set-timezone;
+Action=org.freedesktop.hostname1.set-hostname;org.freedesktop.hostname1.get-product-uuid;org.freedesktop.timedate1.set-timezone;
ResultAny=yes
diff --git a/src/network/systemd-networkd.rules b/src/network/systemd-networkd.rules
index 2e4bc42bfb..b9077c1ea2 100644
--- a/src/network/systemd-networkd.rules
+++ b/src/network/systemd-networkd.rules
@@ -1,6 +1,8 @@
-// Allow systemd-networkd to set timezone and transient hostname
+// Allow systemd-networkd to set timezone, get product UUID,
+// and transient hostname
polkit.addRule(function(action, subject) {
if ((action.id == "org.freedesktop.hostname1.set-hostname" ||
+ action.id == "org.freedesktop.hostname1.get-product-uuid" ||
action.id == "org.freedesktop.timedate1.set-timezone") &&
subject.user == "systemd-network") {
return polkit.Result.YES;
diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c
index 11cd7faa8b..6b110b7110 100644
--- a/src/network/test-network-tables.c
+++ b/src/network/test-network-tables.c
@@ -1,6 +1,8 @@
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
#include "ethtool-util.h"
+#include "lldp-internal.h"
+#include "ndisc-internal.h"
#include "netdev/bond.h"
#include "netdev/ipvlan.h"
#include "netdev/macvlan.h"
@@ -22,6 +24,7 @@ int main(int argc, char **argv) {
test_table(bond_primary_reselect, NETDEV_BOND_PRIMARY_RESELECT);
test_table(bond_xmit_hash_policy, NETDEV_BOND_XMIT_HASH_POLICY);
test_table(dhcp6_message_status, DHCP6_STATUS);
+ test_table_sparse(dhcp6_message_type, DHCP6_MESSAGE); /* enum starts from 1 */
test_table(dhcp_use_domains, DHCP_USE_DOMAINS);
test_table(duplex, DUP);
test_table(ip6tnl_mode, NETDEV_IP6_TNL_MODE);
@@ -32,9 +35,11 @@ int main(int argc, char **argv) {
test_table(lldp_mode, LLDP_MODE);
test_table(netdev_kind, NETDEV_KIND);
test_table(nl_union_link_info_data, NL_UNION_LINK_INFO_DATA);
+ test_table(radv_prefix_delegation, RADV_PREFIX_DELEGATION);
test_table(wol, WOL);
+ test_table(lldp_event, SD_LLDP_EVENT);
+ test_table(ndisc_event, SD_NDISC_EVENT);
- test_table_sparse(dhcp6_message_type, DHCP6_MESSAGE);
test_table_sparse(ipvlan_mode, NETDEV_IPVLAN_MODE);
test_table_sparse(macvlan_mode, NETDEV_MACVLAN_MODE);
diff --git a/src/network/test-network.c b/src/network/test-network.c
index 5349b668be..6e169e0fca 100644
--- a/src/network/test-network.c
+++ b/src/network/test-network.c
@@ -2,13 +2,15 @@
#include <sys/param.h>
+#include "sd-device.h"
+
#include "alloc-util.h"
#include "dhcp-lease-internal.h"
#include "hostname-util.h"
#include "network-internal.h"
#include "networkd-manager.h"
#include "string-util.h"
-#include "udev-util.h"
+#include "tests.h"
static void test_deserialize_in_addr(void) {
_cleanup_free_ struct in_addr *addresses = NULL;
@@ -117,7 +119,7 @@ static int test_load_config(Manager *manager) {
return 0;
}
-static void test_network_get(Manager *manager, struct udev_device *loopback) {
+static void test_network_get(Manager *manager, sd_device *loopback) {
Network *network;
const struct ether_addr mac = {};
@@ -219,30 +221,27 @@ static void test_dhcp_hostname_shorten_overlong(void) {
int main(void) {
_cleanup_(manager_freep) Manager *manager = NULL;
- _cleanup_(sd_event_unrefp) sd_event *event = NULL;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
- _cleanup_(udev_device_unrefp) struct udev_device *loopback = NULL;
- int r;
+ _cleanup_(sd_device_unrefp) sd_device *loopback = NULL;
+ int ifindex, r;
+
+ test_setup_logging(LOG_INFO);
test_deserialize_in_addr();
test_deserialize_dhcp_routes();
test_address_equality();
test_dhcp_hostname_shorten_overlong();
- assert_se(sd_event_default(&event) >= 0);
-
- assert_se(manager_new(&manager, event) >= 0);
+ assert_se(manager_new(&manager) >= 0);
r = test_load_config(manager);
if (r == -EPERM)
- return EXIT_TEST_SKIP;
-
- udev = udev_new();
- assert_se(udev);
+ return log_tests_skipped("Cannot load configuration");
+ assert_se(r == 0);
- loopback = udev_device_new_from_syspath(udev, "/sys/class/net/lo");
+ assert_se(sd_device_new_from_syspath(&loopback, "/sys/class/net/lo") >= 0);
assert_se(loopback);
- assert_se(udev_device_get_ifindex(loopback) == 1);
+ assert_se(sd_device_get_ifindex(loopback, &ifindex) >= 0);
+ assert_se(ifindex == 1);
test_network_get(manager, loopback);
diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c
index fc1646f4bf..86d4e7e733 100644
--- a/src/network/test-networkd-conf.c
+++ b/src/network/test-networkd-conf.c
@@ -11,23 +11,30 @@
#include "networkd-conf.h"
#include "networkd-network.h"
-static void test_config_parse_duid_type_one(const char *rvalue, int ret, DUIDType expected) {
- DUIDType actual = 0;
+static void test_config_parse_duid_type_one(const char *rvalue, int ret, DUIDType expected, usec_t expected_time) {
+ DUID actual = {};
int r;
r = config_parse_duid_type("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL);
- log_info_errno(r, "\"%s\" → %d (%m)", rvalue, actual);
+ log_info_errno(r, "\"%s\" → %d (%m)", rvalue, actual.type);
assert_se(r == ret);
- assert_se(expected == actual);
+ assert_se(expected == actual.type);
+ if (expected == DUID_TYPE_LLT)
+ assert_se(expected_time == actual.llt_time);
}
static void test_config_parse_duid_type(void) {
- test_config_parse_duid_type_one("", 0, 0);
- test_config_parse_duid_type_one("link-layer-time", 0, DUID_TYPE_LLT);
- test_config_parse_duid_type_one("vendor", 0, DUID_TYPE_EN);
- test_config_parse_duid_type_one("link-layer", 0, DUID_TYPE_LL);
- test_config_parse_duid_type_one("uuid", 0, DUID_TYPE_UUID);
- test_config_parse_duid_type_one("foo", 0, 0);
+ test_config_parse_duid_type_one("", 0, 0, 0);
+ test_config_parse_duid_type_one("link-layer-time", 0, DUID_TYPE_LLT, 0);
+ test_config_parse_duid_type_one("link-layer-time:2000-01-01 00:00:00 UTC", 0, DUID_TYPE_LLT, (usec_t) 946684800000000);
+ test_config_parse_duid_type_one("vendor", 0, DUID_TYPE_EN, 0);
+ test_config_parse_duid_type_one("vendor:2000-01-01 00:00:00 UTC", 0, 0, 0);
+ test_config_parse_duid_type_one("link-layer", 0, DUID_TYPE_LL, 0);
+ test_config_parse_duid_type_one("link-layer:2000-01-01 00:00:00 UTC", 0, 0, 0);
+ test_config_parse_duid_type_one("uuid", 0, DUID_TYPE_UUID, 0);
+ test_config_parse_duid_type_one("uuid:2000-01-01 00:00:00 UTC", 0, 0, 0);
+ test_config_parse_duid_type_one("foo", 0, 0, 0);
+ test_config_parse_duid_type_one("foo:2000-01-01 00:00:00 UTC", 0, 0, 0);
}
static void test_config_parse_duid_rawdata_one(const char *rvalue, int ret, const DUID* expected) {
@@ -161,6 +168,48 @@ static void test_config_parse_hwaddr(void) {
test_config_parse_hwaddrs_one("123.4567.89ab aa:bb:cc:dd:ee:fx hogehoge 01-23-45-67-89-ab aaaa aa:Bb:CC:dd:ee:ff", t, 2);
}
+static void test_config_parse_address_one(const char *rvalue, int family, unsigned n_addresses, const union in_addr_union *u, unsigned char prefixlen) {
+ _cleanup_(network_freep) Network *network = NULL;
+
+ assert_se(network = new0(Network, 1));
+ assert_se(config_parse_address("network", "filename", 1, "section", 1, "Address", 0, rvalue, network, network) == 0);
+ assert_se(network->n_static_addresses == n_addresses);
+ if (n_addresses > 0) {
+ assert_se(network->static_addresses);
+ assert_se(network->static_addresses->prefixlen == prefixlen);
+ assert_se(network->static_addresses->family == family);
+ assert_se(in_addr_equal(family, &network->static_addresses->in_addr, u));
+ /* TODO: check Address.in_addr and Address.broadcast */
+ }
+}
+
+static void test_config_parse_address(void) {
+ test_config_parse_address_one("", AF_INET, 0, NULL, 0);
+ test_config_parse_address_one("/", AF_INET, 0, NULL, 0);
+ test_config_parse_address_one("/8", AF_INET, 0, NULL, 0);
+ test_config_parse_address_one("1.2.3.4", AF_INET, 1, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 8);
+ test_config_parse_address_one("1.2.3.4/0", AF_INET, 1, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 0);
+ test_config_parse_address_one("1.2.3.4/1", AF_INET, 1, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 1);
+ test_config_parse_address_one("1.2.3.4/2", AF_INET, 1, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 2);
+ test_config_parse_address_one("1.2.3.4/32", AF_INET, 1, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32);
+ test_config_parse_address_one("1.2.3.4/33", AF_INET, 0, NULL, 0);
+ test_config_parse_address_one("1.2.3.4/-1", AF_INET, 0, NULL, 0);
+
+ test_config_parse_address_one("", AF_INET6, 0, NULL, 0);
+ test_config_parse_address_one("/", AF_INET6, 0, NULL, 0);
+ test_config_parse_address_one("/8", AF_INET6, 0, NULL, 0);
+ test_config_parse_address_one("::1", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0);
+ test_config_parse_address_one("::1/0", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0);
+ test_config_parse_address_one("::1/1", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 1);
+ test_config_parse_address_one("::1/2", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 2);
+ test_config_parse_address_one("::1/32", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 32);
+ test_config_parse_address_one("::1/33", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 33);
+ test_config_parse_address_one("::1/64", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 64);
+ test_config_parse_address_one("::1/128", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128);
+ test_config_parse_address_one("::1/129", AF_INET6, 0, NULL, 0);
+ test_config_parse_address_one("::1/-1", AF_INET6, 0, NULL, 0);
+}
+
int main(int argc, char **argv) {
log_parse_environment();
log_open();
@@ -168,6 +217,7 @@ int main(int argc, char **argv) {
test_config_parse_duid_type();
test_config_parse_duid_rawdata();
test_config_parse_hwaddr();
+ test_config_parse_address();
return 0;
}
diff --git a/src/network/test-routing-policy-rule.c b/src/network/test-routing-policy-rule.c
index 9920f516b7..57bfb6af68 100644
--- a/src/network/test-routing-policy-rule.c
+++ b/src/network/test-routing-policy-rule.c
@@ -9,6 +9,8 @@
#include "network-internal.h"
#include "networkd-manager.h"
#include "string-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
static void test_rule_serialization(const char *title, const char *ruleset, const char *expected) {
char pattern[] = "/tmp/systemd-test-routing-policy-rule.XXXXXX",
@@ -26,14 +28,14 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons
fd = mkostemp_safe(pattern);
assert_se(fd >= 0);
- assert_se(f = fdopen(fd, "a+e"));
+ assert_se(f = fdopen(fd, "a+"));
assert_se(write_string_stream(f, ruleset, 0) == 0);
assert_se(routing_policy_load_rules(pattern, &rules) == 0);
fd2 = mkostemp_safe(pattern2);
assert_se(fd2 >= 0);
- assert_se(f2 = fdopen(fd2, "a+e"));
+ assert_se(f2 = fdopen(fd2, "a+"));
assert_se(routing_policy_serialize_rules(rules, f2) == 0);
assert_se(fflush_and_check(f2) == 0);
@@ -44,7 +46,7 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons
fd3 = mkostemp_safe(pattern3);
assert_se(fd3 >= 0);
- assert_se(f3 = fdopen(fd3, "we"));
+ assert_se(f3 = fdopen(fd3, "w"));
assert_se(write_string_stream(f3, expected ?: ruleset, 0) == 0);
cmd = strjoina("diff -u ", pattern3, " ", pattern2);
@@ -57,9 +59,7 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons
int main(int argc, char **argv) {
_cleanup_free_ char *p = NULL;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_rule_serialization("basic parsing",
"RULE=from=1.2.3.4/32 to=2.3.4.5/32 tos=5 fwmark=1/2 table=10", NULL);
diff --git a/src/network/wait-online/link.h b/src/network/wait-online/link.h
index f1096c99d9..57e8f12e03 100644
--- a/src/network/wait-online/link.h
+++ b/src/network/wait-online/link.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-netlink.h"
typedef struct Link Link;
diff --git a/src/network/wait-online/manager.c b/src/network/wait-online/manager.c
index ccda93babe..e1ccc9ff67 100644
--- a/src/network/wait-online/manager.c
+++ b/src/network/wait-online/manager.c
@@ -9,6 +9,7 @@
#include "manager.h"
#include "netlink-util.h"
#include "network-internal.h"
+#include "strv.h"
#include "time-util.h"
#include "util.h"
@@ -168,11 +169,11 @@ static int manager_rtnl_listen(Manager *m) {
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, on_rtnl_event, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWLINK, on_rtnl_event, NULL, m, "wait-online-on-NEWLINK");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, on_rtnl_event, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELLINK, on_rtnl_event, NULL, m, "wait-online-on-DELLINK");
if (r < 0)
return r;
@@ -262,8 +263,8 @@ int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout)
if (r < 0)
return r;
- sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
- sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+ (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
+ (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
if (timeout > 0) {
usec_t usec;
@@ -291,14 +292,10 @@ int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout)
}
void manager_free(Manager *m) {
- Link *l;
-
if (!m)
return;
- while ((l = hashmap_first(m->links)))
- link_free(l);
- hashmap_free(m->links);
+ hashmap_free_with_destructor(m->links, link_free);
hashmap_free(m->links_by_name);
sd_event_source_unref(m->network_monitor_event_source);
diff --git a/src/network/wait-online/manager.h b/src/network/wait-online/manager.h
index 21f0381dda..0fde272ddb 100644
--- a/src/network/wait-online/manager.h
+++ b/src/network/wait-online/manager.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-event.h"
#include "sd-netlink.h"
#include "sd-network.h"
diff --git a/src/network/wait-online/wait-online.c b/src/network/wait-online/wait-online.c
index a96b2c8fe5..71b6cf6b87 100644
--- a/src/network/wait-online/wait-online.c
+++ b/src/network/wait-online/wait-online.c
@@ -1,11 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-
#include <getopt.h>
#include "sd-daemon.h"
+#include "daemon-util.h"
+#include "main-func.h"
#include "manager.h"
+#include "pretty-print.h"
#include "signal-util.h"
#include "strv.h"
@@ -14,7 +16,17 @@ static usec_t arg_timeout = 120 * USEC_PER_SEC;
static char **arg_interfaces = NULL;
static char **arg_ignore = NULL;
-static void help(void) {
+STATIC_DESTRUCTOR_REGISTER(arg_interfaces, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-networkd-wait-online.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"Block until network is configured.\n\n"
" -h --help Show this help\n"
@@ -23,7 +35,12 @@ static void help(void) {
" -i --interface=INTERFACE Block until at least these interfaces have appeared\n"
" --ignore=INTERFACE Don't take these interfaces into account\n"
" --timeout=SECS Maximum time to wait for network connectivity\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -93,13 +110,12 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
+ _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
@@ -113,37 +129,24 @@ int main(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
r = manager_new(&m, arg_interfaces, arg_ignore, arg_timeout);
- if (r < 0) {
- log_error_errno(r, "Could not create manager: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not create manager: %m");
- if (manager_all_configured(m)) {
- r = 0;
- goto finish;
- }
+ if (manager_all_configured(m))
+ goto success;
- sd_notify(false,
- "READY=1\n"
- "STATUS=Waiting for network connections...");
+ notify_message = notify_start("READY=1\n"
+ "STATUS=Waiting for network connections...",
+ "STATUS=Failed to wait for network connectivity...");
r = sd_event_loop(m->event);
- if (r < 0) {
- log_error_errno(r, "Event loop failed: %m");
- goto finish;
- }
-
-finish:
- strv_free(arg_interfaces);
- strv_free(arg_ignore);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
- if (r >= 0) {
- sd_notify(false, "STATUS=All interfaces configured...");
+success:
+ notify_message = "STATUS=All interfaces configured...";
- return EXIT_SUCCESS;
- } else {
- sd_notify(false, "STATUS=Failed waiting for network connectivity...");
-
- return EXIT_FAILURE;
- }
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/notify/notify.c b/src/notify/notify.c
index e928094d9a..fff5233b0c 100644
--- a/src/notify/notify.c
+++ b/src/notify/notify.c
@@ -12,7 +12,9 @@
#include "env-util.h"
#include "format-util.h"
#include "log.h"
+#include "main-func.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
@@ -25,7 +27,14 @@ static bool arg_booted = false;
static uid_t arg_uid = UID_INVALID;
static gid_t arg_gid = GID_INVALID;
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-notify", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n"
"Notify the init system about service status updates.\n\n"
" -h --help Show this help\n"
@@ -34,8 +43,13 @@ static void help(void) {
" --pid[=PID] Set main PID of daemon\n"
" --uid=USER Set user to send from\n"
" --status=TEXT Set status text\n"
- " --booted Check if the system was booted up with systemd\n",
- program_invocation_short_name);
+ " --booted Check if the system was booted up with systemd\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -70,8 +84,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -83,10 +96,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_PID:
if (optarg) {
- if (parse_pid(optarg, &arg_pid) < 0) {
- log_error("Failed to parse PID %s.", optarg);
- return -EINVAL;
- }
+ if (parse_pid(optarg, &arg_pid) < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse PID %s.", optarg);
} else
arg_pid = getppid();
@@ -103,7 +115,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_UID: {
const char *u = optarg;
- r = get_user_creds(&u, &arg_uid, &arg_gid, NULL, NULL);
+ r = get_user_creds(&u, &arg_uid, &arg_gid, NULL, NULL, 0);
if (r == -ESRCH) /* If the user doesn't exist, then accept it anyway as numeric */
r = parse_uid(u, &arg_uid);
if (r < 0)
@@ -132,7 +144,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char* argv[]) {
+static int run(int argc, char* argv[]) {
_cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL;
_cleanup_strv_free_ char **final_env = NULL;
char* our_env[4];
@@ -144,7 +156,7 @@ int main(int argc, char* argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
if (arg_booted)
return sd_booted() <= 0;
@@ -154,19 +166,15 @@ int main(int argc, char* argv[]) {
if (arg_status) {
status = strappend("STATUS=", arg_status);
- if (!status) {
- r = log_oom();
- goto finish;
- }
+ if (!status)
+ return log_oom();
our_env[i++] = status;
}
if (arg_pid > 0) {
- if (asprintf(&cpid, "MAINPID="PID_FMT, arg_pid) < 0) {
- r = log_oom();
- goto finish;
- }
+ if (asprintf(&cpid, "MAINPID="PID_FMT, arg_pid) < 0)
+ return log_oom();
our_env[i++] = cpid;
}
@@ -174,47 +182,35 @@ int main(int argc, char* argv[]) {
our_env[i++] = NULL;
final_env = strv_env_merge(2, our_env, argv + optind);
- if (!final_env) {
- r = log_oom();
- goto finish;
- }
+ if (!final_env)
+ return log_oom();
- if (strv_isempty(final_env)) {
- r = 0;
- goto finish;
- }
+ if (strv_isempty(final_env))
+ return 0;
n = strv_join(final_env, "\n");
- if (!n) {
- r = log_oom();
- goto finish;
- }
+ if (!n)
+ return log_oom();
/* If this is requested change to the requested UID/GID. Note thta we only change the real UID here, and leave
the effective UID in effect (which is 0 for this to work). That's because we want the privileges to fake the
ucred data, and sd_pid_notify() uses the real UID for filling in ucred. */
- if (arg_gid != GID_INVALID)
- if (setregid(arg_gid, (gid_t) -1) < 0) {
- r = log_error_errno(errno, "Failed to change GID: %m");
- goto finish;
- }
+ if (arg_gid != GID_INVALID &&
+ setregid(arg_gid, (gid_t) -1) < 0)
+ return log_error_errno(errno, "Failed to change GID: %m");
- if (arg_uid != UID_INVALID)
- if (setreuid(arg_uid, (uid_t) -1) < 0) {
- r = log_error_errno(errno, "Failed to change UID: %m");
- goto finish;
- }
+ if (arg_uid != UID_INVALID &&
+ setreuid(arg_uid, (uid_t) -1) < 0)
+ return log_error_errno(errno, "Failed to change UID: %m");
r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n);
- if (r < 0) {
- log_error_errno(r, "Failed to notify init system: %m");
- goto finish;
- } else if (r == 0) {
- log_error("No status data could be sent: $NOTIFY_SOCKET was not set");
- r = -EOPNOTSUPP;
- }
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ if (r < 0)
+ return log_error_errno(r, "Failed to notify init system: %m");
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "No status data could be sent: $NOTIFY_SOCKET was not set");
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c
index d8a39a6959..97fa092cae 100644
--- a/src/nspawn/nspawn-cgroup.c
+++ b/src/nspawn/nspawn-cgroup.c
@@ -5,12 +5,17 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "nspawn-cgroup.h"
+#include "nspawn-mount.h"
+#include "path-util.h"
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
#include "util.h"
static int chown_cgroup_path(const char *path, uid_t uid_shift) {
@@ -55,7 +60,7 @@ int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
if (r < 0)
return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs);
- if (unified_requested == CGROUP_UNIFIED_SYSTEMD) {
+ if (unified_requested == CGROUP_UNIFIED_SYSTEMD || (unified_requested == CGROUP_UNIFIED_NONE && cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)) {
_cleanup_free_ char *lfs = NULL;
/* Always propagate access rights from unified to legacy controller */
@@ -71,7 +76,7 @@ int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
return 0;
}
-int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t arg_uid_shift) {
+int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
_cleanup_free_ char *cgroup = NULL;
char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
bool undo_mount = false;
@@ -118,14 +123,14 @@ int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t arg_uid_shift)
(void) mkdir_parents(fn, 0755);
sprintf(pid_string, PID_FMT, pid);
- r = write_string_file(fn, pid_string, 0);
+ r = write_string_file(fn, pid_string, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0) {
log_error_errno(r, "Failed to move process: %m");
goto finish;
}
fn = strjoina(tree, cgroup);
- r = chown_cgroup_path(fn, arg_uid_shift);
+ r = chown_cgroup_path(fn, uid_shift);
if (r < 0)
log_error_errno(r, "Failed to chown() cgroup %s: %m", fn);
finish:
@@ -185,6 +190,419 @@ int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested)
}
/* Try to enable as many controllers as possible for the new payload. */
- (void) cg_enable_everywhere(supported, supported, cgroup);
+ (void) cg_enable_everywhere(supported, supported, cgroup, NULL);
return 0;
}
+
+/* Retrieve existing subsystems. This function is called in a new cgroup
+ * namespace.
+ */
+static int get_process_controllers(Set **ret) {
+ _cleanup_set_free_free_ Set *controllers = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(ret);
+
+ controllers = set_new(&string_hash_ops);
+ if (!controllers)
+ return -ENOMEM;
+
+ f = fopen("/proc/self/cgroup", "re");
+ if (!f)
+ return errno == ENOENT ? -ESRCH : -errno;
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ char *e, *l;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ l = strchr(line, ':');
+ if (!l)
+ continue;
+
+ l++;
+ e = strchr(l, ':');
+ if (!e)
+ continue;
+
+ *e = 0;
+
+ if (STR_IN_SET(l, "", "name=systemd", "name=unified"))
+ continue;
+
+ r = set_put_strdup(controllers, l);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(controllers);
+
+ return 0;
+}
+
+static int mount_legacy_cgroup_hierarchy(
+ const char *dest,
+ const char *controller,
+ const char *hierarchy,
+ bool read_only) {
+
+ const char *to, *fstype, *opts;
+ int r;
+
+ to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
+
+ r = path_is_mount_point(to, dest, 0);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
+ if (r > 0)
+ return 0;
+
+ mkdir_p(to, 0755);
+
+ /* The superblock mount options of the mount point need to be
+ * identical to the hosts', and hence writable... */
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) {
+ fstype = "cgroup2";
+ opts = NULL;
+ } else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) {
+ fstype = "cgroup";
+ opts = "none,name=systemd,xattr";
+ } else {
+ fstype = "cgroup";
+ opts = controller;
+ }
+
+ r = mount_verbose(LOG_ERR, "cgroup", to, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
+ if (r < 0)
+ return r;
+
+ /* ... hence let's only make the bind mount read-only, not the superblock. */
+ if (read_only) {
+ r = mount_verbose(LOG_ERR, NULL, to, NULL,
+ MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
+/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
+static int mount_legacy_cgns_supported(
+ const char *dest,
+ CGroupUnified unified_requested,
+ bool userns,
+ uid_t uid_shift,
+ uid_t uid_range,
+ const char *selinux_apifs_context) {
+
+ _cleanup_set_free_free_ Set *controllers = NULL;
+ const char *cgroup_root = "/sys/fs/cgroup", *c;
+ int r;
+
+ (void) mkdir_p(cgroup_root, 0755);
+
+ /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
+ r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
+ if (r == 0) {
+ _cleanup_free_ char *options = NULL;
+
+ /* When cgroup namespaces are enabled and user namespaces are
+ * used then the mount of the cgroupfs is done *inside* the new
+ * user namespace. We're root in the new user namespace and the
+ * kernel will happily translate our uid/gid to the correct
+ * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
+ * pass uid 0 and not uid_shift to tmpfs_patch_options().
+ */
+ r = tmpfs_patch_options("mode=755", 0, selinux_apifs_context, &options);
+ if (r < 0)
+ return log_oom();
+
+ r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
+ MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
+ if (r < 0)
+ return r;
+ }
+
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
+ goto skip_controllers;
+
+ r = get_process_controllers(&controllers);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine cgroup controllers: %m");
+
+ for (;;) {
+ _cleanup_free_ const char *controller = NULL;
+
+ controller = set_steal_first(controllers);
+ if (!controller)
+ break;
+
+ r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
+ if (r < 0)
+ return r;
+
+ /* When multiple hierarchies are co-mounted, make their
+ * constituting individual hierarchies a symlink to the
+ * co-mount.
+ */
+ c = controller;
+ for (;;) {
+ _cleanup_free_ char *target = NULL, *tok = NULL;
+
+ r = extract_first_word(&c, &tok, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m");
+ if (r == 0)
+ break;
+
+ if (streq(controller, tok))
+ break;
+
+ target = prefix_root("/sys/fs/cgroup/", tok);
+ if (!target)
+ return log_oom();
+
+ r = symlink_idempotent(controller, target, false);
+ if (r == -EINVAL)
+ return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
+ if (r < 0)
+ return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
+ }
+ }
+
+skip_controllers:
+ if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
+ r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
+ if (r < 0)
+ return r;
+ }
+
+ r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
+ if (r < 0)
+ return r;
+
+ if (!userns)
+ return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
+ MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
+
+ return 0;
+}
+
+/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
+static int mount_legacy_cgns_unsupported(
+ const char *dest,
+ CGroupUnified unified_requested,
+ bool userns,
+ uid_t uid_shift,
+ uid_t uid_range,
+ const char *selinux_apifs_context) {
+
+ _cleanup_set_free_free_ Set *controllers = NULL;
+ const char *cgroup_root;
+ int r;
+
+ cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
+
+ (void) mkdir_p(cgroup_root, 0755);
+
+ /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
+ r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
+ if (r == 0) {
+ _cleanup_free_ char *options = NULL;
+
+ r = tmpfs_patch_options("mode=755", uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
+ if (r < 0)
+ return log_oom();
+
+ r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
+ MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
+ if (r < 0)
+ return r;
+ }
+
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
+ goto skip_controllers;
+
+ r = cg_kernel_controllers(&controllers);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine cgroup controllers: %m");
+
+ for (;;) {
+ _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
+
+ controller = set_steal_first(controllers);
+ if (!controller)
+ break;
+
+ origin = prefix_root("/sys/fs/cgroup/", controller);
+ if (!origin)
+ return log_oom();
+
+ r = readlink_malloc(origin, &combined);
+ if (r == -EINVAL) {
+ /* Not a symbolic link, but directly a single cgroup hierarchy */
+
+ r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
+ if (r < 0)
+ return r;
+
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to read link %s: %m", origin);
+ else {
+ _cleanup_free_ char *target = NULL;
+
+ target = prefix_root(dest, origin);
+ if (!target)
+ return log_oom();
+
+ /* A symbolic link, a combination of controllers in one hierarchy */
+
+ if (!filename_is_valid(combined)) {
+ log_warning("Ignoring invalid combined hierarchy %s.", combined);
+ continue;
+ }
+
+ r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
+ if (r < 0)
+ return r;
+
+ r = symlink_idempotent(combined, target, false);
+ if (r == -EINVAL)
+ return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
+ if (r < 0)
+ return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
+ }
+ }
+
+skip_controllers:
+ if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
+ r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
+ if (r < 0)
+ return r;
+ }
+
+ r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
+ if (r < 0)
+ return r;
+
+ return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
+ MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
+}
+
+static int mount_unified_cgroups(const char *dest) {
+ const char *p;
+ int r;
+
+ assert(dest);
+
+ p = prefix_roota(dest, "/sys/fs/cgroup");
+
+ (void) mkdir_p(p, 0755);
+
+ r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
+ if (r > 0) {
+ p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs");
+ if (access(p, F_OK) >= 0)
+ return 0;
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p);
+
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s is already mounted but not a unified cgroup hierarchy. Refusing.", p);
+ }
+
+ return mount_verbose(LOG_ERR, "cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
+}
+
+int mount_cgroups(
+ const char *dest,
+ CGroupUnified unified_requested,
+ bool userns,
+ uid_t uid_shift,
+ uid_t uid_range,
+ const char *selinux_apifs_context,
+ bool use_cgns) {
+
+ if (unified_requested >= CGROUP_UNIFIED_ALL)
+ return mount_unified_cgroups(dest);
+ if (use_cgns)
+ return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
+
+ return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
+}
+
+static int mount_systemd_cgroup_writable_one(const char *root, const char *own) {
+ int r;
+
+ assert(root);
+ assert(own);
+
+ /* Make our own cgroup a (writable) bind mount */
+ r = mount_verbose(LOG_ERR, own, own, NULL, MS_BIND, NULL);
+ if (r < 0)
+ return r;
+
+ /* And then remount the systemd cgroup root read-only */
+ return mount_verbose(LOG_ERR, NULL, root, NULL,
+ MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
+}
+
+int mount_systemd_cgroup_writable(
+ const char *dest,
+ CGroupUnified unified_requested) {
+
+ _cleanup_free_ char *own_cgroup_path = NULL;
+ const char *root, *own;
+ int r;
+
+ assert(dest);
+
+ r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine our own cgroup path: %m");
+
+ /* If we are living in the top-level, then there's nothing to do... */
+ if (path_equal(own_cgroup_path, "/"))
+ return 0;
+
+ if (unified_requested >= CGROUP_UNIFIED_ALL) {
+
+ root = prefix_roota(dest, "/sys/fs/cgroup");
+ own = strjoina(root, own_cgroup_path);
+
+ } else {
+
+ if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
+ root = prefix_roota(dest, "/sys/fs/cgroup/unified");
+ own = strjoina(root, own_cgroup_path);
+
+ r = mount_systemd_cgroup_writable_one(root, own);
+ if (r < 0)
+ return r;
+ }
+
+ root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
+ own = strjoina(root, own_cgroup_path);
+ }
+
+ return mount_systemd_cgroup_writable_one(root, own);
+}
diff --git a/src/nspawn/nspawn-cgroup.h b/src/nspawn/nspawn-cgroup.h
index 6783c3a39f..035e8fbd0f 100644
--- a/src/nspawn/nspawn-cgroup.h
+++ b/src/nspawn/nspawn-cgroup.h
@@ -9,3 +9,6 @@
int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested);
+
+int mount_cgroups(const char *dest, CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns);
+int mount_systemd_cgroup_writable(const char *dest, CGroupUnified unified_requested);
diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c
index 07da7922f1..13ce8490a0 100644
--- a/src/nspawn/nspawn-expose-ports.c
+++ b/src/nspawn/nspawn-expose-ports.c
@@ -209,11 +209,11 @@ int expose_port_watch_rtnl(
return log_error_errno(r, "Failed to create rtnl object: %m");
}
- r = sd_netlink_add_match(rtnl, RTM_NEWADDR, handler, exposed);
+ r = sd_netlink_add_match(rtnl, NULL, RTM_NEWADDR, handler, NULL, exposed, "nspawn-NEWADDR");
if (r < 0)
return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
- r = sd_netlink_add_match(rtnl, RTM_DELADDR, handler, exposed);
+ r = sd_netlink_add_match(rtnl, NULL, RTM_DELADDR, handler, NULL, exposed, "nspawn-DELADDR");
if (r < 0)
return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
index 6029686ee9..dec53a06f3 100644
--- a/src/nspawn/nspawn-gperf.gperf
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -19,6 +19,7 @@ struct ConfigPerfItem;
%includes
%%
Exec.Boot, config_parse_boot, 0, 0
+Exec.Ephemeral, config_parse_bool, 0, offsetof(Settings, ephemeral)
Exec.ProcessTwo, config_parse_pid2, 0, 0
Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters)
Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment)
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index b5df65e2a4..a9af889747 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -6,11 +6,11 @@
#include "alloc-util.h"
#include "escape.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "label.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "nspawn-mount.h"
#include "parse-util.h"
#include "path-util.h"
@@ -19,6 +19,7 @@
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
@@ -69,20 +70,14 @@ void custom_mount_free_all(CustomMount *l, size_t n) {
free(l);
}
-static int custom_mount_compare(const void *a, const void *b) {
- const CustomMount *x = a, *y = b;
+static int custom_mount_compare(const CustomMount *a, const CustomMount *b) {
int r;
- r = path_compare(x->destination, y->destination);
+ r = path_compare(a->destination, b->destination);
if (r != 0)
return r;
- if (x->type < y->type)
- return -1;
- if (x->type > y->type)
- return 1;
-
- return 0;
+ return CMP(a->type, b->type);
}
static bool source_path_is_valid(const char *p) {
@@ -116,7 +111,7 @@ int custom_mount_prepare_all(const char *dest, CustomMount *l, size_t n) {
assert(l || n == 0);
/* Order the custom mounts, and make sure we have a working directory */
- qsort_safe(l, n, sizeof(CustomMount), custom_mount_compare);
+ typesafe_qsort(l, n, custom_mount_compare);
for (i = 0; i < n; i++) {
CustomMount *m = l + i;
@@ -327,19 +322,15 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl
return 0;
}
-static int tmpfs_patch_options(
+int tmpfs_patch_options(
const char *options,
- bool userns,
- uid_t uid_shift, uid_t uid_range,
- bool patch_ids,
+ uid_t uid_shift,
const char *selinux_apifs_context,
char **ret) {
char *buf = NULL;
- if ((userns && uid_shift != 0) || patch_ids) {
- assert(uid_shift != UID_INVALID);
-
+ if (uid_shift != UID_INVALID) {
if (asprintf(&buf, "%s%suid=" UID_FMT ",gid=" UID_FMT,
strempty(options), options ? "," : "",
uid_shift, uid_shift) < 0)
@@ -433,16 +424,14 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
/* Create mountpoint for cgroups. Otherwise we are not allowed since we
* remount /sys read-only.
*/
- if (cg_ns_supported()) {
- x = prefix_roota(top, "/fs/cgroup");
- (void) mkdir_p(x, 0755);
- }
+ x = prefix_roota(top, "/fs/cgroup");
+ (void) mkdir_p(x, 0755);
return mount_verbose(LOG_ERR, NULL, top, NULL,
MS_BIND|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT|extra_flags, NULL);
}
-static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) {
+static int mkdir_userns(const char *path, mode_t mode, uid_t uid_shift) {
int r;
assert(path);
@@ -451,10 +440,7 @@ static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, u
if (r < 0 && r != -EEXIST)
return r;
- if ((mask & MOUNT_USE_USERNS) == 0)
- return 0;
-
- if (mask & MOUNT_IN_USERNS)
+ if (uid_shift == UID_INVALID)
return 0;
if (lchown(path, uid_shift, uid_shift) < 0)
@@ -463,7 +449,7 @@ static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, u
return 0;
}
-static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) {
+static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, uid_t uid_shift) {
const char *p, *e;
int r;
@@ -490,17 +476,17 @@ static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, Mou
if (prefix && path_startswith(prefix, t))
continue;
- r = mkdir_userns(t, mode, mask, uid_shift);
+ r = mkdir_userns(t, mode, uid_shift);
if (r < 0)
return r;
}
- return mkdir_userns(path, mode, mask, uid_shift);
+ return mkdir_userns(path, mode, uid_shift);
}
int mount_all(const char *dest,
MountSettingsMask mount_settings,
- uid_t uid_shift, uid_t uid_range,
+ uid_t uid_shift,
const char *selinux_apifs_context) {
#define PROC_INACCESSIBLE(path) \
@@ -558,8 +544,8 @@ int mount_all(const char *dest,
/* Then we list outer child mounts (i.e. mounts applied *before* entering user namespacing) */
{ "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
- MOUNT_FATAL },
- { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ MOUNT_FATAL|MOUNT_APPLY_TMPFS_TMP },
+ { "tmpfs", "/sys", "tmpfs", "mode=555", MS_NOSUID|MS_NOEXEC|MS_NODEV,
MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS },
{ "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV,
MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO }, /* skipped if above was mounted */
@@ -571,6 +557,8 @@ int mount_all(const char *dest,
MOUNT_FATAL },
{ "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
MOUNT_FATAL },
+ { "mqueue", "/dev/mqueue", "mqueue", NULL, 0,
+ MOUNT_FATAL },
#if HAVE_SELINUX
{ "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND,
@@ -585,6 +573,7 @@ int mount_all(const char *dest,
bool netns = (mount_settings & MOUNT_APPLY_APIVFS_NETNS);
bool ro = (mount_settings & MOUNT_APPLY_APIVFS_RO);
bool in_userns = (mount_settings & MOUNT_IN_USERNS);
+ bool tmpfs_tmp = (mount_settings & MOUNT_APPLY_TMPFS_TMP);
size_t k;
int r;
@@ -602,6 +591,9 @@ int mount_all(const char *dest,
if (!ro && (bool)(mount_table[k].mount_settings & MOUNT_APPLY_APIVFS_RO))
continue;
+ if (!tmpfs_tmp && (bool)(mount_table[k].mount_settings & MOUNT_APPLY_TMPFS_TMP))
+ continue;
+
r = chase_symlinks(mount_table[k].where, dest, CHASE_NONEXISTENT|CHASE_PREFIX_ROOT, &where);
if (r < 0)
return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].where);
@@ -634,7 +626,7 @@ int mount_all(const char *dest,
if (what && r > 0)
continue;
- r = mkdir_userns_p(dest, where, 0755, mount_settings, uid_shift);
+ r = mkdir_userns_p(dest, where, 0755, (use_userns && !in_userns) ? uid_shift : UID_INVALID);
if (r < 0 && r != -EEXIST) {
if (fatal && r != -EROFS)
return log_error_errno(r, "Failed to create directory %s: %m", where);
@@ -649,10 +641,7 @@ int mount_all(const char *dest,
o = mount_table[k].options;
if (streq_ptr(mount_table[k].type, "tmpfs")) {
- if (in_userns)
- r = tmpfs_patch_options(o, use_userns, 0, uid_range, true, selinux_apifs_context, &options);
- else
- r = tmpfs_patch_options(o, use_userns, uid_shift, uid_range, false, selinux_apifs_context, &options);
+ r = tmpfs_patch_options(o, in_userns ? 0 : uid_shift, selinux_apifs_context, &options);
if (r < 0)
return log_oom();
if (r > 0)
@@ -692,15 +681,15 @@ static int mount_bind(const char *dest, CustomMount *m) {
if (stat(where, &dest_st) < 0)
return log_error_errno(errno, "Failed to stat %s: %m", where);
- if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) {
- log_error("Cannot bind mount directory %s on file %s.", m->source, where);
- return -EINVAL;
- }
+ if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot bind mount directory %s on file %s.",
+ m->source, where);
- if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) {
- log_error("Cannot bind mount file %s on directory %s.", m->source, where);
- return -EINVAL;
- }
+ if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot bind mount file %s on directory %s.",
+ m->source, where);
} else { /* Path doesn't exist yet? */
r = mkdir_parents_label(where, 0755);
@@ -755,7 +744,7 @@ static int mount_tmpfs(
return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where);
}
- r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf);
+ r = tmpfs_patch_options(m->options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
if (r < 0)
return log_oom();
options = r > 0 ? buf : m->options;
@@ -860,419 +849,6 @@ int mount_custom(
return 0;
}
-/* Retrieve existing subsystems. This function is called in a new cgroup
- * namespace.
- */
-static int get_process_controllers(Set **ret) {
- _cleanup_set_free_free_ Set *controllers = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- int r;
-
- assert(ret);
-
- controllers = set_new(&string_hash_ops);
- if (!controllers)
- return -ENOMEM;
-
- f = fopen("/proc/self/cgroup", "re");
- if (!f)
- return errno == ENOENT ? -ESRCH : -errno;
-
- for (;;) {
- _cleanup_free_ char *line = NULL;
- char *e, *l;
-
- r = read_line(f, LONG_LINE_MAX, &line);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- l = strchr(line, ':');
- if (!l)
- continue;
-
- l++;
- e = strchr(l, ':');
- if (!e)
- continue;
-
- *e = 0;
-
- if (STR_IN_SET(l, "", "name=systemd", "name=unified"))
- continue;
-
- r = set_put_strdup(controllers, l);
- if (r < 0)
- return r;
- }
-
- *ret = TAKE_PTR(controllers);
-
- return 0;
-}
-
-static int mount_legacy_cgroup_hierarchy(
- const char *dest,
- const char *controller,
- const char *hierarchy,
- bool read_only) {
-
- const char *to, *fstype, *opts;
- int r;
-
- to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
-
- r = path_is_mount_point(to, dest, 0);
- if (r < 0 && r != -ENOENT)
- return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
- if (r > 0)
- return 0;
-
- mkdir_p(to, 0755);
-
- /* The superblock mount options of the mount point need to be
- * identical to the hosts', and hence writable... */
- if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) {
- fstype = "cgroup2";
- opts = NULL;
- } else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) {
- fstype = "cgroup";
- opts = "none,name=systemd,xattr";
- } else {
- fstype = "cgroup";
- opts = controller;
- }
-
- r = mount_verbose(LOG_ERR, "cgroup", to, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
- if (r < 0)
- return r;
-
- /* ... hence let's only make the bind mount read-only, not the superblock. */
- if (read_only) {
- r = mount_verbose(LOG_ERR, NULL, to, NULL,
- MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
- if (r < 0)
- return r;
- }
-
- return 1;
-}
-
-/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
-static int mount_legacy_cgns_supported(
- const char *dest,
- CGroupUnified unified_requested,
- bool userns,
- uid_t uid_shift,
- uid_t uid_range,
- const char *selinux_apifs_context) {
-
- _cleanup_set_free_free_ Set *controllers = NULL;
- const char *cgroup_root = "/sys/fs/cgroup", *c;
- int r;
-
- (void) mkdir_p(cgroup_root, 0755);
-
- /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
- r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
- if (r < 0)
- return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
- if (r == 0) {
- _cleanup_free_ char *options = NULL;
-
- /* When cgroup namespaces are enabled and user namespaces are
- * used then the mount of the cgroupfs is done *inside* the new
- * user namespace. We're root in the new user namespace and the
- * kernel will happily translate our uid/gid to the correct
- * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
- * pass uid 0 and not uid_shift to tmpfs_patch_options().
- */
- r = tmpfs_patch_options("mode=755", userns, 0, uid_range, true, selinux_apifs_context, &options);
- if (r < 0)
- return log_oom();
-
- r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
- MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
- if (r < 0)
- return r;
- }
-
- r = cg_all_unified();
- if (r < 0)
- return r;
- if (r > 0)
- goto skip_controllers;
-
- r = get_process_controllers(&controllers);
- if (r < 0)
- return log_error_errno(r, "Failed to determine cgroup controllers: %m");
-
- for (;;) {
- _cleanup_free_ const char *controller = NULL;
-
- controller = set_steal_first(controllers);
- if (!controller)
- break;
-
- r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
- if (r < 0)
- return r;
-
- /* When multiple hierarchies are co-mounted, make their
- * constituting individual hierarchies a symlink to the
- * co-mount.
- */
- c = controller;
- for (;;) {
- _cleanup_free_ char *target = NULL, *tok = NULL;
-
- r = extract_first_word(&c, &tok, ",", 0);
- if (r < 0)
- return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m");
- if (r == 0)
- break;
-
- if (streq(controller, tok))
- break;
-
- target = prefix_root("/sys/fs/cgroup/", tok);
- if (!target)
- return log_oom();
-
- r = symlink_idempotent(controller, target);
- if (r == -EINVAL)
- return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
- if (r < 0)
- return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
- }
- }
-
-skip_controllers:
- if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
- r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
- if (r < 0)
- return r;
- }
-
- r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
- if (r < 0)
- return r;
-
- if (!userns)
- return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
- MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
-
- return 0;
-}
-
-/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
-static int mount_legacy_cgns_unsupported(
- const char *dest,
- CGroupUnified unified_requested,
- bool userns,
- uid_t uid_shift,
- uid_t uid_range,
- const char *selinux_apifs_context) {
-
- _cleanup_set_free_free_ Set *controllers = NULL;
- const char *cgroup_root;
- int r;
-
- cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
-
- (void) mkdir_p(cgroup_root, 0755);
-
- /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
- r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
- if (r < 0)
- return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
- if (r == 0) {
- _cleanup_free_ char *options = NULL;
-
- r = tmpfs_patch_options("mode=755", userns, uid_shift, uid_range, false, selinux_apifs_context, &options);
- if (r < 0)
- return log_oom();
-
- r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
- MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
- if (r < 0)
- return r;
- }
-
- r = cg_all_unified();
- if (r < 0)
- return r;
- if (r > 0)
- goto skip_controllers;
-
- r = cg_kernel_controllers(&controllers);
- if (r < 0)
- return log_error_errno(r, "Failed to determine cgroup controllers: %m");
-
- for (;;) {
- _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
-
- controller = set_steal_first(controllers);
- if (!controller)
- break;
-
- origin = prefix_root("/sys/fs/cgroup/", controller);
- if (!origin)
- return log_oom();
-
- r = readlink_malloc(origin, &combined);
- if (r == -EINVAL) {
- /* Not a symbolic link, but directly a single cgroup hierarchy */
-
- r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
- if (r < 0)
- return r;
-
- } else if (r < 0)
- return log_error_errno(r, "Failed to read link %s: %m", origin);
- else {
- _cleanup_free_ char *target = NULL;
-
- target = prefix_root(dest, origin);
- if (!target)
- return log_oom();
-
- /* A symbolic link, a combination of controllers in one hierarchy */
-
- if (!filename_is_valid(combined)) {
- log_warning("Ignoring invalid combined hierarchy %s.", combined);
- continue;
- }
-
- r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
- if (r < 0)
- return r;
-
- r = symlink_idempotent(combined, target);
- if (r == -EINVAL)
- return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
- if (r < 0)
- return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
- }
- }
-
-skip_controllers:
- if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
- r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
- if (r < 0)
- return r;
- }
-
- r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
- if (r < 0)
- return r;
-
- return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
- MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
-}
-
-static int mount_unified_cgroups(const char *dest) {
- const char *p;
- int r;
-
- assert(dest);
-
- p = prefix_roota(dest, "/sys/fs/cgroup");
-
- (void) mkdir_p(p, 0755);
-
- r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
- if (r < 0)
- return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
- if (r > 0) {
- p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs");
- if (access(p, F_OK) >= 0)
- return 0;
- if (errno != ENOENT)
- return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p);
-
- log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p);
- return -EINVAL;
- }
-
- return mount_verbose(LOG_ERR, "cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
-}
-
-int mount_cgroups(
- const char *dest,
- CGroupUnified unified_requested,
- bool userns,
- uid_t uid_shift,
- uid_t uid_range,
- const char *selinux_apifs_context,
- bool use_cgns) {
-
- if (unified_requested >= CGROUP_UNIFIED_ALL)
- return mount_unified_cgroups(dest);
- if (use_cgns)
- return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
-
- return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
-}
-
-static int mount_systemd_cgroup_writable_one(const char *root, const char *own) {
- int r;
-
- assert(root);
- assert(own);
-
- /* Make our own cgroup a (writable) bind mount */
- r = mount_verbose(LOG_ERR, own, own, NULL, MS_BIND, NULL);
- if (r < 0)
- return r;
-
- /* And then remount the systemd cgroup root read-only */
- return mount_verbose(LOG_ERR, NULL, root, NULL,
- MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
-}
-
-int mount_systemd_cgroup_writable(
- const char *dest,
- CGroupUnified unified_requested) {
-
- _cleanup_free_ char *own_cgroup_path = NULL;
- const char *root, *own;
- int r;
-
- assert(dest);
-
- r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
- if (r < 0)
- return log_error_errno(r, "Failed to determine our own cgroup path: %m");
-
- /* If we are living in the top-level, then there's nothing to do... */
- if (path_equal(own_cgroup_path, "/"))
- return 0;
-
- if (unified_requested >= CGROUP_UNIFIED_ALL) {
-
- root = prefix_roota(dest, "/sys/fs/cgroup");
- own = strjoina(root, own_cgroup_path);
-
- } else {
-
- if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
- root = prefix_roota(dest, "/sys/fs/cgroup/unified");
- own = strjoina(root, own_cgroup_path);
-
- r = mount_systemd_cgroup_writable_one(root, own);
- if (r < 0)
- return r;
- }
-
- root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
- own = strjoina(root, own_cgroup_path);
- }
-
- return mount_systemd_cgroup_writable_one(root, own);
-}
-
int setup_volatile_state(
const char *directory,
VolatileMode mode,
@@ -1301,7 +877,7 @@ int setup_volatile_state(
return log_error_errno(errno, "Failed to create %s: %m", directory);
options = "mode=755";
- r = tmpfs_patch_options(options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf);
+ r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
if (r < 0)
return log_oom();
if (r > 0)
@@ -1334,7 +910,7 @@ int setup_volatile(
return log_error_errno(errno, "Failed to create temporary directory: %m");
options = "mode=755";
- r = tmpfs_patch_options(options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf);
+ r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
if (r < 0)
return log_oom();
if (r > 0)
diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h
index b823282cbd..8051a7d9d9 100644
--- a/src/nspawn/nspawn-mount.h
+++ b/src/nspawn/nspawn-mount.h
@@ -14,6 +14,7 @@ typedef enum MountSettingsMask {
MOUNT_APPLY_APIVFS_NETNS = 1 << 4, /* if set, /proc/sys/net will be mounted read-write.
Works only if MOUNT_APPLY_APIVFS_RO is also set. */
MOUNT_INACCESSIBLE_REG = 1 << 5, /* if set, create an inaccessible regular file first and use as bind mount source */
+ MOUNT_APPLY_TMPFS_TMP = 1 << 6, /* if set, /tmp will be mounted as tmpfs */
} MountSettingsMask;
typedef enum CustomMountType {
@@ -43,12 +44,9 @@ int bind_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_only);
int tmpfs_mount_parse(CustomMount **l, size_t *n, const char *s);
int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_only);
-int mount_all(const char *dest, MountSettingsMask mount_settings, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
+int mount_all(const char *dest, MountSettingsMask mount_settings, uid_t uid_shift, const char *selinux_apifs_context);
int mount_sysfs(const char *dest, MountSettingsMask mount_settings);
-int mount_cgroups(const char *dest, CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns);
-int mount_systemd_cgroup_writable(const char *dest, CGroupUnified unified_requested);
-
int mount_custom(const char *dest, CustomMount *mounts, size_t n, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
@@ -56,3 +54,5 @@ int setup_volatile_state(const char *directory, VolatileMode mode, bool userns,
int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s);
int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old);
+
+int tmpfs_patch_options(const char *options,uid_t uid_shift, const char *selinux_apifs_context, char **ret);
diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c
index 01c57bef56..9d0f8a9956 100644
--- a/src/nspawn/nspawn-network.c
+++ b/src/nspawn/nspawn-network.c
@@ -4,20 +4,21 @@
#include <net/if.h>
#include <sys/file.h>
-#include "libudev.h"
+#include "sd-device.h"
#include "sd-id128.h"
#include "sd-netlink.h"
#include "alloc-util.h"
#include "ether-addr-util.h"
#include "lockfile-util.h"
+#include "missing_network.h"
#include "netlink-util.h"
#include "nspawn-network.h"
#include "siphash24.h"
#include "socket-util.h"
#include "stat-util.h"
#include "string-util.h"
-#include "udev-util.h"
+#include "strv.h"
#include "util.h"
#define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1)
@@ -392,21 +393,24 @@ int remove_bridge(const char *bridge_name) {
return remove_one_link(rtnl, bridge_name);
}
-static int parse_interface(struct udev *udev, const char *name) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+static int parse_interface(const char *name) {
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
char ifi_str[2 + DECIMAL_STR_MAX(int)];
- int ifi;
+ int ifi, r;
ifi = (int) if_nametoindex(name);
if (ifi <= 0)
return log_error_errno(errno, "Failed to resolve interface %s: %m", name);
sprintf(ifi_str, "n%i", ifi);
- d = udev_device_new_from_device_id(udev, ifi_str);
- if (!d)
- return log_error_errno(errno, "Failed to get udev device for interface %s: %m", name);
+ r = sd_device_new_from_device_id(&d, ifi_str);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get device for interface %s: %m", name);
- if (udev_device_get_is_initialized(d) <= 0) {
+ r = sd_device_get_is_initialized(d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether interface %s is initialized or not: %m", name);
+ if (r == 0) {
log_error("Network interface %s is not initialized yet.", name);
return -EBUSY;
}
@@ -415,7 +419,6 @@ static int parse_interface(struct udev *udev, const char *name) {
}
int move_network_interfaces(pid_t pid, char **ifaces) {
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char **i;
int r;
@@ -427,17 +430,11 @@ int move_network_interfaces(pid_t pid, char **ifaces) {
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- udev = udev_new();
- if (!udev) {
- log_error("Failed to connect to udev.");
- return -ENOMEM;
- }
-
STRV_FOREACH(i, ifaces) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int ifi;
- ifi = parse_interface(udev, *i);
+ ifi = parse_interface(*i);
if (ifi < 0)
return ifi;
@@ -458,7 +455,6 @@ int move_network_interfaces(pid_t pid, char **ifaces) {
}
int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
unsigned idx = 0;
char **i;
@@ -471,19 +467,13 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- udev = udev_new();
- if (!udev) {
- log_error("Failed to connect to udev.");
- return -ENOMEM;
- }
-
STRV_FOREACH(i, ifaces) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
_cleanup_free_ char *n = NULL;
struct ether_addr mac;
int ifi;
- ifi = parse_interface(udev, *i);
+ ifi = parse_interface(*i);
if (ifi < 0)
return ifi;
@@ -546,7 +536,6 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
}
int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char **i;
int r;
@@ -558,18 +547,12 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- udev = udev_new();
- if (!udev) {
- log_error("Failed to connect to udev.");
- return -ENOMEM;
- }
-
STRV_FOREACH(i, ifaces) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
_cleanup_free_ char *n = NULL;
int ifi;
- ifi = parse_interface(udev, *i);
+ ifi = parse_interface(*i);
if (ifi < 0)
return ifi;
diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c
index 4a46a25eac..db2f5467b5 100644
--- a/src/nspawn/nspawn-patch-uid.c
+++ b/src/nspawn/nspawn-patch-uid.c
@@ -479,10 +479,6 @@ finish:
return r;
}
-int fd_patch_uid(int fd, uid_t shift, uid_t range) {
- return fd_patch_uid_internal(fd, false, shift, range);
-}
-
int path_patch_uid(const char *path, uid_t shift, uid_t range) {
int fd;
diff --git a/src/nspawn/nspawn-patch-uid.h b/src/nspawn/nspawn-patch-uid.h
index 31d2491234..b7c6ce2b73 100644
--- a/src/nspawn/nspawn-patch-uid.h
+++ b/src/nspawn/nspawn-patch-uid.h
@@ -4,5 +4,4 @@
#include <sys/types.h>
-int fd_patch_uid(int fd, uid_t shift, uid_t range);
int path_patch_uid(const char *path, uid_t shift, uid_t range);
diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c
index 93185ecaaa..a7cdfc1c7d 100644
--- a/src/nspawn/nspawn-register.c
+++ b/src/nspawn/nspawn-register.c
@@ -195,18 +195,17 @@ int register_machine(
r = sd_bus_call(bus, m, 0, &error, NULL);
}
- if (r < 0) {
- log_error("Failed to register machine: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to register machine: %s", bus_error_message(&error, r));
return 0;
}
-int terminate_machine(sd_bus *bus, pid_t pid) {
+int terminate_machine(
+ sd_bus *bus,
+ const char *machine_name) {
+
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- const char *path;
int r;
assert(bus);
@@ -216,36 +215,13 @@ int terminate_machine(sd_bus *bus, pid_t pid) {
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
- "GetMachineByPID",
- &error,
- &reply,
- "u",
- (uint32_t) pid);
- if (r < 0) {
- /* Note that the machine might already have been
- * cleaned up automatically, hence don't consider it a
- * failure if we cannot get the machine object. */
- log_debug("Failed to get machine: %s", bus_error_message(&error, r));
- return 0;
- }
-
- r = sd_bus_message_read(reply, "o", &path);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.machine1",
- path,
- "org.freedesktop.machine1.Machine",
- "Terminate",
+ "TerminateMachine",
&error,
NULL,
- NULL);
- if (r < 0) {
+ "s",
+ machine_name);
+ if (r < 0)
log_debug("Failed to terminate machine: %s", bus_error_message(&error, r));
- return 0;
- }
return 0;
}
@@ -260,9 +236,8 @@ int allocate_scope(
int kill_signal,
char **properties) {
+ _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_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
_cleanup_free_ char *scope = NULL;
const char *description, *object;
@@ -299,10 +274,12 @@ int allocate_scope(
description = strjoina("Container ", machine_name);
- r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)",
+ r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)(sv)(sv)",
"PIDs", "au", 1, pid,
"Description", "s", description,
"Delegate", "b", 1,
+ "CollectMode", "s", "inactive-or-failed",
+ "AddRef", "b", 1,
"Slice", "s", isempty(slice) ? SPECIAL_MACHINE_SLICE : slice);
if (r < 0)
return bus_log_create_error(r);
@@ -336,10 +313,8 @@ int allocate_scope(
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0) {
- log_error("Failed to allocate scope: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate scope: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "o", &object);
if (r < 0)
@@ -351,3 +326,63 @@ int allocate_scope(
return 0;
}
+
+int terminate_scope(
+ sd_bus *bus,
+ const char *machine_name) {
+
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *scope = NULL;
+ int r;
+
+ r = unit_name_mangle_with_suffix(machine_name, 0, ".scope", &scope);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle scope name: %m");
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "AbandonScope",
+ &error,
+ NULL,
+ "s",
+ scope);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to abandon scope '%s', ignoring: %s", scope, bus_error_message(&error, r));
+ sd_bus_error_free(&error);
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "KillUnit",
+ &error,
+ NULL,
+ "ssi",
+ scope,
+ "all",
+ (int32_t) SIGKILL);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to SIGKILL scope '%s', ignoring: %s", scope, bus_error_message(&error, r));
+ sd_bus_error_free(&error);
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "UnrefUnit",
+ &error,
+ NULL,
+ "s",
+ scope);
+ if (r < 0)
+ log_debug_errno(r, "Failed to drop reference to scope '%s', ignoring: %s", scope, bus_error_message(&error, r));
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h
index 30807b9687..05f5776f23 100644
--- a/src/nspawn/nspawn-register.h
+++ b/src/nspawn/nspawn-register.h
@@ -8,6 +8,7 @@
#include "nspawn-mount.h"
int register_machine(sd_bus *bus, const char *machine_name, pid_t pid, const char *directory, sd_id128_t uuid, int local_ifindex, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, bool keep_unit, const char *service);
-int terminate_machine(sd_bus *bus, pid_t pid);
+int terminate_machine(sd_bus *bus, const char *machine_name);
int allocate_scope(sd_bus *bus, const char *machine_name, pid_t pid, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties);
+int terminate_scope(sd_bus *bus, const char *machine_name);
diff --git a/src/nspawn/nspawn-seccomp.c b/src/nspawn/nspawn-seccomp.c
index eb1964bb6d..e7ef80f7d6 100644
--- a/src/nspawn/nspawn-seccomp.c
+++ b/src/nspawn/nspawn-seccomp.c
@@ -140,7 +140,7 @@ static int seccomp_add_default_syscall_filter(
*/
};
- int r, c = 0;
+ int r;
size_t i;
char **p;
@@ -148,23 +148,19 @@ static int seccomp_add_default_syscall_filter(
if (whitelist[i].capability != 0 && (cap_list_retain & (1ULL << whitelist[i].capability)) == 0)
continue;
- r = seccomp_add_syscall_filter_item(ctx, whitelist[i].name, SCMP_ACT_ALLOW, syscall_blacklist);
+ r = seccomp_add_syscall_filter_item(ctx, whitelist[i].name, SCMP_ACT_ALLOW, syscall_blacklist, false);
if (r < 0)
- /* If the system call is not known on this architecture, then that's fine, let's ignore it */
- log_debug_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", whitelist[i].name, seccomp_arch_to_string(arch));
- else
- c++;
+ return log_error_errno(r, "Failed to add syscall filter item %s: %m", whitelist[i].name);
}
STRV_FOREACH(p, syscall_whitelist) {
- r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist);
+ r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist, false);
if (r < 0)
- log_debug_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", *p, seccomp_arch_to_string(arch));
- else
- c++;
+ log_warning_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m",
+ *p, seccomp_arch_to_string(arch));
}
- return c;
+ return 0;
}
int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist) {
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
index 62a3486952..505e8cac40 100644
--- a/src/nspawn/nspawn-settings.c
+++ b/src/nspawn/nspawn-settings.c
@@ -177,7 +177,6 @@ int config_parse_capability(
for (;;) {
_cleanup_free_ char *word = NULL;
- int cap;
r = extract_first_word(&rvalue, &word, NULL, 0);
if (r < 0) {
@@ -187,13 +186,13 @@ int config_parse_capability(
if (r == 0)
break;
- cap = capability_from_name(word);
- if (cap < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word);
+ r = capability_from_name(word);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse capability, ignoring: %s", word);
continue;
}
- u |= UINT64_C(1) << cap;
+ u |= UINT64_C(1) << r;
}
if (u == 0)
@@ -391,7 +390,7 @@ int config_parse_network_zone(
j = strappend("vz-", rvalue);
if (!ifname_valid(j)) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name, ignoring: %s", rvalue);
return 0;
}
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index d522f3cb36..a63aa32e90 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -8,6 +8,7 @@
#include "conf-parser.h"
#include "macro.h"
+#include "missing_resource.h"
#include "nspawn-expose-ports.h"
#include "nspawn-mount.h"
@@ -84,9 +85,10 @@ typedef enum SettingsMask {
SETTING_RESOLV_CONF = UINT64_C(1) << 21,
SETTING_LINK_JOURNAL = UINT64_C(1) << 22,
SETTING_TIMEZONE = UINT64_C(1) << 23,
- SETTING_RLIMIT_FIRST = UINT64_C(1) << 24, /* we define one bit per resource limit here */
- SETTING_RLIMIT_LAST = UINT64_C(1) << (24 + _RLIMIT_MAX - 1),
- _SETTINGS_MASK_ALL = (UINT64_C(1) << (24 + _RLIMIT_MAX)) -1,
+ SETTING_EPHEMERAL = UINT64_C(1) << 24,
+ SETTING_RLIMIT_FIRST = UINT64_C(1) << 25, /* we define one bit per resource limit here */
+ SETTING_RLIMIT_LAST = UINT64_C(1) << (25 + _RLIMIT_MAX - 1),
+ _SETTINGS_MASK_ALL = (UINT64_C(1) << (25 + _RLIMIT_MAX)) -1,
_SETTING_FORCE_ENUM_WIDTH = UINT64_MAX
} SettingsMask;
@@ -102,6 +104,7 @@ assert_cc(sizeof(SETTING_RLIMIT_LAST) == 8);
typedef struct Settings {
/* [Run] */
StartMode start_mode;
+ bool ephemeral;
char **parameters;
char **environment;
char *user;
diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c
index 44f5b17675..0026e4e3fc 100644
--- a/src/nspawn/nspawn-setuid.c
+++ b/src/nspawn/nspawn-setuid.c
@@ -12,6 +12,7 @@
#include "mkdir.h"
#include "nspawn-setuid.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
@@ -44,6 +45,8 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
close_all_fds(NULL, 0);
+ (void) rlimit_nofile_safe();
+
execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env);
execle("/bin/getent", "getent", database, key, NULL, &empty_env);
_exit(EXIT_FAILURE);
@@ -88,75 +91,66 @@ int change_uid_gid(const char *user, char **_home) {
if (fd < 0)
return fd;
- f = fdopen(fd, "re");
+ f = fdopen(fd, "r");
if (!f)
return log_oom();
fd = -1;
r = read_line(f, LONG_LINE_MAX, &line);
- if (r == 0) {
- log_error("Failed to resolve user %s.", user);
- return -ESRCH;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+ "Failed to resolve user %s.", user);
if (r < 0)
return log_error_errno(r, "Failed to read from getent: %m");
(void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
x = strchr(line, ':');
- if (!x) {
- log_error("/etc/passwd entry has invalid user field.");
- return -EIO;
- }
+ if (!x)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "/etc/passwd entry has invalid user field.");
u = strchr(x+1, ':');
- if (!u) {
- log_error("/etc/passwd entry has invalid password field.");
- return -EIO;
- }
+ if (!u)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "/etc/passwd entry has invalid password field.");
u++;
g = strchr(u, ':');
- if (!g) {
- log_error("/etc/passwd entry has invalid UID field.");
- return -EIO;
- }
+ if (!g)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "/etc/passwd entry has invalid UID field.");
*g = 0;
g++;
x = strchr(g, ':');
- if (!x) {
- log_error("/etc/passwd entry has invalid GID field.");
- return -EIO;
- }
+ if (!x)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "/etc/passwd entry has invalid GID field.");
*x = 0;
h = strchr(x+1, ':');
- if (!h) {
- log_error("/etc/passwd entry has invalid GECOS field.");
- return -EIO;
- }
+ if (!h)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "/etc/passwd entry has invalid GECOS field.");
h++;
x = strchr(h, ':');
- if (!x) {
- log_error("/etc/passwd entry has invalid home directory field.");
- return -EIO;
- }
+ if (!x)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "/etc/passwd entry has invalid home directory field.");
*x = 0;
r = parse_uid(u, &uid);
- if (r < 0) {
- log_error("Failed to parse UID of user.");
- return -EIO;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to parse UID of user.");
r = parse_gid(g, &gid);
- if (r < 0) {
- log_error("Failed to parse GID of user.");
- return -EIO;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to parse GID of user.");
home = strdup(h);
if (!home)
@@ -170,16 +164,15 @@ int change_uid_gid(const char *user, char **_home) {
if (fd < 0)
return fd;
- f = fdopen(fd, "re");
+ f = fdopen(fd, "r");
if (!f)
return log_oom();
fd = -1;
r = read_line(f, LONG_LINE_MAX, &line);
- if (r == 0) {
- log_error("Failed to resolve user %s.", user);
- return -ESRCH;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+ "Failed to resolve user %s.", user);
if (r < 0)
return log_error_errno(r, "Failed to read from getent: %m");
diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c
index c83d55cb86..5d17df39d9 100644
--- a/src/nspawn/nspawn-stub-pid1.c
+++ b/src/nspawn/nspawn-stub-pid1.c
@@ -5,6 +5,8 @@
#include <sys/prctl.h>
#include <unistd.h>
+#include "def.h"
+#include "exit-status.h"
#include "fd-util.h"
#include "log.h"
#include "missing.h"
@@ -12,7 +14,6 @@
#include "process-util.h"
#include "signal-util.h"
#include "time-util.h"
-#include "def.h"
static int reset_environ(const char *new_environment, size_t length) {
unsigned long start, end;
@@ -122,7 +123,7 @@ int stub_pid1(sd_id128_t uuid) {
if (si.si_pid == pid && si.si_code == CLD_EXITED)
r = si.si_status; /* pass on exit code */
else
- r = 255; /* signal, coredump, timeout, … */
+ r = EXIT_EXCEPTION; /* signal, coredump, timeout, … */
goto finish;
}
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 56877bd932..91c97b60a7 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -6,6 +6,7 @@
#include <errno.h>
#include <getopt.h>
#include <grp.h>
+#include <linux/fs.h>
#include <linux/loop.h>
#include <pwd.h>
#include <sched.h>
@@ -17,7 +18,6 @@
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
-#include <sys/mount.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/types.h>
@@ -60,6 +60,7 @@
#include "missing.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "netlink-util.h"
#include "nspawn-cgroup.h"
#include "nspawn-def.h"
@@ -76,6 +77,7 @@
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "process-util.h"
#include "ptyfwd.h"
#include "random-util.h"
@@ -91,7 +93,7 @@
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
-#include "udev-util.h"
+#include "tmpfile-util.h"
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
@@ -190,7 +192,7 @@ static const char *arg_container_service_name = "systemd-nspawn";
static bool arg_notify_ready = false;
static bool arg_use_cgns = true;
static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS;
-static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO;
+static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_TMPFS_TMP;
static void *arg_root_hash = NULL;
static size_t arg_root_hash_size = 0;
static char **arg_syscall_whitelist = NULL;
@@ -204,11 +206,18 @@ static unsigned arg_cpuset_ncpus = 0;
static ResolvConfMode arg_resolv_conf = RESOLV_CONF_AUTO;
static TimezoneMode arg_timezone = TIMEZONE_AUTO;
-static void help(void) {
- (void) pager_open(false, false);
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ (void) pager_open(false);
+
+ r = terminal_urlify_man("systemd-nspawn", "1", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
- "Spawn a minimal namespace container for debugging, testing and building.\n\n"
+ "Spawn a command or OS in a light-weight container.\n\n"
" -h --help Show this help\n"
" --version Print version string\n"
" -q --quiet Do not show status information\n"
@@ -299,7 +308,12 @@ static void help(void) {
" --volatile[=MODE] Run the system in volatile mode\n"
" --settings=BOOLEAN Load additional settings from .nspawn file\n"
" --notify-ready=BOOLEAN Receive notifications from the child init process\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int custom_mount_check_all(void) {
@@ -309,14 +323,12 @@ static int custom_mount_check_all(void) {
CustomMount *m = &arg_custom_mounts[i];
if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) {
-
- if (arg_userns_chown) {
- log_error("--private-users-chown may not be combined with custom root mounts.");
- return -EINVAL;
- } else if (arg_uid_shift == UID_INVALID) {
- log_error("--private-users with automatic UID shift may not be combined with custom root mounts.");
- return -EINVAL;
- }
+ if (arg_userns_chown)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--private-users-chown may not be combined with custom root mounts.");
+ else if (arg_uid_shift == UID_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--private-users with automatic UID shift may not be combined with custom root mounts.");
}
}
@@ -391,8 +403,14 @@ static void parse_share_ns_env(const char *name, unsigned long ns_flag) {
}
static void parse_mount_settings_env(void) {
- int r;
const char *e;
+ int r;
+
+ r = getenv_bool("SYSTEMD_NSPAWN_TMPFS_TMP");
+ if (r >= 0)
+ SET_FLAG(arg_mount_settings, MOUNT_APPLY_TMPFS_TMP, r > 0);
+ else if (r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_NSPAWN_TMPFS_TMP, ignoring: %m");
e = getenv("SYSTEMD_NSPAWN_API_VFS_WRITABLE");
if (!e)
@@ -413,6 +431,30 @@ static void parse_mount_settings_env(void) {
SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_NETNS, false);
}
+static void parse_environment(void) {
+ const char *e;
+ int r;
+
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_IPC", CLONE_NEWIPC);
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_PID", CLONE_NEWPID);
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS);
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
+
+ parse_mount_settings_env();
+
+ /* SYSTEMD_NSPAWN_USE_CGNS=0 can be used to disable CLONE_NEWCGROUP use,
+ * even if it is supported. If not supported, it has no effect. */
+ r = getenv_bool("SYSTEMD_NSPAWN_USE_CGNS");
+ if (r == 0 || !cg_ns_supported())
+ arg_use_cgns = false;
+
+ e = getenv("SYSTEMD_NSPAWN_CONTAINER_SERVICE");
+ if (e)
+ arg_container_service_name = e;
+
+ detect_unified_cgroup_hierarchy_from_environment();
+}
+
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
@@ -521,7 +563,7 @@ static int parse_argv(int argc, char *argv[]) {
};
int c, r;
- const char *p, *e;
+ const char *p;
uint64_t plus = 0, minus = 0;
bool mask_all_settings = false, mask_no_settings = false;
@@ -532,8 +574,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -558,6 +599,7 @@ static int parse_argv(int argc, char *argv[]) {
case 'x':
arg_ephemeral = true;
+ arg_settings_mask |= SETTING_EPHEMERAL;
break;
case 'u':
@@ -591,10 +633,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_NETWORK_BRIDGE:
- if (!ifname_valid(optarg)) {
- log_error("Bridge interface name not valid: %s", optarg);
- return -EINVAL;
- }
+ if (!ifname_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Bridge interface name not valid: %s", optarg);
r = free_and_strdup(&arg_network_bridge, optarg);
if (r < 0)
@@ -617,10 +658,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NETWORK_INTERFACE:
- if (!ifname_valid(optarg)) {
- log_error("Network interface name not valid: %s", optarg);
- return -EINVAL;
- }
+ if (!ifname_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Network interface name not valid: %s", optarg);
if (strv_extend(&arg_network_interfaces, optarg) < 0)
return log_oom();
@@ -631,10 +671,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_NETWORK_MACVLAN:
- if (!ifname_valid(optarg)) {
- log_error("MACVLAN network interface name not valid: %s", optarg);
- return -EINVAL;
- }
+ if (!ifname_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "MACVLAN network interface name not valid: %s", optarg);
if (strv_extend(&arg_network_macvlan, optarg) < 0)
return log_oom();
@@ -645,10 +684,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_NETWORK_IPVLAN:
- if (!ifname_valid(optarg)) {
- log_error("IPVLAN network interface name not valid: %s", optarg);
- return -EINVAL;
- }
+ if (!ifname_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "IPVLAN network interface name not valid: %s", optarg);
if (strv_extend(&arg_network_ipvlan, optarg) < 0)
return log_oom();
@@ -667,20 +705,18 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'b':
- if (arg_start_mode == START_PID2) {
- log_error("--boot and --as-pid2 may not be combined.");
- return -EINVAL;
- }
+ if (arg_start_mode == START_PID2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--boot and --as-pid2 may not be combined.");
arg_start_mode = START_BOOT;
arg_settings_mask |= SETTING_START_MODE;
break;
case 'a':
- if (arg_start_mode == START_BOOT) {
- log_error("--boot and --as-pid2 may not be combined.");
- return -EINVAL;
- }
+ if (arg_start_mode == START_BOOT)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--boot and --as-pid2 may not be combined.");
arg_start_mode = START_PID2;
arg_settings_mask |= SETTING_START_MODE;
@@ -691,10 +727,9 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Invalid UUID: %s", optarg);
- if (sd_id128_is_null(arg_uuid)) {
- log_error("Machine UUID may not be all zeroes.");
- return -EINVAL;
- }
+ if (sd_id128_is_null(arg_uuid))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Machine UUID may not be all zeroes.");
arg_settings_mask |= SETTING_MACHINE_ID;
break;
@@ -707,10 +742,9 @@ static int parse_argv(int argc, char *argv[]) {
if (isempty(optarg))
arg_machine = mfree(arg_machine);
else {
- if (!machine_name_is_valid(optarg)) {
- log_error("Invalid machine name: %s", optarg);
- return -EINVAL;
- }
+ if (!machine_name_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid machine name: %s", optarg);
r = free_and_strdup(&arg_machine, optarg);
if (r < 0)
@@ -722,10 +756,9 @@ static int parse_argv(int argc, char *argv[]) {
if (isempty(optarg))
arg_hostname = mfree(arg_hostname);
else {
- if (!hostname_is_valid(optarg, false)) {
- log_error("Invalid hostname: %s", optarg);
- return -EINVAL;
- }
+ if (!hostname_is_valid(optarg, false))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid hostname: %s", optarg);
r = free_and_strdup(&arg_hostname, optarg);
if (r < 0)
@@ -767,18 +800,14 @@ static int parse_argv(int argc, char *argv[]) {
else
minus = (uint64_t) -1;
} else {
- int cap;
-
- cap = capability_from_name(t);
- if (cap < 0) {
- log_error("Failed to parse capability %s.", t);
- return -EINVAL;
- }
+ r = capability_from_name(t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse capability %s.", t);
if (c == ARG_CAPABILITY)
- plus |= 1ULL << (uint64_t) cap;
+ plus |= 1ULL << r;
else
- minus |= 1ULL << (uint64_t) cap;
+ minus |= 1ULL << r;
}
}
@@ -842,10 +871,9 @@ static int parse_argv(int argc, char *argv[]) {
case 'E': {
char **n;
- if (!env_assignment_is_valid(optarg)) {
- log_error("Environment variable assignment '%s' is not valid.", optarg);
- return -EINVAL;
- }
+ if (!env_assignment_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Environment variable assignment '%s' is not valid.", optarg);
n = strv_env_set(arg_setenv, optarg);
if (!n)
@@ -884,10 +912,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_PERSONALITY:
arg_personality = personality_from_string(optarg);
- if (arg_personality == PERSONALITY_INVALID) {
- log_error("Unknown or unsupported personality '%s'.", optarg);
- return -EINVAL;
- }
+ if (arg_personality == PERSONALITY_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown or unsupported personality '%s'.", optarg);
arg_settings_mask |= SETTING_PERSONALITY;
break;
@@ -903,10 +930,10 @@ static int parse_argv(int argc, char *argv[]) {
VolatileMode m;
m = volatile_mode_from_string(optarg);
- if (m < 0) {
- log_error("Failed to parse --volatile= argument: %s", optarg);
- return -EINVAL;
- } else
+ if (m < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse --volatile= argument: %s", optarg);
+ else
arg_volatile_mode = m;
}
@@ -980,10 +1007,9 @@ static int parse_argv(int argc, char *argv[]) {
arg_userns_mode = USER_NAMESPACE_FIXED;
}
- if (arg_uid_range <= 0) {
- log_error("UID range cannot be 0.");
- return -EINVAL;
- }
+ if (arg_uid_range <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "UID range cannot be 0.");
arg_settings_mask |= SETTING_USERNS;
break;
@@ -1013,10 +1039,9 @@ static int parse_argv(int argc, char *argv[]) {
}
arg_kill_signal = signal_from_string(optarg);
- if (arg_kill_signal < 0) {
- log_error("Cannot parse signal: %s", optarg);
- return -EINVAL;
- }
+ if (arg_kill_signal < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot parse signal: %s", optarg);
arg_settings_mask |= SETTING_KILL_SIGNAL;
break;
@@ -1057,10 +1082,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CHDIR:
- if (!path_is_absolute(optarg)) {
- log_error("Working directory %s is not an absolute path.", optarg);
- return -EINVAL;
- }
+ if (!path_is_absolute(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Working directory %s is not an absolute path.", optarg);
r = free_and_strdup(&arg_chdir, optarg);
if (r < 0)
@@ -1079,10 +1103,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_NOTIFY_READY:
r = parse_boolean(optarg);
- if (r < 0) {
- log_error("%s is not a valid notify mode. Valid modes are: yes, no, and ready.", optarg);
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s is not a valid notify mode. Valid modes are: yes, no, and ready.", optarg);
arg_notify_ready = r;
arg_settings_mask |= SETTING_NOTIFY_READY;
break;
@@ -1147,20 +1170,18 @@ static int parse_argv(int argc, char *argv[]) {
}
eq = strchr(optarg, '=');
- if (!eq) {
- log_error("--rlimit= expects an '=' assignment.");
- return -EINVAL;
- }
+ if (!eq)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--rlimit= expects an '=' assignment.");
name = strndup(optarg, eq - optarg);
if (!name)
return log_oom();
rl = rlimit_from_string_harder(name);
- if (rl < 0) {
- log_error("Unknown resource limit: %s", name);
- return -EINVAL;
- }
+ if (rl < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown resource limit: %s", name);
if (!arg_rlimit[rl]) {
arg_rlimit[rl] = new0(struct rlimit, 1);
@@ -1208,10 +1229,9 @@ static int parse_argv(int argc, char *argv[]) {
}
arg_resolv_conf = resolv_conf_mode_from_string(optarg);
- if (arg_resolv_conf < 0) {
- log_error("Failed to parse /etc/resolv.conf mode: %s", optarg);
- return -EINVAL;
- }
+ if (arg_resolv_conf < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse /etc/resolv.conf mode: %s", optarg);
arg_settings_mask |= SETTING_RESOLV_CONF;
break;
@@ -1223,10 +1243,9 @@ static int parse_argv(int argc, char *argv[]) {
}
arg_timezone = timezone_mode_from_string(optarg);
- if (arg_timezone < 0) {
- log_error("Failed to parse /etc/localtime mode: %s", optarg);
- return -EINVAL;
- }
+ if (arg_timezone < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse /etc/localtime mode: %s", optarg);
arg_settings_mask |= SETTING_TIMEZONE;
break;
@@ -1238,21 +1257,37 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- /* If --network-namespace-path is given with any other network-related option,
- * we need to error out, to avoid conflicts between different network options. */
- if (arg_network_namespace_path &&
- (arg_network_interfaces || arg_network_macvlan ||
- arg_network_ipvlan || arg_network_veth_extra ||
- arg_network_bridge || arg_network_zone ||
- arg_network_veth || arg_private_network)) {
- log_error("--network-namespace-path cannot be combined with other network options.");
- return -EINVAL;
+ if (argc > optind) {
+ strv_free(arg_parameters);
+ arg_parameters = strv_copy(argv + optind);
+ if (!arg_parameters)
+ return log_oom();
+
+ arg_settings_mask |= SETTING_START_MODE;
}
- parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_IPC", CLONE_NEWIPC);
- parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_PID", CLONE_NEWPID);
- parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS);
- parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
+ if (arg_ephemeral && arg_template && !arg_directory)
+ /* User asked for ephemeral execution but specified --template= instead of --directory=. Semantically
+ * such an invocation makes some sense, see https://github.com/systemd/systemd/issues/3667. Let's
+ * accept this here, and silently make "--ephemeral --template=" equivalent to "--ephemeral
+ * --directory=". */
+ arg_directory = TAKE_PTR(arg_template);
+
+ arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus;
+
+ /* Load all settings from .nspawn files */
+ if (mask_no_settings)
+ arg_settings_mask = 0;
+
+ /* Don't load any settings from .nspawn files */
+ if (mask_all_settings)
+ arg_settings_mask = _SETTINGS_MASK_ALL;
+
+ return 1;
+}
+
+static int verify_arguments(void) {
+ int r;
if (arg_userns_mode != USER_NAMESPACE_NO)
arg_mount_settings |= MOUNT_USE_USERNS;
@@ -1260,146 +1295,78 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_private_network)
arg_mount_settings |= MOUNT_APPLY_APIVFS_NETNS;
- parse_mount_settings_env();
-
if (!(arg_clone_ns_flags & CLONE_NEWPID) ||
!(arg_clone_ns_flags & CLONE_NEWUTS)) {
arg_register = false;
- if (arg_start_mode != START_PID1) {
- log_error("--boot cannot be used without namespacing.");
- return -EINVAL;
- }
+ if (arg_start_mode != START_PID1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--boot cannot be used without namespacing.");
}
if (arg_userns_mode == USER_NAMESPACE_PICK)
arg_userns_chown = true;
- if (arg_keep_unit && arg_register && cg_pid_get_owner_uid(0, NULL) >= 0) {
+ if (arg_start_mode == START_BOOT && arg_kill_signal <= 0)
+ arg_kill_signal = SIGRTMIN+3;
+
+ if (arg_keep_unit && arg_register && cg_pid_get_owner_uid(0, NULL) >= 0)
/* Save the user from accidentally registering either user-$SESSION.scope or user@.service.
* The latter is not technically a user session, but we don't need to labour the point. */
- log_error("--keep-unit --register=yes may not be used when invoked from a user session.");
- return -EINVAL;
- }
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--keep-unit --register=yes may not be used when invoked from a user session.");
- if (arg_directory && arg_image) {
- log_error("--directory= and --image= may not be combined.");
- return -EINVAL;
- }
+ if (arg_directory && arg_image)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--directory= and --image= may not be combined.");
- if (arg_template && arg_image) {
- log_error("--template= and --image= may not be combined.");
- return -EINVAL;
- }
+ if (arg_template && arg_image)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--template= and --image= may not be combined.");
- if (arg_ephemeral && arg_template && !arg_directory) {
- /* User asked for ephemeral execution but specified --template= instead of --directory=. Semantically
- * such an invocation makes some sense, see https://github.com/systemd/systemd/issues/3667. Let's
- * accept this here, and silently make "--ephemeral --template=" equivalent to "--ephemeral
- * --directory=". */
+ if (arg_template && !(arg_directory || arg_machine))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--template= needs --directory= or --machine=.");
- arg_directory = TAKE_PTR(arg_template);
- }
+ if (arg_ephemeral && arg_template)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--ephemeral and --template= may not be combined.");
- if (arg_template && !(arg_directory || arg_machine)) {
- log_error("--template= needs --directory= or --machine=.");
- return -EINVAL;
- }
+ if (arg_ephemeral && !IN_SET(arg_link_journal, LINK_NO, LINK_AUTO))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--ephemeral and --link-journal= may not be combined.");
- if (arg_ephemeral && arg_template) {
- log_error("--ephemeral and --template= may not be combined.");
- return -EINVAL;
- }
-
- if (arg_ephemeral && !IN_SET(arg_link_journal, LINK_NO, LINK_AUTO)) {
- log_error("--ephemeral and --link-journal= may not be combined.");
- return -EINVAL;
- }
-
- if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported()) {
- log_error("--private-users= is not supported, kernel compiled without user namespace support.");
- return -EOPNOTSUPP;
- }
-
- if (arg_userns_chown && arg_read_only) {
- log_error("--read-only and --private-users-chown may not be combined.");
- return -EINVAL;
- }
+ if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported())
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--private-users= is not supported, kernel compiled without user namespace support.");
- if (arg_network_bridge && arg_network_zone) {
- log_error("--network-bridge= and --network-zone= may not be combined.");
- return -EINVAL;
- }
+ if (arg_userns_chown && arg_read_only)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--read-only and --private-users-chown may not be combined.");
- if (argc > optind) {
- arg_parameters = strv_copy(argv + optind);
- if (!arg_parameters)
- return log_oom();
+ /* If --network-namespace-path is given with any other network-related option,
+ * we need to error out, to avoid conflicts between different network options. */
+ if (arg_network_namespace_path &&
+ (arg_network_interfaces || arg_network_macvlan ||
+ arg_network_ipvlan || arg_network_veth_extra ||
+ arg_network_bridge || arg_network_zone ||
+ arg_network_veth || arg_private_network))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--network-namespace-path cannot be combined with other network options.");
- arg_settings_mask |= SETTING_START_MODE;
- }
+ if (arg_network_bridge && arg_network_zone)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--network-bridge= and --network-zone= may not be combined.");
- /* Load all settings from .nspawn files */
- if (mask_no_settings)
- arg_settings_mask = 0;
+ if (arg_userns_mode != USER_NAMESPACE_NO && (arg_mount_settings & MOUNT_APPLY_APIVFS_NETNS) && !arg_private_network)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid namespacing settings. Mounting sysfs with --private-users requires --private-network.");
- /* Don't load any settings from .nspawn files */
- if (mask_all_settings)
- arg_settings_mask = _SETTINGS_MASK_ALL;
+ if (arg_userns_mode != USER_NAMESPACE_NO && !(arg_mount_settings & MOUNT_APPLY_APIVFS_RO))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot combine --private-users with read-write mounts.");
- arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus;
+ if (arg_volatile_mode != VOLATILE_NO && arg_read_only)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot combine --read-only with --volatile. Note that --volatile already implies a read-only base hierarchy.");
- r = cg_unified_flush();
- if (r < 0)
- return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
+ if (arg_expose_ports && !arg_private_network)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --port= without private networking.");
- e = getenv("SYSTEMD_NSPAWN_CONTAINER_SERVICE");
- if (e)
- arg_container_service_name = e;
-
- r = getenv_bool("SYSTEMD_NSPAWN_USE_CGNS");
- if (r < 0)
- arg_use_cgns = cg_ns_supported();
- else
- arg_use_cgns = r;
+#if ! HAVE_LIBIPTC
+ if (arg_expose_ports)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--port= is not supported, compiled without libiptc support.");
+#endif
r = custom_mount_check_all();
if (r < 0)
return r;
- return 1;
-}
-
-static int verify_arguments(void) {
- if (arg_userns_mode != USER_NAMESPACE_NO && (arg_mount_settings & MOUNT_APPLY_APIVFS_NETNS) && !arg_private_network) {
- log_error("Invalid namespacing settings. Mounting sysfs with --private-users requires --private-network.");
- return -EINVAL;
- }
-
- if (arg_userns_mode != USER_NAMESPACE_NO && !(arg_mount_settings & MOUNT_APPLY_APIVFS_RO)) {
- log_error("Cannot combine --private-users with read-write mounts.");
- return -EINVAL;
- }
-
- if (arg_volatile_mode != VOLATILE_NO && arg_read_only) {
- log_error("Cannot combine --read-only with --volatile. Note that --volatile already implies a read-only base hierarchy.");
- return -EINVAL;
- }
-
- if (arg_expose_ports && !arg_private_network) {
- log_error("Cannot use --port= without private networking.");
- return -EINVAL;
- }
-
-#if ! HAVE_LIBIPTC
- if (arg_expose_ports) {
- log_error("--port= is not supported, compiled without libiptc support.");
- return -EOPNOTSUPP;
- }
-#endif
-
- if (arg_start_mode == START_BOOT && arg_kill_signal <= 0)
- arg_kill_signal = SIGRTMIN+3;
-
return 0;
}
@@ -1447,17 +1414,10 @@ static int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t u
}
static const char *timezone_from_path(const char *path) {
- const char *z;
-
- z = path_startswith(path, "../usr/share/zoneinfo/");
- if (z)
- return z;
-
- z = path_startswith(path, "/usr/share/zoneinfo/");
- if (z)
- return z;
-
- return NULL;
+ return PATH_STARTSWITH_SET(
+ path,
+ "../usr/share/zoneinfo/",
+ "/usr/share/zoneinfo/");
}
static int setup_timezone(const char *dest) {
@@ -1646,12 +1606,7 @@ static int setup_resolv_conf(const char *dest) {
if (arg_private_network)
m = RESOLV_CONF_OFF;
else if (have_resolv_conf(STATIC_RESOLV_CONF) > 0 && resolved_listening() > 0)
- /* resolved is enabled on the host. In this, case bind mount its static resolv.conf file into the
- * container, so that the container can use the host's resolver. Given that network namespacing is
- * disabled it's only natural of the container also uses the host's resolver. It also has the big
- * advantage that the container will be able to follow the host's DNS server configuration changes
- * transparently. */
- m = RESOLV_CONF_BIND_STATIC;
+ m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? RESOLV_CONF_BIND_STATIC : RESOLV_CONF_COPY_STATIC;
else if (have_resolv_conf("/etc/resolv.conf") > 0)
m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? RESOLV_CONF_BIND_HOST : RESOLV_CONF_COPY_HOST;
else
@@ -1779,19 +1734,24 @@ static int copy_devnodes(const char *dest) {
struct stat st;
from = strappend("/dev/", d);
+ if (!from)
+ return log_oom();
+
to = prefix_root(dest, from);
+ if (!to)
+ return log_oom();
if (stat(from, &st) < 0) {
if (errno != ENOENT)
return log_error_errno(errno, "Failed to stat %s: %m", from);
- } else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
-
- log_error("%s is not a char or block device, cannot copy.", from);
- return -EIO;
+ } else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "%s is not a char or block device, cannot copy.", from);
+ else {
+ _cleanup_free_ char *sl = NULL, *prefixed = NULL, *dn = NULL, *t = NULL;
- } else {
if (mknod(to, st.st_mode, st.st_rdev) < 0) {
/* Explicitly warn the user when /dev is already populated. */
if (errno == EEXIST)
@@ -1799,8 +1759,7 @@ static int copy_devnodes(const char *dest) {
if (errno != EPERM)
return log_error_errno(errno, "mknod(%s) failed: %m", to);
- /* Some systems abusively restrict mknod but
- * allow bind mounts. */
+ /* Some systems abusively restrict mknod but allow bind mounts. */
r = touch(to);
if (r < 0)
return log_error_errno(r, "touch (%s) failed: %m", to);
@@ -1812,6 +1771,28 @@ static int copy_devnodes(const char *dest) {
r = userns_lchown(to, 0, 0);
if (r < 0)
return log_error_errno(r, "chown() of device node %s failed: %m", to);
+
+ dn = strjoin("/dev/", S_ISCHR(st.st_mode) ? "char" : "block");
+ if (!dn)
+ return log_oom();
+
+ r = userns_mkdir(dest, dn, 0755, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create '%s': %m", dn);
+
+ if (asprintf(&sl, "%s/%u:%u", dn, major(st.st_rdev), minor(st.st_rdev)) < 0)
+ return log_oom();
+
+ prefixed = prefix_root(dest, sl);
+ if (!prefixed)
+ return log_oom();
+
+ t = strjoin("../", d);
+ if (!t)
+ return log_oom();
+
+ if (symlink(t, prefixed) < 0)
+ log_debug_errno(errno, "Failed to symlink '%s' to '%s': %m", t, prefixed);
}
}
@@ -1987,7 +1968,7 @@ static int setup_journal(const char *directory) {
_cleanup_free_ char *d = NULL;
const char *p, *q;
bool try;
- char id[33];
+ char id[33], *dirname;
int r;
/* Don't link journals in ephemeral mode */
@@ -2011,17 +1992,15 @@ static int setup_journal(const char *directory) {
return -EEXIST;
}
- r = userns_mkdir(directory, "/var", 0755, 0, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to create /var: %m");
-
- r = userns_mkdir(directory, "/var/log", 0755, 0, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to create /var/log: %m");
-
- r = userns_mkdir(directory, "/var/log/journal", 0755, 0, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to create /var/log/journal: %m");
+ FOREACH_STRING(dirname, "/var", "/var/log", "/var/log/journal") {
+ r = userns_mkdir(directory, dirname, 0755, 0, 0);
+ if (r < 0) {
+ bool ignore = r == -EROFS && try;
+ log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r,
+ "Failed to create %s%s: %m", dirname, ignore ? ", ignoring" : "");
+ return ignore ? 0 : r;
+ }
+ }
(void) sd_id128_to_string(arg_uuid, id);
@@ -2032,16 +2011,16 @@ static int setup_journal(const char *directory) {
if (try)
return 0;
- log_error("%s: already a mount point, refusing to use for journal", p);
- return -EEXIST;
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "%s: already a mount point, refusing to use for journal", p);
}
if (path_is_mount_point(q, NULL, 0) > 0) {
if (try)
return 0;
- log_error("%s: already a mount point, refusing to use for journal", q);
- return -EEXIST;
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "%s: already a mount point, refusing to use for journal", q);
}
r = readlink_and_make_absolute(p, &d);
@@ -2138,7 +2117,7 @@ static int reset_audit_loginuid(void) {
if (streq(p, "4294967295"))
return 0;
- r = write_string_file("/proc/self/loginuid", "4294967295", 0);
+ r = write_string_file("/proc/self/loginuid", "4294967295", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0) {
log_error_errno(r,
"Failed to reset audit login UID. This probably means that your kernel is too\n"
@@ -2213,10 +2192,9 @@ static int setup_machine_id(const char *directory) {
return log_error_errno(r, "Failed to acquire randomized machine UUID: %m");
}
} else {
- if (sd_id128_is_null(id)) {
- log_error("Machine ID in container image is zero, refusing.");
- return -EINVAL;
- }
+ if (sd_id128_is_null(id))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Machine ID in container image is zero, refusing.");
arg_uuid = id;
}
@@ -2297,12 +2275,12 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) {
_fallthrough_;
case CLD_DUMPED:
- log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status));
- return -EIO;
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status));
default:
- log_error("Container %s failed due to unknown reason.", arg_machine);
- return -EIO;
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Container %s failed due to unknown reason.", arg_machine);
}
}
@@ -2492,18 +2470,16 @@ static int determine_uid_shift(const char *directory) {
arg_uid_shift = st.st_uid & UINT32_C(0xffff0000);
- if (arg_uid_shift != (st.st_gid & UINT32_C(0xffff0000))) {
- log_error("UID and GID base of %s don't match.", directory);
- return -EINVAL;
- }
+ if (arg_uid_shift != (st.st_gid & UINT32_C(0xffff0000)))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "UID and GID base of %s don't match.", directory);
arg_uid_range = UINT32_C(0x10000);
}
- if (arg_uid_shift > (uid_t) -1 - arg_uid_range) {
- log_error("UID base too high for UID range.");
- return -EINVAL;
- }
+ if (arg_uid_shift > (uid_t) -1 - arg_uid_range)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "UID base too high for UID range.");
return 0;
}
@@ -2536,6 +2512,17 @@ static int inner_child(
_cleanup_strv_free_ char **env_use = NULL;
int r;
+ /* This is the "inner" child process, i.e. the one forked off by the "outer" child process, which is the one
+ * the container manager itself forked off. At the time of clone() it gained its own CLONE_NEWNS, CLONE_NEWPID,
+ * CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWUSER namespaces. Note that it has its own CLONE_NEWNS namespace,
+ * separate from the CLONE_NEWNS created for the "outer" child, and also separate from the host's CLONE_NEWNS
+ * namespace. The reason for having two levels of CLONE_NEWNS namespaces is that the "inner" one is owned by
+ * the CLONE_NEWUSER namespace of the container, while the "outer" one is owned by the host's CLONE_NEWUSER
+ * namespace.
+ *
+ * Note at this point we have no CLONE_NEWNET namespace yet. We'll acquire that one later through
+ * unshare(). See below. */
+
assert(barrier);
assert(directory);
assert(kmsg_socket >= 0);
@@ -2545,10 +2532,9 @@ static int inner_child(
(void) barrier_place(barrier); /* #1 */
/* Wait until the parent wrote the UID map */
- if (!barrier_place_and_sync(barrier)) { /* #2 */
- log_error("Parent died too early");
- return -ESRCH;
- }
+ if (!barrier_place_and_sync(barrier)) /* #2 */
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+ "Parent died too early");
}
r = reset_uid_gid();
@@ -2558,7 +2544,6 @@ static int inner_child(
r = mount_all(NULL,
arg_mount_settings | MOUNT_IN_USERNS,
arg_uid_shift,
- arg_uid_range,
arg_selinux_apifs_context);
if (r < 0)
return r;
@@ -2578,12 +2563,11 @@ static int inner_child(
/* Wait until we are cgroup-ified, so that we
* can mount the right cgroup path writable */
- if (!barrier_place_and_sync(barrier)) { /* #4 */
- log_error("Parent died too early");
- return -ESRCH;
- }
+ if (!barrier_place_and_sync(barrier)) /* #4 */
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+ "Parent died too early");
- if (arg_use_cgns && cg_ns_supported()) {
+ if (arg_use_cgns) {
r = unshare(CLONE_NEWCGROUP);
if (r < 0)
return log_error_errno(errno, "Failed to unshare cgroup namespace: %m");
@@ -2701,10 +2685,9 @@ static int inner_child(
/* Let the parent know that we are ready and
* wait until the parent is ready with the
* setup, too... */
- if (!barrier_place_and_sync(barrier)) { /* #5 */
- log_error("Parent died too early");
- return -ESRCH;
- }
+ if (!barrier_place_and_sync(barrier)) /* #5 */
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+ "Parent died too early");
if (arg_chdir)
if (chdir(arg_chdir) < 0)
@@ -2746,7 +2729,18 @@ static int inner_child(
exec_target = "/usr/lib/systemd/systemd, /lib/systemd/systemd, /sbin/init";
} else if (!strv_isempty(arg_parameters)) {
+ const char *dollar_path;
+
exec_target = arg_parameters[0];
+
+ /* Use the user supplied search $PATH if there is one, or DEFAULT_PATH_COMPAT if not to search the
+ * binary. */
+ dollar_path = strv_env_get(env_use, "PATH");
+ if (dollar_path) {
+ if (putenv((char*) dollar_path) != 0)
+ return log_error_errno(errno, "Failed to update $PATH: %m");
+ }
+
execvpe(arg_parameters[0], arg_parameters, env_use);
} else {
if (!arg_chdir)
@@ -2763,10 +2757,10 @@ static int inner_child(
}
static int setup_sd_notify_child(void) {
- static const int one = 1;
- int fd = -1;
+ _cleanup_close_ int fd = -1;
union sockaddr_union sa = {
- .sa.sa_family = AF_UNIX,
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = NSPAWN_NOTIFY_SOCKET_PATH,
};
int r;
@@ -2775,28 +2769,21 @@ static int setup_sd_notify_child(void) {
return log_error_errno(errno, "Failed to allocate notification socket: %m");
(void) mkdir_parents(NSPAWN_NOTIFY_SOCKET_PATH, 0755);
- (void) unlink(NSPAWN_NOTIFY_SOCKET_PATH);
+ (void) sockaddr_un_unlink(&sa.un);
- strncpy(sa.un.sun_path, NSPAWN_NOTIFY_SOCKET_PATH, sizeof(sa.un.sun_path)-1);
r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
- if (r < 0) {
- safe_close(fd);
- return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
- }
+ if (r < 0)
+ return log_error_errno(errno, "bind(" NSPAWN_NOTIFY_SOCKET_PATH ") failed: %m");
r = userns_lchown(NSPAWN_NOTIFY_SOCKET_PATH, 0, 0);
- if (r < 0) {
- safe_close(fd);
+ if (r < 0)
return log_error_errno(r, "Failed to chown " NSPAWN_NOTIFY_SOCKET_PATH ": %m");
- }
- r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
- if (r < 0) {
- safe_close(fd);
- return log_error_errno(errno, "SO_PASSCRED failed: %m");
- }
+ r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
+ if (r < 0)
+ return log_error_errno(r, "SO_PASSCRED failed: %m");
- return fd;
+ return TAKE_FD(fd);
}
static int outer_child(
@@ -2821,6 +2808,11 @@ static int outer_child(
pid_t pid;
ssize_t l;
+ /* This is the "outer" child process, i.e the one forked off by the container manager itself. It already has
+ * its own CLONE_NEWNS namespace (which was created by the clone()). It still lives in the host's CLONE_NEWPID,
+ * CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWUSER and CLONE_NEWNET namespaces. After it completed a number of
+ * initializations a second child (the "inner" one) is forked off it, and it exits. */
+
assert(barrier);
assert(directory);
assert(console);
@@ -2883,10 +2875,9 @@ static int outer_child(
l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send UID shift: %m");
- if (l != sizeof(arg_uid_shift)) {
- log_error("Short write while sending UID shift.");
- return -EIO;
- }
+ if (l != sizeof(arg_uid_shift))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Short write while sending UID shift.");
if (arg_userns_mode == USER_NAMESPACE_PICK) {
/* When we are supposed to pick the UID shift, the parent will check now whether the UID shift
@@ -2896,13 +2887,13 @@ static int outer_child(
l = recv(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0);
if (l < 0)
return log_error_errno(errno, "Failed to recv UID shift: %m");
- if (l != sizeof(arg_uid_shift)) {
- log_error("Short read while receiving UID shift.");
- return -EIO;
- }
+ if (l != sizeof(arg_uid_shift))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Short read while receiving UID shift.");
}
- log_info("Selected user namespace base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range);
+ log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
+ "Selected user namespace base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range);
}
if (dissected_image) {
@@ -2923,10 +2914,9 @@ static int outer_child(
l = send(unified_cgroup_hierarchy_socket, &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send cgroup mode: %m");
- if (l != sizeof(arg_unified_cgroup_hierarchy)) {
- log_error("Short write while sending cgroup mode.");
- return -EIO;
- }
+ if (l != sizeof(arg_unified_cgroup_hierarchy))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Short write while sending cgroup mode.");
unified_cgroup_hierarchy_socket = safe_close(unified_cgroup_hierarchy_socket);
}
@@ -2990,7 +2980,6 @@ static int outer_child(
r = mount_all(directory,
arg_mount_settings,
arg_uid_shift,
- arg_uid_range,
arg_selinux_apifs_context);
if (r < 0)
return r;
@@ -3048,7 +3037,7 @@ static int outer_child(
if (r < 0)
return r;
- if (!arg_use_cgns || !cg_ns_supported()) {
+ if (!arg_use_cgns) {
r = mount_cgroups(
directory,
arg_unified_cgroup_hierarchy,
@@ -3091,7 +3080,7 @@ static int outer_child(
if (arg_network_namespace_path) {
r = namespace_enter(-1, -1, netns_fd, -1, -1);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to join network namespace: %m");
}
r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, fds);
@@ -3104,18 +3093,16 @@ static int outer_child(
l = send(pid_socket, &pid, sizeof(pid), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send PID: %m");
- if (l != sizeof(pid)) {
- log_error("Short write while sending PID.");
- return -EIO;
- }
+ if (l != sizeof(pid))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Short write while sending PID.");
l = send(uuid_socket, &arg_uuid, sizeof(arg_uuid), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send machine ID: %m");
- if (l != sizeof(arg_uuid)) {
- log_error("Short write while sending machine ID.");
- return -EIO;
- }
+ if (l != sizeof(arg_uuid))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Short write while sending machine ID.");
l = send_one_fd(notify_socket, fd, 0);
if (l < 0)
@@ -3208,13 +3195,13 @@ static int setup_uid_map(pid_t pid) {
xsprintf(uid_map, "/proc/" PID_FMT "/uid_map", pid);
xsprintf(line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, arg_uid_shift, arg_uid_range);
- r = write_string_file(uid_map, line, 0);
+ r = write_string_file(uid_map, line, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return log_error_errno(r, "Failed to write UID map: %m");
/* We always assign the same UID and GID ranges */
xsprintf(uid_map, "/proc/" PID_FMT "/gid_map", pid);
- r = write_string_file(uid_map, line, 0);
+ r = write_string_file(uid_map, line, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return log_error_errno(r, "Failed to write GID map: %m");
@@ -3324,6 +3311,9 @@ static int merge_settings(Settings *settings, const char *path) {
strv_free_and_replace(arg_parameters, settings->parameters);
}
+ if ((arg_settings_mask & SETTING_EPHEMERAL) == 0)
+ arg_ephemeral = settings->ephemeral;
+
if ((arg_settings_mask & SETTING_PIVOT_ROOT) == 0 &&
settings->pivot_root_new) {
free_and_replace(arg_pivot_root_new, settings->pivot_root_new);
@@ -3701,10 +3691,12 @@ static int run(int master,
return log_error_errno(errno, "Cannot open file %s: %m", arg_network_namespace_path);
r = fd_is_network_ns(netns_fd);
- if (r < 0 && r != -ENOTTY)
+ if (r == -EUCLEAN)
+ log_debug_errno(r, "Cannot determine if passed network namespace path '%s' really refers to a network namespace, assuming it does.", arg_network_namespace_path);
+ else if (r < 0)
return log_error_errno(r, "Failed to check %s fs type: %m", arg_network_namespace_path);
- if (r == 0) {
- log_error("Path %s doesn't refer to a network namespace", arg_network_namespace_path);
+ else if (r == 0) {
+ log_error("Path %s doesn't refer to a network namespace, refusing.", arg_network_namespace_path);
return -EINVAL;
}
}
@@ -3800,7 +3792,8 @@ static int run(int master,
if (l < 0)
return log_error_errno(errno, "Failed to read cgroup mode: %m");
if (l != sizeof(arg_unified_cgroup_hierarchy)) {
- log_error("Short read while reading cgroup mode.");
+ log_error("Short read while reading cgroup mode (%zu bytes).%s",
+ l, l == 0 ? " The child is most likely dead." : "");
return -EIO;
}
}
@@ -3912,6 +3905,10 @@ static int run(int master,
r = sd_bus_default_system(&bus);
if (r < 0)
return log_error_errno(r, "Failed to open system bus: %m");
+
+ r = sd_bus_set_close_on_exit(bus, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable close-on-exit behaviour: %m");
}
if (!arg_keep_unit) {
@@ -4063,8 +4060,12 @@ static int run(int master,
putc('\n', stdout);
/* Kill if it is not dead yet anyway */
- if (arg_register && !arg_keep_unit && bus)
- terminate_machine(bus, *pid);
+ if (bus) {
+ if (arg_register)
+ terminate_machine(bus, arg_machine);
+ else if (!arg_keep_unit)
+ terminate_scope(bus, arg_machine);
+ }
/* Normally redundant, but better safe than sorry */
(void) kill(*pid, SIGKILL);
@@ -4213,6 +4214,14 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
+ parse_environment();
+
+ r = cg_unified_flush();
+ if (r < 0) {
+ log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
+ goto finish;
+ }
+
r = verify_arguments();
if (r < 0)
goto finish;
@@ -4319,16 +4328,15 @@ int main(int argc, char *argv[]) {
BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE |
BTRFS_SNAPSHOT_RECURSIVE |
BTRFS_SNAPSHOT_QUOTA);
- if (r == -EEXIST) {
- if (!arg_quiet)
- log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template);
- } else if (r < 0) {
+ if (r == -EEXIST)
+ log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
+ "Directory %s already exists, not populating from template %s.", arg_directory, arg_template);
+ else if (r < 0) {
log_error_errno(r, "Couldn't create snapshot %s from %s: %m", arg_directory, arg_template);
goto finish;
- } else {
- if (!arg_quiet)
- log_info("Populated %s from template %s.", arg_directory, arg_template);
- }
+ } else
+ log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
+ "Populated %s from template %s.", arg_directory, arg_template);
}
}
diff --git a/src/nspawn/test-patch-uid.c b/src/nspawn/test-patch-uid.c
index 8e29d3e806..b50f0990d8 100644
--- a/src/nspawn/test-patch-uid.c
+++ b/src/nspawn/test-patch-uid.c
@@ -5,15 +5,14 @@
#include "log.h"
#include "nspawn-patch-uid.h"
#include "user-util.h"
+#include "tests.h"
#include "util.h"
int main(int argc, char *argv[]) {
uid_t shift, range;
int r;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
if (argc != 4) {
log_error("Expected PATH SHIFT RANGE parameters.");
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index f82ce59f2c..5abc0c91bf 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -45,6 +45,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
char *r_name;
unsigned n;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
@@ -64,7 +65,6 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
if (n_addresses <= 0) {
- *errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
@@ -81,7 +81,6 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
/* We respond to our local host name, our hostname suffixed with a single dot. */
if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
- *errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
@@ -157,8 +156,8 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
if (ttlp)
*ttlp = 0;
- /* Explicitly reset all error variables */
- *errnop = 0;
+ /* Explicitly reset both *h_errnop and h_errno to work around
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
*h_errnop = NETDB_SUCCESS;
h_errno = 0;
@@ -286,8 +285,8 @@ static enum nss_status fill_in_hostent(
if (canonp)
*canonp = r_name;
- /* Explicitly reset all error variables */
- *errnop = 0;
+ /* Explicitly reset both *h_errnop and h_errno to work around
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
*h_errnop = NETDB_SUCCESS;
h_errno = 0;
@@ -309,6 +308,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
uint32_t local_address_ipv4 = 0;
int n_addresses = 0;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
@@ -334,7 +334,6 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
n_addresses = local_gateways(NULL, 0, af, &addresses);
if (n_addresses <= 0) {
- *errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
@@ -350,7 +349,6 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
}
if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
- *errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
@@ -393,6 +391,7 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
bool additional_from_hostname = false;
unsigned n;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(addr);
@@ -455,7 +454,6 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
}
}
- *errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c
index 8d6caa0ada..3d1fc28353 100644
--- a/src/nss-mymachines/nss-mymachines.c
+++ b/src/nss-mymachines/nss-mymachines.c
@@ -63,6 +63,20 @@ static int count_addresses(sd_bus_message *m, int af, unsigned *ret) {
return 0;
}
+static bool avoid_deadlock(void) {
+
+ /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
+ * code activating systemd-machined.service. After all, we shouldn't synchronously do lookups to
+ * systemd-machined if we are required to finish before it can be started. This of course won't detect all
+ * possible dead locks of this kind, but it should work for the most obvious cases. */
+
+ if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
+ return false;
+
+ return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-machined.service") &&
+ streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
+}
+
enum nss_status _nss_mymachines_gethostbyname4_r(
const char *name,
struct gaih_addrtuple **pat,
@@ -80,6 +94,7 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
char *r_name;
int n_ifindices, r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
@@ -102,6 +117,11 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
goto fail;
}
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
@@ -126,7 +146,6 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
goto fail;
if (c <= 0) {
- *errnop = ESRCH;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
@@ -200,8 +219,8 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
if (ttlp)
*ttlp = 0;
- /* Explicitly reset all error variables */
- *errnop = 0;
+ /* Explicitly reset both *h_errnop and h_errno to work around
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
*h_errnop = NETDB_SUCCESS;
h_errno = 0;
@@ -230,6 +249,7 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
size_t l, idx, ms, alen;
int r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
@@ -254,6 +274,11 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
goto fail;
}
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
@@ -278,7 +303,6 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
goto fail;
if (c <= 0) {
- *errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
@@ -364,8 +388,8 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
if (canonp)
*canonp = r_name;
- /* Explicitly reset all error variables */
- *errnop = 0;
+ /* Explicitly reset both *h_errnop and h_errno to work around
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
*h_errnop = NETDB_SUCCESS;
h_errno = 0;
@@ -394,6 +418,7 @@ enum nss_status _nss_mymachines_getpwnam_r(
size_t l;
int r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
@@ -401,28 +426,33 @@ enum nss_status _nss_mymachines_getpwnam_r(
p = startswith(name, "vu-");
if (!p)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
e = strrchr(p, '-');
if (!e || e == p)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
r = parse_uid(e + 1, &uid);
if (r < 0)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
machine = strndupa(p, e - p);
if (!machine_name_is_valid(machine))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
/* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve
* these UIDs, but that should be unproblematic as containers should never be able to connect to a bus
* running on the host. */
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
+
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
r = sd_bus_open_system(&bus);
if (r < 0)
@@ -439,7 +469,7 @@ enum nss_status _nss_mymachines_getpwnam_r(
machine, (uint32_t) uid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
goto fail;
}
@@ -450,7 +480,7 @@ enum nss_status _nss_mymachines_getpwnam_r(
/* Refuse to work if the mapped address is in the host UID range, or if there was no mapping at all. */
if (mapped < HOST_UID_LIMIT || mapped == uid)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
l = strlen(name);
if (buflen < l+1) {
@@ -468,13 +498,8 @@ enum nss_status _nss_mymachines_getpwnam_r(
pwd->pw_dir = (char*) "/";
pwd->pw_shell = (char*) "/sbin/nologin";
- *errnop = 0;
return NSS_STATUS_SUCCESS;
-not_found:
- *errnop = 0;
- return NSS_STATUS_NOTFOUND;
-
fail:
*errnop = -r;
return NSS_STATUS_UNAVAIL;
@@ -493,17 +518,23 @@ enum nss_status _nss_mymachines_getpwuid_r(
uint32_t mapped;
int r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
if (!uid_is_valid(uid))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
/* We consider all uids < 65536 host uids */
if (uid < HOST_UID_LIMIT)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
+
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
r = sd_bus_open_system(&bus);
if (r < 0)
@@ -520,7 +551,7 @@ enum nss_status _nss_mymachines_getpwuid_r(
(uint32_t) uid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
goto fail;
}
@@ -530,7 +561,7 @@ enum nss_status _nss_mymachines_getpwuid_r(
goto fail;
if (mapped == uid)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) {
*errnop = ERANGE;
@@ -545,13 +576,8 @@ enum nss_status _nss_mymachines_getpwuid_r(
pwd->pw_dir = (char*) "/";
pwd->pw_shell = (char*) "/sbin/nologin";
- *errnop = 0;
return NSS_STATUS_SUCCESS;
-not_found:
- *errnop = 0;
- return NSS_STATUS_NOTFOUND;
-
fail:
*errnop = -r;
return NSS_STATUS_UNAVAIL;
@@ -574,6 +600,7 @@ enum nss_status _nss_mymachines_getgrnam_r(
size_t l;
int r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
@@ -581,25 +608,30 @@ enum nss_status _nss_mymachines_getgrnam_r(
p = startswith(name, "vg-");
if (!p)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
e = strrchr(p, '-');
if (!e || e == p)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
r = parse_gid(e + 1, &gid);
if (r < 0)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
machine = strndupa(p, e - p);
if (!machine_name_is_valid(machine))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
+
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
r = sd_bus_open_system(&bus);
if (r < 0)
@@ -616,7 +648,7 @@ enum nss_status _nss_mymachines_getgrnam_r(
machine, (uint32_t) gid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
goto fail;
}
@@ -626,7 +658,7 @@ enum nss_status _nss_mymachines_getgrnam_r(
goto fail;
if (mapped < HOST_GID_LIMIT || mapped == gid)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
l = sizeof(char*) + strlen(name) + 1;
if (buflen < l) {
@@ -642,13 +674,8 @@ enum nss_status _nss_mymachines_getgrnam_r(
gr->gr_passwd = (char*) "*"; /* locked */
gr->gr_mem = (char**) buffer;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
-not_found:
- *errnop = 0;
- return NSS_STATUS_NOTFOUND;
-
fail:
*errnop = -r;
return NSS_STATUS_UNAVAIL;
@@ -667,17 +694,23 @@ enum nss_status _nss_mymachines_getgrgid_r(
uint32_t mapped;
int r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
if (!gid_is_valid(gid))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
/* We consider all gids < 65536 host gids */
if (gid < HOST_GID_LIMIT)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
+
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
r = sd_bus_open_system(&bus);
if (r < 0)
@@ -694,7 +727,7 @@ enum nss_status _nss_mymachines_getgrgid_r(
(uint32_t) gid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
goto fail;
}
@@ -704,7 +737,7 @@ enum nss_status _nss_mymachines_getgrgid_r(
goto fail;
if (mapped == gid)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (buflen < sizeof(char*) + 1) {
*errnop = ERANGE;
@@ -722,13 +755,8 @@ enum nss_status _nss_mymachines_getgrgid_r(
gr->gr_passwd = (char*) "*"; /* locked */
gr->gr_mem = (char**) buffer;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
-not_found:
- *errnop = 0;
- return NSS_STATUS_NOTFOUND;
-
fail:
*errnop = -r;
return NSS_STATUS_UNAVAIL;
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c
index eb3d2d977f..a28b5d8ba8 100644
--- a/src/nss-resolve/nss-resolve.c
+++ b/src/nss-resolve/nss-resolve.c
@@ -91,6 +91,20 @@ static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) {
return IN6_IS_ADDR_LINKLOCAL(&in6) ? ifindex : 0;
}
+static bool avoid_deadlock(void) {
+
+ /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
+ * code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to
+ * systemd-resolved if we are required to finish before it can be started. This of course won't detect all
+ * possible dead locks of this kind, but it should work for the most obvious cases. */
+
+ if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
+ return false;
+
+ return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-resolved.service") &&
+ streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
+}
+
enum nss_status _nss_resolve_gethostbyname4_r(
const char *name,
struct gaih_addrtuple **pat,
@@ -108,6 +122,7 @@ enum nss_status _nss_resolve_gethostbyname4_r(
char *r_name;
int c, r, i = 0;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
@@ -116,6 +131,11 @@ enum nss_status _nss_resolve_gethostbyname4_r(
assert(errnop);
assert(h_errnop);
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
@@ -140,20 +160,15 @@ enum nss_status _nss_resolve_gethostbyname4_r(
r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
- if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
+ if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
+ !bus_error_shall_fallback(&error))
+ goto not_found;
/* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
allowing falling back to other nss modules. Treat all other error conditions as
NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
case so that the nsswitch.conf configuration can distuingish such executed but
negative replies from complete failure to talk to resolved). */
- if (!bus_error_shall_fallback(&error))
- ret = NSS_STATUS_NOTFOUND;
-
goto fail;
}
@@ -162,11 +177,8 @@ enum nss_status _nss_resolve_gethostbyname4_r(
r = c;
goto fail;
}
- if (c == 0) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
+ if (c == 0)
+ goto not_found;
if (isempty(canonical))
canonical = name;
@@ -247,8 +259,8 @@ enum nss_status _nss_resolve_gethostbyname4_r(
if (ttlp)
*ttlp = 0;
- /* Explicitly reset all error variables */
- *errnop = 0;
+ /* Explicitly reset both *h_errnop and h_errno to work around
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
*h_errnop = NETDB_SUCCESS;
h_errno = 0;
@@ -258,6 +270,10 @@ fail:
*errnop = -r;
*h_errnop = NO_RECOVERY;
return ret;
+
+not_found:
+ *h_errnop = HOST_NOT_FOUND;
+ return NSS_STATUS_NOTFOUND;
}
enum nss_status _nss_resolve_gethostbyname3_r(
@@ -278,6 +294,7 @@ enum nss_status _nss_resolve_gethostbyname3_r(
const char *canonical;
int c, r, i = 0;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
@@ -294,6 +311,11 @@ enum nss_status _nss_resolve_gethostbyname3_r(
goto fail;
}
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
@@ -318,14 +340,9 @@ enum nss_status _nss_resolve_gethostbyname3_r(
r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
- if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
-
- if (!bus_error_shall_fallback(&error))
- ret = NSS_STATUS_NOTFOUND;
+ if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
+ !bus_error_shall_fallback(&error))
+ goto not_found;
goto fail;
}
@@ -335,11 +352,8 @@ enum nss_status _nss_resolve_gethostbyname3_r(
r = c;
goto fail;
}
- if (c == 0) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
+ if (c == 0)
+ goto not_found;
if (isempty(canonical))
canonical = name;
@@ -427,23 +441,27 @@ enum nss_status _nss_resolve_gethostbyname3_r(
result->h_length = alen;
result->h_addr_list = (char**) r_addr_list;
- /* Explicitly reset all error variables */
- *errnop = 0;
- *h_errnop = NETDB_SUCCESS;
- h_errno = 0;
-
if (ttlp)
*ttlp = 0;
if (canonp)
*canonp = r_name;
+ /* Explicitly reset both *h_errnop and h_errno to work around
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
+ *h_errnop = NETDB_SUCCESS;
+ h_errno = 0;
+
return NSS_STATUS_SUCCESS;
fail:
*errnop = -r;
*h_errnop = NO_RECOVERY;
return ret;
+
+not_found:
+ *h_errnop = HOST_NOT_FOUND;
+ return NSS_STATUS_NOTFOUND;
}
enum nss_status _nss_resolve_gethostbyaddr2_r(
@@ -464,6 +482,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
const char *n;
int r, ifindex;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(addr);
@@ -484,6 +503,11 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
return NSS_STATUS_UNAVAIL;
}
+ if (avoid_deadlock()) {
+ r = -EDEADLK;
+ goto fail;
+ }
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
@@ -516,14 +540,9 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
- if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
-
- if (!bus_error_shall_fallback(&error))
- ret = NSS_STATUS_NOTFOUND;
+ if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
+ !bus_error_shall_fallback(&error))
+ goto not_found;
goto fail;
}
@@ -549,11 +568,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
if (r < 0)
return r;
- if (c <= 0) {
- *errnop = ESRCH;
- *h_errnop = HOST_NOT_FOUND;
- return NSS_STATUS_NOTFOUND;
- }
+ if (c <= 0)
+ goto not_found;
ms += ALIGN(len) + /* the address */
2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
@@ -612,8 +628,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
if (ttlp)
*ttlp = 0;
- /* Explicitly reset all error variables */
- *errnop = 0;
+ /* Explicitly reset both *h_errnop and h_errno to work around
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
*h_errnop = NETDB_SUCCESS;
h_errno = 0;
@@ -623,6 +639,10 @@ fail:
*errnop = -r;
*h_errnop = NO_RECOVERY;
return ret;
+
+not_found:
+ *h_errnop = HOST_NOT_FOUND;
+ return NSS_STATUS_NOTFOUND;
}
NSS_GETHOSTBYNAME_FALLBACKS(resolve);
diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c
index f516b84c63..f554828d49 100644
--- a/src/nss-systemd/nss-systemd.c
+++ b/src/nss-systemd/nss-systemd.c
@@ -145,6 +145,7 @@ enum nss_status _nss_systemd_getpwnam_r(
size_t l;
int bypass, r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
@@ -153,26 +154,24 @@ enum nss_status _nss_systemd_getpwnam_r(
/* 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))
- goto not_found;
+ 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;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (synthesize_nobody() &&
streq(name, nobody_passwd.pw_name)) {
*pwd = nobody_passwd;
- *errnop = 0;
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)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
if (bypass <= 0) {
@@ -184,7 +183,7 @@ enum nss_status _nss_systemd_getpwnam_r(
if (bypass > 0) {
r = direct_lookup_name(name, (uid_t*) &translated);
if (r == -ENOENT)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (r < 0)
goto fail;
} else {
@@ -199,7 +198,7 @@ enum nss_status _nss_systemd_getpwnam_r(
name);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
goto fail;
}
@@ -225,13 +224,8 @@ enum nss_status _nss_systemd_getpwnam_r(
pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
-not_found:
- *errnop = 0;
- return NSS_STATUS_NOTFOUND;
-
fail:
*errnop = -r;
return NSS_STATUS_UNAVAIL;
@@ -251,31 +245,30 @@ enum nss_status _nss_systemd_getpwuid_r(
size_t l;
int bypass, r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
if (!uid_is_valid(uid))
- goto not_found;
+ 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;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (synthesize_nobody() &&
uid == nobody_passwd.pw_uid) {
*pwd = nobody_passwd;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
}
}
if (!uid_is_dynamic(uid))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
if (bypass <= 0) {
@@ -287,7 +280,7 @@ enum nss_status _nss_systemd_getpwuid_r(
if (bypass > 0) {
r = direct_lookup_uid(uid, &direct);
if (r == -ENOENT)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (r < 0)
goto fail;
@@ -305,7 +298,7 @@ enum nss_status _nss_systemd_getpwuid_r(
(uint32_t) uid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
goto fail;
}
@@ -331,13 +324,8 @@ enum nss_status _nss_systemd_getpwuid_r(
pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
-not_found:
- *errnop = 0;
- return NSS_STATUS_NOTFOUND;
-
fail:
*errnop = -r;
return NSS_STATUS_UNAVAIL;
@@ -358,31 +346,30 @@ enum nss_status _nss_systemd_getgrnam_r(
size_t l;
int bypass, r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
assert(gr);
if (!valid_user_group_name(name))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
/* Synthesize records for root and nobody, in case they are missing form /etc/group */
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
if (streq(name, root_group.gr_name)) {
*gr = root_group;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (synthesize_nobody() &&
streq(name, nobody_group.gr_name)) {
*gr = nobody_group;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
}
}
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
if (bypass <= 0) {
@@ -394,7 +381,7 @@ enum nss_status _nss_systemd_getgrnam_r(
if (bypass > 0) {
r = direct_lookup_name(name, (uid_t*) &translated);
if (r == -ENOENT)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (r < 0)
goto fail;
} else {
@@ -409,7 +396,7 @@ enum nss_status _nss_systemd_getgrnam_r(
name);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
goto fail;
}
@@ -433,13 +420,8 @@ enum nss_status _nss_systemd_getgrnam_r(
gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
gr->gr_mem = (char**) buffer;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
-not_found:
- *errnop = 0;
- return NSS_STATUS_NOTFOUND;
-
fail:
*errnop = -r;
return NSS_STATUS_UNAVAIL;
@@ -459,31 +441,30 @@ enum nss_status _nss_systemd_getgrgid_r(
size_t l;
int bypass, r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
if (!gid_is_valid(gid))
- goto not_found;
+ 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;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (synthesize_nobody() &&
gid == nobody_group.gr_gid) {
*gr = nobody_group;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
}
}
if (!gid_is_dynamic(gid))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
if (bypass <= 0) {
@@ -495,7 +476,7 @@ enum nss_status _nss_systemd_getgrgid_r(
if (bypass > 0) {
r = direct_lookup_uid(gid, &direct);
if (r == -ENOENT)
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
if (r < 0)
goto fail;
@@ -513,7 +494,7 @@ enum nss_status _nss_systemd_getgrgid_r(
(uint32_t) gid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
- goto not_found;
+ return NSS_STATUS_NOTFOUND;
goto fail;
}
@@ -537,13 +518,8 @@ enum nss_status _nss_systemd_getgrgid_r(
gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
gr->gr_mem = (char**) buffer;
- *errnop = 0;
return NSS_STATUS_SUCCESS;
-not_found:
- *errnop = 0;
- return NSS_STATUS_NOTFOUND;
-
fail:
*errnop = -r;
return NSS_STATUS_UNAVAIL;
@@ -598,6 +574,7 @@ static void systemd_endent(GetentData *data) {
}
static enum nss_status nss_systemd_endent(GetentData *p) {
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert_se(pthread_mutex_lock(&p->mutex) == 0);
@@ -668,6 +645,7 @@ static enum nss_status systemd_setent(GetentData *p) {
uid_t id;
int bypass, r;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(p);
@@ -750,6 +728,7 @@ enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, siz
UserEntry *p;
size_t len;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(result);
@@ -778,7 +757,6 @@ enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, siz
break;
}
if (!p) {
- *errnop = ENOENT;
ret = NSS_STATUS_NOTFOUND;
goto finalize;
}
@@ -801,6 +779,7 @@ enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size
UserEntry *p;
size_t len;
+ PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(result);
@@ -827,7 +806,6 @@ enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size
break;
}
if (!p) {
- *errnop = ENOENT;
ret = NSS_STATUS_NOTFOUND;
goto finalize;
}
diff --git a/src/partition/growfs.c b/src/partition/growfs.c
index d2053cd391..c9d1c27792 100644
--- a/src/partition/growfs.c
+++ b/src/partition/growfs.c
@@ -19,13 +19,15 @@
#include "format-util.h"
#include "log.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
+#include "stat-util.h"
#include "strv.h"
-const char *arg_target = NULL;
-bool arg_dry_run = false;
+static const char *arg_target = NULL;
+static bool arg_dry_run = false;
static int resize_ext4(const char *path, int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) {
assert((uint64_t) (int) blocksize == blocksize);
@@ -68,13 +70,16 @@ static int resize_btrfs(const char *path, int mountfd, int devfd, uint64_t numbl
#if HAVE_LIBCRYPTSETUP
static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_devno) {
- char devpath[DEV_NUM_PATH_MAX], main_devpath[DEV_NUM_PATH_MAX];
- _cleanup_close_ int main_devfd = -1;
+ _cleanup_free_ char *devpath = NULL, *main_devpath = NULL;
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+ _cleanup_close_ int main_devfd = -1;
uint64_t size;
int r;
- xsprintf_dev_num_path(main_devpath, "block", main_devno);
+ r = device_path_make_major_minor(S_IFBLK, main_devno, &main_devpath);
+ if (r < 0)
+ return log_error_errno(r, "Failed to format device major/minor path: %m");
+
main_devfd = open(main_devpath, O_RDONLY|O_CLOEXEC);
if (main_devfd < 0)
return log_error_errno(errno, "Failed to open \"%s\": %m", main_devpath);
@@ -84,8 +89,10 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_
main_devpath);
log_debug("%s is %"PRIu64" bytes", main_devpath, size);
+ r = device_path_make_major_minor(S_IFBLK, devno, &devpath);
+ if (r < 0)
+ return log_error_errno(r, "Failed to format major/minor path: %m");
- xsprintf_dev_num_path(devpath, "block", devno);
r = crypt_init(&cd, devpath);
if (r < 0)
return log_error_errno(r, "crypt_init(\"%s\") failed: %m", devpath);
@@ -114,9 +121,8 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_
#endif
static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) {
+ _cleanup_free_ char *fstype = NULL, *devpath = NULL;
dev_t devno;
- char devpath[DEV_NUM_PATH_MAX];
- _cleanup_free_ char *fstype = NULL;
int r;
#if HAVE_LIBCRYPTSETUP
@@ -136,7 +142,10 @@ static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) {
if (devno == main_devno)
return 0;
- xsprintf_dev_num_path(devpath, "block", devno);
+ r = device_path_make_major_minor(S_IFBLK, devno, &devpath);
+ if (r < 0)
+ return log_error_errno(r, "Failed to format device major/minor path: %m");
+
r = probe_filesystem(devpath, &fstype);
if (r == -EUCLEAN)
return log_warning_errno(r, "Cannot reliably determine probe \"%s\", refusing to proceed.", devpath);
@@ -152,14 +161,26 @@ static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) {
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-growfs@.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] /path/to/mountpoint\n\n"
"Grow filesystem or encrypted payload to device size.\n\n"
"Options:\n"
" -h --help Show this help and exit\n"
" --version Print version string and exit\n"
" -n --dry-run Just print what would be done\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -182,12 +203,10 @@ static int parse_argv(int argc, char *argv[]) {
while ((c = getopt_long(argc, argv, "hn", options, NULL)) >= 0)
switch(c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
- version();
- return 0;
+ return version();
case 'n':
arg_dry_run = true;
@@ -200,11 +219,10 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind + 1 != argc) {
- log_error("%s excepts exactly one argument (the mount point).",
- program_invocation_short_name);
- return -EINVAL;
- }
+ if (optind + 1 != argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s excepts exactly one argument (the mount point).",
+ program_invocation_short_name);
arg_target = argv[optind];
@@ -212,17 +230,16 @@ static int parse_argv(int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
- dev_t devno;
_cleanup_close_ int mountfd = -1, devfd = -1;
- int blocksize;
+ _cleanup_free_ char *devpath = NULL;
uint64_t size, numblocks;
- char devpath[DEV_NUM_PATH_MAX], fb[FORMAT_BYTES_MAX];
+ char fb[FORMAT_BYTES_MAX];
struct statfs sfs;
+ dev_t devno;
+ int blocksize;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
r = parse_argv(argc, argv);
if (r < 0)
@@ -256,7 +273,12 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- xsprintf_dev_num_path(devpath, "block", devno);
+ r = device_path_make_major_minor(S_IFBLK, devno, &devpath);
+ if (r < 0) {
+ log_error_errno(r, "Failed to format device major/minor path: %m");
+ return EXIT_FAILURE;
+ }
+
devfd = open(devpath, O_RDONLY|O_CLOEXEC);
if (devfd < 0) {
log_error_errno(errno, "Failed to open \"%s\": %m", devpath);
diff --git a/src/partition/makefs.c b/src/partition/makefs.c
index 8c2a783e9d..0b9bae55e7 100644
--- a/src/partition/makefs.c
+++ b/src/partition/makefs.c
@@ -11,6 +11,7 @@
#include "alloc-util.h"
#include "dissect-image.h"
+#include "main-func.h"
#include "process-util.h"
#include "signal-util.h"
#include "string-util.h"
@@ -27,7 +28,7 @@ static int makefs(const char *type, const char *device) {
if (access(mkfs, X_OK) != 0)
return log_error_errno(errno, "%s is not executable: %m", mkfs);
- r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {
@@ -42,49 +43,41 @@ static int makefs(const char *type, const char *device) {
return wait_for_terminate_and_check(mkfs, pid, WAIT_LOG);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
const char *device, *type;
_cleanup_free_ char *detected = NULL;
struct stat st;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
- if (argc != 3) {
- log_error("This program expects two arguments.");
- return EXIT_FAILURE;
- }
+ if (argc != 3)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "This program expects two arguments.");
type = argv[1];
device = argv[2];
- if (stat(device, &st) < 0) {
- r = log_error_errno(errno, "Failed to stat \"%s\": %m", device);
- goto finish;
- }
+ if (stat(device, &st) < 0)
+ return log_error_errno(errno, "Failed to stat \"%s\": %m", device);
if (!S_ISBLK(st.st_mode))
log_info("%s is not a block device.", device);
r = probe_filesystem(device, &detected);
- if (r < 0) {
- log_warning_errno(r,
- r == -EUCLEAN ?
- "Cannot reliably determine probe \"%s\", refusing to proceed." :
- "Failed to probe \"%s\": %m",
- device);
- goto finish;
- }
+ if (r < 0)
+ return log_warning_errno(r,
+ r == -EUCLEAN ?
+ "Cannot reliably determine probe \"%s\", refusing to proceed." :
+ "Failed to probe \"%s\": %m",
+ device);
if (detected) {
log_info("%s is not empty (type %s), exiting", device, detected);
- goto finish;
+ return 0;
}
- r = makefs(type, device);
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return makefs(type, device);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/path/path.c b/src/path/path.c
index 7bd30f22d4..7b630a5714 100644
--- a/src/path/path.c
+++ b/src/path/path.c
@@ -10,6 +10,8 @@
#include "alloc-util.h"
#include "log.h"
#include "macro.h"
+#include "main-func.h"
+#include "pretty-print.h"
#include "string-util.h"
#include "util.h"
@@ -98,17 +100,29 @@ static int print_home(const char *n) {
}
}
- log_error("Path %s not known.", n);
- return -EOPNOTSUPP;
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Path %s not known.", n);
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-path", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] [NAME...]\n\n"
"Show system and user paths.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
- " --suffix=SUFFIX Suffix to append to paths\n",
- program_invocation_short_name);
+ " --suffix=SUFFIX Suffix to append to paths\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -135,8 +149,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -155,7 +168,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char* argv[]) {
+static int run(int argc, char* argv[]) {
int r;
log_parse_environment();
@@ -163,7 +176,7 @@ int main(int argc, char* argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
if (argc > optind) {
int i, q;
@@ -173,9 +186,10 @@ int main(int argc, char* argv[]) {
if (q < 0)
r = q;
}
- } else
- r = list_homes();
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
+ } else
+ return list_homes();
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/portable/portable.c b/src/portable/portable.c
index eb5daf3b0e..01fd1a94a0 100644
--- a/src/portable/portable.c
+++ b/src/portable/portable.c
@@ -26,6 +26,7 @@
#include "socket-util.h"
#include "string-table.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "user-util.h"
static const char profile_dirs[] = CONF_PATHS_NULSTR("systemd/portable/profile");
@@ -92,21 +93,6 @@ PortableMetadata *portable_metadata_unref(PortableMetadata *i) {
return mfree(i);
}
-Hashmap *portable_metadata_hashmap_unref(Hashmap *h) {
-
- for (;;) {
- PortableMetadata *i;
-
- i = hashmap_steal_first(h);
- if (!i)
- break;
-
- portable_metadata_unref(i);
- }
-
- return hashmap_free(h);
-}
-
static int compare_metadata(PortableMetadata *const *x, PortableMetadata *const *y) {
return strcmp((*x)->name, (*y)->name);
}
@@ -232,6 +218,9 @@ static int recv_item(
return 0;
}
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(portable_metadata_hash_ops, char, string_hash_func, string_compare_func,
+ PortableMetadata, portable_metadata_unref);
+
static int extract_now(
const char *where,
char **matches,
@@ -239,7 +228,7 @@ static int extract_now(
PortableMetadata **ret_os_release,
Hashmap **ret_unit_files) {
- _cleanup_(portable_metadata_hashmap_unrefp) Hashmap *unit_files = NULL;
+ _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_(lookup_paths_free) LookupPaths paths = {};
_cleanup_close_ int os_release_fd = -1;
@@ -286,7 +275,7 @@ static int extract_now(
if (r < 0)
return log_debug_errno(r, "Failed to acquire lookup paths: %m");
- unit_files = hashmap_new(&string_hash_ops);
+ unit_files = hashmap_new(&portable_metadata_hash_ops);
if (!unit_files)
return -ENOMEM;
@@ -362,7 +351,7 @@ static int portable_extract_by_path(
Hashmap **ret_unit_files,
sd_bus_error *error) {
- _cleanup_(portable_metadata_hashmap_unrefp) Hashmap *unit_files = NULL;
+ _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata* os_release = NULL;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
int r;
@@ -432,7 +421,7 @@ static int portable_extract_by_path(
seq[1] = safe_close(seq[1]);
- unit_files = hashmap_new(&string_hash_ops);
+ unit_files = hashmap_new(&portable_metadata_hash_ops);
if (!unit_files)
return -ENOMEM;
@@ -782,7 +771,7 @@ static int install_profile_dropin(
r = find_profile(profile, m->name, &from);
if (r < 0) {
- if (r != ENOENT)
+ if (r != -ENOENT)
return log_debug_errno(errno, "Profile '%s' is not accessible: %m", profile);
log_debug_errno(errno, "Skipping link to profile '%s', as it does not exist: %m", profile);
@@ -797,14 +786,14 @@ static int install_profile_dropin(
r = copy_file_atomic(from, dropin, 0644, 0, COPY_REFLINK);
if (r < 0)
- return log_debug_errno(r, "Failed to copy %s %s %s: %m", from, special_glyph(ARROW), dropin);
+ return log_debug_errno(r, "Failed to copy %s %s %s: %m", from, special_glyph(SPECIAL_GLYPH_ARROW), dropin);
(void) portable_changes_add(changes, n_changes, PORTABLE_COPY, dropin, from);
} else {
if (symlink(from, dropin) < 0)
- return log_debug_errno(errno, "Failed to link %s %s %s: %m", from, special_glyph(ARROW), dropin);
+ return log_debug_errno(errno, "Failed to link %s %s %s: %m", from, special_glyph(SPECIAL_GLYPH_ARROW), dropin);
(void) portable_changes_add(changes, n_changes, PORTABLE_SYMLINK, dropin, from);
}
@@ -815,15 +804,15 @@ static int install_profile_dropin(
return 0;
}
-static const char *config_path(const LookupPaths *paths, PortableFlags flags) {
+static const char *attached_path(const LookupPaths *paths, PortableFlags flags) {
const char *where;
assert(paths);
if (flags & PORTABLE_RUNTIME)
- where = paths->runtime_config;
+ where = paths->runtime_attached;
else
- where = paths->persistent_config;
+ where = paths->persistent_attached;
assert(where);
return where;
@@ -849,15 +838,25 @@ static int attach_unit_file(
assert(m);
assert(PORTABLE_METADATA_IS_UNIT(m));
- where = config_path(paths, flags);
- path = strjoina(where, "/", m->name);
+ where = attached_path(paths, flags);
+ (void) mkdir_parents(where, 0755);
+ if (mkdir(where, 0755) < 0) {
+ if (errno != EEXIST)
+ return -errno;
+ } else
+ (void) portable_changes_add(changes, n_changes, PORTABLE_MKDIR, where, NULL);
+
+ path = strjoina(where, "/", m->name);
dropin_dir = strjoin(path, ".d");
if (!dropin_dir)
return -ENOMEM;
- (void) mkdir_p(dropin_dir, 0755);
- (void) portable_changes_add(changes, n_changes, PORTABLE_MKDIR, dropin_dir, NULL);
+ if (mkdir(dropin_dir, 0755) < 0) {
+ if (errno != EEXIST)
+ return -errno;
+ } else
+ (void) portable_changes_add(changes, n_changes, PORTABLE_MKDIR, dropin_dir, NULL);
/* We install the drop-ins first, and the actual unit file last to achieve somewhat atomic behaviour if PID 1
* is reloaded while we are creating things here: as long as only the drop-ins exist the unit doesn't exist at
@@ -960,7 +959,7 @@ static int install_image_symlink(
(void) mkdir_parents(sl, 0755);
if (symlink(image_path, sl) < 0)
- return log_debug_errno(errno, "Failed to link %s %s %s: %m", image_path, special_glyph(ARROW), sl);
+ return log_debug_errno(errno, "Failed to link %s %s %s: %m", image_path, special_glyph(SPECIAL_GLYPH_ARROW), sl);
(void) portable_changes_add(changes, n_changes, PORTABLE_SYMLINK, sl, image_path);
return 0;
@@ -976,7 +975,7 @@ int portable_attach(
size_t *n_changes,
sd_bus_error *error) {
- _cleanup_(portable_metadata_hashmap_unrefp) Hashmap *unit_files = NULL;
+ _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_(lookup_paths_free) LookupPaths paths = {};
_cleanup_(image_unrefp) Image *image = NULL;
PortableMetadata *item;
@@ -1090,7 +1089,7 @@ static int test_chroot_dropin(
return log_debug_errno(errno, "Failed to open %s/%s: %m", where, p);
}
- f = fdopen(fd, "re");
+ f = fdopen(fd, "r");
if (!f)
return log_debug_errno(errno, "Failed to convert file handle: %m");
fd = -1;
@@ -1147,11 +1146,15 @@ int portable_detach(
if (r < 0)
return r;
- where = config_path(&paths, flags);
+ where = attached_path(&paths, flags);
d = opendir(where);
- if (!d)
+ if (!d) {
+ if (errno == ENOENT)
+ goto not_found;
+
return log_debug_errno(errno, "Failed to open '%s' directory: %m", where);
+ }
unit_files = set_new(&string_hash_ops);
if (!unit_files)
@@ -1213,10 +1216,8 @@ int portable_detach(
}
}
- if (set_isempty(unit_files)) {
- log_debug("No unit files associated with '%s' found. Image not attached?", name_or_path);
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "No unit files associated with '%s' found. Image not attached?", name_or_path);
- }
+ if (set_isempty(unit_files))
+ goto not_found;
SET_FOREACH(item, unit_files, iterator) {
_cleanup_free_ char *md = NULL;
@@ -1289,7 +1290,15 @@ int portable_detach(
portable_changes_add(changes, n_changes, PORTABLE_UNLINK, sl, NULL);
}
+ /* Try to remove the unit file directory, if we can */
+ if (rmdir(where) >= 0)
+ portable_changes_add(changes, n_changes, PORTABLE_UNLINK, where, NULL);
+
return ret;
+
+not_found:
+ log_debug("No unit files associated with '%s' found. Image not attached?", name_or_path);
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "No unit files associated with '%s' found. Image not attached?", name_or_path);
}
static int portable_get_state_internal(
@@ -1314,11 +1323,18 @@ static int portable_get_state_internal(
if (r < 0)
return r;
- where = config_path(&paths, flags);
+ where = attached_path(&paths, flags);
d = opendir(where);
- if (!d)
+ if (!d) {
+ if (errno == ENOENT) {
+ /* If the 'attached' directory doesn't exist at all, then we know for sure this image isn't attached. */
+ *ret = PORTABLE_DETACHED;
+ return 0;
+ }
+
return log_debug_errno(errno, "Failed to open '%s' directory: %m", where);
+ }
unit_files = set_new(&string_hash_ops);
if (!unit_files)
diff --git a/src/portable/portable.h b/src/portable/portable.h
index 9fbf61265a..ad81b584a5 100644
--- a/src/portable/portable.h
+++ b/src/portable/portable.h
@@ -54,9 +54,6 @@ typedef struct PortableChange {
PortableMetadata *portable_metadata_unref(PortableMetadata *i);
DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref);
-Hashmap *portable_metadata_hashmap_unref(Hashmap *h);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, portable_metadata_hashmap_unref);
-
int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
int portable_extract(const char *image, char **matches, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, sd_bus_error *error);
diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c
index b4895e6380..bb6cebdf16 100644
--- a/src/portable/portablectl.c
+++ b/src/portable/portablectl.c
@@ -10,22 +10,24 @@
#include "bus-util.h"
#include "def.h"
#include "dirent-util.h"
+#include "env-file.h"
#include "fd-util.h"
-#include "fileio.h"
#include "format-table.h"
#include "fs-util.h"
#include "locale-util.h"
#include "machine-image.h"
+#include "main-func.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "spawn-polkit-agent.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "verbs.h"
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static bool arg_ask_password = true;
static bool arg_quiet = false;
@@ -35,7 +37,7 @@ static bool arg_runtime = false;
static bool arg_reload = true;
static bool arg_cat = false;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
-static char *arg_host = NULL;
+static const char *arg_host = NULL;
static int determine_image(const char *image, bool permit_non_existing, char **ret) {
int r;
@@ -60,10 +62,9 @@ static int determine_image(const char *image, bool permit_non_existing, char **r
return 0;
}
- if (arg_transport != BUS_TRANSPORT_LOCAL) {
- log_error("Operations on images by path not supported when connecting to remote systems.");
- return -EOPNOTSUPP;
- }
+ if (arg_transport != BUS_TRANSPORT_LOCAL)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Operations on images by path not supported when connecting to remote systems.");
r = chase_symlinks(image, NULL, CHASE_TRAIL_SLASH | (permit_non_existing ? CHASE_NONEXISTENT : 0), ret);
if (r < 0)
@@ -133,10 +134,9 @@ static int determine_matches(const char *image, char **l, bool allow_any, char *
} else if (strv_equal(l, STRV_MAKE("-"))) {
- if (!allow_any) {
- log_error("Refusing all unit file match.");
- return -EINVAL;
- }
+ if (!allow_any)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing all unit file match.");
if (!arg_quiet)
log_info("(Matching all unit files.)");
@@ -263,7 +263,7 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
if (arg_cat) {
printf("%s-- OS Release: --%s\n", ansi_highlight(), ansi_normal());
@@ -279,10 +279,9 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
if (!f)
return log_error_errno(errno, "Failed to open /etc/os-release buffer: %m");
- r = parse_env_file(f, "/etc/os-release", NEWLINE,
+ r = parse_env_file(f, "/etc/os-release",
"PORTABLE_PRETTY_NAME", &pretty_portable,
- "PRETTY_NAME", &pretty_os,
- NULL);
+ "PRETTY_NAME", &pretty_os);
if (r < 0)
return log_error_errno(r, "Failed to parse /etc/os-release: %m");
@@ -366,12 +365,12 @@ static int print_changes(sd_bus_message *m) {
break;
if (streq(type, "symlink"))
- log_info("Created symlink %s %s %s.", path, special_glyph(ARROW), source);
+ log_info("Created symlink %s %s %s.", path, special_glyph(SPECIAL_GLYPH_ARROW), source);
else if (streq(type, "copy")) {
if (isempty(source))
log_info("Copied %s.", path);
else
- log_info("Copied %s %s %s.", source, special_glyph(ARROW), path);
+ log_info("Copied %s %s %s.", source, special_glyph(SPECIAL_GLYPH_ARROW), path);
} else if (streq(type, "unlink"))
log_info("Removed %s.", path);
else if (streq(type, "write"))
@@ -501,7 +500,7 @@ static int list_images(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to list images: %s", bus_error_message(&error, r));
- table = table_new("NAME", "TYPE", "RO", "CRTIME", "MTIME", "USAGE", "STATE");
+ table = table_new("name", "type", "ro", "crtime", "mtime", "usage", "state");
if (!table)
return log_oom();
@@ -773,8 +772,14 @@ static int dump_profiles(void) {
}
static int help(int argc, char *argv[], void *userdata) {
+ _cleanup_free_ char *link = NULL;
+ int r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
+
+ r = terminal_urlify_man("portablectl", "1", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Attach or detach portable services from the local system.\n\n"
@@ -803,7 +808,10 @@ static int help(int argc, char *argv[], void *userdata) {
" read-only NAME|PATH [BOOL] Mark or unmark portable service image read-only\n"
" remove NAME|PATH... Remove a portable service image\n"
" set-limit [NAME|PATH] Set image or pool size limit (disk quota)\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
@@ -851,14 +859,13 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help(0, NULL, NULL);
- return 0;
+ return help(0, NULL, NULL);
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
@@ -887,10 +894,9 @@ static int parse_argv(int argc, char *argv[]) {
if (streq(optarg, "help"))
return dump_profiles();
- if (!filename_is_valid(optarg)) {
- log_error("Unit profile name not valid: %s", optarg);
- return -EINVAL;
- }
+ if (!filename_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unit profile name not valid: %s", optarg);
arg_profile = optarg;
break;
@@ -905,10 +911,9 @@ static int parse_argv(int argc, char *argv[]) {
"copy\n"
"symlink");
return 0;
- } else {
- log_error("Failed to parse --copy= argument: %s", optarg);
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse --copy= argument: %s", optarg);
break;
@@ -935,8 +940,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char *argv[]) {
-
+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_images },
@@ -957,12 +961,9 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
-
- r = dispatch_verb(argc, argv, verbs, NULL);
-
-finish:
- pager_close();
+ return r;
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return dispatch_verb(argc, argv, verbs, NULL);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/portable/portabled-bus.c b/src/portable/portabled-bus.c
index 70fc750fe3..3cbdb0b0cc 100644
--- a/src/portable/portabled-bus.c
+++ b/src/portable/portabled-bus.c
@@ -7,6 +7,7 @@
#include "fd-util.h"
#include "io-util.h"
#include "machine-image.h"
+#include "missing_capability.h"
#include "portable.h"
#include "portabled-bus.h"
#include "portabled-image-bus.h"
@@ -131,7 +132,7 @@ static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_erro
static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
+ _cleanup_hashmap_free_ Hashmap *images = NULL;
Manager *m = userdata;
Image *image;
Iterator i;
@@ -140,7 +141,7 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er
assert(message);
assert(m);
- images = hashmap_new(&string_hash_ops);
+ images = hashmap_new(&image_hash_ops);
if (!images)
return -ENOMEM;
diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c
index 02a2db2352..360559811f 100644
--- a/src/portable/portabled-image-bus.c
+++ b/src/portable/portabled-image-bus.c
@@ -8,6 +8,7 @@
#include "fileio.h"
#include "io-util.h"
#include "machine-image.h"
+#include "missing_capability.h"
#include "portable.h"
#include "portabled-bus.h"
#include "portabled-image-bus.h"
@@ -72,7 +73,7 @@ static int append_fd(sd_bus_message *m, PortableMetadata *d) {
assert(d);
assert(d->fd >= 0);
- f = fdopen(d->fd, "re");
+ f = fdopen(d->fd, "r");
if (!f)
return -errno;
@@ -93,7 +94,7 @@ int bus_image_common_get_metadata(
sd_bus_error *error) {
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
- _cleanup_(portable_metadata_hashmap_unrefp) Hashmap *unit_files = NULL;
+ _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ PortableMetadata **sorted = NULL;
_cleanup_strv_free_ char **matches = NULL;
@@ -636,6 +637,10 @@ int bus_image_acquire(
r = image_from_path(name_or_path, &loaded);
}
+ if (r == -EMEDIUMTYPE) {
+ sd_bus_error_setf(error, BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE, "Typ of image '%s' not recognized; supported image types are directories/btrfs subvolumes, block devices, and raw disk image files with suffix '.raw'.", name_or_path);
+ return r;
+ }
if (r < 0)
return r;
@@ -689,7 +694,7 @@ not_found:
}
int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
- _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
+ _cleanup_hashmap_free_ Hashmap *images = NULL;
_cleanup_strv_free_ char **l = NULL;
size_t n_allocated = 0, n = 0;
Manager *m = userdata;
@@ -701,7 +706,7 @@ int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, cha
assert(path);
assert(nodes);
- images = hashmap_new(&string_hash_ops);
+ images = hashmap_new(&image_hash_ops);
if (!images)
return -ENOMEM;
diff --git a/src/portable/portabled-image-bus.h b/src/portable/portabled-image-bus.h
index 81c17f68af..825dea2682 100644
--- a/src/portable/portabled-image-bus.h
+++ b/src/portable/portabled-image-bus.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
#include "sd-bus.h"
diff --git a/src/portable/portabled-image.c b/src/portable/portabled-image.c
index c58197c9a6..d95845b32a 100644
--- a/src/portable/portabled-image.c
+++ b/src/portable/portabled-image.c
@@ -16,7 +16,7 @@ static int image_cache_flush(sd_event_source *s, void *userdata) {
assert(s);
assert(m);
- hashmap_clear_with_destructor(m->image_cache, image_unref);
+ hashmap_clear(m->image_cache);
return 0;
}
@@ -25,7 +25,7 @@ static int manager_image_cache_initialize(Manager *m) {
assert(m);
- r = hashmap_ensure_allocated(&m->image_cache, &string_hash_ops);
+ r = hashmap_ensure_allocated(&m->image_cache, &image_hash_ops);
if (r < 0)
return r;
@@ -58,7 +58,6 @@ int manager_image_cache_add(Manager *m, Image *image) {
*
* 2. If the image was discovered in the search path (i.e. its discoverable boolean set) we'll also add it
* under its short name.
- *
*/
r = manager_image_cache_initialize(m);
diff --git a/src/portable/portabled-image.h b/src/portable/portabled-image.h
index f29bd89e83..26ce2f6b59 100644
--- a/src/portable/portabled-image.h
+++ b/src/portable/portabled-image.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
#include "hashmap.h"
#include "machine-image.h"
diff --git a/src/portable/portabled.c b/src/portable/portabled.c
index 53faa09bac..63fc340469 100644
--- a/src/portable/portabled.c
+++ b/src/portable/portabled.c
@@ -6,6 +6,7 @@
#include "alloc-util.h"
#include "bus-util.h"
#include "def.h"
+#include "main-func.h"
#include "portabled-bus.h"
#include "portabled-image-bus.h"
#include "portabled.h"
@@ -46,7 +47,7 @@ static int manager_new(Manager **ret) {
static Manager* manager_unref(Manager *m) {
assert(m);
- hashmap_free_with_destructor(m->image_cache, image_unref);
+ hashmap_free(m->image_cache);
sd_event_source_unref(m->image_cache_defer_event);
@@ -122,47 +123,41 @@ static int manager_run(Manager *m) {
check_idle, m);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_(manager_unrefp) Manager *m = NULL;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
if (argc != 1) {
log_error("This program takes no arguments.");
- r = -EINVAL;
- goto finish;
+ return -EINVAL;
}
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, -1) >= 0);
r = manager_new(&m);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate manager object: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate manager object: %m");
r = manager_startup(m);
- if (r < 0) {
- log_error_errno(r, "Failed to fully start up daemon: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to fully start up daemon: %m");
log_debug("systemd-portabled running as pid " PID_FMT, getpid_cached());
-
sd_notify(false,
"READY=1\n"
"STATUS=Processing requests...");
r = manager_run(m);
+ (void) sd_notify(false,
+ "STOPPING=1\n"
+ "STATUS=Shutting down...");
log_debug("systemd-portabled stopped as pid " PID_FMT, getpid_cached());
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c
index d891f18b65..90f542a058 100644
--- a/src/quotacheck/quotacheck.c
+++ b/src/quotacheck/quotacheck.c
@@ -6,6 +6,7 @@
#include <sys/prctl.h>
#include <unistd.h>
+#include "main-func.h"
#include "proc-cmdline.h"
#include "process-util.h"
#include "signal-util.h"
@@ -52,17 +53,14 @@ static void test_files(void) {
#endif
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
- if (argc > 1) {
- log_error("This program takes no arguments.");
- return EXIT_FAILURE;
- }
+ log_setup_service();
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ if (argc > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "This program takes no arguments.");
umask(0022);
@@ -74,15 +72,15 @@ int main(int argc, char *argv[]) {
if (!arg_force) {
if (arg_skip)
- return EXIT_SUCCESS;
+ return 0;
if (access("/run/systemd/quotacheck", F_OK) < 0)
- return EXIT_SUCCESS;
+ return 0;
}
- r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_WAIT|FORK_LOG, NULL);
if (r < 0)
- goto finish;
+ return r;
if (r == 0) {
static const char * const cmdline[] = {
QUOTACHECK,
@@ -96,6 +94,7 @@ int main(int argc, char *argv[]) {
_exit(EXIT_FAILURE); /* Operational error */
}
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 223b56306c..0c5f329756 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -6,33 +6,35 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "sd-id128.h"
+
#include "alloc-util.h"
#include "fd-util.h"
#include "io-util.h"
#include "log.h"
+#include "main-func.h"
#include "mkdir.h"
#include "string-util.h"
#include "util.h"
#define POOL_SIZE_MIN 512
+#define POOL_SIZE_MAX (10*1024*1024)
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_close_ int seed_fd = -1, random_fd = -1;
+ bool read_seed_file, write_seed_file;
_cleanup_free_ void* buf = NULL;
size_t buf_size = 0;
+ struct stat st;
ssize_t k;
- int r, open_rw_error;
FILE *f;
- bool refresh_seed_file = true;
+ int r;
- if (argc != 2) {
- log_error("This program requires one argument.");
- return EXIT_FAILURE;
- }
+ log_setup_service();
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ if (argc != 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "This program requires one argument.");
umask(0022);
@@ -46,31 +48,23 @@ int main(int argc, char *argv[]) {
fclose(f);
}
- if (buf_size <= POOL_SIZE_MIN)
+ if (buf_size < POOL_SIZE_MIN)
buf_size = POOL_SIZE_MIN;
- buf = malloc(buf_size);
- if (!buf) {
- r = log_oom();
- goto finish;
- }
-
r = mkdir_parents_label(RANDOM_SEED, 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m");
- /* When we load the seed we read it and write it to the device
- * and then immediately update the saved seed with new data,
- * to make sure the next boot gets seeded differently. */
+ /* When we load the seed we read it and write it to the device and then immediately update the saved seed with
+ * new data, to make sure the next boot gets seeded differently. */
if (streq(argv[1], "load")) {
+ int open_rw_error;
seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
open_rw_error = -errno;
if (seed_fd < 0) {
- refresh_seed_file = false;
+ write_seed_file = false;
seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (seed_fd < 0) {
@@ -80,22 +74,54 @@ int main(int argc, char *argv[]) {
open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m");
r = log_full_errno(missing ? LOG_DEBUG : LOG_ERR,
errno, "Failed to open " RANDOM_SEED " for reading: %m");
- if (missing)
- r = 0;
-
- goto finish;
+ return missing ? 0 : r;
}
- }
+ } else
+ write_seed_file = true;
random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
if (random_fd < 0) {
+ write_seed_file = false;
+
random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600);
- if (random_fd < 0) {
- r = log_error_errno(errno, "Failed to open /dev/urandom: %m");
- goto finish;
- }
+ if (random_fd < 0)
+ return log_error_errno(errno, "Failed to open /dev/urandom: %m");
}
+ read_seed_file = true;
+
+ } else if (streq(argv[1], "save")) {
+
+ random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (random_fd < 0)
+ return log_error_errno(errno, "Failed to open /dev/urandom: %m");
+
+ seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
+ if (seed_fd < 0)
+ return log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
+
+ read_seed_file = false;
+ write_seed_file = true;
+
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown verb '%s'.", argv[1]);
+
+ if (fstat(seed_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat() seed file " RANDOM_SEED ": %m");
+
+ /* If the seed file is larger than what we expect, then honour the existing size and save/restore as much as it says */
+ if ((uint64_t) st.st_size > buf_size)
+ buf_size = MIN(st.st_size, POOL_SIZE_MAX);
+
+ buf = malloc(buf_size);
+ if (!buf)
+ return log_oom();
+
+ if (read_seed_file) {
+ sd_id128_t mid;
+ int z;
+
k = loop_read(seed_fd, buf, buf_size, false);
if (k < 0)
r = log_error_errno(k, "Failed to read seed from " RANDOM_SEED ": %m");
@@ -110,28 +136,23 @@ int main(int argc, char *argv[]) {
log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
}
- } else if (streq(argv[1], "save")) {
-
- seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
- if (seed_fd < 0) {
- r = log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
- goto finish;
+ /* Let's also write the machine ID into the random seed. Why? As an extra protection against "golden
+ * images" that are put together sloppily, i.e. images which are duplicated on multiple systems but
+ * where the random seed file is not properly reset. Frequently the machine ID is properly reset on
+ * those systems however (simply because it's easier to notice, if it isn't due to address clashes and
+ * so on, while random seed equivalence is generally not noticed easily), hence let's simply write the
+ * machined ID into the random pool too. */
+ z = sd_id128_get_machine(&mid);
+ if (z < 0)
+ log_debug_errno(z, "Failed to get machine ID, ignoring: %m");
+ else {
+ z = loop_write(random_fd, &mid, sizeof(mid), false);
+ if (z < 0)
+ log_debug_errno(z, "Failed to write machine ID to /dev/urandom, ignoring: %m");
}
-
- random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (random_fd < 0) {
- r = log_error_errno(errno, "Failed to open /dev/urandom: %m");
- goto finish;
- }
-
- } else {
- log_error("Unknown verb '%s'.", argv[1]);
- r = -EINVAL;
- goto finish;
}
- if (refresh_seed_file) {
-
+ if (write_seed_file) {
/* This is just a safety measure. Given that we are root and
* most likely created the file ourselves the mode and owner
* should be correct anyway. */
@@ -139,21 +160,18 @@ int main(int argc, char *argv[]) {
(void) fchown(seed_fd, 0, 0);
k = loop_read(random_fd, buf, buf_size, false);
- if (k < 0) {
- r = log_error_errno(k, "Failed to read new seed from /dev/urandom: %m");
- goto finish;
- }
- if (k == 0) {
- log_error("Got EOF while reading from /dev/urandom.");
- r = -EIO;
- goto finish;
- }
+ if (k < 0)
+ return log_error_errno(k, "Failed to read new seed from /dev/urandom: %m");
+ if (k == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Got EOF while reading from /dev/urandom.");
r = loop_write(seed_fd, buf, (size_t) k, false);
if (r < 0)
- log_error_errno(r, "Failed to write new random seed file: %m");
+ return log_error_errno(r, "Failed to write new random seed file: %m");
}
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c
index 44b2edec05..7a3948e92d 100644
--- a/src/rc-local-generator/rc-local-generator.c
+++ b/src/rc-local-generator/rc-local-generator.c
@@ -4,16 +4,22 @@
#include <stdio.h>
#include <unistd.h>
+#include "generator.h"
#include "log.h"
#include "mkdir.h"
#include "string-util.h"
#include "util.h"
-static const char *arg_dest = "/tmp";
+static const char *arg_dest = NULL;
+
+/* So you are reading this, and might wonder: why is this implemented as a generator rather than as a plain, statically
+ * enabled service that carries appropriate ConditionFileIsExecutable= lines? The answer is this: conditions bypass
+ * execution of a service's binary, but they have no influence on unit dependencies. Thus, a service that is
+ * conditioned out will still act as synchronization point in the dependency tree, and we'd rather not have that for
+ * these two legacy scripts. */
static int add_symlink(const char *service, const char *where) {
const char *from, *to;
- int r;
assert(service);
assert(where);
@@ -23,8 +29,7 @@ static int add_symlink(const char *service, const char *where) {
(void) mkdir_parents_label(to, 0755);
- r = symlink(from, to);
- if (r < 0) {
+ if (symlink(from, to) < 0) {
if (errno == EEXIST)
return 0;
@@ -34,37 +39,39 @@ static int add_symlink(const char *service, const char *where) {
return 1;
}
-int main(int argc, char *argv[]) {
- int ret = EXIT_SUCCESS;
+static int check_executable(const char *path) {
+ assert(path);
+
+ if (access(path, X_OK) < 0) {
+ if (errno == ENOENT)
+ return log_debug_errno(errno, "%s does not exist, skipping.", path);
+ if (errno == EACCES)
+ return log_info_errno(errno, "%s is not marked executable, skipping.", path);
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
+ return log_warning_errno(errno, "Couldn't determine if %s exists and is executable, skipping: %m", path);
}
- if (argc > 1)
- arg_dest = argv[1];
+ return 0;
+}
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+ int r = 0, k = 0;
- umask(0022);
+ assert_se(arg_dest = dest);
- if (access(RC_LOCAL_SCRIPT_PATH_START, X_OK) >= 0) {
+ if (check_executable(RC_LOCAL_SCRIPT_PATH_START) >= 0) {
log_debug("Automatically adding rc-local.service.");
- if (add_symlink("rc-local.service", "multi-user.target") < 0)
- ret = EXIT_FAILURE;
+ r = add_symlink("rc-local.service", "multi-user.target");
}
- if (access(RC_LOCAL_SCRIPT_PATH_STOP, X_OK) >= 0) {
+ if (check_executable(RC_LOCAL_SCRIPT_PATH_STOP) >= 0) {
log_debug("Automatically adding halt-local.service.");
- if (add_symlink("halt-local.service", "final.target") < 0)
- ret = EXIT_FAILURE;
+ k = add_symlink("halt-local.service", "final.target");
}
- return ret;
+ return r < 0 ? r : k;
}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index 9220a00215..0bac355e0f 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -8,8 +8,10 @@
#include <sys/wait.h>
#include <unistd.h>
+#include "env-util.h"
#include "exit-status.h"
#include "log.h"
+#include "main-func.h"
#include "mount-setup.h"
#include "mount-util.h"
#include "path-util.h"
@@ -18,114 +20,137 @@
#include "strv.h"
#include "util.h"
-/* Goes through /etc/fstab and remounts all API file systems, applying
- * options that are in /etc/fstab that systemd might not have
- * respected */
+/* Goes through /etc/fstab and remounts all API file systems, applying options that are in /etc/fstab that systemd
+ * might not have respected */
-int main(int argc, char *argv[]) {
+static int track_pid(Hashmap **h, const char *path, pid_t pid) {
+ _cleanup_free_ char *c = NULL;
+ int r;
+
+ assert(h);
+ assert(path);
+ assert(pid_is_valid(pid));
+
+ r = hashmap_ensure_allocated(h, NULL);
+ if (r < 0)
+ return log_oom();
+
+ c = strdup(path);
+ if (!c)
+ return log_oom();
+
+ r = hashmap_put(*h, PID_TO_PTR(pid), c);
+ if (r < 0)
+ return log_oom();
+
+ TAKE_PTR(c);
+ return 0;
+}
+
+static int run(int argc, char *argv[]) {
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
_cleanup_endmntent_ FILE *f = NULL;
+ bool has_root = false;
struct mntent* me;
int r;
- if (argc > 1) {
- log_error("This program takes no argument.");
- return EXIT_FAILURE;
- }
+ log_setup_service();
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ if (argc > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "This program takes no arguments.");
umask(0022);
f = setmntent("/etc/fstab", "re");
if (!f) {
- if (errno == ENOENT) {
- r = 0;
- goto finish;
- }
-
- r = log_error_errno(errno, "Failed to open /etc/fstab: %m");
- goto finish;
- }
-
- pids = hashmap_new(NULL);
- if (!pids) {
- r = log_oom();
- goto finish;
- }
-
- while ((me = getmntent(f))) {
- pid_t pid;
- int k;
- char *s;
-
- /* Remount the root fs, /usr and all API VFS */
- if (!mount_point_is_api(me->mnt_dir) &&
- !path_equal(me->mnt_dir, "/") &&
- !path_equal(me->mnt_dir, "/usr"))
- continue;
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to open /etc/fstab: %m");
+ } else {
+ while ((me = getmntent(f))) {
+ pid_t pid;
+
+ /* Remount the root fs, /usr and all API VFS */
+ if (!mount_point_is_api(me->mnt_dir) &&
+ !PATH_IN_SET(me->mnt_dir, "/", "/usr"))
+ continue;
- log_debug("Remounting %s", me->mnt_dir);
+ log_debug("Remounting %s...", me->mnt_dir);
- r = safe_fork("(remount)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
- if (r < 0)
- goto finish;
- if (r == 0) {
- /* Child */
+ if (path_equal(me->mnt_dir, "/"))
+ has_root = true;
- execv(MOUNT_PATH, STRV_MAKE(MOUNT_PATH, me->mnt_dir, "-o", "remount"));
+ r = safe_fork("(remount)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child */
+ execv(MOUNT_PATH, STRV_MAKE(MOUNT_PATH, me->mnt_dir, "-o", "remount"));
+ log_error_errno(errno, "Failed to execute " MOUNT_PATH ": %m");
+ _exit(EXIT_FAILURE);
+ }
- log_error_errno(errno, "Failed to execute " MOUNT_PATH ": %m");
- _exit(EXIT_FAILURE);
+ /* Parent */
+ r = track_pid(&pids, me->mnt_dir, pid);
+ if (r < 0)
+ return r;
}
+ }
- /* Parent */
+ if (!has_root) {
+ /* The $SYSTEMD_REMOUNT_ROOT_RW environment variable is set by systemd-gpt-auto-generator to tell us
+ * whether to remount things. We honour it only if there's no explicit line in /etc/fstab configured
+ * which takes precedence. */
+
+ r = getenv_bool("SYSTEMD_REMOUNT_ROOT_RW");
+ if (r > 0) {
+ pid_t pid;
+
+ log_debug("Remounting / writable...");
+
+ r = safe_fork("(remount-rw)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child */
+ execv(MOUNT_PATH, STRV_MAKE(MOUNT_PATH, "/", "-o", "remount,rw"));
+ log_error_errno(errno, "Failed to execute " MOUNT_PATH ": %m");
+ _exit(EXIT_FAILURE);
+ }
- s = strdup(me->mnt_dir);
- if (!s) {
- r = log_oom();
- goto finish;
- }
+ r = track_pid(&pids, "/", pid);
+ if (r < 0)
+ return r;
- k = hashmap_put(pids, PID_TO_PTR(pid), s);
- if (k < 0) {
- free(s);
- r = log_oom();
- goto finish;
- }
+ } else if (r < 0 && r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_REMOUNT_ROOT_RW, ignoring: %m");
}
r = 0;
while (!hashmap_isempty(pids)) {
+ _cleanup_free_ char *s = NULL;
siginfo_t si = {};
- char *s;
if (waitid(P_ALL, 0, &si, WEXITED) < 0) {
-
if (errno == EINTR)
continue;
- r = log_error_errno(errno, "waitid() failed: %m");
- goto finish;
+ return log_error_errno(errno, "waitid() failed: %m");
}
s = hashmap_remove(pids, PID_TO_PTR(si.si_pid));
- if (s) {
- if (!is_clean_exit(si.si_code, si.si_status, EXIT_CLEAN_COMMAND, NULL)) {
- if (si.si_code == CLD_EXITED)
- log_error(MOUNT_PATH " for %s exited with exit status %i.", s, si.si_status);
- else
- log_error(MOUNT_PATH " for %s terminated by signal %s.", s, signal_to_string(si.si_status));
-
- r = -ENOEXEC;
- }
-
- free(s);
+ if (s &&
+ !is_clean_exit(si.si_code, si.si_status, EXIT_CLEAN_COMMAND, NULL)) {
+ if (si.si_code == CLD_EXITED)
+ log_error(MOUNT_PATH " for %s exited with exit status %i.", s, si.si_status);
+ else
+ log_error(MOUNT_PATH " for %s terminated by signal %s.", s, signal_to_string(si.si_status));
+
+ r = -ENOEXEC;
}
}
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c
index efb68a354f..ee7a0ea130 100644
--- a/src/reply-password/reply-password.c
+++ b/src/reply-password/reply-password.c
@@ -6,7 +6,9 @@
#include <sys/socket.h>
#include <sys/un.h>
+#include "alloc-util.h"
#include "fd-util.h"
+#include "fileio.h"
#include "log.h"
#include "macro.h"
#include "socket-util.h"
@@ -14,31 +16,30 @@
#include "util.h"
static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) {
- union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- };
+ union sockaddr_union sa = {};
+ int salen;
assert(fd >= 0);
assert(socket_name);
assert(packet);
- strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
+ salen = sockaddr_un_set_path(&sa.un, socket_name);
+ if (salen < 0)
+ return log_error_errno(salen, "Specified socket path for AF_UNIX socket invalid, refusing: %s", socket_name);
- if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+ if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, salen) < 0)
return log_error_errno(errno, "Failed to send: %m");
return 0;
}
int main(int argc, char *argv[]) {
+ _cleanup_free_ char *packet = NULL;
_cleanup_close_ int fd = -1;
- char packet[LINE_MAX];
- size_t length;
+ size_t length = 0;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
if (argc != 3) {
log_error("Wrong number of arguments.");
@@ -46,18 +47,36 @@ int main(int argc, char *argv[]) {
}
if (streq(argv[1], "1")) {
+ _cleanup_string_free_erase_ char *line = NULL;
- packet[0] = '+';
- if (!fgets(packet+1, sizeof(packet)-1, stdin)) {
- r = log_error_errno(errno, "Failed to read password: %m");
+ r = read_line(stdin, LONG_LINE_MAX, &line);
+ if (r < 0) {
+ log_error_errno(r, "Failed to read password: %m");
+ goto finish;
+ }
+ if (r == 0) {
+ log_error("Got EOF while reading password.");
+ r = -EIO;
+ goto finish;
+ }
+
+ packet = strjoin("+", line);
+ if (!packet) {
+ r = log_oom();
goto finish;
}
- truncate_nl(packet+1);
- length = 1 + strlen(packet+1) + 1;
+ length = 1 + strlen(line) + 1;
+
} else if (streq(argv[1], "0")) {
- packet[0] = '-';
+ packet = strdup("-");
+ if (!packet) {
+ r = log_oom();
+ goto finish;
+ }
+
length = 1;
+
} else {
log_error("Invalid first argument %s", argv[1]);
r = -EINVAL;
@@ -73,7 +92,7 @@ int main(int argc, char *argv[]) {
r = send_on_socket(fd, argv[2], packet, length);
finish:
- explicit_bzero(packet, sizeof(packet));
+ explicit_bzero_safe(packet, length);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/resolve/meson.build b/src/resolve/meson.build
index 15f3835d55..92b67b6333 100644
--- a/src/resolve/meson.build
+++ b/src/resolve/meson.build
@@ -63,6 +63,7 @@ systemd_resolved_sources = files('''
resolved-dns-stub.c
resolved-etc-hosts.h
resolved-etc-hosts.c
+ resolved-dnstls.h
'''.split())
resolvectl_sources = files('''
@@ -141,7 +142,17 @@ systemd_resolved_sources += [resolved_gperf_c, resolved_dnssd_gperf_c]
systemd_resolved_dependencies = [threads, libgpg_error, libm, libidn]
if conf.get('ENABLE_DNS_OVER_TLS') == 1
- systemd_resolved_dependencies += [libgnutls]
+ if conf.get('DNS_OVER_TLS_USE_GNUTLS') == 1
+ systemd_resolved_sources += files('resolved-dnstls-gnutls.c',
+ 'resolved-dnstls-gnutls.h')
+ systemd_resolved_dependencies += libgnutls
+ elif conf.get('DNS_OVER_TLS_USE_OPENSSL') == 1
+ systemd_resolved_sources += files('resolved-dnstls-openssl.c',
+ 'resolved-dnstls-openssl.h')
+ systemd_resolved_dependencies += libopenssl
+ else
+ error('unknown dependency for supporting DNS-over-TLS')
+ endif
endif
if conf.get('ENABLE_RESOLVE') == 1
@@ -183,6 +194,16 @@ tests += [
libm],
'ENABLE_RESOLVE'],
+ [['src/resolve/test-resolved-etc-hosts.c',
+ 'src/resolve/resolved-etc-hosts.c',
+ 'src/resolve/resolved-etc-hosts.h'],
+ [libsystemd_resolve_core,
+ libshared],
+ [libgcrypt,
+ libgpg_error,
+ libm],
+ 'ENABLE_RESOLVE'],
+
[['src/resolve/test-resolved-packet.c',
dns_type_headers],
[libsystemd_resolve_core,
diff --git a/src/resolve/resolv.conf b/src/resolve/resolv.conf
index ffc460dbf2..c3079aca1d 100644
--- a/src/resolve/resolv.conf
+++ b/src/resolve/resolv.conf
@@ -15,3 +15,4 @@
# operation for /etc/resolv.conf.
nameserver 127.0.0.53
+options edns0
diff --git a/src/resolve/resolvconf-compat.c b/src/resolve/resolvconf-compat.c
index d7e68003e6..383d0b819b 100644
--- a/src/resolve/resolvconf-compat.c
+++ b/src/resolve/resolvconf-compat.c
@@ -9,13 +9,22 @@
#include "extract-word.h"
#include "fileio.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "resolvconf-compat.h"
#include "resolvectl.h"
#include "resolved-def.h"
#include "string-util.h"
#include "strv.h"
+#include "terminal-util.h"
+
+static int resolvconf_help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("resolvectl", "1", &link);
+ if (r < 0)
+ return log_oom();
-static void resolvconf_help(void) {
printf("%1$s -a INTERFACE < FILE\n"
"%1$s -d INTERFACE\n"
"\n"
@@ -34,7 +43,12 @@ static void resolvconf_help(void) {
"implementations are not supported and will cause the invocation to fail: -u,\n"
"-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n"
"--updates-are-enabled.\n"
- , program_invocation_short_name);
+ "\nSee the %2$s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_nameserver(const char *string) {
@@ -53,6 +67,8 @@ static int parse_nameserver(const char *string) {
if (strv_push(&arg_set_dns, word) < 0)
return log_oom();
+
+ word = NULL;
}
return 0;
@@ -107,7 +123,6 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
TYPE_EXCLUSIVE, /* -x */
} type = TYPE_REGULAR;
- const char *dot, *iface;
int c, r;
assert(argc >= 0);
@@ -125,8 +140,7 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
switch(c) {
case 'h':
- resolvconf_help();
- return 0; /* done */;
+ return resolvconf_help();
case ARG_VERSION:
return version();
@@ -169,19 +183,19 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
case 'r':
case 'v':
case 'V':
- log_error("Switch -%c not supported.", c);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Switch -%c not supported.", c);
/* The Debian resolvconf commands we don't support. */
case ARG_ENABLE_UPDATES:
- log_error("Switch --enable-updates not supported.");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Switch --enable-updates not supported.");
case ARG_DISABLE_UPDATES:
- log_error("Switch --disable-updates not supported.");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Switch --disable-updates not supported.");
case ARG_UPDATES_ARE_ENABLED:
- log_error("Switch --updates-are-enabled not supported.");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Switch --updates-are-enabled not supported.");
case '?':
return -EINVAL;
@@ -190,40 +204,19 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (arg_mode == _MODE_INVALID) {
- log_error("Expected either -a or -d on the command line.");
- return -EINVAL;
- }
+ if (arg_mode == _MODE_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Expected either -a or -d on the command line.");
- if (optind+1 != argc) {
- log_error("Expected interface name as argument.");
- return -EINVAL;
- }
-
- dot = strchr(argv[optind], '.');
- if (dot) {
- iface = strndupa(argv[optind], dot - argv[optind]);
- log_debug("Ignoring protocol specifier '%s'.", dot + 1);
- } else
- iface = argv[optind];
- optind++;
+ if (optind+1 != argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Expected interface name as argument.");
- if (parse_ifindex(iface, &arg_ifindex) < 0) {
- int ifi;
+ r = ifname_mangle(argv[optind]);
+ if (r <= 0)
+ return r;
- ifi = if_nametoindex(iface);
- if (ifi <= 0) {
- if (errno == ENODEV && arg_ifindex_permissive) {
- log_debug("Interface '%s' not found, but -f specified, ignoring.", iface);
- return 0; /* done */
- }
-
- return log_error_errno(errno, "Unknown interface '%s': %m", iface);
- }
-
- arg_ifindex = ifi;
- arg_ifname = iface;
- }
+ optind++;
if (arg_mode == MODE_SET_LINK) {
unsigned n = 0;
@@ -273,10 +266,9 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
} else if (type == TYPE_PRIVATE)
log_debug("Private DNS server data not supported, ignoring.");
- if (!arg_set_dns) {
- log_error("No DNS servers specified, refusing operation.");
- return -EINVAL;
- }
+ if (!arg_set_dns)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "No DNS servers specified, refusing operation.");
}
return 1; /* work to do */
diff --git a/src/resolve/resolvconf-compat.h b/src/resolve/resolvconf-compat.h
index 507ac3d222..872add1d56 100644
--- a/src/resolve/resolvconf-compat.h
+++ b/src/resolve/resolvconf-compat.h
@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
int resolvconf_parse_argv(int argc, char *argv[]);
diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c
index e96c13fea6..4d533f851a 100644
--- a/src/resolve/resolvectl.c
+++ b/src/resolve/resolvectl.c
@@ -15,9 +15,12 @@
#include "escape.h"
#include "gcrypt-util.h"
#include "in-addr-util.h"
+#include "main-func.h"
+#include "missing_network.h"
#include "netlink-util.h"
#include "pager.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "resolvconf-compat.h"
#include "resolvectl.h"
#include "resolved-def.h"
@@ -28,13 +31,13 @@
#include "verbs.h"
static int arg_family = AF_UNSPEC;
-int arg_ifindex = 0;
-const char *arg_ifname = NULL;
+static int arg_ifindex = 0;
+static char *arg_ifname = NULL;
static uint16_t arg_type = 0;
static uint16_t arg_class = 0;
static bool arg_legend = true;
static uint64_t arg_flags = 0;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
bool arg_ifindex_permissive = false; /* If true, don't generate an error if the specified interface index doesn't exist */
static const char *arg_service_family = NULL;
@@ -55,10 +58,16 @@ static const char *arg_set_dns_over_tls = NULL;
static const char *arg_set_dnssec = NULL;
static char **arg_set_nta = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_ifname, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_set_dns, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_set_domain, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_set_nta, strv_freep);
+
typedef enum StatusMode {
STATUS_ALL,
STATUS_DNS,
STATUS_DOMAIN,
+ STATUS_DEFAULT_ROUTE,
STATUS_LLMNR,
STATUS_MDNS,
STATUS_PRIVATE,
@@ -66,7 +75,7 @@ typedef enum StatusMode {
STATUS_NTA,
} StatusMode;
-static int parse_ifindex_with_warn(const char *s) {
+static int parse_ifindex_and_warn(const char *s) {
int ifi;
assert(s);
@@ -74,12 +83,52 @@ static int parse_ifindex_with_warn(const char *s) {
if (parse_ifindex(s, &ifi) < 0) {
ifi = if_nametoindex(s);
if (ifi <= 0)
- return log_error_errno(errno, "Unknown interface %s: %m", s);
+ return log_error_errno(errno, "Unknown interface '%s': %m", s);
}
return ifi;
}
+int ifname_mangle(const char *s) {
+ _cleanup_free_ char *iface = NULL;
+ const char *dot;
+ int ifi;
+
+ assert(s);
+
+ dot = strchr(s, '.');
+ if (dot) {
+ log_debug("Ignoring protocol specifier '%s'.", dot + 1);
+ iface = strndup(s, dot - s);
+
+ } else
+ iface = strdup(s);
+ if (!iface)
+ return log_oom();
+
+ if (parse_ifindex(iface, &ifi) < 0) {
+ ifi = if_nametoindex(iface);
+ if (ifi <= 0) {
+ if (errno == ENODEV && arg_ifindex_permissive) {
+ log_debug("Interface '%s' not found, but -f specified, ignoring.", iface);
+ return 0; /* done */
+ }
+
+ return log_error_errno(errno, "Unknown interface '%s': %m", iface);
+ }
+ }
+
+ if (arg_ifindex > 0 && arg_ifindex != ifi) {
+ log_error("Specified multiple different interfaces. Refusing.");
+ return -EINVAL;
+ }
+
+ arg_ifindex = ifi;
+ free_and_replace(arg_ifname, iface);
+
+ return 1;
+}
+
static void print_source(uint64_t flags, usec_t rtt) {
char rtt_str[FORMAT_TIMESTAMP_MAX];
@@ -89,7 +138,7 @@ static void print_source(uint64_t flags, usec_t rtt) {
if (flags == 0)
return;
- fputs("\n-- Information acquired via", stdout);
+ printf("\n%s-- Information acquired via", ansi_grey());
if (flags != 0)
printf(" protocol%s%s%s%s%s",
@@ -101,30 +150,38 @@ static void print_source(uint64_t flags, usec_t rtt) {
assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
- printf(" in %s", rtt_str);
+ printf(" in %s.%s\n"
+ "%s-- Data is authenticated: %s%s\n",
+ rtt_str, ansi_normal(),
+ ansi_grey(), yes_no(flags & SD_RESOLVED_AUTHENTICATED), ansi_normal());
+}
- fputc('.', stdout);
- fputc('\n', stdout);
+static void print_ifindex_comment(int printed_so_far, int ifindex) {
+ char ifname[IF_NAMESIZE];
- printf("-- Data is authenticated: %s\n", yes_no(flags & SD_RESOLVED_AUTHENTICATED));
+ if (ifindex <= 0)
+ return;
+
+ if (!if_indextoname(ifindex, ifname))
+ log_warning_errno(errno, "Failed to resolve interface name for index %i, ignoring: %m", ifindex);
+ else
+ printf("%*s%s-- link: %s%s",
+ 60 > printed_so_far ? 60 - printed_so_far : 0, " ", /* Align comment to the 60th column */
+ ansi_grey(), ifname, ansi_normal());
}
static int resolve_host(sd_bus *bus, const char *name) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char *canonical = NULL;
- char ifname[IF_NAMESIZE] = "";
unsigned c = 0;
- int r;
uint64_t flags;
usec_t ts;
+ int r;
assert(name);
- if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
-
- log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
r = sd_bus_message_new_method_call(
bus,
@@ -154,7 +211,7 @@ static int resolve_host(sd_bus *bus, const char *name) {
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
_cleanup_free_ char *pretty = NULL;
- int ifindex, family;
+ int ifindex, family, k;
const void *a;
size_t sz;
@@ -182,18 +239,16 @@ static int resolve_host(sd_bus *bus, const char *name) {
return -EINVAL;
}
- ifname[0] = 0;
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
-
r = in_addr_ifindex_to_string(family, a, ifindex, &pretty);
if (r < 0)
return log_error_errno(r, "Failed to print address for %s: %m", name);
- printf("%*s%s %s%s%s\n",
- (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
- pretty,
- isempty(ifname) ? "" : "%", ifname);
+ k = printf("%*s%s %s%s%s",
+ (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
+ ansi_highlight(), pretty, ansi_normal());
+
+ print_ifindex_comment(k, ifindex);
+ fputc('\n', stdout);
c++;
}
@@ -227,7 +282,6 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *pretty = NULL;
- char ifname[IF_NAMESIZE] = "";
uint64_t flags;
unsigned c = 0;
usec_t ts;
@@ -244,10 +298,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
if (r < 0)
return log_oom();
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
-
- log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
+ log_debug("Resolving %s.", pretty);
r = sd_bus_message_new_method_call(
bus,
@@ -274,10 +325,8 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
ts = now(CLOCK_MONOTONIC);
r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
- if (r < 0) {
- log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
ts = now(CLOCK_MONOTONIC) - ts;
@@ -287,6 +336,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) {
const char *n;
+ int k;
assert_cc(sizeof(int) == sizeof(int32_t));
@@ -298,16 +348,13 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
if (r < 0)
return r;
- ifname[0] = 0;
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
+ k = printf("%*s%s %s%s%s",
+ (int) strlen(pretty), c == 0 ? pretty : "",
+ c == 0 ? ":" : " ",
+ ansi_highlight(), n, ansi_normal());
- printf("%*s%*s%*s%s %s\n",
- (int) strlen(pretty), c == 0 ? pretty : "",
- isempty(ifname) ? 0 : 1, c > 0 || isempty(ifname) ? "" : "%",
- (int) strlen(ifname), c == 0 ? ifname : "",
- c == 0 ? ":" : " ",
- n);
+ print_ifindex_comment(k, ifindex);
+ fputc('\n', stdout);
c++;
}
@@ -336,7 +383,6 @@ static int output_rr_packet(const void *d, size_t l, int ifindex) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
int r;
- char ifname[IF_NAMESIZE] = "";
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX);
if (r < 0)
@@ -362,15 +408,15 @@ static int output_rr_packet(const void *d, size_t l, int ifindex) {
fwrite(data, 1, k, stdout);
} else {
const char *s;
+ int k;
s = dns_resource_record_to_string(rr);
if (!s)
return log_oom();
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
-
- printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname);
+ k = printf("%s", s);
+ print_ifindex_comment(k, ifindex);
+ fputc('\n', stdout);
}
return 0;
@@ -379,7 +425,6 @@ static int output_rr_packet(const void *d, size_t l, int ifindex) {
static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type, bool warn_missing) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char ifname[IF_NAMESIZE] = "";
unsigned n = 0;
uint64_t flags;
int r;
@@ -388,10 +433,7 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
assert(name);
- if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
-
- log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(arg_ifname) ? "*" : arg_ifname);
r = sd_bus_message_new_method_call(
bus,
@@ -534,10 +576,9 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) {
_cleanup_free_ char *t = NULL;
const char *e;
- if (class != 0) {
- log_error("DNS class specified twice.");
- return -EINVAL;
- }
+ if (class != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "DNS class specified twice.");
e = strchrnul(f, ';');
t = strndup(f, e - f);
@@ -545,10 +586,9 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) {
return log_oom();
r = dns_class_from_string(t);
- if (r < 0) {
- log_error("Unknown DNS class %s.", t);
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown DNS class %s.", t);
class = r;
@@ -565,10 +605,9 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) {
_cleanup_free_ char *t = NULL;
const char *e;
- if (type != 0) {
- log_error("DNS type specified twice.");
- return -EINVAL;
- }
+ if (type != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "DNS type specified twice.");
e = strchrnul(f, ';');
t = strndup(f, e - f);
@@ -576,10 +615,9 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) {
return log_oom();
r = dns_type_from_string(t);
- if (r < 0) {
- log_error("Unknown DNS type %s.", t);
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown DNS type %s.", t);
type = r;
@@ -604,8 +642,8 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) {
return resolve_record(bus, n, class, type, true);
invalid:
- log_error("Invalid DNS URI: %s", name);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid DNS URI: %s", name);
}
static int verb_query(int argc, char **argv, void *userdata) {
@@ -645,7 +683,6 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
const char *canonical_name, *canonical_type, *canonical_domain;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char ifname[IF_NAMESIZE] = "";
size_t indent, sz;
uint64_t flags;
const char *p;
@@ -659,15 +696,12 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
name = empty_to_null(name);
type = empty_to_null(type);
- if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
-
if (name)
- log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
else if (type)
- log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
else
- log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
r = sd_bus_message_new_method_call(
bus,
@@ -726,7 +760,7 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
_cleanup_free_ char *pretty = NULL;
- int ifindex, family;
+ int ifindex, family, k;
const void *a;
assert_cc(sizeof(int) == sizeof(int32_t));
@@ -753,15 +787,13 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
return -EINVAL;
}
- ifname[0] = 0;
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
-
- r = in_addr_to_string(family, a, &pretty);
+ r = in_addr_ifindex_to_string(family, a, ifindex, &pretty);
if (r < 0)
return log_error_errno(r, "Failed to print address for %s: %m", name);
- printf("%*s%s%s%s\n", (int) indent, "", pretty, isempty(ifname) ? "" : "%s", ifname);
+ k = printf("%*s%s", (int) indent, "", pretty);
+ print_ifindex_comment(k, ifindex);
+ fputc('\n', stdout);
}
if (r < 0)
return bus_log_parse_error(r);
@@ -856,13 +888,12 @@ static int resolve_openpgp(sd_bus *bus, const char *address) {
assert(address);
domain = strrchr(address, '@');
- if (!domain) {
- log_error("Address does not contain '@': \"%s\"", address);
- return -EINVAL;
- } else if (domain == address || domain[1] == '\0') {
- log_error("Address starts or ends with '@': \"%s\"", address);
- return -EINVAL;
- }
+ if (!domain)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Address does not contain '@': \"%s\"", address);
+ if (domain == address || domain[1] == '\0')
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Address starts or ends with '@': \"%s\"", address);
domain++;
r = string_hashsum_sha256(address, domain - 1 - address, &hashed);
@@ -879,7 +910,7 @@ static int resolve_openpgp(sd_bus *bus, const char *address) {
arg_type ?: DNS_TYPE_OPENPGPKEY, false);
if (IN_SET(r, -ENXIO, -ESRCH)) { /* NXDOMAIN or NODATA? */
- hashed = NULL;
+ hashed = mfree(hashed);
r = string_hashsum_sha224(address, domain - 1 - address, &hashed);
if (r < 0)
return log_error_errno(r, "Hashing failed: %m");
@@ -1328,26 +1359,34 @@ static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p)
return 0;
}
-static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
-
- struct link_info {
- uint64_t scopes_mask;
- const char *llmnr;
- const char *mdns;
- const char *dns_over_tls;
- const char *dnssec;
- char *current_dns;
- char **dns;
- char **domains;
- char **ntas;
- bool dnssec_supported;
- } link_info = {};
+struct link_info {
+ uint64_t scopes_mask;
+ const char *llmnr;
+ const char *mdns;
+ const char *dns_over_tls;
+ const char *dnssec;
+ char *current_dns;
+ char **dns;
+ char **domains;
+ char **ntas;
+ bool dnssec_supported;
+ bool default_route;
+};
+
+static void link_info_clear(struct link_info *p) {
+ free(p->current_dns);
+ strv_free(p->dns);
+ strv_free(p->domains);
+ strv_free(p->ntas);
+}
+static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
static const struct bus_properties_map property_map[] = {
{ "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) },
{ "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
{ "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) },
{ "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
+ { "DefaultRoute", "b", NULL, offsetof(struct link_info, default_route) },
{ "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) },
{ "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) },
{ "DNSOverTLS", "s", NULL, offsetof(struct link_info, dns_over_tls) },
@@ -1356,9 +1395,9 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
{ "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) },
{}
};
-
_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_free_ char *ifi = NULL, *p = NULL;
char ifname[IF_NAMESIZE] = "";
char **i;
@@ -1389,26 +1428,26 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
&error,
&m,
&link_info);
- if (r < 0) {
- log_error_errno(r, "Failed to get link data for %i: %s", ifindex, bus_error_message(&error, r));
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get link data for %i: %s", ifindex, bus_error_message(&error, r));
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
- if (mode == STATUS_DNS) {
- r = status_print_strv_ifindex(ifindex, name, link_info.dns);
- goto finish;
- }
+ if (mode == STATUS_DNS)
+ return status_print_strv_ifindex(ifindex, name, link_info.dns);
- if (mode == STATUS_DOMAIN) {
- r = status_print_strv_ifindex(ifindex, name, link_info.domains);
- goto finish;
- }
+ if (mode == STATUS_DOMAIN)
+ return status_print_strv_ifindex(ifindex, name, link_info.domains);
+
+ if (mode == STATUS_NTA)
+ return status_print_strv_ifindex(ifindex, name, link_info.ntas);
- if (mode == STATUS_NTA) {
- r = status_print_strv_ifindex(ifindex, name, link_info.ntas);
- goto finish;
+ if (mode == STATUS_DEFAULT_ROUTE) {
+ printf("%sLink %i (%s)%s: %s\n",
+ ansi_highlight(), ifindex, name, ansi_normal(),
+ yes_no(link_info.default_route));
+
+ return 0;
}
if (mode == STATUS_LLMNR) {
@@ -1416,8 +1455,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
ansi_highlight(), ifindex, name, ansi_normal(),
strna(link_info.llmnr));
- r = 0;
- goto finish;
+ return 0;
}
if (mode == STATUS_MDNS) {
@@ -1425,8 +1463,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
ansi_highlight(), ifindex, name, ansi_normal(),
strna(link_info.mdns));
- r = 0;
- goto finish;
+ return 0;
}
if (mode == STATUS_PRIVATE) {
@@ -1434,8 +1471,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
ansi_highlight(), ifindex, name, ansi_normal(),
strna(link_info.dns_over_tls));
- r = 0;
- goto finish;
+ return 0;
}
if (mode == STATUS_DNSSEC) {
@@ -1443,8 +1479,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
ansi_highlight(), ifindex, name, ansi_normal(),
strna(link_info.dnssec));
- r = 0;
- goto finish;
+ return 0;
}
if (empty_line && *empty_line)
@@ -1463,11 +1498,13 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "",
link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : "");
- printf(" LLMNR setting: %s\n"
+ 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),
@@ -1498,14 +1535,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
if (empty_line)
*empty_line = true;
- r = 0;
-
-finish:
- free(link_info.current_dns);
- strv_free(link_info.dns);
- strv_free(link_info.domains);
- strv_free(link_info.ntas);
- return r;
+ return 0;
}
static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
@@ -1602,21 +1632,28 @@ static int status_print_strv_global(char **p) {
return 0;
}
-static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
-
- struct global_info {
- char *current_dns;
- char **dns;
- char **fallback_dns;
- char **domains;
- char **ntas;
- const char *llmnr;
- const char *mdns;
- const char *dns_over_tls;
- const char *dnssec;
- bool dnssec_supported;
- } global_info = {};
+struct global_info {
+ char *current_dns;
+ char **dns;
+ char **fallback_dns;
+ char **domains;
+ char **ntas;
+ const char *llmnr;
+ const char *mdns;
+ const char *dns_over_tls;
+ const char *dnssec;
+ bool dnssec_supported;
+};
+
+static void global_info_clear(struct global_info *p) {
+ free(p->current_dns);
+ strv_free(p->dns);
+ strv_free(p->fallback_dns);
+ strv_free(p->domains);
+ strv_free(p->ntas);
+}
+static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
static const struct bus_properties_map property_map[] = {
{ "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) },
{ "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, fallback_dns) },
@@ -1630,9 +1667,9 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
{ "DNSSECSupported", "b", NULL, offsetof(struct global_info, dnssec_supported) },
{}
};
-
_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;
int r;
@@ -1647,58 +1684,46 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
&error,
&m,
&global_info);
- if (r < 0) {
- log_error_errno(r, "Failed to get global data: %s", bus_error_message(&error, r));
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get global data: %s", bus_error_message(&error, r));
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
- if (mode == STATUS_DNS) {
- r = status_print_strv_global(global_info.dns);
- goto finish;
- }
+ if (mode == STATUS_DNS)
+ return status_print_strv_global(global_info.dns);
- if (mode == STATUS_DOMAIN) {
- r = status_print_strv_global(global_info.domains);
- goto finish;
- }
+ if (mode == STATUS_DOMAIN)
+ return status_print_strv_global(global_info.domains);
- if (mode == STATUS_NTA) {
- r = status_print_strv_global(global_info.ntas);
- goto finish;
- }
+ if (mode == STATUS_NTA)
+ return status_print_strv_global(global_info.ntas);
if (mode == STATUS_LLMNR) {
printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
strna(global_info.llmnr));
- r = 0;
- goto finish;
+ return 0;
}
if (mode == STATUS_MDNS) {
printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
strna(global_info.mdns));
- r = 0;
- goto finish;
+ return 0;
}
if (mode == STATUS_PRIVATE) {
printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
strna(global_info.dns_over_tls));
- r = 0;
- goto finish;
+ return 0;
}
if (mode == STATUS_DNSSEC) {
printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(),
strna(global_info.dnssec));
- r = 0;
- goto finish;
+ return 0;
}
printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
@@ -1744,16 +1769,7 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
*empty_line = true;
- r = 0;
-
-finish:
- free(global_info.current_dns);
- strv_free(global_info.dns);
- strv_free(global_info.fallback_dns);
- strv_free(global_info.domains);
- strv_free(global_info.ntas);
-
- return r;
+ return 0;
}
static int status_all(sd_bus *bus, StatusMode mode) {
@@ -1828,7 +1844,7 @@ static int verb_status(int argc, char **argv, void *userdata) {
STRV_FOREACH(ifname, argv + 1) {
int ifindex;
- ifindex = parse_ifindex_with_warn(*ifname);
+ ifindex = parse_ifindex_and_warn(*ifname);
if (ifindex < 0)
continue;
@@ -1855,25 +1871,22 @@ static int verb_dns(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 *req = NULL;
sd_bus *bus = userdata;
- int ifindex, r;
char **p;
+ int r;
assert(bus);
- if (argc <= 1)
- return status_all(bus, STATUS_DNS);
-
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
}
- if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_DNS, NULL);
+ if (arg_ifindex <= 0)
+ return status_all(bus, STATUS_DNS);
+
+ if (argc < 3)
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNS, NULL);
r = sd_bus_message_new_method_call(
bus,
@@ -1885,7 +1898,7 @@ static int verb_dns(int argc, char **argv, void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(req, "i", ifindex);
+ r = sd_bus_message_append(req, "i", arg_ifindex);
if (r < 0)
return bus_log_create_error(r);
@@ -1893,28 +1906,32 @@ static int verb_dns(int argc, char **argv, void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- STRV_FOREACH(p, argv + 2) {
- struct in_addr_data data;
+ /* If only argument is the empty string, then call SetLinkDNS() with an
+ * empty list, which will clear the list of domains for an interface. */
+ if (!strv_equal(argv + 2, STRV_MAKE(""))) {
+ STRV_FOREACH(p, argv + 2) {
+ struct in_addr_data data;
- r = in_addr_from_string_auto(*p, &data.family, &data.address);
- if (r < 0)
- return log_error_errno(r, "Failed to parse DNS server address: %s", *p);
+ r = in_addr_from_string_auto(*p, &data.family, &data.address);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse DNS server address: %s", *p);
- r = sd_bus_message_open_container(req, 'r', "iay");
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_open_container(req, 'r', "iay");
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_append(req, "i", data.family);
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_append(req, "i", data.family);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_append_array(req, 'y', &data.address, FAMILY_ADDRESS_SIZE(data.family));
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_append_array(req, 'y', &data.address, FAMILY_ADDRESS_SIZE(data.family));
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_close_container(req);
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_close_container(req);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
}
r = sd_bus_message_close_container(req);
@@ -1924,7 +1941,7 @@ static int verb_dns(int argc, char **argv, void *userdata) {
r = sd_bus_call(bus, req, 0, &error, NULL);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
@@ -1940,25 +1957,22 @@ static int verb_domain(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 *req = NULL;
sd_bus *bus = userdata;
- int ifindex, r;
char **p;
+ int r;
assert(bus);
- if (argc <= 1)
- return status_all(bus, STATUS_DOMAIN);
-
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
}
- if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_DOMAIN, NULL);
+ if (arg_ifindex <= 0)
+ return status_all(bus, STATUS_DOMAIN);
+
+ if (argc < 3)
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_DOMAIN, NULL);
r = sd_bus_message_new_method_call(
bus,
@@ -1970,7 +1984,7 @@ static int verb_domain(int argc, char **argv, void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(req, "i", ifindex);
+ r = sd_bus_message_append(req, "i", arg_ifindex);
if (r < 0)
return bus_log_create_error(r);
@@ -1978,22 +1992,26 @@ static int verb_domain(int argc, char **argv, void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- STRV_FOREACH(p, argv + 2) {
- const char *n;
+ /* If only argument is the empty string, then call SetLinkDomains() with an
+ * empty list, which will clear the list of domains for an interface. */
+ if (!strv_equal(argv + 2, STRV_MAKE(""))) {
+ STRV_FOREACH(p, argv + 2) {
+ const char *n;
- n = **p == '~' ? *p + 1 : *p;
+ n = **p == '~' ? *p + 1 : *p;
- r = dns_name_is_valid(n);
- if (r < 0)
- return log_error_errno(r, "Failed to validate specified domain %s: %m", n);
- if (r == 0) {
- log_error("Domain not valid: %s", n);
- return -EINVAL;
- }
+ r = dns_name_is_valid(n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to validate specified domain %s: %m", n);
+ if (r == 0) {
+ log_error("Domain not valid: %s", n);
+ return -EINVAL;
+ }
- r = sd_bus_message_append(req, "(sb)", n, **p == '~');
- if (r < 0)
- return bus_log_create_error(r);
+ r = sd_bus_message_append(req, "(sb)", n, **p == '~');
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
}
r = sd_bus_message_close_container(req);
@@ -2003,7 +2021,7 @@ static int verb_domain(int argc, char **argv, void *userdata) {
r = sd_bus_call(bus, req, 0, &error, NULL);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
@@ -2015,27 +2033,69 @@ static int verb_domain(int argc, char **argv, void *userdata) {
return 0;
}
-static int verb_llmnr(int argc, char **argv, void *userdata) {
+static int verb_default_route(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int ifindex, r;
+ int r, b;
assert(bus);
- if (argc <= 1)
- return status_all(bus, STATUS_LLMNR);
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
+ if (arg_ifindex <= 0)
+ return status_all(bus, STATUS_DEFAULT_ROUTE);
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
+ if (argc < 3)
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_DEFAULT_ROUTE, NULL);
+
+ b = parse_boolean(argv[2]);
+ if (b < 0)
+ return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "SetLinkDefaultRoute",
+ &error,
+ NULL,
+ "ib", arg_ifindex, b);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
+ return log_interface_is_managed(r, arg_ifindex);
+
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
+
+ return log_error_errno(r, "Failed to set default route configuration: %s", bus_error_message(&error, r));
}
- if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_LLMNR, NULL);
+ return 0;
+}
+
+static int verb_llmnr(int argc, char **argv, void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
+ int r;
+
+ assert(bus);
+
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_ifindex <= 0)
+ return status_all(bus, STATUS_LLMNR);
+
+ if (argc < 3)
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_LLMNR, NULL);
r = sd_bus_call_method(bus,
"org.freedesktop.resolve1",
@@ -2044,10 +2104,10 @@ static int verb_llmnr(int argc, char **argv, void *userdata) {
"SetLinkLLMNR",
&error,
NULL,
- "is", ifindex, argv[2]);
+ "is", arg_ifindex, argv[2]);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
@@ -2062,24 +2122,21 @@ static int verb_llmnr(int argc, char **argv, void *userdata) {
static int verb_mdns(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int ifindex, r;
+ int r;
assert(bus);
- if (argc <= 1)
- return status_all(bus, STATUS_MDNS);
-
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
}
- if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_MDNS, NULL);
+ if (arg_ifindex <= 0)
+ return status_all(bus, STATUS_MDNS);
+
+ if (argc < 3)
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_MDNS, NULL);
r = sd_bus_call_method(bus,
"org.freedesktop.resolve1",
@@ -2088,10 +2145,10 @@ static int verb_mdns(int argc, char **argv, void *userdata) {
"SetLinkMulticastDNS",
&error,
NULL,
- "is", ifindex, argv[2]);
+ "is", arg_ifindex, argv[2]);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
@@ -2106,24 +2163,21 @@ static int verb_mdns(int argc, char **argv, void *userdata) {
static int verb_dns_over_tls(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int ifindex, r;
+ int r;
assert(bus);
- if (argc <= 1)
- return status_all(bus, STATUS_PRIVATE);
-
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
}
- if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_PRIVATE, NULL);
+ if (arg_ifindex <= 0)
+ return status_all(bus, STATUS_PRIVATE);
+
+ if (argc < 3)
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_PRIVATE, NULL);
r = sd_bus_call_method(bus,
"org.freedesktop.resolve1",
@@ -2132,10 +2186,10 @@ static int verb_dns_over_tls(int argc, char **argv, void *userdata) {
"SetLinkDNSOverTLS",
&error,
NULL,
- "is", ifindex, argv[2]);
+ "is", arg_ifindex, argv[2]);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
@@ -2150,24 +2204,21 @@ static int verb_dns_over_tls(int argc, char **argv, void *userdata) {
static int verb_dnssec(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int ifindex, r;
+ int r;
assert(bus);
- if (argc <= 1)
- return status_all(bus, STATUS_DNSSEC);
-
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
}
- if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_DNSSEC, NULL);
+ if (arg_ifindex <= 0)
+ return status_all(bus, STATUS_DNSSEC);
+
+ if (argc < 3)
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNSSEC, NULL);
r = sd_bus_call_method(bus,
"org.freedesktop.resolve1",
@@ -2176,10 +2227,10 @@ static int verb_dnssec(int argc, char **argv, void *userdata) {
"SetLinkDNSSEC",
&error,
NULL,
- "is", ifindex, argv[2]);
+ "is", arg_ifindex, argv[2]);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
@@ -2195,34 +2246,38 @@ static int verb_nta(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 *req = NULL;
sd_bus *bus = userdata;
- int ifindex, i, r;
+ char **p;
+ int r;
+ bool clear;
assert(bus);
- if (argc <= 1)
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_ifindex <= 0)
return status_all(bus, STATUS_NTA);
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
+ if (argc < 3)
+ return status_ifindex(bus, arg_ifindex, NULL, STATUS_NTA, NULL);
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
- }
+ /* If only argument is the empty string, then call SetLinkDNSSECNegativeTrustAnchors()
+ * with an empty list, which will clear the list of domains for an interface. */
+ clear = strv_equal(argv + 2, STRV_MAKE(""));
- if (argc == 2)
- return status_ifindex(bus, ifindex, NULL, STATUS_NTA, NULL);
-
- for (i = 2; i < argc; i++) {
- r = dns_name_is_valid(argv[i]);
- if (r < 0)
- return log_error_errno(r, "Failed to validate specified domain %s: %m", argv[i]);
- if (r == 0) {
- log_error("Domain not valid: %s", argv[i]);
- return -EINVAL;
+ if (!clear)
+ STRV_FOREACH(p, argv + 2) {
+ r = dns_name_is_valid(*p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to validate specified domain %s: %m", *p);
+ if (r == 0) {
+ log_error("Domain not valid: %s", *p);
+ return -EINVAL;
+ }
}
- }
r = sd_bus_message_new_method_call(
bus,
@@ -2234,18 +2289,18 @@ static int verb_nta(int argc, char **argv, void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(req, "i", ifindex);
+ r = sd_bus_message_append(req, "i", arg_ifindex);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append_strv(req, argv + 2);
+ r = sd_bus_message_append_strv(req, clear ? NULL : argv + 2);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_call(bus, req, 0, &error, NULL);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY))
- return log_interface_is_managed(r, ifindex);
+ return log_interface_is_managed(r, arg_ifindex);
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
@@ -2260,19 +2315,19 @@ static int verb_nta(int argc, char **argv, void *userdata) {
static int verb_revert_link(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- int ifindex, r;
+ int r;
assert(bus);
- ifindex = parse_ifindex_with_warn(argv[1]);
- if (ifindex < 0)
- return ifindex;
-
- if (ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
}
+ if (arg_ifindex <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Interface argument required.");
+
r = sd_bus_call_method(bus,
"org.freedesktop.resolve1",
"/org/freedesktop/resolve1",
@@ -2280,7 +2335,7 @@ static int verb_revert_link(int argc, char **argv, void *userdata) {
"RevertLink",
&error,
NULL,
- "i", ifindex);
+ "i", arg_ifindex);
if (r < 0) {
if (arg_ifindex_permissive &&
sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
@@ -2312,7 +2367,14 @@ static void help_dns_classes(void) {
DUMP_STRING_TABLE(dns_class, int, _DNS_CLASS_MAX);
}
-static void compat_help(void) {
+static int compat_help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("resolvectl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%1$s [OPTIONS...] HOSTNAME|ADDRESS...\n"
"%1$s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n"
"%1$s [OPTIONS...] --openpgp EMAIL@DOMAIN...\n"
@@ -2353,10 +2415,22 @@ static void compat_help(void) {
" --set-dnssec=MODE Set per-interface DNSSEC mode\n"
" --set-nta=DOMAIN Set per-interface DNSSEC NTA\n"
" --revert Revert per-interface configuration\n"
- , program_invocation_short_name);
+ "\nSee the %2$s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
-static void native_help(void) {
+static int native_help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("resolvectl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%1$s [OPTIONS...] {COMMAND} ...\n"
"\n"
"Send control commands to the network name resolution manager, or\n"
@@ -2391,18 +2465,23 @@ static void native_help(void) {
" reset-server-features Forget learnt DNS server feature levels\n"
" dns [LINK [SERVER...]] Get/set per-interface DNS server address\n"
" domain [LINK [DOMAIN...]] Get/set per-interface search domain\n"
+ " default-route [LINK [BOOL]] Get/set per-interface default route flag\n"
" llmnr [LINK [MODE]] Get/set per-interface LLMNR mode\n"
" mdns [LINK [MODE]] Get/set per-interface MulticastDNS mode\n"
" dnsovertls [LINK [MODE]] Get/set per-interface DNS-over-TLS mode\n"
" dnssec [LINK [MODE]] Get/set per-interface DNSSEC mode\n"
" nta [LINK [DOMAIN...]] Get/set per-interface DNSSEC NTA\n"
" revert LINK Revert per-interface configuration\n"
- , program_invocation_short_name);
+ "\nSee the %2$s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int verb_help(int argc, char **argv, void *userdata) {
- native_help();
- return 0;
+ return native_help();
}
static int compat_parse_argv(int argc, char *argv[]) {
@@ -2475,8 +2554,7 @@ static int compat_parse_argv(int argc, char *argv[]) {
switch(c) {
case 'h':
- compat_help();
- return 0; /* done */;
+ return compat_help();
case ARG_VERSION:
return version();
@@ -2490,12 +2568,9 @@ static int compat_parse_argv(int argc, char *argv[]) {
break;
case 'i':
- r = parse_ifindex_with_warn(optarg);
+ r = ifname_mangle(optarg);
if (r < 0)
return r;
-
- arg_ifname = optarg;
- arg_ifindex = r;
break;
case 't':
@@ -2557,10 +2632,9 @@ static int compat_parse_argv(int argc, char *argv[]) {
arg_flags |= SD_RESOLVED_MDNS_IPV4;
else if (streq(optarg, "mdns-ipv6"))
arg_flags |= SD_RESOLVED_MDNS_IPV6;
- else {
- log_error("Unknown protocol specifier: %s", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown protocol specifier: %s", optarg);
break;
@@ -2576,26 +2650,24 @@ static int compat_parse_argv(int argc, char *argv[]) {
arg_mode = MODE_RESOLVE_TLSA;
if (!optarg || service_family_is_valid(optarg))
arg_service_family = optarg;
- else {
- log_error("Unknown service family \"%s\".", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown service family \"%s\".", optarg);
break;
case ARG_RAW:
- if (on_tty()) {
- log_error("Refusing to write binary data to tty.");
- return -ENOTTY;
- }
+ if (on_tty())
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
+ "Refusing to write binary data to tty.");
if (optarg == NULL || streq(optarg, "payload"))
arg_raw = RAW_PAYLOAD;
else if (streq(optarg, "packet"))
arg_raw = RAW_PACKET;
- else {
- log_error("Unknown --raw specifier \"%s\".", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown --raw specifier \"%s\".",
+ optarg);
arg_legend = false;
break;
@@ -2649,7 +2721,7 @@ static int compat_parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_SET_DNS:
@@ -2707,15 +2779,13 @@ static int compat_parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (arg_type == 0 && arg_class != 0) {
- log_error("--class= may only be used in conjunction with --type=.");
- return -EINVAL;
- }
+ if (arg_type == 0 && arg_class != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--class= may only be used in conjunction with --type=.");
- if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE) {
- log_error("--service and --type= may not be combined.");
- return -EINVAL;
- }
+ if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--service and --type= may not be combined.");
if (arg_type != 0 && arg_class == 0)
arg_class = DNS_CLASS_IN;
@@ -2725,15 +2795,9 @@ static int compat_parse_argv(int argc, char *argv[]) {
if (IN_SET(arg_mode, MODE_SET_LINK, MODE_REVERT_LINK)) {
- if (arg_ifindex <= 0) {
- log_error("--set-dns=, --set-domain=, --set-llmnr=, --set-mdns=, --set-dnsovertls=, --set-dnssec=, --set-nta= and --revert require --interface=.");
- return -EINVAL;
- }
-
- if (arg_ifindex == LOOPBACK_IFINDEX) {
- log_error("Interface can't be the loopback interface (lo). Sorry.");
- return -EINVAL;
- }
+ if (arg_ifindex <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--set-dns=, --set-domain=, --set-llmnr=, --set-mdns=, --set-dnsovertls=, --set-dnssec=, --set-nta= and --revert require --interface=.");
}
return 1 /* work to do */;
@@ -2777,8 +2841,7 @@ static int native_parse_argv(int argc, char *argv[]) {
switch(c) {
case 'h':
- native_help();
- return 0; /* done */;
+ return native_help();
case ARG_VERSION:
return version();
@@ -2792,11 +2855,9 @@ static int native_parse_argv(int argc, char *argv[]) {
break;
case 'i':
- r = parse_ifindex_with_warn(optarg);
+ r = ifname_mangle(optarg);
if (r < 0)
return r;
-
- arg_ifindex = r;
break;
case 't':
@@ -2857,27 +2918,26 @@ static int native_parse_argv(int argc, char *argv[]) {
arg_flags |= SD_RESOLVED_MDNS_IPV4;
else if (streq(optarg, "mdns-ipv6"))
arg_flags |= SD_RESOLVED_MDNS_IPV6;
- else {
- log_error("Unknown protocol specifier: %s", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown protocol specifier: %s",
+ optarg);
break;
case ARG_RAW:
- if (on_tty()) {
- log_error("Refusing to write binary data to tty.");
- return -ENOTTY;
- }
+ if (on_tty())
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
+ "Refusing to write binary data to tty.");
if (optarg == NULL || streq(optarg, "payload"))
arg_raw = RAW_PAYLOAD;
else if (streq(optarg, "packet"))
arg_raw = RAW_PACKET;
- else {
- log_error("Unknown --raw specifier \"%s\".", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown --raw specifier \"%s\".",
+ optarg);
arg_legend = false;
break;
@@ -2911,7 +2971,7 @@ static int native_parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case '?':
@@ -2921,10 +2981,9 @@ static int native_parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (arg_type == 0 && arg_class != 0) {
- log_error("--class= may only be used in conjunction with --type=.");
- return -EINVAL;
- }
+ if (arg_type == 0 && arg_class != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--class= may only be used in conjunction with --type=.");
if (arg_type != 0 && arg_class == 0)
arg_class = DNS_CLASS_IN;
@@ -2950,12 +3009,13 @@ static int native_main(int argc, char *argv[], sd_bus *bus) {
{ "reset-server-features", VERB_ANY, 1, 0, reset_server_features },
{ "dns", VERB_ANY, VERB_ANY, 0, verb_dns },
{ "domain", VERB_ANY, VERB_ANY, 0, verb_domain },
+ { "default-route", VERB_ANY, 3, 0, verb_default_route },
{ "llmnr", VERB_ANY, 3, 0, verb_llmnr },
{ "mdns", VERB_ANY, 3, 0, verb_mdns },
- { "dnsovertls", VERB_ANY, 3, 0, verb_dns_over_tls },
+ { "dnsovertls", VERB_ANY, 3, 0, verb_dns_over_tls },
{ "dnssec", VERB_ANY, 3, 0, verb_dnssec },
{ "nta", VERB_ANY, VERB_ANY, 0, verb_nta },
- { "revert", 2, 2, 0, verb_revert_link },
+ { "revert", VERB_ANY, 2, 0, verb_revert_link },
{}
};
@@ -3015,6 +3075,8 @@ static int compat_main(int argc, char *argv[], sd_bus *bus) {
return translate("status", NULL, argc - optind, argv + optind, bus);
case MODE_SET_LINK:
+ assert(arg_ifname);
+
if (arg_set_dns) {
r = translate("dns", arg_ifname, strv_length(arg_set_dns), arg_set_dns, bus);
if (r < 0)
@@ -3060,6 +3122,8 @@ static int compat_main(int argc, char *argv[], sd_bus *bus) {
return r;
case MODE_REVERT_LINK:
+ assert(arg_ifname);
+
return translate("revert", arg_ifname, 0, NULL, bus);
case _MODE_INVALID:
@@ -3069,8 +3133,8 @@ static int compat_main(int argc, char *argv[], sd_bus *bus) {
return 0;
}
-int main(int argc, char **argv) {
- sd_bus *bus = NULL;
+static int run(int argc, char **argv) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -3084,28 +3148,16 @@ int main(int argc, char **argv) {
else
r = native_parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = sd_bus_open_system(&bus);
- if (r < 0) {
- log_error_errno(r, "sd_bus_open_system: %m");
- goto finish;
- }
-
- if (streq(program_invocation_short_name, "systemd-resolve"))
- r = compat_main(argc, argv, bus);
- else
- r = native_main(argc, argv, bus);
-
-finish:
- /* make sure we terminate the bus connection first, and then close the
- * pager, see issue #3543 for the details. */
- sd_bus_flush_close_unref(bus);
- pager_close();
+ if (r < 0)
+ return log_error_errno(r, "sd_bus_open_system: %m");
- strv_free(arg_set_dns);
- strv_free(arg_set_domain);
- strv_free(arg_set_nta);
+ if (STR_IN_SET(program_invocation_short_name, "systemd-resolve", "resolvconf"))
+ return compat_main(argc, argv, bus);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return native_main(argc, argv, bus);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/resolve/resolvectl.h b/src/resolve/resolvectl.h
index 6ecaa4ff31..6b3a1f8445 100644
--- a/src/resolve/resolvectl.h
+++ b/src/resolve/resolvectl.h
@@ -1,13 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
#include <in-addr-util.h>
#include <stdbool.h>
#include <sys/types.h>
-extern int arg_ifindex;
-extern const char *arg_ifname;
-extern bool arg_ifindex_permissive;
-
typedef enum ExecutionMode {
MODE_RESOLVE_HOST,
MODE_RESOLVE_RECORD,
@@ -25,6 +22,8 @@ typedef enum ExecutionMode {
} ExecutionMode;
extern ExecutionMode arg_mode;
-
extern char **arg_set_dns;
extern char **arg_set_domain;
+extern bool arg_ifindex_permissive;
+
+int ifname_mangle(const char *s);
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index da0a909dd6..5b547ba7ee 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -4,6 +4,7 @@
#include "bus-common-errors.h"
#include "bus-util.h"
#include "dns-domain.h"
+#include "missing_capability.h"
#include "resolved-bus.h"
#include "resolved-def.h"
#include "resolved-dns-synthesize.h"
@@ -191,7 +192,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
/* The key names are not necessarily normalized, make sure that they are when we return them to our bus
* clients. */
- r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized);
+ r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized);
if (r < 0)
goto finish;
@@ -404,7 +405,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
if (r == 0)
continue;
- r = dns_name_normalize(rr->ptr.name, &normalized);
+ r = dns_name_normalize(rr->ptr.name, 0, &normalized);
if (r < 0)
goto finish;
@@ -742,7 +743,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
if (r < 0)
return r;
- r = dns_name_normalize(rr->srv.name, &normalized);
+ r = dns_name_normalize(rr->srv.name, 0, &normalized);
if (r < 0)
return r;
@@ -798,7 +799,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
if (canonical) {
normalized = mfree(normalized);
- r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized);
+ r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized);
if (r < 0)
return r;
}
@@ -1529,6 +1530,10 @@ static int bus_method_set_link_domains(sd_bus_message *message, void *userdata,
return call_link_method(userdata, message, bus_link_method_set_domains, error);
}
+static int bus_method_set_link_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_default_route, error);
+}
+
static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return call_link_method(userdata, message, bus_link_method_set_llmnr, error);
}
@@ -1854,6 +1859,7 @@ static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0),
SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, 0),
+ SD_BUS_METHOD("SetLinkDefaultRoute", "ib", NULL, bus_method_set_link_default_route, 0),
SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0),
SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0),
SD_BUS_METHOD("SetLinkDNSOverTLS", "is", NULL, bus_method_set_link_dns_over_tls, 0),
@@ -1920,7 +1926,7 @@ int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to register dnssd enumerator: %m");
- r = bus_request_name_async_may_reload_dbus(m->bus, NULL, "org.freedesktop.resolve1", 0, NULL);
+ r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.resolve1", 0, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to request name: %m");
@@ -1930,7 +1936,7 @@ int manager_connect_bus(Manager *m) {
r = sd_bus_match_signal_async(
m->bus,
- &m->prepare_for_sleep_slot,
+ NULL,
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h
index 0a0a110534..f6efa175fc 100644
--- a/src/resolve/resolved-conf.h
+++ b/src/resolve/resolved-conf.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "conf-parser.h"
typedef enum DnsStubListenerMode DnsStubListenerMode;
diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c
index 26caa63f94..d7252d3dac 100644
--- a/src/resolve/resolved-dns-answer.c
+++ b/src/resolve/resolved-dns-answer.c
@@ -19,15 +19,6 @@ DnsAnswer *dns_answer_new(size_t n) {
return a;
}
-DnsAnswer *dns_answer_ref(DnsAnswer *a) {
- if (!a)
- return NULL;
-
- assert(a->n_ref > 0);
- a->n_ref++;
- return a;
-}
-
static void dns_answer_flush(DnsAnswer *a) {
DnsResourceRecord *rr;
@@ -40,21 +31,15 @@ static void dns_answer_flush(DnsAnswer *a) {
a->n_rrs = 0;
}
-DnsAnswer *dns_answer_unref(DnsAnswer *a) {
- if (!a)
- return NULL;
-
- assert(a->n_ref > 0);
-
- if (a->n_ref == 1) {
- dns_answer_flush(a);
- free(a);
- } else
- a->n_ref--;
+static DnsAnswer *dns_answer_free(DnsAnswer *a) {
+ assert(a);
- return NULL;
+ dns_answer_flush(a);
+ return mfree(a);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsAnswer, dns_answer, dns_answer_free);
+
static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
assert(rr);
@@ -226,70 +211,6 @@ int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags
return found;
}
-int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
- DnsAnswerFlags flags = 0, i_flags;
- DnsResourceRecord *i;
- bool found = false;
- int r;
-
- assert(rr);
-
- DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
- r = dns_resource_record_equal(i, rr);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (!ret_flags)
- return 1;
-
- if (found)
- flags &= i_flags;
- else {
- flags = i_flags;
- found = true;
- }
- }
-
- if (ret_flags)
- *ret_flags = flags;
-
- return found;
-}
-
-int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
- DnsAnswerFlags flags = 0, i_flags;
- DnsResourceRecord *i;
- bool found = false;
- int r;
-
- assert(key);
-
- DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
- r = dns_resource_key_equal(i->key, key);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (!ret_flags)
- return true;
-
- if (found)
- flags &= i_flags;
- else {
- flags = i_flags;
- found = true;
- }
- }
-
- if (ret_flags)
- *ret_flags = flags;
-
- return found;
-}
-
int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
DnsResourceRecord *i;
@@ -799,7 +720,7 @@ void dns_answer_dump(DnsAnswer *answer, FILE *f) {
}
}
-bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
+int dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
DnsResourceRecord *rr;
int r;
@@ -830,7 +751,6 @@ bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
return r;
if (r > 0)
return 1;
-
}
return 0;
diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h
index aff594a00e..97514c3408 100644
--- a/src/resolve/resolved-dns-answer.h
+++ b/src/resolve/resolved-dns-answer.h
@@ -43,8 +43,6 @@ int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, Dns
int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex);
int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
-int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *combined_flags);
-int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a);
int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone);
@@ -65,7 +63,7 @@ int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr);
int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags);
int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags);
-bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname);
+int dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname);
static inline size_t dns_answer_size(DnsAnswer *a) {
return a ? a->n_rrs : 0;
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 23cd662fb8..99dec28a46 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -228,11 +228,7 @@ void dns_cache_prune(DnsCache *c) {
static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
const DnsCacheItem *x = a, *y = b;
- if (x->until < y->until)
- return -1;
- if (x->until > y->until)
- return 1;
- return 0;
+ return CMP(x->until, y->until);
}
static int dns_cache_init(DnsCache *c) {
@@ -680,13 +676,8 @@ int dns_cache_put(
/* Second, add in positive entries for all contained RRs */
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
- if ((flags & DNS_ANSWER_CACHEABLE) == 0)
- continue;
-
- r = rr_eligible(rr);
- if (r < 0)
- return r;
- if (r == 0)
+ if ((flags & DNS_ANSWER_CACHEABLE) == 0 ||
+ !rr_eligible(rr))
continue;
r = dns_cache_put_positive(
@@ -792,7 +783,7 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
if (dns_type_may_redirect(k->type)) {
/* Check if we have a CNAME record instead */
i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
- if (i)
+ if (i && i->type != DNS_CACHE_NODATA)
return i;
/* OK, let's look for cached DNAME records. */
@@ -801,7 +792,7 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
return NULL;
i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
- if (i)
+ if (i && i->type != DNS_CACHE_NODATA)
return i;
/* Jump one label ahead */
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 0a6f482cc1..335fd47780 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -74,7 +74,7 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
return -ENOBUFS;
for (;;) {
- r = dns_label_unescape(&n, buffer, buffer_max);
+ r = dns_label_unescape(&n, buffer, buffer_max, 0);
if (r < 0)
return r;
if (r == 0)
@@ -114,32 +114,25 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
#if HAVE_GCRYPT
-static int rr_compare(const void *a, const void *b) {
- DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
+static int rr_compare(DnsResourceRecord * const *a, DnsResourceRecord * const *b) {
+ const DnsResourceRecord *x = *a, *y = *b;
size_t m;
int r;
/* Let's order the RRs according to RFC 4034, Section 6.3 */
assert(x);
- assert(*x);
- assert((*x)->wire_format);
+ assert(x->wire_format);
assert(y);
- assert(*y);
- assert((*y)->wire_format);
+ assert(y->wire_format);
- m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y));
+ m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(x), DNS_RESOURCE_RECORD_RDATA_SIZE(y));
- r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m);
+ r = memcmp(DNS_RESOURCE_RECORD_RDATA(x), DNS_RESOURCE_RECORD_RDATA(y), m);
if (r != 0)
return r;
- if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
- return -1;
- else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
- return 1;
-
- return 0;
+ return CMP(DNS_RESOURCE_RECORD_RDATA_SIZE(x), DNS_RESOURCE_RECORD_RDATA_SIZE(y));
}
static int dnssec_rsa_verify_raw(
@@ -806,12 +799,12 @@ int dnssec_verify_rrset(
return -ENODATA;
/* Bring the RRs into canonical order */
- qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
+ typesafe_qsort(list, n, rr_compare);
f = open_memstream(&sig_data, &sig_size);
if (!f)
return -ENOMEM;
- __fsetlocking(f, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
fwrite_uint16(f, rrsig->rrsig.type_covered);
fwrite_uint8(f, rrsig->rrsig.algorithm);
@@ -1279,10 +1272,10 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
if (nsec3->key->type != DNS_TYPE_NSEC3)
return -EINVAL;
- if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
- log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
- return -EOPNOTSUPP;
- }
+ if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX)
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Ignoring NSEC3 RR %s with excessive number of iterations.",
+ dns_resource_record_to_string(nsec3));
algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
if (algorithm < 0)
@@ -1380,22 +1373,18 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
return 0;
if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
return 0;
- if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
+ if (memcmp_safe(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
return 0;
a = dns_resource_key_name(rr->key);
r = dns_name_parent(&a); /* strip off hash */
- if (r < 0)
+ if (r <= 0)
return r;
- if (r == 0)
- return 0;
b = dns_resource_key_name(nsec3->key);
r = dns_name_parent(&b); /* strip off hash */
- if (r < 0)
+ if (r <= 0)
return r;
- if (r == 0)
- return 0;
/* Make sure both have the same parent */
return dns_name_equal(a, b);
@@ -1716,7 +1705,7 @@ static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) {
return 0;
n = dns_resource_key_name(rr->key);
- r = dns_label_unescape(&n, label, sizeof(label));
+ r = dns_label_unescape(&n, label, sizeof label, 0);
if (r <= 0)
return r;
if (r != 1 || label[0] != '*')
@@ -1838,13 +1827,13 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name)
return r;
if (r > 0) /* If the name we are interested in is a child of the NSEC RR, then append the asterisk to the NSEC
* RR's name. */
- r = dns_name_concat("*", dns_resource_key_name(rr->key), &wc);
+ r = dns_name_concat("*", dns_resource_key_name(rr->key), 0, &wc);
else {
r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
if (r < 0)
return r;
- r = dns_name_concat("*", common_suffix, &wc);
+ r = dns_name_concat("*", common_suffix, 0, &wc);
}
if (r < 0)
return r;
@@ -2103,10 +2092,8 @@ static int dnssec_test_positive_wildcard_nsec3(
for (;;) {
next_closer = name;
r = dns_name_parent(&name);
- if (r < 0)
+ if (r <= 0)
return r;
- if (r == 0)
- return 0;
r = dns_name_equal(name, source);
if (r < 0)
@@ -2143,7 +2130,6 @@ static int dnssec_test_positive_wildcard_nsec(
* 3) b.c.d.e.f
* 4) *.c.d.e.f
* 5) c.d.e.f
- *
*/
for (;;) {
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 5266014581..572271be95 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- ***/
#if HAVE_GCRYPT
#include <gcrypt.h>
@@ -50,10 +48,10 @@ int dns_packet_new(
/* The caller may not check what is going to be truly allocated, so do not allow to
* allocate a DNS packet bigger than DNS_PACKET_SIZE_MAX.
*/
- if (min_alloc_dsize > DNS_PACKET_SIZE_MAX) {
- log_error("Requested packet data size too big: %zu", min_alloc_dsize);
- return -EFBIG;
- }
+ if (min_alloc_dsize > DNS_PACKET_SIZE_MAX)
+ return log_error_errno(SYNTHETIC_ERRNO(EFBIG),
+ "Requested packet data size too big: %zu",
+ min_alloc_dsize);
/* When dns_packet_new() is called with min_alloc_dsize == 0, allocate more than the
* absolute minimum (which is the dns packet header size), to avoid
@@ -385,7 +383,7 @@ int dns_packet_append_blob(DnsPacket *p, const void *d, size_t l, size_t *start)
if (r < 0)
return r;
- memcpy(q, d, l);
+ memcpy_safe(q, d, l);
return 0;
}
@@ -537,7 +535,7 @@ int dns_packet_append_name(
}
}
- r = dns_label_unescape(&name, label, sizeof(label));
+ r = dns_label_unescape(&name, label, sizeof label, 0);
if (r < 0)
goto fail;
@@ -854,7 +852,9 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
if (r < 0)
goto fail;
- r = dns_packet_append_name(p, rr->srv.name, true, false, NULL);
+ /* RFC 2782 states "Unless and until permitted by future standards
+ * action, name compression is not to be used for this field." */
+ r = dns_packet_append_name(p, rr->srv.name, false, false, NULL);
break;
case DNS_TYPE_PTR:
@@ -2330,30 +2330,24 @@ int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) {
return dns_resource_key_equal(p->question->keys[0], key);
}
-static void dns_packet_hash_func(const void *p, struct siphash *state) {
- const DnsPacket *s = p;
-
+static void dns_packet_hash_func(const DnsPacket *s, struct siphash *state) {
assert(s);
siphash24_compress(&s->size, sizeof(s->size), state);
siphash24_compress(DNS_PACKET_DATA((DnsPacket*) s), s->size, state);
}
-static int dns_packet_compare_func(const void *a, const void *b) {
- const DnsPacket *x = a, *y = b;
+static int dns_packet_compare_func(const DnsPacket *x, const DnsPacket *y) {
+ int r;
- if (x->size < y->size)
- return -1;
- if (x->size > y->size)
- return 1;
+ r = CMP(x->size, y->size);
+ if (r != 0)
+ return r;
return memcmp(DNS_PACKET_DATA((DnsPacket*) x), DNS_PACKET_DATA((DnsPacket*) y), x->size);
}
-const struct hash_ops dns_packet_hash_ops = {
- .hash = dns_packet_hash_func,
- .compare = dns_packet_compare_func
-};
+DEFINE_HASH_OPS(dns_packet_hash_ops, DnsPacket, dns_packet_hash_func, dns_packet_compare_func);
static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
[DNS_RCODE_SUCCESS] = "SUCCESS",
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 8e9d74a71a..008860b5d1 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- ***/
-
#include <netinet/ip.h>
#include <netinet/udp.h>
@@ -56,7 +53,7 @@ struct DnsPacketHeader {
#define DNS_PACKET_UNICAST_SIZE_LARGE_MAX 4096u
struct DnsPacket {
- int n_ref;
+ unsigned n_ref;
DnsProtocol protocol;
size_t size, allocated, rindex, max_size;
void *_data; /* don't access directly, use DNS_PACKET_DATA()! */
@@ -120,11 +117,14 @@ static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) {
static inline uint16_t DNS_PACKET_PAYLOAD_SIZE_MAX(DnsPacket *p) {
- /* Returns the advertised maximum datagram size for replies, or the DNS default if there's nothing defined. */
+ /* Returns the advertised maximum size for replies, or the DNS default if there's nothing defined. */
if (p->opt)
return MAX(DNS_PACKET_UNICAST_SIZE_MAX, p->opt->key->class);
+ if (p->ipproto == IPPROTO_TCP)
+ return DNS_PACKET_SIZE_MAX;
+
return DNS_PACKET_UNICAST_SIZE_MAX;
}
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index c921fe841f..7a4f97754b 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -554,17 +554,13 @@ static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
return r;
/* If this a single-label domain on DNS, we might append a suitable search domain first. */
- if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0) {
- r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna));
- if (r < 0)
- goto fail;
- if (r > 0) {
- /* OK, we need a search domain now. Let's find one for this scope */
+ if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0 &&
+ dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna))) {
+ /* OK, we need a search domain now. Let's find one for this scope */
- r = dns_query_candidate_next_search_domain(c);
- if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
- goto fail;
- }
+ r = dns_query_candidate_next_search_domain(c);
+ if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
+ goto fail;
}
r = dns_query_candidate_setup_transactions(c);
@@ -686,22 +682,15 @@ int dns_query_go(DnsQuery *q) {
continue;
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
- if (match < 0)
- return match;
-
- if (match == DNS_SCOPE_NO)
+ if (match < 0) {
+ log_debug("Couldn't check if '%s' matches against scope, ignoring.", name);
continue;
+ }
- found = match;
-
- if (match == DNS_SCOPE_YES) {
+ if (match > found) { /* Does this match better? If so, remember how well it matched, and the first one
+ * that matches this well */
+ found = match;
first = s;
- break;
- } else {
- assert(match == DNS_SCOPE_MAYBE);
-
- if (!first)
- first = s;
}
}
@@ -729,10 +718,12 @@ int dns_query_go(DnsQuery *q) {
continue;
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
- if (match < 0)
- goto fail;
+ if (match < 0) {
+ log_debug("Couldn't check if '%s' matches agains scope, ignoring.", name);
+ continue;
+ }
- if (match != found)
+ if (match < found)
continue;
r = dns_query_add_candidate(q, s);
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index 68fb3b9eac..1ed9171564 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -20,33 +20,18 @@ DnsQuestion *dns_question_new(size_t n) {
return q;
}
-DnsQuestion *dns_question_ref(DnsQuestion *q) {
- if (!q)
- return NULL;
-
- assert(q->n_ref > 0);
- q->n_ref++;
- return q;
-}
-
-DnsQuestion *dns_question_unref(DnsQuestion *q) {
- if (!q)
- return NULL;
-
- assert(q->n_ref > 0);
-
- if (q->n_ref == 1) {
- size_t i;
+static DnsQuestion *dns_question_free(DnsQuestion *q) {
+ size_t i;
- for (i = 0; i < q->n_keys; i++)
- dns_resource_key_unref(q->keys[i]);
- free(q);
- } else
- q->n_ref--;
+ assert(q);
- return NULL;
+ for (i = 0; i < q->n_keys; i++)
+ dns_resource_key_unref(q->keys[i]);
+ return mfree(q);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsQuestion, dns_question, dns_question_free);
+
int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
size_t i;
int r;
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index cfd0ed214d..a1dffb08a3 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -77,7 +77,7 @@ int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key
return 0;
}
- r = dns_name_concat(dns_resource_key_name(key), name, &joined);
+ r = dns_name_concat(dns_resource_key_name(key), name, 0, &joined);
if (r < 0)
return r;
@@ -222,7 +222,7 @@ int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr,
if (search_domain) {
_cleanup_free_ char *joined = NULL;
- r = dns_name_concat(dns_resource_key_name(key), search_domain, &joined);
+ r = dns_name_concat(dns_resource_key_name(key), search_domain, 0, &joined);
if (r < 0)
return r;
@@ -254,7 +254,7 @@ int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsRe
if (search_domain) {
_cleanup_free_ char *joined = NULL;
- r = dns_name_concat(dns_resource_key_name(key), search_domain, &joined);
+ r = dns_name_concat(dns_resource_key_name(key), search_domain, 0, &joined);
if (r < 0)
return r;
@@ -282,9 +282,7 @@ int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *
return dns_name_endswith(dns_resource_key_name(key), dns_resource_key_name(soa));
}
-static void dns_resource_key_hash_func(const void *i, struct siphash *state) {
- const DnsResourceKey *k = i;
-
+static void dns_resource_key_hash_func(const DnsResourceKey *k, struct siphash *state) {
assert(k);
dns_name_hash_func(dns_resource_key_name(k), state);
@@ -292,31 +290,25 @@ static void dns_resource_key_hash_func(const void *i, struct siphash *state) {
siphash24_compress(&k->type, sizeof(k->type), state);
}
-static int dns_resource_key_compare_func(const void *a, const void *b) {
- const DnsResourceKey *x = a, *y = b;
+static int dns_resource_key_compare_func(const DnsResourceKey *x, const DnsResourceKey *y) {
int ret;
ret = dns_name_compare_func(dns_resource_key_name(x), dns_resource_key_name(y));
if (ret != 0)
return ret;
- if (x->type < y->type)
- return -1;
- if (x->type > y->type)
- return 1;
+ ret = CMP(x->type, y->type);
+ if (ret != 0)
+ return ret;
- if (x->class < y->class)
- return -1;
- if (x->class > y->class)
- return 1;
+ ret = CMP(x->class, y->class);
+ if (ret != 0)
+ return ret;
return 0;
}
-const struct hash_ops dns_resource_key_hash_ops = {
- .hash = dns_resource_key_hash_func,
- .compare = dns_resource_key_compare_func
-};
+DEFINE_HASH_OPS(dns_resource_key_hash_ops, DnsResourceKey, dns_resource_key_hash_func, dns_resource_key_compare_func);
char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size) {
const char *c, *t;
@@ -401,26 +393,8 @@ DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, c
return dns_resource_record_new(key);
}
-DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
- if (!rr)
- return NULL;
-
- assert(rr->n_ref > 0);
- rr->n_ref++;
-
- return rr;
-}
-
-DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
- if (!rr)
- return NULL;
-
- assert(rr->n_ref > 0);
-
- if (rr->n_ref > 1) {
- rr->n_ref--;
- return NULL;
- }
+static DnsResourceRecord* dns_resource_record_free(DnsResourceRecord *rr) {
+ assert(rr);
if (rr->key) {
switch(rr->key->type) {
@@ -514,6 +488,8 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
return mfree(rr);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsResourceRecord, dns_resource_record, dns_resource_record_free);
+
int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
@@ -1361,9 +1337,7 @@ int dns_resource_record_is_synthetic(DnsResourceRecord *rr) {
return !r;
}
-void dns_resource_record_hash_func(const void *i, struct siphash *state) {
- const DnsResourceRecord *rr = i;
-
+void dns_resource_record_hash_func(const DnsResourceRecord *rr, struct siphash *state) {
assert(rr);
dns_resource_key_hash_func(rr->key, state);
@@ -1504,27 +1478,22 @@ void dns_resource_record_hash_func(const void *i, struct siphash *state) {
}
}
-static int dns_resource_record_compare_func(const void *a, const void *b) {
- const DnsResourceRecord *x = a, *y = b;
- int ret;
+static int dns_resource_record_compare_func(const DnsResourceRecord *x, const DnsResourceRecord *y) {
+ int r;
- ret = dns_resource_key_compare_func(x->key, y->key);
- if (ret != 0)
- return ret;
+ r = dns_resource_key_compare_func(x->key, y->key);
+ if (r != 0)
+ return r;
if (dns_resource_record_equal(x, y))
return 0;
- /* This is a bit dirty, we don't implement proper ordering, but
- * the hashtable doesn't need ordering anyway, hence we don't
- * care. */
- return x < y ? -1 : 1;
+ /* We still use CMP() here, even though don't implement proper
+ * ordering, since the hashtable doesn't need ordering anyway. */
+ return CMP(x, y);
}
-const struct hash_ops dns_resource_record_hash_ops = {
- .hash = dns_resource_record_hash_func,
- .compare = dns_resource_record_compare_func,
-};
+DEFINE_HASH_OPS(dns_resource_record_hash_ops, DnsResourceRecord, dns_resource_record_hash_func, dns_resource_record_compare_func);
DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *copy = NULL;
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index 2f08b6119e..e02fa2d53b 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- ***/
-
#include <netinet/in.h>
#include "bitmap.h"
@@ -247,7 +244,7 @@ struct DnsResourceRecord {
};
};
-static inline const void* DNS_RESOURCE_RECORD_RDATA(DnsResourceRecord *rr) {
+static inline const void* DNS_RESOURCE_RECORD_RDATA(const DnsResourceRecord *rr) {
if (!rr)
return NULL;
@@ -258,7 +255,7 @@ static inline const void* DNS_RESOURCE_RECORD_RDATA(DnsResourceRecord *rr) {
return (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset;
}
-static inline size_t DNS_RESOURCE_RECORD_RDATA_SIZE(DnsResourceRecord *rr) {
+static inline size_t DNS_RESOURCE_RECORD_RDATA_SIZE(const DnsResourceRecord *rr) {
if (!rr)
return 0;
if (!rr->wire_format)
@@ -268,7 +265,7 @@ static inline size_t DNS_RESOURCE_RECORD_RDATA_SIZE(DnsResourceRecord *rr) {
return rr->wire_format_size - rr->wire_format_rdata_offset;
}
-static inline uint8_t DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(DnsResourceRecord *rr) {
+static inline uint8_t DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(const DnsResourceRecord *rr) {
assert(rr);
assert(rr->key->type == DNS_TYPE_OPT);
@@ -329,7 +326,7 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i);
int dns_txt_item_new_empty(DnsTxtItem **ret);
-void dns_resource_record_hash_func(const void *i, struct siphash *state);
+void dns_resource_record_hash_func(const DnsResourceRecord *i, struct siphash *state);
extern const struct hash_ops dns_resource_key_hash_ops;
extern const struct hash_ops dns_resource_record_hash_ops;
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 38ea7fea0a..972e661d72 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -30,15 +30,17 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
assert(m);
assert(ret);
- s = new0(DnsScope, 1);
+ s = new(DnsScope, 1);
if (!s)
return -ENOMEM;
- s->manager = m;
- s->link = l;
- s->protocol = protocol;
- s->family = family;
- s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC;
+ *s = (DnsScope) {
+ .manager = m,
+ .link = l,
+ .protocol = protocol,
+ .family = family,
+ .resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC,
+ };
if (protocol == DNS_PROTOCOL_DNS) {
/* Copy DNSSEC mode from the link if it is set there,
@@ -313,7 +315,6 @@ static int dns_scope_socket(
_cleanup_close_ int fd = -1;
union sockaddr_union sa;
socklen_t salen;
- static const int one = 1;
int r, ifindex;
assert(s);
@@ -324,36 +325,54 @@ static int dns_scope_socket(
ifindex = dns_server_ifindex(server);
- sa.sa.sa_family = server->family;
- if (server->family == AF_INET) {
- sa.in.sin_port = htobe16(port);
- sa.in.sin_addr = server->address.in;
+ switch (server->family) {
+ case AF_INET:
+ sa = (union sockaddr_union) {
+ .in.sin_family = server->family,
+ .in.sin_port = htobe16(port),
+ .in.sin_addr = server->address.in,
+ };
salen = sizeof(sa.in);
- } else if (server->family == AF_INET6) {
- sa.in6.sin6_port = htobe16(port);
- sa.in6.sin6_addr = server->address.in6;
- sa.in6.sin6_scope_id = ifindex;
+ break;
+ case AF_INET6:
+ sa = (union sockaddr_union) {
+ .in6.sin6_family = server->family,
+ .in6.sin6_port = htobe16(port),
+ .in6.sin6_addr = server->address.in6,
+ .in6.sin6_scope_id = ifindex,
+ };
salen = sizeof(sa.in6);
- } else
+ break;
+ default:
return -EAFNOSUPPORT;
+ }
} else {
assert(family != AF_UNSPEC);
assert(address);
- sa.sa.sa_family = family;
ifindex = s->link ? s->link->ifindex : 0;
- if (family == AF_INET) {
- sa.in.sin_port = htobe16(port);
- sa.in.sin_addr = address->in;
+ switch (family) {
+ case AF_INET:
+ sa = (union sockaddr_union) {
+ .in.sin_family = family,
+ .in.sin_port = htobe16(port),
+ .in.sin_addr = address->in,
+ };
salen = sizeof(sa.in);
- } else if (family == AF_INET6) {
- sa.in6.sin6_port = htobe16(port);
- sa.in6.sin6_addr = address->in6;
- sa.in6.sin6_scope_id = ifindex;
+ break;
+ case AF_INET6:
+ sa = (union sockaddr_union) {
+ .in6.sin6_family = family,
+ .in6.sin6_port = htobe16(port),
+ .in6.sin6_addr = address->in6,
+ .in6.sin6_scope_id = ifindex,
+ };
salen = sizeof(sa.in6);
- } else
+ break;
+ default:
return -EAFNOSUPPORT;
+ }
}
fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
@@ -361,9 +380,9 @@ static int dns_scope_socket(
return -errno;
if (type == SOCK_STREAM) {
- r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+ r = setsockopt_int(fd, IPPROTO_TCP, TCP_NODELAY, true);
if (r < 0)
- return -errno;
+ return r;
}
if (s->link) {
@@ -384,13 +403,36 @@ static int dns_scope_socket(
/* RFC 4795, section 2.5 requires the TTL to be set to 1 */
if (sa.sa.sa_family == AF_INET) {
- r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
+ r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, true);
if (r < 0)
- return -errno;
+ return r;
} else if (sa.sa.sa_family == AF_INET6) {
- r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
+ r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, true);
if (r < 0)
- return -errno;
+ return r;
+ }
+ }
+
+ if (type == SOCK_DGRAM) {
+ /* Set IP_RECVERR or IPV6_RECVERR to get ICMP error feedback. See discussion in #10345. */
+
+ if (sa.sa.sa_family == AF_INET) {
+ r = setsockopt_int(fd, IPPROTO_IP, IP_RECVERR, true);
+ if (r < 0)
+ return r;
+
+ r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
+ if (r < 0)
+ return r;
+
+ } else if (sa.sa.sa_family == AF_INET6) {
+ r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVERR, true);
+ if (r < 0)
+ return r;
+
+ r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
+ if (r < 0)
+ return r;
}
}
@@ -417,9 +459,40 @@ int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *add
return dns_scope_socket(s, SOCK_STREAM, family, address, server, port, ret_socket_address);
}
-DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
+static DnsScopeMatch accept_link_local_reverse_lookups(const char *domain) {
+ assert(domain);
+
+ if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0)
+ return DNS_SCOPE_YES_BASE + 4; /* 4 labels match */
+
+ if (dns_name_endswith(domain, "8.e.f.ip6.arpa") > 0 ||
+ dns_name_endswith(domain, "9.e.f.ip6.arpa") > 0 ||
+ dns_name_endswith(domain, "a.e.f.ip6.arpa") > 0 ||
+ dns_name_endswith(domain, "b.e.f.ip6.arpa") > 0)
+ return DNS_SCOPE_YES_BASE + 5; /* 5 labels match */
+
+ return _DNS_SCOPE_MATCH_INVALID;
+}
+
+DnsScopeMatch dns_scope_good_domain(
+ DnsScope *s,
+ int ifindex,
+ uint64_t flags,
+ const char *domain) {
+
DnsSearchDomain *d;
+ /* This returns the following return values:
+ *
+ * DNS_SCOPE_NO → This scope is not suitable for lookups of this domain, at all
+ * DNS_SCOPE_MAYBE → This scope is suitable, but only if nothing else wants it
+ * DNS_SCOPE_YES_BASE+n → This scope is suitable, and 'n' suffix labels match
+ *
+ * (The idea is that the caller will only use the scopes with the longest 'n' returned. If no scopes return
+ * DNS_SCOPE_YES_BASE+n, then it should use those which returned DNS_SCOPE_MAYBE. It should never use those
+ * which returned DNS_SCOPE_NO.)
+ */
+
assert(s);
assert(domain);
@@ -454,23 +527,35 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
switch (s->protocol) {
case DNS_PROTOCOL_DNS: {
- DnsServer *dns_server;
+ int n_best = -1;
/* Never route things to scopes that lack DNS servers */
- dns_server = dns_scope_get_dns_server(s);
- if (!dns_server)
+ if (!dns_scope_get_dns_server(s))
return DNS_SCOPE_NO;
/* Always honour search domains for routing queries, except if this scope lacks DNS servers. Note that
* we return DNS_SCOPE_YES here, rather than just DNS_SCOPE_MAYBE, which means other wildcard scopes
* won't be considered anymore. */
LIST_FOREACH(domains, d, dns_scope_get_search_domains(s))
- if (dns_name_endswith(domain, d->name) > 0)
- return DNS_SCOPE_YES;
+ if (dns_name_endswith(domain, d->name) > 0) {
+ int c;
+
+ c = dns_name_count_labels(d->name);
+ if (c < 0)
+ continue;
- /* If the DNS server has route-only domains, don't send other requests to it. This would be a privacy
- * violation, will most probably fail anyway, and adds unnecessary load. */
- if (dns_server_limited_domains(dns_server))
+ if (c > n_best)
+ n_best = c;
+ }
+
+ /* Let's return the number of labels in the best matching result */
+ if (n_best >= 0) {
+ assert(n_best <= DNS_SCOPE_YES_END - DNS_SCOPE_YES_BASE);
+ return DNS_SCOPE_YES_BASE + n_best;
+ }
+
+ /* See if this scope is suitable as default route. */
+ if (!dns_scope_is_default_route(s))
return DNS_SCOPE_NO;
/* Exclude link-local IP ranges */
@@ -488,25 +573,48 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
return DNS_SCOPE_NO;
}
- case DNS_PROTOCOL_MDNS:
+ case DNS_PROTOCOL_MDNS: {
+ DnsScopeMatch m;
+
+ m = accept_link_local_reverse_lookups(domain);
+ if (m >= 0)
+ return m;
+
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
- (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
- (dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */
+ (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0))
+ return DNS_SCOPE_MAYBE;
+
+ if ((dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */
dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */
manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via mDNS */
- return DNS_SCOPE_MAYBE;
+ return DNS_SCOPE_YES_BASE + 1; /* Return +1, as the top-level .local domain matches, i.e. one label */
return DNS_SCOPE_NO;
+ }
+
+ case DNS_PROTOCOL_LLMNR: {
+ DnsScopeMatch m;
+
+ m = accept_link_local_reverse_lookups(domain);
+ if (m >= 0)
+ return m;
- case DNS_PROTOCOL_LLMNR:
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
- (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
- (dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
+ (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0))
+ return DNS_SCOPE_MAYBE;
+
+ if ((dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
!is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
- return DNS_SCOPE_MAYBE;
+ return DNS_SCOPE_YES_BASE + 1; /* Return +1, as we consider ourselves authoritative for
+ * single-label names, i.e. one label. This is particular
+ * relevant as it means a "." route on some other scope won't
+ * pull all traffic away from us. (If people actually want to
+ * pull traffic away from us they should turn off LLMNR on the
+ * link) */
return DNS_SCOPE_NO;
+ }
default:
assert_not_reached("Unknown scope protocol");
@@ -1282,3 +1390,56 @@ int dns_scope_remove_dnssd_services(DnsScope *scope) {
return 0;
}
+
+static bool dns_scope_has_route_only_domains(DnsScope *scope) {
+ DnsSearchDomain *domain, *first;
+ bool route_only = false;
+
+ assert(scope);
+ assert(scope->protocol == DNS_PROTOCOL_DNS);
+
+ /* Returns 'true' if this scope is suitable for queries to specific domains only. For that we check
+ * if there are any route-only domains on this interface, as a heuristic to discern VPN-style links
+ * from non-VPN-style links. Returns 'false' for all other cases, i.e. if the scope is intended to
+ * take queries to arbitrary domains, i.e. has no routing domains set. */
+
+ if (scope->link)
+ first = scope->link->search_domains;
+ else
+ first = scope->manager->search_domains;
+
+ LIST_FOREACH(domains, domain, first) {
+ /* "." means "any domain", thus the interface takes any kind of traffic. Thus, we exit early
+ * here, as it doesn't really matter whether this link has any route-only domains or not,
+ * "~." really trumps everything and clearly indicates that this interface shall receive all
+ * traffic it can get. */
+ if (dns_name_is_root(DNS_SEARCH_DOMAIN_NAME(domain)))
+ return false;
+
+ if (domain->route_only)
+ route_only = true;
+ }
+
+ return route_only;
+}
+
+bool dns_scope_is_default_route(DnsScope *scope) {
+ assert(scope);
+
+ /* Only use DNS scopes as default routes */
+ if (scope->protocol != DNS_PROTOCOL_DNS)
+ return false;
+
+ /* The global DNS scope is always suitable as default route */
+ if (!scope->link)
+ return true;
+
+ /* Honour whatever is explicitly configured. This is really the best approach, and trumps any
+ * automatic logic. */
+ if (scope->link->default_route >= 0)
+ return scope->link->default_route;
+
+ /* Otherwise check if we have any route-only domains, as a sensible heuristic: if so, let's not
+ * volunteer as default route. */
+ return !dns_scope_has_route_only_domains(scope);
+}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index 04e93f8f73..f4b45c4f20 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -18,9 +18,10 @@ typedef struct DnsScope DnsScope;
typedef enum DnsScopeMatch {
DNS_SCOPE_NO,
DNS_SCOPE_MAYBE,
- DNS_SCOPE_YES,
+ DNS_SCOPE_YES_BASE, /* Add the number of matching labels to this */
+ DNS_SCOPE_YES_END = DNS_SCOPE_YES_BASE + DNS_N_LABELS_MAX,
_DNS_SCOPE_MATCH_MAX,
- _DNS_SCOPE_INVALID = -1
+ _DNS_SCOPE_MATCH_INVALID = -1
} DnsScopeMatch;
struct DnsScope {
@@ -28,6 +29,8 @@ struct DnsScope {
DnsProtocol protocol;
int family;
+
+ /* Copied at scope creation time from the link/manager */
DnssecMode dnssec_mode;
DnsOverTlsMode dns_over_tls_mode;
@@ -104,5 +107,6 @@ int dns_scope_ifindex(DnsScope *s);
int dns_scope_announce(DnsScope *scope, bool goodbye);
int dns_scope_add_dnssd_services(DnsScope *scope);
-
int dns_scope_remove_dnssd_services(DnsScope *scope);
+
+bool dns_scope_is_default_route(DnsScope *scope);
diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c
index c3817acf11..21c2442c51 100644
--- a/src/resolve/resolved-dns-search-domain.c
+++ b/src/resolve/resolved-dns-search-domain.c
@@ -19,7 +19,7 @@ int dns_search_domain_new(
assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l);
assert(name);
- r = dns_name_normalize(name, &normalized);
+ r = dns_name_normalize(name, 0, &normalized);
if (r < 0)
return r;
@@ -65,30 +65,15 @@ int dns_search_domain_new(
return 0;
}
-DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d) {
- if (!d)
- return NULL;
-
- assert(d->n_ref > 0);
- d->n_ref++;
-
- return d;
-}
-
-DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d) {
- if (!d)
- return NULL;
-
- assert(d->n_ref > 0);
- d->n_ref--;
-
- if (d->n_ref > 0)
- return NULL;
+static DnsSearchDomain* dns_search_domain_free(DnsSearchDomain *d) {
+ assert(d);
free(d->name);
return mfree(d);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsSearchDomain, dns_search_domain, dns_search_domain_free);
+
void dns_search_domain_unlink(DnsSearchDomain *d) {
assert(d);
assert(d->manager);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 5476ca2dbd..b85eb75273 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -43,16 +43,18 @@ int dns_server_new(
return -E2BIG;
}
- s = new0(DnsServer, 1);
+ s = new(DnsServer, 1);
if (!s)
return -ENOMEM;
- s->n_ref = 1;
- s->manager = m;
- s->type = type;
- s->family = family;
- s->address = *in_addr;
- s->ifindex = ifindex;
+ *s = (DnsServer) {
+ .n_ref = 1,
+ .manager = m,
+ .type = type,
+ .family = family,
+ .address = *in_addr,
+ .ifindex = ifindex,
+ };
dns_server_reset_features(s);
@@ -81,8 +83,7 @@ int dns_server_new(
s->linked = true;
#if ENABLE_DNS_OVER_TLS
- /* Do not verify cerificate */
- gnutls_certificate_allocate_credentials(&s->tls_cert_cred);
+ dnstls_server_init(s);
#endif
/* A new DNS server that isn't fallback is added and the one
@@ -99,40 +100,21 @@ int dns_server_new(
return 0;
}
-DnsServer* dns_server_ref(DnsServer *s) {
- if (!s)
- return NULL;
-
- assert(s->n_ref > 0);
- s->n_ref++;
-
- return s;
-}
-
-DnsServer* dns_server_unref(DnsServer *s) {
- if (!s)
- return NULL;
-
- assert(s->n_ref > 0);
- s->n_ref--;
-
- if (s->n_ref > 0)
- return NULL;
+static DnsServer* dns_server_free(DnsServer *s) {
+ assert(s);
- dns_stream_unref(s->stream);
+ dns_server_unref_stream(s);
#if ENABLE_DNS_OVER_TLS
- if (s->tls_cert_cred)
- gnutls_certificate_free_credentials(s->tls_cert_cred);
-
- if (s->tls_session_data.data)
- gnutls_free(s->tls_session_data.data);
+ dnstls_server_free(s);
#endif
free(s->server_string);
return mfree(s);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsServer, dns_server, dns_server_free);
+
void dns_server_unlink(DnsServer *s) {
assert(s);
assert(s->manager);
@@ -164,6 +146,8 @@ void dns_server_unlink(DnsServer *s) {
LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
s->manager->n_dns_servers--;
break;
+ default:
+ assert_not_reached("Unknown server type");
}
s->linked = false;
@@ -174,6 +158,9 @@ void dns_server_unlink(DnsServer *s) {
if (s->manager->current_dns_server == s)
manager_set_dns_server(s->manager, NULL);
+ /* No need to keep a default stream around anymore */
+ dns_server_unref_stream(s);
+
dns_server_unref(s);
}
@@ -593,29 +580,7 @@ void dns_server_warn_downgrade(DnsServer *server) {
server->warned_downgrade = true;
}
-bool dns_server_limited_domains(DnsServer *server) {
- DnsSearchDomain *domain;
- bool domain_restricted = false;
-
- /* Check if the server has route-only domains without ~., i. e. whether
- * it should only be used for particular domains */
- if (!server->link)
- return false;
-
- LIST_FOREACH(domains, domain, server->link->search_domains)
- if (domain->route_only) {
- domain_restricted = true;
- /* ~. means "any domain", thus it is a global server */
- if (dns_name_is_root(DNS_SEARCH_DOMAIN_NAME(domain)))
- return false;
- }
-
- return domain_restricted;
-}
-
-static void dns_server_hash_func(const void *p, struct siphash *state) {
- const DnsServer *s = p;
-
+static void dns_server_hash_func(const DnsServer *s, struct siphash *state) {
assert(s);
siphash24_compress(&s->family, sizeof(s->family), state);
@@ -623,31 +588,25 @@ static void dns_server_hash_func(const void *p, struct siphash *state) {
siphash24_compress(&s->ifindex, sizeof(s->ifindex), state);
}
-static int dns_server_compare_func(const void *a, const void *b) {
- const DnsServer *x = a, *y = b;
+static int dns_server_compare_func(const DnsServer *x, const DnsServer *y) {
int r;
- if (x->family < y->family)
- return -1;
- if (x->family > y->family)
- return 1;
+ r = CMP(x->family, y->family);
+ if (r != 0)
+ return r;
r = memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
if (r != 0)
return r;
- if (x->ifindex < y->ifindex)
- return -1;
- if (x->ifindex > y->ifindex)
- return 1;
+ r = CMP(x->ifindex, y->ifindex);
+ if (r != 0)
+ return r;
return 0;
}
-const struct hash_ops dns_server_hash_ops = {
- .hash = dns_server_hash_func,
- .compare = dns_server_compare_func
-};
+DEFINE_HASH_OPS(dns_server_hash_ops, DnsServer, dns_server_hash_func, dns_server_compare_func);
void dns_server_unlink_all(DnsServer *first) {
DnsServer *next;
@@ -850,6 +809,9 @@ void dns_server_reset_features(DnsServer *s) {
s->warned_downgrade = false;
dns_server_reset_counters(s);
+
+ /* Let's close the default stream, so that we reprobe with the new features */
+ dns_server_unref_stream(s);
}
void dns_server_reset_features_all(DnsServer *s) {
@@ -910,6 +872,30 @@ void dns_server_dump(DnsServer *s, FILE *f) {
yes_no(s->packet_rrsig_missing));
}
+void dns_server_unref_stream(DnsServer *s) {
+ DnsStream *ref;
+
+ assert(s);
+
+ /* Detaches the default stream of this server. Some special care needs to be taken here, as that stream and
+ * this server reference each other. First, take the stream out of the server. It's destructor will check if it
+ * is registered with us, hence let's invalidate this separatly, so that it is already unregistered. */
+ ref = TAKE_PTR(s->stream);
+
+ /* And then, unref it */
+ dns_stream_unref(ref);
+}
+
+DnsScope *dns_server_scope(DnsServer *s) {
+ assert(s);
+ assert((s->type == DNS_SERVER_LINK) == !!s->link);
+
+ if (s->link)
+ return s->link->unicast_scope;
+
+ return s->manager->unicast_scope;
+}
+
static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
[DNS_SERVER_SYSTEM] = "system",
[DNS_SERVER_FALLBACK] = "fallback",
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index dffc4217d1..3c4627bca5 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -3,18 +3,15 @@
#include "in-addr-util.h"
-#if ENABLE_DNS_OVER_TLS
-#include <gnutls/gnutls.h>
-#endif
-
typedef struct DnsServer DnsServer;
typedef enum DnsServerType {
DNS_SERVER_SYSTEM,
DNS_SERVER_FALLBACK,
DNS_SERVER_LINK,
+ _DNS_SERVER_TYPE_MAX,
+ _DNS_SERVER_TYPE_INVALID = -1
} DnsServerType;
-#define _DNS_SERVER_TYPE_MAX (DNS_SERVER_LINK + 1)
const char* dns_server_type_to_string(DnsServerType i) _const_;
DnsServerType dns_server_type_from_string(const char *s) _pure_;
@@ -40,6 +37,9 @@ int dns_server_feature_level_from_string(const char *s) _pure_;
#include "resolved-link.h"
#include "resolved-manager.h"
+#if ENABLE_DNS_OVER_TLS
+#include "resolved-dnstls.h"
+#endif
struct DnsServer {
Manager *manager;
@@ -54,11 +54,12 @@ struct DnsServer {
int ifindex; /* for IPv6 link-local DNS servers */
char *server_string;
+
+ /* The long-lived stream towards this server. */
DnsStream *stream;
#if ENABLE_DNS_OVER_TLS
- gnutls_certificate_credentials_t tls_cert_cred;
- gnutls_datum_t tls_session_data;
+ DnsTlsServerData dnstls_data;
#endif
DnsServerFeatureLevel verified_feature_level;
@@ -121,8 +122,6 @@ bool dns_server_dnssec_supported(DnsServer *server);
void dns_server_warn_downgrade(DnsServer *server);
-bool dns_server_limited_domains(DnsServer *server);
-
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex);
void dns_server_unlink_all(DnsServer *first);
@@ -150,3 +149,7 @@ void dns_server_reset_features(DnsServer *s);
void dns_server_reset_features_all(DnsServer *s);
void dns_server_dump(DnsServer *s, FILE *f);
+
+void dns_server_unref_stream(DnsServer *s);
+
+DnsScope *dns_server_scope(DnsServer *s);
diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c
index 066daef96e..aee339a4c8 100644
--- a/src/resolve/resolved-dns-stream.c
+++ b/src/resolve/resolved-dns-stream.c
@@ -11,14 +11,15 @@
#define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
#define DNS_STREAMS_MAX 128
-#define WRITE_TLS_DATA 1
-
static void dns_stream_stop(DnsStream *s) {
assert(s);
s->io_event_source = sd_event_source_unref(s->io_event_source);
s->timeout_event_source = sd_event_source_unref(s->timeout_event_source);
s->fd = safe_close(s->fd);
+
+ /* Disconnect us from the server object if we are now not usable anymore */
+ dns_stream_detach(s);
}
static int dns_stream_update_io(DnsStream *s) {
@@ -38,26 +39,33 @@ static int dns_stream_update_io(DnsStream *s) {
if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size)
f |= EPOLLIN;
+#if ENABLE_DNS_OVER_TLS
+ /* For handshake and clean closing purposes, TLS can override requested events */
+ if (s->dnstls_events)
+ f = s->dnstls_events;
+#endif
+
return sd_event_source_set_io_events(s->io_event_source, f);
}
static int dns_stream_complete(DnsStream *s, int error) {
+ _cleanup_(dns_stream_unrefp) _unused_ DnsStream *ref = dns_stream_ref(s); /* Protect stream while we process it */
+
assert(s);
#if ENABLE_DNS_OVER_TLS
- if (s->tls_session && IN_SET(error, ETIMEDOUT, 0)) {
+ if (s->encrypted) {
int r;
- r = gnutls_bye(s->tls_session, GNUTLS_SHUT_RDWR);
- if (r == GNUTLS_E_AGAIN && !s->tls_bye) {
- dns_stream_ref(s); /* keep reference for closing TLS session */
- s->tls_bye = true;
- } else
+ r = dnstls_stream_shutdown(s, error);
+ if (r != -EAGAIN)
dns_stream_stop(s);
} else
#endif
dns_stream_stop(s);
+ dns_stream_detach(s);
+
if (s->complete)
s->complete(s, error);
else /* the default action if no completion function is set is to close the stream */
@@ -191,34 +199,24 @@ static int dns_stream_identify(DnsStream *s) {
return 0;
}
-static ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags) {
- ssize_t r;
+ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags) {
+ ssize_t m;
assert(s);
assert(iov);
#if ENABLE_DNS_OVER_TLS
- if (s->tls_session && !(flags & WRITE_TLS_DATA)) {
+ if (s->encrypted && !(flags & DNS_STREAM_WRITE_TLS_DATA)) {
ssize_t ss;
size_t i;
- r = 0;
+ m = 0;
for (i = 0; i < iovcnt; i++) {
- ss = gnutls_record_send(s->tls_session, iov[i].iov_base, iov[i].iov_len);
- if (ss < 0) {
- switch(ss) {
-
- case GNUTLS_E_INTERRUPTED:
- return -EINTR;
- case GNUTLS_E_AGAIN:
- return -EAGAIN;
- default:
- log_debug("Failed to invoke gnutls_record_send: %s", gnutls_strerror(ss));
- return -EIO;
- }
- }
+ ss = dnstls_stream_write(s, iov[i].iov_base, iov[i].iov_len);
+ if (ss < 0)
+ return ss;
- r += ss;
+ m += ss;
if (ss != (ssize_t) iov[i].iov_len)
continue;
}
@@ -232,80 +230,47 @@ static ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t i
.msg_namelen = s->tfo_salen
};
- r = sendmsg(s->fd, &hdr, MSG_FASTOPEN);
- if (r < 0) {
+ m = sendmsg(s->fd, &hdr, MSG_FASTOPEN);
+ if (m < 0) {
if (errno == EOPNOTSUPP) {
s->tfo_salen = 0;
- r = connect(s->fd, &s->tfo_address.sa, s->tfo_salen);
- if (r < 0)
+ if (connect(s->fd, &s->tfo_address.sa, s->tfo_salen) < 0)
return -errno;
- r = -EAGAIN;
- } else if (errno == EINPROGRESS)
- r = -EAGAIN;
+ return -EAGAIN;
+ }
+ if (errno == EINPROGRESS)
+ return -EAGAIN;
+
+ return -errno;
} else
s->tfo_salen = 0; /* connection is made */
} else {
- r = writev(s->fd, iov, iovcnt);
- if (r < 0)
- r = -errno;
+ m = writev(s->fd, iov, iovcnt);
+ if (m < 0)
+ return -errno;
}
- return r;
+ return m;
}
static ssize_t dns_stream_read(DnsStream *s, void *buf, size_t count) {
ssize_t ss;
#if ENABLE_DNS_OVER_TLS
- if (s->tls_session) {
- ss = gnutls_record_recv(s->tls_session, buf, count);
- if (ss < 0) {
- switch(ss) {
-
- case GNUTLS_E_INTERRUPTED:
- return -EINTR;
- case GNUTLS_E_AGAIN:
- return -EAGAIN;
- default:
- log_debug("Failed to invoke gnutls_record_send: %s", gnutls_strerror(ss));
- return -EIO;
- }
- } else if (s->on_connection) {
- int r;
-
- r = s->on_connection(s);
- s->on_connection = NULL; /* only call once */
- if (r < 0)
- return r;
- }
- } else
+ if (s->encrypted)
+ ss = dnstls_stream_read(s, buf, count);
+ else
#endif
{
ss = read(s->fd, buf, count);
if (ss < 0)
- ss = -errno;
+ return -errno;
}
return ss;
}
-#if ENABLE_DNS_OVER_TLS
-static ssize_t dns_stream_tls_writev(gnutls_transport_ptr_t p, const giovec_t * iov, int iovcnt) {
- int r;
-
- assert(p);
-
- r = dns_stream_writev((DnsStream*) p, (struct iovec*) iov, iovcnt, WRITE_TLS_DATA);
- if (r < 0) {
- errno = -r;
- return -1;
- }
-
- return r;
-}
-#endif
-
static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
DnsStream *s = userdata;
@@ -315,42 +280,24 @@ static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
}
static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
- DnsStream *s = userdata;
+ _cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */
int r;
assert(s);
#if ENABLE_DNS_OVER_TLS
- if (s->tls_bye) {
- assert(s->tls_session);
-
- r = gnutls_bye(s->tls_session, GNUTLS_SHUT_RDWR);
- if (r != GNUTLS_E_AGAIN) {
- s->tls_bye = false;
- dns_stream_unref(s);
- }
-
- return 0;
- }
-
- if (s->tls_handshake < 0) {
- assert(s->tls_session);
-
- s->tls_handshake = gnutls_handshake(s->tls_session);
- if (s->tls_handshake >= 0) {
- if (s->on_connection && !(gnutls_session_get_flags(s->tls_session) & GNUTLS_SFLAGS_FALSE_START)) {
- r = s->on_connection(s);
- s->on_connection = NULL; /* only call once */
- if (r < 0)
- return r;
- }
- } else {
- if (gnutls_error_is_fatal(s->tls_handshake))
- return dns_stream_complete(s, ECONNREFUSED);
- else
- return 0;
- }
+ if (s->encrypted) {
+ r = dnstls_stream_on_io(s, revents);
+ if (r == DNSTLS_STREAM_CLOSED)
+ return 0;
+ if (r == -EAGAIN)
+ return dns_stream_update_io(s);
+ if (r < 0)
+ return dns_stream_complete(s, -r);
+ r = dns_stream_update_io(s);
+ if (r < 0)
+ return r;
}
#endif
@@ -368,10 +315,8 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
struct iovec iov[2];
ssize_t ss;
- iov[0].iov_base = &s->write_size;
- iov[0].iov_len = sizeof(s->write_size);
- iov[1].iov_base = DNS_PACKET_DATA(s->write_packet);
- iov[1].iov_len = s->write_packet->size;
+ iov[0] = IOVEC_MAKE(&s->write_size, sizeof(s->write_size));
+ iov[1] = IOVEC_MAKE(DNS_PACKET_DATA(s->write_packet), s->write_packet->size);
IOVEC_INCREMENT(iov, 2, s->n_written);
@@ -449,8 +394,8 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
(uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size),
sizeof(s->read_size) + be16toh(s->read_size) - s->n_read);
if (ss < 0) {
- if (!IN_SET(errno, EINTR, EAGAIN))
- return dns_stream_complete(s, errno);
+ if (!IN_SET(-ss, EINTR, EAGAIN))
+ return dns_stream_complete(s, -ss);
} else if (ss == 0)
return dns_stream_complete(s, ECONNRESET);
else
@@ -482,32 +427,22 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
return 0;
}
-DnsStream *dns_stream_unref(DnsStream *s) {
+static DnsStream *dns_stream_free(DnsStream *s) {
DnsPacket *p;
Iterator i;
- if (!s)
- return NULL;
-
- assert(s->n_ref > 0);
- s->n_ref--;
-
- if (s->n_ref > 0)
- return NULL;
+ assert(s);
dns_stream_stop(s);
- if (s->server && s->server->stream == s)
- s->server->stream = NULL;
-
if (s->manager) {
LIST_REMOVE(streams, s->manager->dns_streams, s);
s->manager->n_dns_streams--;
}
#if ENABLE_DNS_OVER_TLS
- if (s->tls_session)
- gnutls_deinit(s->tls_session);
+ if (s->encrypted)
+ dnstls_stream_free(s);
#endif
ORDERED_SET_FOREACH(p, s->write_queue, i)
@@ -522,38 +457,39 @@ DnsStream *dns_stream_unref(DnsStream *s) {
return mfree(s);
}
-DnsStream *dns_stream_ref(DnsStream *s) {
- if (!s)
- return NULL;
-
- assert(s->n_ref > 0);
- s->n_ref++;
+DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsStream, dns_stream, dns_stream_free);
- return s;
-}
+int dns_stream_new(
+ Manager *m,
+ DnsStream **ret,
+ DnsProtocol protocol,
+ int fd,
+ const union sockaddr_union *tfo_address) {
-int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address) {
_cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
int r;
assert(m);
+ assert(ret);
assert(fd >= 0);
if (m->n_dns_streams > DNS_STREAMS_MAX)
return -EBUSY;
- s = new0(DnsStream, 1);
+ s = new(DnsStream, 1);
if (!s)
return -ENOMEM;
+ *s = (DnsStream) {
+ .n_ref = 1,
+ .fd = -1,
+ .protocol = protocol,
+ };
+
r = ordered_set_ensure_allocated(&s->write_queue, &dns_packet_hash_ops);
if (r < 0)
return r;
- s->n_ref = 1;
- s->fd = -1;
- s->protocol = protocol;
-
r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
if (r < 0)
return r;
@@ -572,39 +508,26 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd, co
(void) sd_event_source_set_description(s->timeout_event_source, "dns-stream-timeout");
LIST_PREPEND(streams, m->dns_streams, s);
+ m->n_dns_streams++;
s->manager = m;
+
s->fd = fd;
+
if (tfo_address) {
s->tfo_address = *tfo_address;
s->tfo_salen = tfo_address->sa.sa_family == AF_INET6 ? sizeof(tfo_address->in6) : sizeof(tfo_address->in);
}
- m->n_dns_streams++;
-
*ret = TAKE_PTR(s);
return 0;
}
-#if ENABLE_DNS_OVER_TLS
-int dns_stream_connect_tls(DnsStream *s, gnutls_session_t tls_session) {
- gnutls_transport_set_ptr2(tls_session, (gnutls_transport_ptr_t) (long) s->fd, s);
- gnutls_transport_set_vec_push_function(tls_session, &dns_stream_tls_writev);
-
- s->encrypted = true;
- s->tls_session = tls_session;
- s->tls_handshake = gnutls_handshake(tls_session);
- if (s->tls_handshake < 0 && gnutls_error_is_fatal(s->tls_handshake))
- return -ECONNREFUSED;
-
- return 0;
-}
-#endif
-
int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
int r;
assert(s);
+ assert(p);
r = ordered_set_put(s->write_queue, p);
if (r < 0)
@@ -614,3 +537,31 @@ int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
return dns_stream_update_io(s);
}
+
+DnsPacket *dns_stream_take_read_packet(DnsStream *s) {
+ assert(s);
+
+ if (!s->read_packet)
+ return NULL;
+
+ if (s->n_read < sizeof(s->read_size))
+ return NULL;
+
+ if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size))
+ return NULL;
+
+ s->n_read = 0;
+ return TAKE_PTR(s->read_packet);
+}
+
+void dns_stream_detach(DnsStream *s) {
+ assert(s);
+
+ if (!s->server)
+ return;
+
+ if (s->server->stream != s)
+ return;
+
+ dns_server_unref_stream(s->server);
+}
diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h
index 9a0da226d8..f18fc919e7 100644
--- a/src/resolve/resolved-dns-stream.h
+++ b/src/resolve/resolved-dns-stream.h
@@ -8,11 +8,12 @@ typedef struct DnsStream DnsStream;
#include "resolved-dns-packet.h"
#include "resolved-dns-transaction.h"
#include "resolved-manager.h"
-
#if ENABLE_DNS_OVER_TLS
-#include <gnutls/gnutls.h>
+#include "resolved-dnstls.h"
#endif
+#define DNS_STREAM_WRITE_TLS_DATA 1
+
/* Streams are used by three subsystems:
*
* 1. The normal transaction logic when doing a DNS or LLMNR lookup via TCP
@@ -22,7 +23,7 @@ typedef struct DnsStream DnsStream;
struct DnsStream {
Manager *manager;
- int n_ref;
+ unsigned n_ref;
DnsProtocol protocol;
@@ -40,9 +41,8 @@ struct DnsStream {
socklen_t tfo_salen;
#if ENABLE_DNS_OVER_TLS
- gnutls_session_t tls_session;
- int tls_handshake;
- bool tls_bye;
+ DnsTlsStreamData dnstls_data;
+ int dnstls_events;
#endif
sd_event_source *io_event_source;
@@ -53,13 +53,12 @@ struct DnsStream {
size_t n_written, n_read;
OrderedSet *write_queue;
- int (*on_connection)(DnsStream *s);
int (*on_packet)(DnsStream *s);
int (*complete)(DnsStream *s, int error);
LIST_HEAD(DnsTransaction, transactions); /* when used by the transaction logic */
DnsServer *server; /* when used by the transaction logic */
- DnsQuery *query; /* when used by the DNS stub logic */
+ DnsQuery *query; /* when used by the DNS stub logic */
/* used when DNS-over-TLS is enabled */
bool encrypted:1;
@@ -69,7 +68,7 @@ struct DnsStream {
int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address);
#if ENABLE_DNS_OVER_TLS
-int dns_stream_connect_tls(DnsStream *s, gnutls_session_t tls_session);
+int dns_stream_connect_tls(DnsStream *s, void *tls_session);
#endif
DnsStream *dns_stream_unref(DnsStream *s);
DnsStream *dns_stream_ref(DnsStream *s);
@@ -77,6 +76,7 @@ DnsStream *dns_stream_ref(DnsStream *s);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_unref);
int dns_stream_write_packet(DnsStream *s, DnsPacket *p);
+ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags);
static inline bool DNS_STREAM_QUEUED(DnsStream *s) {
assert(s);
@@ -86,3 +86,7 @@ static inline bool DNS_STREAM_QUEUED(DnsStream *s) {
return !!s->write_packet;
}
+
+DnsPacket *dns_stream_take_read_packet(DnsStream *s);
+
+void dns_stream_detach(DnsStream *s);
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index 5ddf13081e..a00716cd85 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "fd-util.h"
+#include "missing_network.h"
#include "resolved-dns-stub.h"
#include "socket-util.h"
@@ -392,7 +393,6 @@ static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void
}
static int manager_dns_stub_udp_fd(Manager *m) {
- static const int one = 1;
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(53),
@@ -408,14 +408,17 @@ static int manager_dns_stub_udp_fd(Manager *m) {
if (fd < 0)
return -errno;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return r;
- if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
+ if (r < 0)
+ return r;
- if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
+ if (r < 0)
+ return r;
/* Make sure no traffic from outside the local host can leak to onto this socket */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
@@ -434,13 +437,17 @@ static int manager_dns_stub_udp_fd(Manager *m) {
}
static int on_dns_stub_stream_packet(DnsStream *s) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+
assert(s);
- assert(s->read_packet);
- if (dns_packet_validate_query(s->read_packet) > 0) {
- log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
+ p = dns_stream_take_read_packet(s);
+ assert(p);
+
+ if (dns_packet_validate_query(p) > 0) {
+ log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(p));
- dns_stub_process_query(s->manager, s, s->read_packet);
+ dns_stub_process_query(s->manager, s, p);
} else
log_debug("Invalid DNS stub TCP packet, ignoring.");
@@ -479,7 +486,6 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void
}
static int manager_dns_stub_tcp_fd(Manager *m) {
- static const int one = 1;
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
@@ -495,17 +501,21 @@ static int manager_dns_stub_tcp_fd(Manager *m) {
if (fd < 0)
return -errno;
- if (setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, true);
+ if (r < 0)
+ return r;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return r;
- if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
+ if (r < 0)
+ return r;
- if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
- return -errno;
+ r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
+ if (r < 0)
+ return r;
/* Make sure no traffic from outside the local host can leak to onto this socket */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c
index 9a8ce7ae2d..f65116c3b4 100644
--- a/src/resolve/resolved-dns-synthesize.c
+++ b/src/resolve/resolved-dns-synthesize.c
@@ -3,6 +3,7 @@
#include "alloc-util.h"
#include "hostname-util.h"
#include "local-addresses.h"
+#include "missing_network.h"
#include "resolved-dns-synthesize.h"
int dns_synthesize_ifindex(int ifindex) {
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index c60b8215a6..4a2d2ccbde 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -11,11 +11,10 @@
#include "resolved-dns-cache.h"
#include "resolved-dns-transaction.h"
#include "resolved-llmnr.h"
-#include "string-table.h"
-
#if ENABLE_DNS_OVER_TLS
-#include <gnutls/socket.h>
+#include "resolved-dnstls.h"
#endif
+#include "string-table.h"
#define TRANSACTIONS_MAX 4096
#define TRANSACTION_TCP_TIMEOUT_USEC (10U*USEC_PER_SEC)
@@ -503,73 +502,55 @@ static int dns_transaction_on_stream_packet(DnsTransaction *t, DnsPacket *p) {
return 0;
}
-static int on_stream_connection(DnsStream *s) {
-#if ENABLE_DNS_OVER_TLS
- /* Store TLS Ticket for faster succesive TLS handshakes */
- if (s->tls_session && s->server) {
- if (s->server->tls_session_data.data)
- gnutls_free(s->server->tls_session_data.data);
-
- gnutls_session_get_data2(s->tls_session, &s->server->tls_session_data);
- }
-#endif
-
- return 0;
-}
-
static int on_stream_complete(DnsStream *s, int error) {
- DnsTransaction *t, *n;
- int r = 0;
-
- /* Do not let new transactions use this stream */
- if (s->server && s->server->stream == s)
- s->server->stream = dns_stream_unref(s->server->stream);
+ assert(s);
if (ERRNO_IS_DISCONNECT(error) && s->protocol != DNS_PROTOCOL_LLMNR) {
- usec_t usec;
-
log_debug_errno(error, "Connection failure for DNS TCP stream: %m");
if (s->transactions) {
+ DnsTransaction *t;
+
t = s->transactions;
- assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
dns_server_packet_lost(t->server, IPPROTO_TCP, t->current_feature_level);
}
}
- LIST_FOREACH_SAFE(transactions_by_stream, t, n, s->transactions)
- if (error != 0)
+ if (error != 0) {
+ DnsTransaction *t, *n;
+
+ LIST_FOREACH_SAFE(transactions_by_stream, t, n, s->transactions)
on_transaction_stream_error(t, error);
- else if (DNS_PACKET_ID(s->read_packet) == t->id)
- /* As each transaction have a unique id the return code is only set once */
- r = dns_transaction_on_stream_packet(t, s->read_packet);
+ }
- return r;
+ return 0;
}
-static int dns_stream_on_packet(DnsStream *s) {
+static int on_stream_packet(DnsStream *s) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- int r = 0;
DnsTransaction *t;
+ assert(s);
+
/* Take ownership of packet to be able to receive new packets */
- p = TAKE_PTR(s->read_packet);
- s->n_read = 0;
+ p = dns_stream_take_read_packet(s);
+ assert(p);
t = hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
+ if (t)
+ return dns_transaction_on_stream_packet(t, p);
/* Ignore incorrect transaction id as transaction can have been canceled */
- if (t)
- r = dns_transaction_on_stream_packet(t, p);
- else {
- if (dns_packet_validate_reply(p) <= 0) {
- log_debug("Invalid TCP reply packet.");
- on_stream_complete(s, 0);
- }
- return 0;
+ if (dns_packet_validate_reply(p) <= 0) {
+ log_debug("Invalid TCP reply packet.");
+ on_stream_complete(s, 0);
}
- return r;
+ return 0;
+}
+
+static uint16_t dns_port_for_feature_level(DnsServerFeatureLevel level) {
+ return DNS_SERVER_FEATURE_LEVEL_IS_TLS(level) ? 853 : 53;
}
static int dns_transaction_emit_tcp(DnsTransaction *t) {
@@ -577,9 +558,6 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
_cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
union sockaddr_union sa;
int r;
-#if ENABLE_DNS_OVER_TLS
- gnutls_session_t gs;
-#endif
assert(t);
@@ -602,7 +580,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
if (t->server->stream && (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) == t->server->stream->encrypted))
s = dns_stream_ref(t->server->stream);
else
- fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) ? 853 : 53, &sa);
+ fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_port_for_feature_level(t->current_feature_level), &sa);
break;
@@ -645,41 +623,25 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
fd = -1;
- if (t->server) {
- dns_stream_unref(t->server->stream);
- t->server->stream = dns_stream_ref(s);
- s->server = dns_server_ref(t->server);
- }
-
#if ENABLE_DNS_OVER_TLS
- if (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level)) {
- r = gnutls_init(&gs, GNUTLS_CLIENT | GNUTLS_ENABLE_FALSE_START | GNUTLS_NONBLOCK);
- if (r < 0)
- return r;
-
- /* As DNS-over-TLS is a recent protocol, older TLS versions can be disabled */
- r = gnutls_priority_set_direct(gs, "NORMAL:-VERS-ALL:+VERS-TLS1.2", NULL);
- if (r < 0)
- return r;
-
- r = gnutls_credentials_set(gs, GNUTLS_CRD_CERTIFICATE, t->server->tls_cert_cred);
- if (r < 0)
- return r;
-
- if (t->server && t->server->tls_session_data.size > 0)
- gnutls_session_set_data(gs, t->server->tls_session_data.data, t->server->tls_session_data.size);
+ if (t->scope->protocol == DNS_PROTOCOL_DNS &&
+ DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level)) {
- gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
-
- r = dns_stream_connect_tls(s, gs);
+ assert(t->server);
+ r = dnstls_stream_connect_tls(s, t->server);
if (r < 0)
return r;
}
#endif
- s->on_connection = on_stream_connection;
+ if (t->server) {
+ dns_server_unref_stream(t->server);
+ t->server->stream = dns_stream_ref(s);
+ s->server = dns_server_ref(t->server);
+ }
+
s->complete = on_stream_complete;
- s->on_packet = dns_stream_on_packet;
+ s->on_packet = on_stream_packet;
/* The interface index is difficult to determine if we are
* connecting to the local host, hence fill this in right away
@@ -1380,10 +1342,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
dns_transaction_stop_timeout(t);
- r = dns_scope_network_good(t->scope);
- if (r < 0)
- return r;
- if (r == 0) {
+ if (!dns_scope_network_good(t->scope)) {
dns_transaction_complete(t, DNS_TRANSACTION_NETWORK_DOWN);
return 0;
}
@@ -1854,13 +1813,12 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource
if (r > 0) {
char s[DNS_RESOURCE_KEY_STRING_MAX], saux[DNS_RESOURCE_KEY_STRING_MAX];
- log_debug("Potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).",
- aux->id,
- dns_resource_key_to_string(t->key, s, sizeof s),
- t->id,
- dns_resource_key_to_string(aux->key, saux, sizeof saux));
-
- return -ELOOP;
+ return log_debug_errno(SYNTHETIC_ERRNO(ELOOP),
+ "Potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).",
+ aux->id,
+ dns_resource_key_to_string(t->key, s, sizeof s),
+ t->id,
+ dns_resource_key_to_string(aux->key, saux, sizeof saux));
}
}
@@ -2184,6 +2142,14 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
if (r > 0) /* positive reply, we won't need the SOA and hence don't need to validate
* it. */
continue;
+
+ /* Only bother with this if the SOA/NS RR we are looking at is actually a parent of
+ * what we are looking for, otherwise there's no value in it for us. */
+ r = dns_name_endswith(dns_resource_key_name(t->key), dns_resource_key_name(rr->key));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
}
r = dnssec_has_rrsig(t->answer, rr->key);
@@ -2318,21 +2284,21 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
r = dns_name_parent(&name);
if (r > 0) {
type = DNS_TYPE_SOA;
- log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty DS response).",
- t->id, dns_resource_key_name(t->key));
+ log_debug("Requesting parent SOA (→ %s) to validate transaction %" PRIu16 " (%s, unsigned empty DS response).",
+ name, t->id, dns_resource_key_name(t->key));
} else
name = NULL;
} else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) {
type = DNS_TYPE_DS;
- log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).",
- t->id, dns_resource_key_name(t->key));
+ log_debug("Requesting DS (→ %s) to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).",
+ name, t->id, name);
} else {
type = DNS_TYPE_SOA;
- log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).",
- t->id, dns_resource_key_name(t->key));
+ log_debug("Requesting SOA (→ %s) to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).",
+ name, t->id, name);
}
if (name) {
@@ -2483,8 +2449,8 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
return true;
/* A CNAME/DNAME without a parent? That's sooo weird. */
- log_debug("Transaction %" PRIu16 " claims CNAME/DNAME at root. Refusing.", t->id);
- return -EBADMSG;
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Transaction %" PRIu16 " claims CNAME/DNAME at root. Refusing.", t->id);
}
}
diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c
index 533e438fae..c5ec93b724 100644
--- a/src/resolve/resolved-dns-trust-anchor.c
+++ b/src/resolve/resolved-dns-trust-anchor.c
@@ -218,7 +218,10 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u
if (r < 0)
return log_warning_errno(r, "Unable to parse domain in line %s:%u: %m", path, line);
- if (!dns_name_is_valid(domain)) {
+ r = dns_name_is_valid(domain);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to chack validity of domain name '%s', at line %s:%u, ignoring line: %m", domain, path, line);
+ if (r == 0) {
log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
return -EINVAL;
}
@@ -385,7 +388,10 @@ static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, u
if (r < 0)
return log_warning_errno(r, "Unable to parse line %s:%u: %m", path, line);
- if (!dns_name_is_valid(domain)) {
+ r = dns_name_is_valid(domain);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to chack validity of domain name '%s', at line %s:%u, ignoring line: %m", domain, path, line);
+ if (r == 0) {
log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
return -EINVAL;
}
@@ -427,7 +433,6 @@ static int dns_trust_anchor_load_files(
STRV_FOREACH(f, files) {
_cleanup_fclose_ FILE *g = NULL;
- char line[LINE_MAX];
unsigned n = 0;
g = fopen(*f, "r");
@@ -435,13 +440,22 @@ static int dns_trust_anchor_load_files(
if (errno == ENOENT)
continue;
- log_warning_errno(errno, "Failed to open %s: %m", *f);
+ log_warning_errno(errno, "Failed to open '%s', ignoring: %m", *f);
continue;
}
- FOREACH_LINE(line, g, log_warning_errno(errno, "Failed to read %s, ignoring: %m", *f)) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
char *l;
+ r = read_line(g, LONG_LINE_MAX, &line);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read '%s', ignoring: %m", *f);
+ break;
+ }
+ if (r == 0)
+ break;
+
n++;
l = strstrip(line);
@@ -458,10 +472,8 @@ static int dns_trust_anchor_load_files(
return 0;
}
-static int domain_name_cmp(const void *a, const void *b) {
- char **x = (char**) a, **y = (char**) b;
-
- return dns_name_compare_func(*x, *y);
+static int domain_name_cmp(char * const *a, char * const *b) {
+ return dns_name_compare_func(*a, *b);
}
static int dns_trust_anchor_dump(DnsTrustAnchor *d) {
@@ -491,7 +503,7 @@ static int dns_trust_anchor_dump(DnsTrustAnchor *d) {
if (!l)
return log_oom();
- qsort_safe(l, set_size(d->negative_by_name), sizeof(char*), domain_name_cmp);
+ typesafe_qsort(l, set_size(d->negative_by_name), domain_name_cmp);
j = strv_join(l, " ");
if (!j)
diff --git a/src/resolve/resolved-dnssd-bus.c b/src/resolve/resolved-dnssd-bus.c
index c2db31df0f..24bb37b35e 100644
--- a/src/resolve/resolved-dnssd-bus.c
+++ b/src/resolve/resolved-dnssd-bus.c
@@ -1,6 +1,7 @@
#include "alloc-util.h"
#include "bus-util.h"
+#include "missing_capability.h"
#include "resolved-dnssd.h"
#include "resolved-dnssd-bus.h"
#include "resolved-link.h"
diff --git a/src/resolve/resolved-dnssd-bus.h b/src/resolve/resolved-dnssd-bus.h
index 403cb00a53..9ee2ce17ec 100644
--- a/src/resolve/resolved-dnssd-bus.h
+++ b/src/resolve/resolved-dnssd-bus.h
@@ -1,6 +1,5 @@
#pragma once
-
#include "sd-bus.h"
extern const sd_bus_vtable dnssd_vtable[];
diff --git a/src/resolve/resolved-dnssd.c b/src/resolve/resolved-dnssd.c
index aa4bd74788..b8e6a7abe1 100644
--- a/src/resolve/resolved-dnssd.c
+++ b/src/resolve/resolved-dnssd.c
@@ -11,7 +11,7 @@ const char* const dnssd_service_dirs[] = {
"/etc/systemd/dnssd",
"/run/systemd/dnssd",
"/usr/lib/systemd/dnssd",
-#ifdef HAVE_SPLIT_USR
+#if HAVE_SPLIT_USR
"/lib/systemd/dnssd",
#endif
NULL
@@ -141,7 +141,7 @@ static int dnssd_service_load(Manager *manager, const char *filename) {
return 0;
}
-static int specifier_dnssd_host_name(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_dnssd_host_name(char specifier, const void *data, const void *userdata, char **ret) {
DnssdService *s = (DnssdService *) userdata;
char *n;
@@ -175,10 +175,10 @@ int dnssd_render_instance_name(DnssdService *s, char **ret_name) {
if (r < 0)
return log_debug_errno(r, "Failed to replace specifiers: %m");
- if (!dns_service_name_is_valid(name)) {
- log_debug("Service instance name '%s' is invalid.", name);
- return -EINVAL;
- }
+ if (!dns_service_name_is_valid(name))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Service instance name '%s' is invalid.",
+ name);
*ret_name = TAKE_PTR(name);
@@ -228,10 +228,10 @@ int dnssd_update_rrs(DnssdService *s) {
if (r < 0)
return r;
- r = dns_name_concat(s->type, "local", &service_name);
+ r = dns_name_concat(s->type, "local", 0, &service_name);
if (r < 0)
return r;
- r = dns_name_concat(n, service_name, &full_name);
+ r = dns_name_concat(n, service_name, 0, &full_name);
if (r < 0)
return r;
diff --git a/src/resolve/resolved-dnssd.h b/src/resolve/resolved-dnssd.h
index f49cfe2515..f2e101452d 100644
--- a/src/resolve/resolved-dnssd.h
+++ b/src/resolve/resolved-dnssd.h
@@ -1,6 +1,5 @@
#pragma once
-
#include "list.h"
typedef struct DnssdService DnssdService;
diff --git a/src/resolve/resolved-dnstls-gnutls.c b/src/resolve/resolved-dnstls-gnutls.c
new file mode 100644
index 0000000000..ddf9758bb3
--- /dev/null
+++ b/src/resolve/resolved-dnstls-gnutls.c
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_GNUTLS
+#error This source file requires DNS-over-TLS to be enabled and GnuTLS to be available.
+#endif
+
+#include <gnutls/socket.h>
+
+#include "resolved-dns-stream.h"
+#include "resolved-dnstls.h"
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(gnutls_session_t, gnutls_deinit);
+
+static ssize_t dnstls_stream_writev(gnutls_transport_ptr_t p, const giovec_t *iov, int iovcnt) {
+ int r;
+
+ assert(p);
+
+ r = dns_stream_writev((DnsStream*) p, (const struct iovec*) iov, iovcnt, DNS_STREAM_WRITE_TLS_DATA);
+ if (r < 0) {
+ errno = -r;
+ return -1;
+ }
+
+ return r;
+}
+
+int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
+ _cleanup_(gnutls_deinitp) gnutls_session_t gs;
+ int r;
+
+ assert(stream);
+ assert(server);
+
+ r = gnutls_init(&gs, GNUTLS_CLIENT | GNUTLS_ENABLE_FALSE_START | GNUTLS_NONBLOCK);
+ if (r < 0)
+ return r;
+
+ /* As DNS-over-TLS is a recent protocol, older TLS versions can be disabled */
+ r = gnutls_priority_set_direct(gs, "NORMAL:-VERS-ALL:+VERS-TLS1.2", NULL);
+ if (r < 0)
+ return r;
+
+ r = gnutls_credentials_set(gs, GNUTLS_CRD_CERTIFICATE, server->dnstls_data.cert_cred);
+ if (r < 0)
+ return r;
+
+ if (server->dnstls_data.session_data.size > 0) {
+ gnutls_session_set_data(gs, server->dnstls_data.session_data.data, server->dnstls_data.session_data.size);
+
+ // Clear old session ticket
+ gnutls_free(server->dnstls_data.session_data.data);
+ server->dnstls_data.session_data.data = NULL;
+ server->dnstls_data.session_data.size = 0;
+ }
+
+ gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+
+ gnutls_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream);
+ gnutls_transport_set_vec_push_function(gs, &dnstls_stream_writev);
+
+ stream->encrypted = true;
+ stream->dnstls_data.handshake = gnutls_handshake(gs);
+ if (stream->dnstls_data.handshake < 0 && gnutls_error_is_fatal(stream->dnstls_data.handshake))
+ return -ECONNREFUSED;
+
+ stream->dnstls_data.session = TAKE_PTR(gs);
+
+ return 0;
+}
+
+void dnstls_stream_free(DnsStream *stream) {
+ assert(stream);
+ assert(stream->encrypted);
+
+ if (stream->dnstls_data.session)
+ gnutls_deinit(stream->dnstls_data.session);
+}
+
+int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
+ int r;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.session);
+
+ if (stream->dnstls_data.shutdown) {
+ r = gnutls_bye(stream->dnstls_data.session, GNUTLS_SHUT_RDWR);
+ if (r == GNUTLS_E_AGAIN) {
+ stream->dnstls_events = gnutls_record_get_direction(stream->dnstls_data.session) == 1 ? EPOLLOUT : EPOLLIN;
+ return -EAGAIN;
+ } else if (r < 0)
+ log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r));
+
+ stream->dnstls_events = 0;
+ stream->dnstls_data.shutdown = false;
+ dns_stream_unref(stream);
+ return DNSTLS_STREAM_CLOSED;
+ } else if (stream->dnstls_data.handshake < 0) {
+ stream->dnstls_data.handshake = gnutls_handshake(stream->dnstls_data.session);
+ if (stream->dnstls_data.handshake == GNUTLS_E_AGAIN) {
+ stream->dnstls_events = gnutls_record_get_direction(stream->dnstls_data.session) == 1 ? EPOLLOUT : EPOLLIN;
+ return -EAGAIN;
+ } else if (stream->dnstls_data.handshake < 0) {
+ log_debug("Failed to invoke gnutls_handshake: %s", gnutls_strerror(stream->dnstls_data.handshake));
+ if (gnutls_error_is_fatal(stream->dnstls_data.handshake))
+ return -ECONNREFUSED;
+ }
+
+ stream->dnstls_events = 0;
+ }
+
+ return 0;
+}
+
+int dnstls_stream_shutdown(DnsStream *stream, int error) {
+ int r;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.session);
+
+ /* Store TLS Ticket for faster succesive TLS handshakes */
+ if (stream->server && stream->server->dnstls_data.session_data.size == 0 && stream->dnstls_data.handshake == GNUTLS_E_SUCCESS)
+ gnutls_session_get_data2(stream->dnstls_data.session, &stream->server->dnstls_data.session_data);
+
+ if (IN_SET(error, ETIMEDOUT, 0)) {
+ r = gnutls_bye(stream->dnstls_data.session, GNUTLS_SHUT_RDWR);
+ if (r == GNUTLS_E_AGAIN) {
+ if (!stream->dnstls_data.shutdown) {
+ stream->dnstls_data.shutdown = true;
+ dns_stream_ref(stream);
+ return -EAGAIN;
+ }
+ } else if (r < 0)
+ log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r));
+ }
+
+ return 0;
+}
+
+ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
+ ssize_t ss;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.session);
+ assert(buf);
+
+ ss = gnutls_record_send(stream->dnstls_data.session, buf, count);
+ if (ss < 0)
+ switch(ss) {
+ case GNUTLS_E_INTERRUPTED:
+ return -EINTR;
+ case GNUTLS_E_AGAIN:
+ return -EAGAIN;
+ default:
+ return log_debug_errno(SYNTHETIC_ERRNO(EPIPE),
+ "Failed to invoke gnutls_record_send: %s",
+ gnutls_strerror(ss));
+ }
+
+ return ss;
+}
+
+ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
+ ssize_t ss;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.session);
+ assert(buf);
+
+ ss = gnutls_record_recv(stream->dnstls_data.session, buf, count);
+ if (ss < 0)
+ switch(ss) {
+ case GNUTLS_E_INTERRUPTED:
+ return -EINTR;
+ case GNUTLS_E_AGAIN:
+ return -EAGAIN;
+ default:
+ return log_debug_errno(SYNTHETIC_ERRNO(EPIPE),
+ "Failed to invoke gnutls_record_recv: %s",
+ gnutls_strerror(ss));
+ }
+
+ return ss;
+}
+
+void dnstls_server_init(DnsServer *server) {
+ assert(server);
+
+ /* Do not verify cerificate */
+ gnutls_certificate_allocate_credentials(&server->dnstls_data.cert_cred);
+}
+
+void dnstls_server_free(DnsServer *server) {
+ assert(server);
+
+ if (server->dnstls_data.cert_cred)
+ gnutls_certificate_free_credentials(server->dnstls_data.cert_cred);
+
+ if (server->dnstls_data.session_data.data)
+ gnutls_free(server->dnstls_data.session_data.data);
+}
diff --git a/src/resolve/resolved-dnstls-gnutls.h b/src/resolve/resolved-dnstls-gnutls.h
new file mode 100644
index 0000000000..41c89f2737
--- /dev/null
+++ b/src/resolve/resolved-dnstls-gnutls.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_GNUTLS
+#error This source file requires DNS-over-TLS to be enabled and GnuTLS to be available.
+#endif
+
+#include <gnutls/gnutls.h>
+#include <stdbool.h>
+
+struct DnsTlsServerData {
+ gnutls_certificate_credentials_t cert_cred;
+ gnutls_datum_t session_data;
+};
+
+struct DnsTlsStreamData {
+ gnutls_session_t session;
+ int handshake;
+ bool shutdown;
+};
diff --git a/src/resolve/resolved-dnstls-openssl.c b/src/resolve/resolved-dnstls-openssl.c
new file mode 100644
index 0000000000..f269e4d648
--- /dev/null
+++ b/src/resolve/resolved-dnstls-openssl.c
@@ -0,0 +1,353 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_OPENSSL
+#error This source file requires DNS-over-TLS to be enabled and OpenSSL to be available.
+#endif
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+
+#include "io-util.h"
+#include "resolved-dns-stream.h"
+#include "resolved-dnstls.h"
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(SSL*, SSL_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(BIO*, BIO_free);
+
+static int dnstls_flush_write_buffer(DnsStream *stream) {
+ ssize_t ss;
+
+ assert(stream);
+ assert(stream->encrypted);
+
+ if (stream->dnstls_data.write_buffer->length > 0) {
+ assert(stream->dnstls_data.write_buffer->data);
+
+ struct iovec iov[1];
+ iov[0] = IOVEC_MAKE(stream->dnstls_data.write_buffer->data,
+ stream->dnstls_data.write_buffer->length);
+ ss = dns_stream_writev(stream, iov, 1, DNS_STREAM_WRITE_TLS_DATA);
+ if (ss < 0) {
+ if (ss == -EAGAIN)
+ stream->dnstls_events |= EPOLLOUT;
+
+ return ss;
+ } else {
+ stream->dnstls_data.write_buffer->length -= ss;
+ stream->dnstls_data.write_buffer->data += ss;
+
+ if (stream->dnstls_data.write_buffer->length > 0) {
+ stream->dnstls_events |= EPOLLOUT;
+ return -EAGAIN;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
+ _cleanup_(BIO_freep) BIO *rb = NULL, *wb = NULL;
+ _cleanup_(SSL_freep) SSL *s = NULL;
+ int error, r;
+
+ assert(stream);
+ assert(server);
+
+ rb = BIO_new_socket(stream->fd, 0);
+ if (!rb)
+ return -ENOMEM;
+
+ wb = BIO_new(BIO_s_mem());
+ if (!wb)
+ return -ENOMEM;
+
+ BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
+
+ s = SSL_new(server->dnstls_data.ctx);
+ if (!s)
+ return -ENOMEM;
+
+ SSL_set_connect_state(s);
+ SSL_set_session(s, server->dnstls_data.session);
+ SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
+
+ ERR_clear_error();
+ stream->dnstls_data.handshake = SSL_do_handshake(s);
+ if (stream->dnstls_data.handshake <= 0) {
+ error = SSL_get_error(s, stream->dnstls_data.handshake);
+ if (!IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ char errbuf[256];
+
+ ERR_error_string_n(error, errbuf, sizeof(errbuf));
+ log_debug("Failed to invoke SSL_do_handshake: %s", errbuf);
+ return -ECONNREFUSED;
+ }
+ }
+
+ stream->encrypted = true;
+
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0 && r != -EAGAIN)
+ return r;
+
+ stream->dnstls_data.ssl = TAKE_PTR(s);
+
+ return 0;
+}
+
+void dnstls_stream_free(DnsStream *stream) {
+ assert(stream);
+ assert(stream->encrypted);
+
+ if (stream->dnstls_data.ssl)
+ SSL_free(stream->dnstls_data.ssl);
+}
+
+int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
+ int error, r;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.ssl);
+
+ /* Flush write buffer when requested by OpenSSL */
+ if ((revents & EPOLLOUT) && (stream->dnstls_events & EPOLLOUT)) {
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0)
+ return r;
+ }
+
+ if (stream->dnstls_data.shutdown) {
+ ERR_clear_error();
+ r = SSL_shutdown(stream->dnstls_data.ssl);
+ if (r == 0) {
+ stream->dnstls_events = 0;
+
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0)
+ return r;
+
+ return -EAGAIN;
+ } else if (r < 0) {
+ error = SSL_get_error(stream->dnstls_data.ssl, r);
+ if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0)
+ return r;
+
+ return -EAGAIN;
+ } else if (error == SSL_ERROR_SYSCALL) {
+ if (errno > 0)
+ log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
+ } else {
+ char errbuf[256];
+
+ ERR_error_string_n(error, errbuf, sizeof(errbuf));
+ log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
+ }
+ }
+
+ stream->dnstls_events = 0;
+ stream->dnstls_data.shutdown = false;
+
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0)
+ return r;
+
+ dns_stream_unref(stream);
+ return DNSTLS_STREAM_CLOSED;
+ } else if (stream->dnstls_data.handshake <= 0) {
+ ERR_clear_error();
+ stream->dnstls_data.handshake = SSL_do_handshake(stream->dnstls_data.ssl);
+ if (stream->dnstls_data.handshake <= 0) {
+ error = SSL_get_error(stream->dnstls_data.ssl, stream->dnstls_data.handshake);
+ if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0)
+ return r;
+
+ return -EAGAIN;
+ } else {
+ char errbuf[256];
+
+ ERR_error_string_n(error, errbuf, sizeof(errbuf));
+ return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
+ "Failed to invoke SSL_do_handshake: %s",
+ errbuf);
+ }
+ }
+
+ stream->dnstls_events = 0;
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int dnstls_stream_shutdown(DnsStream *stream, int error) {
+ int ssl_error, r;
+ SSL_SESSION *s;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.ssl);
+
+ if (stream->server) {
+ s = SSL_get1_session(stream->dnstls_data.ssl);
+ if (s) {
+ if (stream->server->dnstls_data.session)
+ SSL_SESSION_free(stream->server->dnstls_data.session);
+
+ stream->server->dnstls_data.session = s;
+ }
+ }
+
+ if (error == ETIMEDOUT) {
+ ERR_clear_error();
+ r = SSL_shutdown(stream->dnstls_data.ssl);
+ if (r == 0) {
+ if (!stream->dnstls_data.shutdown) {
+ stream->dnstls_data.shutdown = true;
+ dns_stream_ref(stream);
+ }
+
+ stream->dnstls_events = 0;
+
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0)
+ return r;
+
+ return -EAGAIN;
+ } else if (r < 0) {
+ ssl_error = SSL_get_error(stream->dnstls_data.ssl, r);
+ if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0 && r != -EAGAIN)
+ return r;
+
+ if (!stream->dnstls_data.shutdown) {
+ stream->dnstls_data.shutdown = true;
+ dns_stream_ref(stream);
+ }
+ return -EAGAIN;
+ } else if (ssl_error == SSL_ERROR_SYSCALL) {
+ if (errno > 0)
+ log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
+ } else {
+ char errbuf[256];
+
+ ERR_error_string_n(ssl_error, errbuf, sizeof(errbuf));
+ log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
+ }
+ }
+
+ stream->dnstls_events = 0;
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
+ int error, r;
+ ssize_t ss;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.ssl);
+ assert(buf);
+
+ ERR_clear_error();
+ ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
+ if (r <= 0) {
+ error = SSL_get_error(stream->dnstls_data.ssl, r);
+ if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+ ss = -EAGAIN;
+ } else if (error == SSL_ERROR_ZERO_RETURN) {
+ stream->dnstls_events = 0;
+ ss = 0;
+ } else {
+ char errbuf[256];
+
+ ERR_error_string_n(error, errbuf, sizeof(errbuf));
+ log_debug("Failed to invoke SSL_write: %s", errbuf);
+ stream->dnstls_events = 0;
+ ss = -EPIPE;
+ }
+ } else
+ stream->dnstls_events = 0;
+
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0)
+ return r;
+
+ return ss;
+}
+
+ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
+ int error, r;
+ ssize_t ss;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.ssl);
+ assert(buf);
+
+ ERR_clear_error();
+ ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
+ if (r <= 0) {
+ error = SSL_get_error(stream->dnstls_data.ssl, r);
+ if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+ ss = -EAGAIN;
+ } else if (error == SSL_ERROR_ZERO_RETURN) {
+ stream->dnstls_events = 0;
+ ss = 0;
+ } else {
+ char errbuf[256];
+
+ ERR_error_string_n(error, errbuf, sizeof(errbuf));
+ log_debug("Failed to invoke SSL_read: %s", errbuf);
+ stream->dnstls_events = 0;
+ ss = -EPIPE;
+ }
+ } else
+ stream->dnstls_events = 0;
+
+ /* flush write buffer in cache of renegotiation */
+ r = dnstls_flush_write_buffer(stream);
+ if (r < 0)
+ return r;
+
+ return ss;
+}
+
+void dnstls_server_init(DnsServer *server) {
+ assert(server);
+
+ server->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
+ if (server->dnstls_data.ctx) {
+ SSL_CTX_set_min_proto_version(server->dnstls_data.ctx, TLS1_2_VERSION);
+ SSL_CTX_set_options(server->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
+ }
+}
+
+void dnstls_server_free(DnsServer *server) {
+ assert(server);
+
+ if (server->dnstls_data.ctx)
+ SSL_CTX_free(server->dnstls_data.ctx);
+
+ if (server->dnstls_data.session)
+ SSL_SESSION_free(server->dnstls_data.session);
+}
diff --git a/src/resolve/resolved-dnstls-openssl.h b/src/resolve/resolved-dnstls-openssl.h
new file mode 100644
index 0000000000..f0dccf32e6
--- /dev/null
+++ b/src/resolve/resolved-dnstls-openssl.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_OPENSSL
+#error This source file requires DNS-over-TLS to be enabled and OpenSSL to be available.
+#endif
+
+#include <openssl/ssl.h>
+#include <stdbool.h>
+
+struct DnsTlsServerData {
+ SSL_CTX *ctx;
+ SSL_SESSION *session;
+};
+
+struct DnsTlsStreamData {
+ int handshake;
+ bool shutdown;
+ SSL *ssl;
+ BUF_MEM *write_buffer;
+};
diff --git a/src/resolve/resolved-dnstls.h b/src/resolve/resolved-dnstls.h
new file mode 100644
index 0000000000..fdd85eece6
--- /dev/null
+++ b/src/resolve/resolved-dnstls.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !ENABLE_DNS_OVER_TLS
+#error This source file requires DNS-over-TLS to be enabled
+#endif
+
+typedef struct DnsTlsServerData DnsTlsServerData;
+typedef struct DnsTlsStreamData DnsTlsStreamData;
+
+#if DNS_OVER_TLS_USE_GNUTLS
+#include "resolved-dnstls-gnutls.h"
+#elif DNS_OVER_TLS_USE_OPENSSL
+#include "resolved-dnstls-openssl.h"
+#else
+#error Unknown dependency for supporting DNS-over-TLS
+#endif
+
+#include "resolved-dns-stream.h"
+#include "resolved-dns-transaction.h"
+
+#define DNSTLS_STREAM_CLOSED 1
+
+int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server);
+void dnstls_stream_free(DnsStream *stream);
+int dnstls_stream_on_io(DnsStream *stream, uint32_t revents);
+int dnstls_stream_shutdown(DnsStream *stream, int error);
+ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count);
+ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count);
+
+void dnstls_server_init(DnsServer *server);
+void dnstls_server_free(DnsServer *server);
diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c
index 507f68b47f..01cde4acf7 100644
--- a/src/resolve/resolved-etc-hosts.c
+++ b/src/resolve/resolved-etc-hosts.c
@@ -3,8 +3,8 @@
#include "fd-util.h"
#include "fileio.h"
#include "hostname-util.h"
-#include "resolved-etc-hosts.h"
#include "resolved-dns-synthesize.h"
+#include "resolved-etc-hosts.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@@ -12,103 +12,64 @@
/* Recheck /etc/hosts at most once every 2s */
#define ETC_HOSTS_RECHECK_USEC (2*USEC_PER_SEC)
-typedef struct EtcHostsItem {
- int family;
- union in_addr_union address;
-
- char **names;
-} EtcHostsItem;
-
-typedef struct EtcHostsItemByName {
- char *name;
-
- EtcHostsItem **items;
- size_t n_items, n_allocated;
-} EtcHostsItemByName;
-
-void manager_etc_hosts_flush(Manager *m) {
- EtcHostsItem *item;
- EtcHostsItemByName *bn;
-
- while ((item = set_steal_first(m->etc_hosts_by_address))) {
- strv_free(item->names);
- free(item);
- }
-
- while ((bn = hashmap_steal_first(m->etc_hosts_by_name))) {
- free(bn->name);
- free(bn->items);
- free(bn);
- }
-
- m->etc_hosts_by_address = set_free(m->etc_hosts_by_address);
- m->etc_hosts_by_name = hashmap_free(m->etc_hosts_by_name);
-
- m->etc_hosts_mtime = USEC_INFINITY;
+static inline void etc_hosts_item_free(EtcHostsItem *item) {
+ strv_free(item->names);
+ free(item);
}
-static void etc_hosts_item_hash_func(const void *p, struct siphash *state) {
- const EtcHostsItem *item = p;
-
- siphash24_compress(&item->family, sizeof(item->family), state);
-
- if (item->family == AF_INET)
- siphash24_compress(&item->address.in, sizeof(item->address.in), state);
- else if (item->family == AF_INET6)
- siphash24_compress(&item->address.in6, sizeof(item->address.in6), state);
+static inline void etc_hosts_item_by_name_free(EtcHostsItemByName *item) {
+ free(item->name);
+ free(item->addresses);
+ free(item);
}
-static int etc_hosts_item_compare_func(const void *a, const void *b) {
- const EtcHostsItem *x = a, *y = b;
-
- if (x->family != y->family)
- return x->family - y->family;
-
- if (x->family == AF_INET)
- return memcmp(&x->address.in.s_addr, &y->address.in.s_addr, sizeof(struct in_addr));
-
- if (x->family == AF_INET6)
- return memcmp(&x->address.in6.s6_addr, &y->address.in6.s6_addr, sizeof(struct in6_addr));
-
- return trivial_compare_func(a, b);
+void etc_hosts_free(EtcHosts *hosts) {
+ hosts->by_address = hashmap_free_with_destructor(hosts->by_address, etc_hosts_item_free);
+ hosts->by_name = hashmap_free_with_destructor(hosts->by_name, etc_hosts_item_by_name_free);
+ hosts->no_address = set_free_free(hosts->no_address);
}
-static const struct hash_ops etc_hosts_item_ops = {
- .hash = etc_hosts_item_hash_func,
- .compare = etc_hosts_item_compare_func,
-};
-
-static int add_item(Manager *m, int family, const union in_addr_union *address, char **names) {
+void manager_etc_hosts_flush(Manager *m) {
+ etc_hosts_free(&m->etc_hosts);
+ m->etc_hosts_mtime = USEC_INFINITY;
+}
- EtcHostsItem key = {
- .family = family,
- .address = *address,
- };
+static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
+ _cleanup_free_ char *address_str = NULL;
+ struct in_addr_data address = {};
+ bool found = false;
EtcHostsItem *item;
- char **n;
int r;
- assert(m);
- assert(address);
+ assert(hosts);
+ assert(line);
- r = in_addr_is_null(family, address);
+ r = extract_first_word(&line, &address_str, NULL, EXTRACT_RELAX);
if (r < 0)
- return r;
+ return log_error_errno(r, "/etc/hosts:%u: failed to extract address: %m", nr);
+ assert(r > 0); /* We already checked that the line is not empty, so it should contain *something* */
+
+ r = in_addr_ifindex_from_string_auto(address_str, &address.family, &address.address, NULL);
+ if (r < 0) {
+ log_warning_errno(r, "/etc/hosts:%u: address '%s' is invalid, ignoring: %m", nr, address_str);
+ return 0;
+ }
+
+ r = in_addr_is_null(address.family, &address.address);
+ if (r < 0) {
+ log_warning_errno(r, "/etc/hosts:%u: address '%s' is invalid, ignoring: %m", nr, address_str);
+ return 0;
+ }
if (r > 0)
/* This is an 0.0.0.0 or :: item, which we assume means that we shall map the specified hostname to
* nothing. */
item = NULL;
else {
- /* If this is a normal address, then, simply add entry mapping it to the specified names */
+ /* If this is a normal address, then simply add entry mapping it to the specified names */
- item = set_get(m->etc_hosts_by_address, &key);
- if (item) {
- r = strv_extend_strv(&item->names, names, true);
- if (r < 0)
- return log_oom();
- } else {
-
- r = set_ensure_allocated(&m->etc_hosts_by_address, &etc_hosts_item_ops);
+ item = hashmap_get(hosts->by_address, &address);
+ if (!item) {
+ r = hashmap_ensure_allocated(&hosts->by_address, &in_addr_data_hash_ops);
if (r < 0)
return log_oom();
@@ -116,11 +77,9 @@ static int add_item(Manager *m, int family, const union in_addr_union *address,
if (!item)
return log_oom();
- item->family = family;
- item->address = *address;
- item->names = names;
+ item->address = address;
- r = set_put(m->etc_hosts_by_address, item);
+ r = hashmap_put(hosts->by_address, &item->address, item);
if (r < 0) {
free(item);
return log_oom();
@@ -128,12 +87,51 @@ static int add_item(Manager *m, int family, const union in_addr_union *address,
}
}
- STRV_FOREACH(n, names) {
+ for (;;) {
+ _cleanup_free_ char *name = NULL;
EtcHostsItemByName *bn;
- bn = hashmap_get(m->etc_hosts_by_name, *n);
+ r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX);
+ if (r < 0)
+ return log_error_errno(r, "/etc/hosts:%u: couldn't extract host name: %m", nr);
+ if (r == 0)
+ break;
+
+ found = true;
+
+ r = dns_name_is_valid_ldh(name);
+ if (r <= 0) {
+ log_warning_errno(r, "/etc/hosts:%u: hostname \"%s\" is not valid, ignoring.", nr, name);
+ continue;
+ }
+
+ if (is_localhost(name))
+ /* Suppress the "localhost" line that is often seen */
+ continue;
+
+ if (!item) {
+ /* Optimize the case where we don't need to store any addresses, by storing
+ * only the name in a dedicated Set instead of the hashmap */
+
+ r = set_ensure_allocated(&hosts->no_address, &dns_name_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ r = set_put(hosts->no_address, name);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(name);
+ continue;
+ }
+
+ r = strv_extend(&item->names, name);
+ if (r < 0)
+ return log_oom();
+
+ bn = hashmap_get(hosts->by_name, name);
if (!bn) {
- r = hashmap_ensure_allocated(&m->etc_hosts_by_name, &dns_name_hash_ops);
+ r = hashmap_ensure_allocated(&hosts->by_name, &dns_name_hash_ops);
if (r < 0)
return log_oom();
@@ -141,103 +139,67 @@ static int add_item(Manager *m, int family, const union in_addr_union *address,
if (!bn)
return log_oom();
- bn->name = strdup(*n);
- if (!bn->name) {
- free(bn);
- return log_oom();
- }
-
- r = hashmap_put(m->etc_hosts_by_name, bn->name, bn);
+ r = hashmap_put(hosts->by_name, name, bn);
if (r < 0) {
- free(bn->name);
free(bn);
return log_oom();
}
+
+ bn->name = TAKE_PTR(name);
}
- if (item) {
- if (!GREEDY_REALLOC(bn->items, bn->n_allocated, bn->n_items+1))
- return log_oom();
+ if (!GREEDY_REALLOC(bn->addresses, bn->n_allocated, bn->n_addresses + 1))
+ return log_oom();
- bn->items[bn->n_items++] = item;
- }
+ bn->addresses[bn->n_addresses++] = &item->address;
}
+ if (!found)
+ log_warning("/etc/hosts:%u: line is missing any host names", nr);
+
return 0;
}
-static int parse_line(Manager *m, unsigned nr, const char *line) {
- _cleanup_free_ char *address = NULL;
- _cleanup_strv_free_ char **names = NULL;
- union in_addr_union in;
- bool suppressed = false;
- int family, r;
-
- assert(m);
- assert(line);
-
- r = extract_first_word(&line, &address, NULL, EXTRACT_RELAX);
- if (r < 0)
- return log_error_errno(r, "Couldn't extract address, in line /etc/hosts:%u.", nr);
- if (r == 0) {
- log_error("Premature end of line, in line /etc/hosts:%u.", nr);
- return -EINVAL;
- }
-
- r = in_addr_from_string_auto(address, &family, &in);
- if (r < 0)
- return log_error_errno(r, "Address '%s' is invalid, in line /etc/hosts:%u.", address, nr);
+int etc_hosts_parse(EtcHosts *hosts, FILE *f) {
+ _cleanup_(etc_hosts_free) EtcHosts t = {};
+ unsigned nr = 0;
+ int r;
for (;;) {
- _cleanup_free_ char *name = NULL;
+ _cleanup_free_ char *line = NULL;
+ char *l;
- r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX);
+ r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
- return log_error_errno(r, "Couldn't extract host name, in line /etc/hosts:%u.", nr);
+ return log_error_errno(r, "Failed to read /etc/hosts: %m");
if (r == 0)
break;
- r = dns_name_is_valid(name);
- if (r <= 0)
- return log_error_errno(r, "Hostname %s is not valid, ignoring, in line /etc/hosts:%u.", name, nr);
+ nr++;
- if (is_localhost(name)) {
- /* Suppress the "localhost" line that is often seen */
- suppressed = true;
+ l = strchr(line, '#');
+ if (l)
+ *l = '\0';
+
+ l = strstrip(line);
+ if (isempty(l))
continue;
- }
- r = strv_push(&names, name);
+ r = parse_line(&t, nr, l);
if (r < 0)
- return log_oom();
-
- name = NULL;
- }
-
- if (strv_isempty(names)) {
-
- if (suppressed)
- return 0;
-
- log_error("Line is missing any host names, in line /etc/hosts:%u.", nr);
- return -EINVAL;
+ return r;
}
- /* Takes possession of the names strv */
- r = add_item(m, family, &in, names);
- if (r < 0)
- return r;
-
- names = NULL;
- return r;
+ etc_hosts_free(hosts);
+ *hosts = t;
+ t = (EtcHosts) {}; /* prevent cleanup */
+ return 0;
}
-int manager_etc_hosts_read(Manager *m) {
+static int manager_etc_hosts_read(Manager *m) {
_cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
struct stat st;
usec_t ts;
- unsigned nr = 0;
int r;
assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &ts) >= 0);
@@ -250,12 +212,11 @@ int manager_etc_hosts_read(Manager *m) {
if (m->etc_hosts_mtime != USEC_INFINITY) {
if (stat("/etc/hosts", &st) < 0) {
- if (errno == ENOENT) {
- r = 0;
- goto clear;
- }
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to stat /etc/hosts: %m");
- return log_error_errno(errno, "Failed to stat /etc/hosts: %m");
+ manager_etc_hosts_flush(m);
+ return 0;
}
/* Did the mtime change? If not, there's no point in re-reading the file. */
@@ -265,12 +226,11 @@ int manager_etc_hosts_read(Manager *m) {
f = fopen("/etc/hosts", "re");
if (!f) {
- if (errno == ENOENT) {
- r = 0;
- goto clear;
- }
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to open /etc/hosts: %m");
- return log_error_errno(errno, "Failed to open /etc/hosts: %m");
+ manager_etc_hosts_flush(m);
+ return 0;
}
/* Take the timestamp at the beginning of processing, so that any changes made later are read on the next
@@ -279,38 +239,20 @@ int manager_etc_hosts_read(Manager *m) {
if (r < 0)
return log_error_errno(errno, "Failed to fstat() /etc/hosts: %m");
- manager_etc_hosts_flush(m);
-
- FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) {
- char *l;
-
- nr++;
-
- l = strstrip(line);
- if (isempty(l))
- continue;
- if (l[0] == '#')
- continue;
-
- r = parse_line(m, nr, l);
- if (r == -ENOMEM) /* On OOM we abandon the half-built-up structure. All other errors we ignore and proceed */
- goto clear;
- }
+ r = etc_hosts_parse(&m->etc_hosts, f);
+ if (r < 0)
+ return r;
m->etc_hosts_mtime = timespec_load(&st.st_mtim);
m->etc_hosts_last = ts;
return 1;
-
-clear:
- manager_etc_hosts_flush(m);
- return r;
}
int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
bool found_a = false, found_aaaa = false;
+ struct in_addr_data k = {};
EtcHostsItemByName *bn;
- EtcHostsItem k = {};
DnsResourceKey *t;
const char *name;
unsigned i;
@@ -320,9 +262,10 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
assert(q);
assert(answer);
- r = manager_etc_hosts_read(m);
- if (r < 0)
- return r;
+ if (!m->read_etc_hosts)
+ return 0;
+
+ (void) manager_etc_hosts_read(m);
name = dns_question_first_name(q);
if (!name)
@@ -333,7 +276,7 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
EtcHostsItem *item;
DnsResourceKey *found_ptr = NULL;
- item = set_get(m->etc_hosts_by_address, &k);
+ item = hashmap_get(m->etc_hosts.by_address, &k);
if (!item)
return 0;
@@ -382,13 +325,16 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
return 1;
}
- bn = hashmap_get(m->etc_hosts_by_name, name);
- if (!bn)
- return 0;
-
- r = dns_answer_reserve(answer, bn->n_items);
- if (r < 0)
- return r;
+ bn = hashmap_get(m->etc_hosts.by_name, name);
+ if (bn) {
+ r = dns_answer_reserve(answer, bn->n_addresses);
+ if (r < 0)
+ return r;
+ } else {
+ /* Check if name was listed with no address. If yes, continue to return an answer. */
+ if (!set_contains(m->etc_hosts.no_address, name))
+ return 0;
+ }
DNS_QUESTION_FOREACH(t, q) {
if (!IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_ANY))
@@ -411,14 +357,14 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
break;
}
- for (i = 0; i < bn->n_items; i++) {
+ for (i = 0; bn && i < bn->n_addresses; i++) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- if ((!found_a && bn->items[i]->family == AF_INET) ||
- (!found_aaaa && bn->items[i]->family == AF_INET6))
+ if ((!found_a && bn->addresses[i]->family == AF_INET) ||
+ (!found_aaaa && bn->addresses[i]->family == AF_INET6))
continue;
- r = dns_resource_record_new_address(&rr, bn->items[i]->family, &bn->items[i]->address, bn->name);
+ r = dns_resource_record_new_address(&rr, bn->addresses[i]->family, &bn->addresses[i]->address, bn->name);
if (r < 0)
return r;
diff --git a/src/resolve/resolved-etc-hosts.h b/src/resolve/resolved-etc-hosts.h
index b8e04c393b..caf32a53dd 100644
--- a/src/resolve/resolved-etc-hosts.h
+++ b/src/resolve/resolved-etc-hosts.h
@@ -5,6 +5,21 @@
#include "resolved-dns-question.h"
#include "resolved-dns-answer.h"
+typedef struct EtcHostsItem {
+ struct in_addr_data address;
+
+ char **names;
+} EtcHostsItem;
+
+typedef struct EtcHostsItemByName {
+ char *name;
+
+ struct in_addr_data **addresses;
+ size_t n_addresses, n_allocated;
+} EtcHostsItemByName;
+
+int etc_hosts_parse(EtcHosts *hosts, FILE *f);
+void etc_hosts_free(EtcHosts *hosts);
+
void manager_etc_hosts_flush(Manager *m);
-int manager_etc_hosts_read(Manager *m);
int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer);
diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf
index 648ca4d8a2..9b9290b727 100644
--- a/src/resolve/resolved-gperf.gperf
+++ b/src/resolve/resolved-gperf.gperf
@@ -26,3 +26,4 @@ Resolve.DNSSEC, config_parse_dnssec_mode, 0,
Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Manager, dns_over_tls_mode)
Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache)
Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode)
+Resolve.ReadEtcHosts, config_parse_bool, 0, offsetof(Manager, read_etc_hosts)
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
index b1581740d8..96093fff53 100644
--- a/src/resolve/resolved-link-bus.c
+++ b/src/resolve/resolved-link-bus.c
@@ -107,6 +107,31 @@ static int property_get_domains(
return sd_bus_message_close_container(reply);
}
+static int property_get_default_route(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+
+ assert(reply);
+ assert(l);
+
+ /* Return what is configured, if there's something configured */
+ if (l->default_route >= 0)
+ return sd_bus_message_append(reply, "b", l->default_route);
+
+ /* Otherwise report what is in effect */
+ if (l->unicast_scope)
+ return sd_bus_message_append(reply, "b", dns_scope_is_default_route(l->unicast_scope));
+
+ return sd_bus_message_append(reply, "b", false);
+}
+
static int property_get_scopes_mask(
sd_bus *bus,
const char *path,
@@ -346,6 +371,31 @@ clear:
return r;
}
+int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ int r, b;
+
+ assert(message);
+ assert(l);
+
+ r = verify_unmanaged_link(l, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (l->default_route != b) {
+ l->default_route = b;
+
+ (void) link_save_user(l);
+ (void) manager_write_resolv_conf(l->manager);
+ }
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Link *l = userdata;
ResolveSupport mode;
@@ -550,6 +600,7 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
SD_BUS_PROPERTY("CurrentDNSServer", "(iay)", property_get_current_dns_server, offsetof(Link, current_dns_server), 0),
SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
+ SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0),
SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0),
SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Link, mdns_support), 0),
SD_BUS_PROPERTY("DNSOverTLS", "s", property_get_dns_over_tls_mode, 0, 0),
@@ -559,6 +610,7 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0),
SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, 0),
+ SD_BUS_METHOD("SetDefaultRoute", "b", NULL, bus_link_method_set_default_route, 0),
SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0),
SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0),
SD_BUS_METHOD("SetDNSOverTLS", "s", NULL, bus_link_method_set_dns_over_tls, 0),
diff --git a/src/resolve/resolved-link-bus.h b/src/resolve/resolved-link-bus.h
index 6b8092fdb4..671725101e 100644
--- a/src/resolve/resolved-link-bus.h
+++ b/src/resolve/resolved-link-bus.h
@@ -13,6 +13,7 @@ int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index ff2be12415..44f70aceaa 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -6,6 +6,7 @@
#include "sd-network.h"
#include "alloc-util.h"
+#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
#include "missing.h"
@@ -16,6 +17,7 @@
#include "resolved-mdns.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
int link_new(Manager *m, Link **ret, int ifindex) {
_cleanup_(link_freep) Link *l = NULL;
@@ -28,16 +30,19 @@ int link_new(Manager *m, Link **ret, int ifindex) {
if (r < 0)
return r;
- l = new0(Link, 1);
+ l = new(Link, 1);
if (!l)
return -ENOMEM;
- l->ifindex = ifindex;
- l->llmnr_support = RESOLVE_SUPPORT_YES;
- l->mdns_support = RESOLVE_SUPPORT_NO;
- l->dnssec_mode = _DNSSEC_MODE_INVALID;
- l->dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID;
- l->operstate = IF_OPER_UNKNOWN;
+ *l = (Link) {
+ .ifindex = ifindex,
+ .default_route = -1,
+ .llmnr_support = RESOLVE_SUPPORT_YES,
+ .mdns_support = RESOLVE_SUPPORT_NO,
+ .dnssec_mode = _DNSSEC_MODE_INVALID,
+ .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
+ .operstate = IF_OPER_UNKNOWN,
+ };
if (asprintf(&l->state_file, "/run/systemd/resolve/netif/%i", ifindex) < 0)
return -ENOMEM;
@@ -58,6 +63,7 @@ int link_new(Manager *m, Link **ret, int ifindex) {
void link_flush_settings(Link *l) {
assert(l);
+ l->default_route = -1;
l->llmnr_support = RESOLVE_SUPPORT_YES;
l->mdns_support = RESOLVE_SUPPORT_NO;
l->dnssec_mode = _DNSSEC_MODE_INVALID;
@@ -295,6 +301,27 @@ clear:
return r;
}
+static int link_update_default_route(Link *l) {
+ int r;
+
+ assert(l);
+
+ r = sd_network_link_get_dns_default_route(l->ifindex);
+ if (r == -ENODATA) {
+ r = 0;
+ goto clear;
+ }
+ if (r < 0)
+ goto clear;
+
+ l->default_route = r > 0;
+ return 0;
+
+clear:
+ l->default_route = -1;
+ return r;
+}
+
static int link_update_llmnr_support(Link *l) {
_cleanup_free_ char *b = NULL;
int r;
@@ -611,6 +638,10 @@ static void link_read_settings(Link *l) {
r = link_update_search_domains(l);
if (r < 0)
log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
+
+ r = link_update_default_route(l);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read default route setting for interface %s, proceeding anyway: %m", l->name);
}
int link_update(Link *l) {
@@ -1107,7 +1138,8 @@ static bool link_needs_save(Link *l) {
if (l->llmnr_support != RESOLVE_SUPPORT_YES ||
l->mdns_support != RESOLVE_SUPPORT_NO ||
- l->dnssec_mode != _DNSSEC_MODE_INVALID)
+ l->dnssec_mode != _DNSSEC_MODE_INVALID ||
+ l->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
return true;
if (l->dns_servers ||
@@ -1117,6 +1149,9 @@ static bool link_needs_save(Link *l) {
if (!set_isempty(l->dnssec_negative_trust_anchors))
return true;
+ if (l->default_route >= 0)
+ return true;
+
return false;
}
@@ -1159,6 +1194,9 @@ int link_save_user(Link *l) {
if (v)
fprintf(f, "DNSSEC=%s\n", v);
+ if (l->default_route >= 0)
+ fprintf(f, "DEFAULT_ROUTE=%s\n", yes_no(l->default_route));
+
if (l->dns_servers) {
DnsServer *server;
@@ -1240,7 +1278,8 @@ int link_load_user(Link *l) {
*dnssec = NULL,
*servers = NULL,
*domains = NULL,
- *ntas = NULL;
+ *ntas = NULL,
+ *default_route = NULL;
ResolveSupport s;
const char *p;
@@ -1257,14 +1296,14 @@ int link_load_user(Link *l) {
if (l->is_managed)
return 0; /* if the device is managed, then networkd is our configuration source, not the bus API */
- r = parse_env_file(NULL, l->state_file, NEWLINE,
+ r = parse_env_file(NULL, l->state_file,
"LLMNR", &llmnr,
"MDNS", &mdns,
"DNSSEC", &dnssec,
"SERVERS", &servers,
"DOMAINS", &domains,
"NTAS", &ntas,
- NULL);
+ "DEFAULT_ROUTE", &default_route);
if (r == -ENOENT)
return 0;
if (r < 0)
@@ -1281,6 +1320,10 @@ int link_load_user(Link *l) {
if (s >= 0)
l->mdns_support = s;
+ r = parse_boolean(default_route);
+ if (r >= 0)
+ l->default_route = r;
+
/* If we can't recognize the DNSSEC setting, then set it to invalid, so that the daemon default is used. */
l->dnssec_mode = dnssec_mode_from_string(dnssec);
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index 063d3f35c3..f95ea37a4f 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -16,8 +16,8 @@ typedef struct LinkAddress LinkAddress;
#include "resolved-dns-server.h"
#include "resolved-manager.h"
-#define LINK_SEARCH_DOMAINS_MAX 32
-#define LINK_DNS_SERVERS_MAX 32
+#define LINK_SEARCH_DOMAINS_MAX 256
+#define LINK_DNS_SERVERS_MAX 256
struct LinkAddress {
Link *link;
@@ -51,6 +51,8 @@ struct Link {
LIST_HEAD(DnsSearchDomain, search_domains);
unsigned n_search_domains;
+ int default_route;
+
ResolveSupport llmnr_support;
ResolveSupport mdns_support;
DnsOverTlsMode dns_over_tls_mode;
diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c
index 2f2a1a15ec..dfa55c577c 100644
--- a/src/resolve/resolved-llmnr.c
+++ b/src/resolve/resolved-llmnr.c
@@ -115,7 +115,7 @@ int manager_llmnr_ipv4_udp_fd(Manager *m) {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(LLMNR_PORT),
};
- static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
+ _cleanup_close_ int s = -1;
int r;
assert(m);
@@ -123,90 +123,66 @@ int manager_llmnr_ipv4_udp_fd(Manager *m) {
if (m->llmnr_ipv4_udp_fd >= 0)
return m->llmnr_ipv4_udp_fd;
- m->llmnr_ipv4_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->llmnr_ipv4_udp_fd < 0)
+ s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (s < 0)
return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to create socket: %m");
/* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_TTL: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_TTL, 255);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_TTL: %m");
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_TTL: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_TTL, 255);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_TTL: %m");
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_LOOP: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_LOOP, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_LOOP: %m");
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_PKTINFO: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_PKTINFO: %m");
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_RECVTTL: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_RECVTTL, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_RECVTTL: %m");
/* Disable Don't-Fragment bit in the IP header */
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_MTU_DISCOVER: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_MTU_DISCOVER: %m");
/* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
- r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in));
+ r = bind(s, &sa.sa, sizeof(sa.in));
if (r < 0) {
- if (errno != EADDRINUSE) {
- r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m");
- goto fail;
- }
+ if (errno != EADDRINUSE)
+ return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m");
log_warning("LLMNR-IPv4(UDP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
/* try again with SO_REUSEADDR */
- r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
-
- r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m");
+
+ r = bind(s, &sa.sa, sizeof(sa.in));
+ if (r < 0)
+ return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m");
} else {
/* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
- r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m");
}
- r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, m->llmnr_ipv4_udp_fd, EPOLLIN, on_llmnr_packet, m);
+ r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, s, EPOLLIN, on_llmnr_packet, m);
if (r < 0)
- goto fail;
+ return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to create event source: %m");
(void) sd_event_source_set_description(m->llmnr_ipv4_udp_event_source, "llmnr-ipv4-udp");
- return m->llmnr_ipv4_udp_fd;
-
-fail:
- m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd);
- return r;
+ return m->llmnr_ipv4_udp_fd = TAKE_FD(s);
}
int manager_llmnr_ipv6_udp_fd(Manager *m) {
@@ -214,7 +190,7 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(LLMNR_PORT),
};
- static const int one = 1, ttl = 255;
+ _cleanup_close_ int s = -1;
int r;
assert(m);
@@ -222,104 +198,83 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) {
if (m->llmnr_ipv6_udp_fd >= 0)
return m->llmnr_ipv6_udp_fd;
- m->llmnr_ipv6_udp_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->llmnr_ipv6_udp_fd < 0)
+ s = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (s < 0)
return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to create socket: %m");
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_UNICAST_HOPS: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_UNICAST_HOPS: %m");
/* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_HOPS: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_HOPS: %m");
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_LOOP: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_LOOP: %m");
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_V6ONLY: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_V6ONLY: %m");
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVPKTINFO: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVPKTINFO: %m");
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVHOPLIMIT: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVHOPLIMIT: %m");
/* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
- r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6));
+ r = bind(s, &sa.sa, sizeof(sa.in6));
if (r < 0) {
- if (errno != EADDRINUSE) {
- r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m");
- goto fail;
- }
+ if (errno != EADDRINUSE)
+ return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m");
log_warning("LLMNR-IPv6(UDP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
/* try again with SO_REUSEADDR */
- r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
-
- r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m");
+
+ r = bind(s, &sa.sa, sizeof(sa.in6));
+ if (r < 0)
+ return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m");
} else {
/* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
- r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m");
}
- r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, m->llmnr_ipv6_udp_fd, EPOLLIN, on_llmnr_packet, m);
+ r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, s, EPOLLIN, on_llmnr_packet, m);
if (r < 0)
- goto fail;
+ return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to create event source: %m");
(void) sd_event_source_set_description(m->llmnr_ipv6_udp_event_source, "llmnr-ipv6-udp");
- return m->llmnr_ipv6_udp_fd;
-
-fail:
- m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
- return r;
+ return m->llmnr_ipv6_udp_fd = TAKE_FD(s);
}
static int on_llmnr_stream_packet(DnsStream *s) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsScope *scope;
assert(s);
- assert(s->read_packet);
- scope = manager_find_scope(s->manager, s->read_packet);
+ p = dns_stream_take_read_packet(s);
+ assert(p);
+
+ scope = manager_find_scope(s->manager, p);
if (!scope)
log_debug("Got LLMNR TCP packet on unknown scope. Ignoring.");
- else if (dns_packet_validate_query(s->read_packet) > 0) {
- log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
+ else if (dns_packet_validate_query(p) > 0) {
+ log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(p));
- dns_scope_process_query(scope, s, s->read_packet);
+ dns_scope_process_query(scope, s, p);
} else
log_debug("Invalid LLMNR TCP packet, ignoring.");
@@ -355,7 +310,7 @@ int manager_llmnr_ipv4_tcp_fd(Manager *m) {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(LLMNR_PORT),
};
- static const int one = 1, pmtu = IP_PMTUDISC_DONT;
+ _cleanup_close_ int s = -1;
int r;
assert(m);
@@ -363,84 +318,62 @@ int manager_llmnr_ipv4_tcp_fd(Manager *m) {
if (m->llmnr_ipv4_tcp_fd >= 0)
return m->llmnr_ipv4_tcp_fd;
- m->llmnr_ipv4_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->llmnr_ipv4_tcp_fd < 0)
+ s = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (s < 0)
return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to create socket: %m");
/* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
- r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_TTL: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_TTL, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_TTL: %m");
- r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_PKTINFO: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_PKTINFO: %m");
- r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_RECVTTL: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_RECVTTL, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_RECVTTL: %m");
/* Disable Don't-Fragment bit in the IP header */
- r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_MTU_DISCOVER: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_MTU_DISCOVER: %m");
/* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
- r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in));
+ r = bind(s, &sa.sa, sizeof(sa.in));
if (r < 0) {
- if (errno != EADDRINUSE) {
- r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m");
- goto fail;
- }
+ if (errno != EADDRINUSE)
+ return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m");
log_warning("LLMNR-IPv4(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
/* try again with SO_REUSEADDR */
- r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
-
- r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
+
+ r = bind(s, &sa.sa, sizeof(sa.in));
+ if (r < 0)
+ return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m");
} else {
/* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
- r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
}
- r = listen(m->llmnr_ipv4_tcp_fd, SOMAXCONN);
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to listen the stream: %m");
- goto fail;
- }
+ r = listen(s, SOMAXCONN);
+ if (r < 0)
+ return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to listen the stream: %m");
- r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, m->llmnr_ipv4_tcp_fd, EPOLLIN, on_llmnr_stream, m);
+ r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, s, EPOLLIN, on_llmnr_stream, m);
if (r < 0)
- goto fail;
+ return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to create event source: %m");
(void) sd_event_source_set_description(m->llmnr_ipv4_tcp_event_source, "llmnr-ipv4-tcp");
- return m->llmnr_ipv4_tcp_fd;
-
-fail:
- m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
- return r;
+ return m->llmnr_ipv4_tcp_fd = TAKE_FD(s);
}
int manager_llmnr_ipv6_tcp_fd(Manager *m) {
@@ -448,7 +381,7 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(LLMNR_PORT),
};
- static const int one = 1;
+ _cleanup_close_ int s = -1;
int r;
assert(m);
@@ -456,81 +389,59 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) {
if (m->llmnr_ipv6_tcp_fd >= 0)
return m->llmnr_ipv6_tcp_fd;
- m->llmnr_ipv6_tcp_fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->llmnr_ipv6_tcp_fd < 0)
+ s = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (s < 0)
return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to create socket: %m");
/* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
- r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_UNICAST_HOPS: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_UNICAST_HOPS: %m");
- r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_V6ONLY: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_V6ONLY: %m");
- r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVPKTINFO: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVPKTINFO: %m");
- r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVHOPLIMIT: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVHOPLIMIT: %m");
/* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
- r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6));
+ r = bind(s, &sa.sa, sizeof(sa.in6));
if (r < 0) {
- if (errno != EADDRINUSE) {
- r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m");
- goto fail;
- }
+ if (errno != EADDRINUSE)
+ return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m");
log_warning("LLMNR-IPv6(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
/* try again with SO_REUSEADDR */
- r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
-
- r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
+
+ r = bind(s, &sa.sa, sizeof(sa.in6));
+ if (r < 0)
+ return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m");
} else {
/* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
- r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
}
- r = listen(m->llmnr_ipv6_tcp_fd, SOMAXCONN);
- if (r < 0) {
- r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to listen the stream: %m");
- goto fail;
- }
+ r = listen(s, SOMAXCONN);
+ if (r < 0)
+ return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to listen the stream: %m");
- r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, m->llmnr_ipv6_tcp_fd, EPOLLIN, on_llmnr_stream, m);
+ r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, s, EPOLLIN, on_llmnr_stream, m);
if (r < 0)
- goto fail;
+ return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to create event source: %m");
(void) sd_event_source_set_description(m->llmnr_ipv6_tcp_event_source, "llmnr-ipv6-tcp");
- return m->llmnr_ipv6_tcp_fd;
-
-fail:
- m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
- return r;
+ return m->llmnr_ipv6_tcp_fd = TAKE_FD(s);
}
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 01372fc66b..b7dc09ae37 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -14,9 +14,10 @@
#include "dirent-util.h"
#include "dns-domain.h"
#include "fd-util.h"
-#include "fileio-label.h"
+#include "fileio.h"
#include "hostname-util.h"
#include "io-util.h"
+#include "missing_network.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "ordered-set.h"
@@ -24,8 +25,8 @@
#include "random-util.h"
#include "resolved-bus.h"
#include "resolved-conf.h"
-#include "resolved-dnssd.h"
#include "resolved-dns-stub.h"
+#include "resolved-dnssd.h"
#include "resolved-etc-hosts.h"
#include "resolved-llmnr.h"
#include "resolved-manager.h"
@@ -200,19 +201,19 @@ static int manager_rtnl_listen(Manager *m) {
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, manager_process_link, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWLINK, manager_process_link, NULL, m, "resolve-NEWLINK");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, manager_process_link, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELLINK, manager_process_link, NULL, m, "resolve-DELLINK");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, manager_process_address, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWADDR, manager_process_address, NULL, m, "resolve-NEWADDR");
if (r < 0)
return r;
- r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, manager_process_address, m);
+ r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELADDR, manager_process_address, NULL, m, "resolve-DELADDR");
if (r < 0)
return r;
@@ -333,13 +334,12 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
return log_debug_errno(r, "Can't determine system hostname: %m");
p = h;
- r = dns_label_unescape(&p, label, sizeof label);
+ r = dns_label_unescape(&p, label, sizeof label, 0);
if (r < 0)
return log_error_errno(r, "Failed to unescape host name: %m");
- if (r == 0) {
- log_error("Couldn't find a single label in hostname.");
- return -EINVAL;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Couldn't find a single label in hostname.");
#if HAVE_LIBIDN2
r = idn2_to_unicode_8z8z(label, &utf8, 0);
@@ -356,10 +356,9 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
if (k > 0)
r = k;
- if (!utf8_is_valid(label)) {
- log_error("System hostname is not UTF-8 clean.");
- return -EINVAL;
- }
+ if (!utf8_is_valid(label))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "System hostname is not UTF-8 clean.");
decoded = label;
#else
decoded = label; /* no decoding */
@@ -369,12 +368,11 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
if (r < 0)
return log_error_errno(r, "Failed to escape host name: %m");
- if (is_localhost(n)) {
- log_debug("System hostname is 'localhost', ignoring.");
- return -EINVAL;
- }
+ if (is_localhost(n))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "System hostname is 'localhost', ignoring.");
- r = dns_name_concat(n, "local", mdns_hostname);
+ r = dns_name_concat(n, "local", 0, mdns_hostname);
if (r < 0)
return log_error_errno(r, "Failed to determine mDNS hostname: %m");
@@ -406,7 +404,7 @@ static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname,
assert(mdns_hostname);
p = fallback_hostname();
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, 0);
if (r < 0)
return log_error_errno(r, "Failed to unescape fallback host name: %m");
@@ -416,7 +414,7 @@ static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname,
if (r < 0)
return log_error_errno(r, "Failed to escape fallback hostname: %m");
- r = dns_name_concat(n, "local", &m);
+ r = dns_name_concat(n, "local", 0, &m);
if (r < 0)
return log_error_errno(r, "Failed to concatenate mDNS hostname: %m");
@@ -562,25 +560,33 @@ int manager_new(Manager **ret) {
assert(ret);
- m = new0(Manager, 1);
+ m = new(Manager, 1);
if (!m)
return -ENOMEM;
- m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
- m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
- m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1;
- m->dns_stub_udp_fd = m->dns_stub_tcp_fd = -1;
- m->hostname_fd = -1;
-
- m->llmnr_support = RESOLVE_SUPPORT_YES;
- m->mdns_support = RESOLVE_SUPPORT_YES;
- m->dnssec_mode = DEFAULT_DNSSEC_MODE;
- m->dns_over_tls_mode = DEFAULT_DNS_OVER_TLS_MODE;
- m->enable_cache = true;
- m->dns_stub_listener_mode = DNS_STUB_LISTENER_UDP;
- m->read_resolv_conf = true;
- m->need_builtin_fallbacks = true;
- m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY;
+ *m = (Manager) {
+ .llmnr_ipv4_udp_fd = -1,
+ .llmnr_ipv6_udp_fd = -1,
+ .llmnr_ipv4_tcp_fd = -1,
+ .llmnr_ipv6_tcp_fd = -1,
+ .mdns_ipv4_fd = -1,
+ .mdns_ipv6_fd = -1,
+ .dns_stub_udp_fd = -1,
+ .dns_stub_tcp_fd = -1,
+ .hostname_fd = -1,
+
+ .llmnr_support = RESOLVE_SUPPORT_YES,
+ .mdns_support = RESOLVE_SUPPORT_YES,
+ .dnssec_mode = DEFAULT_DNSSEC_MODE,
+ .dns_over_tls_mode = DEFAULT_DNS_OVER_TLS_MODE,
+ .enable_cache = true,
+ .dns_stub_listener_mode = DNS_STUB_LISTENER_YES,
+ .read_resolv_conf = true,
+ .need_builtin_fallbacks = true,
+ .etc_hosts_last = USEC_INFINITY,
+ .etc_hosts_mtime = USEC_INFINITY,
+ .read_etc_hosts = true,
+ };
r = dns_trust_anchor_load(&m->trust_anchor);
if (r < 0)
@@ -594,10 +600,10 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
- sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
- sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+ (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
+ (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
- sd_event_set_watchdog(m->event, true);
+ (void) sd_event_set_watchdog(m->event, true);
r = manager_watch_hostname(m);
if (r < 0)
@@ -683,7 +689,6 @@ Manager *manager_free(Manager *m) {
manager_mdns_stop(m);
manager_dns_stub_stop(m);
- sd_bus_slot_unref(m->prepare_for_sleep_slot);
sd_bus_unref(m->bus);
sd_event_source_unref(m->sigusr1_event_source);
@@ -723,9 +728,16 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
+ EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */];
} control;
union sockaddr_union sa;
- struct msghdr mh = {};
- struct cmsghdr *cmsg;
struct iovec iov;
+ struct msghdr mh = {
+ .msg_name = &sa.sa,
+ .msg_namelen = sizeof(sa),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
ssize_t ms, l;
int r;
@@ -741,25 +753,17 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
if (r < 0)
return r;
- iov.iov_base = DNS_PACKET_DATA(p);
- iov.iov_len = p->allocated;
-
- mh.msg_name = &sa.sa;
- mh.msg_namelen = sizeof(sa);
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
- mh.msg_control = &control;
- mh.msg_controllen = sizeof(control);
+ iov = IOVEC_MAKE(DNS_PACKET_DATA(p), p->allocated);
l = recvmsg(fd, &mh, 0);
- if (l == 0)
- return 0;
if (l < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
return 0;
return -errno;
}
+ if (l == 0)
+ return 0;
assert(!(mh.msg_flags & MSG_CTRUNC));
assert(!(mh.msg_flags & MSG_TRUNC));
@@ -909,15 +913,18 @@ static int manager_ipv4_send(
uint16_t port,
const struct in_addr *source,
DnsPacket *p) {
- union sockaddr_union sa = {
- .in.sin_family = AF_INET,
- };
union {
struct cmsghdr header; /* For alignment */
uint8_t buffer[CMSG_SPACE(sizeof(struct in_pktinfo))];
- } control;
- struct msghdr mh = {};
+ } control = {};
+ union sockaddr_union sa;
struct iovec iov;
+ struct msghdr mh = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_name = &sa.sa,
+ .msg_namelen = sizeof(sa.in),
+ };
assert(m);
assert(fd >= 0);
@@ -925,23 +932,18 @@ static int manager_ipv4_send(
assert(port > 0);
assert(p);
- iov.iov_base = DNS_PACKET_DATA(p);
- iov.iov_len = p->size;
-
- sa.in.sin_addr = *destination;
- sa.in.sin_port = htobe16(port),
+ iov = IOVEC_MAKE(DNS_PACKET_DATA(p), p->size);
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
- mh.msg_name = &sa.sa;
- mh.msg_namelen = sizeof(sa.in);
+ sa = (union sockaddr_union) {
+ .in.sin_family = AF_INET,
+ .in.sin_addr = *destination,
+ .in.sin_port = htobe16(port),
+ };
if (ifindex > 0) {
struct cmsghdr *cmsg;
struct in_pktinfo *pi;
- zero(control);
-
mh.msg_control = &control;
mh.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo));
@@ -969,15 +971,18 @@ static int manager_ipv6_send(
const struct in6_addr *source,
DnsPacket *p) {
- union sockaddr_union sa = {
- .in6.sin6_family = AF_INET6,
- };
union {
struct cmsghdr header; /* For alignment */
uint8_t buffer[CMSG_SPACE(sizeof(struct in6_pktinfo))];
- } control;
- struct msghdr mh = {};
+ } control = {};
+ union sockaddr_union sa;
struct iovec iov;
+ struct msghdr mh = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_name = &sa.sa,
+ .msg_namelen = sizeof(sa.in6),
+ };
assert(m);
assert(fd >= 0);
@@ -985,24 +990,19 @@ static int manager_ipv6_send(
assert(port > 0);
assert(p);
- iov.iov_base = DNS_PACKET_DATA(p);
- iov.iov_len = p->size;
+ iov = IOVEC_MAKE(DNS_PACKET_DATA(p), p->size);
- sa.in6.sin6_addr = *destination;
- sa.in6.sin6_port = htobe16(port),
- sa.in6.sin6_scope_id = ifindex;
-
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
- mh.msg_name = &sa.sa;
- mh.msg_namelen = sizeof(sa.in6);
+ sa = (union sockaddr_union) {
+ .in6.sin6_family = AF_INET6,
+ .in6.sin6_addr = *destination,
+ .in6.sin6_port = htobe16(port),
+ .in6.sin6_scope_id = ifindex,
+ };
if (ifindex > 0) {
struct cmsghdr *cmsg;
struct in6_pktinfo *pi;
- zero(control);
-
mh.msg_control = &control;
mh.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
@@ -1040,9 +1040,9 @@ int manager_send(
log_debug("Sending %s packet with id %" PRIu16 " on interface %i/%s.", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
if (family == AF_INET)
- return manager_ipv4_send(m, fd, ifindex, &destination->in, port, &source->in, p);
+ return manager_ipv4_send(m, fd, ifindex, &destination->in, port, source ? &source->in : NULL, p);
if (family == AF_INET6)
- return manager_ipv6_send(m, fd, ifindex, &destination->in6, port, &source->in6, p);
+ return manager_ipv6_send(m, fd, ifindex, &destination->in6, port, source ? &source->in6 : NULL, p);
return -EAFNOSUPPORT;
}
@@ -1148,7 +1148,7 @@ int manager_next_hostname(Manager *m) {
if (r < 0)
return r;
- r = dns_name_concat(h, "local", &k);
+ r = dns_name_concat(h, "local", 0, &k);
if (r < 0)
return r;
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index d94b2888ab..06c76f6014 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "sd-event.h"
#include "sd-netlink.h"
#include "sd-network.h"
@@ -21,8 +20,14 @@ typedef struct Manager Manager;
#include "resolved-dns-trust-anchor.h"
#include "resolved-link.h"
-#define MANAGER_SEARCH_DOMAINS_MAX 32
-#define MANAGER_DNS_SERVERS_MAX 32
+#define MANAGER_SEARCH_DOMAINS_MAX 256
+#define MANAGER_DNS_SERVERS_MAX 256
+
+typedef struct EtcHosts {
+ Hashmap *by_address;
+ Hashmap *by_name;
+ Set *no_address;
+} EtcHosts;
struct Manager {
sd_event *event;
@@ -107,9 +112,6 @@ struct Manager {
int hostname_fd;
sd_event_source *hostname_event_source;
- /* Watch for system suspends */
- sd_bus_slot *prepare_for_sleep_slot;
-
sd_event_source *sigusr1_event_source;
sd_event_source *sigusr2_event_source;
sd_event_source *sigrtmin1_event_source;
@@ -118,9 +120,9 @@ struct Manager {
unsigned n_dnssec_verdict[_DNSSEC_VERDICT_MAX];
/* Data from /etc/hosts */
- Set* etc_hosts_by_address;
- Hashmap* etc_hosts_by_name;
+ EtcHosts etc_hosts;
usec_t etc_hosts_last, etc_hosts_mtime;
+ bool read_etc_hosts;
/* Local DNS stub on 127.0.0.53:53 */
int dns_stub_udp_fd;
diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c
index 71a30ae8f8..89c2497d32 100644
--- a/src/resolve/resolved-mdns.c
+++ b/src/resolve/resolved-mdns.c
@@ -53,50 +53,41 @@ eaddrinuse:
return 0;
}
-static int mdns_rr_compare(const void *a, const void *b) {
- DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
+static int mdns_rr_compare(DnsResourceRecord * const *a, DnsResourceRecord * const *b) {
+ DnsResourceRecord *x = *(DnsResourceRecord **) a, *y = *(DnsResourceRecord **) b;
size_t m;
int r;
assert(x);
- assert(*x);
assert(y);
- assert(*y);
- if (CLEAR_CACHE_FLUSH((*x)->key->class) < CLEAR_CACHE_FLUSH((*y)->key->class))
- return -1;
- else if (CLEAR_CACHE_FLUSH((*x)->key->class) > CLEAR_CACHE_FLUSH((*y)->key->class))
- return 1;
+ r = CMP(CLEAR_CACHE_FLUSH(x->key->class), CLEAR_CACHE_FLUSH(y->key->class));
+ if (r != 0)
+ return r;
- if ((*x)->key->type < (*y)->key->type)
- return -1;
- else if ((*x)->key->type > (*y)->key->type)
- return 1;
+ r = CMP(x->key->type, y->key->type);
+ if (r != 0)
+ return r;
- r = dns_resource_record_to_wire_format(*x, false);
+ r = dns_resource_record_to_wire_format(x, false);
if (r < 0) {
log_warning_errno(r, "Can't wire-format RR: %m");
return 0;
}
- r = dns_resource_record_to_wire_format(*y, false);
+ r = dns_resource_record_to_wire_format(y, false);
if (r < 0) {
log_warning_errno(r, "Can't wire-format RR: %m");
return 0;
}
- m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y));
+ m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(x), DNS_RESOURCE_RECORD_RDATA_SIZE(y));
- r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m);
+ r = memcmp(DNS_RESOURCE_RECORD_RDATA(x), DNS_RESOURCE_RECORD_RDATA(y), m);
if (r != 0)
return r;
- if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
- return -1;
- else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
- return 1;
-
- return 0;
+ return CMP(DNS_RESOURCE_RECORD_RDATA_SIZE(x), DNS_RESOURCE_RECORD_RDATA_SIZE(y));
}
static int proposed_rrs_cmp(DnsResourceRecord **x, unsigned x_size, DnsResourceRecord **y, unsigned y_size) {
@@ -110,12 +101,7 @@ static int proposed_rrs_cmp(DnsResourceRecord **x, unsigned x_size, DnsResourceR
return r;
}
- if (x_size < y_size)
- return -1;
- if (x_size > y_size)
- return 1;
-
- return 0;
+ return CMP(x_size, y_size);
}
static int mdns_packet_extract_matching_rrs(DnsPacket *p, DnsResourceKey *key, DnsResourceRecord ***ret_rrs) {
@@ -151,7 +137,7 @@ static int mdns_packet_extract_matching_rrs(DnsPacket *p, DnsResourceKey *key, D
list[n++] = p->answer->items[i].rr;
}
assert(n == size);
- qsort_safe(list, size, sizeof(DnsResourceRecord*), mdns_rr_compare);
+ typesafe_qsort(list, size, mdns_rr_compare);
*ret_rrs = TAKE_PTR(list);
@@ -171,7 +157,8 @@ static int mdns_do_tiebreak(DnsResourceKey *key, DnsAnswer *answer, DnsPacket *p
DNS_ANSWER_FOREACH(rr, answer)
our[i++] = rr;
- qsort_safe(our, size, sizeof(DnsResourceRecord*), mdns_rr_compare);
+
+ typesafe_qsort(our, size, mdns_rr_compare);
r = mdns_packet_extract_matching_rrs(p, key, &remote);
if (r < 0)
@@ -352,7 +339,7 @@ int manager_mdns_ipv4_fd(Manager *m) {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(MDNS_PORT),
};
- static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
+ _cleanup_close_ int s = -1;
int r;
assert(m);
@@ -360,88 +347,64 @@ int manager_mdns_ipv4_fd(Manager *m) {
if (m->mdns_ipv4_fd >= 0)
return m->mdns_ipv4_fd;
- m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->mdns_ipv4_fd < 0)
+ s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (s < 0)
return log_error_errno(errno, "mDNS-IPv4: Failed to create socket: %m");
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_TTL: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_TTL, 255);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv4: Failed to set IP_TTL: %m");
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_MULTICAST_TTL: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_TTL, 255);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv4: Failed to set IP_MULTICAST_TTL: %m");
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_MULTICAST_LOOP: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_LOOP, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv4: Failed to set IP_MULTICAST_LOOP: %m");
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_PKTINFO: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv4: Failed to set IP_PKTINFO: %m");
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_RECVTTL: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_RECVTTL, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv4: Failed to set IP_RECVTTL: %m");
/* Disable Don't-Fragment bit in the IP header */
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_MTU_DISCOVER: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv4: Failed to set IP_MTU_DISCOVER: %m");
/* See the section 15.1 of RFC6762 */
/* first try to bind without SO_REUSEADDR to detect another mDNS responder */
- r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in));
+ r = bind(s, &sa.sa, sizeof(sa.in));
if (r < 0) {
- if (errno != EADDRINUSE) {
- r = log_error_errno(errno, "mDNS-IPv4: Failed to bind socket: %m");
- goto fail;
- }
+ if (errno != EADDRINUSE)
+ return log_error_errno(errno, "mDNS-IPv4: Failed to bind socket: %m");
log_warning("mDNS-IPv4: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers.");
/* try again with SO_REUSEADDR */
- r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
- r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv4: Failed to bind socket: %m");
- goto fail;
- }
+ r = bind(s, &sa.sa, sizeof(sa.in));
+ if (r < 0)
+ return log_error_errno(errno, "mDNS-IPv4: Failed to bind socket: %m");
} else {
/* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
- r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
}
- r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m);
+ r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, s, EPOLLIN, on_mdns_packet, m);
if (r < 0)
- goto fail;
-
- return m->mdns_ipv4_fd;
+ return log_error_errno(r, "mDNS-IPv4: Failed to create event source: %m");
-fail:
- m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
- return r;
+ return m->mdns_ipv4_fd = TAKE_FD(s);
}
int manager_mdns_ipv6_fd(Manager *m) {
@@ -449,7 +412,7 @@ int manager_mdns_ipv6_fd(Manager *m) {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(MDNS_PORT),
};
- static const int one = 1, ttl = 255;
+ _cleanup_close_ int s = -1;
int r;
assert(m);
@@ -457,86 +420,62 @@ int manager_mdns_ipv6_fd(Manager *m) {
if (m->mdns_ipv6_fd >= 0)
return m->mdns_ipv6_fd;
- m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->mdns_ipv6_fd < 0)
+ s = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (s < 0)
return log_error_errno(errno, "mDNS-IPv6: Failed to create socket: %m");
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_UNICAST_HOPS: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_UNICAST_HOPS: %m");
/* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_MULTICAST_HOPS: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_MULTICAST_HOPS: %m");
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_MULTICAST_LOOP: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_MULTICAST_LOOP: %m");
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_V6ONLY: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_V6ONLY: %m");
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_RECVPKTINFO: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_RECVPKTINFO: %m");
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_RECVHOPLIMIT: %m");
- goto fail;
- }
+ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_RECVHOPLIMIT: %m");
/* See the section 15.1 of RFC6762 */
/* first try to bind without SO_REUSEADDR to detect another mDNS responder */
- r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6));
+ r = bind(s, &sa.sa, sizeof(sa.in6));
if (r < 0) {
- if (errno != EADDRINUSE) {
- r = log_error_errno(errno, "mDNS-IPv6: Failed to bind socket: %m");
- goto fail;
- }
+ if (errno != EADDRINUSE)
+ return log_error_errno(errno, "mDNS-IPv6: Failed to bind socket: %m");
log_warning("mDNS-IPv6: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers.");
/* try again with SO_REUSEADDR */
- r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
- r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv6: Failed to bind socket: %m");
- goto fail;
- }
+ r = bind(s, &sa.sa, sizeof(sa.in6));
+ if (r < 0)
+ return log_error_errno(errno, "mDNS-IPv6: Failed to bind socket: %m");
} else {
/* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
- r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = log_error_errno(errno, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
- goto fail;
- }
+ r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
+ if (r < 0)
+ return log_error_errno(r, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
}
- r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m);
+ r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, s, EPOLLIN, on_mdns_packet, m);
if (r < 0)
- goto fail;
+ return log_error_errno(r, "mDNS-IPv6: Failed to create event source: %m");
- return m->mdns_ipv6_fd;
-
-fail:
- m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
- return r;
+ return m->mdns_ipv6_fd = TAKE_FD(s);
}
diff --git a/src/resolve/resolved-mdns.h b/src/resolve/resolved-mdns.h
index d2bce6fcd9..2f69478681 100644
--- a/src/resolve/resolved-mdns.h
+++ b/src/resolve/resolved-mdns.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "resolved-manager.h"
#define MDNS_PORT 5353
diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c
index 5a022507c9..5205071d3f 100644
--- a/src/resolve/resolved-resolv-conf.c
+++ b/src/resolve/resolved-resolv-conf.c
@@ -6,7 +6,6 @@
#include "alloc-util.h"
#include "dns-domain.h"
#include "fd-util.h"
-#include "fileio-label.h"
#include "fileio.h"
#include "ordered-set.h"
#include "resolved-conf.h"
@@ -14,6 +13,7 @@
#include "resolved-resolv-conf.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util-label.h"
/* A resolv.conf file containing the DNS server and domain data we learnt from uplink, i.e. the full uplink data */
#define PRIVATE_UPLINK_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
@@ -21,9 +21,49 @@
/* A resolv.conf file containing the domain data we learnt from uplink, but our own DNS server address. */
#define PRIVATE_STUB_RESOLV_CONF "/run/systemd/resolve/stub-resolv.conf"
-/* A static resolv.conf file containing no domains, but only our own DNS sever address */
+/* A static resolv.conf file containing no domains, but only our own DNS server address */
#define PRIVATE_STATIC_RESOLV_CONF ROOTLIBEXECDIR "/resolv.conf"
+int manager_check_resolv_conf(const Manager *m) {
+ const char *path;
+ struct stat st;
+ int r;
+
+ assert(m);
+
+ /* This warns only when our stub listener is disabled and /etc/resolv.conf is a symlink to
+ * PRIVATE_STATIC_RESOLV_CONF or PRIVATE_STUB_RESOLV_CONF. */
+
+ if (m->dns_stub_listener_mode != DNS_STUB_LISTENER_NO)
+ return 0;
+
+ r = stat("/etc/resolv.conf", &st);
+ if (r < 0) {
+ if (errno == ENOENT)
+ return 0;
+
+ return log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m");
+ }
+
+ FOREACH_STRING(path,
+ PRIVATE_STUB_RESOLV_CONF,
+ PRIVATE_STATIC_RESOLV_CONF) {
+
+ struct stat own;
+
+ /* Is it symlinked to our own uplink file? */
+ if (stat(path, &own) >= 0 &&
+ st.st_dev == own.st_dev &&
+ st.st_ino == own.st_ino) {
+ log_warning("DNSStubListener= is disabled, but /etc/resolv.conf is a symlink to %s "
+ "which expects DNSStubListener= to be enabled.", path);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
static bool file_is_our_own(const struct stat *st) {
const char *path;
@@ -49,7 +89,6 @@ static bool file_is_our_own(const struct stat *st) {
int manager_read_resolv_conf(Manager *m) {
_cleanup_fclose_ FILE *f = NULL;
struct stat st;
- char line[LINE_MAX];
unsigned n = 0;
int r;
@@ -97,10 +136,19 @@ int manager_read_resolv_conf(Manager *m) {
dns_server_mark_all(m->dns_servers);
dns_search_domain_mark_all(m->search_domains);
- FOREACH_LINE(line, f, r = -errno; goto clear) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
const char *a;
char *l;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0) {
+ log_error_errno(r, "Failed to read /etc/resolv.conf: %m");
+ goto clear;
+ }
+ if (r == 0)
+ break;
+
n++;
l = strstrip(line);
@@ -169,6 +217,8 @@ clear:
}
static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
+ DnsScope *scope;
+
assert(s);
assert(f);
assert(count);
@@ -178,13 +228,12 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
return;
}
- /* Check if the DNS server is limited to particular domains;
- * resolv.conf does not have a syntax to express that, so it must not
- * appear as a global name server to avoid routing unrelated domains to
- * it (which is a privacy violation, will most probably fail anyway,
- * and adds unnecessary load) */
- if (dns_server_limited_domains(s)) {
- log_debug("DNS server %s has route-only domains, not using as global name server", dns_server_string(s));
+ /* Check if the scope this DNS server belongs to is suitable as 'default' route for lookups; resolv.conf does
+ * not have a syntax to express that, so it must not appear as a global name server to avoid routing unrelated
+ * domains to it (which is a privacy violation, will most probably fail anyway, and adds unnecessary load) */
+ scope = dns_server_scope(s);
+ if (scope && !dns_scope_is_default_route(scope)) {
+ log_debug("Scope of DNS server %s has only route-only domains, not using as global name server", dns_server_string(s));
return;
}
@@ -273,7 +322,8 @@ static int write_stub_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet
"# See man:systemd-resolved.service(8) for details about the supported modes of\n"
"# operation for /etc/resolv.conf.\n"
"\n"
- "nameserver 127.0.0.53\n", f);
+ "nameserver 127.0.0.53\n"
+ "options edns0\n", f);
if (!ordered_set_isempty(domains))
write_resolv_conf_search(domains, f);
diff --git a/src/resolve/resolved-resolv-conf.h b/src/resolve/resolved-resolv-conf.h
index 105ae4cae8..f69cf2a441 100644
--- a/src/resolve/resolved-resolv-conf.h
+++ b/src/resolve/resolved-resolv-conf.h
@@ -1,8 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "resolved-manager.h"
+int manager_check_resolv_conf(const Manager *m);
int manager_read_resolv_conf(Manager *m);
int manager_write_resolv_conf(Manager *m);
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
index c01e53e9da..f4efddf8e5 100644
--- a/src/resolve/resolved.c
+++ b/src/resolve/resolved.c
@@ -4,6 +4,8 @@
#include "sd-event.h"
#include "capability-util.h"
+#include "daemon-util.h"
+#include "main-func.h"
#include "mkdir.h"
#include "resolved-conf.h"
#include "resolved-manager.h"
@@ -12,45 +14,35 @@
#include "signal-util.h"
#include "user-util.h"
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
+ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
const char *user = "systemd-resolve";
uid_t uid;
gid_t gid;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
- if (argc != 1) {
- log_error("This program takes no arguments.");
- r = -EINVAL;
- goto finish;
- }
+ if (argc != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
umask(0022);
r = mac_selinux_init();
- if (r < 0) {
- log_error_errno(r, "SELinux setup failed: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "SELinux setup failed: %m");
- r = get_user_creds(&user, &uid, &gid, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "Cannot resolve user name %s: %m", user);
- goto finish;
- }
+ r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Cannot resolve user name %s: %m", user);
/* Always create the directory where resolv.conf will live */
r = mkdir_safe_label("/run/systemd/resolve", 0755, uid, gid, MKDIR_WARN_MODE);
- if (r < 0) {
- log_error_errno(r, "Could not create runtime directory: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not create runtime directory: %m");
- /* Drop privileges, but only if we have been started as root. If we are not running as root we assume all
+ /* Drop privileges, but only if we have been started as root. If we are not running as root we assume most
* privileges are already dropped. */
if (getuid() == 0) {
@@ -60,49 +52,38 @@ int main(int argc, char *argv[]) {
(UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */
(UINT64_C(1) << CAP_SETPCAP) /* needed in order to drop the caps later */);
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to drop privileges: %m");
}
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, SIGUSR2, SIGRTMIN+1, -1) >= 0);
r = manager_new(&m);
- if (r < 0) {
- log_error_errno(r, "Could not create manager: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not create manager: %m");
r = manager_start(m);
- if (r < 0) {
- log_error_errno(r, "Failed to start manager: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to start manager: %m");
/* Write finish default resolv.conf to avoid a dangling symlink */
(void) manager_write_resolv_conf(m);
+ (void) manager_check_resolv_conf(m);
+
/* Let's drop the remaining caps now */
r = capability_bounding_set_drop(0, true);
- if (r < 0) {
- log_error_errno(r, "Failed to drop remaining caps: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to drop remaining caps: %m");
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing requests...");
+ notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
r = sd_event_loop(m->event);
- if (r < 0) {
- log_error_errno(r, "Event loop failed: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
- sd_event_get_exit_code(m->event, &r);
+ (void) sd_event_get_exit_code(m->event, &r);
-finish:
- sd_notify(false,
- "STOPPING=1\n"
- "STATUS=Shutting down...");
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in
index 2528340f74..6898c7848b 100644
--- a/src/resolve/resolved.conf.in
+++ b/src/resolve/resolved.conf.in
@@ -20,4 +20,5 @@
#DNSSEC=@DEFAULT_DNSSEC_MODE@
#DNSOverTLS=@DEFAULT_DNS_OVER_TLS_MODE@
#Cache=yes
-#DNSStubListener=udp
+#DNSStubListener=yes
+#ReadEtcHosts=yes
diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c
index 905f000dc2..f6df9135ee 100644
--- a/src/resolve/test-dns-packet.c
+++ b/src/resolve/test-dns-packet.c
@@ -12,6 +12,7 @@
#include "macro.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-rr.h"
+#include "path-util.h"
#include "string-util.h"
#include "strv.h"
#include "tests.h"
@@ -92,6 +93,7 @@ static void test_packet_from_file(const char* filename, bool canonical) {
int main(int argc, char **argv) {
int i, N;
+ _cleanup_free_ char *pkts_glob = NULL;
_cleanup_globfree_ glob_t g = {};
char **fnames;
@@ -101,7 +103,8 @@ int main(int argc, char **argv) {
N = argc - 1;
fnames = argv + 1;
} else {
- assert_se(glob(get_testdata_dir("/test-resolve/*.pkts"), GLOB_NOSORT, NULL, &g) == 0);
+ pkts_glob = path_join(get_testdata_dir(), "test-resolve/*.pkts");
+ assert_se(glob(pkts_glob, GLOB_NOSORT, NULL, &g) == 0);
N = g.gl_pathc;
fnames = g.gl_pathv;
}
diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c
index 568d8e0790..840c4fa1db 100644
--- a/src/resolve/test-dnssec.c
+++ b/src/resolve/test-dnssec.c
@@ -497,7 +497,7 @@ static void test_dnssec_nsec3_hash(void) {
#endif
-int main(int argc, char*argv[]) {
+int main(int argc, char *argv[]) {
test_dnssec_canonicalize();
diff --git a/src/resolve/test-resolve-tables.c b/src/resolve/test-resolve-tables.c
index 842d42b311..2230a66ef5 100644
--- a/src/resolve/test-resolve-tables.c
+++ b/src/resolve/test-resolve-tables.c
@@ -1,11 +1,18 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "dns-type.h"
+#include "resolved-dns-dnssec.h"
+#include "resolved-dns-packet.h"
#include "test-tables.h"
int main(int argc, char **argv) {
uint16_t i;
+ test_table(dns_protocol, DNS_PROTOCOL);
+ test_table(dnssec_result, DNSSEC_RESULT);
+ test_table(dnssec_verdict, DNSSEC_VERDICT);
+
+ test_table_sparse(dns_rcode, DNS_RCODE);
test_table_sparse(dns_type, DNS_TYPE);
log_info("/* DNS_TYPE */");
diff --git a/src/resolve/test-resolved-etc-hosts.c b/src/resolve/test-resolved-etc-hosts.c
new file mode 100644
index 0000000000..dbff889e3e
--- /dev/null
+++ b/src/resolve/test-resolved-etc-hosts.c
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "log.h"
+#include "resolved-etc-hosts.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+
+static void test_parse_etc_hosts_system(void) {
+ _cleanup_fclose_ FILE *f = NULL;
+
+ log_info("/* %s */", __func__);
+
+ f = fopen("/etc/hosts", "re");
+ if (!f) {
+ assert_se(errno == ENOENT);
+ return;
+ }
+
+ _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+ assert_se(etc_hosts_parse(&hosts, f) == 0);
+}
+
+#define address_equal_4(_addr, _address) \
+ ((_addr)->family == AF_INET && \
+ !memcmp(&(_addr)->address.in, &(struct in_addr) { .s_addr = (_address) }, 4))
+
+#define address_equal_6(_addr, ...) \
+ ((_addr)->family == AF_INET6 && \
+ !memcmp(&(_addr)->address.in6, &(struct in6_addr) { .s6_addr = __VA_ARGS__}, 16) )
+
+static void test_parse_etc_hosts(void) {
+ _cleanup_(unlink_tempfilep) char
+ t[] = "/tmp/test-resolved-etc-hosts.XXXXXX";
+
+ log_info("/* %s */", __func__);
+
+ int fd;
+ _cleanup_fclose_ FILE *f;
+ const char *s;
+
+ fd = mkostemp_safe(t);
+ assert_se(fd >= 0);
+
+ f = fdopen(fd, "r+");
+ assert_se(f);
+ fputs("1.2.3.4 some.where\n"
+ "1.2.3.5 some.where\n"
+ "1.2.3.6 dash dash-dash.where-dash\n"
+ "1.2.3.7 bad-dash- -bad-dash -bad-dash.bad-\n"
+ "1.2.3.8\n"
+ "1.2.3.9 before.comment # within.comment\n"
+ "1.2.3.10 before.comment#within.comment2\n"
+ "1.2.3.11 before.comment# within.comment3\n"
+ "1.2.3.12 before.comment#\n"
+ "1.2.3 short.address\n"
+ "1.2.3.4.5 long.address\n"
+ "1::2::3 multi.colon\n"
+
+ "::0 some.where some.other\n"
+ "0.0.0.0 black.listed\n"
+ "::5\t\t\t \tsome.where\tsome.other foobar.foo.foo\t\t\t\n"
+ " \n", f);
+ assert_se(fflush_and_check(f) >= 0);
+ rewind(f);
+
+ _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+ assert_se(etc_hosts_parse(&hosts, f) == 0);
+
+ EtcHostsItemByName *bn;
+ assert_se(bn = hashmap_get(hosts.by_name, "some.where"));
+ assert_se(bn->n_addresses == 3);
+ assert_se(bn->n_allocated >= 3);
+ assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.4")));
+ assert_se(address_equal_4(bn->addresses[1], inet_addr("1.2.3.5")));
+ assert_se(address_equal_6(bn->addresses[2], {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}));
+
+ assert_se(bn = hashmap_get(hosts.by_name, "dash"));
+ assert_se(bn->n_addresses == 1);
+ assert_se(bn->n_allocated >= 1);
+ assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.6")));
+
+ assert_se(bn = hashmap_get(hosts.by_name, "dash-dash.where-dash"));
+ assert_se(bn->n_addresses == 1);
+ assert_se(bn->n_allocated >= 1);
+ assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.6")));
+
+ /* See https://tools.ietf.org/html/rfc1035#section-2.3.1 */
+ FOREACH_STRING(s, "bad-dash-", "-bad-dash", "-bad-dash.bad-")
+ assert_se(!hashmap_get(hosts.by_name, s));
+
+ assert_se(bn = hashmap_get(hosts.by_name, "before.comment"));
+ assert_se(bn->n_addresses == 4);
+ assert_se(bn->n_allocated >= 4);
+ assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.9")));
+ assert_se(address_equal_4(bn->addresses[1], inet_addr("1.2.3.10")));
+ assert_se(address_equal_4(bn->addresses[2], inet_addr("1.2.3.11")));
+ assert_se(address_equal_4(bn->addresses[3], inet_addr("1.2.3.12")));
+
+ assert(!hashmap_get(hosts.by_name, "within.comment"));
+ assert(!hashmap_get(hosts.by_name, "within.comment2"));
+ assert(!hashmap_get(hosts.by_name, "within.comment3"));
+ assert(!hashmap_get(hosts.by_name, "#"));
+
+ assert(!hashmap_get(hosts.by_name, "short.address"));
+ assert(!hashmap_get(hosts.by_name, "long.address"));
+ assert(!hashmap_get(hosts.by_name, "multi.colon"));
+ assert_se(!set_contains(hosts.no_address, "short.address"));
+ assert_se(!set_contains(hosts.no_address, "long.address"));
+ assert_se(!set_contains(hosts.no_address, "multi.colon"));
+
+ assert_se(bn = hashmap_get(hosts.by_name, "some.other"));
+ assert_se(bn->n_addresses == 1);
+ assert_se(bn->n_allocated >= 1);
+ assert_se(address_equal_6(bn->addresses[0], {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}));
+
+ assert_se( set_contains(hosts.no_address, "some.where"));
+ assert_se( set_contains(hosts.no_address, "some.other"));
+ assert_se( set_contains(hosts.no_address, "black.listed"));
+ assert_se(!set_contains(hosts.no_address, "foobar.foo.foo"));
+}
+
+static void test_parse_file(const char *fname) {
+ _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+ _cleanup_fclose_ FILE *f;
+
+ log_info("/* %s(\"%s\") */", __func__, fname);
+
+ assert_se(f = fopen(fname, "re"));
+ assert_se(etc_hosts_parse(&hosts, f) == 0);
+}
+
+int main(int argc, char **argv) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ if (argc == 1) {
+ test_parse_etc_hosts_system();
+ test_parse_etc_hosts();
+ } else
+ test_parse_file(argv[1]);
+
+ return 0;
+}
diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c
index 7f3e055b1a..ac21dc064c 100644
--- a/src/rfkill/rfkill.c
+++ b/src/rfkill/rfkill.c
@@ -3,14 +3,16 @@
#include <linux/rfkill.h>
#include <poll.h>
-#include "libudev.h"
#include "sd-daemon.h"
+#include "sd-device.h"
#include "alloc-util.h"
+#include "device-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "io-util.h"
+#include "main-func.h"
#include "mkdir.h"
#include "parse-util.h"
#include "proc-cmdline.h"
@@ -31,6 +33,11 @@ typedef struct write_queue_item {
int state;
} write_queue_item;
+typedef struct Context {
+ LIST_HEAD(write_queue_item, write_queue);
+ int rfkill_fd;
+} Context;
+
static struct write_queue_item* write_queue_item_free(struct write_queue_item *item) {
if (!item)
return NULL;
@@ -54,121 +61,39 @@ static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int);
static int find_device(
- struct udev *udev,
const struct rfkill_event *event,
- struct udev_device **ret) {
-
+ sd_device **ret) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
_cleanup_free_ char *sysname = NULL;
- struct udev_device *device;
const char *name;
+ int r;
- assert(udev);
assert(event);
assert(ret);
if (asprintf(&sysname, "rfkill%i", event->idx) < 0)
return log_oom();
- device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
- if (!device)
- return log_full_errno(IN_SET(errno, ENOENT, ENXIO, ENODEV) ? LOG_DEBUG : LOG_ERR, errno,
- "Failed to open device '%s': %m", sysname);
-
- name = udev_device_get_sysattr_value(device, "name");
- if (!name) {
- log_debug("Device has no name, ignoring.");
- udev_device_unref(device);
- return -ENOENT;
- }
-
- log_debug("Operating on rfkill device '%s'.", name);
-
- *ret = device;
- return 0;
-}
-
-static int wait_for_initialized(
- struct udev *udev,
- struct udev_device *device,
- struct udev_device **ret) {
-
- _cleanup_(udev_monitor_unrefp) struct udev_monitor *monitor = NULL;
- struct udev_device *d;
- const char *sysname;
- int watch_fd, r;
-
- assert(udev);
- assert(device);
- assert(ret);
-
- if (udev_device_get_is_initialized(device) != 0) {
- *ret = udev_device_ref(device);
- return 0;
- }
-
- assert_se(sysname = udev_device_get_sysname(device));
-
- /* Wait until the device is initialized, so that we can get
- * access to the ID_PATH property */
-
- monitor = udev_monitor_new_from_netlink(udev, "udev");
- if (!monitor)
- return log_error_errno(errno, "Failed to acquire monitor: %m");
-
- r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL);
+ r = sd_device_new_from_subsystem_sysname(&device, "rfkill", sysname);
if (r < 0)
- return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m");
-
- r = udev_monitor_enable_receiving(monitor);
- if (r < 0)
- return log_error_errno(r, "Failed to enable udev receiving: %m");
-
- watch_fd = udev_monitor_get_fd(monitor);
- if (watch_fd < 0)
- return log_error_errno(watch_fd, "Failed to get watch fd: %m");
-
- /* Check again, maybe things changed */
- d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname);
- if (!d)
- return log_full_errno(IN_SET(errno, ENOENT, ENXIO, ENODEV) ? LOG_DEBUG : LOG_ERR, errno,
+ return log_full_errno(IN_SET(r, -ENOENT, -ENXIO, -ENODEV) ? LOG_DEBUG : LOG_ERR, r,
"Failed to open device '%s': %m", sysname);
- if (udev_device_get_is_initialized(d) != 0) {
- *ret = d;
- return 0;
- }
-
- for (;;) {
- _cleanup_(udev_device_unrefp) struct udev_device *t = NULL;
-
- r = fd_wait_for_event(watch_fd, POLLIN, EXIT_USEC);
- if (r == -EINTR)
- continue;
- if (r < 0)
- return log_error_errno(r, "Failed to watch udev monitor: %m");
- if (r == 0) {
- log_error("Timed out waiting for udev monitor.");
- return -ETIMEDOUT;
- }
+ r = sd_device_get_sysattr_value(device, "name", &name);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Device has no name, ignoring: %m");
- t = udev_monitor_receive_device(monitor);
- if (!t)
- continue;
+ log_device_debug(device, "Operating on rfkill device '%s'.", name);
- if (streq_ptr(udev_device_get_sysname(t), sysname)) {
- *ret = udev_device_ref(t);
- return 0;
- }
- }
+ *ret = TAKE_PTR(device);
+ return 0;
}
static int determine_state_file(
- struct udev *udev,
const struct rfkill_event *event,
char **ret) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
- _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL, *device = NULL;
const char *path_id, *type;
char *state_file;
int r;
@@ -176,18 +101,17 @@ static int determine_state_file(
assert(event);
assert(ret);
- r = find_device(udev, event, &d);
+ r = find_device(event, &d);
if (r < 0)
return r;
- r = wait_for_initialized(udev, d, &device);
+ r = device_wait_for_initialization(d, "rfkill", &device);
if (r < 0)
return r;
assert_se(type = rfkill_type_to_string(event->type));
- path_id = udev_device_get_property_value(device, "ID_PATH");
- if (path_id) {
+ if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
_cleanup_free_ char *escaped_path_id = NULL;
escaped_path_id = cescape(path_id);
@@ -205,24 +129,20 @@ static int determine_state_file(
return 0;
}
-static int load_state(
- int rfkill_fd,
- struct udev *udev,
- const struct rfkill_event *event) {
-
+static int load_state(Context *c, const struct rfkill_event *event) {
_cleanup_free_ char *state_file = NULL, *value = NULL;
struct rfkill_event we;
ssize_t l;
int b, r;
- assert(rfkill_fd >= 0);
- assert(udev);
+ assert(c);
+ assert(c->rfkill_fd >= 0);
assert(event);
if (shall_restore_state() == 0)
return 0;
- r = determine_state_file(udev, event, &state_file);
+ r = determine_state_file(event, &state_file);
if (r < 0)
return r;
@@ -250,53 +170,45 @@ static int load_state(
.soft = b,
};
- l = write(rfkill_fd, &we, sizeof(we));
+ l = write(c->rfkill_fd, &we, sizeof(we));
if (l < 0)
return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
- if (l != sizeof(we)) {
- log_error("Couldn't write rfkill event structure, too short.");
- return -EIO;
- }
+ if (l != sizeof(we))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Couldn't write rfkill event structure, too short.");
log_debug("Loaded state '%s' from %s.", one_zero(b), state_file);
return 0;
}
-static void save_state_queue_remove(
- struct write_queue_item **write_queue,
- int idx,
- char *state_file) {
-
+static void save_state_queue_remove(Context *c, int idx, const char *state_file) {
struct write_queue_item *item, *tmp;
- LIST_FOREACH_SAFE(queue, item, tmp, *write_queue) {
+ assert(c);
+
+ LIST_FOREACH_SAFE(queue, item, tmp, c->write_queue) {
if ((state_file && streq(item->file, state_file)) || idx == item->rfkill_idx) {
log_debug("Canceled previous save state of '%s' to %s.", one_zero(item->state), item->file);
- LIST_REMOVE(queue, *write_queue, item);
+ LIST_REMOVE(queue, c->write_queue, item);
write_queue_item_free(item);
}
}
}
-static int save_state_queue(
- struct write_queue_item **write_queue,
- int rfkill_fd,
- struct udev *udev,
- const struct rfkill_event *event) {
-
+static int save_state_queue(Context *c, const struct rfkill_event *event) {
_cleanup_free_ char *state_file = NULL;
struct write_queue_item *item;
int r;
- assert(rfkill_fd >= 0);
- assert(udev);
+ assert(c);
+ assert(c->rfkill_fd >= 0);
assert(event);
- r = determine_state_file(udev, event, &state_file);
+ r = determine_state_file(event, &state_file);
if (r < 0)
return r;
- save_state_queue_remove(write_queue, event->idx, state_file);
+ save_state_queue_remove(c, event->idx, state_file);
item = new0(struct write_queue_item, 1);
if (!item)
@@ -306,119 +218,90 @@ static int save_state_queue(
item->rfkill_idx = event->idx;
item->state = event->soft;
- LIST_APPEND(queue, *write_queue, item);
+ LIST_APPEND(queue, c->write_queue, item);
return 0;
}
-static int save_state_cancel(
- struct write_queue_item **write_queue,
- int rfkill_fd,
- struct udev *udev,
- const struct rfkill_event *event) {
-
+static int save_state_cancel(Context *c, const struct rfkill_event *event) {
_cleanup_free_ char *state_file = NULL;
int r;
- assert(rfkill_fd >= 0);
- assert(udev);
+ assert(c);
+ assert(c->rfkill_fd >= 0);
assert(event);
- r = determine_state_file(udev, event, &state_file);
- save_state_queue_remove(write_queue, event->idx, state_file);
+ r = determine_state_file(event, &state_file);
+ save_state_queue_remove(c, event->idx, state_file);
if (r < 0)
return r;
return 0;
}
-static int save_state_write(struct write_queue_item **write_queue) {
- struct write_queue_item *item, *tmp;
- int result = 0;
- bool error_logged = false;
+static int save_state_write_one(struct write_queue_item *item) {
int r;
- LIST_FOREACH_SAFE(queue, item, tmp, *write_queue) {
- r = write_string_file(item->file, one_zero(item->state), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
- if (r < 0) {
- result = r;
- if (!error_logged) {
- log_error_errno(r, "Failed to write state file %s: %m", item->file);
- error_logged = true;
- } else
- log_warning_errno(r, "Failed to write state file %s: %m", item->file);
- } else
- log_debug("Saved state '%s' to %s.", one_zero(item->state), item->file);
-
- LIST_REMOVE(queue, *write_queue, item);
- write_queue_item_free(item);
+ r = write_string_file(item->file, one_zero(item->state), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write state file %s: %m", item->file);
+
+ log_debug("Saved state '%s' to %s.", one_zero(item->state), item->file);
+ return 0;
+}
+
+static void context_save_and_clear(Context *c) {
+ struct write_queue_item *i;
+
+ assert(c);
+
+ while ((i = c->write_queue)) {
+ LIST_REMOVE(queue, c->write_queue, i);
+ (void) save_state_write_one(i);
+ write_queue_item_free(i);
}
- return result;
+
+ safe_close(c->rfkill_fd);
}
-int main(int argc, char *argv[]) {
- LIST_HEAD(write_queue_item, write_queue);
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
- _cleanup_close_ int rfkill_fd = -1;
+static int run(int argc, char *argv[]) {
+ _cleanup_(context_save_and_clear) Context c = { .rfkill_fd = -1 };
bool ready = false;
int r, n;
- if (argc > 1) {
- log_error("This program requires no arguments.");
- return EXIT_FAILURE;
- }
-
- LIST_HEAD_INIT(write_queue);
+ if (argc > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires no arguments.");
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
- udev = udev_new();
- if (!udev) {
- r = log_oom();
- goto finish;
- }
-
r = mkdir_p("/var/lib/systemd/rfkill", 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create rfkill directory: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create rfkill directory: %m");
n = sd_listen_fds(false);
- if (n < 0) {
- r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
- goto finish;
- }
- if (n > 1) {
- log_error("Got too many file descriptors.");
- r = -EINVAL;
- goto finish;
- }
+ if (n < 0)
+ return log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m");
+ if (n > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Got too many file descriptors.");
if (n == 0) {
- rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
- if (rfkill_fd < 0) {
+ c.rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (c.rfkill_fd < 0) {
if (errno == ENOENT) {
log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting.");
- r = 0;
- goto finish;
+ return 0;
}
- r = log_error_errno(errno, "Failed to open /dev/rfkill: %m");
- goto finish;
+ return log_error_errno(errno, "Failed to open /dev/rfkill: %m");
}
} else {
- rfkill_fd = SD_LISTEN_FDS_START;
+ c.rfkill_fd = SD_LISTEN_FDS_START;
- r = fd_nonblock(rfkill_fd, 1);
- if (r < 0) {
- log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
- goto finish;
- }
+ r = fd_nonblock(c.rfkill_fd, 1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m");
}
for (;;) {
@@ -426,7 +309,7 @@ int main(int argc, char *argv[]) {
const char *type;
ssize_t l;
- l = read(rfkill_fd, &event, sizeof(event));
+ l = read(c.rfkill_fd, &event, sizeof(event));
if (l < 0) {
if (errno == EAGAIN) {
@@ -441,13 +324,11 @@ int main(int argc, char *argv[]) {
/* Hang around for a bit, maybe there's more coming */
- r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC);
+ r = fd_wait_for_event(c.rfkill_fd, POLLIN, EXIT_USEC);
if (r == -EINTR)
continue;
- if (r < 0) {
- log_error_errno(r, "Failed to poll() on device: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to poll() on device: %m");
if (r > 0)
continue;
@@ -458,11 +339,8 @@ int main(int argc, char *argv[]) {
log_error_errno(errno, "Failed to read from /dev/rfkill: %m");
}
- if (l != RFKILL_EVENT_SIZE_V1) {
- log_error("Read event structure of invalid size.");
- r = -EIO;
- goto finish;
- }
+ if (l != RFKILL_EVENT_SIZE_V1)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Read event structure of invalid size.");
type = rfkill_type_to_string(event.type);
if (!type) {
@@ -474,17 +352,17 @@ int main(int argc, char *argv[]) {
case RFKILL_OP_ADD:
log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type);
- (void) load_state(rfkill_fd, udev, &event);
+ (void) load_state(&c, &event);
break;
case RFKILL_OP_DEL:
log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type);
- (void) save_state_cancel(&write_queue, rfkill_fd, udev, &event);
+ (void) save_state_cancel(&c, &event);
break;
case RFKILL_OP_CHANGE:
log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type);
- (void) save_state_queue(&write_queue, rfkill_fd, udev, &event);
+ (void) save_state_queue(&c, &event);
break;
default:
@@ -493,10 +371,7 @@ int main(int argc, char *argv[]) {
}
}
- r = 0;
-
-finish:
- (void) save_state_write(&write_queue);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/run-generator/run-generator.c b/src/run-generator/run-generator.c
new file mode 100644
index 0000000000..a5dfac01d4
--- /dev/null
+++ b/src/run-generator/run-generator.c
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "generator.h"
+#include "mkdir.h"
+#include "proc-cmdline.h"
+#include "specifier.h"
+#include "strv.h"
+
+static const char *arg_dest = NULL;
+static char **arg_commands = NULL;
+static char *arg_success_action = NULL;
+static char *arg_failure_action = NULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_commands, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_success_action, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_failure_action, freep);
+
+static int parse(const char *key, const char *value, void *data) {
+ int r;
+
+ if (proc_cmdline_key_streq(key, "systemd.run")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = strv_extend(&arg_commands, value);
+ if (r < 0)
+ return log_oom();
+
+ } else if (proc_cmdline_key_streq(key, "systemd.run_success_action")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ if (free_and_strdup(&arg_success_action, value) < 0)
+ return log_oom();
+
+ } else if (proc_cmdline_key_streq(key, "systemd.run_failure_action")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ if (free_and_strdup(&arg_failure_action, value) < 0)
+ return log_oom();
+ }
+
+ return 0;
+}
+
+static int generate(void) {
+ _cleanup_fclose_ FILE *f = NULL;
+ const char *p;
+ char **c;
+ int r;
+
+ if (strv_isempty(arg_commands) && !arg_success_action)
+ return 0;
+
+ p = strjoina(arg_dest, "/kernel-command-line.service");
+ f = fopen(p, "wxe");
+ if (!f)
+ return log_error_errno(errno, "Failed to create unit file %s: %m", p);
+
+ fputs("# Automatically generated by systemd-run-generator\n\n"
+ "[Unit]\n"
+ "Description=Command from Kernel Command Line\n"
+ "Documentation=man:systemd-run-generator(8)\n"
+ "SourcePath=/proc/cmdline\n", f);
+
+ if (!streq_ptr(arg_success_action, "none"))
+ fprintf(f, "SuccessAction=%s\n",
+ arg_success_action ?: "exit");
+
+ if (!streq_ptr(arg_failure_action, "none"))
+ fprintf(f, "FailureAction=%s\n",
+ arg_failure_action ?: "exit");
+
+ fputs("\n"
+ "[Service]\n"
+ "Type=oneshot\n"
+ "StandardOutput=journal+console\n", f);
+
+ STRV_FOREACH(c, arg_commands) {
+ _cleanup_free_ char *a = NULL;
+
+ a = specifier_escape(*c);
+ if (!a)
+ return log_oom();
+
+ fprintf(f, "ExecStart=%s\n", a);
+ }
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", p);
+
+ /* Let's create a a target we can link "default.target" to */
+ p = strjoina(arg_dest, "/kernel-command-line.target");
+ r = write_string_file(
+ p,
+ "# Automatically generated by systemd-run-generator\n\n"
+ "[Unit]\n"
+ "Description=Command from Kernel Command Line\n"
+ "Documentation=man:systemd-run-generator(8)\n"
+ "SourcePath=/proc/cmdline\n"
+ "Requires=kernel-command-line.service\n"
+ "After=kernel-command-line.service\n",
+ WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_NOFOLLOW);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create unit file %s: %m", p);
+
+ /* And now redirect default.target to our new target */
+ p = strjoina(arg_dest, "/default.target");
+ if (symlink("kernel-command-line.target", p) < 0)
+ return log_error_errno(errno, "Failed to link unit file kernel-command-line.target → %s: %m", p);
+
+ return 0;
+}
+
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+ int r;
+
+ assert_se(arg_dest = dest);
+
+ r = proc_cmdline_parse(parse, NULL, PROC_CMDLINE_RD_STRICT|PROC_CMDLINE_STRIP_RD_PREFIX);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+
+ return generate();
+}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/run/run.c b/src/run/run.c
index 2910fcb272..e9e31282b6 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -14,8 +14,10 @@
#include "env-util.h"
#include "fd-util.h"
#include "format-util.h"
+#include "main-func.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "process-util.h"
#include "ptyfwd.h"
#include "signal-util.h"
@@ -57,8 +59,26 @@ static char **arg_timer_property = NULL;
static bool with_timer = false;
static bool arg_quiet = false;
static bool arg_aggressive_gc = false;
+static char *arg_working_directory = NULL;
+static bool arg_shell = false;
+static char **arg_cmdline = NULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_path_property, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_socket_property, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_timer_property, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-run", "1", &link);
+ if (r < 0)
+ return log_oom();
-static void help(void) {
printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
"Run the specified command in a transient scope or service.\n\n"
" -h --help Show this help\n"
@@ -80,12 +100,15 @@ static void help(void) {
" --uid=USER Run as system user\n"
" --gid=GROUP Run as system group\n"
" --nice=NICE Nice level\n"
+ " --working-directory=PATH Set working directory\n"
+ " -d --same-dir Inherit working directory from caller\n"
" -E --setenv=NAME=VALUE Set environment\n"
" -t --pty Run service on pseudo TTY as STDIN/STDOUT/\n"
" STDERR\n"
" -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n"
" -q --quiet Suppress information messages during runtime\n"
- " -G --collect Unload unit after it ran, even when failed\n\n"
+ " -G --collect Unload unit after it ran, even when failed\n"
+ " -S --shell Invoke a $SHELL interactively\n\n"
"Path options:\n"
" --path-property=NAME=VALUE Set path unit property\n\n"
"Socket options:\n"
@@ -98,7 +121,12 @@ static void help(void) {
" --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n"
" --on-calendar=SPEC Realtime timer\n"
" --timer-property=NAME=VALUE Set timer unit property\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int add_timer_property(const char *name, const char *val) {
@@ -144,44 +172,49 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_BLOCK,
ARG_NO_ASK_PASSWORD,
ARG_WAIT,
+ ARG_WORKING_DIRECTORY,
+ ARG_SHELL,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "user", no_argument, NULL, ARG_USER },
- { "system", no_argument, NULL, ARG_SYSTEM },
- { "scope", no_argument, NULL, ARG_SCOPE },
- { "unit", required_argument, NULL, ARG_UNIT },
- { "description", required_argument, NULL, ARG_DESCRIPTION },
- { "slice", required_argument, NULL, ARG_SLICE },
- { "remain-after-exit", no_argument, NULL, 'r' },
- { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
- { "host", required_argument, NULL, 'H' },
- { "machine", required_argument, NULL, 'M' },
- { "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
- { "wait", no_argument, NULL, ARG_WAIT },
- { "uid", required_argument, NULL, ARG_EXEC_USER },
- { "gid", required_argument, NULL, ARG_EXEC_GROUP },
- { "nice", required_argument, NULL, ARG_NICE },
- { "setenv", required_argument, NULL, 'E' },
- { "property", required_argument, NULL, 'p' },
- { "tty", no_argument, NULL, 't' }, /* deprecated alias */
- { "pty", no_argument, NULL, 't' },
- { "pipe", no_argument, NULL, 'P' },
- { "quiet", no_argument, NULL, 'q' },
- { "on-active", required_argument, NULL, ARG_ON_ACTIVE },
- { "on-boot", required_argument, NULL, ARG_ON_BOOT },
- { "on-startup", required_argument, NULL, ARG_ON_STARTUP },
- { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE },
- { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
- { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
- { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
- { "path-property", required_argument, NULL, ARG_PATH_PROPERTY },
- { "socket-property", required_argument, NULL, ARG_SOCKET_PROPERTY },
- { "no-block", no_argument, NULL, ARG_NO_BLOCK },
- { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
- { "collect", no_argument, NULL, 'G' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "user", no_argument, NULL, ARG_USER },
+ { "system", no_argument, NULL, ARG_SYSTEM },
+ { "scope", no_argument, NULL, ARG_SCOPE },
+ { "unit", required_argument, NULL, ARG_UNIT },
+ { "description", required_argument, NULL, ARG_DESCRIPTION },
+ { "slice", required_argument, NULL, ARG_SLICE },
+ { "remain-after-exit", no_argument, NULL, 'r' },
+ { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
+ { "host", required_argument, NULL, 'H' },
+ { "machine", required_argument, NULL, 'M' },
+ { "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
+ { "wait", no_argument, NULL, ARG_WAIT },
+ { "uid", required_argument, NULL, ARG_EXEC_USER },
+ { "gid", required_argument, NULL, ARG_EXEC_GROUP },
+ { "nice", required_argument, NULL, ARG_NICE },
+ { "setenv", required_argument, NULL, 'E' },
+ { "property", required_argument, NULL, 'p' },
+ { "tty", no_argument, NULL, 't' }, /* deprecated alias */
+ { "pty", no_argument, NULL, 't' },
+ { "pipe", no_argument, NULL, 'P' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "on-active", required_argument, NULL, ARG_ON_ACTIVE },
+ { "on-boot", required_argument, NULL, ARG_ON_BOOT },
+ { "on-startup", required_argument, NULL, ARG_ON_STARTUP },
+ { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE },
+ { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
+ { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
+ { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
+ { "path-property", required_argument, NULL, ARG_PATH_PROPERTY },
+ { "socket-property", required_argument, NULL, ARG_SOCKET_PROPERTY },
+ { "no-block", no_argument, NULL, ARG_NO_BLOCK },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+ { "collect", no_argument, NULL, 'G' },
+ { "working-directory", required_argument, NULL, ARG_WORKING_DIRECTORY },
+ { "same-dir", no_argument, NULL, 'd' },
+ { "shell", no_argument, NULL, 'S' },
{},
};
@@ -191,13 +224,12 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPqG", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPqGdS", options, NULL)) >= 0)
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -352,12 +384,13 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom();
with_timer = with_timer ||
- !!startswith(optarg, "OnActiveSec=") ||
- !!startswith(optarg, "OnBootSec=") ||
- !!startswith(optarg, "OnStartupSec=") ||
- !!startswith(optarg, "OnUnitActiveSec=") ||
- !!startswith(optarg, "OnUnitInactiveSec=") ||
- !!startswith(optarg, "OnCalendar=");
+ STARTSWITH_SET(optarg,
+ "OnActiveSec=",
+ "OnBootSec=",
+ "OnStartupSec=",
+ "OnUnitActiveSec=",
+ "OnUnitInactiveSec=",
+ "OnCalendar=");
break;
case ARG_PATH_PROPERTY:
@@ -382,10 +415,35 @@ static int parse_argv(int argc, char *argv[]) {
arg_wait = true;
break;
+ case ARG_WORKING_DIRECTORY:
+ r = parse_path_argument_and_warn(optarg, true, &arg_working_directory);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case 'd': {
+ _cleanup_free_ char *p = NULL;
+
+ r = safe_getcwd(&p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get current working directory: %m");
+
+ if (empty_or_root(p))
+ arg_working_directory = mfree(arg_working_directory);
+ else
+ free_and_replace(arg_working_directory, p);
+ break;
+ }
+
case 'G':
arg_aggressive_gc = true;
break;
+ case 'S':
+ arg_shell = true;
+ break;
+
case '?':
return -EINVAL;
@@ -396,9 +454,34 @@ static int parse_argv(int argc, char *argv[]) {
with_trigger = !!arg_path_property || !!arg_socket_property || with_timer;
/* currently, only single trigger (path, socket, timer) unit can be created simultaneously */
- if ((int) !!arg_path_property + (int) !!arg_socket_property + (int) with_timer > 1) {
- log_error("Only single trigger (path, socket, timer) unit can be created.");
- return -EINVAL;
+ if ((int) !!arg_path_property + (int) !!arg_socket_property + (int) with_timer > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Only single trigger (path, socket, timer) unit can be created.");
+
+ if (arg_shell) {
+ /* If --shell is imply --pty --pipe --same-dir --service-type=exec --wait --collect, unless otherwise
+ * specified. */
+
+ if (!arg_scope) {
+ if (arg_stdio == ARG_STDIO_NONE)
+ arg_stdio = ARG_STDIO_AUTO;
+
+ if (!arg_working_directory) {
+ r = safe_getcwd(&arg_working_directory);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get current working directory: %m");
+ }
+
+ if (!arg_service_type) {
+ arg_service_type = strdup("exec");
+ if (!arg_service_type)
+ return log_oom();
+ }
+
+ arg_wait = true;
+ }
+
+ arg_aggressive_gc = true;
}
if (arg_stdio == ARG_STDIO_AUTO) {
@@ -410,66 +493,79 @@ static int parse_argv(int argc, char *argv[]) {
ARG_STDIO_DIRECT;
}
- if ((optind >= argc) && (!arg_unit || !with_trigger)) {
- log_error("Command line to execute required.");
- return -EINVAL;
- }
+ if (argc > optind) {
+ char **l;
- if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
- log_error("Execution in user context is not supported on non-local systems.");
- return -EINVAL;
- }
+ if (arg_shell)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "If --shell is used, no command line is expected.");
- if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
- log_error("Scope execution is not supported on non-local systems.");
- return -EINVAL;
- }
+ l = strv_copy(argv + optind);
+ if (!l)
+ return log_oom();
- if (arg_scope && (arg_remain_after_exit || arg_service_type)) {
- log_error("--remain-after-exit and --service-type= are not supported in --scope mode.");
- return -EINVAL;
- }
+ strv_free_and_replace(arg_cmdline, l);
- if (arg_stdio != ARG_STDIO_NONE && (with_trigger || arg_scope)) {
- log_error("--pty/--pipe is not compatible in timer or --scope mode.");
- return -EINVAL;
- }
+ } else if (arg_shell) {
+ _cleanup_free_ char *s = NULL;
+ char **l;
- if (arg_stdio != ARG_STDIO_NONE && arg_transport == BUS_TRANSPORT_REMOTE) {
- log_error("--pty/--pipe is only supported when connecting to the local system or containers.");
- return -EINVAL;
- }
+ r = get_shell(&s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine shell: %m");
- if (arg_stdio != ARG_STDIO_NONE && arg_no_block) {
- log_error("--pty/--pipe is not compatible with --no-block.");
- return -EINVAL;
- }
+ l = strv_new(s);
+ if (!l)
+ return log_oom();
- if (arg_scope && with_trigger) {
- log_error("Path, socket or timer options are not supported in --scope mode.");
- return -EINVAL;
- }
+ strv_free_and_replace(arg_cmdline, l);
- if (arg_timer_property && !with_timer) {
- log_error("--timer-property= has no effect without any other timer options.");
- return -EINVAL;
- }
+ } else if (!arg_unit || !with_trigger)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Command line to execute required.");
+
+ if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Execution in user context is not supported on non-local systems.");
+
+ if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Scope execution is not supported on non-local systems.");
+
+ if (arg_scope && (arg_remain_after_exit || arg_service_type))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--remain-after-exit and --service-type= are not supported in --scope mode.");
+
+ if (arg_stdio != ARG_STDIO_NONE && (with_trigger || arg_scope))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--pty/--pipe is not compatible in timer or --scope mode.");
+
+ if (arg_stdio != ARG_STDIO_NONE && arg_transport == BUS_TRANSPORT_REMOTE)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--pty/--pipe is only supported when connecting to the local system or containers.");
+
+ if (arg_stdio != ARG_STDIO_NONE && arg_no_block)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--pty/--pipe is not compatible with --no-block.");
+
+ if (arg_scope && with_trigger)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Path, socket or timer options are not supported in --scope mode.");
+
+ if (arg_timer_property && !with_timer)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--timer-property= has no effect without any other timer options.");
if (arg_wait) {
- if (arg_no_block) {
- log_error("--wait may not be combined with --no-block.");
- return -EINVAL;
- }
+ if (arg_no_block)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--wait may not be combined with --no-block.");
- if (with_trigger) {
- log_error("--wait may not be combined with path, socket or timer operations.");
- return -EINVAL;
- }
+ if (with_trigger)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--wait may not be combined with path, socket or timer operations.");
- if (arg_scope) {
- log_error("--wait may not be combined with --scope.");
- return -EINVAL;
- }
+ if (arg_scope)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--wait may not be combined with --scope.");
}
return 1;
@@ -528,7 +624,7 @@ static int transient_kill_set_properties(sd_bus_message *m) {
return 0;
}
-static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) {
+static int transient_service_set_properties(sd_bus_message *m, const char *pty_path) {
bool send_term = false;
int r;
@@ -582,6 +678,12 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
return bus_log_create_error(r);
}
+ if (arg_working_directory) {
+ r = sd_bus_message_append(m, "(sv)", "WorkingDirectory", "s", arg_working_directory);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
if (pty_path) {
r = sd_bus_message_append(m,
"(sv)(sv)(sv)(sv)",
@@ -649,7 +751,7 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
}
/* Exec container */
- {
+ if (!strv_isempty(arg_cmdline)) {
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
return bus_log_create_error(r);
@@ -670,11 +772,11 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(m, "s", argv[0]);
+ r = sd_bus_message_append(m, "s", arg_cmdline[0]);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append_strv(m, argv);
+ r = sd_bus_message_append_strv(m, arg_cmdline);
if (r < 0)
return bus_log_create_error(r);
@@ -775,10 +877,10 @@ static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
* name our transient units. */
id = startswith(unique, ":1.");
- if (!id) {
- log_error("Unique name %s has unexpected format.", unique);
- return -EINVAL;
- }
+ if (!id)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unique name %s has unexpected format.",
+ unique);
p = strjoin("run-u", id, ".", unit_type_to_string(t));
if (!p)
@@ -895,7 +997,6 @@ static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
static int start_transient_service(
sd_bus *bus,
- char **argv,
int *retval) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
@@ -906,7 +1007,6 @@ static int start_transient_service(
int r;
assert(bus);
- assert(argv);
assert(retval);
if (arg_stdio == ARG_STDIO_PTY) {
@@ -940,10 +1040,8 @@ static int start_transient_service(
&error,
&pty_reply,
"s", arg_host);
- if (r < 0) {
- log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get machine PTY: %s", bus_error_message(&error, -r));
r = sd_bus_message_read(pty_reply, "hs", &master, &s);
if (r < 0)
@@ -1000,7 +1098,7 @@ static int start_transient_service(
if (r < 0)
return bus_log_create_error(r);
- r = transient_service_set_properties(m, argv, pty_path);
+ r = transient_service_set_properties(m, pty_path);
if (r < 0)
return r;
@@ -1150,20 +1248,50 @@ static int start_transient_service(
return 0;
}
-static int start_transient_scope(
- sd_bus *bus,
- char **argv) {
+static int acquire_invocation_id(sd_bus *bus, sd_id128_t *ret) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ const void *p;
+ size_t l;
+ int r;
+
+ assert(bus);
+ assert(ret);
+ r = sd_bus_get_property(bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1/unit/self",
+ "org.freedesktop.systemd1.Unit",
+ "InvocationID",
+ &error,
+ &reply,
+ "ay");
+ if (r < 0)
+ return log_error_errno(r, "Failed to request invocation ID for scope: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_read_array(reply, 'y', &p, &l);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (l != sizeof(sd_id128_t))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid UUID size, %zu != %zu.", l, sizeof(sd_id128_t));
+
+ memcpy(ret, p, l);
+ return 0;
+}
+
+static int start_transient_scope(sd_bus *bus) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
_cleanup_strv_free_ char **env = NULL, **user_env = NULL;
_cleanup_free_ char *scope = NULL;
const char *object = NULL;
+ sd_id128_t invocation_id;
int r;
assert(bus);
- assert(argv);
+ assert(!strv_isempty(arg_cmdline));
r = bus_wait_for_jobs_new(bus, &w);
if (r < 0)
@@ -1219,10 +1347,24 @@ static int start_transient_scope(
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0) {
- log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r));
+ if (r < 0)
+ return log_error_errno(r, "Failed to start transient scope unit: %s", bus_error_message(&error, -r));
+
+ r = sd_bus_message_read(reply, "o", &object);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = bus_wait_for_jobs_one(w, object, arg_quiet);
+ if (r < 0)
return r;
- }
+
+ r = acquire_invocation_id(bus, &invocation_id);
+ if (r < 0)
+ return r;
+
+ r = strv_extendf(&user_env, "INVOCATION_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(invocation_id));
+ if (r < 0)
+ return log_oom();
if (arg_nice_set) {
if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
@@ -1232,7 +1374,7 @@ static int start_transient_scope(
if (arg_exec_group) {
gid_t gid;
- r = get_group_creds(&arg_exec_group, &gid);
+ r = get_group_creds(&arg_exec_group, &gid, 0);
if (r < 0)
return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
@@ -1245,7 +1387,7 @@ static int start_transient_scope(
uid_t uid;
gid_t gid;
- r = get_user_creds_clean(&arg_exec_user, &uid, &gid, &home, &shell);
+ r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell, USER_CREDS_CLEAN|USER_CREDS_PREFER_NSS);
if (r < 0)
return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
@@ -1282,25 +1424,16 @@ static int start_transient_scope(
if (!env)
return log_oom();
- r = sd_bus_message_read(reply, "o", &object);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = bus_wait_for_jobs_one(w, object, arg_quiet);
- if (r < 0)
- return r;
-
if (!arg_quiet)
log_info("Running scope as unit: %s", scope);
- execvpe(argv[0], argv, env);
+ execvpe(arg_cmdline[0], arg_cmdline, env);
return log_error_errno(errno, "Failed to execute: %m");
}
static int start_transient_trigger(
sd_bus *bus,
- char **argv,
const char *suffix) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -1311,7 +1444,6 @@ static int start_transient_trigger(
int r;
assert(bus);
- assert(argv);
r = bus_wait_for_jobs_new(bus, &w);
if (r < 0)
@@ -1404,7 +1536,7 @@ static int start_transient_trigger(
if (r < 0)
return bus_log_create_error(r);
- if (!strv_isempty(argv)) {
+ if (!strv_isempty(arg_cmdline)) {
r = sd_bus_message_open_container(m, 'r', "sa(sv)");
if (r < 0)
return bus_log_create_error(r);
@@ -1417,7 +1549,7 @@ static int start_transient_trigger(
if (r < 0)
return bus_log_create_error(r);
- r = transient_service_set_properties(m, argv, NULL);
+ r = transient_service_set_properties(m, NULL);
if (r < 0)
return r;
@@ -1437,10 +1569,8 @@ static int start_transient_trigger(
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0) {
- log_error("Failed to start transient %s unit: %s", suffix + 1, bus_error_message(&error, -r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to start transient %s unit: %s", suffix + 1, bus_error_message(&error, -r));
r = sd_bus_message_read(reply, "o", &object);
if (r < 0)
@@ -1452,16 +1582,16 @@ static int start_transient_trigger(
if (!arg_quiet) {
log_info("Running %s as unit: %s", suffix + 1, trigger);
- if (argv[0])
+ if (!strv_isempty(arg_cmdline))
log_info("Will run service as unit: %s", service);
}
return 0;
}
-int main(int argc, char* argv[]) {
+static int run(int argc, char* argv[]) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_free_ char *description = NULL, *command = NULL;
+ _cleanup_free_ char *description = NULL;
int r, retval = EXIT_SUCCESS;
log_parse_environment();
@@ -1469,31 +1599,29 @@ int main(int argc, char* argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
+
+ if (!strv_isempty(arg_cmdline) && arg_transport == BUS_TRANSPORT_LOCAL) {
+ _cleanup_free_ char *command = NULL;
- if (argc > optind && arg_transport == BUS_TRANSPORT_LOCAL) {
/* Patch in an absolute path */
- r = find_binary(argv[optind], &command);
- if (r < 0) {
- log_error_errno(r, "Failed to find executable %s: %m", argv[optind]);
- goto finish;
- }
+ r = find_binary(arg_cmdline[0], &command);
+ if (r < 0)
+ return log_error_errno(r, "Failed to find executable %s: %m", arg_cmdline[0]);
- argv[optind] = command;
+ free_and_replace(arg_cmdline[0], command);
}
if (!arg_description) {
- description = strv_join(argv + optind, " ");
- if (!description) {
- r = log_oom();
- goto finish;
- }
+ description = strv_join(arg_cmdline, " ");
+ if (!description)
+ return log_oom();
if (arg_unit && isempty(description)) {
r = free_and_strdup(&description, arg_unit);
if (r < 0)
- goto finish;
+ return log_oom();
}
arg_description = description;
@@ -1505,28 +1633,23 @@ int main(int argc, char* argv[]) {
r = bus_connect_transport(arg_transport, arg_host, arg_user, &bus);
else
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
- if (r < 0) {
- log_error_errno(r, "Failed to create bus connection: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
if (arg_scope)
- r = start_transient_scope(bus, argv + optind);
+ r = start_transient_scope(bus);
else if (arg_path_property)
- r = start_transient_trigger(bus, argv + optind, ".path");
+ r = start_transient_trigger(bus, ".path");
else if (arg_socket_property)
- r = start_transient_trigger(bus, argv + optind, ".socket");
+ r = start_transient_trigger(bus, ".socket");
else if (with_timer)
- r = start_transient_trigger(bus, argv + optind, ".timer");
+ r = start_transient_trigger(bus, ".timer");
else
- r = start_transient_service(bus, argv + optind, &retval);
-
-finish:
- strv_free(arg_environment);
- strv_free(arg_property);
- strv_free(arg_path_property);
- strv_free(arg_socket_property);
- strv_free(arg_timer_property);
+ r = start_transient_service(bus, &retval);
+ if (r < 0)
+ return r;
- return r < 0 ? EXIT_FAILURE : retval;
+ return retval;
}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c
index 1eaf653103..6f0657174c 100644
--- a/src/shared/acl-util.c
+++ b/src/shared/acl-util.c
@@ -219,14 +219,11 @@ int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want
STRV_FOREACH(entry, split) {
char *p;
- p = startswith(*entry, "default:");
+ p = STARTSWITH_SET(*entry, "default:", "d:");
if (!p)
- p = startswith(*entry, "d:");
+ p = *entry;
- if (p)
- r = strv_push(&d, p);
- else
- r = strv_push(&a, *entry);
+ r = strv_push(&d, p);
if (r < 0)
return r;
}
diff --git a/src/shared/acpi-fpdt.h b/src/shared/acpi-fpdt.h
index 47bfefc5a0..8d288937ef 100644
--- a/src/shared/acpi-fpdt.h
+++ b/src/shared/acpi-fpdt.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <time-util.h>
int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit);
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 682dc754fc..246e27a135 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -27,6 +27,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
+#include "fs-util.h"
#include "io-util.h"
#include "log.h"
#include "macro.h"
@@ -40,6 +41,7 @@
#include "strv.h"
#include "terminal-util.h"
#include "time-util.h"
+#include "tmpfile-util.h"
#include "umask-util.h"
#include "utf8.h"
#include "util.h"
@@ -79,7 +81,7 @@ static int retrieve_key(key_serial_t serial, char ***ret) {
if (n < m)
break;
- explicit_bzero(p, n);
+ explicit_bzero_safe(p, n);
free(p);
m *= 2;
}
@@ -88,7 +90,7 @@ static int retrieve_key(key_serial_t serial, char ***ret) {
if (!l)
return -ENOMEM;
- explicit_bzero(p, n);
+ explicit_bzero_safe(p, n);
*ret = l;
return 0;
@@ -124,7 +126,7 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
return r;
serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING);
- explicit_bzero(p, n);
+ explicit_bzero_safe(p, n);
if (serial == -1)
return -errno;
@@ -133,6 +135,9 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
(unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0)
log_debug_errno(errno, "Failed to adjust timeout: %m");
+ /* Tell everyone to check the keyring */
+ (void) touch("/run/systemd/ask-password");
+
log_debug("Added key to keyring as %" PRIi32 ".", serial);
return 1;
@@ -211,7 +216,7 @@ int ask_password_tty(
usec_t until,
AskPasswordFlags flags,
const char *flag_file,
- char **ret) {
+ char ***ret) {
enum {
POLL_TTY,
@@ -223,6 +228,7 @@ int ask_password_tty(
_cleanup_close_ int cttyfd = -1, notify = -1;
struct termios old_termios, new_termios;
char passphrase[LINE_MAX + 1] = {}, *x;
+ _cleanup_strv_free_erase_ char **l = NULL;
struct pollfd pollfd[_POLL_MAX];
size_t p = 0, codepoint = 0;
int r;
@@ -235,14 +241,25 @@ int ask_password_tty(
if (!message)
message = "Password:";
- if (flag_file) {
+ if (flag_file || ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname)) {
notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
if (notify < 0)
return -errno;
-
+ }
+ if (flag_file) {
if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0)
return -errno;
}
+ if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) {
+ r = ask_password_keyring(keyname, flags, ret);
+ if (r >= 0)
+ return 0;
+ else if (r != -ENOKEY)
+ return r;
+
+ if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_ATTRIB /* for mtime */) < 0)
+ return -errno;
+ }
/* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
if (ttyfd < 0)
@@ -324,9 +341,17 @@ int ask_password_tty(
goto finish;
}
- if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
+ if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) {
(void) flush_fd(notify);
+ r = ask_password_keyring(keyname, flags, ret);
+ if (r >= 0) {
+ r = 0;
+ goto finish;
+ } else if (r != -ENOKEY)
+ goto finish;
+ }
+
if (pollfd[POLL_TTY].revents == 0)
continue;
@@ -349,7 +374,7 @@ int ask_password_tty(
if (!(flags & ASK_PASSWORD_SILENT))
backspace_string(ttyfd, passphrase);
- explicit_bzero(passphrase, sizeof(passphrase));
+ explicit_bzero_safe(passphrase, sizeof(passphrase));
p = codepoint = 0;
} else if (IN_SET(c, '\b', 127)) {
@@ -379,7 +404,7 @@ int ask_password_tty(
}
p = codepoint = q == (size_t) -1 ? p - 1 : q;
- explicit_bzero(passphrase + p, sizeof(passphrase) - p);
+ explicit_bzero_safe(passphrase + p, sizeof(passphrase) - p);
} else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) {
@@ -430,16 +455,20 @@ int ask_password_tty(
}
x = strndup(passphrase, p);
- explicit_bzero(passphrase, sizeof(passphrase));
+ explicit_bzero_safe(passphrase, sizeof(passphrase));
if (!x) {
r = -ENOMEM;
goto finish;
}
+ r = strv_consume(&l, x);
+ if (r < 0)
+ goto finish;
+
if (keyname)
- (void) add_to_keyring_and_log(keyname, flags, STRV_MAKE(x));
+ (void) add_to_keyring_and_log(keyname, flags, l);
- *ret = x;
+ *ret = TAKE_PTR(l);
r = 0;
finish:
@@ -451,41 +480,36 @@ finish:
return r;
}
-static int create_socket(char **name) {
- union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- };
+static int create_socket(char **ret) {
+ _cleanup_free_ char *path = NULL;
+ union sockaddr_union sa = {};
_cleanup_close_ int fd = -1;
- static const int one = 1;
- char *c;
- int r;
+ int salen, r;
- assert(name);
+ assert(ret);
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return -errno;
- snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
+ 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;
RUN_WITH_UMASK(0177) {
- if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+ if (bind(fd, &sa.sa, salen) < 0)
return -errno;
}
- if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
- return -errno;
-
- c = strdup(sa.un.sun_path);
- if (!c)
- return -ENOMEM;
-
- *name = c;
-
- r = fd;
- fd = -1;
+ r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
+ if (r < 0)
+ return r;
- return r;
+ *ret = TAKE_PTR(path);
+ return TAKE_FD(fd);
}
int ask_password_agent(
@@ -500,14 +524,15 @@ int ask_password_agent(
enum {
FD_SOCKET,
FD_SIGNAL,
+ FD_INOTIFY,
_FD_MAX
};
- _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1;
+ _cleanup_close_ int socket_fd = -1, signal_fd = -1, notify = -1, fd = -1;
char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
char final[sizeof(temp)] = "";
_cleanup_free_ char *socket_name = NULL;
- _cleanup_strv_free_ char **l = NULL;
+ _cleanup_strv_free_erase_ char **l = NULL;
_cleanup_fclose_ FILE *f = NULL;
struct pollfd pollfd[_FD_MAX];
sigset_t mask, oldmask;
@@ -524,6 +549,25 @@ int ask_password_agent(
(void) mkdir_p_label("/run/systemd/ask-password", 0755);
+ if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) {
+ r = ask_password_keyring(keyname, flags, ret);
+ if (r >= 0) {
+ r = 0;
+ goto finish;
+ } else if (r != -ENOKEY)
+ goto finish;
+
+ notify = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
+ if (notify < 0) {
+ r = -errno;
+ goto finish;
+ }
+ if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_ATTRIB /* for mtime */) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
fd = mkostemp_safe(temp);
if (fd < 0) {
r = fd;
@@ -594,6 +638,8 @@ int ask_password_agent(
pollfd[FD_SOCKET].events = POLLIN;
pollfd[FD_SIGNAL].fd = signal_fd;
pollfd[FD_SIGNAL].events = POLLIN;
+ pollfd[FD_INOTIFY].fd = notify;
+ pollfd[FD_INOTIFY].events = POLLIN;
for (;;) {
char passphrase[LINE_MAX+1];
@@ -615,7 +661,7 @@ int ask_password_agent(
goto finish;
}
- k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1);
+ k = poll(pollfd, notify >= 0 ? _FD_MAX : _FD_MAX - 1, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1);
if (k < 0) {
if (errno == EINTR)
continue;
@@ -634,14 +680,26 @@ int ask_password_agent(
goto finish;
}
+ if (notify >= 0 && pollfd[FD_INOTIFY].revents != 0) {
+ (void) flush_fd(notify);
+
+ r = ask_password_keyring(keyname, flags, ret);
+ if (r >= 0) {
+ r = 0;
+ goto finish;
+ } else if (r != -ENOKEY)
+ goto finish;
+ }
+
+ if (pollfd[FD_SOCKET].revents == 0)
+ continue;
+
if (pollfd[FD_SOCKET].revents != POLLIN) {
r = -EIO;
goto finish;
}
- zero(iovec);
- iovec.iov_base = passphrase;
- iovec.iov_len = sizeof(passphrase);
+ iovec = IOVEC_MAKE(passphrase, sizeof(passphrase));
zero(control);
zero(msghdr);
@@ -683,10 +741,10 @@ int ask_password_agent(
if (passphrase[0] == '+') {
/* An empty message refers to the empty password */
if (n == 1)
- l = strv_new("", NULL);
+ l = strv_new("");
else
l = strv_parse_nulstr(passphrase+1, n-1);
- explicit_bzero(passphrase, n);
+ explicit_bzero_safe(passphrase, n);
if (!l) {
r = -ENOMEM;
goto finish;
@@ -741,29 +799,17 @@ int ask_password_auto(
assert(ret);
- if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) {
+ if ((flags & ASK_PASSWORD_ACCEPT_CACHED) &&
+ keyname &&
+ ((flags & ASK_PASSWORD_NO_TTY) || !isatty(STDIN_FILENO)) &&
+ (flags & ASK_PASSWORD_NO_AGENT)) {
r = ask_password_keyring(keyname, flags, ret);
if (r != -ENOKEY)
return r;
}
- if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO)) {
- char *s = NULL, **l = NULL;
-
- r = ask_password_tty(-1, message, keyname, until, flags, NULL, &s);
- if (r < 0)
- return r;
-
- r = strv_push(&l, s);
- if (r < 0) {
- string_erase(s);
- free(s);
- return -ENOMEM;
- }
-
- *ret = l;
- return 0;
- }
+ if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO))
+ return ask_password_tty(-1, message, keyname, until, flags, NULL, ret);
if (!(flags & ASK_PASSWORD_NO_AGENT))
return ask_password_agent(message, icon, id, keyname, until, flags, ret);
diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h
index 93ca8bff52..2d84ba6b04 100644
--- a/src/shared/ask-password-api.h
+++ b/src/shared/ask-password-api.h
@@ -15,7 +15,7 @@ typedef enum AskPasswordFlags {
ASK_PASSWORD_CONSOLE_COLOR = 1 << 6, /* Use color if /dev/console points to a console that supports color */
} AskPasswordFlags;
-int ask_password_tty(int tty_fd, const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char **ret);
+int ask_password_tty(int tty_fd, const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char ***ret);
int ask_password_agent(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret);
int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret);
int ask_password_auto(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret);
diff --git a/src/basic/barrier.c b/src/shared/barrier.c
index 587852aac8..bb5869dad4 100644
--- a/src/basic/barrier.c
+++ b/src/shared/barrier.c
@@ -150,9 +150,7 @@ void barrier_destroy(Barrier *b) {
* Note that barriers could be supported without fork() or clone(). However,
* this is currently not needed so it hasn't been implemented.
*/
-void barrier_set_role(Barrier *b, unsigned int role) {
- int fd;
-
+void barrier_set_role(Barrier *b, unsigned role) {
assert(b);
assert(IN_SET(role, BARRIER_PARENT, BARRIER_CHILD));
/* make sure this is only called once */
@@ -164,9 +162,7 @@ void barrier_set_role(Barrier *b, unsigned int role) {
b->pipe[0] = safe_close(b->pipe[0]);
/* swap me/them for children */
- fd = b->me;
- b->me = b->them;
- b->them = fd;
+ SWAP_TWO(b->me, b->them);
}
}
diff --git a/src/basic/barrier.h b/src/shared/barrier.h
index 96a9955c6f..0eb3d2792e 100644
--- a/src/basic/barrier.h
+++ b/src/shared/barrier.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
@@ -42,7 +41,7 @@ void barrier_destroy(Barrier *b);
DEFINE_TRIVIAL_CLEANUP_FUNC(Barrier*, barrier_destroy);
-void barrier_set_role(Barrier *b, unsigned int role);
+void barrier_set_role(Barrier *b, unsigned role);
bool barrier_place(Barrier *b);
bool barrier_abort(Barrier *b);
diff --git a/src/shared/base-filesystem.h b/src/shared/base-filesystem.h
index d1d4eaa662..39d970811d 100644
--- a/src/shared/base-filesystem.h
+++ b/src/shared/base-filesystem.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <sys/types.h>
int base_filesystem_create(const char *root, uid_t uid, gid_t gid);
diff --git a/src/basic/bitmap.c b/src/shared/bitmap.c
index c17c6a7a02..a4cd6451b0 100644
--- a/src/basic/bitmap.c
+++ b/src/shared/bitmap.c
@@ -206,7 +206,7 @@ bool bitmap_equal(Bitmap *a, Bitmap *b) {
return true;
common_n_bitmaps = MIN(a->n_bitmaps, b->n_bitmaps);
- if (memcmp(a->bitmaps, b->bitmaps, sizeof(uint64_t) * common_n_bitmaps) != 0)
+ if (memcmp_safe(a->bitmaps, b->bitmaps, sizeof(uint64_t) * common_n_bitmaps) != 0)
return false;
c = a->n_bitmaps > b->n_bitmaps ? a : b;
diff --git a/src/basic/bitmap.h b/src/shared/bitmap.h
index 7ff5cffd26..843d27d24d 100644
--- a/src/basic/bitmap.h
+++ b/src/shared/bitmap.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <stdbool.h>
#include "hashmap.h"
diff --git a/src/basic/blkid-util.h b/src/shared/blkid-util.h
index e4eb600ed6..eb07a88ba9 100644
--- a/src/basic/blkid-util.h
+++ b/src/shared/blkid-util.h
@@ -2,11 +2,9 @@
#pragma once
#if HAVE_BLKID
-#include <blkid.h>
-#endif
+# include <blkid.h>
-#include "util.h"
+# include "macro.h"
-#if HAVE_BLKID
DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe);
#endif
diff --git a/src/shared/boot-timestamps.h b/src/shared/boot-timestamps.h
index c719dd3602..4e648f18e2 100644
--- a/src/shared/boot-timestamps.h
+++ b/src/shared/boot-timestamps.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <time-util.h>
int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader);
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index 47a7dbafbd..7e276f1edf 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -12,18 +12,21 @@
#include "def.h"
#include "device-nodes.h"
#include "efivars.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "parse-util.h"
+#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "virt.h"
-void boot_entry_free(BootEntry *entry) {
+static void boot_entry_free(BootEntry *entry) {
assert(entry);
- free(entry->filename);
+ free(entry->id);
+ free(entry->path);
free(entry->title);
free(entry->show_title);
free(entry->version);
@@ -36,7 +39,7 @@ void boot_entry_free(BootEntry *entry) {
free(entry->device_tree);
}
-int boot_entry_load(const char *path, BootEntry *entry) {
+static int boot_entry_load(const char *path, BootEntry *entry) {
_cleanup_(boot_entry_free) BootEntry tmp = {};
_cleanup_fclose_ FILE *f = NULL;
unsigned line = 1;
@@ -53,8 +56,12 @@ int boot_entry_load(const char *path, BootEntry *entry) {
}
b = basename(path);
- tmp.filename = strndup(b, c - b);
- if (!tmp.filename)
+ tmp.id = strndup(b, c - b);
+ if (!tmp.id)
+ return log_oom();
+
+ tmp.path = strdup(path);
+ if (!tmp.path)
return log_oom();
f = fopen(path, "re");
@@ -139,7 +146,7 @@ void boot_config_free(BootConfig *config) {
free(config->entries);
}
-int boot_loader_read_conf(const char *path, BootConfig *config) {
+static int boot_loader_read_conf(const char *path, BootConfig *config) {
_cleanup_fclose_ FILE *f = NULL;
unsigned line = 1;
int r;
@@ -148,8 +155,12 @@ int boot_loader_read_conf(const char *path, BootConfig *config) {
assert(config);
f = fopen(path, "re");
- if (!f)
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
+
return log_error_errno(errno, "Failed to open \"%s\": %m", path);
+ }
for (;;) {
_cleanup_free_ char *buf = NULL, *field = NULL;
@@ -199,16 +210,14 @@ int boot_loader_read_conf(const char *path, BootConfig *config) {
return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
}
- return 0;
+ return 1;
}
-static int boot_entry_compare(const void *a, const void *b) {
- const BootEntry *aa = a, *bb = b;
-
- return str_verscmp(aa->filename, bb->filename);
+static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
+ return str_verscmp(a->id, b->id);
}
-int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) {
+static int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) {
_cleanup_strv_free_ char **files = NULL;
char **f;
int r;
@@ -234,7 +243,7 @@ int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_en
n++;
}
- qsort_safe(array, n, sizeof(BootEntry), boot_entry_compare);
+ typesafe_qsort(array, n, boot_entry_compare);
*ret_entries = array;
*ret_n_entries = n;
@@ -302,7 +311,7 @@ static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
/* Add file name to non-unique titles */
for (i = 0; i < n_entries; i++)
if (arr[i]) {
- r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].filename);
+ r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id);
if (r < 0)
return -ENOMEM;
@@ -319,30 +328,30 @@ static int boot_entries_select_default(const BootConfig *config) {
if (config->entry_oneshot)
for (i = config->n_entries - 1; i >= 0; i--)
- if (streq(config->entry_oneshot, config->entries[i].filename)) {
- log_debug("Found default: filename \"%s\" is matched by LoaderEntryOneShot",
- config->entries[i].filename);
+ if (streq(config->entry_oneshot, config->entries[i].id)) {
+ log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
+ config->entries[i].id);
return i;
}
if (config->entry_default)
for (i = config->n_entries - 1; i >= 0; i--)
- if (streq(config->entry_default, config->entries[i].filename)) {
- log_debug("Found default: filename \"%s\" is matched by LoaderEntryDefault",
- config->entries[i].filename);
+ if (streq(config->entry_default, config->entries[i].id)) {
+ log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
+ config->entries[i].id);
return i;
}
if (config->default_pattern)
for (i = config->n_entries - 1; i >= 0; i--)
- if (fnmatch(config->default_pattern, config->entries[i].filename, FNM_CASEFOLD) == 0) {
- log_debug("Found default: filename \"%s\" is matched by pattern \"%s\"",
- config->entries[i].filename, config->default_pattern);
+ if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) {
+ log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
+ config->entries[i].id, config->default_pattern);
return i;
}
if (config->n_entries > 0)
- log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].filename);
+ log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
else
log_debug("Found no default boot entry :(");
@@ -359,24 +368,26 @@ int boot_entries_load_config(const char *esp_path, BootConfig *config) {
p = strjoina(esp_path, "/loader/loader.conf");
r = boot_loader_read_conf(p, config);
if (r < 0)
- return log_error_errno(r, "Failed to read boot config from \"%s\": %m", p);
+ return r;
p = strjoina(esp_path, "/loader/entries");
r = boot_entries_find(p, &config->entries, &config->n_entries);
if (r < 0)
- return log_error_errno(r, "Failed to read boot entries from \"%s\": %m", p);
+ return r;
r = boot_entries_uniquify(config->entries, config->n_entries);
if (r < 0)
return log_error_errno(r, "Failed to uniquify boot entries: %m");
- r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
- if (r < 0 && r != -ENOENT)
- return log_error_errno(r, "Failed to read EFI var \"LoaderEntryOneShot\": %m");
+ if (is_efi_boot()) {
+ r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to read EFI var \"LoaderEntryOneShot\": %m");
- r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
- if (r < 0 && r != -ENOENT)
- return log_error_errno(r, "Failed to read EFI var \"LoaderEntryDefault\": %m");
+ r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to read EFI var \"LoaderEntryDefault\": %m");
+ }
config->default_entry = boot_entries_select_default(config);
return 0;
@@ -394,7 +405,7 @@ static int verify_esp(
sd_id128_t *ret_uuid) {
#if HAVE_BLKID
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
- char t[DEV_NUM_PATH_MAX];
+ _cleanup_free_ char *node = NULL;
const char *v;
#endif
uint64_t pstart = 0, psize = 0;
@@ -403,28 +414,33 @@ static int verify_esp(
struct statfs sfs;
sd_id128_t uuid = SD_ID128_NULL;
uint32_t part = 0;
+ bool relax_checks;
int r;
assert(p);
+ relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
+
/* Non-root user can only check the status, so if an error occured in the following, it does not cause any
* issues. Let's also, silence the error messages. */
- if (statfs(p, &sfs) < 0) {
- /* If we are searching for the mount point, don't generate a log message if we can't find the path */
- if (errno == ENOENT && searching)
- return -ENOENT;
+ if (!relax_checks) {
+ if (statfs(p, &sfs) < 0) {
+ /* If we are searching for the mount point, don't generate a log message if we can't find the path */
+ if (errno == ENOENT && searching)
+ return -ENOENT;
- return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
- "Failed to check file system type of \"%s\": %m", p);
- }
+ return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
+ "Failed to check file system type of \"%s\": %m", p);
+ }
- if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) {
- if (searching)
- return -EADDRNOTAVAIL;
+ if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) {
+ if (searching)
+ return -EADDRNOTAVAIL;
- log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
- return -ENODEV;
+ log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
+ return -ENODEV;
+ }
}
if (stat(p, &st) < 0)
@@ -449,13 +465,15 @@ static int verify_esp(
/* In a container we don't have access to block devices, skip this part of the verification, we trust the
* container manager set everything up correctly on its own. Also skip the following verification for non-root user. */
- if (detect_container() > 0 || unprivileged_mode)
+ if (detect_container() > 0 || unprivileged_mode || relax_checks)
goto finish;
#if HAVE_BLKID
- xsprintf_dev_num_path(t, "block", st.st_dev);
+ r = device_path_make_major_minor(S_IFBLK, st.st_dev, &node);
+ if (r < 0)
+ return log_error_errno(r, "Failed to format major/minor device path: %m");
errno = 0;
- b = blkid_new_probe_from_filename(t);
+ b = blkid_new_probe_from_filename(node);
if (!b)
return log_error_errno(errno ?: ENOMEM, "Failed to open file system \"%s\": %m", p);
@@ -575,6 +593,18 @@ int find_esp_and_warn(
goto found;
}
+ path = getenv("SYSTEMD_ESP_PATH");
+ if (path) {
+ if (!path_is_valid(path) || !path_is_absolute(path))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
+ path);
+
+ /* Note: when the user explicitly configured things with an env var we won't validate the mount
+ * point. After all we want this to be useful for testing. */
+ goto found;
+ }
+
FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
@@ -600,3 +630,36 @@ found:
return 0;
}
+
+int find_default_boot_entry(
+ const char *esp_path,
+ char **esp_where,
+ BootConfig *config,
+ const BootEntry **e) {
+
+ _cleanup_free_ char *where = NULL;
+ int r;
+
+ assert(config);
+ assert(e);
+
+ r = find_esp_and_warn(esp_path, false, &where, NULL, NULL, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ r = boot_entries_load_config(where, config);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", where);
+
+ if (config->default_entry < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+ "No entry suitable as default, refusing to guess.");
+
+ *e = &config->entries[config->default_entry];
+ log_debug("Found default boot entry in file \"%s\"", (*e)->path);
+
+ if (esp_where)
+ *esp_where = TAKE_PTR(where);
+
+ return 0;
+}
diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h
index f47c073a46..ed576210fe 100644
--- a/src/shared/bootspec.h
+++ b/src/shared/bootspec.h
@@ -2,11 +2,15 @@
#pragma once
-#include <stdlib.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
-typedef struct BootEntry {
- char *filename;
+#include "sd-id128.h"
+typedef struct BootEntry {
+ char *id; /* This is the file basename without extension */
+ char *path; /* This is the full path to the file */
char *title;
char *show_title;
char *version;
@@ -35,16 +39,13 @@ typedef struct BootConfig {
ssize_t default_entry;
} BootConfig;
-void boot_entry_free(BootEntry *entry);
-int boot_entry_load(const char *path, BootEntry *entry);
-int boot_entries_find(const char *dir, BootEntry **entries, size_t *n_entries);
-
-int boot_loader_read_conf(const char *path, BootConfig *config);
void boot_config_free(BootConfig *config);
int boot_entries_load_config(const char *esp_path, BootConfig *config);
static inline const char* boot_entry_title(const BootEntry *entry) {
- return entry->show_title ?: entry->title ?: entry->filename;
+ return entry->show_title ?: entry->title ?: entry->id;
}
int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid);
+
+int find_default_boot_entry(const char *esp_path, char **esp_where, BootConfig *config, const BootEntry **e);
diff --git a/src/basic/bpf-program.c b/src/shared/bpf-program.c
index ed57f9ffdc..2c61e04132 100644
--- a/src/basic/bpf-program.c
+++ b/src/shared/bpf-program.c
@@ -29,25 +29,8 @@ int bpf_program_new(uint32_t prog_type, BPFProgram **ret) {
return 0;
}
-BPFProgram *bpf_program_ref(BPFProgram *p) {
- if (!p)
- return NULL;
-
- assert(p->n_ref > 0);
- p->n_ref++;
-
- return p;
-}
-
-BPFProgram *bpf_program_unref(BPFProgram *p) {
- if (!p)
- return NULL;
-
- assert(p->n_ref > 0);
- p->n_ref--;
-
- if (p->n_ref > 0)
- return NULL;
+static BPFProgram *bpf_program_free(BPFProgram *p) {
+ assert(p);
/* Unfortunately, the kernel currently doesn't implicitly detach BPF programs from their cgroups when the last
* fd to the BPF program is closed. This has nasty side-effects since this means that abnormally terminated
@@ -66,6 +49,8 @@ BPFProgram *bpf_program_unref(BPFProgram *p) {
return mfree(p);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(BPFProgram, bpf_program, bpf_program_free);
+
int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *instructions, size_t count) {
assert(p);
diff --git a/src/basic/bpf-program.h b/src/shared/bpf-program.h
index c21eb2f72a..c21eb2f72a 100644
--- a/src/basic/bpf-program.h
+++ b/src/shared/bpf-program.h
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 3238b442c0..dce8646f37 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -15,9 +15,11 @@
#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
+#include "ip-protocol-list.h"
#include "list.h"
#include "locale-util.h"
-#include "mount-util.h"
+#include "missing_fs.h"
+#include "mountpoint-util.h"
#include "nsflags.h"
#include "parse-util.h"
#include "path-util.h"
@@ -25,7 +27,6 @@
#include "rlimit-util.h"
#include "securebits-util.h"
#include "signal-util.h"
-#include "socket-protocol-list.h"
#include "string-util.h"
#include "syslog-util.h"
#include "terminal-util.h"
@@ -83,10 +84,8 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
int r; \
\
r = parse_func(eq); \
- if (r < 0) { \
- log_error("Failed to parse %s: %s", field, eq); \
- return -EINVAL; \
- } \
+ if (r < 0) \
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse %s: %s", field, eq); \
\
r = sd_bus_message_append(m, "(sv)", field, \
bus_type, (int32_t) r); \
@@ -105,7 +104,7 @@ DEFINE_BUS_APPEND_PARSE("i", parse_errno);
DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string);
DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string);
DEFINE_BUS_APPEND_PARSE("i", signal_from_string);
-DEFINE_BUS_APPEND_PARSE("i", socket_protocol_from_name);
+DEFINE_BUS_APPEND_PARSE("i", parse_ip_protocol);
DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority);
DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice);
DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi);
@@ -277,8 +276,8 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
case '+':
case '!':
/* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
- log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
- return -EOPNOTSUPP;
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Sorry, but +, ! and !! are currently not supported for transient services.");
default:
done = true;
@@ -413,7 +412,7 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
return 1;
}
- if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
+ if (STR_IN_SET(field, "MemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
if (isempty(eq) || streq(eq, "infinity")) {
r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
@@ -422,16 +421,16 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
return 1;
}
- r = parse_percent(eq);
+ r = parse_permille(eq);
if (r >= 0) {
char *n;
- /* When this is a percentage we'll convert this into a relative value in the range
- * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
- * ones). This way the physical memory size can be determined server-side */
+ /* When this is a percentage we'll convert this into a relative value in the range 0…UINT32_MAX
+ * and pass it in the MemoryLowScale property (and related ones). This way the physical memory
+ * size can be determined server-side. */
n = strjoina(field, "Scale");
- r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
+ r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
if (r < 0)
return bus_log_create_error(r);
@@ -449,13 +448,14 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
else {
- r = parse_percent_unbounded(eq);
- if (r <= 0) {
- log_error_errno(r, "CPU quota '%s' invalid.", eq);
- return -EINVAL;
- }
+ r = parse_permille_unbounded(eq);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
+ "CPU quota too small.");
+ if (r < 0)
+ return log_error_errno(r, "CPU quota '%s' invalid.", eq);
- r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U);
+ r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 1000U));
}
if (r < 0)
@@ -495,17 +495,17 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
uint64_t bytes;
e = strchr(eq, ' ');
- if (!e) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
+ if (!e)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse %s value %s.",
+ field, eq);
path = strndupa(eq, e - eq);
bandwidth = e+1;
- if (streq(bandwidth, "infinity")) {
+ if (streq(bandwidth, "infinity"))
bytes = CGROUP_LIMIT_MAX;
- } else {
+ else {
r = parse_size(bandwidth, 1000, &bytes);
if (r < 0)
return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
@@ -529,10 +529,10 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
uint64_t u;
e = strchr(eq, ' ');
- if (!e) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
+ if (!e)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse %s value %s.",
+ field, eq);
path = strndupa(eq, e - eq);
weight = e+1;
@@ -550,6 +550,37 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
return 1;
}
+ if (streq(field, "IODeviceLatencyTargetSec")) {
+ const char *field_usec = "IODeviceLatencyTargetUSec";
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", USEC_INFINITY);
+ else {
+ const char *path, *target, *e;
+ usec_t usec;
+
+ e = strchr(eq, ' ');
+ if (!e)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse %s value %s.",
+ field, eq);
+
+ path = strndupa(eq, e - eq);
+ target = e+1;
+
+ r = parse_sec(target, &usec);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s value %s: %m", field, target);
+
+ r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 1, path, usec);
+ }
+
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) {
unsigned char prefixlen;
union in_addr_union prefix = {};
@@ -634,13 +665,25 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
return bus_log_create_error(r);
} else {
- r = in_addr_prefix_from_string_auto(eq, &family, &prefix, &prefixlen);
- if (r < 0)
- return log_error_errno(r, "Failed to parse IP address prefix: %s", eq);
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
- r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
- if (r < 0)
- return bus_log_create_error(r);
+ r = extract_first_word(&eq, &word, NULL, 0);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s: %s", field, eq);
+
+ r = in_addr_prefix_from_string_auto(word, &family, &prefix, &prefixlen);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse IP address prefix: %s", word);
+
+ r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
}
r = sd_bus_message_close_container(m);
@@ -755,6 +798,14 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
return bus_append_parse_nsec(m, field, eq);
+ if (streq(field, "LogRateLimitIntervalSec"))
+
+ return bus_append_parse_sec_rename(m, field, eq);
+
+ if (streq(field, "LogRateLimitBurst"))
+
+ return bus_append_safe_atou(m, field, eq);
+
if (streq(field, "MountFlags"))
return bus_append_mount_propagation_flags_from_string(m, field, eq);
@@ -823,9 +874,11 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
} else if ((n = startswith(eq, "file:"))) {
appended = strjoina(field, "File");
r = sd_bus_message_append(m, "(sv)", appended, "s", n);
+ } else if ((n = startswith(eq, "append:"))) {
+ appended = strjoina(field, "FileToAppend");
+ r = sd_bus_message_append(m, "(sv)", appended, "s", n);
} else
r = sd_bus_message_append(m, "(sv)", field, "s", eq);
-
if (r < 0)
return bus_log_create_error(r);
@@ -1072,10 +1125,10 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
- if (r == 0) {
- log_error("Missing argument after ':': %s", eq);
- return -EINVAL;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Missing argument after ':': %s",
+ eq);
d = destination;
@@ -1090,10 +1143,10 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
flags = MS_REC;
else if (streq(options, "norbind"))
flags = 0;
- else {
- log_error("Unknown options: %s", eq);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown options: %s",
+ eq);
}
} else
d = s;
@@ -1151,10 +1204,10 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
- if (r == 0) {
- log_error("Failed to parse argument: %s", p);
- return -EINVAL;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse argument: %s",
+ p);
r = sd_bus_message_append(m, "(ss)", path, w);
if (r < 0)
@@ -1189,7 +1242,7 @@ static int bus_append_kill_property(sd_bus_message *m, const char *field, const
return bus_append_parse_boolean(m, field, eq);
- if (streq(field, "KillSignal"))
+ if (STR_IN_SET(field, "KillSignal", "FinalKillSignal", "WatchdogSignal"))
return bus_append_signal_from_string(m, field, eq);
@@ -1414,7 +1467,7 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons
if (streq(field, "SocketProtocol"))
- return bus_append_socket_protocol_from_name(m, field, eq);
+ return bus_append_parse_ip_protocol(m, field, eq);
if (STR_IN_SET(field,
"ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
@@ -1505,6 +1558,25 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const
return bus_append_safe_atou(m, field, eq);
+ if (STR_IN_SET(field, "SuccessActionExitStatus", "FailureActionExitStatus")) {
+
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", field, "i", -1);
+ else {
+ uint8_t u;
+
+ r = safe_atou8(eq, &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s=%s", field, eq);
+
+ r = sd_bus_message_append(m, "(sv)", field, "i", (int) u);
+ }
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
if (unit_dependency_from_string(field) >= 0 ||
STR_IN_SET(field, "Documentation", "RequiresMountsFor"))
@@ -1550,10 +1622,9 @@ int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const cha
assert(assignment);
eq = strchr(assignment, '=');
- if (!eq) {
- log_error("Not an assignment: %s", assignment);
- return -EINVAL;
- }
+ if (!eq)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not an assignment: %s", assignment);
field = strndupa(assignment, eq - assignment);
eq++;
@@ -1656,20 +1727,20 @@ int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const cha
case UNIT_TARGET:
case UNIT_DEVICE:
case UNIT_SWAP:
- log_error("Not supported unit type");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not supported unit type");
default:
- log_error("Invalid unit type");
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid unit type");
}
r = bus_append_unit_property(m, field, eq);
if (r != 0)
return r;
- log_error("Unknown assignment: %s", assignment);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown assignment: %s", assignment);
}
int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
@@ -1953,8 +2024,8 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const*
else if (STR_IN_SET(d->result, "done", "skipped"))
return 0;
- log_debug("Unexpected job result, assuming server side newer than us: %s", d->result);
- return -EIO;
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Unexpected job result, assuming server side newer than us: %s", d->result);
}
int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
@@ -2173,13 +2244,8 @@ static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
free(cg);
}
-static int cgroup_info_compare_func(const void *a, const void *b) {
- const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
-
- assert(x);
- assert(y);
-
- return strcmp(x->cgroup_path, y->cgroup_path);
+static int cgroup_info_compare_func(struct CGroupInfo * const *a, struct CGroupInfo * const *b) {
+ return strcmp((*a)->cgroup_path, (*b)->cgroup_path);
}
static int dump_processes(
@@ -2216,7 +2282,7 @@ static int dump_processes(
pids[n++] = PTR_TO_PID(pidp);
assert(n == hashmap_size(cg->pids));
- qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
+ typesafe_qsort(pids, n, pid_compare_func);
width = DECIMAL_STR_WIDTH(pids[n-1]);
@@ -2239,7 +2305,7 @@ static int dump_processes(
}
more = i+1 < n || cg->children;
- special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
+ special = special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT);
fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
prefix,
@@ -2258,7 +2324,7 @@ static int dump_processes(
LIST_FOREACH(siblings, child, cg->children)
children[n++] = child;
assert(n == cg->n_children);
- qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
+ typesafe_qsort(children, n, cgroup_info_compare_func);
if (n_columns != 0)
n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
@@ -2276,14 +2342,14 @@ static int dump_processes(
name++;
more = i+1 < n;
- special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
+ special = special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT);
fputs(prefix, stdout);
fputs(special, stdout);
fputs(name, stdout);
fputc('\n', stdout);
- special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
+ special = special_glyph(more ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE);
pp = strappend(prefix, special);
if (!pp)
@@ -2345,7 +2411,7 @@ static int dump_extra_processes(
if (n == 0)
return 0;
- qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
+ typesafe_qsort(pids, n, pid_compare_func);
width = DECIMAL_STR_WIDTH(pids[n-1]);
for (k = 0; k < n; k++) {
@@ -2367,7 +2433,7 @@ static int dump_extra_processes(
fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
prefix,
- special_glyph(TRIANGULAR_BULLET),
+ special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET),
width, pids[k],
name);
}
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index a4f2deba31..976643e4ce 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -28,7 +28,7 @@
#include "escape.h"
#include "fd-util.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "nsflags.h"
#include "parse-util.h"
#include "proc-cmdline.h"
@@ -500,7 +500,7 @@ int bus_verify_polkit_async(
if (r < 0)
return r;
- r = sd_bus_message_append(pk, "us", !!interactive, NULL);
+ r = sd_bus_message_append(pk, "us", interactive, NULL);
if (r < 0)
return r;
@@ -628,15 +628,43 @@ int bus_connect_user_systemd(sd_bus **_bus) {
return 0;
}
-#define print_property(name, fmt, ...) \
- do { \
- if (value) \
- printf(fmt "\n", __VA_ARGS__); \
- else \
- printf("%s=" fmt "\n", name, __VA_ARGS__); \
- } while (0)
+int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) {
+ va_list ap;
+ int r;
+
+ assert(name);
+ assert(fmt);
+
+ if (expected_value) {
+ _cleanup_free_ char *s = NULL;
+
+ va_start(ap, fmt);
+ r = vasprintf(&s, fmt, ap);
+ va_end(ap);
+ if (r < 0)
+ return -ENOMEM;
+
+ if (streq_ptr(expected_value, s)) {
+ if (only_value)
+ puts(s);
+ else
+ printf("%s=%s\n", name, s);
+ }
+
+ return 0;
+ }
+
+ if (!only_value)
+ printf("%s=", name);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ puts("");
+
+ return 0;
+}
-int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all) {
+static int bus_print_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) {
char type;
const char *contents;
int r;
@@ -663,7 +691,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
/* This property has a single value, so we need to take
* care not to print a new line, everything else is OK. */
good = !strchr(s, '\n');
- print_property(name, "%s", good ? s : "[unprintable]");
+ bus_print_property_value(name, expected_value, value, "%s", good ? s : "[unprintable]");
}
return 1;
@@ -676,8 +704,10 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
if (r < 0)
return r;
- print_property(name, "%s", yes_no(b));
+ if (expected_value && parse_boolean(expected_value) != b)
+ return 1;
+ bus_print_property_value(name, NULL, value, "%s", yes_no(b));
return 1;
}
@@ -698,12 +728,14 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
t = format_timestamp(timestamp, sizeof(timestamp), u);
if (t || all)
- print_property(name, "%s", strempty(t));
+ bus_print_property_value(name, expected_value, value, "%s", strempty(t));
} else if (strstr(name, "USec")) {
char timespan[FORMAT_TIMESPAN_MAX];
- print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0));
+ (void) format_timespan(timespan, sizeof(timespan), u, 0);
+ bus_print_property_value(name, expected_value, value, "%s", timespan);
+
} else if (streq(name, "RestrictNamespaces")) {
_cleanup_free_ char *s = NULL;
const char *result;
@@ -720,7 +752,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
result = s;
}
- print_property(name, "%s", result);
+ bus_print_property_value(name, expected_value, value, "%s", result);
} else if (streq(name, "MountFlags")) {
const char *result;
@@ -729,7 +761,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
if (!result)
return -EINVAL;
- print_property(name, "%s", result);
+ bus_print_property_value(name, expected_value, value, "%s", result);
} else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) {
_cleanup_free_ char *s = NULL;
@@ -738,7 +770,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
if (r < 0)
return r;
- print_property(name, "%s", s);
+ bus_print_property_value(name, expected_value, value, "%s", s);
} else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
(STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) ||
@@ -746,16 +778,16 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
(STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == (uint64_t) -1) ||
(endswith(name, "NSec") && u == (uint64_t) -1))
- print_property(name, "%s", "[not set]");
+ bus_print_property_value(name, expected_value, value, "%s", "[not set]");
else if ((STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
(STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) ||
(startswith(name, "Limit") && u == (uint64_t) -1) ||
(startswith(name, "DefaultLimit") && u == (uint64_t) -1))
- print_property(name, "%s", "infinity");
+ bus_print_property_value(name, expected_value, value, "%s", "infinity");
else
- print_property(name, "%"PRIu64, u);
+ bus_print_property_value(name, expected_value, value, "%"PRIu64, u);
return 1;
}
@@ -767,8 +799,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
if (r < 0)
return r;
- print_property(name, "%"PRIi64, i);
-
+ bus_print_property_value(name, expected_value, value, "%"PRIi64, i);
return 1;
}
@@ -780,19 +811,20 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
return r;
if (strstr(name, "UMask") || strstr(name, "Mode"))
- print_property(name, "%04o", u);
+ bus_print_property_value(name, expected_value, value, "%04o", u);
+
else if (streq(name, "UID")) {
if (u == UID_INVALID)
- print_property(name, "%s", "[not set]");
+ bus_print_property_value(name, expected_value, value, "%s", "[not set]");
else
- print_property(name, "%"PRIu32, u);
+ bus_print_property_value(name, expected_value, value, "%"PRIu32, u);
} else if (streq(name, "GID")) {
if (u == GID_INVALID)
- print_property(name, "%s", "[not set]");
+ bus_print_property_value(name, expected_value, value, "%s", "[not set]");
else
- print_property(name, "%"PRIu32, u);
+ bus_print_property_value(name, expected_value, value, "%"PRIu32, u);
} else
- print_property(name, "%"PRIu32, u);
+ bus_print_property_value(name, expected_value, value, "%"PRIu32, u);
return 1;
}
@@ -804,7 +836,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
if (r < 0)
return r;
- print_property(name, "%"PRIi32, i);
+ bus_print_property_value(name, expected_value, value, "%"PRIi32, i);
return 1;
}
@@ -815,7 +847,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
if (r < 0)
return r;
- print_property(name, "%g", d);
+ bus_print_property_value(name, expected_value, value, "%g", d);
return 1;
}
@@ -865,7 +897,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
return r;
if (all || n > 0) {
- unsigned int i;
+ unsigned i;
if (!value)
printf("%s=", name);
@@ -887,7 +919,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all
return r;
if (all || n > 0) {
- unsigned int i;
+ unsigned i;
if (!value)
printf("%s=", name);
@@ -924,8 +956,8 @@ int bus_message_print_all_properties(
return r;
while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
- const char *name;
- const char *contents;
+ _cleanup_free_ char *name_with_equal = NULL;
+ const char *name, *contents, *expected_value = NULL;
r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name);
if (r < 0)
@@ -937,11 +969,16 @@ int bus_message_print_all_properties(
return log_oom();
r = set_put(*found_properties, name);
- if (r < 0 && r != EEXIST)
+ if (r < 0 && r != -EEXIST)
return log_oom();
}
- if (!filter || strv_find(filter, name)) {
+ name_with_equal = strappend(name, "=");
+ if (!name_with_equal)
+ return log_oom();
+
+ if (!filter || strv_find(filter, name) ||
+ (expected_value = strv_find_startswith(filter, name_with_equal))) {
r = sd_bus_message_peek_type(m, NULL, &contents);
if (r < 0)
return r;
@@ -951,13 +988,13 @@ int bus_message_print_all_properties(
return r;
if (func)
- r = func(name, m, value, all);
+ r = func(name, expected_value, m, value, all);
if (!func || r == 0)
- r = bus_print_property(name, m, value, all);
+ r = bus_print_property(name, expected_value, m, value, all);
if (r < 0)
return r;
if (r == 0) {
- if (all)
+ if (all && !expected_value)
printf("%s=[unprintable]\n", name);
/* skip what we didn't read */
r = sd_bus_message_skip(m, contents);
@@ -1200,44 +1237,6 @@ int bus_message_map_all_properties(
return sd_bus_message_exit_container(m);
}
-int bus_message_map_properties_changed(
- sd_bus_message *m,
- const struct bus_properties_map *map,
- unsigned flags,
- sd_bus_error *error,
- void *userdata) {
-
- const char *member;
- int r, invalidated, i;
-
- assert(m);
- assert(map);
-
- r = bus_message_map_all_properties(m, map, flags, error, userdata);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
- if (r < 0)
- return r;
-
- invalidated = 0;
- while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0)
- for (i = 0; map[i].member; i++)
- if (streq(map[i].member, member)) {
- ++invalidated;
- break;
- }
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- return invalidated;
-}
-
int bus_map_all_properties(
sd_bus *bus,
const char *destination,
@@ -1345,12 +1344,10 @@ int bus_connect_transport_systemd(BusTransport transport, const char *host, bool
if (user)
r = bus_connect_user_systemd(bus);
else {
- if (sd_booted() <= 0) {
+ if (sd_booted() <= 0)
/* Print a friendly message when the local system is actually not running systemd as PID 1. */
- log_error("System has not been booted with systemd as init system (PID 1). Can't operate.");
-
- return -EHOSTDOWN;
- }
+ return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
+ "System has not been booted with systemd as init system (PID 1). Can't operate.");
r = bus_connect_system_systemd(bus);
}
break;
@@ -1724,169 +1721,6 @@ int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *descri
return 0;
}
-struct request_name_data {
- unsigned n_ref;
-
- const char *name;
- uint64_t flags;
- void *userdata;
-};
-
-static void request_name_destroy_callback(void *userdata) {
- struct request_name_data *data = userdata;
-
- assert(data);
- assert(data->n_ref > 0);
-
- log_info("%s n_ref=%u", __func__, data->n_ref);
-
- data->n_ref--;
- if (data->n_ref == 0)
- free(data);
-}
-
-static int reload_dbus_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
- struct request_name_data *data = userdata;
- const sd_bus_error *e;
- int r;
-
- assert(data);
- assert(data->name);
- assert(data->n_ref > 0);
-
- e = sd_bus_message_get_error(m);
- if (e) {
- log_error_errno(sd_bus_error_get_errno(e), "Failed to reload DBus configuration: %s", e->message);
- return 1;
- }
-
- /* Here, use the default request name handler to avoid an infinite loop of reloading and requesting. */
- r = sd_bus_request_name_async(sd_bus_message_get_bus(m), NULL, data->name, data->flags, NULL, data->userdata);
- if (r < 0)
- log_error_errno(r, "Failed to request name: %m");
-
- return 1;
-}
-
-static int request_name_handler_may_reload_dbus(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
- struct request_name_data *data = userdata;
- uint32_t ret;
- int r;
-
- assert(m);
- assert(data);
-
- if (sd_bus_message_is_method_error(m, NULL)) {
- const sd_bus_error *e = sd_bus_message_get_error(m);
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
-
- if (!sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED)) {
- log_debug_errno(sd_bus_error_get_errno(e),
- "Unable to request name, failing connection: %s",
- e->message);
-
- bus_enter_closing(sd_bus_message_get_bus(m));
- return 1;
- }
-
- log_debug_errno(sd_bus_error_get_errno(e),
- "Unable to request name, will retry after reloading DBus configuration: %s",
- e->message);
-
- /* If systemd-timesyncd.service enables DynamicUser= and dbus.service
- * started before the dynamic user is realized, then the DBus policy
- * about timesyncd has not been enabled yet. So, let's try to reload
- * DBus configuration, and after that request the name again. Note that it
- * seems that no privileges are necessary to call the following method. */
-
- r = sd_bus_call_method_async(
- sd_bus_message_get_bus(m),
- &slot,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "ReloadConfig",
- reload_dbus_handler,
- data, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to reload DBus configuration: %m");
- bus_enter_closing(sd_bus_message_get_bus(m));
- return 1;
- }
-
- data->n_ref ++;
- assert_se(sd_bus_slot_set_destroy_callback(slot, request_name_destroy_callback) >= 0);
-
- r = sd_bus_slot_set_floating(slot, true);
- if (r < 0)
- return r;
-
- return 1;
- }
-
- r = sd_bus_message_read(m, "u", &ret);
- if (r < 0)
- return r;
-
- switch (ret) {
-
- case BUS_NAME_ALREADY_OWNER:
- log_debug("Already owner of requested service name, ignoring.");
- return 1;
-
- case BUS_NAME_IN_QUEUE:
- log_debug("In queue for requested service name.");
- return 1;
-
- case BUS_NAME_PRIMARY_OWNER:
- log_debug("Successfully acquired requested service name.");
- return 1;
-
- case BUS_NAME_EXISTS:
- log_debug("Requested service name already owned, failing connection.");
- bus_enter_closing(sd_bus_message_get_bus(m));
- return 1;
- }
-
- log_debug("Unexpected response from RequestName(), failing connection.");
- bus_enter_closing(sd_bus_message_get_bus(m));
- return 1;
-}
-
-int bus_request_name_async_may_reload_dbus(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, uint64_t flags, void *userdata) {
- _cleanup_free_ struct request_name_data *data = NULL;
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
- int r;
-
- data = new(struct request_name_data, 1);
- if (!data)
- return -ENOMEM;
-
- *data = (struct request_name_data) {
- .n_ref = 1,
- .name = name,
- .flags = flags,
- .userdata = userdata,
- };
-
- r = sd_bus_request_name_async(bus, &slot, name, flags, request_name_handler_may_reload_dbus, data);
- if (r < 0)
- return r;
-
- assert_se(sd_bus_slot_set_destroy_callback(slot, request_name_destroy_callback) >= 0);
- TAKE_PTR(data);
-
- if (ret_slot)
- *ret_slot = TAKE_PTR(slot);
- else {
- r = sd_bus_slot_set_floating(slot, true);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
int bus_reply_pair_array(sd_bus_message *m, char **l) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
char **k, **v;
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
index b400eb81e2..71c248fe3c 100644
--- a/src/shared/bus-util.h
+++ b/src/shared/bus-util.h
@@ -38,7 +38,6 @@ enum {
int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata);
-int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata);
int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map,
unsigned flags, sd_bus_error *error, sd_bus_message **reply, void *userdata);
@@ -63,9 +62,9 @@ int bus_connect_user_systemd(sd_bus **_bus);
int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
-typedef int (*bus_message_print_t) (const char *name, sd_bus_message *m, bool value, bool all);
+typedef int (*bus_message_print_t) (const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all);
-int bus_print_property(const char *name, sd_bus_message *property, bool value, bool all);
+int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) _printf_(4,5);
int bus_message_print_all_properties(sd_bus_message *m, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties);
int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties);
@@ -177,6 +176,4 @@ static inline int bus_open_system_watch_bind(sd_bus **ret) {
return bus_open_system_watch_bind_with_description(ret, NULL);
}
-int bus_request_name_async_may_reload_dbus(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, uint64_t flags, void *userdata);
-
int bus_reply_pair_array(sd_bus_message *m, char **l);
diff --git a/src/basic/calendarspec.c b/src/shared/calendarspec.c
index 8cb645aeac..dafc09e8f8 100644
--- a/src/basic/calendarspec.c
+++ b/src/shared/calendarspec.c
@@ -57,25 +57,18 @@ CalendarSpec* calendar_spec_free(CalendarSpec *c) {
return mfree(c);
}
-static int component_compare(const void *_a, const void *_b) {
- CalendarComponent * const *a = _a, * const *b = _b;
-
- if ((*a)->start < (*b)->start)
- return -1;
- if ((*a)->start > (*b)->start)
- return 1;
+static int component_compare(CalendarComponent * const *a, CalendarComponent * const *b) {
+ int r;
- if ((*a)->stop < (*b)->stop)
- return -1;
- if ((*a)->stop > (*b)->stop)
- return 1;
+ r = CMP((*a)->start, (*b)->start);
+ if (r != 0)
+ return r;
- if ((*a)->repeat < (*b)->repeat)
- return -1;
- if ((*a)->repeat > (*b)->repeat)
- return 1;
+ r = CMP((*a)->stop, (*b)->stop);
+ if (r != 0)
+ return r;
- return 0;
+ return CMP((*a)->repeat, (*b)->repeat);
}
static void normalize_chain(CalendarComponent **c) {
@@ -103,7 +96,7 @@ static void normalize_chain(CalendarComponent **c) {
for (i = *c; i; i = i->next)
*(j++) = i;
- qsort(b, n, sizeof(CalendarComponent*), component_compare);
+ typesafe_qsort(b, n, component_compare);
b[n-1]->next = NULL;
next = b[n-1];
diff --git a/src/basic/calendarspec.h b/src/shared/calendarspec.h
index 3bf8a39e1a..3bf8a39e1a 100644
--- a/src/basic/calendarspec.h
+++ b/src/shared/calendarspec.h
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index 4d1a90bd55..61df7511a1 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -12,8 +12,8 @@
#include "bus-util.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
+#include "env-file.h"
#include "fd-util.h"
-#include "fileio.h"
#include "format-util.h"
#include "locale-util.h"
#include "macro.h"
@@ -38,7 +38,7 @@ static void show_pid_array(
if (n_pids == 0)
return;
- qsort(pids, n_pids, sizeof(pid_t), pid_compare_func);
+ typesafe_qsort(pids, n_pids, pid_compare_func);
/* Filter duplicates */
for (j = 0, i = 1; i < n_pids; i++) {
@@ -63,9 +63,9 @@ static void show_pid_array(
(void) get_process_cmdline(pids[i], n_columns, true, &t);
if (extra)
- printf("%s%s ", prefix, special_glyph(TRIANGULAR_BULLET));
+ printf("%s%s ", prefix, special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
else
- printf("%s%s", prefix, special_glyph(((more || i < n_pids-1) ? TREE_BRANCH : TREE_RIGHT)));
+ printf("%s%s", prefix, special_glyph(((more || i < n_pids-1) ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT)));
printf("%*"PID_PRI" %s\n", pid_width, pids[i], strna(t));
}
@@ -159,10 +159,10 @@ int show_cgroup_by_path(
}
if (last) {
- printf("%s%s%s\n", prefix, special_glyph(TREE_BRANCH), cg_unescape(basename(last)));
+ printf("%s%s%s\n", prefix, special_glyph(SPECIAL_GLYPH_TREE_BRANCH), cg_unescape(basename(last)));
if (!p1) {
- p1 = strappend(prefix, special_glyph(TREE_VERTICAL));
+ p1 = strappend(prefix, special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
if (!p1)
return -ENOMEM;
}
@@ -181,7 +181,7 @@ int show_cgroup_by_path(
show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags);
if (last) {
- printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), cg_unescape(basename(last)));
+ printf("%s%s%s\n", prefix, special_glyph(SPECIAL_GLYPH_TREE_RIGHT), cg_unescape(basename(last)));
if (!p2) {
p2 = strappend(prefix, " ");
@@ -277,26 +277,6 @@ int show_cgroup_and_extra(
return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
}
-int show_cgroup_and_extra_by_spec(
- const char *spec,
- const char *prefix,
- unsigned n_columns,
- const pid_t extra_pids[],
- unsigned n_extra_pids,
- OutputFlags flags) {
-
- _cleanup_free_ char *controller = NULL, *path = NULL;
- int r;
-
- assert(spec);
-
- r = cg_split_spec(spec, &controller, &path);
- if (r < 0)
- return r;
-
- return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
-}
-
int show_cgroup_get_unit_path_and_warn(
sd_bus *bus,
const char *unit,
@@ -339,7 +319,7 @@ int show_cgroup_get_path_and_warn(
const char *m;
m = strjoina("/run/systemd/machines/", machine);
- r = parse_env_file(NULL, m, NEWLINE, "SCOPE", &unit, NULL);
+ r = parse_env_file(NULL, m, "SCOPE", &unit);
if (r < 0)
return log_error_errno(r, "Failed to load machine data: %m");
diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h
index 4e510fb73c..3593e9dcf4 100644
--- a/src/shared/cgroup-show.h
+++ b/src/shared/cgroup-show.h
@@ -12,7 +12,6 @@
int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, OutputFlags flags);
int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, OutputFlags flags);
-int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags);
int show_cgroup_get_unit_path_and_warn(
diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c
index 08dc7b9e43..46fa68027e 100644
--- a/src/shared/clean-ipc.c
+++ b/src/shared/clean-ipc.c
@@ -39,9 +39,8 @@ static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid
static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid, bool rm) {
_cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
bool first = true;
- int ret = 0;
+ int ret = 0, r;
f = fopen("/proc/sysvipc/shm", "re");
if (!f) {
@@ -51,20 +50,25 @@ static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid, bool rm) {
return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
}
- FOREACH_LINE(line, f, goto fail) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
unsigned n_attached;
pid_t cpid, lpid;
uid_t uid, cuid;
gid_t gid, cgid;
int shmid;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
+ if (r == 0)
+ break;
+
if (first) {
first = false;
continue;
}
- truncate_nl(line);
-
if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
&shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8)
continue;
@@ -95,16 +99,12 @@ static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid, bool rm) {
}
return ret;
-
-fail:
- return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
}
static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid, bool rm) {
_cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
bool first = true;
- int ret = 0;
+ int ret = 0, r;
f = fopen("/proc/sysvipc/sem", "re");
if (!f) {
@@ -114,18 +114,23 @@ static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid, bool rm) {
return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
}
- FOREACH_LINE(line, f, goto fail) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
uid_t uid, cuid;
gid_t gid, cgid;
int semid;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to read /proc/sysvipc/sem: %m");
+ if (r == 0)
+ break;
+
if (first) {
first = false;
continue;
}
- truncate_nl(line);
-
if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
&semid, &uid, &gid, &cuid, &cgid) != 5)
continue;
@@ -153,16 +158,12 @@ static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid, bool rm) {
}
return ret;
-
-fail:
- return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
}
static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid, bool rm) {
_cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
bool first = true;
- int ret = 0;
+ int ret = 0, r;
f = fopen("/proc/sysvipc/msg", "re");
if (!f) {
@@ -172,19 +173,24 @@ static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid, bool rm) {
return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
}
- FOREACH_LINE(line, f, goto fail) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
uid_t uid, cuid;
gid_t gid, cgid;
pid_t cpid, lpid;
int msgid;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to read /proc/sysvipc/msg: %m");
+ if (r == 0)
+ break;
+
if (first) {
first = false;
continue;
}
- truncate_nl(line);
-
if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
&msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
continue;
@@ -212,9 +218,6 @@ static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid, bool rm) {
}
return ret;
-
-fail:
- return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
}
static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid, bool rm) {
diff --git a/src/basic/clock-util.c b/src/shared/clock-util.c
index 3ea016af0e..1877a81434 100644
--- a/src/basic/clock-util.c
+++ b/src/shared/clock-util.c
@@ -10,8 +10,10 @@
#include <sys/ioctl.h>
#include <sys/time.h>
+#include "alloc-util.h"
#include "clock-util.h"
#include "fd-util.h"
+#include "fileio.h"
#include "macro.h"
#include "string-util.h"
#include "util.h"
@@ -54,6 +56,7 @@ int clock_set_hwclock(const struct tm *tm) {
int clock_is_localtime(const char* adjtime_path) {
_cleanup_fclose_ FILE *f;
+ int r;
if (!adjtime_path)
adjtime_path = "/etc/adjtime";
@@ -67,36 +70,42 @@ int clock_is_localtime(const char* adjtime_path) {
*/
f = fopen(adjtime_path, "re");
if (f) {
- char line[LINE_MAX];
- bool b;
+ _cleanup_free_ char *line = NULL;
+ unsigned i;
+
+ for (i = 0; i < 2; i++) { /* skip the first two lines */
+ r = read_line(f, LONG_LINE_MAX, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return false; /* less than three lines → default to UTC */
+ }
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return false; /* less than three lines → default to UTC */
- b = fgets(line, sizeof(line), f) &&
- fgets(line, sizeof(line), f) &&
- fgets(line, sizeof(line), f);
- if (!b)
- /* less than three lines -> default to UTC */
- return 0;
-
- truncate_nl(line);
return streq(line, "LOCAL");
} else if (errno != ENOENT)
return -errno;
- /* adjtime not present -> default to UTC */
- return 0;
+ /* adjtime not present → default to UTC */
+ return false;
}
int clock_set_timezone(int *min) {
const struct timeval *tv_null = NULL;
struct timespec ts;
- struct tm *tm;
+ struct tm tm;
int minutesdelta;
struct timezone tz;
assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
- assert_se(tm = localtime(&ts.tv_sec));
- minutesdelta = tm->tm_gmtoff / 60;
+ assert_se(localtime_r(&ts.tv_sec, &tm));
+ minutesdelta = tm.tm_gmtoff / 60;
tz.tz_minuteswest = -minutesdelta;
tz.tz_dsttime = 0; /* DST_NONE */
diff --git a/src/basic/clock-util.h b/src/shared/clock-util.h
index b9db54eac9..b9db54eac9 100644
--- a/src/basic/clock-util.h
+++ b/src/shared/clock-util.h
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 2969a89b4e..fb77966264 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -30,7 +30,8 @@
#include "ima-util.h"
#include "list.h"
#include "macro.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "env-file.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
@@ -219,7 +220,7 @@ static int condition_test_user(Condition *c) {
return streq(c->parameter, "root");
u = c->parameter;
- r = get_user_creds(&u, &id, NULL, NULL, NULL);
+ r = get_user_creds(&u, &id, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
if (r < 0)
return 0;
@@ -384,10 +385,9 @@ static int condition_test_security(Condition *c) {
}
static int condition_test_capability(Condition *c) {
+ unsigned long long capabilities = (unsigned long long) -1;
_cleanup_fclose_ FILE *f = NULL;
- int value;
- char line[LINE_MAX];
- unsigned long long capabilities = -1;
+ int value, r;
assert(c);
assert(c->parameter);
@@ -405,11 +405,21 @@ static int condition_test_capability(Condition *c) {
if (!f)
return -errno;
- while (fgets(line, sizeof(line), f)) {
- truncate_nl(line);
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ const char *p;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ p = startswith(line, "CapBnd:");
+ if (p) {
+ if (sscanf(line+7, "%llx", &capabilities) != 1)
+ return -EIO;
- if (startswith(line, "CapBnd:")) {
- (void) sscanf(line+7, "%llx", &capabilities);
break;
}
}
@@ -462,7 +472,7 @@ static int condition_test_needs_update(Condition *c) {
uint64_t timestamp;
int r;
- r = parse_env_file(NULL, p, NULL, "TIMESTAMP_NSEC", &timestamp_str, NULL);
+ r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", &timestamp_str);
if (r < 0) {
log_error_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
return true;
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 2d62fdf05d..8fe177990a 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -18,9 +18,11 @@
#include "fs-util.h"
#include "log.h"
#include "macro.h"
+#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "string-util.h"
@@ -28,7 +30,6 @@
#include "syslog-util.h"
#include "time-util.h"
#include "utf8.h"
-#include "rlimit-util.h"
int config_item_table_lookup(
const void *table,
@@ -82,19 +83,13 @@ int config_item_perf_lookup(
assert(ltype);
assert(data);
- if (!section)
- p = lookup(lvalue, strlen(lvalue));
- else {
- char *key;
-
- key = strjoin(section, ".", lvalue);
- if (!key)
- return -ENOMEM;
+ if (section) {
+ const char *key;
+ key = strjoina(section, ".", lvalue);
p = lookup(key, strlen(key));
- free(key);
- }
-
+ } else
+ p = lookup(lvalue, strlen(lvalue));
if (!p)
return 0;
@@ -132,7 +127,6 @@ static int next_assignment(
r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
if (r < 0)
return r;
-
if (r > 0) {
if (func)
return func(unit, filename, line, section, section_line,
@@ -174,7 +168,7 @@ static int parse_line(
if (!*l)
return 0;
- if (strchr(COMMENTS "\n", *l))
+ if (*l == '\n')
return 0;
include = first_word(l, ".include");
@@ -327,6 +321,9 @@ int config_parse(const char *unit,
return r;
}
+ if (strchr(COMMENTS, *buf))
+ continue;
+
l = buf;
if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
char *q;
@@ -532,8 +529,10 @@ int config_parse_iec_size(const char* unit,
assert(data);
r = parse_size(rvalue, 1024, &v);
- if (r < 0 || (uint64_t) (size_t) v != v) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
+ if (r >= 0 && (uint64_t) (size_t) v != v)
+ r = -ERANGE;
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue);
return 0;
}
@@ -563,8 +562,10 @@ int config_parse_si_size(
assert(data);
r = parse_size(rvalue, 1000, &v);
- if (r < 0 || (uint64_t) (size_t) v != v) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
+ if (r >= 0 && (uint64_t) (size_t) v != v)
+ r = -ERANGE;
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue);
return 0;
}
@@ -1006,121 +1007,6 @@ int config_parse_ip_port(
return 0;
}
-int config_parse_join_controllers(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char ****ret = data;
- const char *whole_rvalue = rvalue;
- unsigned n = 0;
- _cleanup_(strv_free_freep) char ***controllers = NULL;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(ret);
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
- char **l;
- int r;
-
- r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
- return r;
- }
- if (r == 0)
- break;
-
- l = strv_split(word, ",");
- if (!l)
- return log_oom();
- strv_uniq(l);
-
- if (strv_length(l) <= 1) {
- strv_free(l);
- continue;
- }
-
- if (!controllers) {
- controllers = new(char**, 2);
- if (!controllers) {
- strv_free(l);
- return log_oom();
- }
-
- controllers[0] = l;
- controllers[1] = NULL;
-
- n = 1;
- } else {
- char ***a;
- char ***t;
-
- t = new0(char**, n+2);
- if (!t) {
- strv_free(l);
- return log_oom();
- }
-
- n = 0;
-
- for (a = controllers; *a; a++)
- if (strv_overlap(*a, l)) {
- if (strv_extend_strv(&l, *a, false) < 0) {
- strv_free(l);
- strv_free_free(t);
- return log_oom();
- }
-
- } else {
- char **c;
-
- c = strv_copy(*a);
- if (!c) {
- strv_free(l);
- strv_free_free(t);
- return log_oom();
- }
-
- t[n++] = c;
- }
-
- t[n++] = strv_uniq(l);
-
- strv_free_free(controllers);
- controllers = t;
- }
- }
- if (!isempty(rvalue))
- log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
-
- /* As a special case, return a single empty strv, to override the default */
- if (!controllers) {
- controllers = new(char**, 2);
- if (!controllers)
- return log_oom();
- controllers[0] = strv_new(NULL, NULL);
- if (!controllers[0])
- return log_oom();
- controllers[1] = NULL;
- }
-
- strv_free_free(*ret);
- *ret = TAKE_PTR(controllers);
-
- return 0;
-}
-
int config_parse_mtu(
const char *unit,
const char *filename,
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 16f042d894..865db4278b 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -137,7 +137,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_personality);
CONFIG_PARSER_PROTOTYPE(config_parse_permille);
CONFIG_PARSER_PROTOTYPE(config_parse_ifname);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
-CONFIG_PARSER_PROTOTYPE(config_parse_join_controllers);
CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
diff --git a/src/basic/cpu-set-util.c b/src/shared/cpu-set-util.c
index b1c927bcb8..9a789ae756 100644
--- a/src/basic/cpu-set-util.c
+++ b/src/shared/cpu-set-util.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2015 Filipe Brandenburger
-***/
#include <errno.h>
#include <stddef.h>
diff --git a/src/basic/cpu-set-util.h b/src/shared/cpu-set-util.h
index 88470fe15a..1b6bd35b1c 100644
--- a/src/basic/cpu-set-util.h
+++ b/src/shared/cpu-set-util.h
@@ -1,10 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- Copyright © 2015 Filipe Brandenburger
-***/
-
#include <sched.h>
#include "macro.h"
diff --git a/src/shared/crypt-util.c b/src/shared/crypt-util.c
new file mode 100644
index 0000000000..20bdc5489e
--- /dev/null
+++ b/src/shared/crypt-util.c
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if HAVE_LIBCRYPTSETUP
+#include "crypt-util.h"
+#include "log.h"
+
+void cryptsetup_log_glue(int level, const char *msg, void *usrptr) {
+ switch (level) {
+ case CRYPT_LOG_NORMAL:
+ level = LOG_NOTICE;
+ break;
+ case CRYPT_LOG_ERROR:
+ level = LOG_ERR;
+ break;
+ case CRYPT_LOG_VERBOSE:
+ level = LOG_INFO;
+ break;
+ case CRYPT_LOG_DEBUG:
+ level = LOG_DEBUG;
+ break;
+ default:
+ log_error("Unknown libcryptsetup log level: %d", level);
+ level = LOG_ERR;
+ }
+
+ log_full(level, "%s", msg);
+}
+#endif
diff --git a/src/basic/crypt-util.h b/src/shared/crypt-util.h
index 5b03cb072d..8c86714aec 100644
--- a/src/basic/crypt-util.h
+++ b/src/shared/crypt-util.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
#if HAVE_LIBCRYPTSETUP
#include <libcryptsetup.h>
diff --git a/src/shared/daemon-util.h b/src/shared/daemon-util.h
new file mode 100644
index 0000000000..5e9eca1d9e
--- /dev/null
+++ b/src/shared/daemon-util.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+#include "sd-daemon.h"
+
+#define NOTIFY_READY "READY=1\n" "STATUS=Processing requests..."
+#define NOTIFY_STOPPING "STOPPING=1\n" "STATUS=Shutting down..."
+
+static inline const char *notify_start(const char *start, const char *stop) {
+ if (start)
+ (void) sd_notify(false, start);
+
+ return stop;
+}
+
+/* This is intended to be used with _cleanup_ attribute. */
+static inline void notify_on_cleanup(const char **p) {
+ if (p)
+ (void) sd_notify(false, *p);
+}
diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c
index d117fbfda0..b545c2a1c0 100644
--- a/src/shared/dev-setup.c
+++ b/src/shared/dev-setup.c
@@ -9,6 +9,7 @@
#include "label.h"
#include "log.h"
#include "path-util.h"
+#include "umask-util.h"
#include "user-util.h"
#include "util.h"
@@ -54,3 +55,61 @@ int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
return 0;
}
+
+int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid) {
+ static const struct {
+ const char *name;
+ mode_t mode;
+ } table[] = {
+ { "/run/systemd", S_IFDIR | 0755 },
+ { "/run/systemd/inaccessible", S_IFDIR | 0000 },
+ { "/run/systemd/inaccessible/reg", S_IFREG | 0000 },
+ { "/run/systemd/inaccessible/dir", S_IFDIR | 0000 },
+ { "/run/systemd/inaccessible/fifo", S_IFIFO | 0000 },
+ { "/run/systemd/inaccessible/sock", S_IFSOCK | 0000 },
+
+ /* The following two are likely to fail if we lack the privs for it (for example in an userns
+ * environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0
+ * device nodes to be created). But that's entirely fine. Consumers of these files should carry
+ * fallback to use a different node then, for example /run/systemd/inaccessible/sock, which is close
+ * enough in behaviour and semantics for most uses. */
+ { "/run/systemd/inaccessible/chr", S_IFCHR | 0000 },
+ { "/run/systemd/inaccessible/blk", S_IFBLK | 0000 },
+ };
+
+ _cleanup_umask_ mode_t u;
+ size_t i;
+ int r;
+
+ u = umask(0000);
+
+ /* Set up inaccessible (and empty) file nodes of all types. This are used to as mount sources for over-mounting
+ * ("masking") file nodes that shall become inaccessible and empty for specific containers or services. We try
+ * to lock down these nodes as much as we can, but otherwise try to match them as closely as possible with the
+ * underlying file, i.e. in the best case we offer the same node type as the underlying node. */
+
+ for (i = 0; i < ELEMENTSOF(table); i++) {
+ _cleanup_free_ char *path = NULL;
+
+ path = prefix_root(root, table[i].name);
+ if (!path)
+ return log_oom();
+
+ if (S_ISDIR(table[i].mode))
+ r = mkdir(path, table[i].mode & 07777);
+ else
+ r = mknod(path, table[i].mode, makedev(0, 0));
+ if (r < 0) {
+ if (errno != EEXIST)
+ log_debug_errno(errno, "Failed to create '%s', ignoring: %m", path);
+ continue;
+ }
+
+ if (uid != UID_INVALID || gid != GID_INVALID) {
+ if (lchown(path, uid, gid) < 0)
+ log_debug_errno(errno, "Failed to chown '%s': %m", path);
+ }
+ }
+
+ return 0;
+}
diff --git a/src/shared/dev-setup.h b/src/shared/dev-setup.h
index f105f2f20f..72b90ec4de 100644
--- a/src/shared/dev-setup.h
+++ b/src/shared/dev-setup.h
@@ -4,3 +4,5 @@
#include <sys/types.h>
int dev_setup(const char *prefix, uid_t uid, gid_t gid);
+
+int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid);
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index fa1cf26ee1..3a46faf769 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -4,6 +4,7 @@
#include <sys/prctl.h>
#include <sys/wait.h>
+#include "sd-device.h"
#include "sd-id128.h"
#include "architecture.h"
@@ -14,7 +15,9 @@
#include "crypt-util.h"
#include "def.h"
#include "device-nodes.h"
+#include "device-util.h"
#include "dissect-image.h"
+#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
@@ -25,6 +28,7 @@
#include "linux-3.13/dm-ioctl.h"
#include "missing.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "os-util.h"
#include "path-util.h"
#include "process-util.h"
@@ -35,6 +39,7 @@
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "udev-util.h"
#include "user-util.h"
#include "xattr-util.h"
@@ -94,23 +99,173 @@ not_found:
#if HAVE_BLKID
/* Detect RPMB and Boot partitions, which are not listed by blkid.
* See https://github.com/systemd/systemd/issues/5806. */
-static bool device_is_mmc_special_partition(struct udev_device *d) {
+static bool device_is_mmc_special_partition(sd_device *d) {
const char *sysname;
- sysname = udev_device_get_sysname(d);
- return sysname && startswith(sysname, "mmcblk") &&
+ assert(d);
+
+ if (sd_device_get_sysname(d, &sysname) < 0)
+ return false;
+
+ return startswith(sysname, "mmcblk") &&
(endswith(sysname, "rpmb") || endswith(sysname, "boot0") || endswith(sysname, "boot1"));
}
-static bool device_is_block(struct udev_device *d) {
+static bool device_is_block(sd_device *d) {
const char *ss;
- ss = udev_device_get_subsystem(d);
- if (!ss)
+ assert(d);
+
+ if (sd_device_get_subsystem(d, &ss) < 0)
return false;
return streq(ss, "block");
}
+
+static int enumerator_for_parent(sd_device *d, sd_device_enumerator **ret) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ int r;
+
+ assert(d);
+ assert(ret);
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_parent(e, d);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(e);
+ return 0;
+}
+
+/* how many times to wait for the device nodes to appear */
+#define N_DEVICE_NODE_LIST_ATTEMPTS 10
+
+static int wait_for_partitions_to_appear(
+ int fd,
+ sd_device *d,
+ unsigned num_partitions,
+ DissectImageFlags flags,
+ sd_device_enumerator **ret_enumerator) {
+
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *q;
+ unsigned n;
+ int r;
+
+ assert(fd >= 0);
+ assert(d);
+ assert(ret_enumerator);
+
+ r = enumerator_for_parent(d, &e);
+ if (r < 0)
+ return r;
+
+ /* Count the partitions enumerated by the kernel */
+ n = 0;
+ FOREACH_DEVICE(e, q) {
+ if (sd_device_get_devnum(q, NULL) < 0)
+ continue;
+ if (!device_is_block(q))
+ continue;
+ if (device_is_mmc_special_partition(q))
+ continue;
+
+ if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) {
+ r = device_wait_for_initialization(q, "block", NULL);
+ if (r < 0)
+ return r;
+ }
+
+ n++;
+ }
+
+ if (n == num_partitions + 1) {
+ *ret_enumerator = TAKE_PTR(e);
+ return 0; /* success! */
+ }
+ if (n > num_partitions + 1)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "blkid and kernel partition lists do not match.");
+
+ /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running or it
+ * got EBUSY because udev already opened the device. Let's reprobe the device, which is a synchronous
+ * call that waits until probing is complete. */
+
+ for (unsigned j = 0; ; j++) {
+ if (j++ > 20)
+ return -EBUSY;
+
+ if (ioctl(fd, BLKRRPART, 0) >= 0)
+ break;
+ r = -errno;
+ if (r == -EINVAL) {
+ struct loop_info64 info;
+
+ /* If we are running on a loop device that has partition scanning off, return
+ * an explicit recognizable error about this, so that callers can generate a
+ * proper message explaining the situation. */
+
+ if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) {
+ log_debug("Device is a loop device and partition scanning is off!");
+ return -EPROTONOSUPPORT;
+ }
+ }
+ if (r != -EBUSY)
+ return r;
+
+ /* If something else has the device open, such as an udev rule, the ioctl will return
+ * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a bit,
+ * and try again.
+ *
+ * This is really something they should fix in the kernel! */
+ (void) usleep(50 * USEC_PER_MSEC);
+
+ }
+
+ return -EAGAIN; /* no success yet, try again */
+}
+
+static int loop_wait_for_partitions_to_appear(
+ int fd,
+ sd_device *d,
+ unsigned num_partitions,
+ DissectImageFlags flags,
+ sd_device_enumerator **ret_enumerator) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ int r;
+
+ assert(fd >= 0);
+ assert(d);
+ assert(ret_enumerator);
+
+ log_debug("Waiting for device (parent + %d partitions) to appear...", num_partitions);
+
+ if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) {
+ r = device_wait_for_initialization(d, "block", &device);
+ if (r < 0)
+ return r;
+ } else
+ device = sd_device_ref(d);
+
+ for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) {
+ r = wait_for_partitions_to_appear(fd, device, num_partitions, flags, ret_enumerator);
+ if (r != -EAGAIN)
+ return r;
+ }
+
+ return log_debug_errno(SYNTHETIC_ERRNO(ENXIO),
+ "Kernel partitions dit not appear within %d attempts",
+ N_DEVICE_NODE_LIST_ATTEMPTS);
+}
+
#endif
int dissect_image(
@@ -122,19 +277,18 @@ int dissect_image(
#if HAVE_BLKID
sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
bool is_gpt, is_mbr, generic_rw, multiple_generic = false;
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
_cleanup_free_ char *generic_node = NULL;
sd_id128_t generic_uuid = SD_ID128_NULL;
const char *pttype = NULL;
- struct udev_list_entry *first, *item;
blkid_partlist pl;
int r, generic_nr;
struct stat st;
+ sd_device *q;
unsigned i;
assert(fd >= 0);
@@ -200,6 +354,10 @@ int dissect_image(
if (!m)
return -ENOMEM;
+ r = sd_device_new_from_devnum(&d, 'b', st.st_rdev);
+ if (r < 0)
+ return r;
+
if (!(flags & DISSECT_IMAGE_GPT_ONLY) &&
(flags & DISSECT_IMAGE_REQUIRE_ROOT)) {
const char *usage = NULL;
@@ -218,8 +376,9 @@ int dissect_image(
return -ENOMEM;
}
- if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
- return -ENOMEM;
+ r = device_path_make_major_minor(st.st_mode, st.st_rdev, &n);
+ if (r < 0)
+ return r;
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
.found = true,
@@ -230,7 +389,11 @@ int dissect_image(
.node = TAKE_PTR(n),
};
- m->encrypted = streq(fstype, "crypto_LUKS");
+ m->encrypted = streq_ptr(fstype, "crypto_LUKS");
+
+ r = loop_wait_for_partitions_to_appear(fd, d, 0, flags, &e);
+ if (r < 0)
+ return r;
*ret = TAKE_PTR(m);
@@ -253,124 +416,19 @@ int dissect_image(
if (!pl)
return -errno ?: -ENOMEM;
- udev = udev_new();
- if (!udev)
- return -errno;
-
- d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
- if (!d)
- return -ENOMEM;
-
- for (i = 0;; i++) {
- int n, z;
-
- if (i >= 10) {
- log_debug("Kernel partitions never appeared.");
- return -ENXIO;
- }
-
- e = udev_enumerate_new(udev);
- if (!e)
- return -errno;
-
- r = udev_enumerate_add_match_parent(e, d);
- if (r < 0)
- return r;
-
- r = udev_enumerate_scan_devices(e);
- if (r < 0)
- return r;
-
- /* Count the partitions enumerated by the kernel */
- n = 0;
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *q;
- dev_t qn;
-
- q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!q)
- return -errno;
-
- qn = udev_device_get_devnum(q);
- if (major(qn) == 0)
- continue;
-
- if (!device_is_block(q))
- continue;
-
- if (device_is_mmc_special_partition(q))
- continue;
- n++;
- }
-
- /* Count the partitions enumerated by blkid */
- z = blkid_partlist_numof_partitions(pl);
- if (n == z + 1)
- break;
- if (n > z + 1) {
- log_debug("blkid and kernel partition list do not match.");
- return -EIO;
- }
- if (n < z + 1) {
- unsigned j = 0;
-
- /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running
- * or it got EBUSY because udev already opened the device. Let's reprobe the device, which is a
- * synchronous call that waits until probing is complete. */
-
- for (;;) {
- if (j++ > 20)
- return -EBUSY;
-
- if (ioctl(fd, BLKRRPART, 0) < 0) {
- r = -errno;
-
- if (r == -EINVAL) {
- struct loop_info64 info;
-
- /* If we are running on a loop device that has partition scanning off,
- * return an explicit recognizable error about this, so that callers
- * can generate a proper message explaining the situation. */
-
- if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) {
- log_debug("Device is loop device and partition scanning is off!");
- return -EPROTONOSUPPORT;
- }
- }
- if (r != -EBUSY)
- return r;
- } else
- break;
-
- /* If something else has the device open, such as an udev rule, the ioctl will return
- * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a
- * bit, and try again.
- *
- * This is really something they should fix in the kernel! */
-
- (void) usleep(50 * USEC_PER_MSEC);
- }
- }
-
- e = udev_enumerate_unref(e);
- }
+ r = loop_wait_for_partitions_to_appear(fd, d, blkid_partlist_numof_partitions(pl), flags, &e);
+ if (r < 0)
+ return r;
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- _cleanup_(udev_device_unrefp) struct udev_device *q;
+ FOREACH_DEVICE(e, q) {
unsigned long long pflags;
blkid_partition pp;
const char *node;
dev_t qn;
int nr;
- q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!q)
- return -errno;
-
- qn = udev_device_get_devnum(q);
- if (major(qn) == 0)
+ r = sd_device_get_devnum(q, &qn);
+ if (r < 0)
continue;
if (st.st_rdev == qn)
@@ -382,8 +440,8 @@ int dissect_image(
if (device_is_mmc_special_partition(q))
continue;
- node = udev_device_get_devnode(q);
- if (!node)
+ r = sd_device_get_devname(q, &node);
+ if (r < 0)
continue;
pp = blkid_partlist_devno_to_partition(pl, qn);
@@ -787,7 +845,7 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
}
}
- if ((flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY))
+ if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
return 0;
r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags);
@@ -1101,19 +1159,16 @@ int dissected_image_decrypt_interactively(
return r;
if (r == -EKEYREJECTED)
log_error_errno(r, "Incorrect passphrase, try again!");
- else if (r != -ENOKEY) {
- log_error_errno(r, "Failed to decrypt image: %m");
- return r;
- }
+ else if (r != -ENOKEY)
+ return log_error_errno(r, "Failed to decrypt image: %m");
- if (--n < 0) {
- log_error("Too many retries.");
- return -EKEYREJECTED;
- }
+ if (--n < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EKEYREJECTED),
+ "Too many retries.");
z = strv_free(z);
- r = ask_password_auto("Please enter image passphrase!", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z);
+ r = ask_password_auto("Please enter image passphrase:", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z);
if (r < 0)
return log_error_errno(r, "Failed to query for passphrase: %m");
@@ -1317,7 +1372,7 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
fds[2*k+1] = safe_close(fds[2*k+1]);
- f = fdopen(fds[2*k], "re");
+ f = fdopen(fds[2*k], "r");
if (!f) {
r = -errno;
goto finish;
@@ -1353,14 +1408,14 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
}
case META_MACHINE_INFO:
- r = load_env_file_pairs(f, "machine-info", NULL, &machine_info);
+ r = load_env_file_pairs(f, "machine-info", &machine_info);
if (r < 0)
log_debug_errno(r, "Failed to read /etc/machine-info: %m");
break;
case META_OS_RELEASE:
- r = load_env_file_pairs(f, "os-release", NULL, &os_release);
+ r = load_env_file_pairs(f, "os-release", &os_release);
if (r < 0)
log_debug_errno(r, "Failed to read OS release file: %m");
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index 0033921fa0..f50b40ea11 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -57,6 +57,7 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_MOUNT_ROOT_ONLY = 1 << 6, /* Mount only the root partition */
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 identifyable as OS images */
+ DISSECT_IMAGE_NO_UDEV = 1 << 9, /* Don't wait for udev initializing things */
} DissectImageFlags;
struct DissectedImage {
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index de2fcca8b2..4b31cb36ed 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- ***/
#if HAVE_LIBIDN2
# include <idn2.h>
@@ -19,6 +17,7 @@
#include "dns-domain.h"
#include "hashmap.h"
#include "hexdecoct.h"
+#include "hostname-util.h"
#include "in-addr-util.h"
#include "macro.h"
#include "parse-util.h"
@@ -26,9 +25,9 @@
#include "strv.h"
#include "utf8.h"
-int dns_label_unescape(const char **name, char *dest, size_t sz) {
+int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) {
const char *n;
- char *d;
+ char *d, last_char = 0;
int r = 0;
assert(name);
@@ -38,13 +37,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
d = dest;
for (;;) {
- if (*n == '.') {
- n++;
- break;
- }
+ if (*n == 0 || *n == '.') {
+ if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-')
+ /* Trailing dash */
+ return -EINVAL;
- if (*n == 0)
+ if (*n == '.')
+ n++;
break;
+ }
if (r >= DNS_LABEL_MAX)
return -EINVAL;
@@ -54,6 +55,8 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
if (*n == '\\') {
/* Escaped character */
+ if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES))
+ return -EINVAL;
n++;
@@ -64,6 +67,10 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
else if (IN_SET(*n, '\\', '.')) {
/* Escaped backslash or dot */
+ if (FLAGS_SET(flags, DNS_LABEL_LDH))
+ return -EINVAL;
+
+ last_char = *n;
if (d)
*(d++) = *n;
sz--;
@@ -92,6 +99,11 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
if (k > 255)
return -EINVAL;
+ if (FLAGS_SET(flags, DNS_LABEL_LDH) &&
+ !valid_ldh_char((char) k))
+ return -EINVAL;
+
+ last_char = (char) k;
if (d)
*(d++) = (char) k;
sz--;
@@ -105,6 +117,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
/* Normal character */
+ if (FLAGS_SET(flags, DNS_LABEL_LDH)) {
+ if (!valid_ldh_char(*n))
+ return -EINVAL;
+ if (r == 0 && *n == '-')
+ /* Leading dash */
+ return -EINVAL;
+ }
+
+ last_char = *n;
if (d)
*(d++) = *n;
sz--;
@@ -186,7 +207,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
terminal--;
}
- r = dns_label_unescape(&name, dest, sz);
+ r = dns_label_unescape(&name, dest, sz, 0);
if (r < 0)
return r;
@@ -380,7 +401,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
}
#endif
-int dns_name_concat(const char *a, const char *b, char **_ret) {
+int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
_cleanup_free_ char *ret = NULL;
size_t n = 0, allocated = 0;
const char *p;
@@ -397,7 +418,7 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
for (;;) {
char label[DNS_LABEL_MAX];
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, flags);
if (r < 0)
return r;
if (r == 0) {
@@ -462,8 +483,7 @@ finish:
return 0;
}
-void dns_name_hash_func(const void *s, struct siphash *state) {
- const char *p = s;
+void dns_name_hash_func(const char *p, struct siphash *state) {
int r;
assert(p);
@@ -471,7 +491,7 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
for (;;) {
char label[DNS_LABEL_MAX+1];
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, 0);
if (r < 0)
break;
if (r == 0)
@@ -486,15 +506,15 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
string_hash_func("", state);
}
-int dns_name_compare_func(const void *a, const void *b) {
+int dns_name_compare_func(const char *a, const char *b) {
const char *x, *y;
int r, q;
assert(a);
assert(b);
- x = (const char *) a + strlen(a);
- y = (const char *) b + strlen(b);
+ x = a + strlen(a);
+ y = b + strlen(b);
for (;;) {
char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
@@ -505,7 +525,7 @@ int dns_name_compare_func(const void *a, const void *b) {
r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
if (r < 0 || q < 0)
- return r - q;
+ return CMP(r, q);
r = ascii_strcasecmp_nn(la, r, lb, q);
if (r != 0)
@@ -513,10 +533,7 @@ int dns_name_compare_func(const void *a, const void *b) {
}
}
-const struct hash_ops dns_name_hash_ops = {
- .hash = dns_name_hash_func,
- .compare = dns_name_compare_func
-};
+DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
int dns_name_equal(const char *x, const char *y) {
int r, q;
@@ -527,11 +544,11 @@ int dns_name_equal(const char *x, const char *y) {
for (;;) {
char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
- r = dns_label_unescape(&x, la, sizeof(la));
+ r = dns_label_unescape(&x, la, sizeof la, 0);
if (r < 0)
return r;
- q = dns_label_unescape(&y, lb, sizeof(lb));
+ q = dns_label_unescape(&y, lb, sizeof lb, 0);
if (q < 0)
return q;
@@ -558,14 +575,14 @@ int dns_name_endswith(const char *name, const char *suffix) {
for (;;) {
char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
- r = dns_label_unescape(&n, ln, sizeof(ln));
+ r = dns_label_unescape(&n, ln, sizeof ln, 0);
if (r < 0)
return r;
if (!saved_n)
saved_n = n;
- q = dns_label_unescape(&s, ls, sizeof(ls));
+ q = dns_label_unescape(&s, ls, sizeof ls, 0);
if (q < 0)
return q;
@@ -596,13 +613,13 @@ int dns_name_startswith(const char *name, const char *prefix) {
for (;;) {
char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
- r = dns_label_unescape(&p, lp, sizeof(lp));
+ r = dns_label_unescape(&p, lp, sizeof lp, 0);
if (r < 0)
return r;
if (r == 0)
return true;
- q = dns_label_unescape(&n, ln, sizeof(ln));
+ q = dns_label_unescape(&n, ln, sizeof ln, 0);
if (q < 0)
return q;
@@ -631,14 +648,14 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
if (!saved_before)
saved_before = n;
- r = dns_label_unescape(&n, ln, sizeof(ln));
+ r = dns_label_unescape(&n, ln, sizeof ln, 0);
if (r < 0)
return r;
if (!saved_after)
saved_after = n;
- q = dns_label_unescape(&s, ls, sizeof(ls));
+ q = dns_label_unescape(&s, ls, sizeof ls, 0);
if (q < 0)
return q;
@@ -661,7 +678,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
/* Found it! Now generate the new name */
prefix = strndupa(name, saved_before - name);
- r = dns_name_concat(prefix, new_suffix, ret);
+ r = dns_name_concat(prefix, new_suffix, 0, ret);
if (r < 0)
return r;
@@ -739,7 +756,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
for (i = 0; i < ELEMENTSOF(a); i++) {
char label[DNS_LABEL_MAX+1];
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, 0);
if (r < 0)
return r;
if (r == 0)
@@ -776,7 +793,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
char label[DNS_LABEL_MAX+1];
int x, y;
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, 0);
if (r <= 0)
return r;
if (r != 1)
@@ -785,7 +802,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
if (x < 0)
return -EINVAL;
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, 0);
if (r <= 0)
return r;
if (r != 1)
@@ -853,7 +870,7 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo
* dns_label_unescape() returns 0 when it hits the end
* of the domain name, which we rely on here to encode
* the trailing NUL byte. */
- r = dns_label_unescape(&domain, (char *) out, len);
+ r = dns_label_unescape(&domain, (char *) out, len, 0);
if (r < 0)
return r;
@@ -920,7 +937,7 @@ bool dns_srv_type_is_valid(const char *name) {
/* This more or less implements RFC 6335, Section 5.1 */
- r = dns_label_unescape(&name, label, sizeof(label));
+ r = dns_label_unescape(&name, label, sizeof label, 0);
if (r < 0)
return false;
if (r == 0)
@@ -980,7 +997,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha
return -EINVAL;
if (!name)
- return dns_name_concat(type, domain, ret);
+ return dns_name_concat(type, domain, 0, ret);
if (!dns_service_name_is_valid(name))
return -EINVAL;
@@ -989,11 +1006,11 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha
if (r < 0)
return r;
- r = dns_name_concat(type, domain, &n);
+ r = dns_name_concat(type, domain, 0, &n);
if (r < 0)
return r;
- return dns_name_concat(escaped, n, ret);
+ return dns_name_concat(escaped, n, 0, ret);
}
static bool dns_service_name_label_is_valid(const char *label, size_t n) {
@@ -1018,7 +1035,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
assert(joined);
/* Get first label from the full name */
- an = dns_label_unescape(&p, a, sizeof(a));
+ an = dns_label_unescape(&p, a, sizeof(a), 0);
if (an < 0)
return an;
@@ -1026,7 +1043,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
x++;
/* If there was a first label, try to get the second one */
- bn = dns_label_unescape(&p, b, sizeof(b));
+ bn = dns_label_unescape(&p, b, sizeof(b), 0);
if (bn < 0)
return bn;
@@ -1035,7 +1052,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
/* If there was a second label, try to get the third one */
q = p;
- cn = dns_label_unescape(&p, c, sizeof(c));
+ cn = dns_label_unescape(&p, c, sizeof(c), 0);
if (cn < 0)
return cn;
@@ -1085,7 +1102,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
d = joined;
finish:
- r = dns_name_normalize(d, &domain);
+ r = dns_name_normalize(d, 0, &domain);
if (r < 0)
return r;
@@ -1101,7 +1118,7 @@ finish:
return 0;
}
-static int dns_name_build_suffix_table(const char *name, const char*table[]) {
+static int dns_name_build_suffix_table(const char *name, const char *table[]) {
const char *p;
unsigned n = 0;
int r;
@@ -1230,12 +1247,12 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
}
x = a_labels[n - 1 - k];
- r = dns_label_unescape(&x, la, sizeof(la));
+ r = dns_label_unescape(&x, la, sizeof la, 0);
if (r < 0)
return r;
y = b_labels[m - 1 - k];
- q = dns_label_unescape(&y, lb, sizeof(lb));
+ q = dns_label_unescape(&y, lb, sizeof lb, 0);
if (q < 0)
return q;
@@ -1286,7 +1303,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r));
if (r == IDN2_2HYPHEN)
- /* The name has two hypens — forbidden by IDNA2008 in some cases */
+ /* The name has two hyphens — forbidden by IDNA2008 in some cases */
return 0;
if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL))
return -ENOSPC;
@@ -1303,13 +1320,13 @@ int dns_name_apply_idna(const char *name, char **ret) {
for (;;) {
char label[DNS_LABEL_MAX];
- r = dns_label_unescape(&name, label, sizeof(label));
+ r = dns_label_unescape(&name, label, sizeof label, 0);
if (r < 0)
return r;
if (r == 0)
break;
- q = dns_label_apply_idna(label, r, label, sizeof(label));
+ q = dns_label_apply_idna(label, r, label, sizeof label);
if (q < 0)
return q;
if (q > 0)
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
index 0fd72cb029..6ed512c6b1 100644
--- a/src/shared/dns-domain.h
+++ b/src/shared/dns-domain.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- ***/
-
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
@@ -27,13 +24,18 @@
/* Maximum number of labels per valid hostname */
#define DNS_N_LABELS_MAX 127
-int dns_label_unescape(const char **name, char *dest, size_t sz);
+typedef enum DNSLabelFlags {
+ DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */
+ DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */
+} DNSLabelFlags;
+
+int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags);
int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
int dns_label_escape_new(const char *p, size_t l, char **ret);
static inline int dns_name_parent(const char **name) {
- return dns_label_unescape(name, NULL, DNS_LABEL_MAX);
+ return dns_label_unescape(name, NULL, DNS_LABEL_MAX, 0);
}
#if HAVE_LIBIDN
@@ -41,18 +43,29 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded
int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
#endif
-int dns_name_concat(const char *a, const char *b, char **ret);
+int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret);
-static inline int dns_name_normalize(const char *s, char **ret) {
+static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char **ret) {
/* dns_name_concat() normalizes as a side-effect */
- return dns_name_concat(s, NULL, ret);
+ return dns_name_concat(s, NULL, flags, ret);
}
static inline int dns_name_is_valid(const char *s) {
int r;
/* dns_name_normalize() verifies as a side effect */
- r = dns_name_normalize(s, NULL);
+ r = dns_name_normalize(s, 0, NULL);
+ if (r == -EINVAL)
+ return 0;
+ if (r < 0)
+ return r;
+ return 1;
+}
+
+static inline int dns_name_is_valid_ldh(const char *s) {
+ int r;
+
+ r = dns_name_concat(s, NULL, DNS_LABEL_LDH|DNS_LABEL_NO_ESCAPES, NULL);
if (r == -EINVAL)
return 0;
if (r < 0)
@@ -60,8 +73,8 @@ static inline int dns_name_is_valid(const char *s) {
return 1;
}
-void dns_name_hash_func(const void *s, struct siphash *state);
-int dns_name_compare_func(const void *a, const void *b);
+void dns_name_hash_func(const char *s, struct siphash *state);
+int dns_name_compare_func(const char *a, const char *b);
extern const struct hash_ops dns_name_hash_ops;
int dns_name_between(const char *a, const char *b, const char *c);
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index 357c66d800..409eef21ff 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -204,10 +204,10 @@ static int unit_file_find_dirs(
return 0;
type = unit_name_to_type(name);
- if (type < 0) {
- log_error("Failed to to derive unit type from unit name: %s", name);
- return -EINVAL;
- }
+ if (type < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to to derive unit type from unit name: %s",
+ name);
if (is_instance) {
r = unit_name_to_instance(name, &instance);
diff --git a/src/shared/efivars.c b/src/shared/efivars.c
index fcc0db8b0b..cb9e13c15f 100644
--- a/src/shared/efivars.c
+++ b/src/shared/efivars.c
@@ -4,6 +4,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <linux/fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -13,6 +14,7 @@
#include "sd-id128.h"
#include "alloc-util.h"
+#include "chattr-util.h"
#include "dirent-util.h"
#include "efivars.h"
#include "fd-util.h"
@@ -20,6 +22,7 @@
#include "macro.h"
#include "parse-util.h"
#include "stdio-util.h"
+#include "strv.h"
#include "time-util.h"
#include "utf8.h"
#include "util.h"
@@ -63,7 +66,10 @@ struct device_path {
} _packed_;
bool is_efi_boot(void) {
- return access("/sys/firmware/efi", F_OK) >= 0;
+ if (detect_container() > 0)
+ return false;
+
+ return access("/sys/firmware/efi/", F_OK) >= 0;
}
static int read_flag(const char *varname) {
@@ -72,6 +78,9 @@ static int read_flag(const char *varname) {
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;
@@ -80,7 +89,7 @@ static int read_flag(const char *varname) {
return -EINVAL;
b = *(uint8_t *)v;
- return b > 0;
+ return !!b;
}
bool is_efi_secure_boot(void) {
@@ -97,7 +106,7 @@ int efi_reboot_to_firmware_supported(void) {
size_t s;
int r;
- if (!is_efi_boot() || detect_container() > 0)
+ if (!is_efi_boot())
return -EOPNOTSUPP;
r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
@@ -120,21 +129,18 @@ static int get_os_indications(uint64_t *os_indication) {
size_t s;
int r;
+ /* Let's verify general support first */
r = efi_reboot_to_firmware_supported();
if (r < 0)
return r;
r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
if (r == -ENOENT) {
- /* Some firmware implementations that do support
- * OsIndications and report that with
- * OsIndicationsSupported will remove the
- * OsIndications variable when it is unset. Let's
- * pretend it's 0 then, to hide this implementation
- * detail. Note that this call will return -ENOENT
- * then only if the support for OsIndications is
- * missing entirely, as determined by
- * efi_reboot_to_firmware_supported() above. */
+ /* Some firmware implementations that do support OsIndications and report that with
+ * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's pretend it's 0
+ * then, to hide this implementation detail. Note that this call will return -ENOENT then only if the
+ * support for OsIndications is missing entirely, as determined by efi_reboot_to_firmware_supported()
+ * above. */
*os_indication = 0;
return 0;
} else if (r < 0)
@@ -240,6 +246,24 @@ int efi_get_variable(
return 0;
}
+int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
+ _cleanup_free_ void *s = NULL;
+ size_t ss = 0;
+ int r;
+ char *x;
+
+ r = efi_get_variable(vendor, name, NULL, &s, &ss);
+ if (r < 0)
+ return r;
+
+ x = utf16_to_utf8(s, ss);
+ if (!x)
+ return -ENOMEM;
+
+ *p = x;
+ return 0;
+}
+
int efi_set_variable(
sd_id128_t vendor,
const char *name,
@@ -252,6 +276,9 @@ int efi_set_variable(
} _packed_ * _cleanup_free_ buf = NULL;
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1;
+ bool saved_flags_valid = false;
+ unsigned saved_flags;
+ int r;
assert(name);
assert(value || size == 0);
@@ -261,42 +288,70 @@ int efi_set_variable(
name, SD_ID128_FORMAT_VAL(vendor)) < 0)
return -ENOMEM;
+ /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
+ * them for accidental removal and modification. We are not changing these variables accidentally however,
+ * hence let's unset the bit first. */
+
+ r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
+
+ saved_flags_valid = r >= 0;
+
if (size == 0) {
- if (unlink(p) < 0)
- return -errno;
+ if (unlink(p) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
return 0;
}
fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
- if (fd < 0)
- return -errno;
+ if (fd < 0) {
+ r = -errno;
+ goto finish;
+ }
buf = malloc(sizeof(uint32_t) + size);
- if (!buf)
- return -ENOMEM;
+ if (!buf) {
+ r = -ENOMEM;
+ goto finish;
+ }
buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
memcpy(buf->buf, value, size);
- return loop_write(fd, buf, sizeof(uint32_t) + size, false);
-}
+ r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
+ if (r < 0)
+ goto finish;
-int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
- _cleanup_free_ void *s = NULL;
- size_t ss = 0;
- int r;
- char *x;
+ r = 0;
- r = efi_get_variable(vendor, name, NULL, &s, &ss);
- if (r < 0)
- return r;
+finish:
+ if (saved_flags_valid) {
+ int q;
- x = utf16_to_utf8(s, ss);
- if (!x)
+ /* Restore the original flags field, just in case */
+ if (fd < 0)
+ q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
+ else
+ q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
+ if (q < 0)
+ log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
+ }
+
+ return r;
+}
+
+int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *v) {
+ _cleanup_free_ char16_t *u16 = NULL;
+
+ u16 = utf8_to_utf16(v, strlen(v));
+ if (!u16)
return -ENOMEM;
- *p = x;
- return 0;
+ return efi_set_variable(vendor, name, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
}
static size_t utf16_size(const uint16_t *s) {
@@ -344,6 +399,9 @@ int efi_get_boot_option(
sd_id128_t p_uuid = SD_ID128_NULL;
int r;
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
xsprintf(boot_id, "Boot%04X", id);
r = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
if (r < 0)
@@ -364,9 +422,13 @@ int efi_get_boot_option(
if (header->path_len > 0) {
uint8_t *dbuf;
- size_t dnext;
+ size_t dnext, doff;
+
+ doff = offsetof(struct boot_option, title) + title_size;
+ dbuf = buf + doff;
+ if (header->path_len > l - doff)
+ return -EINVAL;
- dbuf = buf + offsetof(struct boot_option, title) + title_size;
dnext = 0;
while (dnext < header->path_len) {
struct device_path *dpath;
@@ -403,6 +465,9 @@ int efi_get_boot_option(
/* Sub-Type 4 – File Path */
if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) {
p = utf16_to_utf8(dpath->path, dpath->length-4);
+ if (!p)
+ return -ENOMEM;
+
efi_tilt_backslashes(p);
continue;
}
@@ -455,23 +520,30 @@ static uint16_t *tilt_slashes(uint16_t *s) {
return s;
}
-int efi_add_boot_option(uint16_t id, const char *title,
- uint32_t part, uint64_t pstart, uint64_t psize,
- sd_id128_t part_uuid, const char *path) {
- char boot_id[9];
- size_t size;
- size_t title_len;
- size_t path_len;
+int efi_add_boot_option(
+ uint16_t id,
+ const char *title,
+ uint32_t part,
+ uint64_t pstart,
+ uint64_t psize,
+ sd_id128_t part_uuid,
+ const char *path) {
+
+ size_t size, title_len, path_len;
+ _cleanup_free_ char *buf = NULL;
struct boot_option *option;
struct device_path *devicep;
- _cleanup_free_ char *buf = NULL;
+ char boot_id[9];
+
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
title_len = (strlen(title)+1) * 2;
path_len = (strlen(path)+1) * 2;
- buf = calloc(sizeof(struct boot_option) + title_len +
- sizeof(struct drive_path) +
- sizeof(struct device_path) + path_len, 1);
+ buf = malloc0(sizeof(struct boot_option) + title_len +
+ sizeof(struct drive_path) +
+ sizeof(struct device_path) + path_len);
if (!buf)
return -ENOMEM;
@@ -520,6 +592,9 @@ int efi_add_boot_option(uint16_t id, const char *title,
int efi_remove_boot_option(uint16_t id) {
char boot_id[9];
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
xsprintf(boot_id, "Boot%04X", id);
return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0);
}
@@ -529,6 +604,9 @@ int efi_get_boot_order(uint16_t **order) {
size_t l;
int r;
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
if (r < 0)
return r;
@@ -545,12 +623,15 @@ int efi_get_boot_order(uint16_t **order) {
}
int efi_set_boot_order(uint16_t *order, size_t n) {
+
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t));
}
static int boot_id_hex(const char s[4]) {
- int i;
- int id = 0;
+ int id = 0, i;
for (i = 0; i < 4; i++)
if (s[i] >= '0' && s[i] <= '9')
@@ -563,21 +644,22 @@ static int boot_id_hex(const char s[4]) {
return id;
}
-static int cmp_uint16(const void *_a, const void *_b) {
- const uint16_t *a = _a, *b = _b;
-
- return (int)*a - (int)*b;
+static int cmp_uint16(const uint16_t *a, const uint16_t *b) {
+ return CMP(*a, *b);
}
int efi_get_boot_options(uint16_t **options) {
_cleanup_closedir_ DIR *dir = NULL;
- struct dirent *de;
_cleanup_free_ uint16_t *list = NULL;
+ struct dirent *de;
size_t alloc = 0;
int count = 0;
assert(options);
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
dir = opendir("/sys/firmware/efi/efivars/");
if (!dir)
return -errno;
@@ -604,7 +686,7 @@ int efi_get_boot_options(uint16_t **options) {
list[count++] = id;
}
- qsort_safe(list, count, sizeof(uint16_t), cmp_uint16);
+ typesafe_qsort(list, count, cmp_uint16);
*options = TAKE_PTR(list);
@@ -638,6 +720,9 @@ int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
assert(firmware);
assert(loader);
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
if (r < 0)
return r;
@@ -662,6 +747,9 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
_cleanup_free_ char *p = NULL;
int r, parsed[16];
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
if (r < 0)
return r;
@@ -683,6 +771,118 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
return 0;
}
+bool efi_loader_entry_name_valid(const char *s) {
+ if (isempty(s))
+ return false;
+
+ if (strlen(s) > FILENAME_MAX) /* Make sure entry names fit in filenames */
+ return false;
+
+ return in_charset(s, ALPHANUMERICAL "-");
+}
+
+int efi_loader_get_entries(char ***ret) {
+ _cleanup_free_ char16_t *entries = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ size_t size, i, start;
+ int r;
+
+ assert(ret);
+
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
+ r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderEntries", NULL, (void**) &entries, &size);
+ if (r < 0)
+ return r;
+
+ /* The variable contains a series of individually NUL terminated UTF-16 strings. */
+
+ for (i = 0, start = 0;; i++) {
+ _cleanup_free_ char *decoded = NULL;
+ bool end;
+
+ /* Is this the end of the variable's data? */
+ end = i * sizeof(char16_t) >= size;
+
+ /* Are we in the middle of a string? (i.e. not at the end of the variable, nor at a NUL terminator?) If
+ * so, let's go to the next entry. */
+ if (!end && entries[i] != 0)
+ continue;
+
+ /* We reached the end of a string, let's decode it into UTF-8 */
+ decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
+ if (!decoded)
+ return -ENOMEM;
+
+ if (efi_loader_entry_name_valid(decoded)) {
+ r = strv_consume(&l, TAKE_PTR(decoded));
+ if (r < 0)
+ return r;
+ } else
+ log_debug("Ignoring invalid loader entry '%s'.", decoded);
+
+ /* We reached the end of the variable */
+ if (end)
+ break;
+
+ /* Continue after the NUL byte */
+ start = i + 1;
+ }
+
+ *ret = TAKE_PTR(l);
+ return 0;
+}
+
+int efi_loader_get_features(uint64_t *ret) {
+ _cleanup_free_ void *v = NULL;
+ size_t s;
+ int r;
+
+ if (!is_efi_boot()) {
+ *ret = 0;
+ return 0;
+ }
+
+ r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderFeatures", NULL, &v, &s);
+ if (r == -ENOENT) {
+ _cleanup_free_ char *info = NULL;
+
+ /* The new (v240+) LoaderFeatures variable is not supported, let's see if it's systemd-boot at all */
+ r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo", &info);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return r;
+
+ /* Variable not set, definitely means not systemd-boot */
+
+ } else if (first_word(info, "systemd-boot")) {
+
+ /* An older systemd-boot version. Let's hardcode the feature set, since it was pretty
+ * static in all its versions. */
+
+ *ret = EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
+ EFI_LOADER_FEATURE_ENTRY_DEFAULT |
+ EFI_LOADER_FEATURE_ENTRY_ONESHOT;
+
+ return 0;
+ }
+
+ /* No features supported */
+ *ret = 0;
+ return 0;
+ }
+ if (r < 0)
+ return r;
+
+ if (s != sizeof(uint64_t))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "LoaderFeatures EFI variable doesn't have the right size.");
+
+ memcpy(ret, v, sizeof(uint64_t));
+ return 0;
+}
+
#endif
char *efi_tilt_backslashes(char *s) {
diff --git a/src/shared/efivars.h b/src/shared/efivars.h
index dac66da9f8..92670c82c7 100644
--- a/src/shared/efivars.h
+++ b/src/shared/efivars.h
@@ -18,6 +18,12 @@
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
+#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT (UINT64_C(1) << 0)
+#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (UINT64_C(1) << 1)
+#define EFI_LOADER_FEATURE_ENTRY_DEFAULT (UINT64_C(1) << 2)
+#define EFI_LOADER_FEATURE_ENTRY_ONESHOT (UINT64_C(1) << 3)
+#define EFI_LOADER_FEATURE_BOOT_COUNTING (UINT64_C(1) << 4)
+
#if ENABLE_EFI
bool is_efi_boot(void);
@@ -28,8 +34,9 @@ int efi_get_reboot_to_firmware(void);
int efi_set_reboot_to_firmware(bool value);
int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
-int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);
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);
int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active);
int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path);
@@ -41,6 +48,12 @@ int efi_get_boot_options(uint16_t **options);
int efi_loader_get_device_part_uuid(sd_id128_t *u);
int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader);
+int efi_loader_get_entries(char ***ret);
+
+bool efi_loader_entry_name_valid(const char *s);
+
+int efi_loader_get_features(uint64_t *ret);
+
#else
static inline bool is_efi_boot(void) {
@@ -71,11 +84,15 @@ static inline int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t
return -EOPNOTSUPP;
}
+static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
+ return -EOPNOTSUPP;
+}
+
static inline int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size) {
return -EOPNOTSUPP;
}
-static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
+static inline int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p) {
return -EOPNOTSUPP;
}
@@ -111,6 +128,14 @@ static inline int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
return -EOPNOTSUPP;
}
+static inline int efi_loader_get_entries(char ***ret) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_loader_get_features(uint64_t *ret) {
+ return -EOPNOTSUPP;
+}
+
#endif
char *efi_tilt_backslashes(char *s);
diff --git a/src/shared/enable-mempool.c b/src/shared/enable-mempool.c
new file mode 100644
index 0000000000..a571b43f57
--- /dev/null
+++ b/src/shared/enable-mempool.c
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "mempool.h"
+
+const bool mempool_use_allowed = true;
diff --git a/src/shared/env-file-label.c b/src/shared/env-file-label.c
new file mode 100644
index 0000000000..add68d224d
--- /dev/null
+++ b/src/shared/env-file-label.c
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/stat.h>
+
+#include "env-file-label.h"
+#include "env-file.h"
+#include "selinux-util.h"
+
+int write_env_file_label(const char *fname, char **l) {
+ int r;
+
+ r = mac_selinux_create_file_prepare(fname, S_IFREG);
+ if (r < 0)
+ return r;
+
+ r = write_env_file(fname, l);
+
+ mac_selinux_create_file_clear();
+
+ return r;
+}
diff --git a/src/shared/env-file-label.h b/src/shared/env-file-label.h
new file mode 100644
index 0000000000..158fc4ec0b
--- /dev/null
+++ b/src/shared/env-file-label.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+/* These functions are split out of fileio.h (and not for example just flags to the functions they wrap) in order to
+ * optimize linking: This way, -lselinux is needed only for the callers of these functions that need selinux, but not
+ * for all */
+
+int write_env_file_label(const char *fname, char **l);
diff --git a/src/basic/exec-util.c b/src/shared/exec-util.c
index 7e336f9ce1..17a278a00f 100644
--- a/src/basic/exec-util.c
+++ b/src/shared/exec-util.c
@@ -9,6 +9,7 @@
#include "alloc-util.h"
#include "conf-files.h"
+#include "env-file.h"
#include "env-util.h"
#include "exec-util.h"
#include "fd-util.h"
@@ -16,12 +17,15 @@
#include "hashmap.h"
#include "macro.h"
#include "process-util.h"
+#include "rlimit-util.h"
+#include "serialize.h"
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "util.h"
/* Put this test here for a lack of better place */
@@ -49,6 +53,8 @@ static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
_exit(EXIT_FAILURE);
}
+ (void) rlimit_nofile_safe();
+
if (!argv) {
_argv[0] = (char*) path;
_argv[1] = NULL;
@@ -71,11 +77,12 @@ static int do_execute(
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void* const callback_args[_STDOUT_CONSUME_MAX],
int output_fd,
- char *argv[]) {
+ char *argv[],
+ char *envp[]) {
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
_cleanup_strv_free_ char **paths = NULL;
- char **path;
+ char **path, **e;
int r;
/* We fork this all off from a child process so that we can somewhat cleanly make
@@ -86,7 +93,7 @@ static int do_execute(
r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to enumerate executables: %m");
if (!callbacks) {
pids = hashmap_new(NULL);
@@ -100,6 +107,10 @@ static int do_execute(
if (timeout != USEC_INFINITY)
alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
+ STRV_FOREACH(e, envp)
+ if (putenv(*e) != 0)
+ return log_error_errno(errno, "Failed to set environment variable: %m");
+
STRV_FOREACH(path, paths) {
_cleanup_free_ char *t = NULL;
_cleanup_close_ int fd = -1;
@@ -166,7 +177,8 @@ int execute_directories(
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void* const callback_args[_STDOUT_CONSUME_MAX],
- char *argv[]) {
+ char *argv[],
+ char *envp[]) {
char **dirs = (char**) directories;
_cleanup_close_ int fd = -1;
@@ -197,7 +209,7 @@ int execute_directories(
if (r < 0)
return r;
if (r == 0) {
- r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
+ r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp);
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
@@ -234,7 +246,7 @@ static int gather_environment_generate(int fd, void *arg) {
return -errno;
}
- r = load_env_file_pairs(f, NULL, NULL, &new);
+ r = load_env_file_pairs(f, NULL, &new);
if (r < 0)
return r;
@@ -262,8 +274,8 @@ static int gather_environment_generate(int fd, void *arg) {
}
static int gather_environment_collect(int fd, void *arg) {
- char ***env = arg;
_cleanup_fclose_ FILE *f = NULL;
+ char ***env = arg;
int r;
/* Write out a series of env=cescape(VAR=value) assignments to fd. */
@@ -276,21 +288,21 @@ static int gather_environment_collect(int fd, void *arg) {
return -errno;
}
- r = serialize_environment(f, *env);
+ r = serialize_strv(f, "env", *env);
if (r < 0)
return r;
- if (ferror(f))
- return errno > 0 ? -errno : -EIO;
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
return 0;
}
static int gather_environment_consume(int fd, void *arg) {
- char ***env = arg;
_cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
- int r = 0, k;
+ char ***env = arg;
+ int r = 0;
/* Read a series of env=cescape(VAR=value) assignments from fd into env. */
@@ -302,14 +314,33 @@ static int gather_environment_consume(int fd, void *arg) {
return -errno;
}
- FOREACH_LINE(line, f, return -EIO) {
- truncate_nl(line);
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ const char *v;
+ int k;
- k = deserialize_environment(env, line);
+ k = read_line(f, LONG_LINE_MAX, &line);
if (k < 0)
- log_error_errno(k, "Invalid line \"%s\": %m", line);
- if (k < 0 && r == 0)
- r = k;
+ return k;
+ if (k == 0)
+ break;
+
+ v = startswith(line, "env=");
+ if (!v) {
+ log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line);
+ if (r == 0)
+ r = -EINVAL;
+
+ continue;
+ }
+
+ k = deserialize_environment(v, env);
+ if (k < 0) {
+ log_debug_errno(k, "Invalid serialization line \"%s\": %m", line);
+
+ if (r == 0)
+ r = k;
+ }
}
return r;
diff --git a/src/basic/exec-util.h b/src/shared/exec-util.h
index 8b1f18139c..6ac3c9000a 100644
--- a/src/basic/exec-util.h
+++ b/src/shared/exec-util.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
#include <stdbool.h>
@@ -18,6 +19,7 @@ int execute_directories(
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void* const callback_args[_STDOUT_CONSUME_MAX],
- char *argv[]);
+ char *argv[],
+ char *envp[]);
extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
diff --git a/src/basic/exit-status.c b/src/shared/exit-status.c
index 21af8c4c71..26b3060d9b 100644
--- a/src/basic/exit-status.c
+++ b/src/shared/exit-status.c
@@ -19,9 +19,10 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) {
* 79…199 │ (Currently unmapped)
* 200…241 │ systemd's private error codes (might be extended to 254 in future development)
* 242…254 │ (Currently unmapped, but see above)
- * 255 │ (We should probably stay away from that one, it's frequently used by applications to indicate an
- * │ exit reason that cannot really be expressed in a single exit status value — such as a propagated
- * │ signal or such)
+ *
+ * 255 │ EXIT_EXCEPTION (We use this to propagate exit-by-signal events. It's frequently used by others apps (like bash)
+ * │ to indicate exit reason that cannot really be expressed in a single exit status value — such as a propagated
+ * │ signal or such, and we follow that logic here.)
*/
switch (status) { /* We always cover the ISO C ones */
@@ -155,6 +156,9 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) {
case EXIT_CONFIGURATION_DIRECTORY:
return "CONFIGURATION_DIRECTORY";
+
+ case EXIT_EXCEPTION:
+ return "EXCEPTION";
}
}
diff --git a/src/basic/exit-status.h b/src/shared/exit-status.h
index c41e8b82c3..510eb319cf 100644
--- a/src/basic/exit-status.h
+++ b/src/shared/exit-status.h
@@ -69,6 +69,8 @@ enum {
EXIT_CACHE_DIRECTORY,
EXIT_LOGS_DIRECTORY, /* 240 */
EXIT_CONFIGURATION_DIRECTORY,
+
+ EXIT_EXCEPTION = 255, /* Whenever we want to propagate an abnormal/signal exit, in line with bash */
};
typedef enum ExitStatusLevel {
diff --git a/src/shared/fdset.c b/src/shared/fdset.c
index 8c852f1137..5d277328c7 100644
--- a/src/shared/fdset.c
+++ b/src/shared/fdset.c
@@ -211,13 +211,16 @@ fail:
int fdset_close_others(FDSet *fds) {
void *e;
Iterator i;
- int *a;
+ int *a = NULL;
size_t j = 0, m;
m = fdset_size(fds);
- a = newa(int, m);
- SET_FOREACH(e, MAKE_SET(fds), i)
- a[j++] = PTR_TO_FD(e);
+
+ if (m > 0) {
+ a = newa(int, m);
+ SET_FOREACH(e, MAKE_SET(fds), i)
+ a[j++] = PTR_TO_FD(e);
+ }
assert(j == m);
diff --git a/src/basic/fileio-label.c b/src/shared/fileio-label.c
index 5f8d8af9af..49ab29720b 100644
--- a/src/basic/fileio-label.c
+++ b/src/shared/fileio-label.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2010 Harald Hoyer
-***/
#include <sys/stat.h>
@@ -23,35 +20,6 @@ int write_string_file_atomic_label_ts(const char *fn, const char *line, struct t
return r;
}
-int write_env_file_label(const char *fname, char **l) {
- int r;
-
- r = mac_selinux_create_file_prepare(fname, S_IFREG);
- if (r < 0)
- return r;
-
- r = write_env_file(fname, l);
-
- mac_selinux_create_file_clear();
-
- return r;
-}
-
-int fopen_temporary_label(const char *target,
- const char *path, FILE **f, char **temp_path) {
- int r;
-
- r = mac_selinux_create_file_prepare(target, S_IFREG);
- if (r < 0)
- return r;
-
- r = fopen_temporary(path, f, temp_path);
-
- mac_selinux_create_file_clear();
-
- return r;
-}
-
int create_shutdown_run_nologin_or_warn(void) {
int r;
diff --git a/src/basic/fileio-label.h b/src/shared/fileio-label.h
index db8e738326..8f88955d81 100644
--- a/src/basic/fileio-label.h
+++ b/src/shared/fileio-label.h
@@ -1,23 +1,15 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- Copyright © 2010 Harald Hoyer
-***/
-
#include <stdio.h>
-#include "fileio.h"
-
-/* These functions are split out of fileio.h (and not for examplement just as flags to the functions they wrap) in
- * order to optimize linking: This way, -lselinux is needed only for the callers of these functions that need selinux,
- * but not for all */
+/* These functions are split out of fileio.h (and not for example just flags to the functions they wrap) in order to
+ * optimize linking: This way, -lselinux is needed only for the callers of these functions that need selinux, but not
+ * for all */
int write_string_file_atomic_label_ts(const char *fn, const char *line, struct timespec *ts);
static inline int write_string_file_atomic_label(const char *fn, const char *line) {
return write_string_file_atomic_label_ts(fn, line, NULL);
}
-int write_env_file_label(const char *fname, char **l);
-int fopen_temporary_label(const char *target, const char *path, FILE **f, char **temp_path);
int create_shutdown_run_nologin_or_warn(void);
diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c
index eb4f5ff616..cba52fb419 100644
--- a/src/shared/firewall-util.c
+++ b/src/shared/firewall-util.c
@@ -50,8 +50,14 @@ static int entry_fill_basics(
entry->ip.proto = protocol;
if (in_interface) {
+ size_t l;
+
+ l = strlen(in_interface);
+ assert(l < sizeof entry->ip.iniface);
+ assert(l < sizeof entry->ip.iniface_mask);
+
strcpy(entry->ip.iniface, in_interface);
- memset(entry->ip.iniface_mask, 0xFF, strlen(in_interface)+1);
+ memset(entry->ip.iniface_mask, 0xFF, l + 1);
}
if (source) {
entry->ip.src = source->in;
diff --git a/src/basic/format-table.c b/src/shared/format-table.c
index 94e796d1ca..7d529801a1 100644
--- a/src/basic/format-table.c
+++ b/src/shared/format-table.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <ctype.h>
#include <stdio_ext.h>
#include "alloc-util.h"
@@ -9,6 +10,7 @@
#include "gunicode.h"
#include "pager.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "string-util.h"
#include "terminal-util.h"
#include "time-util.h"
@@ -36,7 +38,7 @@
that. The first row is always the header row. If header display is turned off we simply skip outputting the first
row. Also, when sorting rows we always leave the first row where it is, as the header shouldn't move.
- - Note because there's no row and no column object some properties that might be approproate as row/column properties
+ - Note because there's no row and no column object some properties that might be appropriate as row/column properties
are exposed as cell properties instead. For example, the "weight" of a column (which is used to determine where to
add/remove space preferable when expanding/compressing tables horizontally) is actually made the "weight" of a
cell. Given that we usually need it per-column though we will calculate the average across every cell of the column
@@ -57,7 +59,10 @@ typedef struct TableData {
unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */
unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */
+ bool uppercase; /* Uppercase string on display */
+
const char *color; /* ANSI color string to use for this cell. When written to terminal should not move cursor. Will automatically be reset after the cell */
+ char *url; /* A URL to use for a clickable hyperlink */
char *formatted; /* A cached textual representation of the cell data, before ellipsation/alignment */
union {
@@ -68,16 +73,18 @@ typedef struct TableData {
uint64_t size;
char string[0];
uint32_t uint32;
+ uint64_t uint64;
+ int percent; /* we use 'int' as datatype for percent values in order to match the result of parse_percent() */
/* … add more here as we start supporting more cell data types … */
};
} TableData;
static size_t TABLE_CELL_TO_INDEX(TableCell *cell) {
- unsigned i;
+ size_t i;
assert(cell);
- i = PTR_TO_UINT(cell);
+ i = PTR_TO_SIZE(cell);
assert(i > 0);
return i-1;
@@ -85,7 +92,7 @@ static size_t TABLE_CELL_TO_INDEX(TableCell *cell) {
static TableCell* TABLE_INDEX_TO_CELL(size_t index) {
assert(index != (size_t) -1);
- return UINT_TO_PTR((unsigned) (index + 1));
+ return SIZE_TO_PTR(index + 1);
}
struct Table {
@@ -103,6 +110,8 @@ struct Table {
size_t *sort_map; /* The columns to order rows by, in order of preference. */
size_t n_sort_map;
+
+ bool *reverse_map;
};
Table *table_new_raw(size_t n_columns) {
@@ -126,6 +135,7 @@ Table *table_new_raw(size_t n_columns) {
Table *table_new_internal(const char *first_header, ...) {
_cleanup_(table_unrefp) Table *t = NULL;
size_t n_columns = 1;
+ const char *h;
va_list ap;
int r;
@@ -133,8 +143,6 @@ Table *table_new_internal(const char *first_header, ...) {
va_start(ap, first_header);
for (;;) {
- const char *h;
-
h = va_arg(ap, const char*);
if (!h)
break;
@@ -147,19 +155,18 @@ Table *table_new_internal(const char *first_header, ...) {
if (!t)
return NULL;
- r = table_add_cell(t, NULL, TABLE_STRING, first_header);
- if (r < 0)
- return NULL;
-
va_start(ap, first_header);
- for (;;) {
- const char *h;
+ for (h = first_header; h; h = va_arg(ap, const char*)) {
+ TableCell *cell;
- h = va_arg(ap, const char*);
- if (!h)
- break;
+ r = table_add_cell(t, &cell, TABLE_STRING, h);
+ if (r < 0) {
+ va_end(ap);
+ return NULL;
+ }
- r = table_add_cell(t, NULL, TABLE_STRING, h);
+ /* Make the table header uppercase */
+ r = table_set_uppercase(t, cell, true);
if (r < 0) {
va_end(ap);
return NULL;
@@ -171,32 +178,18 @@ Table *table_new_internal(const char *first_header, ...) {
return TAKE_PTR(t);
}
-static TableData *table_data_unref(TableData *d) {
- if (!d)
- return NULL;
-
- assert(d->n_ref > 0);
- d->n_ref--;
-
- if (d->n_ref > 0)
- return NULL;
+static TableData *table_data_free(TableData *d) {
+ assert(d);
free(d->formatted);
+ free(d->url);
+
return mfree(d);
}
+DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(TableData, table_data, table_data_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(TableData*, table_data_unref);
-static TableData *table_data_ref(TableData *d) {
- if (!d)
- return NULL;
-
- assert(d->n_ref > 0);
- d->n_ref++;
-
- return d;
-}
-
Table *table_unref(Table *t) {
size_t i;
@@ -209,6 +202,7 @@ Table *table_unref(Table *t) {
free(t->data);
free(t->display_map);
free(t->sort_map);
+ free(t->reverse_map);
return mfree(t);
}
@@ -231,11 +225,15 @@ static size_t table_data_size(TableDataType type, const void *data) {
return sizeof(usec_t);
case TABLE_SIZE:
+ case TABLE_UINT64:
return sizeof(uint64_t);
case TABLE_UINT32:
return sizeof(uint32_t);
+ case TABLE_PERCENT:
+ return sizeof(int);
+
default:
assert_not_reached("Uh? Unexpected cell type");
}
@@ -272,13 +270,21 @@ static bool table_data_matches(
if (d->ellipsize_percent != ellipsize_percent)
return false;
+ /* If a color/url/uppercase flag is set, refuse to merge */
+ if (d->color)
+ return false;
+ if (d->url)
+ return false;
+ if (d->uppercase)
+ return false;
+
k = table_data_size(type, data);
l = table_data_size(d->type, d->data);
if (k != l)
return false;
- return memcmp(data, d->data, l) == 0;
+ return memcmp_safe(data, d->data, l) == 0;
}
static TableData *table_data_new(
@@ -392,6 +398,7 @@ int table_dup_cell(Table *t, TableCell *cell) {
}
static int table_dedup_cell(Table *t, TableCell *cell) {
+ _cleanup_free_ char *curl = NULL;
TableData *nd, *od;
size_t i;
@@ -410,10 +417,27 @@ static int table_dedup_cell(Table *t, TableCell *cell) {
assert(od->n_ref > 1);
- nd = table_data_new(od->type, od->data, od->minimum_width, od->maximum_width, od->weight, od->align_percent, od->ellipsize_percent);
+ if (od->url) {
+ curl = strdup(od->url);
+ if (!curl)
+ return -ENOMEM;
+ }
+
+ nd = table_data_new(
+ od->type,
+ od->data,
+ od->minimum_width,
+ od->maximum_width,
+ od->weight,
+ od->align_percent,
+ od->ellipsize_percent);
if (!nd)
return -ENOMEM;
+ nd->color = od->color;
+ nd->url = TAKE_PTR(curl);
+ nd->uppercase = od->uppercase;
+
table_data_unref(od);
t->data[i] = nd;
@@ -540,6 +564,88 @@ int table_set_color(Table *t, TableCell *cell, const char *color) {
return 0;
}
+int table_set_url(Table *t, TableCell *cell, const char *url) {
+ _cleanup_free_ char *copy = NULL;
+ int r;
+
+ assert(t);
+ assert(cell);
+
+ if (url) {
+ copy = strdup(url);
+ if (!copy)
+ return -ENOMEM;
+ }
+
+ r = table_dedup_cell(t, cell);
+ if (r < 0)
+ return r;
+
+ return free_and_replace(table_get_data(t, cell)->url, copy);
+}
+
+int table_set_uppercase(Table *t, TableCell *cell, bool b) {
+ TableData *d;
+ int r;
+
+ assert(t);
+ assert(cell);
+
+ r = table_dedup_cell(t, cell);
+ if (r < 0)
+ return r;
+
+ assert_se(d = table_get_data(t, cell));
+
+ if (d->uppercase == b)
+ return 0;
+
+ d->formatted = mfree(d->formatted);
+ d->uppercase = b;
+ return 1;
+}
+
+int table_update(Table *t, TableCell *cell, TableDataType type, const void *data) {
+ _cleanup_free_ char *curl = NULL;
+ TableData *nd, *od;
+ size_t i;
+
+ assert(t);
+ assert(cell);
+
+ i = TABLE_CELL_TO_INDEX(cell);
+ if (i >= t->n_cells)
+ return -ENXIO;
+
+ assert_se(od = t->data[i]);
+
+ if (od->url) {
+ curl = strdup(od->url);
+ if (!curl)
+ return -ENOMEM;
+ }
+
+ nd = table_data_new(
+ type,
+ data,
+ od->minimum_width,
+ od->maximum_width,
+ od->weight,
+ od->align_percent,
+ od->ellipsize_percent);
+ if (!nd)
+ return -ENOMEM;
+
+ nd->color = od->color;
+ nd->url = TAKE_PTR(curl);
+ nd->uppercase = od->uppercase;
+
+ table_data_unref(od);
+ t->data[i] = nd;
+
+ return 0;
+}
+
int table_add_many_internal(Table *t, TableDataType first_type, ...) {
TableDataType type;
va_list ap;
@@ -558,6 +664,8 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
uint64_t size;
usec_t usec;
uint32_t uint32;
+ uint64_t uint64;
+ int percent;
bool b;
} buffer;
@@ -592,6 +700,16 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
data = &buffer.uint32;
break;
+ case TABLE_UINT64:
+ buffer.uint64 = va_arg(ap, uint64_t);
+ data = &buffer.uint64;
+ break;
+
+ case TABLE_PERCENT:
+ buffer.percent = va_arg(ap, int);
+ data = &buffer.percent;
+ break;
+
case _TABLE_DATA_TYPE_MAX:
/* Used as end marker */
va_end(ap);
@@ -704,32 +822,22 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
return 0;
case TABLE_TIMESTAMP:
- if (a->timestamp < b->timestamp)
- return -1;
- if (a->timestamp > b->timestamp)
- return 1;
- return 0;
+ return CMP(a->timestamp, b->timestamp);
case TABLE_TIMESPAN:
- if (a->timespan < b->timespan)
- return -1;
- if (a->timespan > b->timespan)
- return 1;
- return 0;
+ return CMP(a->timespan, b->timespan);
case TABLE_SIZE:
- if (a->size < b->size)
- return -1;
- if (a->size > b->size)
- return 1;
- return 0;
+ return CMP(a->size, b->size);
case TABLE_UINT32:
- if (a->uint32 < b->uint32)
- return -1;
- if (a->uint32 > b->uint32)
- return 1;
- return 0;
+ return CMP(a->uint32, b->uint32);
+
+ case TABLE_UINT64:
+ return CMP(a->uint64, b->uint64);
+
+ case TABLE_PERCENT:
+ return CMP(a->percent, b->percent);
default:
;
@@ -737,17 +845,10 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
}
/* Generic fallback using the orginal order in which the cells where added. */
- if (index_a < index_b)
- return -1;
- if (index_a > index_b)
- return 1;
-
- return 0;
+ return CMP(index_a, index_b);
}
-static int table_data_compare(const void *x, const void *y, void *userdata) {
- const size_t *a = x, *b = y;
- Table *t = userdata;
+static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
size_t i;
int r;
@@ -771,16 +872,11 @@ static int table_data_compare(const void *x, const void *y, void *userdata) {
r = cell_data_compare(d, *a, dd, *b);
if (r != 0)
- return r;
+ return t->reverse_map && t->reverse_map[t->sort_map[i]] ? -r : r;
}
/* Order identical lines by the order there were originally added in */
- if (*a < *b)
- return -1;
- if (*a > *b)
- return 1;
-
- return 0;
+ return CMP(*a, *b);
}
static const char *table_data_format(TableData *d) {
@@ -794,6 +890,20 @@ static const char *table_data_format(TableData *d) {
return "";
case TABLE_STRING:
+ if (d->uppercase) {
+ char *p, *q;
+
+ d->formatted = new(char, strlen(d->string) + 1);
+ if (!d->formatted)
+ return NULL;
+
+ for (p = d->string, q = d->formatted; *p; p++, q++)
+ *q = (char) toupper((unsigned char) *p);
+ *q = 0;
+
+ return d->formatted;
+ }
+
return d->string;
case TABLE_BOOLEAN:
@@ -820,7 +930,7 @@ static const char *table_data_format(TableData *d) {
if (!p)
return NULL;
- if (!format_timespan(p, FORMAT_TIMESPAN_MAX, d->timestamp, 0))
+ if (!format_timespan(p, FORMAT_TIMESPAN_MAX, d->timespan, 0))
return "n/a";
d->formatted = TAKE_PTR(p);
@@ -853,6 +963,30 @@ static const char *table_data_format(TableData *d) {
break;
}
+ case TABLE_UINT64: {
+ _cleanup_free_ char *p;
+
+ p = new(char, DECIMAL_STR_WIDTH(d->uint64) + 1);
+ if (!p)
+ return NULL;
+
+ sprintf(p, "%" PRIu64, d->uint64);
+ d->formatted = TAKE_PTR(p);
+ break;
+ }
+
+ case TABLE_PERCENT: {
+ _cleanup_free_ char *p;
+
+ p = new(char, DECIMAL_STR_WIDTH(d->percent) + 2);
+ if (!p)
+ return NULL;
+
+ sprintf(p, "%i%%" , d->percent);
+ d->formatted = TAKE_PTR(p);
+ break;
+ }
+
default:
assert_not_reached("Unexpected type?");
}
@@ -882,11 +1016,13 @@ static int table_data_requested_width(TableData *d, size_t *ret) {
return 0;
}
-static char *align_string_mem(const char *str, size_t new_length, unsigned percent) {
- size_t w = 0, space, lspace, old_length;
+static char *align_string_mem(const char *str, const char *url, size_t new_length, unsigned percent) {
+ size_t w = 0, space, lspace, old_length, clickable_length;
+ _cleanup_free_ char *clickable = NULL;
const char *p;
char *ret;
size_t i;
+ int r;
/* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */
@@ -895,6 +1031,15 @@ static char *align_string_mem(const char *str, size_t new_length, unsigned perce
old_length = strlen(str);
+ if (url) {
+ r = terminal_urlify(url, str, &clickable);
+ if (r < 0)
+ return NULL;
+
+ clickable_length = strlen(clickable);
+ } else
+ clickable_length = old_length;
+
/* Determine current width on screen */
p = str;
while (p < str + old_length) {
@@ -911,23 +1056,23 @@ static char *align_string_mem(const char *str, size_t new_length, unsigned perce
/* Already wider than the target, if so, don't do anything */
if (w >= new_length)
- return strndup(str, old_length);
+ return clickable ? TAKE_PTR(clickable) : strdup(str);
/* How much spaces shall we add? An how much on the left side? */
space = new_length - w;
lspace = space * percent / 100U;
- ret = new(char, space + old_length + 1);
+ ret = new(char, space + clickable_length + 1);
if (!ret)
return NULL;
for (i = 0; i < lspace; i++)
ret[i] = ' ';
- memcpy(ret + lspace, str, old_length);
- for (i = lspace + old_length; i < space + old_length; i++)
+ memcpy(ret + lspace, clickable ?: str, clickable_length);
+ for (i = lspace + clickable_length; i < space + clickable_length; i++)
ret[i] = ' ';
- ret[space + old_length] = 0;
+ ret[space + clickable_length] = 0;
return ret;
}
@@ -960,7 +1105,7 @@ int table_print(Table *t, FILE *f) {
for (i = 0; i < n_rows; i++)
sorted[i] = i * t->n_columns;
- qsort_r_safe(sorted, n_rows, sizeof(size_t), table_data_compare, t);
+ typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
}
if (t->display_map)
@@ -1134,14 +1279,12 @@ int table_print(Table *t, FILE *f) {
assert(weight_sum >= column_weight[j]);
weight_sum -= column_weight[j];
- if (restart)
+ if (restart && !finalize)
break;
}
- if (finalize) {
- assert(!restart);
+ if (finalize)
break;
- }
if (!restart)
finalize = true;
@@ -1182,23 +1325,41 @@ int table_print(Table *t, FILE *f) {
} else if (l < width[j]) {
/* Field is shorter than allocated space. Let's align with spaces */
- buffer = align_string_mem(field, width[j], d->align_percent);
+ buffer = align_string_mem(field, d->url, width[j], d->align_percent);
if (!buffer)
return -ENOMEM;
field = buffer;
}
+ if (l >= width[j] && d->url) {
+ _cleanup_free_ char *clickable = NULL;
+
+ r = terminal_urlify(d->url, field, &clickable);
+ if (r < 0)
+ return r;
+
+ free_and_replace(buffer, clickable);
+ field = buffer;
+ }
+
+ if (row == t->data) /* underline header line fully, including the column separator */
+ fputs(ansi_underline(), f);
+
if (j > 0)
fputc(' ', f); /* column separator */
- if (d->color)
+ if (d->color && colors_enabled()) {
+ if (row == t->data) /* first undo header underliner */
+ fputs(ANSI_NORMAL, f);
+
fputs(d->color, f);
+ }
fputs(field, f);
- if (d->color)
- fputs(ansi_normal(), f);
+ if (colors_enabled() && (d->color || row == t->data))
+ fputs(ANSI_NORMAL, f);
}
fputc('\n', f);
@@ -1245,3 +1406,220 @@ size_t table_get_columns(Table *t) {
assert(t->n_columns > 0);
return t->n_columns;
}
+
+int table_set_reverse(Table *t, size_t column, bool b) {
+ assert(t);
+ assert(column < t->n_columns);
+
+ if (!t->reverse_map) {
+ if (!b)
+ return 0;
+
+ t->reverse_map = new0(bool, t->n_columns);
+ if (!t->reverse_map)
+ return -ENOMEM;
+ }
+
+ t->reverse_map[column] = b;
+ return 0;
+}
+
+TableCell *table_get_cell(Table *t, size_t row, size_t column) {
+ size_t i;
+
+ assert(t);
+
+ if (column >= t->n_columns)
+ return NULL;
+
+ i = row * t->n_columns + column;
+ if (i >= t->n_cells)
+ return NULL;
+
+ return TABLE_INDEX_TO_CELL(i);
+}
+
+const void *table_get(Table *t, TableCell *cell) {
+ TableData *d;
+
+ assert(t);
+
+ d = table_get_data(t, cell);
+ if (!d)
+ return NULL;
+
+ return d->data;
+}
+
+const void* table_get_at(Table *t, size_t row, size_t column) {
+ TableCell *cell;
+
+ cell = table_get_cell(t, row, column);
+ if (!cell)
+ return NULL;
+
+ return table_get(t, cell);
+}
+
+static int table_data_to_json(TableData *d, JsonVariant **ret) {
+
+ switch (d->type) {
+
+ case TABLE_EMPTY:
+ return json_variant_new_null(ret);
+
+ case TABLE_STRING:
+ return json_variant_new_string(ret, d->string);
+
+ case TABLE_BOOLEAN:
+ return json_variant_new_boolean(ret, d->boolean);
+
+ case TABLE_TIMESTAMP:
+ if (d->timestamp == USEC_INFINITY)
+ return json_variant_new_null(ret);
+
+ return json_variant_new_unsigned(ret, d->timestamp);
+
+ case TABLE_TIMESPAN:
+ if (d->timespan == USEC_INFINITY)
+ return json_variant_new_null(ret);
+
+ return json_variant_new_unsigned(ret, d->timespan);
+
+ case TABLE_SIZE:
+ if (d->size == (size_t) -1)
+ return json_variant_new_null(ret);
+
+ return json_variant_new_unsigned(ret, d->size);
+
+ case TABLE_UINT32:
+ return json_variant_new_unsigned(ret, d->uint32);
+
+ case TABLE_UINT64:
+ return json_variant_new_unsigned(ret, d->uint64);
+
+ case TABLE_PERCENT:
+ return json_variant_new_integer(ret, d->percent);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+int table_to_json(Table *t, JsonVariant **ret) {
+ JsonVariant **rows = NULL, **elements = NULL;
+ _cleanup_free_ size_t *sorted = NULL;
+ size_t n_rows, i, j, display_columns;
+ int r;
+
+ assert(t);
+
+ /* Ensure we have no incomplete rows */
+ assert(t->n_cells % t->n_columns == 0);
+
+ n_rows = t->n_cells / t->n_columns;
+ assert(n_rows > 0); /* at least the header row must be complete */
+
+ if (t->sort_map) {
+ /* If sorting is requested, let's calculate an index table we use to lookup the actual index to display with. */
+
+ sorted = new(size_t, n_rows);
+ if (!sorted) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ for (i = 0; i < n_rows; i++)
+ sorted[i] = i * t->n_columns;
+
+ typesafe_qsort_r(sorted, n_rows, table_data_compare, t);
+ }
+
+ if (t->display_map)
+ display_columns = t->n_display_map;
+ else
+ display_columns = t->n_columns;
+ assert(display_columns > 0);
+
+ elements = new0(JsonVariant*, display_columns * 2);
+ if (!elements) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ for (j = 0; j < display_columns; j++) {
+ TableData *d;
+
+ assert_se(d = t->data[t->display_map ? t->display_map[j] : j]);
+
+ r = table_data_to_json(d, elements + j*2);
+ if (r < 0)
+ goto finish;
+ }
+
+ rows = new0(JsonVariant*, n_rows-1);
+ if (!rows) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ for (i = 1; i < n_rows; i++) {
+ TableData **row;
+
+ if (sorted)
+ row = t->data + sorted[i];
+ else
+ row = t->data + i * t->n_columns;
+
+ for (j = 0; j < display_columns; j++) {
+ TableData *d;
+ size_t k;
+
+ assert_se(d = row[t->display_map ? t->display_map[j] : j]);
+
+ k = j*2+1;
+ elements[k] = json_variant_unref(elements[k]);
+
+ r = table_data_to_json(d, elements + k);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = json_variant_new_object(rows + i - 1, elements, display_columns * 2);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = json_variant_new_array(ret, rows, n_rows - 1);
+
+finish:
+ if (rows) {
+ json_variant_unref_many(rows, n_rows-1);
+ free(rows);
+ }
+
+ if (elements) {
+ json_variant_unref_many(elements, display_columns*2);
+ free(elements);
+ }
+
+ return r;
+}
+
+int table_print_json(Table *t, FILE *f, JsonFormatFlags flags) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ int r;
+
+ assert(t);
+
+ if (!f)
+ f = stdout;
+
+ r = table_to_json(t, &v);
+ if (r < 0)
+ return r;
+
+ json_variant_dump(v, flags, f, NULL);
+
+ return fflush_and_check(f);
+}
diff --git a/src/basic/format-table.h b/src/shared/format-table.h
index 6dc2d16052..5ff247953b 100644
--- a/src/basic/format-table.h
+++ b/src/shared/format-table.h
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <sys/types.h>
+#include "json.h"
#include "macro.h"
typedef enum TableDataType {
@@ -15,6 +16,8 @@ typedef enum TableDataType {
TABLE_TIMESPAN,
TABLE_SIZE,
TABLE_UINT32,
+ TABLE_UINT64,
+ TABLE_PERCENT,
_TABLE_DATA_TYPE_MAX,
_TABLE_DATA_TYPE_INVALID = -1,
} TableDataType;
@@ -42,6 +45,10 @@ int table_set_weight(Table *t, TableCell *cell, unsigned weight);
int table_set_align_percent(Table *t, TableCell *cell, unsigned percent);
int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent);
int table_set_color(Table *t, TableCell *cell, const char *color);
+int table_set_url(Table *t, TableCell *cell, const char *color);
+int table_set_uppercase(Table *t, TableCell *cell, bool b);
+
+int table_update(Table *t, TableCell *cell, TableDataType type, const void *data);
int table_add_many_internal(Table *t, TableDataType first_type, ...);
#define table_add_many(t, ...) table_add_many_internal(t, __VA_ARGS__, _TABLE_DATA_TYPE_MAX)
@@ -50,6 +57,7 @@ void table_set_header(Table *table, bool b);
void table_set_width(Table *t, size_t width);
int table_set_display(Table *t, size_t first_column, ...);
int table_set_sort(Table *t, size_t first_column, ...);
+int table_set_reverse(Table *t, size_t column, bool b);
int table_print(Table *t, FILE *f);
int table_format(Table *t, char **ret);
@@ -60,3 +68,11 @@ static inline TableCell* TABLE_HEADER_CELL(size_t i) {
size_t table_get_rows(Table *t);
size_t table_get_columns(Table *t);
+
+TableCell *table_get_cell(Table *t, size_t row, size_t column);
+
+const void *table_get(Table *t, TableCell *cell);
+const void *table_get_at(Table *t, size_t row, size_t column);
+
+int table_to_json(Table *t, JsonVariant **ret);
+int table_print_json(Table *t, FILE *f, unsigned json_flags);
diff --git a/src/basic/generate-socket-protocol-list.sh b/src/shared/generate-ip-protocol-list.sh
index a9b1e0fb57..a9b1e0fb57 100755
--- a/src/basic/generate-socket-protocol-list.sh
+++ b/src/shared/generate-ip-protocol-list.sh
diff --git a/src/shared/generator.c b/src/shared/generator.c
index 8d4fd3267b..0adaaf2c56 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -103,6 +103,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) {
"Documentation=man:systemd-fsck-root.service(8)\n"
"DefaultDependencies=no\n"
"BindsTo=%3$s\n"
+ "Conflicts=shutdown.target\n"
"After=initrd-root-device.target local-fs-pre.target %3$s\n"
"Before=shutdown.target\n"
"\n"
@@ -196,10 +197,9 @@ int generator_write_timeouts(
const char *opts,
char **filtered) {
- /* Allow configuration how long we wait for a device that
- * backs a mount point to show up. This is useful to support
- * endless device timeouts for devices that show up only after
- * user input, like crypto devices. */
+ /* Configure how long we wait for a device that backs a mount point or a
+ * swap partition to show up. This is useful to support endless device timeouts
+ * for devices that show up only after user input, like crypto devices. */
_cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
usec_t u;
@@ -310,10 +310,10 @@ int generator_hook_up_mkswap(
return log_oom();
/* Nothing to work on. */
- if (!is_device_path(node)) {
- log_error("Cannot format something that is not a device node: %s", node);
- return -EINVAL;
- }
+ if (!is_device_path(node))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot format something that is not a device node: %s",
+ node);
r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
if (r < 0)
@@ -344,9 +344,9 @@ int generator_hook_up_mkswap(
"Documentation=man:systemd-mkswap@.service(8)\n"
"DefaultDependencies=no\n"
"BindsTo=%%i.device\n"
+ "Conflicts=shutdown.target\n"
"After=%%i.device\n"
- "Before=%s\n"
- "Before=shutdown.target\n"
+ "Before=shutdown.target %s\n"
"\n"
"[Service]\n"
"Type=oneshot\n"
@@ -380,15 +380,15 @@ int generator_hook_up_mkfs(
return log_oom();
/* Nothing to work on. */
- if (!is_device_path(node)) {
- log_error("Cannot format something that is not a device node: %s", node);
- return -EINVAL;
- }
+ if (!is_device_path(node))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot format something that is not a device node: %s",
+ node);
- if (!type || streq(type, "auto")) {
- log_error("Cannot format partition %s, filesystem type is not specified", node);
- return -EINVAL;
- }
+ if (!type || streq(type, "auto"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot format partition %s, filesystem type is not specified",
+ node);
r = unit_name_from_path_instance("systemd-mkfs", node, ".service", &unit);
if (r < 0)
@@ -419,12 +419,11 @@ int generator_hook_up_mkfs(
"Documentation=man:systemd-mkfs@.service(8)\n"
"DefaultDependencies=no\n"
"BindsTo=%%i.device\n"
+ "Conflicts=shutdown.target\n"
"After=%%i.device\n"
/* fsck might or might not be used, so let's be safe and order
* ourselves before both systemd-fsck@.service and the mount unit. */
- "Before=systemd-fsck@%%i.service\n"
- "Before=%s\n"
- "Before=shutdown.target\n"
+ "Before=shutdown.target systemd-fsck@%%i.service %s\n"
"\n"
"[Service]\n"
"Type=oneshot\n"
@@ -483,9 +482,9 @@ int generator_hook_up_growfs(
"Documentation=man:systemd-growfs@.service(8)\n"
"DefaultDependencies=no\n"
"BindsTo=%%i.mount\n"
+ "Conflicts=shutdown.target\n"
"After=%%i.mount\n"
- "Before=shutdown.target\n"
- "Before=%s\n"
+ "Before=shutdown.target %s\n"
"\n"
"[Service]\n"
"Type=oneshot\n"
@@ -498,3 +497,8 @@ int generator_hook_up_growfs(
return generator_add_symlink(dir, where_unit, "wants", unit);
}
+
+void log_setup_generator(void) {
+ log_set_prohibit_ipc(true);
+ log_setup_service();
+}
diff --git a/src/shared/generator.h b/src/shared/generator.h
index f0a493b21e..5a1c1e32f7 100644
--- a/src/shared/generator.h
+++ b/src/shared/generator.h
@@ -3,6 +3,8 @@
#include <stdio.h>
+#include "main-func.h"
+
int generator_open_unit_file(
const char *dest,
const char *source,
@@ -47,3 +49,19 @@ int generator_hook_up_growfs(
const char *dir,
const char *where,
const char *target);
+
+void log_setup_generator(void);
+
+/* Similar to DEFINE_MAIN_FUNCTION, but initializes logging and assigns positional arguments. */
+#define DEFINE_MAIN_GENERATOR_FUNCTION(impl) \
+ _DEFINE_MAIN_FUNCTION( \
+ ({ \
+ log_setup_generator(); \
+ if (argc > 1 && argc != 4) \
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), \
+ "This program takes zero or three arguments."); \
+ }), \
+ impl(argc > 1 ? argv[1] : "/tmp", \
+ argc > 1 ? argv[2] : "/tmp", \
+ argc > 1 ? argv[3] : "/tmp"), \
+ r < 0 ? EXIT_FAILURE : EXIT_SUCCESS)
diff --git a/src/shared/id128-print.c b/src/shared/id128-print.c
new file mode 100644
index 0000000000..1b20b8f644
--- /dev/null
+++ b/src/shared/id128-print.c
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <stdio.h>
+
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "id128-print.h"
+#include "log.h"
+#include "pretty-print.h"
+#include "terminal-util.h"
+
+int id128_pretty_print(sd_id128_t id, bool pretty) {
+ unsigned i;
+ _cleanup_free_ char *man_link = NULL, *mod_link = NULL;
+ const char *on, *off;
+
+ if (!pretty) {
+ printf(SD_ID128_FORMAT_STR "\n",
+ SD_ID128_FORMAT_VAL(id));
+ return 0;
+ }
+
+ on = ansi_highlight();
+ off = ansi_normal();
+
+ if (terminal_urlify("man:systemd-id128(1)", "systemd-id128(1)", &man_link) < 0)
+ return log_oom();
+
+ if (terminal_urlify("https://docs.python.org/3/library/uuid.html", "uuid", &mod_link) < 0)
+ return log_oom();
+
+ printf("As string:\n"
+ "%s" SD_ID128_FORMAT_STR "%s\n\n"
+ "As UUID:\n"
+ "%s%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%s\n\n"
+ "As %s macro:\n"
+ "%s#define MESSAGE_XYZ SD_ID128_MAKE(",
+ on, SD_ID128_FORMAT_VAL(id), off,
+ on, SD_ID128_FORMAT_VAL(id), off,
+ man_link,
+ on);
+ 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"
+ ">>> %sMESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')%s\n",
+ mod_link,
+ on, SD_ID128_FORMAT_VAL(id), off);
+
+ return 0;
+}
+
+int id128_print_new(bool pretty) {
+ sd_id128_t id;
+ int r;
+
+ r = sd_id128_randomize(&id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate ID: %m");
+
+ return id128_pretty_print(id, pretty);
+}
diff --git a/src/shared/id128-print.h b/src/shared/id128-print.h
new file mode 100644
index 0000000000..5d50de0fc8
--- /dev/null
+++ b/src/shared/id128-print.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include "sd-id128.h"
+
+int id128_pretty_print(sd_id128_t id, bool pretty);
+int id128_print_new(bool pretty);
diff --git a/src/shared/import-util.c b/src/shared/import-util.c
index d53b90796f..bcd6c0c5ea 100644
--- a/src/shared/import-util.c
+++ b/src/shared/import-util.c
@@ -160,7 +160,7 @@ int import_assign_pool_quota_and_warn(const char *path) {
if (r < 0)
return log_error_errno(r, "Failed to set up default quota hierarchy for %s: %m", path);
if (r > 0)
- log_info("Set up default quota hierarchy for %s.", path);
+ log_debug("Set up default quota hierarchy for %s.", path);
return 0;
}
diff --git a/src/shared/initreq.h b/src/shared/initreq.h
index 9b19b5d729..1d7ff81df1 100644
--- a/src/shared/initreq.h
+++ b/src/shared/initreq.h
@@ -9,10 +9,9 @@
* version 2 of the License, or (at your option) any later version.
*
* Version: @(#)initreq.h 1.28 31-Mar-2004 MvS
- *
*/
-#ifndef _INITREQ_H
-#define _INITREQ_H
+
+#pragma once
#include <sys/param.h>
@@ -72,5 +71,3 @@ struct init_request {
char data[368];
} i;
};
-
-#endif
diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c
index 078d5734be..d2143beda3 100644
--- a/src/shared/install-printf.c
+++ b/src/shared/install-printf.c
@@ -14,7 +14,7 @@
#include "unit-name.h"
#include "user-util.h"
-static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_prefix_and_instance(char specifier, const void *data, const void *userdata, char **ret) {
const UnitFileInstallInfo *i = userdata;
_cleanup_free_ char *prefix = NULL;
int r;
@@ -38,7 +38,7 @@ static int specifier_prefix_and_instance(char specifier, void *data, void *userd
return 0;
}
-static int specifier_name(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_name(char specifier, const void *data, const void *userdata, char **ret) {
const UnitFileInstallInfo *i = userdata;
char *ans;
@@ -54,7 +54,7 @@ static int specifier_name(char specifier, void *data, void *userdata, char **ret
return 0;
}
-static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_prefix(char specifier, const void *data, const void *userdata, char **ret) {
const UnitFileInstallInfo *i = userdata;
assert(i);
@@ -62,7 +62,7 @@ static int specifier_prefix(char specifier, void *data, void *userdata, char **r
return unit_name_to_prefix(i->name, ret);
}
-static int specifier_instance(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_instance(char specifier, const void *data, const void *userdata, char **ret) {
const UnitFileInstallInfo *i = userdata;
char *instance;
int r;
@@ -83,7 +83,7 @@ static int specifier_instance(char specifier, void *data, void *userdata, char *
return 0;
}
-static int specifier_last_component(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_last_component(char specifier, const void *data, const void *userdata, char **ret) {
_cleanup_free_ char *prefix = NULL;
char *dash;
int r;
@@ -104,8 +104,7 @@ static int specifier_last_component(char specifier, void *data, void *userdata,
return 0;
}
-int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) {
-
+int install_full_printf(const UnitFileInstallInfo *i, const char *format, char **ret) {
/* This is similar to unit_full_printf() but does not support
* anything path-related.
*
@@ -129,6 +128,8 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret)
{ 'i', specifier_instance, NULL },
{ 'j', specifier_last_component, NULL },
+ { 'g', specifier_group_name, NULL },
+ { 'G', specifier_group_id, NULL },
{ 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
diff --git a/src/shared/install-printf.h b/src/shared/install-printf.h
index 378b3bfd92..fa8ea7ef81 100644
--- a/src/shared/install-printf.h
+++ b/src/shared/install-printf.h
@@ -3,4 +3,4 @@
#include "install.h"
-int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret);
+int install_full_printf(const UnitFileInstallInfo *i, const char *format, char **ret);
diff --git a/src/shared/install.c b/src/shared/install.c
index 77ae812878..3104043af6 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -60,6 +60,7 @@ typedef enum {
typedef struct {
char *pattern;
PresetAction action;
+ char **instances;
} PresetRule;
typedef struct {
@@ -67,7 +68,7 @@ typedef struct {
size_t n_rules;
} Presets;
-static inline bool unit_file_install_info_has_rules(UnitFileInstallInfo *i) {
+static inline bool unit_file_install_info_has_rules(const UnitFileInstallInfo *i) {
assert(i);
return !strv_isempty(i->aliases) ||
@@ -75,7 +76,7 @@ static inline bool unit_file_install_info_has_rules(UnitFileInstallInfo *i) {
!strv_isempty(i->required_by);
}
-static inline bool unit_file_install_info_has_also(UnitFileInstallInfo *i) {
+static inline bool unit_file_install_info_has_also(const UnitFileInstallInfo *i) {
assert(i);
return !strv_isempty(i->also);
@@ -87,8 +88,10 @@ static inline void presets_freep(Presets *p) {
if (!p)
return;
- for (i = 0; i < p->n_rules; i++)
+ for (i = 0; i < p->n_rules; i++) {
free(p->rules[i].pattern);
+ strv_free(p->rules[i].instances);
+ }
free(p->rules);
p->n_rules = 0;
@@ -343,7 +346,7 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang
if (!quiet)
log_info("Created symlink %s %s %s.",
changes[i].path,
- special_glyph(ARROW),
+ special_glyph(SPECIAL_GLYPH_ARROW),
changes[i].source);
break;
case UNIT_FILE_UNLINK:
@@ -474,8 +477,10 @@ static int create_symlink(
if (!dirname)
return -ENOMEM;
- if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path))
+ if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path)) {
+ log_debug("Symlink %s → %s already exists", new_path, dest);
return 1;
+ }
if (!force) {
unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest);
@@ -681,7 +686,7 @@ static int remove_marked_symlinks(
return r;
}
-static bool is_symlink_with_known_name(const UnitFileInstallInfo *i, const char *name) {
+static int is_symlink_with_known_name(const UnitFileInstallInfo *i, const char *name) {
int r;
if (streq(name, i->name))
@@ -708,8 +713,9 @@ static bool is_symlink_with_known_name(const UnitFileInstallInfo *i, const char
static int find_symlinks_fd(
const char *root_dir,
- UnitFileInstallInfo *i,
+ const UnitFileInstallInfo *i,
bool match_aliases,
+ bool ignore_same_name,
int fd,
const char *path,
const char *config_path,
@@ -756,7 +762,7 @@ static int find_symlinks_fd(
}
/* This will close nfd, regardless whether it succeeds or not */
- q = find_symlinks_fd(root_dir, i, match_aliases, nfd,
+ q = find_symlinks_fd(root_dir, i, match_aliases, ignore_same_name, nfd,
p, config_path, same_name_link);
if (q > 0)
return 1;
@@ -765,7 +771,7 @@ static int find_symlinks_fd(
} else if (de->d_type == DT_LNK) {
_cleanup_free_ char *p = NULL, *dest = NULL;
- bool found_path, found_dest, b = false;
+ bool found_path = false, found_dest, b = false;
int q;
/* Acquire symlink name */
@@ -791,23 +797,20 @@ static int find_symlinks_fd(
if (!x)
return -ENOMEM;
- free(dest);
- dest = x;
+ free_and_replace(dest, x);
}
- /* Check if the symlink itself matches what we
- * are looking for */
- if (path_is_absolute(i->name))
- found_path = path_equal(p, i->name);
- else
- found_path = streq(de->d_name, i->name);
+ assert(unit_name_is_valid(i->name, UNIT_NAME_ANY));
+ if (!ignore_same_name)
+ /* Check if the symlink itself matches what we are looking for.
+ *
+ * If ignore_same_name is specified, we are in one of the directories which
+ * have lower priority than the unit file, and even if a file or symlink with
+ * this name was found, we should ignore it. */
+ found_path = streq(de->d_name, i->name);
- /* Check if what the symlink points to
- * matches what we are looking for */
- if (path_is_absolute(i->name))
- found_dest = path_equal(dest, i->name);
- else
- found_dest = streq(basename(dest), i->name);
+ /* Check if what the symlink points to matches what we are looking for */
+ found_dest = streq(basename(dest), i->name);
if (found_path && found_dest) {
_cleanup_free_ char *t = NULL;
@@ -842,8 +845,9 @@ static int find_symlinks_fd(
static int find_symlinks(
const char *root_dir,
- UnitFileInstallInfo *i,
+ const UnitFileInstallInfo *i,
bool match_name,
+ bool ignore_same_name,
const char *config_path,
bool *same_name_link) {
@@ -861,29 +865,34 @@ static int find_symlinks(
}
/* This takes possession of fd and closes it */
- return find_symlinks_fd(root_dir, i, match_name, fd,
+ return find_symlinks_fd(root_dir, i, match_name, ignore_same_name, fd,
config_path, config_path, same_name_link);
}
static int find_symlinks_in_scope(
UnitFileScope scope,
const LookupPaths *paths,
- UnitFileInstallInfo *i,
+ const UnitFileInstallInfo *i,
bool match_name,
UnitFileState *state) {
bool same_name_link_runtime = false, same_name_link_config = false;
bool enabled_in_runtime = false, enabled_at_all = false;
+ bool ignore_same_name = false;
char **p;
int r;
assert(paths);
assert(i);
+ /* As we iterate over the list of search paths in paths->search_path, we may encounter "same name"
+ * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
+ * efectively masked, so we should ignore them. */
+
STRV_FOREACH(p, paths->search_path) {
bool same_name_link = false;
- r = find_symlinks(paths->root_dir, i, match_name, *p, &same_name_link);
+ r = find_symlinks(paths->root_dir, i, match_name, ignore_same_name, *p, &same_name_link);
if (r < 0)
return r;
if (r > 0) {
@@ -920,6 +929,11 @@ static int find_symlinks_in_scope(
same_name_link_runtime = true;
}
}
+
+ /* Check if next iteration will be "below" the unit file (either a regular file
+ * or a symlink), and hence should be ignored */
+ if (!ignore_same_name && path_startswith(i->path, *p))
+ ignore_same_name = true;
}
if (enabled_in_runtime) {
@@ -984,7 +998,7 @@ static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *nam
}
static int install_info_may_process(
- UnitFileInstallInfo *i,
+ const UnitFileInstallInfo *i,
const LookupPaths *paths,
UnitFileChange **changes,
size_t *n_changes) {
@@ -1045,11 +1059,14 @@ static int install_info_add(
if (r < 0)
return r;
- i = new0(UnitFileInstallInfo, 1);
+ i = new(UnitFileInstallInfo, 1);
if (!i)
return -ENOMEM;
- i->type = _UNIT_FILE_TYPE_INVALID;
- i->auxiliary = auxiliary;
+
+ *i = (UnitFileInstallInfo) {
+ .type = _UNIT_FILE_TYPE_INVALID,
+ .auxiliary = auxiliary,
+ };
i->name = strdup(name);
if (!i->name) {
@@ -1229,10 +1246,9 @@ static int unit_file_load(
type = unit_name_to_type(info->name);
if (type < 0)
return -EINVAL;
- if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type)) {
- log_error("Unit type %s cannot be templated.", unit_type_to_string(type));
- return -EINVAL;
- }
+ if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unit type %s cannot be templated.", unit_type_to_string(type));
if (!(flags & SEARCH_LOAD)) {
r = lstat(path, &st);
@@ -1281,7 +1297,7 @@ static int unit_file_load(
if (r < 0)
return r;
- f = fdopen(fd, "re");
+ f = fdopen(fd, "r");
if (!f)
return -errno;
fd = -1;
@@ -1445,10 +1461,10 @@ static int unit_file_search(
}
}
- if (!found_unit) {
- log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template));
- return -ENOENT;
- }
+ if (!found_unit)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Cannot find unit %s%s%s.",
+ info->name, template ? " or " : "", strempty(template));
if (info->type == UNIT_FILE_TYPE_MASKED)
return result;
@@ -1459,7 +1475,7 @@ static int unit_file_search(
STRV_FOREACH(p, paths->search_path) {
char *path;
- path = path_join(NULL, *p, dropin_dir_name);
+ path = path_join(*p, dropin_dir_name);
if (!path)
return -ENOMEM;
@@ -1473,7 +1489,7 @@ static int unit_file_search(
STRV_FOREACH(p, paths->search_path) {
char *path;
- path = path_join(NULL, *p, dropin_template_dir_name);
+ path = path_join(*p, dropin_template_dir_name);
if (!path)
return -ENOMEM;
@@ -1673,6 +1689,25 @@ static int install_info_discover(
return r;
}
+static int install_info_discover_and_check(
+ UnitFileScope scope,
+ InstallContext *c,
+ const LookupPaths *paths,
+ const char *name,
+ SearchFlags flags,
+ UnitFileInstallInfo **ret,
+ UnitFileChange **changes,
+ size_t *n_changes) {
+
+ int r;
+
+ r = install_info_discover(scope, c, paths, name, flags, ret, changes, n_changes);
+ if (r < 0)
+ return r;
+
+ return install_info_may_process(ret ? *ret : NULL, paths, changes, n_changes);
+}
+
static int install_info_symlink_alias(
UnitFileInstallInfo *i,
const LookupPaths *paths,
@@ -1728,12 +1763,16 @@ static int install_info_symlink_wants(
if (strv_isempty(list))
return 0;
- if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE) && i->default_instance) {
+ if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
UnitFileInstallInfo instance = {
.type = _UNIT_FILE_TYPE_INVALID,
};
_cleanup_free_ char *path = NULL;
+ /* If this is a template, and we have no instance, don't do anything */
+ if (!i->default_instance)
+ return 1;
+
r = unit_name_replace_instance(i->name, i->default_instance, &buf);
if (r < 0)
return r;
@@ -1874,10 +1913,10 @@ static int install_context_apply(
if (q < 0)
return q;
- r = install_info_traverse(scope, c, paths, i, flags, NULL);
- if (r < 0) {
+ q = install_info_traverse(scope, c, paths, i, flags, NULL);
+ if (q < 0) {
unit_file_changes_add(changes, n_changes, r, i->name, NULL);
- return r;
+ return q;
}
/* We can attempt to process a masked unit when a different unit
@@ -1911,6 +1950,7 @@ static int install_context_mark_for_removal(
InstallContext *c,
const LookupPaths *paths,
Set **remove_symlinks_to,
+ const char *config_path,
UnitFileChange **changes,
size_t *n_changes) {
@@ -1919,6 +1959,7 @@ static int install_context_mark_for_removal(
assert(c);
assert(paths);
+ assert(config_path);
/* Marks all items for removal */
@@ -2028,7 +2069,7 @@ int unit_file_unmask(
size_t n_todo = 0, n_allocated = 0;
const char *config_path;
char **i;
- bool dry_run = !!(flags & UNIT_FILE_DRY_RUN);
+ bool dry_run;
int r, q;
assert(scope >= 0);
@@ -2038,71 +2079,73 @@ int unit_file_unmask(
if (r < 0)
return r;
+ config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
+
+ dry_run = !!(flags & UNIT_FILE_DRY_RUN);
+
STRV_FOREACH(i, files) {
+ _cleanup_free_ char *path = NULL;
+
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
return -EINVAL;
- FOREACH_STRING(config_path, paths.runtime_config, paths.persistent_config) {
- _cleanup_free_ char *path = NULL;
-
- path = path_make_absolute(*i, config_path);
- if (!path)
- return -ENOMEM;
+ path = path_make_absolute(*i, config_path);
+ if (!path)
+ return -ENOMEM;
- r = null_or_empty_path(path);
- if (r == -ENOENT)
- continue;
- if (r < 0)
- return r;
- if (r == 0)
- continue;
+ r = null_or_empty_path(path);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
- if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
- return -ENOMEM;
+ if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
+ return -ENOMEM;
- todo[n_todo] = strdup(*i);
- if (!todo[n_todo])
- return -ENOMEM;
+ todo[n_todo] = strdup(*i);
+ if (!todo[n_todo])
+ return -ENOMEM;
- n_todo++;
- }
+ n_todo++;
}
strv_uniq(todo);
r = 0;
- FOREACH_STRING(config_path, paths.runtime_config, paths.persistent_config) {
- STRV_FOREACH(i, todo) {
- _cleanup_free_ char *path = NULL;
- const char *rp;
-
- path = path_make_absolute(*i, config_path);
- if (!path)
- return -ENOMEM;
+ STRV_FOREACH(i, todo) {
+ _cleanup_free_ char *path = NULL;
+ const char *rp;
- if (!dry_run && unlink(path) < 0) {
- if (errno != ENOENT) {
- if (r >= 0)
- r = -errno;
- unit_file_changes_add(changes, n_changes, -errno, path, NULL);
- }
+ path = path_make_absolute(*i, config_path);
+ if (!path)
+ return -ENOMEM;
- continue;
+ if (!dry_run && unlink(path) < 0) {
+ if (errno != ENOENT) {
+ if (r >= 0)
+ r = -errno;
+ unit_file_changes_add(changes, n_changes, -errno, path, NULL);
}
- unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
-
- rp = skip_root(&paths, path);
- q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
- if (q < 0)
- return q;
+ continue;
}
- q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
- if (r >= 0)
- r = q;
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+
+ rp = skip_root(&paths, path);
+ q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
+ if (q < 0)
+ return q;
}
+ q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
+ if (r >= 0)
+ r = q;
+
return r;
}
@@ -2396,11 +2439,8 @@ int unit_file_add_dependency(
if (!config_path)
return -ENXIO;
- r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
- &target_info, changes, n_changes);
- if (r < 0)
- return r;
- r = install_info_may_process(target_info, &paths, changes, n_changes);
+ r = install_info_discover_and_check(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &target_info, changes, n_changes);
if (r < 0)
return r;
@@ -2409,11 +2449,8 @@ int unit_file_add_dependency(
STRV_FOREACH(f, files) {
char ***l;
- r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
- &i, changes, n_changes);
- if (r < 0)
- return r;
- r = install_info_may_process(i, &paths, changes, n_changes);
+ r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
if (r < 0)
return r;
@@ -2429,7 +2466,7 @@ int unit_file_add_dependency(
l = &i->required_by;
strv_free(*l);
- *l = strv_new(target_info->name, NULL);
+ *l = strv_new(target_info->name);
if (!*l)
return -ENOMEM;
}
@@ -2464,11 +2501,8 @@ int unit_file_enable(
return -ENXIO;
STRV_FOREACH(f, files) {
- r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
- &i, changes, n_changes);
- if (r < 0)
- return r;
- r = install_info_may_process(i, &paths, changes, n_changes);
+ r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
if (r < 0)
return r;
@@ -2494,7 +2528,6 @@ int unit_file_disable(
_cleanup_(lookup_paths_free) LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
- bool dry_run = !!(flags & UNIT_FILE_DRY_RUN);
const char *config_path;
char **i;
int r;
@@ -2506,6 +2539,10 @@ int unit_file_disable(
if (r < 0)
return r;
+ config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
+
STRV_FOREACH(i, files) {
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
return -EINVAL;
@@ -2515,17 +2552,11 @@ int unit_file_disable(
return r;
}
- r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, changes, n_changes);
+ r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes);
if (r < 0)
return r;
- FOREACH_STRING(config_path, paths.runtime_config, paths.persistent_config) {
- r = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
- if (r < 0)
- return r;
- }
-
- return 0;
+ return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, !!(flags & UNIT_FILE_DRY_RUN), changes, n_changes);
}
int unit_file_reenable(
@@ -2582,10 +2613,7 @@ int unit_file_set_default(
if (r < 0)
return r;
- r = install_info_discover(scope, &c, &paths, name, 0, &i, changes, n_changes);
- if (r < 0)
- return r;
- r = install_info_may_process(i, &paths, changes, n_changes);
+ r = install_info_discover_and_check(scope, &c, &paths, name, 0, &i, changes, n_changes);
if (r < 0)
return r;
@@ -2648,7 +2676,11 @@ int unit_file_lookup_state(
r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
&i, NULL, NULL);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to discover unit %s: %m", name);
+
+ assert(IN_SET(i->type, UNIT_FILE_TYPE_REGULAR, UNIT_FILE_TYPE_MASKED));
+ log_debug("Found unit %s at %s (%s)", name, strna(i->path),
+ i->type == UNIT_FILE_TYPE_REGULAR ? "regular file" : "mask");
/* Shortcut things, if the caller just wants to know if this unit exists. */
if (!ret)
@@ -2755,6 +2787,39 @@ int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *
return 1;
}
+static int split_pattern_into_name_and_instances(const char *pattern, char **out_unit_name, char ***out_instances) {
+ _cleanup_strv_free_ char **instances = NULL;
+ _cleanup_free_ char *unit_name = NULL;
+ int r;
+
+ assert(pattern);
+ assert(out_instances);
+ assert(out_unit_name);
+
+ r = extract_first_word(&pattern, &unit_name, NULL, 0);
+ if (r < 0)
+ return r;
+
+ /* We handle the instances logic when unit name is extracted */
+ if (pattern) {
+ /* We only create instances when a rule of templated unit
+ * is seen. A rule like enable foo@.service a b c will
+ * result in an array of (a, b, c) as instance names */
+ if (!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE))
+ return -EINVAL;
+
+ instances = strv_split(pattern, WHITESPACE);
+ if (!instances)
+ return -ENOMEM;
+
+ *out_instances = TAKE_PTR(instances);
+ }
+
+ *out_unit_name = TAKE_PTR(unit_name);
+
+ return 0;
+}
+
static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) {
_cleanup_(presets_freep) Presets ps = {};
size_t n_allocated = 0;
@@ -2798,7 +2863,6 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
STRV_FOREACH(p, files) {
_cleanup_fclose_ FILE *f;
- char line[LINE_MAX];
int n = 0;
f = fopen(*p, "re");
@@ -2809,11 +2873,18 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
return -errno;
}
- FOREACH_LINE(line, f, return -errno) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
PresetRule rule = {};
const char *parameter;
char *l;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
l = strstrip(line);
n++;
@@ -2824,15 +2895,20 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
parameter = first_word(l, "enable");
if (parameter) {
- char *pattern;
+ char *unit_name;
+ char **instances = NULL;
- pattern = strdup(parameter);
- if (!pattern)
- return -ENOMEM;
+ /* Unit_name will remain the same as parameter when no instances are specified */
+ r = split_pattern_into_name_and_instances(parameter, &unit_name, &instances);
+ if (r < 0) {
+ log_syntax(NULL, LOG_WARNING, *p, n, r, "Couldn't parse line '%s'. Ignoring.", line);
+ continue;
+ }
rule = (PresetRule) {
- .pattern = pattern,
+ .pattern = unit_name,
.action = PRESET_ENABLE,
+ .instances = instances,
};
}
@@ -2868,15 +2944,71 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
return 0;
}
-static int query_presets(const char *name, const Presets presets) {
+static int pattern_match_multiple_instances(
+ const PresetRule rule,
+ const char *unit_name,
+ char ***ret) {
+
+ _cleanup_free_ char *templated_name = NULL;
+ int r;
+
+ /* If no ret is needed or the rule itself does not have instances
+ * initalized, we return not matching */
+ if (!ret || !rule.instances)
+ return 0;
+
+ r = unit_name_template(unit_name, &templated_name);
+ if (r < 0)
+ return r;
+ if (!streq(rule.pattern, templated_name))
+ return 0;
+
+ /* Compose a list of specified instances when unit name is a template */
+ if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
+ _cleanup_free_ char *prefix = NULL;
+ _cleanup_strv_free_ char **out_strv = NULL;
+ char **iter;
+
+ r = unit_name_to_prefix(unit_name, &prefix);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(iter, rule.instances) {
+ _cleanup_free_ char *name = NULL;
+ r = unit_name_build(prefix, *iter, ".service", &name);
+ if (r < 0)
+ return r;
+ r = strv_extend(&out_strv, name);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(out_strv);
+ return 1;
+ } else {
+ /* We now know the input unit name is an instance name */
+ _cleanup_free_ char *instance_name = NULL;
+
+ r = unit_name_to_instance(unit_name, &instance_name);
+ if (r < 0)
+ return r;
+
+ if (strv_find(rule.instances, instance_name))
+ return 1;
+ }
+ return 0;
+}
+
+static int query_presets(const char *name, const Presets presets, char ***instance_name_list) {
PresetAction action = PRESET_UNKNOWN;
size_t i;
-
+ char **s;
if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
for (i = 0; i < presets.n_rules; i++)
- if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
+ if (pattern_match_multiple_instances(presets.rules[i], name, instance_name_list) > 0 ||
+ fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
action = presets.rules[i].action;
break;
}
@@ -2886,7 +3018,11 @@ static int query_presets(const char *name, const Presets presets) {
log_debug("Preset files don't specify rule for %s. Enabling.", name);
return 1;
case PRESET_ENABLE:
- log_debug("Preset files say enable %s.", name);
+ if (instance_name_list && *instance_name_list)
+ STRV_FOREACH(s, *instance_name_list)
+ log_debug("Preset files say enable %s.", *s);
+ else
+ log_debug("Preset files say enable %s.", name);
return 1;
case PRESET_DISABLE:
log_debug("Preset files say disable %s.", name);
@@ -2904,50 +3040,50 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
if (r < 0)
return r;
- return query_presets(name, presets);
+ return query_presets(name, presets, NULL);
}
static int execute_preset(
UnitFileScope scope,
- UnitFileFlags flags,
InstallContext *plus,
InstallContext *minus,
const LookupPaths *paths,
+ const char *config_path,
char **files,
UnitFilePresetMode mode,
+ bool force,
UnitFileChange **changes,
size_t *n_changes) {
- const char *config_path;
- bool force = !!(flags & UNIT_FILE_FORCE);
- bool runtime = !!(flags & UNIT_FILE_RUNTIME);
- int r = 0, q;
+ int r;
assert(plus);
assert(minus);
assert(paths);
+ assert(config_path);
if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
- q = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, changes, n_changes);
- if (q < 0)
- return q;
+ r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes);
+ if (r < 0)
+ return r;
- FOREACH_STRING(config_path, paths->runtime_config, paths->persistent_config) {
- q = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
- if (r == 0)
- r = q;
- }
- }
+ r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
+ } else
+ r = 0;
if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
+ int q;
+
/* Returns number of symlinks that where supposed to be installed. */
- q = install_context_apply(scope, plus, paths,
- runtime ? paths->runtime_config : paths->persistent_config,
- force, SEARCH_LOAD, changes, n_changes);
- if (r == 0)
- r = q;
+ q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes);
+ if (r >= 0) {
+ if (q < 0)
+ r = q;
+ else
+ r += q;
+ }
}
return r;
@@ -2964,6 +3100,7 @@ static int preset_prepare_one(
size_t *n_changes) {
_cleanup_(install_context_done) InstallContext tmp = {};
+ _cleanup_strv_free_ char **instance_name_list = NULL;
UnitFileInstallInfo *i;
int r;
@@ -2979,19 +3116,26 @@ static int preset_prepare_one(
return 0;
}
- r = query_presets(name, presets);
+ r = query_presets(name, presets, &instance_name_list);
if (r < 0)
return r;
if (r > 0) {
- r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
- &i, changes, n_changes);
- if (r < 0)
- return r;
+ if (instance_name_list) {
+ char **s;
+ STRV_FOREACH(s, instance_name_list) {
+ r = install_info_discover_and_check(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ r = install_info_discover_and_check(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
+ if (r < 0)
+ return r;
+ }
- r = install_info_may_process(i, paths, changes, n_changes);
- if (r < 0)
- return r;
} else
r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
&i, changes, n_changes);
@@ -3011,6 +3155,7 @@ int unit_file_preset(
_cleanup_(install_context_done) InstallContext plus = {}, minus = {};
_cleanup_(lookup_paths_free) LookupPaths paths = {};
_cleanup_(presets_freep) Presets presets = {};
+ const char *config_path;
char **i;
int r;
@@ -3022,6 +3167,10 @@ int unit_file_preset(
if (r < 0)
return r;
+ config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
+
r = read_presets(scope, root_dir, &presets);
if (r < 0)
return r;
@@ -3032,7 +3181,7 @@ int unit_file_preset(
return r;
}
- return execute_preset(scope, flags, &plus, &minus, &paths, files, mode, changes, n_changes);
+ return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
}
int unit_file_preset_all(
@@ -3046,6 +3195,7 @@ int unit_file_preset_all(
_cleanup_(install_context_done) InstallContext plus = {}, minus = {};
_cleanup_(lookup_paths_free) LookupPaths paths = {};
_cleanup_(presets_freep) Presets presets = {};
+ const char *config_path = NULL;
char **i;
int r;
@@ -3057,6 +3207,10 @@ int unit_file_preset_all(
if (r < 0)
return r;
+ config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
+ if (!config_path)
+ return -ENXIO;
+
r = read_presets(scope, root_dir, &presets);
if (r < 0)
return r;
@@ -3074,6 +3228,7 @@ int unit_file_preset_all(
}
FOREACH_DIRENT(de, d, return -errno) {
+
if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
@@ -3097,7 +3252,7 @@ int unit_file_preset_all(
}
}
- return execute_preset(scope, flags, &plus, &minus, &paths, NULL, mode, changes, n_changes);
+ return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
}
static void unit_file_list_free_one(UnitFileList *f) {
diff --git a/src/shared/ip-protocol-list.c b/src/shared/ip-protocol-list.c
new file mode 100644
index 0000000000..aa675ea10b
--- /dev/null
+++ b/src/shared/ip-protocol-list.c
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <netinet/in.h>
+
+#include "alloc-util.h"
+#include "ip-protocol-list.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+static const struct ip_protocol_name* lookup_ip_protocol(register const char *str, register GPERF_LEN_TYPE len);
+
+#include "ip-protocol-from-name.h"
+#include "ip-protocol-to-name.h"
+
+const char *ip_protocol_to_name(int id) {
+
+ if (id < 0)
+ return NULL;
+
+ if ((size_t) id >= ELEMENTSOF(ip_protocol_names))
+ return NULL;
+
+ return ip_protocol_names[id];
+}
+
+int ip_protocol_from_name(const char *name) {
+ const struct ip_protocol_name *sc;
+
+ assert(name);
+
+ sc = lookup_ip_protocol(name, strlen(name));
+ if (!sc)
+ return -EINVAL;
+
+ return sc->id;
+}
+
+int parse_ip_protocol(const char *s) {
+ _cleanup_free_ char *str = NULL;
+ int i, r;
+
+ assert(s);
+
+ if (isempty(s))
+ return IPPROTO_IP;
+
+ /* Do not use strdupa() here, as the input string may come from *
+ * command line or config files. */
+ str = strdup(s);
+ if (!str)
+ return -ENOMEM;
+
+ i = ip_protocol_from_name(ascii_strlower(str));
+ if (i >= 0)
+ return i;
+
+ r = safe_atoi(str, &i);
+ if (r < 0)
+ return r;
+
+ if (!ip_protocol_to_name(i))
+ return -EINVAL;
+
+ return i;
+}
diff --git a/src/shared/ip-protocol-list.h b/src/shared/ip-protocol-list.h
new file mode 100644
index 0000000000..5c94969695
--- /dev/null
+++ b/src/shared/ip-protocol-list.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+const char *ip_protocol_to_name(int id);
+int ip_protocol_from_name(const char *name);
+int parse_ip_protocol(const char *s);
diff --git a/src/basic/socket-protocol-to-name.awk b/src/shared/ip-protocol-to-name.awk
index 4848a7631a..824f811f5c 100644
--- a/src/basic/socket-protocol-to-name.awk
+++ b/src/shared/ip-protocol-to-name.awk
@@ -1,5 +1,5 @@
BEGIN{
- print "static const char* const socket_protocol_names[] = { "
+ print "static const char* const ip_protocol_names[] = { "
}
!/HOPOPTS/ {
printf " [IPPROTO_%s] = \"%s\",\n", $1, tolower($1)
diff --git a/src/basic/journal-importer.c b/src/shared/journal-importer.c
index ca203bbbfc..b0e619205d 100644
--- a/src/basic/journal-importer.c
+++ b/src/shared/journal-importer.c
@@ -96,10 +96,10 @@ static int get_line(JournalImporter *imp, char **line, size_t *size) {
}
imp->scanned = imp->filled;
- if (imp->scanned >= DATA_SIZE_MAX) {
- log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX);
- return -E2BIG;
- }
+ if (imp->scanned >= DATA_SIZE_MAX)
+ return log_error_errno(SYNTHETIC_ERRNO(E2BIG),
+ "Entry is bigger than %u bytes.",
+ DATA_SIZE_MAX);
if (imp->passive_fd)
/* we have to wait for some data to come to us */
@@ -192,11 +192,10 @@ static int get_data_size(JournalImporter *imp) {
return r;
imp->data_size = unaligned_read_le64(data);
- if (imp->data_size > DATA_SIZE_MAX) {
- log_error("Stream declares field with size %zu > DATA_SIZE_MAX = %u",
- imp->data_size, DATA_SIZE_MAX);
- return -EINVAL;
- }
+ if (imp->data_size > DATA_SIZE_MAX)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Stream declares field with size %zu > DATA_SIZE_MAX = %u",
+ imp->data_size, DATA_SIZE_MAX);
if (imp->data_size == 0)
log_warning("Binary field with zero length");
@@ -234,8 +233,8 @@ static int get_data_newline(JournalImporter *imp) {
int l;
l = cescape_char(*data, buf);
- log_error("Expected newline, got '%.*s'", l, buf);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Expected newline, got '%.*s'", l, buf);
}
return 1;
@@ -446,12 +445,12 @@ int journal_importer_push_data(JournalImporter *imp, const char *data, size_t si
assert(imp);
assert(imp->state != IMPORTER_STATE_EOF);
- if (!realloc_buffer(imp, imp->filled + size)) {
- log_error("Failed to store received data of size %zu "
- "(in addition to existing %zu bytes with %zu filled): %s",
- size, imp->size, imp->filled, strerror(ENOMEM));
- return -ENOMEM;
- }
+ if (!realloc_buffer(imp, imp->filled + size))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOMEM),
+ "Failed to store received data of size %zu "
+ "(in addition to existing %zu bytes with %zu filled): %s",
+ size, imp->size, imp->filled,
+ strerror(ENOMEM));
memcpy(imp->buf + imp->filled, data, size);
imp->filled += size;
diff --git a/src/basic/journal-importer.h b/src/shared/journal-importer.h
index f49ce734a1..53354b7c78 100644
--- a/src/basic/journal-importer.h
+++ b/src/shared/journal-importer.h
@@ -11,9 +11,14 @@
#include "time-util.h"
/* Make sure not to make this smaller than the maximum coredump size.
- * See COREDUMP_MAX in coredump.c */
+ * See JOURNAL_SIZE_MAX in coredump.c */
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#define ENTRY_SIZE_MAX (1024*1024*770u)
#define DATA_SIZE_MAX (1024*1024*768u)
+#else
+#define ENTRY_SIZE_MAX (1024*1024*13u)
+#define DATA_SIZE_MAX (1024*1024*11u)
+#endif
#define LINE_CHUNK 8*1024u
struct iovec_wrapper {
diff --git a/src/shared/journal-util.h b/src/shared/journal-util.h
index 404fa53b30..da86434a67 100644
--- a/src/shared/journal-util.h
+++ b/src/shared/journal-util.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
#include <stdbool.h>
#include <sys/types.h>
diff --git a/src/shared/json-internal.h b/src/shared/json-internal.h
new file mode 100644
index 0000000000..bf158bff0d
--- /dev/null
+++ b/src/shared/json-internal.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#pragma once
+
+#include "json.h"
+
+/* This header should include all prototypes only the JSON parser itself and
+ * its tests need access to. Normal code consuming the JSON parser should not
+ * interface with this. */
+
+typedef union JsonValue {
+ /* Encodes a simple value. On x86-64 this structure is 16 bytes wide (as long double is 128bit). */
+ bool boolean;
+ long double real;
+ intmax_t integer;
+ uintmax_t unsig;
+} JsonValue;
+
+/* Let's protect us against accidental structure size changes on our most relevant arch */
+#ifdef __x86_64__
+assert_cc(sizeof(JsonValue) == 16U);
+#endif
+
+#define JSON_VALUE_NULL ((JsonValue) {})
+
+/* 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)
+
+/* 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);
+
+enum { /* JSON tokens */
+ JSON_TOKEN_END,
+ JSON_TOKEN_COLON,
+ JSON_TOKEN_COMMA,
+ JSON_TOKEN_OBJECT_OPEN,
+ JSON_TOKEN_OBJECT_CLOSE,
+ JSON_TOKEN_ARRAY_OPEN,
+ JSON_TOKEN_ARRAY_CLOSE,
+ JSON_TOKEN_STRING,
+ JSON_TOKEN_REAL,
+ JSON_TOKEN_INTEGER,
+ JSON_TOKEN_UNSIGNED,
+ JSON_TOKEN_BOOLEAN,
+ JSON_TOKEN_NULL,
+ _JSON_TOKEN_MAX,
+ _JSON_TOKEN_INVALID = -1,
+};
+
+int json_tokenize(const char **p, char **ret_string, JsonValue *ret_value, unsigned *ret_line, unsigned *ret_column, void **state, unsigned *line, unsigned *column);
diff --git a/src/shared/json.c b/src/shared/json.c
new file mode 100644
index 0000000000..59c4617592
--- /dev/null
+++ b/src/shared/json.c
@@ -0,0 +1,3481 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "sd-messages.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "float.h"
+#include "hexdecoct.h"
+#include "json-internal.h"
+#include "json.h"
+#include "macro.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "utf8.h"
+
+/* Refuse putting together variants with a larger depth than 4K 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)
+assert_cc(DEPTH_MAX <= UINT16_MAX);
+
+typedef struct JsonSource {
+ /* When we parse from a file or similar, encodes the filename, to indicate the source of a json variant */
+ size_t n_ref;
+ unsigned max_line;
+ unsigned max_column;
+ char name[];
+} JsonSource;
+
+/* On x86-64 this whole structure should have a size of 6 * 64 bit = 48 bytes */
+struct JsonVariant {
+ union {
+ /* We either maintain a reference counter for this variant itself, or we are embedded into an
+ * array/object, in which case only that surrounding object is ref-counted. (If 'embedded' is false,
+ * see below.) */
+ size_t n_ref;
+
+ /* If this JsonVariant is part of an array/object, then this field points to the surrounding
+ * JSON_VARIANT_ARRAY/JSON_VARIANT_OBJECT object. (If 'embedded' is true, see below.) */
+ JsonVariant *parent;
+ };
+
+ /* If this was parsed from some file or buffer, this stores where from, as well as the source line/column */
+ JsonSource *source;
+ unsigned line, column;
+
+ JsonVariantType type:5;
+
+ /* A marker whether this variant is embedded into in array/object or not. If true, the 'parent' pointer above
+ * is valid. If false, the 'n_ref' field above is valid instead. */
+ bool is_embedded:1;
+
+ /* In some conditions (for example, if this object is part of an array of strings or objects), we don't store
+ * any data inline, but instead simply reference an external object and act as surrogate of it. In that case
+ * this bool is set, and the external object is referenced through the .reference field below. */
+ bool is_reference:1;
+
+ /* While comparing two arrays, we use this for marking what we already have seen */
+ bool is_marked:1;
+
+ /* The current 'depth' of the JsonVariant, i.e. how many levels of member variants this has */
+ uint16_t depth;
+
+ union {
+ /* For simple types we store the value in-line. */
+ JsonValue value;
+
+ /* For objects and arrays we store the number of elements immediately following */
+ size_t n_elements;
+
+ /* If is_reference as indicated above is set, this is where the reference object is actually stored. */
+ JsonVariant *reference;
+
+ /* Strings are placed immediately after the structure. Note that when this is a JsonVariant embedded
+ * into an array we might encode strings up to INLINE_STRING_LENGTH characters directly inside the
+ * element, while longer strings are stored as references. When this object is not embedded into an
+ * array, but stand-alone we allocate the right size for the whole structure, i.e. the array might be
+ * much larger than INLINE_STRING_LENGTH.
+ *
+ * Note that because we want to allocate arrays of the JsonVariant structure we specify [0] here,
+ * rather than the prettier []. If we wouldn't, then this char array would have undefined size, and so
+ * would the union and then the struct this is included in. And of structures with undefined size we
+ * can't allocate arrays (at least not easily). */
+ char string[0];
+ };
+};
+
+/* Inside string arrays we have a series of JasonVariant structures one after the other. In this case, strings longer
+ * than INLINE_STRING_MAX are stored as references, and all shorter ones inline. (This means — on x86-64 — strings up
+ * to 15 chars are stored within the array elements, and all others in separate allocations) */
+#define INLINE_STRING_MAX (sizeof(JsonVariant) - offsetof(JsonVariant, string) - 1U)
+
+/* Let's make sure this structure isn't increased in size accidentally. This check is only for our most relevant arch
+ * (x86-64). */
+#ifdef __x86_64__
+assert_cc(sizeof(JsonVariant) == 48U);
+assert_cc(INLINE_STRING_MAX == 15U);
+#endif
+
+static JsonSource* json_source_new(const char *name) {
+ JsonSource *s;
+
+ assert(name);
+
+ s = malloc(offsetof(JsonSource, name) + strlen(name) + 1);
+ if (!s)
+ return NULL;
+
+ *s = (JsonSource) {
+ .n_ref = 1,
+ };
+ strcpy(s->name, name);
+
+ return s;
+}
+
+DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(JsonSource, json_source, mfree);
+
+static bool json_source_equal(JsonSource *a, JsonSource *b) {
+ if (a == b)
+ return true;
+
+ if (!a || !b)
+ return false;
+
+ return streq(a->name, b->name);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JsonSource*, json_source_unref);
+
+/* There are four kind of JsonVariant* pointers:
+ *
+ * 1. NULL
+ * 2. A 'regular' one, i.e. pointing to malloc() memory
+ * 3. A 'magic' one, i.e. one of the special JSON_VARIANT_MAGIC_XYZ values, that encode a few very basic values directly in the pointer.
+ * 4. A 'const string' one, i.e. a pointer to a const string.
+ *
+ * The four kinds of pointers can be discerned like this:
+ *
+ * Detecting #1 is easy, just compare with NULL. Detecting #3 is similarly easy: all magic pointers are below
+ * _JSON_VARIANT_MAGIC_MAX (which is pretty low, within the first memory page, which is special on Linux and other
+ * OSes, as it is a faulting page). In order to discern #2 and #4 we check the lowest bit. If it's off it's #2,
+ * otherwise #4. This makes use of the fact that malloc() will return "maximum aligned" memory, which definitely
+ * means the pointer is even. This means we can use the uneven pointers to reference static strings, as long as we
+ * make sure that all static strings used like this are aligned to 2 (or higher), and that we mask the bit on
+ * access. The JSON_VARIANT_STRING_CONST() macro encodes strings as JsonVariant* pointers, with the bit set. */
+
+static bool json_variant_is_magic(const JsonVariant *v) {
+ if (!v)
+ return false;
+
+ return v < _JSON_VARIANT_MAGIC_MAX;
+}
+
+static bool json_variant_is_const_string(const JsonVariant *v) {
+
+ if (v < _JSON_VARIANT_MAGIC_MAX)
+ return false;
+
+ /* A proper JsonVariant is aligned to whatever malloc() aligns things too, which is definitely not uneven. We
+ * hence use all uneven pointers as indicators for const strings. */
+
+ return (((uintptr_t) v) & 1) != 0;
+}
+
+static bool json_variant_is_regular(const JsonVariant *v) {
+
+ if (v < _JSON_VARIANT_MAGIC_MAX)
+ return false;
+
+ return (((uintptr_t) v) & 1) == 0;
+}
+
+static JsonVariant *json_variant_dereference(JsonVariant *v) {
+
+ /* Recursively dereference variants that are references to other variants */
+
+ if (!v)
+ return NULL;
+
+ if (!json_variant_is_regular(v))
+ return v;
+
+ if (!v->is_reference)
+ return v;
+
+ return json_variant_dereference(v->reference);
+}
+
+static uint16_t json_variant_depth(JsonVariant *v) {
+
+ v = json_variant_dereference(v);
+ if (!v)
+ return 0;
+
+ if (!json_variant_is_regular(v))
+ return 0;
+
+ return v->depth;
+}
+
+static JsonVariant *json_variant_normalize(JsonVariant *v) {
+
+ /* Converts json variants to their normalized form, i.e. fully dereferenced and wherever possible converted to
+ * the "magic" version if there is one */
+
+ if (!v)
+ return NULL;
+
+ v = json_variant_dereference(v);
+
+ switch (json_variant_type(v)) {
+
+ case JSON_VARIANT_BOOLEAN:
+ return json_variant_boolean(v) ? JSON_VARIANT_MAGIC_TRUE : JSON_VARIANT_MAGIC_FALSE;
+
+ case JSON_VARIANT_NULL:
+ return JSON_VARIANT_MAGIC_NULL;
+
+ case JSON_VARIANT_INTEGER:
+ return json_variant_integer(v) == 0 ? JSON_VARIANT_MAGIC_ZERO_INTEGER : v;
+
+ case JSON_VARIANT_UNSIGNED:
+ return json_variant_unsigned(v) == 0 ? JSON_VARIANT_MAGIC_ZERO_UNSIGNED : v;
+
+ case JSON_VARIANT_REAL:
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+ return json_variant_real(v) == 0.0 ? JSON_VARIANT_MAGIC_ZERO_REAL : v;
+#pragma GCC diagnostic pop
+
+ case JSON_VARIANT_STRING:
+ return isempty(json_variant_string(v)) ? JSON_VARIANT_MAGIC_EMPTY_STRING : v;
+
+ case JSON_VARIANT_ARRAY:
+ return json_variant_elements(v) == 0 ? JSON_VARIANT_MAGIC_EMPTY_ARRAY : v;
+
+ case JSON_VARIANT_OBJECT:
+ return json_variant_elements(v) == 0 ? JSON_VARIANT_MAGIC_EMPTY_OBJECT : v;
+
+ default:
+ return v;
+ }
+}
+
+static JsonVariant *json_variant_conservative_normalize(JsonVariant *v) {
+
+ /* Much like json_variant_normalize(), but won't simplify if the variant has a source/line location attached to
+ * it, in order not to lose context */
+
+ if (!v)
+ return NULL;
+
+ if (!json_variant_is_regular(v))
+ return v;
+
+ if (v->source || v->line > 0 || v->column > 0)
+ return v;
+
+ return json_variant_normalize(v);
+}
+
+static int json_variant_new(JsonVariant **ret, JsonVariantType type, size_t space) {
+ JsonVariant *v;
+
+ assert_return(ret, -EINVAL);
+
+ v = malloc0(offsetof(JsonVariant, value) + space);
+ if (!v)
+ return -ENOMEM;
+
+ v->n_ref = 1;
+ v->type = type;
+
+ *ret = v;
+ return 0;
+}
+
+int json_variant_new_integer(JsonVariant **ret, intmax_t i) {
+ JsonVariant *v;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ if (i == 0) {
+ *ret = JSON_VARIANT_MAGIC_ZERO_INTEGER;
+ return 0;
+ }
+
+ r = json_variant_new(&v, JSON_VARIANT_INTEGER, sizeof(i));
+ if (r < 0)
+ return r;
+
+ v->value.integer = i;
+ *ret = v;
+
+ return 0;
+}
+
+int json_variant_new_unsigned(JsonVariant **ret, uintmax_t u) {
+ JsonVariant *v;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ if (u == 0) {
+ *ret = JSON_VARIANT_MAGIC_ZERO_UNSIGNED;
+ return 0;
+ }
+
+ r = json_variant_new(&v, JSON_VARIANT_UNSIGNED, sizeof(u));
+ if (r < 0)
+ return r;
+
+ v->value.unsig = u;
+ *ret = v;
+
+ return 0;
+}
+
+int json_variant_new_real(JsonVariant **ret, long double d) {
+ JsonVariant *v;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+ if (d == 0.0) {
+#pragma GCC diagnostic pop
+ *ret = JSON_VARIANT_MAGIC_ZERO_REAL;
+ return 0;
+ }
+
+ r = json_variant_new(&v, JSON_VARIANT_REAL, sizeof(d));
+ if (r < 0)
+ return r;
+
+ v->value.real = d;
+ *ret = v;
+
+ return 0;
+}
+
+int json_variant_new_boolean(JsonVariant **ret, bool b) {
+ assert_return(ret, -EINVAL);
+
+ if (b)
+ *ret = JSON_VARIANT_MAGIC_TRUE;
+ else
+ *ret = JSON_VARIANT_MAGIC_FALSE;
+
+ return 0;
+}
+
+int json_variant_new_null(JsonVariant **ret) {
+ assert_return(ret, -EINVAL);
+
+ *ret = JSON_VARIANT_MAGIC_NULL;
+ return 0;
+}
+
+int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n) {
+ JsonVariant *v;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ if (!s) {
+ assert_return(n == 0, -EINVAL);
+ return json_variant_new_null(ret);
+ }
+ if (n == 0) {
+ *ret = JSON_VARIANT_MAGIC_EMPTY_STRING;
+ return 0;
+ }
+
+ r = json_variant_new(&v, JSON_VARIANT_STRING, n + 1);
+ if (r < 0)
+ return r;
+
+ memcpy(v->string, s, n);
+ v->string[n] = 0;
+
+ *ret = v;
+ return 0;
+}
+
+static void json_variant_set(JsonVariant *a, JsonVariant *b) {
+ assert(a);
+
+ b = json_variant_dereference(b);
+ if (!b) {
+ a->type = JSON_VARIANT_NULL;
+ return;
+ }
+
+ a->type = json_variant_type(b);
+ switch (a->type) {
+
+ case JSON_VARIANT_INTEGER:
+ a->value.integer = json_variant_integer(b);
+ break;
+
+ case JSON_VARIANT_UNSIGNED:
+ a->value.unsig = json_variant_unsigned(b);
+ break;
+
+ case JSON_VARIANT_REAL:
+ a->value.real = json_variant_real(b);
+ break;
+
+ case JSON_VARIANT_BOOLEAN:
+ a->value.boolean = json_variant_boolean(b);
+ break;
+
+ case JSON_VARIANT_STRING: {
+ const char *s;
+
+ assert_se(s = json_variant_string(b));
+
+ /* Short strings we can store inline */
+ if (strnlen(s, INLINE_STRING_MAX+1) <= INLINE_STRING_MAX) {
+ strcpy(a->string, s);
+ break;
+ }
+
+ /* For longer strings, use a reference… */
+ _fallthrough_;
+ }
+
+ case JSON_VARIANT_ARRAY:
+ case JSON_VARIANT_OBJECT:
+ a->is_reference = true;
+ a->reference = json_variant_ref(json_variant_conservative_normalize(b));
+ break;
+
+ case JSON_VARIANT_NULL:
+ break;
+
+ default:
+ assert_not_reached("Unexpected variant type");
+ }
+}
+
+static void json_variant_copy_source(JsonVariant *v, JsonVariant *from) {
+ assert(v);
+ assert(from);
+
+ if (!json_variant_is_regular(from))
+ return;
+
+ v->line = from->line;
+ v->column = from->column;
+ v->source = json_source_ref(from->source);
+}
+
+int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ assert_return(ret, -EINVAL);
+ if (n == 0) {
+ *ret = JSON_VARIANT_MAGIC_EMPTY_ARRAY;
+ return 0;
+ }
+ assert_return(array, -EINVAL);
+
+ v = new(JsonVariant, n + 1);
+ if (!v)
+ return -ENOMEM;
+
+ *v = (JsonVariant) {
+ .n_ref = 1,
+ .type = JSON_VARIANT_ARRAY,
+ };
+
+ for (v->n_elements = 0; v->n_elements < n; v->n_elements++) {
+ JsonVariant *w = v + 1 + v->n_elements,
+ *c = array[v->n_elements];
+ uint16_t d;
+
+ d = json_variant_depth(c);
+ if (d >= DEPTH_MAX) /* Refuse too deep nesting */
+ return -ELNRNG;
+ if (d >= v->depth)
+ v->depth = d + 1;
+
+ *w = (JsonVariant) {
+ .is_embedded = true,
+ .parent = v,
+ };
+
+ json_variant_set(w, c);
+ json_variant_copy_source(w, c);
+ }
+
+ *ret = TAKE_PTR(v);
+ return 0;
+}
+
+int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n) {
+ JsonVariant *v;
+ size_t i;
+
+ assert_return(ret, -EINVAL);
+ if (n == 0) {
+ *ret = JSON_VARIANT_MAGIC_EMPTY_ARRAY;
+ return 0;
+ }
+ assert_return(p, -EINVAL);
+
+ v = new(JsonVariant, n + 1);
+ if (!v)
+ return -ENOMEM;
+
+ *v = (JsonVariant) {
+ .n_ref = 1,
+ .type = JSON_VARIANT_ARRAY,
+ .n_elements = n,
+ .depth = 1,
+ };
+
+ for (i = 0; i < n; i++) {
+ JsonVariant *w = v + 1 + i;
+
+ *w = (JsonVariant) {
+ .is_embedded = true,
+ .parent = v,
+ .type = JSON_VARIANT_UNSIGNED,
+ .value.unsig = ((const uint8_t*) p)[i],
+ };
+ }
+
+ *ret = v;
+ return 0;
+}
+
+int json_variant_new_array_strv(JsonVariant **ret, char **l) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ size_t n;
+ int r;
+
+ assert(ret);
+
+ n = strv_length(l);
+ if (n == 0) {
+ *ret = JSON_VARIANT_MAGIC_EMPTY_ARRAY;
+ return 0;
+ }
+
+ v = new(JsonVariant, n + 1);
+ if (!v)
+ return -ENOMEM;
+
+ *v = (JsonVariant) {
+ .n_ref = 1,
+ .type = JSON_VARIANT_ARRAY,
+ .depth = 1,
+ };
+
+ for (v->n_elements = 0; v->n_elements < n; v->n_elements++) {
+ JsonVariant *w = v + 1 + v->n_elements;
+ size_t k;
+
+ *w = (JsonVariant) {
+ .is_embedded = true,
+ .parent = v,
+ .type = JSON_VARIANT_STRING,
+ };
+
+ k = strlen(l[v->n_elements]);
+
+ if (k > INLINE_STRING_MAX) {
+ /* If string is too long, store it as reference. */
+
+ r = json_variant_new_stringn(&w->reference, l[v->n_elements], k);
+ if (r < 0)
+ return r;
+
+ w->is_reference = true;
+ } else
+ memcpy(w->string, l[v->n_elements], k+1);
+ }
+
+ *ret = TAKE_PTR(v);
+ return 0;
+}
+
+int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ assert_return(ret, -EINVAL);
+ if (n == 0) {
+ *ret = JSON_VARIANT_MAGIC_EMPTY_OBJECT;
+ return 0;
+ }
+ assert_return(array, -EINVAL);
+ assert_return(n % 2 == 0, -EINVAL);
+
+ v = new(JsonVariant, n + 1);
+ if (!v)
+ return -ENOMEM;
+
+ *v = (JsonVariant) {
+ .n_ref = 1,
+ .type = JSON_VARIANT_OBJECT,
+ };
+
+ for (v->n_elements = 0; v->n_elements < n; v->n_elements++) {
+ JsonVariant *w = v + 1 + v->n_elements,
+ *c = array[v->n_elements];
+ uint16_t d;
+
+ if ((v->n_elements & 1) == 0 &&
+ !json_variant_is_string(c))
+ return -EINVAL; /* Every second one needs to be a string, as it is the key name */
+
+ d = json_variant_depth(c);
+ if (d >= DEPTH_MAX) /* Refuse too deep nesting */
+ return -ELNRNG;
+ if (d >= v->depth)
+ v->depth = d + 1;
+
+ *w = (JsonVariant) {
+ .is_embedded = true,
+ .parent = v,
+ };
+
+ json_variant_set(w, c);
+ json_variant_copy_source(w, c);
+ }
+
+ *ret = TAKE_PTR(v);
+ return 0;
+}
+
+static void json_variant_free_inner(JsonVariant *v) {
+ assert(v);
+
+ if (!json_variant_is_regular(v))
+ return;
+
+ json_source_unref(v->source);
+
+ if (v->is_reference) {
+ json_variant_unref(v->reference);
+ return;
+ }
+
+ if (IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT)) {
+ size_t i;
+
+ for (i = 0; i < v->n_elements; i++)
+ json_variant_free_inner(v + 1 + i);
+ }
+}
+
+JsonVariant *json_variant_ref(JsonVariant *v) {
+ if (!v)
+ return NULL;
+ if (!json_variant_is_regular(v))
+ return v;
+
+ if (v->is_embedded)
+ json_variant_ref(v->parent); /* ref the compounding variant instead */
+ else {
+ assert(v->n_ref > 0);
+ v->n_ref++;
+ }
+
+ return v;
+}
+
+JsonVariant *json_variant_unref(JsonVariant *v) {
+ if (!v)
+ return NULL;
+ if (!json_variant_is_regular(v))
+ return NULL;
+
+ if (v->is_embedded)
+ json_variant_unref(v->parent);
+ else {
+ assert(v->n_ref > 0);
+ v->n_ref--;
+
+ if (v->n_ref == 0) {
+ json_variant_free_inner(v);
+ free(v);
+ }
+ }
+
+ return NULL;
+}
+
+void json_variant_unref_many(JsonVariant **array, size_t n) {
+ size_t i;
+
+ assert(array || n == 0);
+
+ for (i = 0; i < n; i++)
+ json_variant_unref(array[i]);
+}
+
+const char *json_variant_string(JsonVariant *v) {
+ if (!v)
+ return NULL;
+ if (v == JSON_VARIANT_MAGIC_EMPTY_STRING)
+ return "";
+ if (json_variant_is_magic(v))
+ goto mismatch;
+ if (json_variant_is_const_string(v)) {
+ uintptr_t p = (uintptr_t) v;
+
+ assert((p & 1) != 0);
+ return (const char*) (p ^ 1U);
+ }
+
+ if (v->is_reference)
+ return json_variant_string(v->reference);
+ if (v->type != JSON_VARIANT_STRING)
+ goto mismatch;
+
+ return v->string;
+
+mismatch:
+ log_debug("Non-string JSON variant requested as string, returning NULL.");
+ return NULL;
+}
+
+bool json_variant_boolean(JsonVariant *v) {
+ if (!v)
+ goto mismatch;
+ if (v == JSON_VARIANT_MAGIC_TRUE)
+ return true;
+ if (v == JSON_VARIANT_MAGIC_FALSE)
+ return false;
+ if (!json_variant_is_regular(v))
+ goto mismatch;
+ if (v->type != JSON_VARIANT_BOOLEAN)
+ goto mismatch;
+ if (v->is_reference)
+ return json_variant_boolean(v->reference);
+
+ return v->value.boolean;
+
+mismatch:
+ log_debug("Non-boolean JSON variant requested as boolean, returning false.");
+ return false;
+}
+
+intmax_t json_variant_integer(JsonVariant *v) {
+ if (!v)
+ goto mismatch;
+ if (v == JSON_VARIANT_MAGIC_ZERO_INTEGER ||
+ v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED ||
+ v == JSON_VARIANT_MAGIC_ZERO_REAL)
+ return 0;
+ if (!json_variant_is_regular(v))
+ goto mismatch;
+ if (v->is_reference)
+ return json_variant_integer(v->reference);
+
+ switch (v->type) {
+
+ case JSON_VARIANT_INTEGER:
+ return v->value.integer;
+
+ case JSON_VARIANT_UNSIGNED:
+ if (v->value.unsig <= INTMAX_MAX)
+ return (intmax_t) v->value.unsig;
+
+ log_debug("Unsigned integer %ju requested as signed integer and out of range, returning 0.", v->value.unsig);
+ return 0;
+
+ case JSON_VARIANT_REAL: {
+ intmax_t converted;
+
+ converted = (intmax_t) v->value.real;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+ if ((long double) converted == v->value.real)
+#pragma GCC diagnostic pop
+ return converted;
+
+ log_debug("Real %Lg requested as integer, and cannot be converted losslessly, returning 0.", v->value.real);
+ return 0;
+ }
+
+ default:
+ break;
+ }
+
+mismatch:
+ log_debug("Non-integer JSON variant requested as integer, returning 0.");
+ return 0;
+}
+
+uintmax_t json_variant_unsigned(JsonVariant *v) {
+ if (!v)
+ goto mismatch;
+ if (v == JSON_VARIANT_MAGIC_ZERO_INTEGER ||
+ v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED ||
+ v == JSON_VARIANT_MAGIC_ZERO_REAL)
+ return 0;
+ if (!json_variant_is_regular(v))
+ goto mismatch;
+ if (v->is_reference)
+ return json_variant_integer(v->reference);
+
+ switch (v->type) {
+
+ case JSON_VARIANT_INTEGER:
+ if (v->value.integer >= 0)
+ return (uintmax_t) v->value.integer;
+
+ log_debug("Signed integer %ju requested as unsigned integer and out of range, returning 0.", v->value.integer);
+ return 0;
+
+ case JSON_VARIANT_UNSIGNED:
+ return v->value.unsig;
+
+ case JSON_VARIANT_REAL: {
+ uintmax_t converted;
+
+ converted = (uintmax_t) v->value.real;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+ if ((long double) converted == v->value.real)
+#pragma GCC diagnostic pop
+ return converted;
+
+ log_debug("Real %Lg requested as unsigned integer, and cannot be converted losslessly, returning 0.", v->value.real);
+ return 0;
+ }
+
+ default:
+ break;
+ }
+
+mismatch:
+ log_debug("Non-integer JSON variant requested as unsigned, returning 0.");
+ return 0;
+}
+
+long double json_variant_real(JsonVariant *v) {
+ if (!v)
+ return 0.0;
+ if (v == JSON_VARIANT_MAGIC_ZERO_INTEGER ||
+ v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED ||
+ v == JSON_VARIANT_MAGIC_ZERO_REAL)
+ return 0.0;
+ if (!json_variant_is_regular(v))
+ goto mismatch;
+ if (v->is_reference)
+ return json_variant_real(v->reference);
+
+ switch (v->type) {
+
+ case JSON_VARIANT_REAL:
+ return v->value.real;
+
+ case JSON_VARIANT_INTEGER: {
+ long double converted;
+
+ converted = (long double) v->value.integer;
+
+ if ((intmax_t) converted == v->value.integer)
+ return converted;
+
+ log_debug("Signed integer %ji requested as real, and cannot be converted losslessly, returning 0.", v->value.integer);
+ return 0.0;
+ }
+
+ case JSON_VARIANT_UNSIGNED: {
+ long double converted;
+
+ converted = (long double) v->value.unsig;
+
+ if ((uintmax_t) converted == v->value.unsig)
+ return converted;
+
+ log_debug("Unsigned integer %ju requested as real, and cannot be converted losslessly, returning 0.", v->value.unsig);
+ return 0.0;
+ }
+
+ default:
+ break;
+ }
+
+mismatch:
+ log_debug("Non-integer JSON variant requested as integer, returning 0.");
+ return 0.0;
+}
+
+bool json_variant_is_negative(JsonVariant *v) {
+ if (!v)
+ goto mismatch;
+ if (v == JSON_VARIANT_MAGIC_ZERO_INTEGER ||
+ v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED ||
+ v == JSON_VARIANT_MAGIC_ZERO_REAL)
+ return false;
+ if (!json_variant_is_regular(v))
+ goto mismatch;
+ if (v->is_reference)
+ return json_variant_is_negative(v->reference);
+
+ /* This function is useful as checking whether numbers are negative is pretty complex since we have three types
+ * of numbers. And some JSON code (OCI for example) uses negative numbers to mark "not defined" numeric
+ * values. */
+
+ switch (v->type) {
+
+ case JSON_VARIANT_REAL:
+ return v->value.real < 0;
+
+ case JSON_VARIANT_INTEGER:
+ return v->value.integer < 0;
+
+ case JSON_VARIANT_UNSIGNED:
+ return false;
+
+ default:
+ break;
+ }
+
+mismatch:
+ log_debug("Non-integer JSON variant tested for negativity, returning false.");
+ return false;
+}
+
+JsonVariantType json_variant_type(JsonVariant *v) {
+
+ if (!v)
+ return _JSON_VARIANT_TYPE_INVALID;
+
+ if (json_variant_is_const_string(v))
+ return JSON_VARIANT_STRING;
+
+ if (v == JSON_VARIANT_MAGIC_TRUE || v == JSON_VARIANT_MAGIC_FALSE)
+ return JSON_VARIANT_BOOLEAN;
+
+ if (v == JSON_VARIANT_MAGIC_NULL)
+ return JSON_VARIANT_NULL;
+
+ if (v == JSON_VARIANT_MAGIC_ZERO_INTEGER)
+ return JSON_VARIANT_INTEGER;
+
+ if (v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED)
+ return JSON_VARIANT_UNSIGNED;
+
+ if (v == JSON_VARIANT_MAGIC_ZERO_REAL)
+ return JSON_VARIANT_REAL;
+
+ if (v == JSON_VARIANT_MAGIC_EMPTY_STRING)
+ return JSON_VARIANT_STRING;
+
+ if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY)
+ return JSON_VARIANT_ARRAY;
+
+ if (v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
+ return JSON_VARIANT_OBJECT;
+
+ return v->type;
+}
+
+bool json_variant_has_type(JsonVariant *v, JsonVariantType type) {
+ JsonVariantType rt;
+
+ v = json_variant_dereference(v);
+
+ rt = json_variant_type(v);
+ if (rt == type)
+ return true;
+
+ /* If it's a const string, then it only can be a string, and if it is not, it's not */
+ if (json_variant_is_const_string(v))
+ return false;
+
+ /* All three magic zeroes qualify as integer, unsigned and as real */
+ if ((v == JSON_VARIANT_MAGIC_ZERO_INTEGER || v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED || v == JSON_VARIANT_MAGIC_ZERO_REAL) &&
+ IN_SET(type, JSON_VARIANT_INTEGER, JSON_VARIANT_UNSIGNED, JSON_VARIANT_REAL, JSON_VARIANT_NUMBER))
+ return true;
+
+ /* All other magic variant types are only equal to themselves */
+ if (json_variant_is_magic(v))
+ return false;
+
+ /* Handle the "number" pseudo type */
+ if (type == JSON_VARIANT_NUMBER)
+ return IN_SET(rt, JSON_VARIANT_INTEGER, JSON_VARIANT_UNSIGNED, JSON_VARIANT_REAL);
+
+ /* Integer conversions are OK in many cases */
+ if (rt == JSON_VARIANT_INTEGER && type == JSON_VARIANT_UNSIGNED)
+ return v->value.integer >= 0;
+ if (rt == JSON_VARIANT_UNSIGNED && type == JSON_VARIANT_INTEGER)
+ return v->value.unsig <= INTMAX_MAX;
+
+ /* Any integer that can be converted lossley to a real and back may also be considered a real */
+ if (rt == JSON_VARIANT_INTEGER && type == JSON_VARIANT_REAL)
+ return (intmax_t) (long double) v->value.integer == v->value.integer;
+ if (rt == JSON_VARIANT_UNSIGNED && type == JSON_VARIANT_REAL)
+ return (uintmax_t) (long double) v->value.unsig == v->value.unsig;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+ /* Any real that can be converted losslessly to an integer and back may also be considered an integer */
+ if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_INTEGER)
+ return (long double) (intmax_t) v->value.real == v->value.real;
+ if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_UNSIGNED)
+ return (long double) (uintmax_t) v->value.real == v->value.real;
+#pragma GCC diagnostic pop
+
+ return false;
+}
+
+size_t json_variant_elements(JsonVariant *v) {
+ if (!v)
+ return 0;
+ if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY ||
+ v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
+ return 0;
+ if (!json_variant_is_regular(v))
+ goto mismatch;
+ if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT))
+ goto mismatch;
+ if (v->is_reference)
+ return json_variant_elements(v->reference);
+
+ return v->n_elements;
+
+mismatch:
+ log_debug("Number of elements in non-array/non-object JSON variant requested, returning 0.");
+ return 0;
+}
+
+JsonVariant *json_variant_by_index(JsonVariant *v, size_t idx) {
+ if (!v)
+ return NULL;
+ if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY ||
+ v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
+ return NULL;
+ if (!json_variant_is_regular(v))
+ goto mismatch;
+ if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT))
+ goto mismatch;
+ if (v->is_reference)
+ return json_variant_by_index(v->reference, idx);
+ if (idx >= v->n_elements)
+ return NULL;
+
+ return json_variant_conservative_normalize(v + 1 + idx);
+
+mismatch:
+ log_debug("Element in non-array/non-object JSON variant requested by index, returning NULL.");
+ return NULL;
+}
+
+JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVariant **ret_key) {
+ size_t i;
+
+ if (!v)
+ goto not_found;
+ if (!key)
+ goto not_found;
+ if (v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
+ goto not_found;
+ if (!json_variant_is_regular(v))
+ goto mismatch;
+ if (v->type != JSON_VARIANT_OBJECT)
+ goto mismatch;
+ if (v->is_reference)
+ return json_variant_by_key(v->reference, key);
+
+ for (i = 0; i < v->n_elements; i += 2) {
+ JsonVariant *p;
+
+ p = json_variant_dereference(v + 1 + i);
+
+ if (!json_variant_has_type(p, JSON_VARIANT_STRING))
+ continue;
+
+ if (streq(json_variant_string(p), key)) {
+
+ if (ret_key)
+ *ret_key = json_variant_conservative_normalize(v + 1 + i);
+
+ return json_variant_conservative_normalize(v + 1 + i + 1);
+ }
+ }
+
+not_found:
+ if (ret_key)
+ *ret_key = NULL;
+
+ return NULL;
+
+mismatch:
+ log_debug("Element in non-object JSON variant requested by key, returning NULL.");
+ if (ret_key)
+ *ret_key = NULL;
+
+ return NULL;
+}
+
+JsonVariant *json_variant_by_key(JsonVariant *v, const char *key) {
+ return json_variant_by_key_full(v, key, NULL);
+}
+
+bool json_variant_equal(JsonVariant *a, JsonVariant *b) {
+ JsonVariantType t;
+
+ a = json_variant_normalize(a);
+ b = json_variant_normalize(b);
+
+ if (a == b)
+ return true;
+
+ t = json_variant_type(a);
+ if (!json_variant_has_type(b, t))
+ return false;
+
+ switch (t) {
+
+ case JSON_VARIANT_STRING:
+ return streq(json_variant_string(a), json_variant_string(b));
+
+ case JSON_VARIANT_INTEGER:
+ return json_variant_integer(a) == json_variant_integer(b);
+
+ case JSON_VARIANT_UNSIGNED:
+ return json_variant_unsigned(a) == json_variant_unsigned(b);
+
+ case JSON_VARIANT_REAL:
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+ return json_variant_real(a) == json_variant_real(b);
+#pragma GCC diagnostic pop
+
+ case JSON_VARIANT_BOOLEAN:
+ return json_variant_boolean(a) == json_variant_boolean(b);
+
+ case JSON_VARIANT_NULL:
+ return true;
+
+ case JSON_VARIANT_ARRAY: {
+ size_t i, n;
+
+ n = json_variant_elements(a);
+ if (n != json_variant_elements(b))
+ return false;
+
+ for (i = 0; i < n; i++) {
+ if (!json_variant_equal(json_variant_by_index(a, i), json_variant_by_index(b, i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ case JSON_VARIANT_OBJECT: {
+ size_t i, n;
+
+ n = json_variant_elements(a);
+ if (n != json_variant_elements(b))
+ return false;
+
+ /* Iterate through all keys in 'a' */
+ for (i = 0; i < n; i += 2) {
+ bool found = false;
+ size_t j;
+
+ /* Match them against all keys in 'b' */
+ for (j = 0; j < n; j += 2) {
+ JsonVariant *key_b;
+
+ key_b = json_variant_by_index(b, j);
+
+ /* During the first iteration unmark everything */
+ if (i == 0)
+ key_b->is_marked = false;
+ else if (key_b->is_marked) /* In later iterations if we already marked something, don't bother with it again */
+ continue;
+
+ if (found)
+ continue;
+
+ if (json_variant_equal(json_variant_by_index(a, i), key_b) &&
+ json_variant_equal(json_variant_by_index(a, i+1), json_variant_by_index(b, j+1))) {
+ /* Key and values match! */
+ key_b->is_marked = found = true;
+
+ /* In the first iteration we continue the inner loop since we want to mark
+ * everything, otherwise exit the loop quickly after we found what we were
+ * looking for. */
+ if (i != 0)
+ break;
+ }
+ }
+
+ if (!found)
+ return false;
+ }
+
+ return true;
+ }
+
+ default:
+ assert_not_reached("Unknown variant type.");
+ }
+}
+
+int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column) {
+ assert_return(v, -EINVAL);
+
+ if (ret_source)
+ *ret_source = json_variant_is_regular(v) && v->source ? v->source->name : NULL;
+
+ if (ret_line)
+ *ret_line = json_variant_is_regular(v) ? v->line : 0;
+
+ if (ret_column)
+ *ret_column = json_variant_is_regular(v) ? v->column : 0;
+
+ return 0;
+}
+
+static int print_source(FILE *f, JsonVariant *v, JsonFormatFlags flags, bool whitespace) {
+ size_t w, k;
+
+ if (!FLAGS_SET(flags, JSON_FORMAT_SOURCE|JSON_FORMAT_PRETTY))
+ return 0;
+
+ if (!json_variant_is_regular(v))
+ return 0;
+
+ if (!v->source && v->line == 0 && v->column == 0)
+ return 0;
+
+ /* The max width we need to format the line numbers for this source file */
+ w = (v->source && v->source->max_line > 0) ?
+ DECIMAL_STR_WIDTH(v->source->max_line) :
+ DECIMAL_STR_MAX(unsigned)-1;
+ k = (v->source && v->source->max_column > 0) ?
+ DECIMAL_STR_WIDTH(v->source->max_column) :
+ DECIMAL_STR_MAX(unsigned) -1;
+
+ if (whitespace) {
+ size_t i, n;
+
+ n = 1 + (v->source ? strlen(v->source->name) : 0) +
+ ((v->source && (v->line > 0 || v->column > 0)) ? 1 : 0) +
+ (v->line > 0 ? w : 0) +
+ (((v->source || v->line > 0) && v->column > 0) ? 1 : 0) +
+ (v->column > 0 ? k : 0) +
+ 2;
+
+ for (i = 0; i < n; i++)
+ fputc(' ', f);
+ } else {
+ fputc('[', f);
+
+ if (v->source)
+ fputs(v->source->name, f);
+ if (v->source && (v->line > 0 || v->column > 0))
+ fputc(':', f);
+ if (v->line > 0)
+ fprintf(f, "%*u", (int) w, v->line);
+ if ((v->source || v->line > 0) || v->column > 0)
+ fputc(':', f);
+ if (v->column > 0)
+ fprintf(f, "%*u", (int) k, v->column);
+
+ fputc(']', f);
+ fputc(' ', f);
+ }
+
+ return 0;
+}
+
+static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const char *prefix) {
+ int r;
+
+ assert(f);
+ assert(v);
+
+ switch (json_variant_type(v)) {
+
+ case JSON_VARIANT_REAL: {
+ locale_t loc;
+
+ loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
+ if (loc == (locale_t) 0)
+ return -errno;
+
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_HIGHLIGHT_BLUE, f);
+
+ fprintf(f, "%.*Le", DECIMAL_DIG, json_variant_real(v));
+
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_NORMAL, f);
+
+ freelocale(loc);
+ break;
+ }
+
+ case JSON_VARIANT_INTEGER:
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_HIGHLIGHT_BLUE, f);
+
+ fprintf(f, "%" PRIdMAX, json_variant_integer(v));
+
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_NORMAL, f);
+ break;
+
+ case JSON_VARIANT_UNSIGNED:
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_HIGHLIGHT_BLUE, f);
+
+ fprintf(f, "%" PRIuMAX, json_variant_unsigned(v));
+
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_NORMAL, f);
+ break;
+
+ case JSON_VARIANT_BOOLEAN:
+
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_HIGHLIGHT, f);
+
+ if (json_variant_boolean(v))
+ fputs("true", f);
+ else
+ fputs("false", f);
+
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_NORMAL, f);
+
+ break;
+
+ case JSON_VARIANT_NULL:
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_HIGHLIGHT, f);
+
+ fputs("null", f);
+
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_NORMAL, f);
+ break;
+
+ case JSON_VARIANT_STRING: {
+ const char *q;
+
+ fputc('"', f);
+
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_GREEN, f);
+
+ for (q = json_variant_string(v); *q; q++) {
+
+ switch (*q) {
+
+ case '"':
+ fputs("\\\"", f);
+ break;
+
+ case '\\':
+ fputs("\\\\", f);
+ break;
+
+ case '\b':
+ fputs("\\b", f);
+ break;
+
+ case '\f':
+ fputs("\\f", f);
+ break;
+
+ case '\n':
+ fputs("\\n", f);
+ break;
+
+ case '\r':
+ fputs("\\r", f);
+ break;
+
+ case '\t':
+ fputs("\\t", f);
+ break;
+
+ default:
+ if ((signed char) *q >= 0 && *q < ' ')
+ fprintf(f, "\\u%04x", *q);
+ else
+ fputc(*q, f);
+ break;
+ }
+ }
+
+ if (flags & JSON_FORMAT_COLOR)
+ fputs(ANSI_NORMAL, f);
+
+ fputc('"', f);
+ break;
+ }
+
+ case JSON_VARIANT_ARRAY: {
+ size_t i, n;
+
+ n = json_variant_elements(v);
+
+ if (n == 0)
+ fputs("[]", f);
+ else {
+ _cleanup_free_ char *joined = NULL;
+ const char *prefix2;
+
+ if (flags & JSON_FORMAT_PRETTY) {
+ joined = strjoin(strempty(prefix), "\t");
+ if (!joined)
+ return -ENOMEM;
+
+ prefix2 = joined;
+ fputs("[\n", f);
+ } else {
+ prefix2 = strempty(prefix);
+ fputc('[', f);
+ }
+
+ for (i = 0; i < n; i++) {
+ JsonVariant *e;
+
+ assert_se(e = json_variant_by_index(v, i));
+
+ if (i > 0) {
+ if (flags & JSON_FORMAT_PRETTY)
+ fputs(",\n", f);
+ else
+ fputc(',', f);
+ }
+
+ if (flags & JSON_FORMAT_PRETTY) {
+ print_source(f, e, flags, false);
+ fputs(prefix2, f);
+ }
+
+ r = json_format(f, e, flags, prefix2);
+ if (r < 0)
+ return r;
+ }
+
+ if (flags & JSON_FORMAT_PRETTY) {
+ fputc('\n', f);
+ print_source(f, v, flags, true);
+ fputs(strempty(prefix), f);
+ }
+
+ fputc(']', f);
+ }
+ break;
+ }
+
+ case JSON_VARIANT_OBJECT: {
+ size_t i, n;
+
+ n = json_variant_elements(v);
+
+ if (n == 0)
+ fputs("{}", f);
+ else {
+ _cleanup_free_ char *joined = NULL;
+ const char *prefix2;
+
+ if (flags & JSON_FORMAT_PRETTY) {
+ joined = strjoin(strempty(prefix), "\t");
+ if (!joined)
+ return -ENOMEM;
+
+ prefix2 = joined;
+ fputs("{\n", f);
+ } else {
+ prefix2 = strempty(prefix);
+ fputc('{', f);
+ }
+
+ for (i = 0; i < n; i += 2) {
+ JsonVariant *e;
+
+ e = json_variant_by_index(v, i);
+
+ if (i > 0) {
+ if (flags & JSON_FORMAT_PRETTY)
+ fputs(",\n", f);
+ else
+ fputc(',', f);
+ }
+
+ if (flags & JSON_FORMAT_PRETTY) {
+ print_source(f, e, flags, false);
+ fputs(prefix2, f);
+ }
+
+ r = json_format(f, e, flags, prefix2);
+ if (r < 0)
+ return r;
+
+ fputs(flags & JSON_FORMAT_PRETTY ? " : " : ":", f);
+
+ r = json_format(f, json_variant_by_index(v, i+1), flags, prefix2);
+ if (r < 0)
+ return r;
+ }
+
+ if (flags & JSON_FORMAT_PRETTY) {
+ fputc('\n', f);
+ print_source(f, v, flags, true);
+ fputs(strempty(prefix), f);
+ }
+
+ fputc('}', f);
+ }
+ break;
+ }
+
+ default:
+ assert_not_reached("Unexpected variant type.");
+ }
+
+ return 0;
+}
+
+int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ size_t sz = 0;
+ int r;
+
+ assert_return(v, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ {
+ _cleanup_fclose_ FILE *f = NULL;
+
+ f = open_memstream(&s, &sz);
+ if (!f)
+ return -ENOMEM;
+
+ (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+
+ json_variant_dump(v, flags, f, NULL);
+
+ r = fflush_and_check(f);
+ }
+ if (r < 0)
+ return r;
+
+ assert(s);
+ *ret = TAKE_PTR(s);
+
+ return (int) sz;
+}
+
+void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix) {
+ if (!v)
+ return;
+
+ if (!f)
+ f = stdout;
+
+ print_source(f, v, flags, false);
+
+ if (((flags & (JSON_FORMAT_COLOR_AUTO|JSON_FORMAT_COLOR)) == JSON_FORMAT_COLOR_AUTO) && colors_enabled())
+ flags |= JSON_FORMAT_COLOR;
+
+ if (flags & JSON_FORMAT_SSE)
+ fputs("data: ", f);
+ if (flags & JSON_FORMAT_SEQ)
+ fputc('\x1e', f); /* ASCII Record Separator */
+
+ json_format(f, v, flags, prefix);
+
+ if (flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_SEQ|JSON_FORMAT_SSE|JSON_FORMAT_NEWLINE))
+ fputc('\n', f);
+ if (flags & JSON_FORMAT_SSE)
+ fputc('\n', f); /* In case of SSE add a second newline */
+}
+
+static int json_variant_copy(JsonVariant **nv, JsonVariant *v) {
+ JsonVariantType t;
+ JsonVariant *c;
+ JsonValue value;
+ const void *source;
+ size_t k;
+
+ assert(nv);
+ assert(v);
+
+ /* Let's copy the simple types literally, and the larger types by references */
+ t = json_variant_type(v);
+ switch (t) {
+ case JSON_VARIANT_INTEGER:
+ k = sizeof(intmax_t);
+ value.integer = json_variant_integer(v);
+ source = &value;
+ break;
+
+ case JSON_VARIANT_UNSIGNED:
+ k = sizeof(uintmax_t);
+ value.unsig = json_variant_unsigned(v);
+ source = &value;
+ break;
+
+ case JSON_VARIANT_REAL:
+ k = sizeof(long double);
+ value.real = json_variant_real(v);
+ source = &value;
+ break;
+
+ case JSON_VARIANT_BOOLEAN:
+ k = sizeof(bool);
+ value.boolean = json_variant_boolean(v);
+ source = &value;
+ break;
+
+ case JSON_VARIANT_NULL:
+ k = 0;
+ source = NULL;
+ break;
+
+ case JSON_VARIANT_STRING:
+ source = json_variant_string(v);
+ k = strnlen(source, INLINE_STRING_MAX + 1);
+ if (k <= INLINE_STRING_MAX) {
+ k ++;
+ break;
+ }
+
+ _fallthrough_;
+
+ default:
+ /* Everything else copy by reference */
+
+ c = malloc0(offsetof(JsonVariant, reference) + sizeof(JsonVariant*));
+ if (!c)
+ return -ENOMEM;
+
+ c->n_ref = 1;
+ c->type = t;
+ c->is_reference = true;
+ c->reference = json_variant_ref(json_variant_normalize(v));
+
+ *nv = c;
+ return 0;
+ }
+
+ c = malloc0(offsetof(JsonVariant, value) + k);
+ if (!c)
+ return -ENOMEM;
+
+ c->n_ref = 1;
+ c->type = t;
+
+ memcpy_safe(&c->value, source, k);
+
+ *nv = c;
+ return 0;
+}
+
+static bool json_single_ref(JsonVariant *v) {
+
+ /* Checks whether the caller is the single owner of the object, i.e. can get away with changing it */
+
+ if (!json_variant_is_regular(v))
+ return false;
+
+ if (v->is_embedded)
+ return json_single_ref(v->parent);
+
+ assert(v->n_ref > 0);
+ return v->n_ref == 1;
+}
+
+static int json_variant_set_source(JsonVariant **v, JsonSource *source, unsigned line, unsigned column) {
+ JsonVariant *w;
+ int r;
+
+ assert(v);
+
+ /* Patch in source and line/column number. Tries to do this in-place if the caller is the sole referencer of
+ * the object. If not, allocates a new object, possibly a surrogate for the original one */
+
+ if (!*v)
+ return 0;
+
+ if (source && line > source->max_line)
+ source->max_line = line;
+ if (source && column > source->max_column)
+ source->max_column = column;
+
+ if (!json_variant_is_regular(*v)) {
+
+ if (!source && line == 0 && column == 0)
+ return 0;
+
+ } else {
+ if (json_source_equal((*v)->source, source) &&
+ (*v)->line == line &&
+ (*v)->column == column)
+ return 0;
+
+ if (json_single_ref(*v)) { /* Sole reference? */
+ json_source_unref((*v)->source);
+ (*v)->source = json_source_ref(source);
+ (*v)->line = line;
+ (*v)->column = column;
+ return 1;
+ }
+ }
+
+ r = json_variant_copy(&w, *v);
+ if (r < 0)
+ return r;
+
+ assert(json_variant_is_regular(w));
+ assert(!w->is_embedded);
+ assert(w->n_ref == 1);
+ assert(!w->source);
+
+ w->source = json_source_ref(source);
+ w->line = line;
+ w->column = column;
+
+ json_variant_unref(*v);
+ *v = w;
+
+ return 1;
+}
+
+static void inc_lines_columns(unsigned *line, unsigned *column, const char *s, size_t n) {
+ assert(line);
+ assert(column);
+ assert(s || n == 0);
+
+ while (n > 0) {
+
+ if (*s == '\n') {
+ (*line)++;
+ *column = 1;
+ } else if ((signed char) *s >= 0 && *s < 127) /* Process ASCII chars quickly */
+ (*column)++;
+ else {
+ int w;
+
+ w = utf8_encoded_valid_unichar(s);
+ if (w < 0) /* count invalid unichars as normal characters */
+ w = 1;
+ else if ((size_t) w > n) /* never read more than the specified number of characters */
+ w = (int) n;
+
+ (*column)++;
+
+ s += w;
+ n -= w;
+ continue;
+ }
+
+ s++;
+ n--;
+ }
+}
+
+static int unhex_ucs2(const char *c, uint16_t *ret) {
+ int aa, bb, cc, dd;
+ uint16_t x;
+
+ assert(c);
+ assert(ret);
+
+ aa = unhexchar(c[0]);
+ if (aa < 0)
+ return -EINVAL;
+
+ bb = unhexchar(c[1]);
+ if (bb < 0)
+ return -EINVAL;
+
+ cc = unhexchar(c[2]);
+ if (cc < 0)
+ return -EINVAL;
+
+ dd = unhexchar(c[3]);
+ if (dd < 0)
+ return -EINVAL;
+
+ x = ((uint16_t) aa << 12) |
+ ((uint16_t) bb << 8) |
+ ((uint16_t) cc << 4) |
+ ((uint16_t) dd);
+
+ if (x <= 0)
+ return -EINVAL;
+
+ *ret = x;
+
+ return 0;
+}
+
+static int json_parse_string(const char **p, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ size_t n = 0, allocated = 0;
+ const char *c;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ c = *p;
+
+ if (*c != '"')
+ return -EINVAL;
+
+ c++;
+
+ for (;;) {
+ int len;
+
+ /* Check for EOF */
+ if (*c == 0)
+ return -EINVAL;
+
+ /* Check for control characters 0x00..0x1f */
+ if (*c > 0 && *c < ' ')
+ return -EINVAL;
+
+ /* Check for control character 0x7f */
+ if (*c == 0x7f)
+ return -EINVAL;
+
+ if (*c == '"') {
+ if (!s) {
+ s = strdup("");
+ if (!s)
+ return -ENOMEM;
+ } else
+ s[n] = 0;
+
+ *p = c + 1;
+
+ *ret = TAKE_PTR(s);
+ return JSON_TOKEN_STRING;
+ }
+
+ if (*c == '\\') {
+ char ch = 0;
+ c++;
+
+ if (*c == 0)
+ return -EINVAL;
+
+ if (IN_SET(*c, '"', '\\', '/'))
+ ch = *c;
+ else if (*c == 'b')
+ ch = '\b';
+ else if (*c == 'f')
+ ch = '\f';
+ else if (*c == 'n')
+ ch = '\n';
+ else if (*c == 'r')
+ ch = '\r';
+ else if (*c == 't')
+ ch = '\t';
+ else if (*c == 'u') {
+ char16_t x;
+ int r;
+
+ r = unhex_ucs2(c + 1, &x);
+ if (r < 0)
+ return r;
+
+ c += 5;
+
+ if (!GREEDY_REALLOC(s, allocated, n + 5))
+ return -ENOMEM;
+
+ if (!utf16_is_surrogate(x))
+ n += utf8_encode_unichar(s + n, (char32_t) x);
+ else if (utf16_is_trailing_surrogate(x))
+ return -EINVAL;
+ else {
+ char16_t y;
+
+ if (c[0] != '\\' || c[1] != 'u')
+ return -EINVAL;
+
+ r = unhex_ucs2(c + 2, &y);
+ if (r < 0)
+ return r;
+
+ c += 6;
+
+ if (!utf16_is_trailing_surrogate(y))
+ return -EINVAL;
+
+ n += utf8_encode_unichar(s + n, utf16_surrogate_pair_to_unichar(x, y));
+ }
+
+ continue;
+ } else
+ return -EINVAL;
+
+ if (!GREEDY_REALLOC(s, allocated, n + 2))
+ return -ENOMEM;
+
+ s[n++] = ch;
+ c ++;
+ continue;
+ }
+
+ len = utf8_encoded_valid_unichar(c);
+ if (len < 0)
+ return len;
+
+ if (!GREEDY_REALLOC(s, allocated, n + len + 1))
+ return -ENOMEM;
+
+ memcpy(s + n, c, len);
+ n += len;
+ c += len;
+ }
+}
+
+static int json_parse_number(const char **p, JsonValue *ret) {
+ bool negative = false, exponent_negative = false, is_real = false;
+ long double x = 0.0, y = 0.0, exponent = 0.0, shift = 1.0;
+ intmax_t i = 0;
+ uintmax_t u = 0;
+ const char *c;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ c = *p;
+
+ if (*c == '-') {
+ negative = true;
+ c++;
+ }
+
+ if (*c == '0')
+ c++;
+ else {
+ if (!strchr("123456789", *c) || *c == 0)
+ return -EINVAL;
+
+ do {
+ if (!is_real) {
+ if (negative) {
+
+ if (i < INTMAX_MIN / 10) /* overflow */
+ is_real = true;
+ else {
+ intmax_t t = 10 * i;
+
+ if (t < INTMAX_MIN + (*c - '0')) /* overflow */
+ is_real = true;
+ else
+ i = t - (*c - '0');
+ }
+ } else {
+ if (u > UINTMAX_MAX / 10) /* overflow */
+ is_real = true;
+ else {
+ uintmax_t t = 10 * u;
+
+ if (t > UINTMAX_MAX - (*c - '0')) /* overflow */
+ is_real = true;
+ else
+ u = t + (*c - '0');
+ }
+ }
+ }
+
+ x = 10.0 * x + (*c - '0');
+
+ c++;
+ } while (strchr("0123456789", *c) && *c != 0);
+ }
+
+ if (*c == '.') {
+ is_real = true;
+ c++;
+
+ if (!strchr("0123456789", *c) || *c == 0)
+ return -EINVAL;
+
+ do {
+ y = 10.0 * y + (*c - '0');
+ shift = 10.0 * shift;
+ c++;
+ } while (strchr("0123456789", *c) && *c != 0);
+ }
+
+ if (IN_SET(*c, 'e', 'E')) {
+ is_real = true;
+ c++;
+
+ if (*c == '-') {
+ exponent_negative = true;
+ c++;
+ } else if (*c == '+')
+ c++;
+
+ if (!strchr("0123456789", *c) || *c == 0)
+ return -EINVAL;
+
+ do {
+ exponent = 10.0 * exponent + (*c - '0');
+ c++;
+ } while (strchr("0123456789", *c) && *c != 0);
+ }
+
+ *p = c;
+
+ if (is_real) {
+ ret->real = ((negative ? -1.0 : 1.0) * (x + (y / shift))) * exp10l((exponent_negative ? -1.0 : 1.0) * exponent);
+ return JSON_TOKEN_REAL;
+ } else if (negative) {
+ ret->integer = i;
+ return JSON_TOKEN_INTEGER;
+ } else {
+ ret->unsig = u;
+ return JSON_TOKEN_UNSIGNED;
+ }
+}
+
+int json_tokenize(
+ const char **p,
+ char **ret_string,
+ JsonValue *ret_value,
+ unsigned *ret_line, /* 'ret_line' returns the line at the beginning of this token */
+ unsigned *ret_column,
+ void **state,
+ unsigned *line, /* 'line' is used as a line state, it always reflect the line we are at after the token was read */
+ unsigned *column) {
+
+ unsigned start_line, start_column;
+ const char *start, *c;
+ size_t n;
+ int t, r;
+
+ enum {
+ STATE_NULL,
+ STATE_VALUE,
+ STATE_VALUE_POST,
+ };
+
+ assert(p);
+ assert(*p);
+ assert(ret_string);
+ assert(ret_value);
+ assert(ret_line);
+ assert(ret_column);
+ assert(line);
+ assert(column);
+ assert(state);
+
+ t = PTR_TO_INT(*state);
+ if (t == STATE_NULL) {
+ *line = 1;
+ *column = 1;
+ t = STATE_VALUE;
+ }
+
+ /* Skip over the whitespace */
+ n = strspn(*p, WHITESPACE);
+ inc_lines_columns(line, column, *p, n);
+ c = *p + n;
+
+ /* Remember where we started processing this token */
+ start = c;
+ start_line = *line;
+ start_column = *column;
+
+ if (*c == 0) {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ r = JSON_TOKEN_END;
+ goto finish;
+ }
+
+ switch (t) {
+
+ case STATE_VALUE:
+
+ if (*c == '{') {
+ c++;
+ *state = INT_TO_PTR(STATE_VALUE);
+ r = JSON_TOKEN_OBJECT_OPEN;
+ goto null_return;
+
+ } else if (*c == '}') {
+ c++;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ r = JSON_TOKEN_OBJECT_CLOSE;
+ goto null_return;
+
+ } else if (*c == '[') {
+ c++;
+ *state = INT_TO_PTR(STATE_VALUE);
+ r = JSON_TOKEN_ARRAY_OPEN;
+ goto null_return;
+
+ } else if (*c == ']') {
+ c++;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ r = JSON_TOKEN_ARRAY_CLOSE;
+ goto null_return;
+
+ } else if (*c == '"') {
+
+ r = json_parse_string(&c, ret_string);
+ if (r < 0)
+ return r;
+
+ *ret_value = JSON_VALUE_NULL;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ goto finish;
+
+ } else if (strchr("-0123456789", *c)) {
+
+ r = json_parse_number(&c, ret_value);
+ if (r < 0)
+ return r;
+
+ *ret_string = NULL;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ goto finish;
+
+ } else if (startswith(c, "true")) {
+ *ret_string = NULL;
+ ret_value->boolean = true;
+ c += 4;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ r = JSON_TOKEN_BOOLEAN;
+ goto finish;
+
+ } else if (startswith(c, "false")) {
+ *ret_string = NULL;
+ ret_value->boolean = false;
+ c += 5;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ r = JSON_TOKEN_BOOLEAN;
+ goto finish;
+
+ } else if (startswith(c, "null")) {
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+ c += 4;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ r = JSON_TOKEN_NULL;
+ goto finish;
+
+ }
+
+ return -EINVAL;
+
+ case STATE_VALUE_POST:
+
+ if (*c == ':') {
+ c++;
+ *state = INT_TO_PTR(STATE_VALUE);
+ r = JSON_TOKEN_COLON;
+ goto null_return;
+
+ } else if (*c == ',') {
+ c++;
+ *state = INT_TO_PTR(STATE_VALUE);
+ r = JSON_TOKEN_COMMA;
+ goto null_return;
+
+ } else if (*c == '}') {
+ c++;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ r = JSON_TOKEN_OBJECT_CLOSE;
+ goto null_return;
+
+ } else if (*c == ']') {
+ c++;
+ *state = INT_TO_PTR(STATE_VALUE_POST);
+ r = JSON_TOKEN_ARRAY_CLOSE;
+ goto null_return;
+ }
+
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unexpected tokenizer state");
+ }
+
+null_return:
+ *ret_string = NULL;
+ *ret_value = JSON_VALUE_NULL;
+
+finish:
+ inc_lines_columns(line, column, start, c - start);
+ *p = c;
+
+ *ret_line = start_line;
+ *ret_column = start_column;
+
+ return r;
+}
+
+typedef enum JsonExpect {
+ /* The following values are used by json_parse() */
+ EXPECT_TOPLEVEL,
+ EXPECT_END,
+ EXPECT_OBJECT_FIRST_KEY,
+ EXPECT_OBJECT_NEXT_KEY,
+ EXPECT_OBJECT_COLON,
+ EXPECT_OBJECT_VALUE,
+ EXPECT_OBJECT_COMMA,
+ EXPECT_ARRAY_FIRST_ELEMENT,
+ EXPECT_ARRAY_NEXT_ELEMENT,
+ EXPECT_ARRAY_COMMA,
+
+ /* And these are used by json_build() */
+ EXPECT_ARRAY_ELEMENT,
+ EXPECT_OBJECT_KEY,
+} JsonExpect;
+
+typedef struct JsonStack {
+ JsonExpect expect;
+ JsonVariant **elements;
+ size_t n_elements, n_elements_allocated;
+ unsigned line_before;
+ unsigned column_before;
+ size_t n_suppress; /* When building: if > 0, suppress this many subsequent elements. If == (size_t) -1, suppress all subsequent elements */
+} JsonStack;
+
+static void json_stack_release(JsonStack *s) {
+ assert(s);
+
+ json_variant_unref_many(s->elements, s->n_elements);
+ s->elements = mfree(s->elements);
+}
+
+static int json_parse_internal(
+ const char **input,
+ JsonSource *source,
+ JsonVariant **ret,
+ unsigned *line,
+ unsigned *column,
+ bool continue_end) {
+
+ size_t n_stack = 1, n_stack_allocated = 0, i;
+ unsigned line_buffer = 0, column_buffer = 0;
+ void *tokenizer_state = NULL;
+ JsonStack *stack = NULL;
+ const char *p;
+ int r;
+
+ assert_return(input, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ p = *input;
+
+ if (!GREEDY_REALLOC(stack, n_stack_allocated, n_stack))
+ return -ENOMEM;
+
+ stack[0] = (JsonStack) {
+ .expect = EXPECT_TOPLEVEL,
+ };
+
+ if (!line)
+ line = &line_buffer;
+ if (!column)
+ column = &column_buffer;
+
+ for (;;) {
+ _cleanup_free_ char *string = NULL;
+ unsigned line_token, column_token;
+ JsonVariant *add = NULL;
+ JsonStack *current;
+ JsonValue value;
+ int token;
+
+ assert(n_stack > 0);
+ current = stack + n_stack - 1;
+
+ if (continue_end && current->expect == EXPECT_END)
+ goto done;
+
+ token = json_tokenize(&p, &string, &value, &line_token, &column_token, &tokenizer_state, line, column);
+ if (token < 0) {
+ r = token;
+ goto finish;
+ }
+
+ switch (token) {
+
+ case JSON_TOKEN_END:
+ if (current->expect != EXPECT_END) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ assert(current->n_elements == 1);
+ assert(n_stack == 1);
+ goto done;
+
+ case JSON_TOKEN_COLON:
+
+ if (current->expect != EXPECT_OBJECT_COLON) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ current->expect = EXPECT_OBJECT_VALUE;
+ break;
+
+ case JSON_TOKEN_COMMA:
+
+ if (current->expect == EXPECT_OBJECT_COMMA)
+ current->expect = EXPECT_OBJECT_NEXT_KEY;
+ else if (current->expect == EXPECT_ARRAY_COMMA)
+ current->expect = EXPECT_ARRAY_NEXT_ELEMENT;
+ else {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ break;
+
+ case JSON_TOKEN_OBJECT_OPEN:
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if (!GREEDY_REALLOC(stack, n_stack_allocated, n_stack+1)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ current = stack + n_stack - 1;
+
+ /* Prepare the expect for when we return from the child */
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_COMMA;
+ else {
+ assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT));
+ current->expect = EXPECT_ARRAY_COMMA;
+ }
+
+ stack[n_stack++] = (JsonStack) {
+ .expect = EXPECT_OBJECT_FIRST_KEY,
+ .line_before = line_token,
+ .column_before = column_token,
+ };
+
+ current = stack + n_stack - 1;
+ break;
+
+ case JSON_TOKEN_OBJECT_CLOSE:
+ if (!IN_SET(current->expect, EXPECT_OBJECT_FIRST_KEY, EXPECT_OBJECT_COMMA)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ assert(n_stack > 1);
+
+ r = json_variant_new_object(&add, current->elements, current->n_elements);
+ if (r < 0)
+ goto finish;
+
+ line_token = current->line_before;
+ column_token = current->column_before;
+
+ json_stack_release(current);
+ n_stack--, current--;
+
+ break;
+
+ case JSON_TOKEN_ARRAY_OPEN:
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if (!GREEDY_REALLOC(stack, n_stack_allocated, n_stack+1)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ current = stack + n_stack - 1;
+
+ /* Prepare the expect for when we return from the child */
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_COMMA;
+ else {
+ assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT));
+ current->expect = EXPECT_ARRAY_COMMA;
+ }
+
+ stack[n_stack++] = (JsonStack) {
+ .expect = EXPECT_ARRAY_FIRST_ELEMENT,
+ .line_before = line_token,
+ .column_before = column_token,
+ };
+
+ break;
+
+ case JSON_TOKEN_ARRAY_CLOSE:
+ if (!IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_COMMA)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ assert(n_stack > 1);
+
+ r = json_variant_new_array(&add, current->elements, current->n_elements);
+ if (r < 0)
+ goto finish;
+
+ line_token = current->line_before;
+ column_token = current->column_before;
+
+ json_stack_release(current);
+ n_stack--, current--;
+ break;
+
+ case JSON_TOKEN_STRING:
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_FIRST_KEY, EXPECT_OBJECT_NEXT_KEY, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ r = json_variant_new_string(&add, string);
+ if (r < 0)
+ goto finish;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (IN_SET(current->expect, EXPECT_OBJECT_FIRST_KEY, EXPECT_OBJECT_NEXT_KEY))
+ current->expect = EXPECT_OBJECT_COLON;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_COMMA;
+ else {
+ assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT));
+ current->expect = EXPECT_ARRAY_COMMA;
+ }
+
+ break;
+
+ case JSON_TOKEN_REAL:
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ r = json_variant_new_real(&add, value.real);
+ if (r < 0)
+ goto finish;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_COMMA;
+ else {
+ assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT));
+ current->expect = EXPECT_ARRAY_COMMA;
+ }
+
+ break;
+
+ case JSON_TOKEN_INTEGER:
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ r = json_variant_new_integer(&add, value.integer);
+ if (r < 0)
+ goto finish;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_COMMA;
+ else {
+ assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT));
+ current->expect = EXPECT_ARRAY_COMMA;
+ }
+
+ break;
+
+ case JSON_TOKEN_UNSIGNED:
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ r = json_variant_new_unsigned(&add, value.unsig);
+ if (r < 0)
+ goto finish;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_COMMA;
+ else {
+ assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT));
+ current->expect = EXPECT_ARRAY_COMMA;
+ }
+
+ break;
+
+ case JSON_TOKEN_BOOLEAN:
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ r = json_variant_new_boolean(&add, value.boolean);
+ if (r < 0)
+ goto finish;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_COMMA;
+ else {
+ assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT));
+ current->expect = EXPECT_ARRAY_COMMA;
+ }
+
+ break;
+
+ case JSON_TOKEN_NULL:
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ r = json_variant_new_null(&add);
+ if (r < 0)
+ goto finish;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_COMMA;
+ else {
+ assert(IN_SET(current->expect, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT));
+ current->expect = EXPECT_ARRAY_COMMA;
+ }
+
+ break;
+
+ default:
+ assert_not_reached("Unexpected token");
+ }
+
+ if (add) {
+ (void) json_variant_set_source(&add, source, line_token, column_token);
+
+ if (!GREEDY_REALLOC(current->elements, current->n_elements_allocated, current->n_elements + 1)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ current->elements[current->n_elements++] = add;
+ }
+ }
+
+done:
+ assert(n_stack == 1);
+ assert(stack[0].n_elements == 1);
+
+ *ret = json_variant_ref(stack[0].elements[0]);
+ *input = p;
+ r = 0;
+
+finish:
+ for (i = 0; i < n_stack; i++)
+ json_stack_release(stack + i);
+
+ free(stack);
+
+ return r;
+}
+
+int json_parse(const char *input, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
+ return json_parse_internal(&input, NULL, ret, ret_line, ret_column, false);
+}
+
+int json_parse_continue(const char **p, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
+ return json_parse_internal(p, NULL, ret, ret_line, ret_column, true);
+}
+
+int json_parse_file(FILE *f, const char *path, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
+ _cleanup_(json_source_unrefp) JsonSource *source = NULL;
+ _cleanup_free_ char *text = NULL;
+ const char *p;
+ int r;
+
+ if (f)
+ r = read_full_stream(f, &text, NULL);
+ else if (path)
+ r = read_full_file(path, &text, NULL);
+ else
+ return -EINVAL;
+ if (r < 0)
+ return r;
+
+ if (path) {
+ source = json_source_new(path);
+ if (!source)
+ return -ENOMEM;
+ }
+
+ p = text;
+ return json_parse_internal(&p, source, ret, ret_line, ret_column, false);
+}
+
+int json_buildv(JsonVariant **ret, va_list ap) {
+ JsonStack *stack = NULL;
+ size_t n_stack = 1, n_stack_allocated = 0, i;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ if (!GREEDY_REALLOC(stack, n_stack_allocated, n_stack))
+ return -ENOMEM;
+
+ stack[0] = (JsonStack) {
+ .expect = EXPECT_TOPLEVEL,
+ };
+
+ for (;;) {
+ _cleanup_(json_variant_unrefp) JsonVariant *add = NULL;
+ size_t n_subtract = 0; /* how much to subtract from current->n_suppress, i.e. how many elements would
+ * have been added to the current variant */
+ JsonStack *current;
+ int command;
+
+ assert(n_stack > 0);
+ current = stack + n_stack - 1;
+
+ if (current->expect == EXPECT_END)
+ goto done;
+
+ command = va_arg(ap, int);
+
+ switch (command) {
+
+ case _JSON_BUILD_STRING: {
+ const char *p;
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ p = va_arg(ap, const char *);
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_string(&add, p);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ break;
+ }
+
+ case _JSON_BUILD_INTEGER: {
+ intmax_t j;
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ j = va_arg(ap, intmax_t);
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_integer(&add, j);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ break;
+ }
+
+ case _JSON_BUILD_UNSIGNED: {
+ uintmax_t j;
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ j = va_arg(ap, uintmax_t);
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_unsigned(&add, j);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ break;
+ }
+
+ case _JSON_BUILD_REAL: {
+ long double d;
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ d = va_arg(ap, long double);
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_real(&add, d);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ break;
+ }
+
+ case _JSON_BUILD_BOOLEAN: {
+ bool b;
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ b = va_arg(ap, int);
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_boolean(&add, b);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ break;
+ }
+
+ case _JSON_BUILD_NULL:
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_null(&add);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ break;
+
+ case _JSON_BUILD_VARIANT:
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ /* Note that we don't care for current->n_suppress here, after all the variant is already
+ * allocated anyway... */
+ add = va_arg(ap, JsonVariant*);
+ if (!add)
+ add = JSON_VARIANT_MAGIC_NULL;
+ else
+ json_variant_ref(add);
+
+ n_subtract = 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ break;
+
+ case _JSON_BUILD_LITERAL: {
+ const char *l;
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ l = va_arg(ap, const char *);
+
+ if (l) {
+ /* Note that we don't care for current->n_suppress here, we should generate parsing
+ * errors even in suppressed object properties */
+
+ r = json_parse(l, &add, NULL, NULL);
+ if (r < 0)
+ goto finish;
+ } else
+ add = JSON_VARIANT_MAGIC_NULL;
+
+ n_subtract = 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ break;
+ }
+
+ case _JSON_BUILD_ARRAY_BEGIN:
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if (!GREEDY_REALLOC(stack, n_stack_allocated, n_stack+1)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ current = stack + n_stack - 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ stack[n_stack++] = (JsonStack) {
+ .expect = EXPECT_ARRAY_ELEMENT,
+ .n_suppress = current->n_suppress != 0 ? (size_t) -1 : 0, /* if we shall suppress the
+ * new array, then we should
+ * also suppress all array
+ * members */
+ };
+
+ break;
+
+ case _JSON_BUILD_ARRAY_END:
+ if (current->expect != EXPECT_ARRAY_ELEMENT) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ assert(n_stack > 1);
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_array(&add, current->elements, current->n_elements);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ json_stack_release(current);
+ n_stack--, current--;
+
+ break;
+
+ case _JSON_BUILD_STRV: {
+ char **l;
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ l = va_arg(ap, char **);
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_array_strv(&add, l);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ break;
+ }
+
+ case _JSON_BUILD_OBJECT_BEGIN:
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if (!GREEDY_REALLOC(stack, n_stack_allocated, n_stack+1)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ current = stack + n_stack - 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ stack[n_stack++] = (JsonStack) {
+ .expect = EXPECT_OBJECT_KEY,
+ .n_suppress = current->n_suppress != 0 ? (size_t) -1 : 0, /* if we shall suppress the
+ * new object, then we should
+ * also suppress all object
+ * members */
+ };
+
+ break;
+
+ case _JSON_BUILD_OBJECT_END:
+
+ if (current->expect != EXPECT_OBJECT_KEY) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ assert(n_stack > 1);
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_object(&add, current->elements, current->n_elements);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ json_stack_release(current);
+ n_stack--, current--;
+
+ break;
+
+ case _JSON_BUILD_PAIR: {
+ const char *n;
+
+ if (current->expect != EXPECT_OBJECT_KEY) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ n = va_arg(ap, const char *);
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_string(&add, n);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ current->expect = EXPECT_OBJECT_VALUE;
+ break;
+ }
+
+ case _JSON_BUILD_PAIR_CONDITION: {
+ const char *n;
+ bool b;
+
+ if (current->expect != EXPECT_OBJECT_KEY) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ b = va_arg(ap, int);
+ n = va_arg(ap, const char *);
+
+ if (b && current->n_suppress == 0) {
+ r = json_variant_new_string(&add, n);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1; /* we generated one item */
+
+ if (!b && current->n_suppress != (size_t) -1)
+ current->n_suppress += 2; /* Suppress this one and the next item */
+
+ current->expect = EXPECT_OBJECT_VALUE;
+ break;
+ }}
+
+ /* If a variant was generated, add it to our current variant, but only if we are not supposed to suppress additions */
+ if (add && current->n_suppress == 0) {
+ if (!GREEDY_REALLOC(current->elements, current->n_elements_allocated, current->n_elements + 1)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ current->elements[current->n_elements++] = TAKE_PTR(add);
+ }
+
+ /* If we are supposed to suppress items, let's subtract how many items where generated from that
+ * counter. Except if the counter is (size_t) -1, i.e. we shall suppress an infinite number of elements
+ * on this stack level */
+ if (current->n_suppress != (size_t) -1) {
+ if (current->n_suppress <= n_subtract) /* Saturated */
+ current->n_suppress = 0;
+ else
+ current->n_suppress -= n_subtract;
+ }
+ }
+
+done:
+ assert(n_stack == 1);
+ assert(stack[0].n_elements == 1);
+
+ *ret = json_variant_ref(stack[0].elements[0]);
+ r = 0;
+
+finish:
+ for (i = 0; i < n_stack; i++)
+ json_stack_release(stack + i);
+
+ free(stack);
+
+ va_end(ap);
+
+ return r;
+}
+
+int json_build(JsonVariant **ret, ...) {
+ va_list ap;
+ int r;
+
+ va_start(ap, ret);
+ r = json_buildv(ret, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int json_log_internal(
+ JsonVariant *variant,
+ int level,
+ int error,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format, ...) {
+
+ PROTECT_ERRNO;
+
+ unsigned source_line, source_column;
+ char buffer[LINE_MAX];
+ const char *source;
+ va_list ap;
+ int r;
+
+ if (error < 0)
+ error = -error;
+
+ errno = error;
+
+ va_start(ap, format);
+ (void) vsnprintf(buffer, sizeof buffer, format, ap);
+ va_end(ap);
+
+ if (variant) {
+ r = json_variant_get_source(variant, &source, &source_line, &source_column);
+ if (r < 0)
+ return r;
+ } else {
+ source = NULL;
+ source_line = 0;
+ source_column = 0;
+ }
+
+ if (source && source_line > 0 && source_column > 0)
+ return log_struct_internal(
+ LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level),
+ error,
+ file, line, func,
+ "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
+ "CONFIG_FILE=%s", source,
+ "CONFIG_LINE=%u", source_line,
+ "CONFIG_COLUMN=%u", source_column,
+ LOG_MESSAGE("%s:%u: %s", source, line, buffer),
+ NULL);
+ else
+ return log_struct_internal(
+ LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level),
+ error,
+ file, line, func,
+ "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
+ LOG_MESSAGE("%s", buffer),
+ NULL);
+}
+
+int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata) {
+ const JsonDispatch *p;
+ size_t i, n, m;
+ int r, done = 0;
+ bool *found;
+
+ if (!json_variant_is_object(v)) {
+ json_log(v, flags, 0, "JSON variant is not an object.");
+
+ if (flags & JSON_PERMISSIVE)
+ return 0;
+
+ return -EINVAL;
+ }
+
+ for (p = table, m = 0; p->name; p++)
+ m++;
+
+ found = newa0(bool, m);
+
+ n = json_variant_elements(v);
+ for (i = 0; i < n; i += 2) {
+ JsonVariant *key, *value;
+
+ assert_se(key = json_variant_by_index(v, i));
+ assert_se(value = json_variant_by_index(v, i+1));
+
+ for (p = table; p->name; p++)
+ if (p->name == (const char*) -1 ||
+ streq_ptr(json_variant_string(key), p->name))
+ break;
+
+ if (p->name) { /* Found a matching entry! :-) */
+ JsonDispatchFlags merged_flags;
+
+ merged_flags = flags | p->flags;
+
+ if (p->type != _JSON_VARIANT_TYPE_INVALID &&
+ !json_variant_has_type(value, p->type)) {
+
+ json_log(value, merged_flags, 0,
+ "Object field '%s' has wrong type %s, expected %s.", json_variant_string(key),
+ json_variant_type_to_string(json_variant_type(value)), json_variant_type_to_string(p->type));
+
+ if (merged_flags & JSON_PERMISSIVE)
+ continue;
+
+ return -EINVAL;
+ }
+
+ if (found[p-table]) {
+ json_log(value, merged_flags, 0, "Duplicate object field '%s'.", json_variant_string(key));
+
+ if (merged_flags & JSON_PERMISSIVE)
+ continue;
+
+ return -ENOTUNIQ;
+ }
+
+ found[p-table] = true;
+
+ if (p->callback) {
+ r = p->callback(json_variant_string(key), value, merged_flags, (uint8_t*) userdata + p->offset);
+ if (r < 0) {
+ if (merged_flags & JSON_PERMISSIVE)
+ continue;
+
+ return r;
+ }
+ }
+
+ done ++;
+
+ } else { /* Didn't find a matching entry! :-( */
+
+ if (bad) {
+ r = bad(json_variant_string(key), value, flags, userdata);
+ if (r < 0) {
+ if (flags & JSON_PERMISSIVE)
+ continue;
+
+ return r;
+ } else
+ done ++;
+
+ } else {
+ json_log(value, flags, 0, "Unexpected object field '%s'.", json_variant_string(key));
+
+ if (flags & JSON_PERMISSIVE)
+ continue;
+
+ return -EADDRNOTAVAIL;
+ }
+ }
+ }
+
+ for (p = table; p->name; p++) {
+ JsonDispatchFlags merged_flags = p->flags | flags;
+
+ if ((merged_flags & JSON_MANDATORY) && !found[p-table]) {
+ json_log(v, merged_flags, 0, "Missing object field '%s'.", p->name);
+
+ if ((merged_flags & JSON_PERMISSIVE))
+ continue;
+
+ return -ENXIO;
+ }
+ }
+
+ return done;
+}
+
+int json_dispatch_boolean(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ bool *b = userdata;
+
+ assert(variant);
+ assert(b);
+
+ if (!json_variant_is_boolean(variant)) {
+ json_log(variant, flags, 0, "JSON field '%s' is not a boolean.", strna(name));
+ return -EINVAL;
+ }
+
+ *b = json_variant_boolean(variant);
+ return 0;
+}
+
+int json_dispatch_tristate(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ int *b = userdata;
+
+ assert(variant);
+ assert(b);
+
+ if (!json_variant_is_boolean(variant)) {
+ json_log(variant, flags, 0, "JSON field '%s' is not a boolean.", strna(name));
+ return -EINVAL;
+ }
+
+ *b = json_variant_boolean(variant);
+ return 0;
+}
+
+int json_dispatch_integer(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ intmax_t *i = userdata;
+
+ assert(variant);
+ assert(i);
+
+ if (!json_variant_is_integer(variant)) {
+ json_log(variant, flags, 0, "JSON field '%s' is not an integer.", strna(name));
+ return -EINVAL;
+ }
+
+ *i = json_variant_integer(variant);
+ return 0;
+}
+
+int json_dispatch_unsigned(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ uintmax_t *u = userdata;
+
+ assert(variant);
+ assert(u);
+
+ if (!json_variant_is_unsigned(variant)) {
+ json_log(variant, flags, 0, "JSON field '%s' is not an unsigned integer.", strna(name));
+ return -EINVAL;
+ }
+
+ *u = json_variant_unsigned(variant);
+ return 0;
+}
+
+int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ uint32_t *u = userdata;
+
+ assert(variant);
+ assert(u);
+
+ if (!json_variant_is_unsigned(variant)) {
+ json_log(variant, flags, 0, "JSON field '%s' is not an unsigned integer.", strna(name));
+ return -EINVAL;
+ }
+
+ if (json_variant_unsigned(variant) > UINT32_MAX) {
+ json_log(variant, flags, 0, "JSON field '%s' out of bounds.", strna(name));
+ return -ERANGE;
+ }
+
+ *u = (uint32_t) json_variant_unsigned(variant);
+ return 0;
+}
+
+int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ int32_t *i = userdata;
+
+ assert(variant);
+ assert(i);
+
+ if (!json_variant_is_integer(variant)) {
+ json_log(variant, flags, 0, "JSON field '%s' is not an integer.", strna(name));
+ return -EINVAL;
+ }
+
+ if (json_variant_integer(variant) < INT32_MIN || json_variant_integer(variant) > INT32_MAX) {
+ json_log(variant, flags, 0, "JSON field '%s' out of bounds.", strna(name));
+ return -ERANGE;
+ }
+
+ *i = (int32_t) json_variant_integer(variant);
+ return 0;
+}
+
+int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ char **s = userdata;
+ int r;
+
+ assert(variant);
+ assert(s);
+
+ if (json_variant_is_null(variant)) {
+ *s = mfree(*s);
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant)) {
+ json_log(variant, flags, 0, "JSON field '%s' is not a string.", strna(name));
+ return -EINVAL;
+ }
+
+ r = free_and_strdup(s, json_variant_string(variant));
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+ return 0;
+}
+
+int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ _cleanup_strv_free_ char **l = NULL;
+ char ***s = userdata;
+ size_t i;
+ int r;
+
+ assert(variant);
+ assert(s);
+
+ if (json_variant_is_null(variant)) {
+ *s = strv_free(*s);
+ return 0;
+ }
+
+ if (!json_variant_is_array(variant)) {
+ json_log(variant, 0, flags, "JSON field '%s' is not an array.", strna(name));
+ return -EINVAL;
+ }
+
+ for (i = 0; i < json_variant_elements(variant); i++) {
+ JsonVariant *e;
+
+ assert_se(e = json_variant_by_index(variant, i));
+
+ if (!json_variant_is_string(e)) {
+ json_log(e, 0, flags, "JSON array element is not a string.");
+ return -EINVAL;
+ }
+
+ r = strv_extend(&l, json_variant_string(e));
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to append array element: %m");
+ }
+
+ strv_free_and_replace(*s, l);
+ return 0;
+}
+
+int json_dispatch_variant(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ JsonVariant **p = userdata;
+
+ assert(variant);
+ assert(p);
+
+ json_variant_unref(*p);
+ *p = json_variant_ref(variant);
+
+ return 0;
+}
+
+static const char* const json_variant_type_table[_JSON_VARIANT_TYPE_MAX] = {
+ [JSON_VARIANT_STRING] = "string",
+ [JSON_VARIANT_INTEGER] = "integer",
+ [JSON_VARIANT_UNSIGNED] = "unsigned",
+ [JSON_VARIANT_REAL] = "real",
+ [JSON_VARIANT_NUMBER] = "number",
+ [JSON_VARIANT_BOOLEAN] = "boolean",
+ [JSON_VARIANT_ARRAY] = "array",
+ [JSON_VARIANT_OBJECT] = "object",
+ [JSON_VARIANT_NULL] = "null",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(json_variant_type, JsonVariantType);
diff --git a/src/shared/json.h b/src/shared/json.h
new file mode 100644
index 0000000000..4eba91c272
--- /dev/null
+++ b/src/shared/json.h
@@ -0,0 +1,286 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "macro.h"
+#include "string-util.h"
+#include "util.h"
+
+/*
+ In case you wonder why we have our own JSON implementation, here are a couple of reasons why this implementation has
+ benefits over various other implementatins:
+
+ - We need support for 64bit signed and unsigned integers, i.e. the full 64,5bit range of -9223372036854775808…18446744073709551615
+ - All our variants are immutable after creation
+ - Special values such as true, false, zero, null, empty strings, empty array, empty objects require zero dynamic memory
+ - Progressive parsing
+ - Our integer/real type implicitly converts, but only if that's safe and loss-lessly possible
+ - There's a "builder" for putting together objects easily in varargs function calls
+ - There's a "dispatcher" for mapping objects to C data structures
+ - Every variant optionally carries parsing location information, which simplifies debugging and parse log error generation
+ - Formatter has color, line, column support
+
+ Limitations:
+ - Doesn't allow embedded NUL in strings
+ - Can't store integers outside of the -9223372036854775808…18446744073709551615 range (it will use 'long double' for
+ values outside this range, which is lossy)
+ - Can't store negative zero (will be treated identical to positive zero, and not retained across serialization)
+ - Can't store non-integer numbers that can't be stored in "long double" losslessly
+ - Allows creation and parsing of objects with duplicate keys. The "dispatcher" will refuse them however. This means
+ we can parse and pass around such objects, but will carefully refuse them when we convert them into our own data.
+
+ (These limitations should be pretty much in line with those of other JSON implementations, in fact might be less
+ limiting in most cases even.)
+*/
+
+typedef struct JsonVariant JsonVariant;
+
+typedef enum JsonVariantType {
+ JSON_VARIANT_STRING,
+ JSON_VARIANT_INTEGER,
+ JSON_VARIANT_UNSIGNED,
+ JSON_VARIANT_REAL,
+ JSON_VARIANT_NUMBER, /* This a pseudo-type: we can never create variants of this type, but we use it as wildcard check for the above three types */
+ JSON_VARIANT_BOOLEAN,
+ JSON_VARIANT_ARRAY,
+ JSON_VARIANT_OBJECT,
+ JSON_VARIANT_NULL,
+ _JSON_VARIANT_TYPE_MAX,
+ _JSON_VARIANT_TYPE_INVALID = -1
+} JsonVariantType;
+
+int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n);
+int json_variant_new_integer(JsonVariant **ret, intmax_t i);
+int json_variant_new_unsigned(JsonVariant **ret, uintmax_t u);
+int json_variant_new_real(JsonVariant **ret, long double d);
+int json_variant_new_boolean(JsonVariant **ret, bool b);
+int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n);
+int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n);
+int json_variant_new_array_strv(JsonVariant **ret, char **l);
+int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n);
+int json_variant_new_null(JsonVariant **ret);
+
+static inline int json_variant_new_string(JsonVariant **ret, const char *s) {
+ return json_variant_new_stringn(ret, s, strlen_ptr(s));
+}
+
+JsonVariant *json_variant_ref(JsonVariant *v);
+JsonVariant *json_variant_unref(JsonVariant *v);
+void json_variant_unref_many(JsonVariant **array, size_t n);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref);
+
+const char *json_variant_string(JsonVariant *v);
+intmax_t json_variant_integer(JsonVariant *v);
+uintmax_t json_variant_unsigned(JsonVariant *v);
+long double json_variant_real(JsonVariant *v);
+bool json_variant_boolean(JsonVariant *v);
+
+JsonVariantType json_variant_type(JsonVariant *v);
+bool json_variant_has_type(JsonVariant *v, JsonVariantType type);
+
+static inline bool json_variant_is_string(JsonVariant *v) {
+ return json_variant_has_type(v, JSON_VARIANT_STRING);
+}
+
+static inline bool json_variant_is_integer(JsonVariant *v) {
+ return json_variant_has_type(v, JSON_VARIANT_INTEGER);
+}
+
+static inline bool json_variant_is_unsigned(JsonVariant *v) {
+ return json_variant_has_type(v, JSON_VARIANT_UNSIGNED);
+}
+
+static inline bool json_variant_is_real(JsonVariant *v) {
+ return json_variant_has_type(v, JSON_VARIANT_REAL);
+}
+
+static inline bool json_variant_is_number(JsonVariant *v) {
+ return json_variant_has_type(v, JSON_VARIANT_NUMBER);
+}
+
+static inline bool json_variant_is_boolean(JsonVariant *v) {
+ return json_variant_has_type(v, JSON_VARIANT_BOOLEAN);
+}
+
+static inline bool json_variant_is_array(JsonVariant *v) {
+ return json_variant_has_type(v, JSON_VARIANT_ARRAY);
+}
+
+static inline bool json_variant_is_object(JsonVariant *v) {
+ return json_variant_has_type(v, JSON_VARIANT_OBJECT);
+}
+
+static inline bool json_variant_is_null(JsonVariant *v) {
+ return json_variant_has_type(v, JSON_VARIANT_NULL);
+}
+
+bool json_variant_is_negative(JsonVariant *v);
+
+size_t json_variant_elements(JsonVariant *v);
+JsonVariant *json_variant_by_index(JsonVariant *v, size_t index);
+JsonVariant *json_variant_by_key(JsonVariant *v, const char *key);
+JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVariant **ret_key);
+
+bool json_variant_equal(JsonVariant *a, JsonVariant *b);
+
+struct json_variant_foreach_state {
+ JsonVariant *variant;
+ size_t idx;
+};
+
+#define JSON_VARIANT_ARRAY_FOREACH(i, v) \
+ for (struct json_variant_foreach_state _state = { (v), 0 }; \
+ _state.idx < json_variant_elements(_state.variant) && \
+ ({ i = json_variant_by_index(_state.variant, _state.idx); \
+ true; }); \
+ _state.idx++)
+
+#define JSON_VARIANT_OBJECT_FOREACH(k, e, v) \
+ for (struct json_variant_foreach_state _state = { (v), 0 }; \
+ _state.idx < json_variant_elements(_state.variant) && \
+ ({ k = json_variant_by_index(_state.variant, _state.idx); \
+ e = json_variant_by_index(_state.variant, _state.idx + 1); \
+ true; }); \
+ _state.idx += 2)
+
+int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column);
+
+typedef enum JsonFormatFlags {
+ JSON_FORMAT_NEWLINE = 1 << 0, /* suffix with newline */
+ JSON_FORMAT_PRETTY = 1 << 1, /* add internal whitespace to appeal to human readers */
+ JSON_FORMAT_COLOR = 1 << 2, /* insert ANSI color sequences */
+ JSON_FORMAT_COLOR_AUTO = 1 << 3, /* insetr ANSI color sequences if colors_enabled() says so */
+ JSON_FORMAT_SOURCE = 1 << 4, /* prefix with source filename/line/column */
+ JSON_FORMAT_SSE = 1 << 5, /* prefix/suffix with W3C server-sent events */
+ JSON_FORMAT_SEQ = 1 << 6, /* prefix/suffix with RFC 7464 application/json-seq */
+} JsonFormatFlags;
+
+int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
+void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix);
+
+int json_parse(const char *string, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
+int json_parse_continue(const char **p, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
+int json_parse_file(FILE *f, const char *path, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
+
+enum {
+ _JSON_BUILD_STRING,
+ _JSON_BUILD_INTEGER,
+ _JSON_BUILD_UNSIGNED,
+ _JSON_BUILD_REAL,
+ _JSON_BUILD_BOOLEAN,
+ _JSON_BUILD_ARRAY_BEGIN,
+ _JSON_BUILD_ARRAY_END,
+ _JSON_BUILD_OBJECT_BEGIN,
+ _JSON_BUILD_OBJECT_END,
+ _JSON_BUILD_PAIR,
+ _JSON_BUILD_PAIR_CONDITION,
+ _JSON_BUILD_NULL,
+ _JSON_BUILD_VARIANT,
+ _JSON_BUILD_LITERAL,
+ _JSON_BUILD_STRV,
+ _JSON_BUILD_MAX,
+};
+
+#define JSON_BUILD_STRING(s) _JSON_BUILD_STRING, ({ const char *_x = s; _x; })
+#define JSON_BUILD_INTEGER(i) _JSON_BUILD_INTEGER, ({ intmax_t _x = i; _x; })
+#define JSON_BUILD_UNSIGNED(u) _JSON_BUILD_UNSIGNED, ({ uintmax_t _x = u; _x; })
+#define JSON_BUILD_REAL(d) _JSON_BUILD_REAL, ({ long double _x = d; _x; })
+#define JSON_BUILD_BOOLEAN(b) _JSON_BUILD_BOOLEAN, ({ bool _x = b; _x; })
+#define JSON_BUILD_ARRAY(...) _JSON_BUILD_ARRAY_BEGIN, __VA_ARGS__, _JSON_BUILD_ARRAY_END
+#define JSON_BUILD_OBJECT(...) _JSON_BUILD_OBJECT_BEGIN, __VA_ARGS__, _JSON_BUILD_OBJECT_END
+#define JSON_BUILD_PAIR(n, ...) _JSON_BUILD_PAIR, ({ const char *_x = n; _x; }), __VA_ARGS__
+#define JSON_BUILD_PAIR_CONDITION(c, n, ...) _JSON_BUILD_PAIR_CONDITION, ({ bool _x = c; _x; }), ({ const char *_x = n; _x; }), __VA_ARGS__
+#define JSON_BUILD_NULL _JSON_BUILD_NULL
+#define JSON_BUILD_VARIANT(v) _JSON_BUILD_VARIANT, ({ JsonVariant *_x = v; _x; })
+#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, ({ const char *_x = l; _x; })
+#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, ({ char **_x = l; _x; })
+
+int json_build(JsonVariant **ret, ...);
+int json_buildv(JsonVariant **ret, va_list ap);
+
+/* A bitmask of flags used by the dispatch logic. Note that this is a combined bit mask, that is generated from the bit
+ * mask originally passed into json_dispatch(), the individual bitmask associated with the static JsonDispatch callout
+ * entry, as well the bitmask specified for json_log() calls */
+typedef enum JsonDispatchFlags {
+ /* The following three may be set in JsonDispatch's .flags field or the json_dispatch() flags parameter */
+ JSON_PERMISSIVE = 1 << 0, /* Shall parsing errors be considered fatal for this property? */
+ JSON_MANDATORY = 1 << 1, /* Should existance of this property be mandatory? */
+ JSON_LOG = 1 << 2, /* Should the parser log about errors? */
+
+ /* The following two may be passed into log_json() in addition to the three above */
+ JSON_DEBUG = 1 << 3, /* Indicates that this log message is a debug message */
+ JSON_WARNING = 1 << 4, /* Indicates that this log message is a warning message */
+} JsonDispatchFlags;
+
+typedef int (*JsonDispatchCallback)(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+
+typedef struct JsonDispatch {
+ const char *name;
+ JsonVariantType type;
+ JsonDispatchCallback callback;
+ size_t offset;
+ JsonDispatchFlags flags;
+} JsonDispatch;
+
+int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata);
+
+int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_boolean(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_tristate(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_variant(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_integer(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_unsigned(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+
+assert_cc(sizeof(uintmax_t) == sizeof(uint64_t))
+#define json_dispatch_uint64 json_dispatch_unsigned
+
+assert_cc(sizeof(intmax_t) == sizeof(int64_t))
+#define json_dispatch_int64 json_dispatch_integer
+
+static inline int json_dispatch_level(JsonDispatchFlags flags) {
+
+ /* Did the user request no logging? If so, then never log higher than LOG_DEBUG. Also, if this is marked as
+ * debug message, then also log at debug level. */
+
+ if (!(flags & JSON_LOG) ||
+ (flags & JSON_DEBUG))
+ return LOG_DEBUG;
+
+ /* Are we invoked in permissive mode, or is this explicitly marked as warning message? Then this should be
+ * printed at LOG_WARNING */
+ if (flags & (JSON_PERMISSIVE|JSON_WARNING))
+ return LOG_WARNING;
+
+ /* Otherwise it's an error. */
+ return LOG_ERR;
+}
+
+int json_log_internal(JsonVariant *variant, int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(7, 8);
+
+#define json_log(variant, flags, error, ...) \
+ ({ \
+ int _level = json_dispatch_level(flags), _e = (error); \
+ (log_get_max_level() >= LOG_PRI(_level)) \
+ ? json_log_internal(variant, _level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ : -abs(_e); \
+ })
+
+#define JSON_VARIANT_STRING_CONST(x) _JSON_VARIANT_STRING_CONST(UNIQ, (x))
+
+#define _JSON_VARIANT_STRING_CONST(xq, x) \
+ ({ \
+ _align_(2) static const char UNIQ_T(json_string_const, xq)[] = (x); \
+ assert((((uintptr_t) UNIQ_T(json_string_const, xq)) & 1) == 0); \
+ (JsonVariant*) ((uintptr_t) UNIQ_T(json_string_const, xq) + 1); \
+ })
+
+const char *json_variant_type_to_string(JsonVariantType t);
+JsonVariantType json_variant_type_from_string(const char *s);
diff --git a/src/shared/linux/libbpf.h b/src/shared/linux/libbpf.h
index ad131fcb12..391eee5a4e 100644
--- a/src/shared/linux/libbpf.h
+++ b/src/shared/linux/libbpf.h
@@ -174,6 +174,16 @@ struct bpf_insn;
.off = OFF, \
.imm = IMM })
+/* Unconditional jumps */
+
+#define BPF_JMP_A(OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_JA, \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = OFF, \
+ .imm = 0 })
+
/* Raw code statement block */
#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
diff --git a/src/basic/lockfile-util.c b/src/shared/lockfile-util.c
index 4bae23b243..4bae23b243 100644
--- a/src/basic/lockfile-util.c
+++ b/src/shared/lockfile-util.c
diff --git a/src/basic/lockfile-util.h b/src/shared/lockfile-util.h
index c2abd9956f..e0eef34cdc 100644
--- a/src/basic/lockfile-util.h
+++ b/src/shared/lockfile-util.h
@@ -1,11 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-#include <stddef.h>
-
-#include "macro.h"
-#include "missing.h"
-
typedef struct LockFile {
char *path;
int fd;
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 33afbe2f7f..525a948f36 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -21,6 +21,7 @@
#include "hostname-util.h"
#include "io-util.h"
#include "journal-internal.h"
+#include "json.h"
#include "log.h"
#include "logs-show.h"
#include "macro.h"
@@ -41,7 +42,7 @@
#define PRINT_LINE_THRESHOLD 3
#define PRINT_CHAR_THRESHOLD 300
-#define JSON_THRESHOLD 4096
+#define JSON_THRESHOLD 4096U
static int print_catalog(FILE *f, sd_journal *j) {
int r;
@@ -170,6 +171,10 @@ static bool print_multiline(
color_on = ANSI_HIGHLIGHT;
color_off = ANSI_NORMAL;
highlight_on = ANSI_HIGHLIGHT_RED;
+ } else if (priority >= LOG_DEBUG) {
+ color_on = ANSI_GREY;
+ color_off = ANSI_NORMAL;
+ highlight_on = ANSI_HIGHLIGHT_RED;
}
}
@@ -302,10 +307,9 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
k = format_timestamp_utc(buf, sizeof(buf), x);
else
k = format_timestamp(buf, sizeof(buf), x);
- if (!k) {
- log_error("Failed to format timestamp: %"PRIu64, x);
- return -EINVAL;
- }
+ if (!k)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to format timestamp: %" PRIu64, x);
} else {
char usec[7];
@@ -320,18 +324,16 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
break;
case OUTPUT_SHORT_ISO:
- if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0) {
- log_error("Failed to format ISO time");
- return -EINVAL;
- }
+ if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to format ISO time");
break;
case OUTPUT_SHORT_ISO_PRECISE:
/* No usec in strftime, so we leave space and copy over */
- if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t, &tm)) <= 0) {
- log_error("Failed to format ISO-precise time");
- return -EINVAL;
- }
+ if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t, &tm)) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to format ISO-precise time");
xsprintf(usec, "%06"PRI_USEC, x % USEC_PER_SEC);
memcpy(buf + 20, usec, 6);
break;
@@ -339,10 +341,9 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
case OUTPUT_SHORT:
case OUTPUT_SHORT_PRECISE:
- if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0) {
- log_error("Failed to format syslog time");
- return -EINVAL;
- }
+ if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to format syslog time");
if (mode == OUTPUT_SHORT_PRECISE) {
size_t k;
@@ -351,10 +352,9 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
k = sizeof(buf) - strlen(buf);
r = snprintf(buf + strlen(buf), k, ".%06"PRIu64, x % USEC_PER_SEC);
- if (r <= 0 || (size_t) r >= k) { /* too long? */
- log_error("Failed to format precise time");
- return -EINVAL;
- }
+ if (r <= 0 || (size_t) r >= k) /* too long? */
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to format precise time");
}
break;
@@ -374,7 +374,7 @@ static int output_short(
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
int r;
const void *data;
@@ -505,7 +505,7 @@ static int output_verbose(
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
const void *data;
size_t length;
@@ -562,10 +562,9 @@ static int output_verbose(
const char *on = "", *off = "";
c = memchr(data, '=', length);
- if (!c) {
- log_error("Invalid field.");
- return -EINVAL;
- }
+ if (!c)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid field.");
fieldlen = c - (const char*) data;
r = field_set_test(output_fields, data, fieldlen);
@@ -613,7 +612,7 @@ static int output_export(
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
sd_id128_t boot_id;
char sid[33];
@@ -657,10 +656,9 @@ static int output_export(
continue;
c = memchr(data, '=', length);
- if (!c) {
- log_error("Invalid field.");
- return -EINVAL;
- }
+ if (!c)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid field.");
r = field_set_test(output_fields, data, c - (const char *) data);
if (r < 0)
@@ -747,6 +745,96 @@ void json_escape(
}
}
+struct json_data {
+ JsonVariant* name;
+ size_t n_values;
+ JsonVariant* values[];
+};
+
+static int update_json_data(
+ Hashmap *h,
+ OutputFlags flags,
+ const char *name,
+ const void *value,
+ size_t size) {
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ struct json_data *d;
+ int r;
+
+ if (!(flags & OUTPUT_SHOW_ALL) && strlen(name) + 1 + size >= JSON_THRESHOLD)
+ r = json_variant_new_null(&v);
+ else if (utf8_is_printable(value, size))
+ r = json_variant_new_stringn(&v, value, size);
+ else
+ r = json_variant_new_array_bytes(&v, value, size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate JSON data: %m");
+
+ d = hashmap_get(h, name);
+ if (d) {
+ struct json_data *w;
+
+ w = realloc(d, offsetof(struct json_data, values) + sizeof(JsonVariant*) * (d->n_values + 1));
+ if (!w)
+ return log_oom();
+
+ d = w;
+ assert_se(hashmap_update(h, json_variant_string(d->name), d) >= 0);
+ } else {
+ _cleanup_(json_variant_unrefp) JsonVariant *n = NULL;
+
+ r = json_variant_new_string(&n, name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate JSON name variant: %m");
+
+ d = malloc0(offsetof(struct json_data, values) + sizeof(JsonVariant*));
+ if (!d)
+ return log_oom();
+
+ r = hashmap_put(h, json_variant_string(n), d);
+ if (r < 0) {
+ free(d);
+ return log_error_errno(r, "Failed to insert JSON name into hashmap: %m");
+ }
+
+ d->name = TAKE_PTR(n);
+ }
+
+ d->values[d->n_values++] = TAKE_PTR(v);
+ return 0;
+}
+
+static int update_json_data_split(
+ Hashmap *h,
+ OutputFlags flags,
+ Set *output_fields,
+ const void *data,
+ size_t size) {
+
+ const char *eq;
+ char *name;
+
+ assert(h);
+ assert(data || size == 0);
+
+ if (memory_startswith(data, size, "_BOOT_ID="))
+ return 0;
+
+ eq = memchr(data, '=', MIN(size, JSON_THRESHOLD));
+ if (!eq)
+ return 0;
+
+ if (eq == data)
+ return 0;
+
+ name = strndupa(data, eq - (const char*) data);
+ if (output_fields && !set_get(output_fields, name))
+ return 0;
+
+ return update_json_data(h, flags, name, eq + 1, size - (eq - (const char*) data) - 1);
+}
+
static int output_json(
FILE *f,
sd_journal *j,
@@ -754,21 +842,23 @@ static int output_json(
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
- uint64_t realtime, monotonic;
+ char sid[SD_ID128_STRING_MAX], usecbuf[DECIMAL_STR_MAX(usec_t)];
+ _cleanup_(json_variant_unrefp) JsonVariant *object = NULL;
_cleanup_free_ char *cursor = NULL;
- const void *data;
- size_t length;
+ uint64_t realtime, monotonic;
+ JsonVariant **array = NULL;
+ struct json_data *d;
sd_id128_t boot_id;
- char sid[33], *k;
- int r;
Hashmap *h = NULL;
- bool done, separator;
+ size_t n = 0;
+ Iterator i;
+ int r;
assert(j);
- sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
+ (void) sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0)
@@ -782,182 +872,106 @@ static int output_json(
if (r < 0)
return log_error_errno(r, "Failed to get cursor: %m");
- if (mode == OUTPUT_JSON_PRETTY)
- fprintf(f,
- "{\n"
- "\t\"__CURSOR\" : \"%s\",\n"
- "\t\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\",\n"
- "\t\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\",\n"
- "\t\"_BOOT_ID\" : \"%s\"",
- cursor,
- realtime,
- monotonic,
- sd_id128_to_string(boot_id, sid));
- else {
- if (mode == OUTPUT_JSON_SSE)
- fputs("data: ", f);
-
- fprintf(f,
- "{ \"__CURSOR\" : \"%s\", "
- "\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\", "
- "\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\", "
- "\"_BOOT_ID\" : \"%s\"",
- cursor,
- realtime,
- monotonic,
- sd_id128_to_string(boot_id, sid));
- }
-
h = hashmap_new(&string_hash_ops);
if (!h)
return log_oom();
- /* First round, iterate through the entry and count how often each field appears */
- JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
- const char *eq;
- char *n;
- unsigned u;
-
- if (memory_startswith(data, length, "_BOOT_ID="))
- continue;
-
- eq = memchr(data, '=', length);
- if (!eq)
- continue;
-
- n = memdup_suffix0(data, eq - (const char*) data);
- if (!n) {
- r = log_oom();
- goto finish;
- }
-
- u = PTR_TO_UINT(hashmap_get(h, n));
- if (u == 0) {
- r = hashmap_put(h, n, UINT_TO_PTR(1));
- if (r < 0) {
- free(n);
- log_oom();
- goto finish;
- }
- } else {
- r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
- free(n);
- if (r < 0) {
- log_oom();
- goto finish;
- }
- }
- }
- if (r == -EBADMSG) {
- log_debug_errno(r, "Skipping message we can't read: %m");
- return 0;
- }
+ r = update_json_data(h, flags, "__CURSOR", cursor, strlen(cursor));
if (r < 0)
- return r;
-
- separator = true;
- do {
- done = true;
-
- SD_JOURNAL_FOREACH_DATA(j, data, length) {
- const char *eq;
- char *kk;
- _cleanup_free_ char *n = NULL;
- size_t m;
- unsigned u;
-
- /* We already printed the boot id from the data in
- * the header, hence let's suppress it here */
- if (memory_startswith(data, length, "_BOOT_ID="))
- continue;
-
- eq = memchr(data, '=', length);
- if (!eq)
- continue;
-
- m = eq - (const char*) data;
- n = memdup_suffix0(data, m);
- if (!n) {
- r = log_oom();
- goto finish;
- }
-
- if (output_fields && !set_get(output_fields, n))
- continue;
-
- if (separator)
- fputs(mode == OUTPUT_JSON_PRETTY ? ",\n\t" : ", ", f);
-
- u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
- if (u == 0)
- /* We already printed this, let's jump to the next */
- separator = false;
-
- else if (u == 1) {
- /* Field only appears once, output it directly */
-
- json_escape(f, data, m, flags);
- fputs(" : ", f);
-
- json_escape(f, eq + 1, length - m - 1, flags);
-
- hashmap_remove(h, n);
- free(kk);
+ goto finish;
- separator = true;
+ xsprintf(usecbuf, USEC_FMT, realtime);
+ r = update_json_data(h, flags, "__REALTIME_TIMESTAMP", usecbuf, strlen(usecbuf));
+ if (r < 0)
+ goto finish;
- } else {
- /* Field appears multiple times, output it as array */
- json_escape(f, data, m, flags);
- fputs(" : [ ", f);
- json_escape(f, eq + 1, length - m - 1, flags);
+ xsprintf(usecbuf, USEC_FMT, monotonic);
+ r = update_json_data(h, flags, "__MONOTONIC_TIMESTAMP", usecbuf, strlen(usecbuf));
+ if (r < 0)
+ goto finish;
- /* Iterate through the end of the list */
+ sd_id128_to_string(boot_id, sid);
+ r = update_json_data(h, flags, "_BOOT_ID", sid, strlen(sid));
+ if (r < 0)
+ goto finish;
- while (sd_journal_enumerate_data(j, &data, &length) > 0) {
- if (length < m + 1)
- continue;
+ for (;;) {
+ const void *data;
+ size_t size;
- if (memcmp(data, n, m) != 0)
- continue;
+ r = sd_journal_enumerate_data(j, &data, &size);
+ if (r == -EBADMSG) {
+ log_debug_errno(r, "Skipping message we can't read: %m");
+ r = 0;
+ goto finish;
+ }
+ if (r < 0) {
+ log_error_errno(r, "Failed to read journal: %m");
+ goto finish;
+ }
+ if (r == 0)
+ break;
- if (((const char*) data)[m] != '=')
- continue;
+ r = update_json_data_split(h, flags, output_fields, data, size);
+ if (r < 0)
+ goto finish;
+ }
- fputs(", ", f);
- json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
- }
+ array = new(JsonVariant*, hashmap_size(h)*2);
+ if (!array) {
+ r = log_oom();
+ goto finish;
+ }
- fputs(" ]", f);
+ HASHMAP_FOREACH(d, h, i) {
+ assert(d->n_values > 0);
- hashmap_remove(h, n);
- free(kk);
+ array[n++] = json_variant_ref(d->name);
- /* Iterate data fields form the beginning */
- done = false;
- separator = true;
+ if (d->n_values == 1)
+ array[n++] = json_variant_ref(d->values[0]);
+ else {
+ _cleanup_(json_variant_unrefp) JsonVariant *q = NULL;
- break;
+ r = json_variant_new_array(&q, d->values, d->n_values);
+ if (r < 0) {
+ log_error_errno(r, "Failed to create JSON array: %m");
+ goto finish;
}
+
+ array[n++] = TAKE_PTR(q);
}
+ }
- } while (!done);
+ r = json_variant_new_object(&object, array, n);
+ if (r < 0) {
+ log_error_errno(r, "Failed to allocate JSON object: %m");
+ goto finish;
+ }
- if (mode == OUTPUT_JSON_PRETTY)
- fputs("\n}\n", f);
- else if (mode == OUTPUT_JSON_SSE)
- fputs("}\n\n", f);
- else
- fputs(" }\n", f);
+ json_variant_dump(object,
+ output_mode_to_json_format_flags(mode) |
+ (FLAGS_SET(flags, OUTPUT_COLOR) ? JSON_FORMAT_COLOR : 0),
+ f, NULL);
r = 0;
finish:
- while ((k = hashmap_steal_first_key(h)))
- free(k);
+ while ((d = hashmap_steal_first(h))) {
+ size_t k;
+
+ json_variant_unref(d->name);
+ for (k = 0; k < d->n_values; k++)
+ json_variant_unref(d->values[k]);
+
+ free(d);
+ }
hashmap_free(h);
+ json_variant_unref_many(array, n);
+ free(array);
+
return r;
}
@@ -968,7 +982,7 @@ static int output_cat(
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
const void *data;
size_t l;
@@ -1018,12 +1032,12 @@ static int output_cat(
static int (*output_funcs[_OUTPUT_MODE_MAX])(
FILE *f,
- sd_journal*j,
+ sd_journal *j,
OutputMode mode,
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) = {
+ const size_t highlight[2]) = {
[OUTPUT_SHORT] = output_short,
[OUTPUT_SHORT_ISO] = output_short,
@@ -1037,6 +1051,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
[OUTPUT_JSON] = output_json,
[OUTPUT_JSON_PRETTY] = output_json,
[OUTPUT_JSON_SSE] = output_json,
+ [OUTPUT_JSON_SEQ] = output_json,
[OUTPUT_CAT] = output_cat,
[OUTPUT_WITH_UNIT] = output_short,
};
@@ -1048,7 +1063,7 @@ int show_journal_entry(
unsigned n_columns,
OutputFlags flags,
char **output_fields,
- size_t highlight[2],
+ const size_t highlight[2],
bool *ellipsized) {
int ret;
@@ -1321,7 +1336,8 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- r = safe_fork("(sd-bootid)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ pidnsfd, mntnsfd, -1, -1, rootfd, &child);
if (r < 0)
return r;
if (r == 0) {
@@ -1329,10 +1345,6 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
pair[0] = safe_close(pair[0]);
- r = namespace_enter(pidnsfd, mntnsfd, -1, -1, rootfd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
_exit(EXIT_FAILURE);
@@ -1351,7 +1363,7 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-bootid)", child, 0);
+ r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
if (r < 0)
return r;
if (r != EXIT_SUCCESS)
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index 002d06af84..1e0c4ea146 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -20,7 +20,7 @@ int show_journal_entry(
unsigned n_columns,
OutputFlags flags,
char **output_fields,
- size_t highlight[2],
+ const size_t highlight[2],
bool *ellipsized);
int show_journal(
FILE *f,
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 00a5eff670..af06ab22e8 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -7,6 +7,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fs.h>
@@ -17,9 +18,9 @@
#include "copy.h"
#include "dirent-util.h"
#include "dissect-image.h"
+#include "env-file.h"
#include "env-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "hashmap.h"
#include "hostname-util.h"
@@ -56,15 +57,8 @@ static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
"/usr/lib/portables\0",
};
-Image *image_unref(Image *i) {
- if (!i)
- return NULL;
-
- assert(i->n_ref > 0);
- i->n_ref--;
-
- if (i->n_ref > 0)
- return NULL;
+static Image *image_free(Image *i) {
+ assert(i);
free(i->name);
free(i->path);
@@ -76,15 +70,9 @@ Image *image_unref(Image *i) {
return mfree(i);
}
-Image *image_ref(Image *i) {
- if (!i)
- return NULL;
-
- assert(i->n_ref > 0);
- i->n_ref++;
-
- return i;
-}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Image, image, image_free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(image_hash_ops, char, string_hash_func, string_compare_func,
+ Image, image_unref);
static char **image_settings_path(Image *image) {
_cleanup_strv_free_ char **l = NULL;
@@ -652,7 +640,7 @@ int image_remove(Image *i) {
case IMAGE_DIRECTORY:
/* Allow deletion of read-only directories */
- (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL);
+ (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL, NULL);
r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
if (r < 0)
return r;
@@ -751,7 +739,7 @@ int image_rename(Image *i, const char *new_name) {
(void) read_attr_path(i->path, &file_attr);
if (file_attr & FS_IMMUTABLE_FL)
- (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL);
+ (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL, NULL);
_fallthrough_;
case IMAGE_SUBVOLUME:
@@ -792,7 +780,7 @@ int image_rename(Image *i, const char *new_name) {
/* Restore the immutable bit, if it was set before */
if (file_attr & FS_IMMUTABLE_FL)
- (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
+ (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
free_and_replace(i->path, new_path);
free_and_replace(i->name, nn);
@@ -942,7 +930,7 @@ int image_read_only(Image *i, bool b) {
a read-only subvolume, but at least something, and
we can read the value back. */
- r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL);
+ r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL, NULL);
if (r < 0)
return r;
@@ -1129,7 +1117,7 @@ int image_read_metadata(Image *i) {
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to chase /etc/machine-info in image %s: %m", i->name);
else if (r >= 0) {
- r = load_env_file_pairs(NULL, path, NULL, &machine_info);
+ r = load_env_file_pairs(NULL, path, &machine_info);
if (r < 0)
log_debug_errno(r, "Failed to parse machine-info data of %s: %m", i->name);
}
diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h
index 2e38522acd..9fd45899c8 100644
--- a/src/shared/machine-image.h
+++ b/src/shared/machine-image.h
@@ -4,6 +4,8 @@
#include <stdbool.h>
#include <stdint.h>
+#include "sd-id128.h"
+
#include "hashmap.h"
#include "lockfile-util.h"
#include "macro.h"
@@ -57,12 +59,7 @@ typedef struct Image {
Image *image_unref(Image *i);
Image *image_ref(Image *i);
-static inline Hashmap* image_hashmap_free(Hashmap *map) {
- return hashmap_free_with_destructor(map, image_unref);
-}
-
DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, image_hashmap_free);
int image_find(ImageClass class, const char *name, Image **ret);
int image_from_path(const char *path, Image **ret);
@@ -111,3 +108,5 @@ static inline bool IMAGE_IS_HOST(const struct Image *i) {
return false;
}
+
+extern const struct hash_ops image_hash_ops;
diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c
index 53c5609f9b..de4f704252 100644
--- a/src/shared/machine-pool.c
+++ b/src/shared/machine-pool.c
@@ -1,46 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
-#include <fcntl.h>
-#include <linux/loop.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/file.h>
-#include <sys/ioctl.h>
-#include <sys/mount.h>
-#include <sys/prctl.h>
-#include <sys/stat.h>
#include <sys/statfs.h>
-#include <sys/statvfs.h>
-#include <unistd.h>
-#include "sd-bus-protocol.h"
-#include "sd-bus.h"
-
-#include "alloc-util.h"
#include "btrfs-util.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "fs-util.h"
#include "label.h"
-#include "lockfile-util.h"
-#include "log.h"
#include "machine-pool.h"
-#include "macro.h"
#include "missing.h"
-#include "mkdir.h"
-#include "mount-util.h"
-#include "parse-util.h"
-#include "path-util.h"
-#include "process-util.h"
-#include "signal-util.h"
#include "stat-util.h"
-#include "string-util.h"
-
-#define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
-#define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
static int check_btrfs(void) {
struct statfs sfs;
@@ -56,338 +23,24 @@ static int check_btrfs(void) {
return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
}
-static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
- _cleanup_free_ char *tmp = NULL;
- _cleanup_close_ int fd = -1;
- struct statvfs ss;
- pid_t pid = 0;
+int setup_machine_directory(sd_bus_error *error) {
int r;
- /* We want to be able to make use of btrfs-specific file
- * system features, in particular subvolumes, reflinks and
- * quota. Hence, if we detect that /var/lib/machines.raw is
- * not located on btrfs, let's create a loopback file, place a
- * btrfs file system into it, and mount it to
- * /var/lib/machines. */
-
- fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
- if (fd >= 0)
- return TAKE_FD(fd);
-
- if (errno != ENOENT)
- return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m");
-
- r = tempfn_xxxxxx("/var/lib/machines.raw", NULL, &tmp);
- if (r < 0)
- return r;
-
- (void) mkdir_p_label("/var/lib", 0755);
- fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600);
- if (fd < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m");
-
- if (fstatvfs(fd, &ss) < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m");
- goto fail;
- }
-
- if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) {
- r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines.");
- goto fail;
- }
-
- if (ftruncate(fd, size) < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m");
- goto fail;
- }
-
- r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid);
- if (r < 0) {
- sd_bus_error_set_errnof(error, r, "Failed to fork mkfs.btrfs: %m");
- goto fail;
- }
- if (r == 0) {
-
- /* Child */
-
- fd = safe_close(fd);
-
- execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
- if (errno == ENOENT)
- _exit(99);
-
- _exit(EXIT_FAILURE);
- }
-
- r = wait_for_terminate_and_check("mkfs", pid, 0);
- pid = 0;
-
- if (r < 0) {
- sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
- goto fail;
- }
- if (r == 99) {
- r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
- goto fail;
- }
- if (r != EXIT_SUCCESS) {
- r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", r);
- goto fail;
- }
-
- r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw");
- if (r < 0) {
- sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m");
- goto fail;
- }
-
- return TAKE_FD(fd);
-
-fail:
- unlink_noerrno(tmp);
-
- if (pid > 1)
- kill_and_sigcont(pid, SIGKILL);
-
- return r;
-}
-
-int setup_machine_directory(uint64_t size, sd_bus_error *error) {
- _cleanup_(release_lock_file) LockFile lock_file = LOCK_FILE_INIT;
- struct loop_info64 info = {
- .lo_flags = LO_FLAGS_AUTOCLEAR,
- };
- _cleanup_close_ int fd = -1, control = -1, loop = -1;
- _cleanup_free_ char* loopdev = NULL;
- char tmpdir[] = "/tmp/machine-pool.XXXXXX", *mntdir = NULL;
- bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
- char buf[FORMAT_BYTES_MAX];
- int r, nr = -1;
-
- /* btrfs cannot handle file systems < 16M, hence use this as minimum */
- if (size == (uint64_t) -1)
- size = VAR_LIB_MACHINES_SIZE_START;
- else if (size < 16*1024*1024)
- size = 16*1024*1024;
-
- /* Make sure we only set the directory up once at a time */
- r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file);
- if (r < 0)
- return r;
-
r = check_btrfs();
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
- if (r > 0) {
- (void) btrfs_subvol_make_label("/var/lib/machines");
-
- r = btrfs_quota_enable("/var/lib/machines", true);
- if (r < 0)
- log_warning_errno(r, "Failed to enable quota for /var/lib/machines, ignoring: %m");
-
- r = btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true);
- if (r < 0)
- log_warning_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m");
-
- return 1;
- }
-
- if (path_is_mount_point("/var/lib/machines", NULL, AT_SYMLINK_FOLLOW) > 0) {
- log_debug("/var/lib/machines is already a mount point, not creating loopback file for it.");
- return 0;
- }
-
- r = dir_is_populated("/var/lib/machines");
- if (r < 0 && r != -ENOENT)
- return r;
- if (r > 0) {
- log_debug("/var/log/machines is already populated, not creating loopback file for it.");
- return 0;
- }
-
- r = mkfs_exists("btrfs");
if (r == 0)
- return sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
- if (r < 0)
- return r;
-
- fd = setup_machine_raw(size, error);
- if (fd < 0)
- return fd;
-
- control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
- if (control < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");
-
- nr = ioctl(control, LOOP_CTL_GET_FREE);
- if (nr < 0)
- return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");
-
- if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
- r = -ENOMEM;
- goto fail;
- }
-
- loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
- if (loop < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
- goto fail;
- }
-
- if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
- goto fail;
- }
-
- if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m");
- goto fail;
- }
-
- /* We need to make sure the new /var/lib/machines directory
- * has an access mode of 0700 at the time it is first made
- * available. mkfs will create it with 0755 however. Hence,
- * let's mount the directory into an inaccessible directory
- * below /tmp first, fix the access mode, and move it to the
- * public place then. */
-
- if (!mkdtemp(tmpdir)) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
- goto fail;
- }
- tmpdir_made = true;
-
- mntdir = strjoina(tmpdir, "/mnt");
- if (mkdir(mntdir, 0700) < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m");
- goto fail;
- }
- mntdir_made = true;
+ return 0;
- if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
- goto fail;
- }
- mntdir_mounted = true;
+ (void) btrfs_subvol_make_label("/var/lib/machines");
- r = btrfs_quota_enable(mntdir, true);
+ r = btrfs_quota_enable("/var/lib/machines", true);
if (r < 0)
- log_warning_errno(r, "Failed to enable quota, ignoring: %m");
+ log_warning_errno(r, "Failed to enable quota for /var/lib/machines, ignoring: %m");
- r = btrfs_subvol_auto_qgroup(mntdir, 0, true);
+ r = btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true);
if (r < 0)
- log_warning_errno(r, "Failed to set up default quota hierarchy, ignoring: %m");
-
- if (chmod(mntdir, 0700) < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
- goto fail;
- }
-
- (void) mkdir_p_label("/var/lib/machines", 0700);
-
- if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m");
- goto fail;
- }
-
- (void) syncfs(fd);
-
- log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size));
-
- (void) umount2(mntdir, MNT_DETACH);
- (void) rmdir(mntdir);
- (void) rmdir(tmpdir);
-
- return 1;
-
-fail:
- if (mntdir_mounted)
- (void) umount2(mntdir, MNT_DETACH);
-
- if (mntdir_made)
- (void) rmdir(mntdir);
- if (tmpdir_made)
- (void) rmdir(tmpdir);
-
- if (loop >= 0) {
- (void) ioctl(loop, LOOP_CLR_FD);
- loop = safe_close(loop);
- }
-
- if (control >= 0 && nr >= 0)
- (void) ioctl(control, LOOP_CTL_REMOVE, nr);
-
- return r;
-}
-
-static int sync_path(const char *p) {
- _cleanup_close_ int fd = -1;
-
- fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return -errno;
-
- if (syncfs(fd) < 0)
- return -errno;
-
- return 0;
-}
-
-int grow_machine_directory(void) {
- char buf[FORMAT_BYTES_MAX];
- struct statvfs a, b;
- uint64_t old_size, new_size, max_add;
- int r;
-
- /* Ensure the disk space data is accurate */
- sync_path("/var/lib/machines");
- sync_path("/var/lib/machines.raw");
-
- if (statvfs("/var/lib/machines.raw", &a) < 0)
- return -errno;
-
- if (statvfs("/var/lib/machines", &b) < 0)
- return -errno;
-
- /* Don't grow if not enough disk space is available on the host */
- if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN)
- return 0;
-
- /* Don't grow if at least 1/3th of the fs is still free */
- if (b.f_bavail > b.f_blocks / 3)
- return 0;
-
- /* Calculate how much we are willing to add at most */
- max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN;
-
- /* Calculate the old size */
- old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize;
-
- /* Calculate the new size as three times the size of what is used right now */
- new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3;
-
- /* Always, grow at least to the start size */
- if (new_size < VAR_LIB_MACHINES_SIZE_START)
- new_size = VAR_LIB_MACHINES_SIZE_START;
-
- /* If the new size is smaller than the old size, don't grow */
- if (new_size < old_size)
- return 0;
-
- /* Ensure we never add more than the maximum */
- if (new_size > old_size + max_add)
- new_size = old_size + max_add;
-
- r = btrfs_resize_loopback("/var/lib/machines", new_size, true);
- if (r <= 0)
- return r;
-
- /* Also bump the quota, of both the subvolume leaf qgroup, as
- * well as of any subtree quota group by the same id but a
- * higher level, if it exists. */
- (void) btrfs_qgroup_set_limit("/var/lib/machines", 0, new_size);
- (void) btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, new_size);
+ log_warning_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m");
- log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf, sizeof(buf), new_size));
return 1;
}
diff --git a/src/shared/machine-pool.h b/src/shared/machine-pool.h
index fd09296f23..6f59a18fb6 100644
--- a/src/shared/machine-pool.h
+++ b/src/shared/machine-pool.h
@@ -5,8 +5,4 @@
#include "sd-bus.h"
-/* Grow the /var/lib/machines directory after each 10MiB written */
-#define GROW_INTERVAL_BYTES (UINT64_C(10) * UINT64_C(1024) * UINT64_C(1024))
-
-int setup_machine_directory(uint64_t size, sd_bus_error *error);
-int grow_machine_directory(void);
+int setup_machine_directory(sd_bus_error *error);
diff --git a/src/shared/main-func.h b/src/shared/main-func.h
new file mode 100644
index 0000000000..3c182e802b
--- /dev/null
+++ b/src/shared/main-func.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdlib.h>
+
+#include "pager.h"
+#include "selinux-util.h"
+#include "spawn-ask-password-agent.h"
+#include "spawn-polkit-agent.h"
+#include "static-destruct.h"
+
+#define _DEFINE_MAIN_FUNCTION(intro, impl, ret) \
+ int main(int argc, char *argv[]) { \
+ int r; \
+ intro; \
+ r = impl; \
+ static_destruct(); \
+ ask_password_agent_close(); \
+ polkit_agent_close(); \
+ mac_selinux_finish(); \
+ pager_close(); \
+ return ret; \
+ }
+
+/* Negative return values from impl are mapped to EXIT_FAILURE, and
+ * everything else means success! */
+#define DEFINE_MAIN_FUNCTION(impl) \
+ _DEFINE_MAIN_FUNCTION(,impl(argc, argv), r < 0 ? EXIT_FAILURE : EXIT_SUCCESS)
+
+/* Zero is mapped to EXIT_SUCCESS, negative values are mapped to EXIT_FAILURE,
+ * and postive values are propagated.
+ * Note: "true" means failure! */
+#define DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(impl) \
+ _DEFINE_MAIN_FUNCTION(,impl(argc, argv), r < 0 ? EXIT_FAILURE : r)
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 54e77e9af6..99d6ba14f1 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -8,24 +8,40 @@ shared_sources = files('''
apparmor-util.h
ask-password-api.c
ask-password-api.h
+ barrier.c
+ barrier.h
base-filesystem.c
base-filesystem.h
+ bitmap.c
+ bitmap.h
+ blkid-util.h
boot-timestamps.c
boot-timestamps.h
bootspec.c
bootspec.h
+ bpf-program.c
+ bpf-program.h
bus-unit-util.c
bus-unit-util.h
bus-util.c
bus-util.h
+ calendarspec.c
+ calendarspec.h
cgroup-show.c
cgroup-show.h
clean-ipc.c
clean-ipc.h
+ clock-util.c
+ clock-util.h
condition.c
condition.h
conf-parser.c
conf-parser.h
+ cpu-set-util.c
+ cpu-set-util.h
+ crypt-util.c
+ crypt-util.h
+ daemon-util.h
dev-setup.c
dev-setup.h
dissect-image.c
@@ -36,25 +52,47 @@ shared_sources = files('''
dropin.h
efivars.c
efivars.h
+ enable-mempool.c
+ env-file-label.c
+ env-file-label.h
+ exec-util.c
+ exec-util.h
+ exit-status.c
+ exit-status.h
fdset.c
fdset.h
+ fileio-label.c
+ fileio-label.h
firewall-util.h
+ format-table.c
+ format-table.h
fstab-util.c
fstab-util.h
generator.c
generator.h
gpt.h
+ id128-print.c
+ id128-print.h
ima-util.c
ima-util.h
import-util.c
import-util.h
initreq.h
- install.c
- install.h
install-printf.c
install-printf.h
+ install.c
+ install.h
+ ip-protocol-list.c
+ ip-protocol-list.h
+ journal-importer.c
+ journal-importer.h
journal-util.c
journal-util.h
+ json-internal.h
+ json.c
+ json.h
+ lockfile-util.c
+ lockfile-util.h
logs-show.c
logs-show.h
loop-util.c
@@ -63,17 +101,35 @@ shared_sources = files('''
machine-image.h
machine-pool.c
machine-pool.h
+ main-func.h
+ module-util.h
+ mount-util.c
+ mount-util.h
+ nscd-flush.c
+ nscd-flush.h
nsflags.c
nsflags.h
+ os-util.c
+ os-util.h
output-mode.c
output-mode.h
+ pager.c
+ pager.h
path-lookup.c
path-lookup.h
+ pretty-print.c
+ pretty-print.h
ptyfwd.c
ptyfwd.h
+ reboot-util.c
+ reboot-util.h
resolve-util.c
resolve-util.h
seccomp-util.h
+ securebits-util.c
+ securebits-util.h
+ serialize.c
+ serialize.h
sleep-config.c
sleep-config.h
spawn-ask-password-agent.c
@@ -86,26 +142,36 @@ shared_sources = files('''
switch-root.h
sysctl-util.c
sysctl-util.h
- tests.c
- tests.h
+ tmpfile-util-label.c
+ tmpfile-util-label.h
tomoyo-util.c
tomoyo-util.h
- udev-util.h
udev-util.c
+ udev-util.h
uid-range.c
uid-range.h
utmp-wtmp.h
+ verbs.c
+ verbs.h
vlan-util.c
vlan-util.h
volatile-util.c
volatile-util.h
watchdog.c
watchdog.h
+ web-util.c
+ web-util.h
wireguard-netlink.h
+ xml.c
+ xml.h
'''.split())
+if get_option('tests') != 'false'
+ shared_sources += files('tests.c', 'tests.h')
+endif
+
test_tables_h = files('test-tables.h')
-shared_sources += [test_tables_h]
+shared_sources += test_tables_h
if conf.get('HAVE_ACL') == 1
shared_sources += files('acl-util.c')
@@ -123,6 +189,50 @@ if conf.get('HAVE_LIBIPTC') == 1
shared_sources += files('firewall-util.c')
endif
+if conf.get('HAVE_KMOD') == 1
+ shared_sources += files('module-util.c')
+endif
+
+generate_ip_protocol_list = find_program('generate-ip-protocol-list.sh')
+ip_protocol_list_txt = custom_target(
+ 'ip-protocol-list.txt',
+ output : 'ip-protocol-list.txt',
+ command : [generate_ip_protocol_list, cpp],
+ capture : true)
+
+fname = 'ip-protocol-from-name.gperf'
+gperf_file = custom_target(
+ fname,
+ input : ip_protocol_list_txt,
+ output : fname,
+ command : [generate_gperfs, 'ip_protocol', 'IPPROTO_', '@INPUT@'],
+ capture : true)
+
+fname = 'ip-protocol-from-name.h'
+target1 = custom_target(
+ fname,
+ input : gperf_file,
+ output : fname,
+ command : [gperf,
+ '-L', 'ANSI-C', '-t', '--ignore-case',
+ '-N', 'lookup_ip_protocol',
+ '-H', 'hash_ip_protocol_name',
+ '-p', '-C',
+ '@INPUT@'],
+ capture : true)
+
+fname = 'ip-protocol-to-name.h'
+awkscript = 'ip-protocol-to-name.awk'
+target2 = custom_target(
+ fname,
+ input : [awkscript, ip_protocol_list_txt],
+ output : fname,
+ command : [awk, '-f', '@INPUT0@', '@INPUT1@'],
+ capture : true)
+
+shared_generated_gperf_headers = [target1, target2]
+shared_sources += shared_generated_gperf_headers
+
libshared_name = 'systemd-shared-@0@'.format(meson.project_version())
libshared_deps = [threads,
@@ -132,6 +242,8 @@ libshared_deps = [threads,
libcryptsetup,
libgcrypt,
libiptc,
+ libkmod,
+ libmount,
libseccomp,
libselinux,
libidn,
diff --git a/src/shared/module-util.c b/src/shared/module-util.c
new file mode 100644
index 0000000000..a34fe8fb95
--- /dev/null
+++ b/src/shared/module-util.c
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+
+#include "module-util.h"
+
+int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) {
+ const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
+ struct kmod_list *itr;
+ _cleanup_(kmod_module_unref_listp) struct kmod_list *modlist = NULL;
+ int r = 0;
+
+ /* verbose==true means we should log at non-debug level if we
+ * fail to find or load the module. */
+
+ log_debug("Loading module: %s", module);
+
+ 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);
+
+ if (!modlist) {
+ log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
+ "Failed to find module '%s'", module);
+ return -ENOENT;
+ }
+
+ kmod_list_foreach(itr, modlist) {
+ _cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
+ int state, err;
+
+ mod = kmod_module_get_module(itr);
+ state = kmod_module_get_initstate(mod);
+
+ switch (state) {
+ case KMOD_MODULE_BUILTIN:
+ log_full(verbose ? LOG_INFO : LOG_DEBUG,
+ "Module '%s' is builtin", kmod_module_get_name(mod));
+ break;
+
+ case KMOD_MODULE_LIVE:
+ log_debug("Module '%s' is already loaded", kmod_module_get_name(mod));
+ break;
+
+ default:
+ err = kmod_module_probe_insert_module(mod, probe_flags,
+ NULL, NULL, NULL, NULL);
+ if (err == 0)
+ log_full(verbose ? LOG_INFO : LOG_DEBUG,
+ "Inserted module '%s'", kmod_module_get_name(mod));
+ else if (err == KMOD_PROBE_APPLY_BLACKLIST)
+ log_full(verbose ? LOG_INFO : LOG_DEBUG,
+ "Module '%s' is blacklisted", kmod_module_get_name(mod));
+ else {
+ assert(err < 0);
+
+ log_full_errno(!verbose ? LOG_DEBUG :
+ err == -ENODEV ? LOG_NOTICE :
+ err == -ENOENT ? LOG_WARNING :
+ LOG_ERR,
+ err,
+ "Failed to insert module '%s': %m",
+ kmod_module_get_name(mod));
+ if (!IN_SET(err, -ENODEV, -ENOENT))
+ r = err;
+ }
+ }
+ }
+
+ return r;
+}
diff --git a/src/basic/module-util.h b/src/shared/module-util.h
index 8fa121ed98..c386c5b459 100644
--- a/src/basic/module-util.h
+++ b/src/shared/module-util.h
@@ -8,3 +8,5 @@
DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_ctx*, kmod_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_module*, kmod_module_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_list*, kmod_module_unref_list);
+
+int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose);
diff --git a/src/basic/mount-util.c b/src/shared/mount-util.c
index ebe41a4c6c..9fa995f693 100644
--- a/src/basic/mount-util.c
+++ b/src/shared/mount-util.c
@@ -20,6 +20,7 @@
#include "fs-util.h"
#include "hashmap.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "set.h"
@@ -27,283 +28,6 @@
#include "string-util.h"
#include "strv.h"
-/* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
- * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
- * is recompiled then it would cease working on old kernels, as those refuse any sizes larger than this value with
- * EINVAL right-away. Hence, let's disconnect ourselves from any such API changes, and stick to the original definition
- * from when it was introduced. We use it as a start value only anyway (see below), and hence should be able to deal
- * with large file handles anyway. */
-#define ORIGINAL_MAX_HANDLE_SZ 128
-
-int name_to_handle_at_loop(
- int fd,
- const char *path,
- struct file_handle **ret_handle,
- int *ret_mnt_id,
- int flags) {
-
- _cleanup_free_ struct file_handle *h = NULL;
- size_t n = ORIGINAL_MAX_HANDLE_SZ;
-
- /* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
- * buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
- * start value, it is not an upper bound on the buffer size required.
- *
- * This improves on raw name_to_handle_at() also in one other regard: ret_handle and ret_mnt_id can be passed
- * as NULL if there's no interest in either. */
-
- for (;;) {
- int mnt_id = -1;
-
- h = malloc0(offsetof(struct file_handle, f_handle) + n);
- if (!h)
- return -ENOMEM;
-
- h->handle_bytes = n;
-
- if (name_to_handle_at(fd, path, h, &mnt_id, flags) >= 0) {
-
- if (ret_handle)
- *ret_handle = TAKE_PTR(h);
-
- if (ret_mnt_id)
- *ret_mnt_id = mnt_id;
-
- return 0;
- }
- if (errno != EOVERFLOW)
- return -errno;
-
- if (!ret_handle && ret_mnt_id && mnt_id >= 0) {
-
- /* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the
- * buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to
- * be filled in, and the caller was interested in only the mount ID an nothing else. */
-
- *ret_mnt_id = mnt_id;
- return 0;
- }
-
- /* If name_to_handle_at() didn't increase the byte size, then this EOVERFLOW is caused by something
- * else (apparently EOVERFLOW is returned for untriggered nfs4 mounts sometimes), not by the too small
- * buffer. In that case propagate EOVERFLOW */
- if (h->handle_bytes <= n)
- return -EOVERFLOW;
-
- /* The buffer was too small. Size the new buffer by what name_to_handle_at() returned. */
- n = h->handle_bytes;
- if (offsetof(struct file_handle, f_handle) + n < n) /* check for addition overflow */
- return -EOVERFLOW;
-
- h = mfree(h);
- }
-}
-
-static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
- char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
- _cleanup_free_ char *fdinfo = NULL;
- _cleanup_close_ int subfd = -1;
- char *p;
- int r;
-
- if ((flags & AT_EMPTY_PATH) && isempty(filename))
- xsprintf(path, "/proc/self/fdinfo/%i", fd);
- else {
- subfd = openat(fd, filename, O_CLOEXEC|O_PATH);
- if (subfd < 0)
- return -errno;
-
- xsprintf(path, "/proc/self/fdinfo/%i", subfd);
- }
-
- r = read_full_file(path, &fdinfo, NULL);
- if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
- return -EOPNOTSUPP;
- if (r < 0)
- return r;
-
- p = startswith(fdinfo, "mnt_id:");
- if (!p) {
- p = strstr(fdinfo, "\nmnt_id:");
- if (!p) /* The mnt_id field is a relatively new addition */
- return -EOPNOTSUPP;
-
- p += 8;
- }
-
- p += strspn(p, WHITESPACE);
- p[strcspn(p, WHITESPACE)] = 0;
-
- return safe_atoi(p, mnt_id);
-}
-
-int fd_is_mount_point(int fd, const char *filename, int flags) {
- _cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
- int mount_id = -1, mount_id_parent = -1;
- bool nosupp = false, check_st_dev = true;
- struct stat a, b;
- int r;
-
- assert(fd >= 0);
- assert(filename);
-
- /* First we will try the name_to_handle_at() syscall, which
- * tells us the mount id and an opaque file "handle". It is
- * not supported everywhere though (kernel compile-time
- * option, not all file systems are hooked up). If it works
- * the mount id is usually good enough to tell us whether
- * something is a mount point.
- *
- * If that didn't work we will try to read the mount id from
- * /proc/self/fdinfo/<fd>. This is almost as good as
- * name_to_handle_at(), however, does not return the
- * opaque file handle. The opaque file handle is pretty useful
- * to detect the root directory, which we should always
- * consider a mount point. Hence we use this only as
- * fallback. Exporting the mnt_id in fdinfo is a pretty recent
- * kernel addition.
- *
- * As last fallback we do traditional fstat() based st_dev
- * comparisons. This is how things were traditionally done,
- * but unionfs breaks this since it exposes file
- * systems with a variety of st_dev reported. Also, btrfs
- * subvolumes have different st_dev, even though they aren't
- * real mounts of their own. */
-
- r = name_to_handle_at_loop(fd, filename, &h, &mount_id, flags);
- if (IN_SET(r, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL))
- /* This kernel does not support name_to_handle_at() at all (ENOSYS), or the syscall was blocked
- * (EACCES/EPERM; maybe through seccomp, because we are running inside of a container?), or the mount
- * point is not triggered yet (EOVERFLOW, think nfs4), or some general name_to_handle_at() flakiness
- * (EINVAL): fall back to simpler logic. */
- goto fallback_fdinfo;
- else if (r == -EOPNOTSUPP)
- /* This kernel or file system does not support name_to_handle_at(), hence let's see if the upper fs
- * supports it (in which case it is a mount point), otherwise fallback to the traditional stat()
- * logic */
- nosupp = true;
- else if (r < 0)
- return r;
-
- r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
- if (r == -EOPNOTSUPP) {
- if (nosupp)
- /* Neither parent nor child do name_to_handle_at()? We have no choice but to fall back. */
- goto fallback_fdinfo;
- else
- /* The parent can't do name_to_handle_at() but the directory we are interested in can? If so,
- * it must be a mount point. */
- return 1;
- } else if (r < 0)
- return r;
-
- /* The parent can do name_to_handle_at() but the
- * directory we are interested in can't? If so, it
- * must be a mount point. */
- if (nosupp)
- return 1;
-
- /* If the file handle for the directory we are
- * interested in and its parent are identical, we
- * assume this is the root directory, which is a mount
- * point. */
-
- if (h->handle_bytes == h_parent->handle_bytes &&
- h->handle_type == h_parent->handle_type &&
- memcmp(h->f_handle, h_parent->f_handle, h->handle_bytes) == 0)
- return 1;
-
- return mount_id != mount_id_parent;
-
-fallback_fdinfo:
- r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
- if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
- goto fallback_fstat;
- if (r < 0)
- return r;
-
- r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
- if (r < 0)
- return r;
-
- if (mount_id != mount_id_parent)
- return 1;
-
- /* Hmm, so, the mount ids are the same. This leaves one
- * special case though for the root file system. For that,
- * let's see if the parent directory has the same inode as we
- * are interested in. Hence, let's also do fstat() checks now,
- * too, but avoid the st_dev comparisons, since they aren't
- * that useful on unionfs mounts. */
- check_st_dev = false;
-
-fallback_fstat:
- /* yay for fstatat() taking a different set of flags than the other
- * _at() above */
- if (flags & AT_SYMLINK_FOLLOW)
- flags &= ~AT_SYMLINK_FOLLOW;
- else
- flags |= AT_SYMLINK_NOFOLLOW;
- if (fstatat(fd, filename, &a, flags) < 0)
- return -errno;
-
- if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
- return -errno;
-
- /* A directory with same device and inode as its parent? Must
- * be the root directory */
- if (a.st_dev == b.st_dev &&
- a.st_ino == b.st_ino)
- return 1;
-
- return check_st_dev && (a.st_dev != b.st_dev);
-}
-
-/* flags can be AT_SYMLINK_FOLLOW or 0 */
-int path_is_mount_point(const char *t, const char *root, int flags) {
- _cleanup_free_ char *canonical = NULL, *parent = NULL;
- _cleanup_close_ int fd = -1;
- int r;
-
- assert(t);
- assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
-
- if (path_equal(t, "/"))
- return 1;
-
- /* we need to resolve symlinks manually, we can't just rely on
- * fd_is_mount_point() to do that for us; if we have a structure like
- * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
- * look at needs to be /usr, not /. */
- if (flags & AT_SYMLINK_FOLLOW) {
- r = chase_symlinks(t, root, CHASE_TRAIL_SLASH, &canonical);
- if (r < 0)
- return r;
-
- t = canonical;
- }
-
- parent = dirname_malloc(t);
- if (!parent)
- return -ENOMEM;
-
- fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH);
- if (fd < 0)
- return -errno;
-
- return fd_is_mount_point(fd, last_path_component(t), flags);
-}
-
-int path_get_mnt_id(const char *path, int *ret) {
- int r;
-
- r = name_to_handle_at_loop(AT_FDCWD, path, NULL, ret, 0);
- if (IN_SET(r, -EOPNOTSUPP, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL)) /* kernel/fs don't support this, or seccomp blocks access, or untriggered mount, or name_to_handle_at() is flaky */
- return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret);
-
- return r;
-}
-
int umount_recursive(const char *prefix, int flags) {
bool again;
int n = 0, r;
@@ -475,7 +199,7 @@ int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **bl
if (path_startswith(p, *i)) {
blacklisted = true;
- log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned);
+ log_debug("Not remounting %s blacklisted by %s, called for %s", p, *i, cleaned);
break;
}
}
@@ -521,7 +245,7 @@ int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **bl
(void) get_mount_flags(cleaned, &orig_flags);
orig_flags &= ~MS_RDONLY;
- if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
+ if (mount(NULL, cleaned, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
return -errno;
log_debug("Made top-level directory %s a mount point.", prefix);
@@ -547,6 +271,18 @@ int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **bl
r = path_is_mount_point(x, NULL, 0);
if (IN_SET(r, 0, -ENOENT))
continue;
+ if (IN_SET(r, -EACCES, -EPERM)) {
+ /* Even if root user invoke this, submounts under private FUSE or NFS mount points
+ * may not be acceessed. E.g.,
+ *
+ * $ bindfs --no-allow-other ~/mnt/mnt ~/mnt/mnt
+ * $ bindfs --no-allow-other ~/mnt ~/mnt
+ *
+ * Then, root user cannot access the mount point ~/mnt/mnt.
+ * In such cases, the submounts are ignored, as we have no way to manage them. */
+ log_debug_errno(r, "Failed to determine '%s' is mount point or not, ignoring: %m", x);
+ continue;
+ }
if (r < 0)
return r;
@@ -593,86 +329,6 @@ int mount_move_root(const char *path) {
return 0;
}
-bool fstype_is_network(const char *fstype) {
- const char *x;
-
- x = startswith(fstype, "fuse.");
- if (x)
- fstype = x;
-
- return STR_IN_SET(fstype,
- "afs",
- "cifs",
- "smbfs",
- "sshfs",
- "ncpfs",
- "ncp",
- "nfs",
- "nfs4",
- "gfs",
- "gfs2",
- "glusterfs",
- "pvfs2", /* OrangeFS */
- "ocfs2",
- "lustre");
-}
-
-bool fstype_is_api_vfs(const char *fstype) {
- return STR_IN_SET(fstype,
- "autofs",
- "bpf",
- "cgroup",
- "cgroup2",
- "configfs",
- "cpuset",
- "debugfs",
- "devpts",
- "devtmpfs",
- "efivarfs",
- "fusectl",
- "hugetlbfs",
- "mqueue",
- "proc",
- "pstore",
- "ramfs",
- "securityfs",
- "sysfs",
- "tmpfs",
- "tracefs");
-}
-
-bool fstype_is_ro(const char *fstype) {
- /* All Linux file systems that are necessarily read-only */
- return STR_IN_SET(fstype,
- "DM_verity_hash",
- "iso9660",
- "squashfs");
-}
-
-bool fstype_can_discard(const char *fstype) {
- return STR_IN_SET(fstype,
- "btrfs",
- "ext4",
- "vfat",
- "xfs");
-}
-
-bool fstype_can_uid_gid(const char *fstype) {
-
- /* All file systems that have a uid=/gid= mount option that fixates the owners of all files and directories,
- * current and future. */
-
- return STR_IN_SET(fstype,
- "adfs",
- "fat",
- "hfs",
- "hpfs",
- "iso9660",
- "msdos",
- "ntfs",
- "vfat");
-}
-
int repeat_unmount(const char *path, int flags) {
bool done = false;
@@ -832,8 +488,8 @@ int mount_verbose(
strna(type), where, strnull(fl), strempty(o));
if (mount(what, where, type, f, o) < 0)
return log_full_errno(error_log_level, errno,
- "Failed to mount %s on %s (%s \"%s\"): %m",
- strna(type), where, strnull(fl), strempty(o));
+ "Failed to mount %s (type %s) on %s (%s \"%s\"): %m",
+ strna(what), strna(type), where, strnull(fl), strempty(o));
return 0;
}
@@ -844,37 +500,6 @@ int umount_verbose(const char *what) {
return 0;
}
-const char *mount_propagation_flags_to_string(unsigned long flags) {
-
- switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
- case 0:
- return "";
- case MS_SHARED:
- return "shared";
- case MS_SLAVE:
- return "slave";
- case MS_PRIVATE:
- return "private";
- }
-
- return NULL;
-}
-
-int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
-
- if (isempty(name))
- *ret = 0;
- else if (streq(name, "shared"))
- *ret = MS_SHARED;
- else if (streq(name, "slave"))
- *ret = MS_SLAVE;
- else if (streq(name, "private"))
- *ret = MS_PRIVATE;
- else
- return -EINVAL;
- return 0;
-}
-
int mount_option_mangle(
const char *options,
unsigned long mount_flags,
diff --git a/src/basic/mount-util.h b/src/shared/mount-util.h
index 3cfea3bb20..00df1b0e55 100644
--- a/src/basic/mount-util.h
+++ b/src/shared/mount-util.h
@@ -1,25 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-#include <fcntl.h>
#include <mntent.h>
-#include <stdbool.h>
#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include "macro.h"
-#include "missing.h"
-
-int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
-
-int path_get_mnt_id(const char *path, int *ret);
-
-int fd_is_mount_point(int fd, const char *filename, int flags);
-int path_is_mount_point(const char *path, const char *root, int flags);
int repeat_unmount(const char *path, int flags);
-
int umount_recursive(const char *target, int flags);
int bind_remount_recursive(const char *prefix, bool ro, char **blacklist);
int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **blacklist, FILE *proc_self_mountinfo);
@@ -29,14 +16,6 @@ int mount_move_root(const char *path);
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
#define _cleanup_endmntent_ _cleanup_(endmntentp)
-bool fstype_is_network(const char *fstype);
-bool fstype_is_api_vfs(const char *fstype);
-bool fstype_is_ro(const char *fsype);
-bool fstype_can_discard(const char *fstype);
-bool fstype_can_uid_gid(const char *fstype);
-
-const char* mode_to_inaccessible_node(mode_t mode);
-
int mount_verbose(
int error_log_level,
const char *what,
@@ -46,11 +25,10 @@ int mount_verbose(
const char *options);
int umount_verbose(const char *where);
-const char *mount_propagation_flags_to_string(unsigned long flags);
-int mount_propagation_flags_from_string(const char *name, unsigned long *ret);
-
int mount_option_mangle(
const char *options,
unsigned long mount_flags,
unsigned long *ret_mount_flags,
char **ret_remaining_options);
+
+const char* mode_to_inaccessible_node(mode_t mode);
diff --git a/src/shared/nscd-flush.c b/src/shared/nscd-flush.c
new file mode 100644
index 0000000000..5a04468d2c
--- /dev/null
+++ b/src/shared/nscd-flush.c
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <sys/poll.h>
+
+#include "fd-util.h"
+#include "io-util.h"
+#include "nscd-flush.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "time-util.h"
+
+#define NSCD_FLUSH_CACHE_TIMEOUT_USEC (5*USEC_PER_SEC)
+
+struct nscdInvalidateRequest {
+ int32_t version;
+ int32_t type; /* in glibc this is an enum. We don't replicate this here 1:1. Also, wtf, how unportable is that
+ * even? */
+ int32_t key_len;
+ char dbname[];
+};
+
+static const union sockaddr_union nscd_sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/nscd/socket",
+};
+
+static int nscd_flush_cache_one(const char *database, usec_t end) {
+ size_t req_size, has_written = 0, has_read = 0, l;
+ struct nscdInvalidateRequest *req;
+ _cleanup_close_ int fd = -1;
+ int32_t resp;
+ int events;
+
+ assert(database);
+
+ l = strlen(database);
+ req_size = offsetof(struct nscdInvalidateRequest, dbname) + l + 1;
+
+ req = alloca(req_size);
+ *req = (struct nscdInvalidateRequest) {
+ .version = 2,
+ .type = 10,
+ .key_len = l + 1,
+ };
+
+ strcpy(req->dbname, database);
+
+ fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return log_debug_errno(errno, "Failed to allocate nscd socket: %m");
+
+ /* Note: connect() returns EINPROGRESS if O_NONBLOCK is set and establishing a connection takes time. The
+ * kernel lets us know this way that the connection is now being established, and we should watch with poll()
+ * to learn when it is fully established. That said, AF_UNIX on Linux never triggers this IRL (connect() is
+ * always instant on AF_UNIX), hence handling this is mostly just an excercise in defensive, protocol-agnostic
+ * programming.
+ *
+ * connect() returns EAGAIN if the socket's backlog limit has been reached. When we see this we give up right
+ * away, after all this entire function here is written in a defensive style so that a non-responding nscd
+ * doesn't stall us for good. (Even if we wanted to handle this better: the Linux kernel doesn't really have a
+ * nice way to connect() to a server synchronously with a time limit that would also cover dealing with the
+ * backlog limit. After all SO_RCVTIMEO and SR_SNDTIMEO don't apply to connect(), and alarm() is frickin' ugly
+ * and not really reasonably usable from threads-aware code.) */
+ if (connect(fd, &nscd_sa.sa, SOCKADDR_UN_LEN(nscd_sa.un)) < 0) {
+ if (errno == EAGAIN)
+ return log_debug_errno(errno, "nscd is overloaded (backlog limit reached) and refuses to take further connections: %m");
+ if (errno != EINPROGRESS)
+ return log_debug_errno(errno, "Failed to connect to nscd socket: %m");
+
+ /* Continue in case of EINPROGRESS, but don't bother with send() or recv() until being notified that
+ * establishing the connection is complete. */
+ events = 0;
+ } else
+ events = POLLIN|POLLOUT; /* Let's assume initially that we can write and read to the fd, to suppress
+ * one poll() invocation */
+ for (;;) {
+ usec_t p;
+
+ if (events & POLLOUT) {
+ ssize_t m;
+
+ assert(has_written < req_size);
+
+ m = send(fd, (uint8_t*) req + has_written, req_size - has_written, MSG_NOSIGNAL);
+ if (m < 0) {
+ if (errno != EAGAIN) /* Note that EAGAIN is returned by the kernel whenever it can't
+ * take the data right now, and that includes if the connect() is
+ * asynchronous and we saw EINPROGRESS on it, and it hasn't
+ * completed yet. */
+ return log_debug_errno(errno, "Failed to write to nscd socket: %m");
+ } else
+ has_written += m;
+ }
+
+ if (events & (POLLIN|POLLERR|POLLHUP)) {
+ ssize_t m;
+
+ if (has_read >= sizeof(resp))
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Response from nscd longer than expected: %m");
+
+ m = recv(fd, (uint8_t*) &resp + has_read, sizeof(resp) - has_read, 0);
+ if (m < 0) {
+ if (errno != EAGAIN)
+ return log_debug_errno(errno, "Failed to read from nscd socket: %m");
+ } else if (m == 0) { /* EOF */
+ if (has_read == 0 && has_written >= req_size) /* Older nscd immediately terminated the
+ * connection, accept that as OK */
+ return 1;
+
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "nscd prematurely ended connection.");
+ } else
+ has_read += m;
+ }
+
+ if (has_written >= req_size && has_read >= sizeof(resp)) { /* done? */
+ if (resp < 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "nscd sent us a negative error numer: %i", resp);
+ if (resp > 0)
+ return log_debug_errno(resp, "nscd return failure code on invalidating '%s'.", database);
+ return 1;
+ }
+
+ p = now(CLOCK_MONOTONIC);
+ if (p >= end)
+ return -ETIMEDOUT;
+
+ events = fd_wait_for_event(fd, POLLIN | (has_written < req_size ? POLLOUT : 0), end - p);
+ if (events < 0)
+ return events;
+ }
+}
+
+int nscd_flush_cache(char **databases) {
+ usec_t end;
+ int r = 0;
+ char **i;
+
+ /* Tries to invalidate the specified database in nscd. We do this carefully, with a 5s time-out, so that we
+ * don't block indefinitely on another service. */
+
+ end = usec_add(now(CLOCK_MONOTONIC), NSCD_FLUSH_CACHE_TIMEOUT_USEC);
+
+ STRV_FOREACH(i, databases) {
+ int k;
+
+ k = nscd_flush_cache_one(*i, end);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
+
+ return r;
+}
diff --git a/src/shared/nscd-flush.h b/src/shared/nscd-flush.h
new file mode 100644
index 0000000000..22774bf4b7
--- /dev/null
+++ b/src/shared/nscd-flush.h
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+int nscd_flush_cache(char **databases);
diff --git a/src/shared/nsflags.c b/src/shared/nsflags.c
index a5beb9200f..8cc2d0873d 100644
--- a/src/shared/nsflags.c
+++ b/src/shared/nsflags.c
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <sched.h>
+#include <errno.h>
#include "alloc-util.h"
#include "extract-word.h"
diff --git a/src/shared/nsflags.h b/src/shared/nsflags.h
index 7cc26a441d..0aeb0bc891 100644
--- a/src/shared/nsflags.h
+++ b/src/shared/nsflags.h
@@ -1,9 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-#include <sched.h>
-
-#include "missing.h"
+#include "missing_sched.h"
/* The combination of all namespace flags defined by the kernel. The right type for this isn't clear. setns() and
* unshare() expect these flags to be passed as (signed) "int", while clone() wants them as "unsigned long". The latter
diff --git a/src/basic/os-util.c b/src/shared/os-util.c
index 207594cef8..b2d5ce32e7 100644
--- a/src/basic/os-util.c
+++ b/src/shared/os-util.c
@@ -1,13 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
+#include "env-file.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
#include "os-util.h"
-#include "strv.h"
-#include "fileio.h"
#include "string-util.h"
+#include "strv.h"
int path_is_os_tree(const char *path) {
int r;
@@ -74,7 +74,7 @@ int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) {
if (r < 0)
return r;
- f = fdopen(fd, "re");
+ f = fdopen(fd, "r");
if (!f)
return -errno;
fd = -1;
@@ -98,7 +98,7 @@ int parse_os_release(const char *root, ...) {
return r;
va_start(ap, root);
- r = parse_env_filev(f, p, NEWLINE, ap);
+ r = parse_env_filev(f, p, ap);
va_end(ap);
return r;
@@ -113,5 +113,5 @@ int load_os_release_pairs(const char *root, char ***ret) {
if (r < 0)
return r;
- return load_env_file_pairs(f, p, NEWLINE, ret);
+ return load_env_file_pairs(f, p, ret);
}
diff --git a/src/basic/os-util.h b/src/shared/os-util.h
index 6b9e033941..27ec7ac8d7 100644
--- a/src/basic/os-util.h
+++ b/src/shared/os-util.h
@@ -8,5 +8,5 @@ int path_is_os_tree(const char *path);
int open_os_release(const char *root, char **ret_path, int *ret_fd);
int fopen_os_release(const char *root, char **ret_path, FILE **ret_file);
-int parse_os_release(const char *root, ...);
+int parse_os_release(const char *root, ...) _sentinel_;
int load_os_release_pairs(const char *root, char ***ret);
diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c
index bb33ba3d10..107b345538 100644
--- a/src/shared/output-mode.c
+++ b/src/shared/output-mode.c
@@ -3,6 +3,24 @@
#include "output-mode.h"
#include "string-table.h"
+JsonFormatFlags output_mode_to_json_format_flags(OutputMode m) {
+
+ switch (m) {
+
+ case OUTPUT_JSON_SSE:
+ return JSON_FORMAT_SSE;
+
+ case OUTPUT_JSON_SEQ:
+ return JSON_FORMAT_SEQ;
+
+ case OUTPUT_JSON_PRETTY:
+ return JSON_FORMAT_PRETTY;
+
+ default:
+ return JSON_FORMAT_NEWLINE;
+ }
+}
+
static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
[OUTPUT_SHORT] = "short",
[OUTPUT_SHORT_FULL] = "short-full",
@@ -16,6 +34,7 @@ static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
[OUTPUT_JSON] = "json",
[OUTPUT_JSON_PRETTY] = "json-pretty",
[OUTPUT_JSON_SSE] = "json-sse",
+ [OUTPUT_JSON_SEQ] = "json-seq",
[OUTPUT_CAT] = "cat",
[OUTPUT_WITH_UNIT] = "with-unit",
};
diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h
index fe3903b3c5..00b6032056 100644
--- a/src/shared/output-mode.h
+++ b/src/shared/output-mode.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include "json.h"
#include "macro.h"
typedef enum OutputMode {
@@ -16,12 +17,17 @@ typedef enum OutputMode {
OUTPUT_JSON,
OUTPUT_JSON_PRETTY,
OUTPUT_JSON_SSE,
+ OUTPUT_JSON_SEQ,
OUTPUT_CAT,
OUTPUT_WITH_UNIT,
_OUTPUT_MODE_MAX,
_OUTPUT_MODE_INVALID = -1
} OutputMode;
+static inline bool OUTPUT_MODE_IS_JSON(OutputMode m) {
+ return IN_SET(m, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ);
+}
+
/* The output flags definitions are shared by the logs and process tree output. Some apply to both, some only to the
* logs output, others only to the process tree output. */
@@ -38,5 +44,7 @@ typedef enum OutputFlags {
OUTPUT_NO_HOSTNAME = 1 << 9,
} OutputFlags;
+JsonFormatFlags output_mode_to_json_format_flags(OutputMode m);
+
const char* output_mode_to_string(OutputMode m) _const_;
OutputMode output_mode_from_string(const char *s) _pure_;
diff --git a/src/basic/pager.c b/src/shared/pager.c
index f241261119..ce4ca9bdb2 100644
--- a/src/basic/pager.c
+++ b/src/shared/pager.c
@@ -12,11 +12,14 @@
#include "copy.h"
#include "fd-util.h"
+#include "fileio.h"
+#include "io-util.h"
#include "locale-util.h"
#include "log.h"
#include "macro.h"
#include "pager.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
@@ -41,12 +44,50 @@ _noreturn_ static void pager_fallback(void) {
_exit(EXIT_SUCCESS);
}
-int pager_open(bool no_pager, bool jump_to_end) {
- _cleanup_close_pair_ int fd[2] = { -1, -1 };
- const char *pager;
+static int no_quit_on_interrupt(int exe_name_fd, const char *less_opts) {
+ _cleanup_fclose_ FILE *file = NULL;
+ _cleanup_free_ char *line = NULL;
int r;
- if (no_pager)
+ assert(exe_name_fd >= 0);
+ assert(less_opts);
+
+ /* This takes ownership of exe_name_fd */
+ file = fdopen(exe_name_fd, "r");
+ if (!file) {
+ safe_close(exe_name_fd);
+ return log_error_errno(errno, "Failed to create FILE object: %m");
+ }
+
+ /* Find the last line */
+ for (;;) {
+ _cleanup_free_ char *t = NULL;
+
+ r = read_line(file, LONG_LINE_MAX, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read from socket: %m");
+ if (r == 0)
+ break;
+
+ free_and_replace(line, t);
+ }
+
+ /* We only treat "less" specially.
+ * Return true whenever option K is *not* set. */
+ r = streq_ptr(line, "less") && !strchr(less_opts, 'K');
+
+ log_debug("Pager executable is \"%s\", options \"%s\", quit_on_interrupt: %s",
+ strnull(line), less_opts, yes_no(!r));
+ return r;
+}
+
+int pager_open(PagerFlags flags) {
+ _cleanup_close_pair_ int fd[2] = { -1, -1 }, exe_name_pipe[2] = { -1, -1 };
+ _cleanup_strv_free_ char **pager_args = NULL;
+ const char *pager, *less_opts;
+ int r;
+
+ if (flags & PAGER_DISABLE)
return 0;
if (pager_pid > 0)
@@ -56,15 +97,21 @@ int pager_open(bool no_pager, bool jump_to_end) {
return 0;
if (!is_main_thread())
- return -EPERM;
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Pager invoked from wrong thread.");
pager = getenv("SYSTEMD_PAGER");
if (!pager)
pager = getenv("PAGER");
- /* If the pager is explicitly turned off, honour it */
- if (pager && STR_IN_SET(pager, "", "cat"))
- return 0;
+ if (pager) {
+ pager_args = strv_split(pager, WHITESPACE);
+ if (!pager_args)
+ return log_oom();
+
+ /* If the pager is explicitly turned off, honour it */
+ if (strv_isempty(pager_args) || strv_equal(pager_args, STRV_MAKE("cat")))
+ return 0;
+ }
/* Determine and cache number of columns/lines before we spawn the pager so that we get the value from the
* actual tty */
@@ -74,25 +121,36 @@ int pager_open(bool no_pager, bool jump_to_end) {
if (pipe2(fd, O_CLOEXEC) < 0)
return log_error_errno(errno, "Failed to create pager pipe: %m");
- r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pager_pid);
+ /* This is a pipe to feed the name of the executed pager binary into the parent */
+ if (pipe2(exe_name_pipe, O_CLOEXEC) < 0)
+ return log_error_errno(errno, "Failed to create exe_name pipe: %m");
+
+ /* Initialize a good set of less options */
+ less_opts = getenv("SYSTEMD_LESS");
+ if (!less_opts)
+ less_opts = "FRSXMK";
+ if (flags & PAGER_JUMP_TO_END)
+ less_opts = strjoina(less_opts, " +G");
+
+ r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pager_pid);
if (r < 0)
return r;
if (r == 0) {
- const char* less_opts, *less_charset;
+ const char *less_charset, *exe;
/* In the child start the pager */
- (void) dup2(fd[0], STDIN_FILENO);
+ if (dup2(fd[0], STDIN_FILENO) < 0) {
+ log_error_errno(errno, "Failed to duplicate file descriptor to STDIN: %m");
+ _exit(EXIT_FAILURE);
+ }
+
safe_close_pair(fd);
- /* Initialize a good set of less options */
- less_opts = getenv("SYSTEMD_LESS");
- if (!less_opts)
- less_opts = "FRSXMK";
- if (jump_to_end)
- less_opts = strjoina(less_opts, " +G");
- if (setenv("LESS", less_opts, 1) < 0)
+ if (setenv("LESS", less_opts, 1) < 0) {
+ log_error_errno(errno, "Failed to set environment variable LESS: %m");
_exit(EXIT_FAILURE);
+ }
/* Initialize a good charset for less. This is
* particularly important if we output UTF-8
@@ -101,12 +159,21 @@ int pager_open(bool no_pager, bool jump_to_end) {
if (!less_charset && is_locale_utf8())
less_charset = "utf-8";
if (less_charset &&
- setenv("LESSCHARSET", less_charset, 1) < 0)
+ setenv("LESSCHARSET", less_charset, 1) < 0) {
+ log_error_errno(errno, "Failed to set environment variable LESSCHARSET: %m");
_exit(EXIT_FAILURE);
+ }
- if (pager) {
- execlp(pager, pager, NULL);
- execl("/bin/sh", "sh", "-c", pager, NULL);
+ if (pager_args) {
+ r = loop_write(exe_name_pipe[1], pager_args[0], strlen(pager_args[0]) + 1, false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write pager name to socket: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ execvp(pager_args[0], pager_args);
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed execute %s, using fallback pagers: %m", pager_args[0]);
}
/* Debian's alternatives command for pagers is
@@ -115,11 +182,22 @@ int pager_open(bool no_pager, bool jump_to_end) {
* shell script that implements a logic that
* is similar to this one anyway, but is
* Debian-specific. */
- execlp("pager", "pager", NULL);
-
- execlp("less", "less", NULL);
- execlp("more", "more", NULL);
+ FOREACH_STRING(exe, "pager", "less", "more") {
+ r = loop_write(exe_name_pipe[1], exe, strlen(exe) + 1, false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write pager name to socket: %m");
+ _exit(EXIT_FAILURE);
+ }
+ execlp(exe, exe, NULL);
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed execute %s, using next fallback pager: %m", exe);
+ }
+ r = loop_write(exe_name_pipe[1], "(built-in)", strlen("(built-in") + 1, false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write pager name to socket: %m");
+ _exit(EXIT_FAILURE);
+ }
pager_fallback();
/* not reached */
}
@@ -139,6 +217,14 @@ int pager_open(bool no_pager, bool jump_to_end) {
}
stderr_redirected = true;
+ exe_name_pipe[1] = safe_close(exe_name_pipe[1]);
+
+ r = no_quit_on_interrupt(TAKE_FD(exe_name_pipe[0]), less_opts);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ (void) ignore_signals(SIGINT, -1);
+
return 1;
}
@@ -192,7 +278,7 @@ int show_man_page(const char *desc, bool null_stdio) {
} else
args[1] = desc;
- r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0)|FORK_LOG, &pid);
+ r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0)|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {
diff --git a/src/basic/pager.h b/src/shared/pager.h
index e0fd34af97..8299e23856 100644
--- a/src/basic/pager.h
+++ b/src/shared/pager.h
@@ -5,7 +5,12 @@
#include "macro.h"
-int pager_open(bool no_pager, bool jump_to_end);
+typedef enum PagerFlags {
+ PAGER_DISABLE = 1 << 0,
+ PAGER_JUMP_TO_END = 1 << 1,
+} PagerFlags;
+
+int pager_open(PagerFlags flags);
void pager_close(void);
bool pager_have(void) _pure_;
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 42a5b62d5d..442fde7b2d 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -6,7 +6,6 @@
#include <string.h>
#include "alloc-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "install.h"
#include "log.h"
@@ -18,6 +17,7 @@
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
@@ -137,14 +137,13 @@ int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs) {
data_dirs = strv_split(e, ":");
else
data_dirs = strv_new("/usr/local/share",
- "/usr/share",
- NULL);
+ "/usr/share");
if (!data_dirs)
return -ENOMEM;
- *ret_config_dirs = config_dirs;
- *ret_data_dirs = data_dirs;
- config_dirs = data_dirs = NULL;
+ *ret_config_dirs = TAKE_PTR(config_dirs);
+ *ret_data_dirs = TAKE_PTR(data_dirs);
+
return 0;
}
@@ -421,6 +420,34 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r
return 0;
}
+static int acquire_attached_dirs(
+ UnitFileScope scope,
+ char **ret_persistent,
+ char **ret_runtime) {
+
+ _cleanup_free_ char *a = NULL, *b = NULL;
+
+ assert(ret_persistent);
+ assert(ret_runtime);
+
+ /* Portable services are not available to regular users for now. */
+ if (scope != UNIT_FILE_SYSTEM)
+ return -EOPNOTSUPP;
+
+ a = strdup("/etc/systemd/system.attached");
+ if (!a)
+ return -ENOMEM;
+
+ b = strdup("/run/systemd/system.attached");
+ if (!b)
+ return -ENOMEM;
+
+ *ret_persistent = TAKE_PTR(a);
+ *ret_runtime = TAKE_PTR(b);
+
+ return 0;
+}
+
static int patch_root_prefix(char **p, const char *root_dir) {
char *c;
@@ -468,7 +495,8 @@ int lookup_paths_init(
*global_persistent_config = NULL, *global_runtime_config = NULL,
*generator = NULL, *generator_early = NULL, *generator_late = NULL,
*transient = NULL,
- *persistent_control = NULL, *runtime_control = NULL;
+ *persistent_control = NULL, *runtime_control = NULL,
+ *persistent_attached = NULL, *runtime_attached = NULL;
bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
_cleanup_strv_free_ char **paths = NULL;
const char *e;
@@ -500,7 +528,7 @@ int lookup_paths_init(
if (flags & LOOKUP_PATHS_TEMPORARY_GENERATED) {
r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
if (r < 0)
- return log_error_errno(r, "Failed to create temporary directory: %m");
+ return log_debug_errno(r, "Failed to create temporary directory: %m");
}
/* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
@@ -532,6 +560,10 @@ int lookup_paths_init(
if (r < 0 && r != -EOPNOTSUPP)
return r;
+ r = acquire_attached_dirs(scope, &persistent_attached, &runtime_attached);
+ if (r < 0 && r != -EOPNOTSUPP)
+ return r;
+
/* First priority is whatever has been passed to us via env vars */
e = getenv("SYSTEMD_UNIT_PATH");
if (e) {
@@ -574,15 +606,16 @@ int lookup_paths_init(
persistent_config,
SYSTEM_CONFIG_UNIT_PATH,
"/etc/systemd/system",
+ STRV_IFNOTNULL(persistent_attached),
runtime_config,
"/run/systemd/system",
+ STRV_IFNOTNULL(runtime_attached),
STRV_IFNOTNULL(generator),
"/usr/local/lib/systemd/system",
SYSTEM_DATA_UNIT_PATH,
"/usr/lib/systemd/system",
STRV_IFNOTNULL(flags & LOOKUP_PATHS_SPLIT_USR ? "/lib/systemd/system" : NULL),
- STRV_IFNOTNULL(generator_late),
- NULL);
+ STRV_IFNOTNULL(generator_late));
break;
case UNIT_FILE_GLOBAL:
@@ -605,8 +638,7 @@ int lookup_paths_init(
"/usr/local/lib/systemd/user",
USER_DATA_UNIT_PATH,
"/usr/lib/systemd/user",
- STRV_IFNOTNULL(generator_late),
- NULL);
+ STRV_IFNOTNULL(generator_late));
break;
case UNIT_FILE_USER:
@@ -658,33 +690,44 @@ int lookup_paths_init(
r = patch_root_prefix(&persistent_control, root);
if (r < 0)
return r;
-
r = patch_root_prefix(&runtime_control, root);
if (r < 0)
return r;
+ r = patch_root_prefix(&persistent_attached, root);
+ if (r < 0)
+ return r;
+ r = patch_root_prefix(&runtime_attached, root);
+ if (r < 0)
+ return r;
+
r = patch_root_prefix_strv(paths, root);
if (r < 0)
return -ENOMEM;
- p->search_path = strv_uniq(paths);
- paths = NULL;
+ *p = (LookupPaths) {
+ .search_path = strv_uniq(paths),
- p->persistent_config = TAKE_PTR(persistent_config);
- p->runtime_config = TAKE_PTR(runtime_config);
+ .persistent_config = TAKE_PTR(persistent_config),
+ .runtime_config = TAKE_PTR(runtime_config),
- p->generator = TAKE_PTR(generator);
- p->generator_early = TAKE_PTR(generator_early);
- p->generator_late = TAKE_PTR(generator_late);
+ .generator = TAKE_PTR(generator),
+ .generator_early = TAKE_PTR(generator_early),
+ .generator_late = TAKE_PTR(generator_late),
- p->transient = TAKE_PTR(transient);
+ .transient = TAKE_PTR(transient),
- p->persistent_control = TAKE_PTR(persistent_control);
- p->runtime_control = TAKE_PTR(runtime_control);
+ .persistent_control = TAKE_PTR(persistent_control),
+ .runtime_control = TAKE_PTR(runtime_control),
- p->root_dir = TAKE_PTR(root);
- p->temporary_dir = TAKE_PTR(tempdir);
+ .persistent_attached = TAKE_PTR(persistent_attached),
+ .runtime_attached = TAKE_PTR(runtime_attached),
+ .root_dir = TAKE_PTR(root),
+ .temporary_dir = TAKE_PTR(tempdir),
+ };
+
+ paths = NULL;
return 0;
}
@@ -697,6 +740,9 @@ void lookup_paths_free(LookupPaths *p) {
p->persistent_config = mfree(p->persistent_config);
p->runtime_config = mfree(p->runtime_config);
+ p->persistent_attached = mfree(p->persistent_attached);
+ p->runtime_attached = mfree(p->runtime_attached);
+
p->generator = mfree(p->generator);
p->generator_early = mfree(p->generator_early);
p->generator_late = mfree(p->generator_late);
@@ -842,16 +888,14 @@ char **generator_binary_paths(UnitFileScope scope) {
return strv_new("/run/systemd/system-generators",
"/etc/systemd/system-generators",
"/usr/local/lib/systemd/system-generators",
- SYSTEM_GENERATOR_PATH,
- NULL);
+ SYSTEM_GENERATOR_PATH);
case UNIT_FILE_GLOBAL:
case UNIT_FILE_USER:
return strv_new("/run/systemd/user-generators",
"/etc/systemd/user-generators",
"/usr/local/lib/systemd/user-generators",
- USER_GENERATOR_PATH,
- NULL);
+ USER_GENERATOR_PATH);
default:
assert_not_reached("Hmm, unexpected scope.");
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
index 963e09db67..cb7d4d537f 100644
--- a/src/shared/path-lookup.h
+++ b/src/shared/path-lookup.h
@@ -24,6 +24,10 @@ struct LookupPaths {
char *persistent_config;
char *runtime_config;
+ /* Where units from a portable service image shall be placed. */
+ char *persistent_attached;
+ char *runtime_attached;
+
/* Where to place generated unit files (i.e. those a "generator" tool generated). Note the special semantics of
* this directory: the generators are flushed each time a "systemctl daemon-reload" is issued. The user should
* not alter these directories directly. */
@@ -50,10 +54,12 @@ struct LookupPaths {
};
int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir);
+
int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs);
int xdg_user_runtime_dir(char **ret, const char *suffix);
int xdg_user_config_dir(char **ret, const char *suffix);
int xdg_user_data_dir(char **ret, const char *suffix);
+
bool path_is_user_data_dir(const char *path);
bool path_is_user_config_dir(const char *path);
diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c
new file mode 100644
index 0000000000..de6274a3da
--- /dev/null
+++ b/src/shared/pretty-print.c
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/utsname.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "conf-files.h"
+#include "def.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "pager.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "util.h"
+
+static bool urlify_enabled(void) {
+ static int cached_urlify_enabled = -1;
+
+ /* Unfortunately 'less' doesn't support links like this yet 😭, hence let's disable this as long as there's a
+ * pager in effect. Let's drop this check as soon as less got fixed a and enough time passed so that it's safe
+ * to assume that a link-enabled 'less' version has hit most installations. */
+
+ if (cached_urlify_enabled < 0) {
+ int val;
+
+ val = getenv_bool("SYSTEMD_URLIFY");
+ if (val >= 0)
+ cached_urlify_enabled = val;
+ else
+ cached_urlify_enabled = colors_enabled() && !pager_have();
+ }
+
+ return cached_urlify_enabled;
+}
+
+int terminal_urlify(const char *url, const char *text, char **ret) {
+ char *n;
+
+ assert(url);
+
+ /* Takes an URL and a pretty string and formats it as clickable link for the terminal. See
+ * https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda for details. */
+
+ if (isempty(text))
+ text = url;
+
+ if (urlify_enabled())
+ n = strjoin("\x1B]8;;", url, "\a", text, "\x1B]8;;\a");
+ else
+ n = strdup(text);
+ if (!n)
+ return -ENOMEM;
+
+ *ret = n;
+ return 0;
+}
+
+int file_url_from_path(const char *path, char **ret) {
+ _cleanup_free_ char *absolute = NULL;
+ struct utsname u;
+ char *url = NULL;
+ int r;
+
+ if (uname(&u) < 0)
+ return -errno;
+
+ if (!path_is_absolute(path)) {
+ r = path_make_absolute_cwd(path, &absolute);
+ if (r < 0)
+ return r;
+
+ path = absolute;
+ }
+
+ /* As suggested by https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, let's include the local
+ * hostname here. Note that we don't use gethostname_malloc() or gethostname_strict() since we are interested
+ * in the raw string the kernel has set, whatever it may be, under the assumption that terminals are not overly
+ * careful with validating the strings either. */
+
+ url = strjoin("file://", u.nodename, path);
+ if (!url)
+ return -ENOMEM;
+
+ *ret = url;
+ return 0;
+}
+
+int terminal_urlify_path(const char *path, const char *text, char **ret) {
+ _cleanup_free_ char *url = NULL;
+ int r;
+
+ assert(path);
+
+ /* Much like terminal_urlify() above, but takes a file system path as input
+ * and turns it into a proper file:// URL first. */
+
+ if (isempty(path))
+ return -EINVAL;
+
+ if (isempty(text))
+ text = path;
+
+ if (!urlify_enabled()) {
+ char *n;
+
+ n = strdup(text);
+ if (!n)
+ return -ENOMEM;
+
+ *ret = n;
+ return 0;
+ }
+
+ r = file_url_from_path(path, &url);
+ if (r < 0)
+ return r;
+
+ return terminal_urlify(url, text, ret);
+}
+
+int terminal_urlify_man(const char *page, const char *section, char **ret) {
+ const char *url, *text;
+
+ url = strjoina("man:", page, "(", section, ")");
+ text = strjoina(page, "(", section, ") man page");
+
+ return terminal_urlify(url, text, ret);
+}
+
+static int cat_file(const char *filename, bool newline) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *urlified = NULL;
+ int r;
+
+ f = fopen(filename, "re");
+ if (!f)
+ return -errno;
+
+ r = terminal_urlify_path(filename, NULL, &urlified);
+ if (r < 0)
+ return r;
+
+ printf("%s%s# %s%s\n",
+ newline ? "\n" : "",
+ ansi_highlight_blue(),
+ urlified,
+ ansi_normal());
+ fflush(stdout);
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read \"%s\": %m", filename);
+ if (r == 0)
+ break;
+
+ puts(line);
+ }
+
+ return 0;
+}
+
+int cat_files(const char *file, char **dropins, CatFlags flags) {
+ char **path;
+ int r;
+
+ if (file) {
+ r = cat_file(file, false);
+ if (r == -ENOENT && (flags & CAT_FLAGS_MAIN_FILE_OPTIONAL))
+ printf("%s# config file %s not found%s\n",
+ ansi_highlight_magenta(),
+ file,
+ ansi_normal());
+ else if (r < 0)
+ return log_warning_errno(r, "Failed to cat %s: %m", file);
+ }
+
+ STRV_FOREACH(path, dropins) {
+ r = cat_file(*path, file || path != dropins);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to cat %s: %m", *path);
+ }
+
+ return 0;
+}
+
+void print_separator(void) {
+
+ /* Outputs a separator line that resolves to whitespace when copied from the terminal. We do that by outputting
+ * one line filled with spaces with ANSI underline set, followed by a second (empty) line. */
+
+ if (underline_enabled()) {
+ size_t i, c;
+
+ c = columns();
+
+ flockfile(stdout);
+ fputs_unlocked(ANSI_UNDERLINE, stdout);
+
+ for (i = 0; i < c; i++)
+ fputc_unlocked(' ', stdout);
+
+ fputs_unlocked(ANSI_NORMAL "\n\n", stdout);
+ funlockfile(stdout);
+ } else
+ fputs("\n\n", stdout);
+}
+
+int conf_files_cat(const char *root, const char *name) {
+ _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
+ _cleanup_free_ char *path = NULL;
+ const char *dir;
+ char **t;
+ int r;
+
+ NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
+ assert(endswith(dir, "/"));
+ r = strv_extendf(&dirs, "%s%s.d", dir, name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to build directory list: %m");
+ }
+
+ r = conf_files_list_strv(&files, ".conf", root, 0, (const char* const*) dirs);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query file list: %m");
+
+ path = path_join(root, "/etc", name);
+ if (!path)
+ return log_oom();
+
+ if (DEBUG_LOGGING) {
+ log_debug("Looking for configuration in:");
+ log_debug(" %s", path);
+ STRV_FOREACH(t, dirs)
+ log_debug(" %s/*.conf", *t);
+ }
+
+ /* show */
+ return cat_files(path, files, CAT_FLAGS_MAIN_FILE_OPTIONAL);
+}
diff --git a/src/shared/pretty-print.h b/src/shared/pretty-print.h
new file mode 100644
index 0000000000..12ab9acf58
--- /dev/null
+++ b/src/shared/pretty-print.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+void print_separator(void);
+
+int file_url_from_path(const char *path, char **ret);
+
+int terminal_urlify(const char *url, const char *text, char **ret);
+int terminal_urlify_path(const char *path, const char *text, char **ret);
+int terminal_urlify_man(const char *page, const char *section, char **ret);
+
+typedef enum CatFlags {
+ CAT_FLAGS_MAIN_FILE_OPTIONAL = 1 << 0,
+} CatFlags;
+
+int cat_files(const char *file, char **dropins, CatFlags flags);
+int conf_files_cat(const char *root, const char *name);
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index c38ec81bbc..fe17b3781a 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -20,6 +20,7 @@
#include "log.h"
#include "macro.h"
#include "ptyfwd.h"
+#include "terminal-util.h"
#include "time-util.h"
struct PTYForward {
@@ -88,8 +89,8 @@ static void pty_forward_disconnect(PTYForward *f) {
}
/* STDIN/STDOUT should not be nonblocking normally, so let's unconditionally reset it */
- fd_nonblock(STDIN_FILENO, false);
- fd_nonblock(STDOUT_FILENO, false);
+ (void) fd_nonblock(STDIN_FILENO, false);
+ (void) fd_nonblock(STDOUT_FILENO, false);
}
static int pty_forward_done(PTYForward *f, int rcode) {
@@ -391,11 +392,14 @@ int pty_forward_new(
struct winsize ws;
int r;
- f = new0(PTYForward, 1);
+ f = new(PTYForward, 1);
if (!f)
return -ENOMEM;
- f->flags = flags;
+ *f = (struct PTYForward) {
+ .flags = flags,
+ .master = -1,
+ };
if (event)
f->event = sd_event_ref(event);
@@ -421,8 +425,17 @@ int pty_forward_new(
f->master = master;
- if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
- (void) ioctl(master, TIOCSWINSZ, &ws);
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
+ /* If we can't get the resolution from the output fd, then use our internal, regular width/height,
+ * i.e. something derived from $COLUMNS and $LINES if set. */
+
+ ws = (struct winsize) {
+ .ws_row = lines(),
+ .ws_col = columns(),
+ };
+ }
+
+ (void) ioctl(master, TIOCSWINSZ, &ws);
if (!(flags & PTY_FORWARD_READ_ONLY)) {
if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
@@ -577,3 +590,42 @@ int pty_forward_set_priority(PTYForward *f, int64_t priority) {
return 0;
}
+
+int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height) {
+ struct winsize ws;
+
+ assert(f);
+
+ if (width == (unsigned) -1 && height == (unsigned) -1)
+ return 0; /* noop */
+
+ if (width != (unsigned) -1 &&
+ (width == 0 || width > USHRT_MAX))
+ return -ERANGE;
+
+ if (height != (unsigned) -1 &&
+ (height == 0 || height > USHRT_MAX))
+ return -ERANGE;
+
+ if (width == (unsigned) -1 || height == (unsigned) -1) {
+ if (ioctl(f->master, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (width != (unsigned) -1)
+ ws.ws_col = width;
+ if (height != (unsigned) -1)
+ ws.ws_row = height;
+ } else
+ ws = (struct winsize) {
+ .ws_row = height,
+ .ws_col = width,
+ };
+
+ if (ioctl(f->master, TIOCSWINSZ, &ws) < 0)
+ return -errno;
+
+ /* Make sure we ignore SIGWINCH window size events from now on */
+ f->sigwinch_event_source = sd_event_source_unref(f->sigwinch_event_source);
+
+ return 0;
+}
diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h
index e4a083ac24..887d3cba53 100644
--- a/src/shared/ptyfwd.h
+++ b/src/shared/ptyfwd.h
@@ -19,7 +19,7 @@ typedef enum PTYForwardFlags {
PTY_FORWARD_IGNORE_INITIAL_VHANGUP = 4,
} PTYForwardFlags;
-typedef int (*PTYForwardHandler)(PTYForward *f, int rcode, void*userdata);
+typedef int (*PTYForwardHandler)(PTYForward *f, int rcode, void *userdata);
int pty_forward_new(sd_event *event, int master, PTYForwardFlags flags, PTYForward **f);
PTYForward *pty_forward_free(PTYForward *f);
@@ -37,4 +37,6 @@ bool pty_forward_drain(PTYForward *f);
int pty_forward_set_priority(PTYForward *f, int64_t priority);
+int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free);
diff --git a/src/basic/reboot-util.c b/src/shared/reboot-util.c
index ca40159b96..ca40159b96 100644
--- a/src/basic/reboot-util.c
+++ b/src/shared/reboot-util.c
diff --git a/src/basic/reboot-util.h b/src/shared/reboot-util.h
index d459333efc..d459333efc 100644
--- a/src/basic/reboot-util.h
+++ b/src/shared/reboot-util.h
diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
index c433cb90dc..42d6dd2a94 100644
--- a/src/shared/seccomp-util.c
+++ b/src/shared/seccomp-util.c
@@ -308,6 +308,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
"io_cancel\0"
"io_destroy\0"
"io_getevents\0"
+ "io_pgetevents\0"
"io_setup\0"
"io_submit\0"
},
@@ -370,8 +371,6 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
.value =
"lookup_dcookie\0"
"perf_event_open\0"
- "process_vm_readv\0"
- "process_vm_writev\0"
"ptrace\0"
"rtas\0"
#ifdef __NR_s390_runtime_instr
@@ -619,7 +618,9 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
"bpf\0"
"capset\0"
"chroot\0"
+ "fanotify_init\0"
"nfsservctl\0"
+ "open_by_handle_at\0"
"pivot_root\0"
"quotactl\0"
"setdomainname\0"
@@ -651,6 +652,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
"rt_sigqueueinfo\0"
"rt_tgsigqueueinfo\0"
"setns\0"
+ "swapcontext\0" /* Some archs e.g. powerpc32 are using it to do userspace context switches */
"tgkill\0"
"times\0"
"tkill\0"
@@ -857,11 +859,9 @@ const SyscallFilterSet *syscall_filter_set_find(const char *name) {
return NULL;
}
-static int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterSet *set, uint32_t action, char **exclude);
-
-int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name, uint32_t action, char **exclude) {
- int r;
+static int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterSet *set, uint32_t action, char **exclude, bool log_missing);
+int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name, uint32_t action, char **exclude, bool log_missing) {
assert(seccomp);
assert(name);
@@ -872,37 +872,45 @@ int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name,
const SyscallFilterSet *other;
other = syscall_filter_set_find(name);
- if (!other) {
- log_debug("Filter set %s is not known!", name);
- return -EINVAL;
- }
+ if (!other)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Filter set %s is not known!",
+ name);
+
+ return seccomp_add_syscall_filter_set(seccomp, other, action, exclude, log_missing);
- r = seccomp_add_syscall_filter_set(seccomp, other, action, exclude);
- if (r < 0)
- return r;
} else {
- int id;
+ int id, r;
id = seccomp_syscall_resolve_name(name);
if (id == __NR_SCMP_ERROR) {
- log_debug("System call %s is not known, ignoring.", name);
+ if (log_missing)
+ log_debug("System call %s is not known, ignoring.", name);
return 0;
}
r = seccomp_rule_add_exact(seccomp, action, id, 0);
- if (r < 0)
+ if (r < 0) {
/* If the system call is not known on this architecture, then that's fine, let's ignore it */
- log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", name, id);
- }
+ bool ignore = r == -EDOM;
- return 0;
+ if (!ignore || log_missing)
+ log_debug_errno(r, "Failed to add rule for system call %s() / %d%s: %m",
+ name, id, ignore ? ", ignoring" : "");
+ if (!ignore)
+ return r;
+ }
+
+ return 0;
+ }
}
static int seccomp_add_syscall_filter_set(
scmp_filter_ctx seccomp,
const SyscallFilterSet *set,
uint32_t action,
- char **exclude) {
+ char **exclude,
+ bool log_missing) {
const char *sys;
int r;
@@ -911,7 +919,7 @@ static int seccomp_add_syscall_filter_set(
assert(set);
NULSTR_FOREACH(sys, set->value) {
- r = seccomp_add_syscall_filter_item(seccomp, sys, action, exclude);
+ r = seccomp_add_syscall_filter_item(seccomp, sys, action, exclude, log_missing);
if (r < 0)
return r;
}
@@ -919,14 +927,14 @@ static int seccomp_add_syscall_filter_set(
return 0;
}
-int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action) {
+int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action, bool log_missing) {
uint32_t arch;
int r;
assert(set);
/* The one-stop solution: allocate a seccomp object, add the specified filter to it, and apply it. Once for
- * earch local arch. */
+ * each local arch. */
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
@@ -937,11 +945,9 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter
if (r < 0)
return r;
- r = seccomp_add_syscall_filter_set(seccomp, set, action, NULL);
- if (r < 0) {
- log_debug_errno(r, "Failed to add filter set, ignoring: %m");
- continue;
- }
+ r = seccomp_add_syscall_filter_set(seccomp, set, action, NULL, log_missing);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add filter set: %m");
r = seccomp_load(seccomp);
if (IN_SET(r, -EPERM, -EACCES))
@@ -953,7 +959,7 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter
return 0;
}
-int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action) {
+int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action, bool log_missing) {
uint32_t arch;
int r;
@@ -966,7 +972,7 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
Iterator i;
- void *id, *val;
+ void *syscall_id, *val;
log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch));
@@ -974,20 +980,27 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u
if (r < 0)
return r;
- HASHMAP_FOREACH_KEY(val, id, set, i) {
+ HASHMAP_FOREACH_KEY(val, syscall_id, set, i) {
uint32_t a = action;
- int e = PTR_TO_INT(val);
+ int id = PTR_TO_INT(syscall_id) - 1;
+ int error = PTR_TO_INT(val);
- if (action != SCMP_ACT_ALLOW && e >= 0)
- a = SCMP_ACT_ERRNO(e);
+ if (action != SCMP_ACT_ALLOW && error >= 0)
+ a = SCMP_ACT_ERRNO(error);
- r = seccomp_rule_add_exact(seccomp, a, PTR_TO_INT(id) - 1, 0);
+ r = seccomp_rule_add_exact(seccomp, a, id, 0);
if (r < 0) {
/* If the system call is not known on this architecture, then that's fine, let's ignore it */
_cleanup_free_ char *n = NULL;
-
- n = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
- log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", strna(n), PTR_TO_INT(id) - 1);
+ bool ignore;
+
+ n = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, id);
+ ignore = r == -EDOM;
+ if (!ignore || log_missing)
+ log_debug_errno(r, "Failed to add rule for system call %s() / %d%s: %m",
+ strna(n), id, ignore ? ", ignoring" : "");
+ if (!ignore)
+ return r;
}
}
@@ -1056,7 +1069,15 @@ int seccomp_parse_syscall_filter_full(
if (!(flags & SECCOMP_PARSE_INVERT) == !!(flags & SECCOMP_PARSE_WHITELIST)) {
r = hashmap_put(filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num));
if (r < 0)
- return flags & SECCOMP_PARSE_LOG ? log_oom() : -ENOMEM;
+ switch (r) {
+ case -ENOMEM:
+ return flags & SECCOMP_PARSE_LOG ? log_oom() : -ENOMEM;
+ case -EEXIST:
+ assert_se(hashmap_update(filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num)) == 0);
+ break;
+ default:
+ return r;
+ }
} else
(void) hashmap_remove(filter, INT_TO_PTR(id + 1));
}
@@ -1460,7 +1481,7 @@ int seccomp_restrict_realtime(void) {
static int add_seccomp_syscall_filter(scmp_filter_ctx seccomp,
uint32_t arch,
int nr,
- unsigned int arg_cnt,
+ unsigned arg_cnt,
const struct scmp_arg_cmp arg) {
int r;
diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h
index eac857afb9..d8a36c4e21 100644
--- a/src/shared/seccomp-util.h
+++ b/src/shared/seccomp-util.h
@@ -58,10 +58,10 @@ const SyscallFilterSet *syscall_filter_set_find(const char *name);
int seccomp_filter_set_add(Hashmap *s, bool b, const SyscallFilterSet *set);
-int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint32_t action, char **exclude);
+int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint32_t action, char **exclude, bool log_missing);
-int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action);
-int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action);
+int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action, bool log_missing);
+int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action, bool log_missing);
typedef enum SeccompParseFlags {
SECCOMP_PARSE_INVERT = 1 << 0,
diff --git a/src/basic/securebits-util.c b/src/shared/securebits-util.c
index ad091f6d95..6d31dfeff0 100644
--- a/src/basic/securebits-util.c
+++ b/src/shared/securebits-util.c
@@ -5,7 +5,6 @@
#include "alloc-util.h"
#include "extract-word.h"
-#include "securebits.h"
#include "securebits-util.h"
#include "string-util.h"
diff --git a/src/basic/securebits-util.h b/src/shared/securebits-util.h
index 10a221d6a0..b5ec6ee0e6 100644
--- a/src/basic/securebits-util.h
+++ b/src/shared/securebits-util.h
@@ -1,8 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
-#include "securebits.h"
+#include "missing_securebits.h"
int secure_bits_to_string_alloc(int i, char **s);
int secure_bits_from_string(const char *s);
diff --git a/src/shared/serialize.c b/src/shared/serialize.c
new file mode 100644
index 0000000000..0333f87b7b
--- /dev/null
+++ b/src/shared/serialize.c
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/mman.h>
+
+#include "alloc-util.h"
+#include "env-util.h"
+#include "escape.h"
+#include "fileio.h"
+#include "missing.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "serialize.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+
+int serialize_item(FILE *f, const char *key, const char *value) {
+ assert(f);
+ assert(key);
+
+ if (!value)
+ return 0;
+
+ /* Make sure that anything we serialize we can also read back again with read_line() with a maximum line size
+ * of LONG_LINE_MAX. This is a safety net only. All code calling us should filter this out earlier anyway. */
+ if (strlen(key) + 1 + strlen(value) + 1 > LONG_LINE_MAX) {
+ log_warning("Attempted to serialize overly long item '%s', refusing.", key);
+ return -EINVAL;
+ }
+
+ fputs(key, f);
+ fputc('=', f);
+ fputs(value, f);
+ fputc('\n', f);
+
+ return 1;
+}
+
+int serialize_item_escaped(FILE *f, const char *key, const char *value) {
+ _cleanup_free_ char *c = NULL;
+
+ assert(f);
+ assert(key);
+
+ if (!value)
+ return 0;
+
+ c = cescape(value);
+ if (!c)
+ return log_oom();
+
+ return serialize_item(f, key, c);
+}
+
+int serialize_item_format(FILE *f, const char *key, const char *format, ...) {
+ char buf[LONG_LINE_MAX];
+ va_list ap;
+ int k;
+
+ assert(f);
+ assert(key);
+ assert(format);
+
+ va_start(ap, format);
+ k = vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+
+ if (k < 0 || (size_t) k >= sizeof(buf) || strlen(key) + 1 + k + 1 > LONG_LINE_MAX) {
+ log_warning("Attempted to serialize overly long item '%s', refusing.", key);
+ return -EINVAL;
+ }
+
+ fputs(key, f);
+ fputc('=', f);
+ fputs(buf, f);
+ fputc('\n', f);
+
+ return 1;
+}
+
+int serialize_fd(FILE *f, FDSet *fds, const char *key, int fd) {
+ int copy;
+
+ assert(f);
+ assert(key);
+
+ if (fd < 0)
+ return 0;
+
+ copy = fdset_put_dup(fds, fd);
+ if (copy < 0)
+ return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
+
+ return serialize_item_format(f, key, "%i", copy);
+}
+
+int serialize_usec(FILE *f, const char *key, usec_t usec) {
+ assert(f);
+ assert(key);
+
+ if (usec == USEC_INFINITY)
+ return 0;
+
+ return serialize_item_format(f, key, USEC_FMT, usec);
+}
+
+int serialize_dual_timestamp(FILE *f, const char *name, const dual_timestamp *t) {
+ assert(f);
+ assert(name);
+ assert(t);
+
+ if (!dual_timestamp_is_set(t))
+ return 0;
+
+ return serialize_item_format(f, name, USEC_FMT " " USEC_FMT, t->realtime, t->monotonic);
+}
+
+int serialize_strv(FILE *f, const char *key, char **l) {
+ int ret = 0, r;
+ char **i;
+
+ /* Returns the first error, or positive if anything was serialized, 0 otherwise. */
+
+ STRV_FOREACH(i, l) {
+ r = serialize_item_escaped(f, key, *i);
+ if ((ret >= 0 && r < 0) ||
+ (ret == 0 && r > 0))
+ ret = r;
+ }
+
+ return ret;
+}
+
+int deserialize_usec(const char *value, usec_t *ret) {
+ int r;
+
+ assert(value);
+
+ r = safe_atou64(value, ret);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse usec value \"%s\": %m", value);
+
+ return 0;
+}
+
+int deserialize_dual_timestamp(const char *value, dual_timestamp *t) {
+ uint64_t a, b;
+ int r, pos;
+
+ assert(value);
+ assert(t);
+
+ pos = strspn(value, WHITESPACE);
+ if (value[pos] == '-')
+ return -EINVAL;
+ pos += strspn(value + pos, DIGITS);
+ pos += strspn(value + pos, WHITESPACE);
+ if (value[pos] == '-')
+ return -EINVAL;
+
+ r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
+ if (r != 2)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse dual timestamp value \"%s\".",
+ value);
+
+ if (value[pos] != '\0')
+ /* trailing garbage */
+ return -EINVAL;
+
+ t->realtime = a;
+ t->monotonic = b;
+
+ return 0;
+}
+
+int deserialize_environment(const char *value, char ***list) {
+ _cleanup_free_ char *unescaped = NULL;
+ int r;
+
+ assert(value);
+ assert(list);
+
+ /* Changes the *environment strv inline. */
+
+ r = cunescape(value, 0, &unescaped);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unescape: %m");
+
+ r = strv_env_replace(list, unescaped);
+ if (r < 0)
+ return log_error_errno(r, "Failed to append environment variable: %m");
+
+ unescaped = NULL; /* now part of 'list' */
+ return 0;
+}
+
+int open_serialization_fd(const char *ident) {
+ int fd;
+
+ fd = memfd_create(ident, MFD_CLOEXEC);
+ if (fd < 0) {
+ const char *path;
+
+ path = getpid_cached() == 1 ? "/run/systemd" : "/tmp";
+ fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ log_debug("Serializing %s to %s.", ident, path);
+ } else
+ log_debug("Serializing %s to memfd.", ident);
+
+ return fd;
+}
diff --git a/src/shared/serialize.h b/src/shared/serialize.h
new file mode 100644
index 0000000000..4cbd98bb34
--- /dev/null
+++ b/src/shared/serialize.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdio.h>
+
+#include "fdset.h"
+#include "macro.h"
+
+int serialize_item(FILE *f, const char *key, const char *value);
+int serialize_item_escaped(FILE *f, const char *key, const char *value);
+int serialize_item_format(FILE *f, const char *key, const char *value, ...) _printf_(3,4);
+int serialize_fd(FILE *f, FDSet *fds, const char *key, int fd);
+int serialize_usec(FILE *f, const char *key, usec_t usec);
+int serialize_dual_timestamp(FILE *f, const char *key, const dual_timestamp *t);
+int serialize_strv(FILE *f, const char *key, char **l);
+
+static inline int serialize_bool(FILE *f, const char *key, bool b) {
+ return serialize_item(f, key, yes_no(b));
+}
+
+int deserialize_usec(const char *value, usec_t *timestamp);
+int deserialize_dual_timestamp(const char *value, dual_timestamp *t);
+int deserialize_environment(const char *value, char ***environment);
+
+int open_serialization_fd(const char *ident);
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
index 9e4ce183d3..2e22bd0bbe 100644
--- a/src/shared/sleep-config.c
+++ b/src/shared/sleep-config.c
@@ -9,6 +9,7 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <syslog.h>
#include <unistd.h>
@@ -21,12 +22,15 @@
#include "log.h"
#include "macro.h"
#include "parse-util.h"
+#include "path-util.h"
#include "sleep-config.h"
#include "string-util.h"
#include "strv.h"
-int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t *_delay) {
-
+int parse_sleep_config(const char *verb, bool *ret_allow, char ***ret_modes, char ***ret_states, usec_t *ret_delay) {
+ int allow_suspend = -1, allow_hibernate = -1,
+ allow_s2h = -1, allow_hybrid_sleep = -1;
+ bool allow;
_cleanup_strv_free_ char
**suspend_mode = NULL, **suspend_state = NULL,
**hibernate_mode = NULL, **hibernate_state = NULL,
@@ -35,13 +39,19 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
usec_t delay = 180 * USEC_PER_MINUTE;
const ConfigTableItem items[] = {
- { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
- { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state },
- { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode },
- { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state },
- { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode },
- { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state },
- { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &delay},
+ { "Sleep", "AllowSuspend", config_parse_tristate, 0, &allow_suspend },
+ { "Sleep", "AllowHibernation", config_parse_tristate, 0, &allow_hibernate },
+ { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h },
+ { "Sleep", "AllowHybridSleep", config_parse_tristate, 0, &allow_hybrid_sleep },
+
+ { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
+ { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state },
+ { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode },
+ { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state },
+ { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode },
+ { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state },
+
+ { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &delay},
{}
};
@@ -51,51 +61,63 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
CONFIG_PARSE_WARN, NULL);
if (streq(verb, "suspend")) {
+ allow = allow_suspend != 0;
+
/* empty by default */
modes = TAKE_PTR(suspend_mode);
if (suspend_state)
states = TAKE_PTR(suspend_state);
else
- states = strv_new("mem", "standby", "freeze", NULL);
+ states = strv_new("mem", "standby", "freeze");
} else if (streq(verb, "hibernate")) {
+ allow = allow_hibernate != 0;
+
if (hibernate_mode)
modes = TAKE_PTR(hibernate_mode);
else
- modes = strv_new("platform", "shutdown", NULL);
+ modes = strv_new("platform", "shutdown");
if (hibernate_state)
states = TAKE_PTR(hibernate_state);
else
- states = strv_new("disk", NULL);
+ states = strv_new("disk");
} else if (streq(verb, "hybrid-sleep")) {
+ allow = allow_hybrid_sleep > 0 ||
+ (allow_suspend != 0 && allow_hibernate != 0);
+
if (hybrid_mode)
modes = TAKE_PTR(hybrid_mode);
else
- modes = strv_new("suspend", "platform", "shutdown", NULL);
+ modes = strv_new("suspend", "platform", "shutdown");
if (hybrid_state)
states = TAKE_PTR(hybrid_state);
else
- states = strv_new("disk", NULL);
+ states = strv_new("disk");
+
+ } else if (streq(verb, "suspend-then-hibernate")) {
+ allow = allow_s2h > 0 ||
+ (allow_suspend != 0 && allow_hibernate != 0);
- } else if (streq(verb, "suspend-then-hibernate"))
modes = states = NULL;
- else
+ } else
assert_not_reached("what verb");
if ((!modes && STR_IN_SET(verb, "hibernate", "hybrid-sleep")) ||
(!states && !streq(verb, "suspend-then-hibernate")))
return log_oom();
- if (_modes)
- *_modes = TAKE_PTR(modes);
- if (_states)
- *_states = TAKE_PTR(states);
- if (_delay)
- *_delay = delay;
+ if (ret_allow)
+ *ret_allow = allow;
+ if (ret_modes)
+ *ret_modes = TAKE_PTR(modes);
+ if (ret_states)
+ *ret_states = TAKE_PTR(states);
+ if (ret_delay)
+ *ret_delay = delay;
return 0;
}
@@ -138,12 +160,16 @@ int can_sleep_disk(char **types) {
return true;
/* If /sys is read-only we cannot sleep */
- if (access("/sys/power/disk", W_OK) < 0)
+ if (access("/sys/power/disk", W_OK) < 0) {
+ log_debug_errno(errno, "/sys/power/disk is not writable: %m");
return false;
+ }
r = read_one_line_file("/sys/power/disk", &p);
- if (r < 0)
+ if (r < 0) {
+ log_debug_errno(r, "Couldn't read /sys/power/disk: %m");
return false;
+ }
STRV_FOREACH(type, types) {
const char *word, *state;
@@ -193,18 +219,30 @@ int find_hibernate_location(char **device, char **type, size_t *size, size_t *us
"%zu " /* used */
"%*i\n", /* priority */
&dev_field, &type_field, &size_field, &used_field);
+ if (k == EOF)
+ break;
if (k != 4) {
- if (k == EOF)
- break;
-
log_warning("Failed to parse /proc/swaps:%u", i);
continue;
}
- if (streq(type_field, "partition") && endswith(dev_field, "\\040(deleted)")) {
- log_warning("Ignoring deleted swapfile '%s'.", dev_field);
- continue;
+ if (streq(type_field, "file")) {
+
+ if (endswith(dev_field, "\\040(deleted)")) {
+ log_warning("Ignoring deleted swap file '%s'.", dev_field);
+ continue;
+ }
+
+ } else if (streq(type_field, "partition")) {
+ const char *fn;
+
+ fn = path_startswith(dev_field, "/dev/");
+ if (fn && startswith(fn, "zram")) {
+ log_debug("Ignoring compressed RAM swap device '%s'.", dev_field);
+ continue;
+ }
}
+
if (device)
*device = TAKE_PTR(dev_field);
if (type)
@@ -216,8 +254,8 @@ int find_hibernate_location(char **device, char **type, size_t *size, size_t *us
return 0;
}
- log_debug("No swap partitions were found.");
- return -ENOSYS;
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOSYS),
+ "No swap partitions were found.");
}
static bool enough_swap_for_hibernation(void) {
@@ -235,20 +273,19 @@ static bool enough_swap_for_hibernation(void) {
r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
if (r < 0) {
- log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
+ log_debug_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
return false;
}
r = safe_atollu(active, &act);
if (r < 0) {
- log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
- active);
+ log_debug_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m", active);
return false;
}
r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD;
- log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
- r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
+ log_debug("%s swap for hibernation, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
+ r ? "Enough" : "Not enough", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
return r;
}
@@ -337,6 +374,8 @@ int read_fiemap(int fd, struct fiemap **ret) {
return 0;
}
+static int can_sleep_internal(const char *verb, bool check_allowed);
+
static bool can_s2h(void) {
const char *p;
int r;
@@ -349,8 +388,8 @@ static bool can_s2h(void) {
}
FOREACH_STRING(p, "suspend", "hibernate") {
- r = can_sleep(p);
- if (IN_SET(r, 0, -ENOSPC)) {
+ r = can_sleep_internal(p, false);
+ if (IN_SET(r, 0, -ENOSPC, -EADV)) {
log_debug("Unable to %s system.", p);
return false;
}
@@ -361,19 +400,25 @@ static bool can_s2h(void) {
return true;
}
-int can_sleep(const char *verb) {
+static int can_sleep_internal(const char *verb, bool check_allowed) {
+ bool allow;
_cleanup_strv_free_ char **modes = NULL, **states = NULL;
int r;
assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"));
- if (streq(verb, "suspend-then-hibernate"))
- return can_s2h();
-
- r = parse_sleep_config(verb, &modes, &states, NULL);
+ r = parse_sleep_config(verb, &allow, &modes, &states, NULL);
if (r < 0)
return false;
+ if (check_allowed && !allow) {
+ log_debug("Sleep mode \"%s\" is disabled by configuration.", verb);
+ return false;
+ }
+
+ if (streq(verb, "suspend-then-hibernate"))
+ return can_s2h();
+
if (!can_sleep_state(states) || !can_sleep_disk(modes))
return false;
@@ -385,3 +430,7 @@ int can_sleep(const char *verb) {
return true;
}
+
+int can_sleep(const char *verb) {
+ return can_sleep_internal(verb, true);
+}
diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h
index 6bf035969a..c584f44d39 100644
--- a/src/shared/sleep-config.h
+++ b/src/shared/sleep-config.h
@@ -5,7 +5,7 @@
#include "time-util.h"
int read_fiemap(int fd, struct fiemap **ret);
-int parse_sleep_config(const char *verb, char ***modes, char ***states, usec_t *delay);
+int parse_sleep_config(const char *verb, bool *ret_allow, char ***ret_modes, char ***ret_states, usec_t *ret_delay);
int find_hibernate_location(char **device, char **type, size_t *size, size_t *used);
int can_sleep(const char *verb);
diff --git a/src/shared/specifier.c b/src/shared/specifier.c
index d698b42e07..b8f7537f7a 100644
--- a/src/shared/specifier.c
+++ b/src/shared/specifier.c
@@ -21,14 +21,13 @@
/*
* Generic infrastructure for replacing %x style specifiers in
* strings. Will call a callback for each replacement.
- *
*/
/* Any ASCII character or digit: our pool of potential specifiers,
* and "%" used for escaping. */
#define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
-int specifier_printf(const char *text, const Specifier table[], void *userdata, char **_ret) {
+int specifier_printf(const char *text, const Specifier table[], const void *userdata, char **_ret) {
size_t l, allocated = 0;
_cleanup_free_ char *ret = NULL;
char *t;
@@ -103,7 +102,7 @@ int specifier_printf(const char *text, const Specifier table[], void *userdata,
/* Generic handler for simple string replacements */
-int specifier_string(char specifier, void *data, void *userdata, char **ret) {
+int specifier_string(char specifier, const void *data, const void *userdata, char **ret) {
char *n;
n = strdup(strempty(data));
@@ -114,7 +113,7 @@ int specifier_string(char specifier, void *data, void *userdata, char **ret) {
return 0;
}
-int specifier_machine_id(char specifier, void *data, void *userdata, char **ret) {
+int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret) {
sd_id128_t id;
char *n;
int r;
@@ -131,7 +130,7 @@ int specifier_machine_id(char specifier, void *data, void *userdata, char **ret)
return 0;
}
-int specifier_boot_id(char specifier, void *data, void *userdata, char **ret) {
+int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret) {
sd_id128_t id;
char *n;
int r;
@@ -148,7 +147,7 @@ int specifier_boot_id(char specifier, void *data, void *userdata, char **ret) {
return 0;
}
-int specifier_host_name(char specifier, void *data, void *userdata, char **ret) {
+int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret) {
char *n;
n = gethostname_malloc();
@@ -159,7 +158,7 @@ int specifier_host_name(char specifier, void *data, void *userdata, char **ret)
return 0;
}
-int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret) {
+int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret) {
struct utsname uts;
char *n;
int r;
@@ -176,7 +175,25 @@ int specifier_kernel_release(char specifier, void *data, void *userdata, char **
return 0;
}
-int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
+int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret) {
+ char *t;
+
+ t = gid_to_name(getgid());
+ if (!t)
+ return -ENOMEM;
+
+ *ret = t;
+ return 0;
+}
+
+int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret) {
+ if (asprintf(ret, UID_FMT, getgid()) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int specifier_user_name(char specifier, const void *data, const void *userdata, char **ret) {
char *t;
/* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able
@@ -194,7 +211,7 @@ int specifier_user_name(char specifier, void *data, void *userdata, char **ret)
return 0;
}
-int specifier_user_id(char specifier, void *data, void *userdata, char **ret) {
+int specifier_user_id(char specifier, const void *data, const void *userdata, char **ret) {
if (asprintf(ret, UID_FMT, getuid()) < 0)
return -ENOMEM;
@@ -202,7 +219,7 @@ int specifier_user_id(char specifier, void *data, void *userdata, char **ret) {
return 0;
}
-int specifier_user_home(char specifier, void *data, void *userdata, char **ret) {
+int specifier_user_home(char specifier, const void *data, const void *userdata, char **ret) {
/* On PID 1 (which runs as root) this will not result in NSS,
* which is good. See above */
@@ -210,7 +227,7 @@ int specifier_user_home(char specifier, void *data, void *userdata, char **ret)
return get_home_dir(ret);
}
-int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
+int specifier_user_shell(char specifier, const void *data, const void *userdata, char **ret) {
/* On PID 1 (which runs as root) this will not result in NSS,
* which is good. See above */
@@ -218,7 +235,7 @@ int specifier_user_shell(char specifier, void *data, void *userdata, char **ret)
return get_shell(ret);
}
-int specifier_tmp_dir(char specifier, void *data, void *userdata, char **ret) {
+int specifier_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) {
const char *p;
char *copy;
int r;
@@ -235,7 +252,7 @@ int specifier_tmp_dir(char specifier, void *data, void *userdata, char **ret) {
return 0;
}
-int specifier_var_tmp_dir(char specifier, void *data, void *userdata, char **ret) {
+int specifier_var_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) {
const char *p;
char *copy;
int r;
diff --git a/src/shared/specifier.h b/src/shared/specifier.h
index e1895129d7..d0221ef714 100644
--- a/src/shared/specifier.h
+++ b/src/shared/specifier.h
@@ -3,30 +3,32 @@
#include "string-util.h"
-typedef int (*SpecifierCallback)(char specifier, void *data, void *userdata, char **ret);
+typedef int (*SpecifierCallback)(char specifier, const void *data, const void *userdata, char **ret);
typedef struct Specifier {
const char specifier;
const SpecifierCallback lookup;
- void *data;
+ const void *data;
} Specifier;
-int specifier_printf(const char *text, const Specifier table[], void *userdata, char **ret);
+int specifier_printf(const char *text, const Specifier table[], const void *userdata, char **ret);
-int specifier_string(char specifier, void *data, void *userdata, char **ret);
+int specifier_string(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_machine_id(char specifier, void *data, void *userdata, char **ret);
-int specifier_boot_id(char specifier, void *data, void *userdata, char **ret);
-int specifier_host_name(char specifier, void *data, void *userdata, char **ret);
-int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret);
+int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_user_name(char specifier, void *data, void *userdata, char **ret);
-int specifier_user_id(char specifier, void *data, void *userdata, char **ret);
-int specifier_user_home(char specifier, void *data, void *userdata, char **ret);
-int specifier_user_shell(char specifier, void *data, void *userdata, char **ret);
+int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_user_name(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_user_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_user_home(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_user_shell(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_tmp_dir(char specifier, void *data, void *userdata, char **ret);
-int specifier_var_tmp_dir(char specifier, void *data, void *userdata, char **ret);
+int specifier_tmp_dir(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_var_tmp_dir(char specifier, const void *data, const void *userdata, char **ret);
static inline char* specifier_escape(const char *string) {
return strreplace(string, "%", "%%");
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index 28740cb474..ee31c448fc 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2012 Harald Hoyer
-***/
#include <errno.h>
#include <fcntl.h>
@@ -19,6 +16,7 @@
#include "missing.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "stdio-util.h"
diff --git a/src/shared/switch-root.h b/src/shared/switch-root.h
index 83294a4525..f4d48cb431 100644
--- a/src/shared/switch-root.h
+++ b/src/shared/switch-root.h
@@ -1,10 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-/***
- Copyright © 2012 Harald Hoyer
-***/
-
#include <stdbool.h>
int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags);
diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c
index 326652273c..480e6c38a1 100644
--- a/src/shared/sysctl-util.c
+++ b/src/shared/sysctl-util.c
@@ -44,7 +44,7 @@ int sysctl_write(const char *property, const char *value) {
assert(property);
assert(value);
- log_debug("Setting '%s' to '%s'", property, value);
+ log_debug("Setting '%s' to '%.*s'.", property, (int) strcspn(value, NEWLINE), value);
p = strjoina("/proc/sys/", property);
fd = open(p, O_WRONLY|O_CLOEXEC);
diff --git a/src/shared/tests.c b/src/shared/tests.c
index 6b3df0aa07..11ea12ed69 100644
--- a/src/shared/tests.c
+++ b/src/shared/tests.c
@@ -1,13 +1,26 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <alloc-util.h>
-#include <fs-util.h>
-#include <libgen.h>
+#include <sched.h>
+#include <signal.h>
#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
#include <util.h>
-#include "tests.h"
+/* When we include libgen.h because we need dirname() we immediately
+ * undefine basename() since libgen.h defines it as a macro to the POSIX
+ * version which is really broken. We prefer GNU basename(). */
+#include <libgen.h>
+#undef basename
+
+#include "alloc-util.h"
+#include "env-file.h"
+#include "env-util.h"
+#include "fs-util.h"
+#include "log.h"
#include "path-util.h"
+#include "strv.h"
+#include "tests.h"
char* setup_fake_runtime_dir(void) {
char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p;
@@ -19,38 +32,120 @@ char* setup_fake_runtime_dir(void) {
return p;
}
-const char* get_testdata_dir(const char *suffix) {
+static void load_testdata_env(void) {
+ static bool called = false;
+ _cleanup_free_ char *s = NULL;
+ _cleanup_free_ char *envpath = NULL;
+ _cleanup_strv_free_ char **pairs = NULL;
+ char **k, **v;
+
+ if (called)
+ return;
+ called = true;
+
+ assert_se(readlink_and_make_absolute("/proc/self/exe", &s) >= 0);
+ dirname(s);
+
+ envpath = path_join(s, "systemd-runtest.env");
+ if (load_env_file_pairs(NULL, envpath, &pairs) < 0)
+ return;
+
+ STRV_FOREACH_PAIR(k, v, pairs)
+ setenv(*k, *v, 0);
+}
+
+const char* get_testdata_dir(void) {
const char *env;
- /* convenience: caller does not need to free result */
- static char testdir[PATH_MAX];
+
+ load_testdata_env();
/* if the env var is set, use that */
env = getenv("SYSTEMD_TEST_DATA");
- testdir[sizeof(testdir) - 1] = '\0';
- if (env) {
- if (access(env, F_OK) < 0) {
- fputs("ERROR: $SYSTEMD_TEST_DATA directory does not exist\n", stderr);
- exit(EXIT_FAILURE);
- }
- strncpy(testdir, env, sizeof(testdir) - 1);
- } else {
- _cleanup_free_ char *exedir = NULL;
- assert_se(readlink_and_make_absolute("/proc/self/exe", &exedir) >= 0);
-
- /* Check if we're running from the builddir. If so, use the compiled in path. */
- if (path_startswith(exedir, ABS_BUILD_DIR))
- assert_se(snprintf(testdir, sizeof(testdir), "%s/test", ABS_SRC_DIR) > 0);
- else
- /* Try relative path, according to the install-test layout */
- assert_se(snprintf(testdir, sizeof(testdir), "%s/testdata", dirname(exedir)) > 0);
-
- /* test this without the suffix, as it may contain a glob */
- if (access(testdir, F_OK) < 0) {
- fputs("ERROR: Cannot find testdata directory, set $SYSTEMD_TEST_DATA\n", stderr);
- exit(EXIT_FAILURE);
- }
+ if (!env)
+ env = SYSTEMD_TEST_DATA;
+ if (access(env, F_OK) < 0) {
+ fprintf(stderr, "ERROR: $SYSTEMD_TEST_DATA directory [%s] does not exist\n", env);
+ exit(EXIT_FAILURE);
+ }
+
+ return env;
+}
+
+const char* get_catalog_dir(void) {
+ const char *env;
+
+ load_testdata_env();
+
+ /* if the env var is set, use that */
+ env = getenv("SYSTEMD_CATALOG_DIR");
+ if (!env)
+ env = SYSTEMD_CATALOG_DIR;
+ if (access(env, F_OK) < 0) {
+ fprintf(stderr, "ERROR: $SYSTEMD_CATALOG_DIR directory [%s] does not exist\n", env);
+ exit(EXIT_FAILURE);
}
+ return env;
+}
+
+bool slow_tests_enabled(void) {
+ int r;
+
+ r = getenv_bool("SYSTEMD_SLOW_TESTS");
+ if (r >= 0)
+ return r;
+
+ if (r != -ENXIO)
+ log_warning_errno(r, "Cannot parse $SYSTEMD_SLOW_TESTS, ignoring.");
+ return SYSTEMD_SLOW_TESTS_DEFAULT;
+}
+
+void test_setup_logging(int level) {
+ log_set_max_level(level);
+ log_parse_environment();
+ log_open();
+}
+
+int log_tests_skipped(const char *message) {
+ log_notice("%s: %s, skipping tests.",
+ program_invocation_short_name, message);
+ return EXIT_TEST_SKIP;
+}
+
+int log_tests_skipped_errno(int r, const char *message) {
+ log_notice_errno(r, "%s: %s, skipping tests: %m",
+ program_invocation_short_name, message);
+ return EXIT_TEST_SKIP;
+}
+
+bool have_namespaces(void) {
+ siginfo_t si = {};
+ pid_t pid;
+
+ /* Checks whether namespaces are available. In some cases they aren't. We do this by calling unshare(), and we
+ * do so in a child process in order not to affect our own process. */
+
+ pid = fork();
+ assert_se(pid >= 0);
+
+ if (pid == 0) {
+ /* child */
+ if (unshare(CLONE_NEWNS) < 0)
+ _exit(EXIT_FAILURE);
+
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ assert_se(waitid(P_PID, pid, &si, WEXITED) >= 0);
+ assert_se(si.si_code == CLD_EXITED);
+
+ if (si.si_status == EXIT_SUCCESS)
+ return true;
+
+ if (si.si_status == EXIT_FAILURE)
+ return false;
- strncpy(testdir + strlen(testdir), suffix, sizeof(testdir) - strlen(testdir) - 1);
- return testdir;
+ assert_not_reached("unexpected exit code");
}
diff --git a/src/shared/tests.h b/src/shared/tests.h
index b88135ed93..718196f134 100644
--- a/src/shared/tests.h
+++ b/src/shared/tests.h
@@ -1,5 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <stdbool.h>
+
char* setup_fake_runtime_dir(void);
-const char* get_testdata_dir(const char *suffix);
+const char* get_testdata_dir(void);
+const char* get_catalog_dir(void);
+bool slow_tests_enabled(void);
+void test_setup_logging(int level);
+int log_tests_skipped(const char *message);
+int log_tests_skipped_errno(int r, const char *message);
+
+bool have_namespaces(void);
diff --git a/src/shared/tmpfile-util-label.c b/src/shared/tmpfile-util-label.c
new file mode 100644
index 0000000000..c12d7c1375
--- /dev/null
+++ b/src/shared/tmpfile-util-label.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/stat.h>
+
+#include "selinux-util.h"
+#include "tmpfile-util-label.h"
+#include "tmpfile-util.h"
+
+int fopen_temporary_label(
+ const char *target,
+ const char *path,
+ FILE **f,
+ char **temp_path) {
+
+ int r;
+
+ r = mac_selinux_create_file_prepare(target, S_IFREG);
+ if (r < 0)
+ return r;
+
+ r = fopen_temporary(path, f, temp_path);
+
+ mac_selinux_create_file_clear();
+
+ return r;
+}
diff --git a/src/shared/tmpfile-util-label.h b/src/shared/tmpfile-util-label.h
new file mode 100644
index 0000000000..97a875161b
--- /dev/null
+++ b/src/shared/tmpfile-util-label.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdio.h>
+
+/* These functions are split out of tmpfile-util.h (and not for example just flags to the functions they wrap) in order
+ * to optimize linking: This way, -lselinux is needed only for the callers of these functions that need selinux, but
+ * not for all */
+
+int fopen_temporary_label(const char *target, const char *path, FILE **f, char **temp_path);
diff --git a/src/shared/tomoyo-util.h b/src/shared/tomoyo-util.h
index 03e6975469..06e8227857 100644
--- a/src/shared/tomoyo-util.h
+++ b/src/shared/tomoyo-util.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <stdbool.h>
bool mac_tomoyo_use(void);
diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c
index 16a0eed841..4200032b3b 100644
--- a/src/shared/udev-util.c
+++ b/src/shared/udev-util.c
@@ -1,62 +1,171 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <errno.h>
#include <string.h>
-#include "fileio.h"
+#include "alloc-util.h"
+#include "env-file.h"
#include "log.h"
+#include "parse-util.h"
+#include "string-table.h"
#include "string-util.h"
#include "udev-util.h"
+#include "udev.h"
-int udev_parse_config(void) {
- _cleanup_free_ char *val = NULL;
- const char *log;
- size_t n;
+static const char* const resolve_name_timing_table[_RESOLVE_NAME_TIMING_MAX] = {
+ [RESOLVE_NAME_NEVER] = "never",
+ [RESOLVE_NAME_LATE] = "late",
+ [RESOLVE_NAME_EARLY] = "early",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(resolve_name_timing, ResolveNameTiming);
+
+int udev_parse_config_full(
+ unsigned *ret_children_max,
+ usec_t *ret_exec_delay_usec,
+ usec_t *ret_event_timeout_usec,
+ ResolveNameTiming *ret_resolve_name_timing) {
+
+ _cleanup_free_ char *log_val = NULL, *children_max = NULL, *exec_delay = NULL, *event_timeout = NULL, *resolve_names = NULL;
int r;
- r = parse_env_file(NULL, "/etc/udev/udev.conf", NEWLINE, "udev_log", &val, NULL);
- if (r == -ENOENT || !val)
+ r = parse_env_file(NULL, "/etc/udev/udev.conf",
+ "udev_log", &log_val,
+ "children_max", &children_max,
+ "exec_delay", &exec_delay,
+ "event_timeout", &event_timeout,
+ "resolve_names", &resolve_names);
+ if (r == -ENOENT)
return 0;
if (r < 0)
return r;
- /* unquote */
- n = strlen(val);
- if (n >= 2 &&
- ((val[0] == '"' && val[n-1] == '"') ||
- (val[0] == '\'' && val[n-1] == '\''))) {
- val[n - 1] = '\0';
- log = val + 1;
- } else
- log = val;
-
- /* we set the udev log level here explicitly, this is supposed
- * to regulate the code in libudev/ and udev/. */
- r = log_set_max_level_from_string_realm(LOG_REALM_UDEV, log);
- if (r < 0)
- log_debug_errno(r, "/etc/udev/udev.conf: failed to set udev log level '%s', ignoring: %m", log);
+ if (log_val) {
+ const char *log;
+ size_t n;
+
+ /* unquote */
+ n = strlen(log_val);
+ if (n >= 2 &&
+ ((log_val[0] == '"' && log_val[n-1] == '"') ||
+ (log_val[0] == '\'' && log_val[n-1] == '\''))) {
+ log_val[n - 1] = '\0';
+ log = log_val + 1;
+ } else
+ log = log_val;
+
+ /* we set the udev log level here explicitly, this is supposed
+ * to regulate the code in libudev/ and udev/. */
+ r = log_set_max_level_from_string_realm(LOG_REALM_UDEV, log);
+ if (r < 0)
+ log_debug_errno(r, "/etc/udev/udev.conf: failed to set udev log level '%s', ignoring: %m", log);
+ }
+
+ if (ret_children_max && children_max) {
+ r = safe_atou(children_max, ret_children_max);
+ if (r < 0)
+ log_notice_errno(r, "/etc/udev/udev.conf: failed to set parse children_max=%s, ignoring: %m", children_max);
+ }
+
+ if (ret_exec_delay_usec && exec_delay) {
+ r = parse_sec(exec_delay, ret_exec_delay_usec);
+ if (r < 0)
+ log_notice_errno(r, "/etc/udev/udev.conf: failed to set parse exec_delay=%s, ignoring: %m", exec_delay);
+ }
+
+ if (ret_event_timeout_usec && event_timeout) {
+ r = parse_sec(event_timeout, ret_event_timeout_usec);
+ if (r < 0)
+ log_notice_errno(r, "/etc/udev/udev.conf: failed to set parse event_timeout=%s, ignoring: %m", event_timeout);
+ }
+
+ if (ret_resolve_name_timing && resolve_names) {
+ ResolveNameTiming t;
+
+ t = resolve_name_timing_from_string(resolve_names);
+ if (t < 0)
+ log_notice("/etc/udev/udev.conf: failed to set parse resolve_names=%s, ignoring.", resolve_names);
+ else
+ *ret_resolve_name_timing = t;
+ }
return 0;
}
-int udev_device_new_from_stat_rdev(struct udev *udev, const struct stat *st, struct udev_device **ret) {
- struct udev_device *nd;
- char type;
+struct DeviceMonitorData {
+ const char *sysname;
+ sd_device *device;
+};
+
+static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
+ struct DeviceMonitorData *data = userdata;
+ const char *sysname;
+
+ assert(device);
+ assert(data);
+ assert(data->sysname);
+ assert(!data->device);
+
+ if (sd_device_get_sysname(device, &sysname) >= 0 && streq(sysname, data->sysname)) {
+ data->device = sd_device_ref(device);
+ return sd_event_exit(sd_device_monitor_get_event(monitor), 0);
+ }
- assert(udev);
- assert(st);
- assert(ret);
+ return 0;
+}
+
+int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_device **ret) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ struct DeviceMonitorData data = {};
+ int r;
- if (S_ISBLK(st->st_mode))
- type = 'b';
- else if (S_ISCHR(st->st_mode))
- type = 'c';
- else
- return -ENOTTY;
+ assert(device);
+ assert(subsystem);
- nd = udev_device_new_from_devnum(udev, type, st->st_rdev);
- if (!nd)
- return -errno;
+ if (sd_device_get_is_initialized(device) > 0) {
+ if (ret)
+ *ret = sd_device_ref(device);
+ return 0;
+ }
+
+ assert_se(sd_device_get_sysname(device, &data.sysname) >= 0);
+
+ /* Wait until the device is initialized, so that we can get access to the ID_PATH property */
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get default event: %m");
+
+ r = sd_device_monitor_new(&monitor);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire monitor: %m");
+
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, subsystem, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add %s subsystem match to monitor: %m", subsystem);
+
+ r = sd_device_monitor_attach_event(monitor, event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach event to device monitor: %m");
+
+ r = sd_device_monitor_start(monitor, device_monitor_handler, &data);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start device monitor: %m");
+
+ /* Check again, maybe things changed. Udev will re-read the db if the device wasn't initialized
+ * yet. */
+ if (sd_device_get_is_initialized(device) > 0) {
+ if (ret)
+ *ret = sd_device_ref(device);
+ return 0;
+ }
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
- *ret = nd;
+ if (ret)
+ *ret = TAKE_PTR(data.device);
return 0;
}
diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h
index f36c568b5a..932c4a9cd5 100644
--- a/src/shared/udev-util.h
+++ b/src/shared/udev-util.h
@@ -1,19 +1,29 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-#include "udev.h"
-#include "util.h"
+#include "sd-device.h"
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev*, udev_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_device*, udev_device_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_enumerate*, udev_enumerate_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_event*, udev_event_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_rules*, udev_rules_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_connection*, udev_ctrl_connection_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_msg*, udev_ctrl_msg_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref);
+#include "time-util.h"
-int udev_parse_config(void);
+typedef enum ResolveNameTiming {
+ RESOLVE_NAME_NEVER,
+ RESOLVE_NAME_LATE,
+ RESOLVE_NAME_EARLY,
+ _RESOLVE_NAME_TIMING_MAX,
+ _RESOLVE_NAME_TIMING_INVALID = -1,
+} ResolveNameTiming;
-int udev_device_new_from_stat_rdev(struct udev *udev, const struct stat *st, struct udev_device **ret);
+ResolveNameTiming resolve_name_timing_from_string(const char *s) _pure_;
+const char *resolve_name_timing_to_string(ResolveNameTiming i) _const_;
+
+int udev_parse_config_full(
+ unsigned *ret_children_max,
+ usec_t *ret_exec_delay_usec,
+ usec_t *ret_event_timeout_usec,
+ ResolveNameTiming *ret_resolve_name_timing);
+
+static inline int udev_parse_config(void) {
+ return udev_parse_config_full(NULL, NULL, NULL, NULL);
+}
+
+int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_device **ret);
diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c
index 434ce6ff4d..5fa7bd277e 100644
--- a/src/shared/uid-range.c
+++ b/src/shared/uid-range.c
@@ -8,6 +8,7 @@
#include "macro.h"
#include "uid-range.h"
#include "user-util.h"
+#include "util.h"
static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) {
assert(range);
@@ -45,20 +46,14 @@ static void uid_range_coalesce(UidRange **p, unsigned *n) {
}
}
-static int uid_range_compare(const void *a, const void *b) {
- const UidRange *x = a, *y = b;
-
- if (x->start < y->start)
- return -1;
- if (x->start > y->start)
- return 1;
+static int uid_range_compare(const UidRange *a, const UidRange *b) {
+ int r;
- if (x->nr < y->nr)
- return -1;
- if (x->nr > y->nr)
- return 1;
+ r = CMP(a->start, b->start);
+ if (r != 0)
+ return r;
- return 0;
+ return CMP(a->nr, b->nr);
}
int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) {
@@ -102,7 +97,7 @@ int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) {
x->nr = nr;
}
- qsort(*p, *n, sizeof(UidRange), uid_range_compare);
+ typesafe_qsort(*p, *n, uid_range_compare);
uid_range_coalesce(p, n);
return *n;
diff --git a/src/basic/verbs.c b/src/shared/verbs.c
index 1893ea3733..7c5dcb02a2 100644
--- a/src/basic/verbs.c
+++ b/src/shared/verbs.c
@@ -23,7 +23,7 @@ bool running_in_chroot_or_offline(void) {
/* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset", but
* not "start"/"restart" for example.
*
- * See doc/ENVIRONMENT.md for docs.
+ * See docs/ENVIRONMENT.md for docs.
*/
r = getenv_bool("SYSTEMD_OFFLINE");
if (r < 0 && r != -ENXIO)
@@ -59,7 +59,9 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
assert(argc >= optind);
left = argc - optind;
- name = argv[optind];
+ argv += optind;
+ optind = 0;
+ name = argv[0];
for (i = 0;; i++) {
bool found;
@@ -90,16 +92,14 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
left = 1;
if (verb->min_args != VERB_ANY &&
- (unsigned) left < verb->min_args) {
- log_error("Too few arguments.");
- return -EINVAL;
- }
+ (unsigned) left < verb->min_args)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too few arguments.");
if (verb->max_args != VERB_ANY &&
- (unsigned) left > verb->max_args) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
+ (unsigned) left > verb->max_args)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many arguments.");
if ((verb->flags & VERB_ONLINE_ONLY) && running_in_chroot_or_offline()) {
if (name)
@@ -116,7 +116,7 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
}
if (name)
- return verb->dispatch(left, argv + optind, userdata);
+ return verb->dispatch(left, argv, userdata);
else {
char* fake[2] = {
(char*) verb->verb,
diff --git a/src/basic/verbs.h b/src/shared/verbs.h
index e174255a76..010c0df3fd 100644
--- a/src/basic/verbs.h
+++ b/src/shared/verbs.h
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <stdbool.h>
+
#define VERB_ANY ((unsigned) -1)
typedef enum VerbFlags {
diff --git a/src/shared/vlan-util.c b/src/shared/vlan-util.c
index 400994a354..2f9df7dd1b 100644
--- a/src/shared/vlan-util.c
+++ b/src/shared/vlan-util.c
@@ -9,6 +9,9 @@ int parse_vlanid(const char *p, uint16_t *ret) {
uint16_t id;
int r;
+ assert(p);
+ assert(ret);
+
r = safe_atou16(p, &id);
if (r < 0)
return r;
diff --git a/src/basic/web-util.c b/src/shared/web-util.c
index 82221af194..edf650d200 100644
--- a/src/basic/web-util.c
+++ b/src/shared/web-util.c
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include "string-util.h"
+#include "strv.h"
#include "utf8.h"
#include "web-util.h"
@@ -13,7 +14,7 @@ bool http_etag_is_valid(const char *etag) {
if (!endswith(etag, "\""))
return false;
- if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
+ if (!STARTSWITH_SET(etag, "\"", "W/\""))
return false;
return true;
@@ -25,9 +26,7 @@ bool http_url_is_valid(const char *url) {
if (isempty(url))
return false;
- p = startswith(url, "http://");
- if (!p)
- p = startswith(url, "https://");
+ p = STARTSWITH_SET(url, "http://", "https://");
if (!p)
return false;
@@ -46,12 +45,7 @@ bool documentation_url_is_valid(const char *url) {
if (http_url_is_valid(url))
return true;
- p = startswith(url, "file:/");
- if (!p)
- p = startswith(url, "info:");
- if (!p)
- p = startswith(url, "man:");
-
+ p = STARTSWITH_SET(url, "file:/", "info:", "man:");
if (isempty(p))
return false;
diff --git a/src/basic/web-util.h b/src/shared/web-util.h
index c9e67e5c0a..c9e67e5c0a 100644
--- a/src/basic/web-util.h
+++ b/src/shared/web-util.h
diff --git a/src/basic/xml.c b/src/shared/xml.c
index cb34d870c1..cb34d870c1 100644
--- a/src/basic/xml.c
+++ b/src/shared/xml.c
diff --git a/src/basic/xml.h b/src/shared/xml.h
index 8da2ff5f75..8da2ff5f75 100644
--- a/src/basic/xml.h
+++ b/src/shared/xml.h
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index f26aa453c9..5b7984a6f2 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -11,12 +11,14 @@
#include "sd-messages.h"
-#include "parse-util.h"
#include "def.h"
#include "exec-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
+#include "main-func.h"
+#include "parse-util.h"
+#include "pretty-print.h"
#include "sleep-config.h"
#include "stdio-util.h"
#include "string-util.h"
@@ -40,22 +42,26 @@ static int write_hibernate_location_info(void) {
return log_debug_errno(r, "Unable to find hibernation location: %m");
/* if it's a swap partition, we just write the disk to /sys/power/resume */
- if (streq(type, "partition"))
- return write_string_file("/sys/power/resume", device, 0);
- else if (!streq(type, "file"))
- return log_debug_errno(EINVAL, "Invalid hibernate type %s: %m",
- type);
+ if (streq(type, "partition")) {
+ r = write_string_file("/sys/power/resume", device, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0)
+ return log_debug_errno(r, "Faileed to write partitoin device to /sys/power/resume: %m");
+
+ return r;
+ }
+ if (!streq(type, "file"))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid hibernate type: %s", type);
/* Only available in 4.17+ */
- if (access("/sys/power/resume_offset", F_OK) < 0) {
- if (errno == ENOENT)
+ if (access("/sys/power/resume_offset", W_OK) < 0) {
+ if (errno == ENOENT) {
+ log_debug("Kernel too old, can't configure resume offset, ignoring.");
return 0;
- return log_debug_errno(errno, "/sys/power/resume_offset unavailable: %m");
- }
+ }
- r = access("/sys/power/resume_offset", W_OK);
- if (r < 0)
return log_debug_errno(errno, "/sys/power/resume_offset not writeable: %m");
+ }
fd = open(device, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
@@ -63,26 +69,25 @@ static int write_hibernate_location_info(void) {
r = fstat(fd, &stb);
if (r < 0)
return log_debug_errno(errno, "Unable to stat %s: %m", device);
+
r = read_fiemap(fd, &fiemap);
if (r < 0)
- return log_debug_errno(r, "Unable to read extent map for '%s': %m",
- device);
- if (fiemap->fm_mapped_extents == 0) {
- log_debug("No extents found in '%s'", device);
- return -EINVAL;
- }
+ return log_debug_errno(r, "Unable to read extent map for '%s': %m", device);
+ if (fiemap->fm_mapped_extents == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "No extents found in '%s'", device);
+
offset = fiemap->fm_extents[0].fe_physical / page_size();
xsprintf(offset_str, "%" PRIu64, offset);
- r = write_string_file("/sys/power/resume_offset", offset_str, 0);
+ r = write_string_file("/sys/power/resume_offset", offset_str, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
- return log_debug_errno(r, "Failed to write offset '%s': %m",
- offset_str);
+ return log_debug_errno(r, "Failed to write offset '%s': %m", offset_str);
xsprintf(device_str, "%lx", (unsigned long)stb.st_dev);
- r = write_string_file("/sys/power/resume", device_str, 0);
+ r = write_string_file("/sys/power/resume", device_str, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
- return log_debug_errno(r, "Failed to write device '%s': %m",
- device_str);
+ return log_debug_errno(r, "Failed to write device '%s': %m", device_str);
+
return 0;
}
@@ -93,13 +98,12 @@ static int write_mode(char **modes) {
STRV_FOREACH(mode, modes) {
int k;
- k = write_string_file("/sys/power/disk", *mode, 0);
- if (k == 0)
+ k = write_string_file("/sys/power/disk", *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (k >= 0)
return 0;
- log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m",
- *mode);
- if (r == 0)
+ log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", *mode);
+ if (r >= 0)
r = k;
}
@@ -113,12 +117,11 @@ static int write_state(FILE **f, char **states) {
STRV_FOREACH(state, states) {
int k;
- k = write_string_stream(*f, *state, 0);
- if (k == 0)
+ k = write_string_stream(*f, *state, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (k >= 0)
return 0;
- log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m",
- *state);
- if (r == 0)
+ log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", *state);
+ if (r >= 0)
r = k;
fclose(*f);
@@ -131,7 +134,6 @@ static int write_state(FILE **f, char **states) {
}
static int execute(char **modes, char **states) {
-
char *arguments[] = {
NULL,
(char*) "pre",
@@ -152,6 +154,8 @@ static int execute(char **modes, char **states) {
if (!f)
return log_error_errno(errno, "Failed to open /sys/power/state: %m");
+ setvbuf(f, NULL, _IONBF, 0);
+
/* Configure the hibernation mode */
if (!strv_isempty(modes)) {
r = write_hibernate_location_info();
@@ -162,7 +166,7 @@ static int execute(char **modes, char **states) {
return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");;
}
- execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL);
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
@@ -171,39 +175,46 @@ static int execute(char **modes, char **states) {
r = write_state(&f, states);
if (r < 0)
- return log_error_errno(r, "Failed to write /sys/power/state: %m");
-
- log_struct(LOG_INFO,
- "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
- LOG_MESSAGE("System resumed."),
- "SLEEP=%s", arg_verb);
+ log_struct_errno(LOG_ERR, r,
+ "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
+ LOG_MESSAGE("Failed to suspend system. System resumed again: %m"),
+ "SLEEP=%s", arg_verb);
+ else
+ log_struct(LOG_INFO,
+ "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
+ LOG_MESSAGE("System resumed."),
+ "SLEEP=%s", arg_verb);
arguments[1] = (char*) "post";
- execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL);
return r;
}
-static int read_wakealarm(uint64_t *result) {
+static int rtc_read_time(uint64_t *ret_sec) {
_cleanup_free_ char *t = NULL;
+ int r;
- if (read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t) >= 0)
- return safe_atou64(t, result);
- return -EBADF;
-}
+ r = read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read RTC time: %m");
+
+ r = safe_atou64(t, ret_sec);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse RTC time '%s': %m", t);
-static int write_wakealarm(const char *str) {
+ return 0;
+}
- _cleanup_fclose_ FILE *f = NULL;
+static int rtc_write_wake_alarm(uint64_t sec) {
+ char buf[DECIMAL_STR_MAX(uint64_t)];
int r;
- f = fopen("/sys/class/rtc/rtc0/wakealarm", "we");
- if (!f)
- return log_error_errno(errno, "Failed to open /sys/class/rtc/rtc0/wakealarm: %m");
+ xsprintf(buf, "%" PRIu64, sec);
- r = write_string_stream(f, str, 0);
+ r = write_string_file("/sys/class/rtc/rtc0/wakealarm", buf, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
- return log_error_errno(r, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", str);
+ return log_error_errno(r, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", buf);
return 0;
}
@@ -212,67 +223,84 @@ static int execute_s2h(usec_t hibernate_delay_sec) {
_cleanup_strv_free_ char **hibernate_modes = NULL, **hibernate_states = NULL,
**suspend_modes = NULL, **suspend_states = NULL;
- usec_t orig_time, cmp_time;
- char time_str[DECIMAL_STR_MAX(uint64_t)];
+ usec_t original_time, wake_time, cmp_time;
int r;
- r = parse_sleep_config("suspend", &suspend_modes, &suspend_states,
- NULL);
+ r = parse_sleep_config("suspend", NULL, &suspend_modes, &suspend_states, NULL);
if (r < 0)
return r;
- r = parse_sleep_config("hibernate", &hibernate_modes,
- &hibernate_states, NULL);
+ r = parse_sleep_config("hibernate", NULL, &hibernate_modes, &hibernate_states, NULL);
if (r < 0)
return r;
- r = read_wakealarm(&orig_time);
+ r = rtc_read_time(&original_time);
if (r < 0)
- return log_error_errno(errno, "Failed to read time: %d", r);
-
- orig_time += hibernate_delay_sec / USEC_PER_SEC;
- xsprintf(time_str, "%" PRIu64, orig_time);
+ return r;
- r = write_wakealarm(time_str);
+ wake_time = original_time + DIV_ROUND_UP(hibernate_delay_sec, USEC_PER_SEC);
+ r = rtc_write_wake_alarm(wake_time);
if (r < 0)
return r;
- log_debug("Set RTC wake alarm for %s", time_str);
+ log_debug("Set RTC wake alarm for %" PRIu64, wake_time);
r = execute(suspend_modes, suspend_states);
if (r < 0)
return r;
- r = read_wakealarm(&cmp_time);
+ /* Reset RTC right-away */
+ r = rtc_write_wake_alarm(0);
if (r < 0)
- return log_error_errno(errno, "Failed to read time: %d", r);
+ return r;
- /* reset RTC */
- r = write_wakealarm("0");
+ r = rtc_read_time(&cmp_time);
if (r < 0)
return r;
log_debug("Woke up at %"PRIu64, cmp_time);
- /* if woken up after alarm time, hibernate */
- if (cmp_time >= orig_time)
- r = execute(hibernate_modes, hibernate_states);
+ if (cmp_time < wake_time) /* We woke up before the alarm time, we are done. */
+ return 0;
+
+ /* If woken up after alarm time, hibernate */
+ r = execute(hibernate_modes, hibernate_states);
+ if (r < 0) {
+ log_notice("Couldn't hibernate, will try to suspend again.");
+ r = execute(suspend_modes, suspend_states);
+ if (r < 0) {
+ log_notice("Could neither hibernate nor suspend again, giving up.");
+ return r;
+ }
+ }
- return r;
+ return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-suspend.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s COMMAND\n\n"
"Suspend the system, hibernate the system, or both.\n\n"
- "Commands:\n"
- " -h --help Show this help and exit\n"
- " --version Print version string and exit\n"
- " suspend Suspend the system\n"
- " hibernate Hibernate the system\n"
- " hybrid-sleep Both hibernate and suspend the system\n"
+ " -h --help Show this help and exit\n"
+ " --version Print version string and exit\n"
+ "\nCommands:\n"
+ " suspend Suspend the system\n"
+ " hibernate Hibernate the system\n"
+ " hybrid-sleep Both hibernate and suspend the system\n"
" suspend-then-hibernate Initially suspend and then hibernate\n"
- " the system after a fixed period of time\n"
- , program_invocation_short_name);
+ " the system after a fixed period of time\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -294,8 +322,7 @@ static int parse_argv(int argc, char *argv[]) {
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
switch(c) {
case 'h':
- help();
- return 0; /* done */
+ return help();
case ARG_VERSION:
return version();
@@ -307,46 +334,45 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (argc - optind != 1) {
- log_error("Usage: %s COMMAND",
- program_invocation_short_name);
- return -EINVAL;
- }
+ if (argc - optind != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Usage: %s COMMAND",
+ program_invocation_short_name);
arg_verb = argv[optind];
- if (!streq(arg_verb, "suspend") &&
- !streq(arg_verb, "hibernate") &&
- !streq(arg_verb, "hybrid-sleep") &&
- !streq(arg_verb, "suspend-then-hibernate")) {
- log_error("Unknown command '%s'.", arg_verb);
- return -EINVAL;
- }
+ if (!STR_IN_SET(arg_verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown command '%s'.", arg_verb);
return 1 /* work to do */;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
+ bool allow;
_cleanup_strv_free_ char **modes = NULL, **states = NULL;
usec_t delay = 0;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
- r = parse_sleep_config(arg_verb, &modes, &states, &delay);
+ r = parse_sleep_config(arg_verb, &allow, &modes, &states, &delay);
if (r < 0)
- goto finish;
+ return r;
+
+ if (!allow)
+ return log_error_errno(SYNTHETIC_ERRNO(EACCES),
+ "Sleep mode \"%s\" is disabled by configuration, refusing.",
+ arg_verb);
if (streq(arg_verb, "suspend-then-hibernate"))
- r = execute_s2h(delay);
+ return execute_s2h(delay);
else
- r = execute(modes, states);
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return execute(modes, states);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c
index 3d07483eb4..f882a665a8 100644
--- a/src/socket-proxy/socket-proxyd.c
+++ b/src/socket-proxy/socket-proxyd.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2013 David Strauss
- ***/
#include <errno.h>
#include <fcntl.h>
@@ -21,11 +18,14 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "log.h"
+#include "main-func.h"
+#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
+#include "resolve-private.h"
#include "set.h"
#include "socket-util.h"
#include "string-util.h"
-#include "parse-util.h"
#include "util.h"
#define BUFFER_SIZE (256 * 1024)
@@ -76,7 +76,7 @@ static void connection_free(Connection *c) {
free(c);
}
-static void context_free(Context *context) {
+static void context_clear(Context *context) {
assert(context);
set_free_with_destructor(context->listen, sd_event_source_unref);
@@ -348,9 +348,7 @@ fail:
return 0; /* ignore errors, continue serving */
}
-static int resolve_cb(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) {
- Connection *c = userdata;
-
+static int resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, Connection *c) {
assert(q);
assert(c);
@@ -380,17 +378,16 @@ static int resolve_remote(Connection *c) {
const char *node, *service;
int r;
- if (path_is_absolute(arg_remote_host)) {
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path));
- return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un));
- }
+ if (IN_SET(arg_remote_host[0], '/', '@')) {
+ int salen;
- if (arg_remote_host[0] == '@') {
- sa.un.sun_family = AF_UNIX;
- sa.un.sun_path[0] = 0;
- strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-1);
- return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+ 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");
+ goto fail;
+ }
+
+ return connection_start(c, &sa.sa, salen);
}
service = strrchr(arg_remote_host, ':');
@@ -403,7 +400,7 @@ static int resolve_remote(Connection *c) {
}
log_debug("Looking up address info for %s:%s", node, service);
- r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c);
+ r = resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_handler, NULL, c);
if (r < 0) {
log_error_errno(r, "Failed to resolve remote host: %m");
goto fail;
@@ -508,10 +505,9 @@ static int add_listen_socket(Context *context, int fd) {
r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
if (r < 0)
return log_error_errno(r, "Failed to determine socket type: %m");
- if (r == 0) {
- log_error("Passed in socket is not a stream socket.");
- return -EINVAL;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Passed in socket is not a stream socket.");
r = fd_nonblock(fd, true);
if (r < 0)
@@ -537,14 +533,26 @@ static int add_listen_socket(Context *context, int fd) {
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-socket-proxyd", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%1$s [HOST:PORT]\n"
"%1$s [SOCKET]\n\n"
"Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
" -c --connections-max= Set the maximum number of connections to be accepted\n"
" -h --help Show this help\n"
- " --version Show package version\n",
- program_invocation_short_name);
+ " --version Show package version\n"
+ "\nSee the %2$s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -571,8 +579,10 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
+
+ case ARG_VERSION:
+ return version();
case 'c':
r = safe_atou(optarg, &arg_connections_max);
@@ -581,16 +591,12 @@ static int parse_argv(int argc, char *argv[]) {
return r;
}
- if (arg_connections_max < 1) {
- log_error("Connection limit is too low.");
- return -EINVAL;
- }
+ if (arg_connections_max < 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Connection limit is too low.");
break;
- case ARG_VERSION:
- return version();
-
case '?':
return -EINVAL;
@@ -598,22 +604,20 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind >= argc) {
- log_error("Not enough parameters.");
- return -EINVAL;
- }
+ if (optind >= argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not enough parameters.");
- if (argc != optind+1) {
- log_error("Too many parameters.");
- return -EINVAL;
- }
+ if (argc != optind+1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many parameters.");
arg_remote_host = argv[optind];
return 1;
}
-int main(int argc, char *argv[]) {
- Context context = {};
+static int run(int argc, char *argv[]) {
+ _cleanup_(context_clear) Context context = {};
int r, n, fd;
log_parse_environment();
@@ -621,53 +625,41 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = sd_event_default(&context.event);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate event loop: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
r = sd_resolve_default(&context.resolve);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate resolver: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate resolver: %m");
r = sd_resolve_attach_event(context.resolve, context.event, 0);
- if (r < 0) {
- log_error_errno(r, "Failed to attach resolver: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach resolver: %m");
sd_event_set_watchdog(context.event, true);
- n = sd_listen_fds(1);
- if (n < 0) {
- log_error("Failed to receive sockets from parent.");
- r = n;
- goto finish;
- } else if (n == 0) {
- log_error("Didn't get any sockets passed in.");
- r = -EINVAL;
- goto finish;
- }
+ r = sd_listen_fds(1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to receive sockets from parent.");
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Didn't get any sockets passed in.");
+
+ n = r;
for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
r = add_listen_socket(&context, fd);
if (r < 0)
- goto finish;
+ return r;
}
r = sd_event_loop(context.event);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- goto finish;
- }
-
-finish:
- context_free(&context);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c
index 519a92a094..3a21aa4aed 100644
--- a/src/stdio-bridge/stdio-bridge.c
+++ b/src/stdio-bridge/stdio-bridge.c
@@ -10,15 +10,18 @@
#include "sd-bus.h"
#include "sd-daemon.h"
+#include "alloc-util.h"
+#include "build.h"
#include "bus-internal.h"
#include "bus-util.h"
-#include "build.h"
#include "log.h"
+#include "main-func.h"
#include "util.h"
#define DEFAULT_BUS_PATH "unix:path=/run/dbus/system_bus_socket"
-const char *arg_bus_path = DEFAULT_BUS_PATH;
+static const char *arg_bus_path = DEFAULT_BUS_PATH;
+static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static int help(void) {
@@ -26,7 +29,8 @@ static int help(void) {
"STDIO or socket-activatable proxy to a given DBus endpoint.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
- " -p --bus-path=PATH Path to the kernel bus (default: %s)\n",
+ " -p --bus-path=PATH Path to the kernel bus (default: %s)\n"
+ " -M --machine=MACHINE Name of machine to connect to\n",
program_invocation_short_name, DEFAULT_BUS_PATH);
return 0;
@@ -36,12 +40,14 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
+ ARG_MACHINE,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "bus-path", required_argument, NULL, 'p' },
+ { "machine", required_argument, NULL, 'M' },
{},
};
@@ -66,18 +72,25 @@ static int parse_argv(int argc, char *argv[]) {
case 'p':
arg_bus_path = optarg;
+
break;
+ case 'M':
+ arg_bus_path = optarg;
+
+ arg_transport = BUS_TRANSPORT_MACHINE;
+
+ break;
default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown option code %c", c);
}
}
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_(sd_bus_unrefp) sd_bus *a = NULL, *b = NULL;
sd_id128_t server_id;
bool is_unix;
@@ -89,7 +102,7 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = sd_listen_fds(0);
if (r == 0) {
@@ -100,7 +113,7 @@ int main(int argc, char *argv[]) {
out_fd = SD_LISTEN_FDS_START;
} else {
log_error("Illegal number of file descriptors passed.");
- goto finish;
+ return -EINVAL;
}
is_unix =
@@ -108,70 +121,51 @@ int main(int argc, char *argv[]) {
sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
r = sd_bus_new(&a);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate bus: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate bus: %m");
- r = sd_bus_set_address(a, arg_bus_path);
- if (r < 0) {
- log_error_errno(r, "Failed to set address to connect to: %m");
- goto finish;
- }
+ if (arg_transport == BUS_TRANSPORT_MACHINE)
+ r = bus_set_address_system_machine(a, arg_bus_path);
+ else
+ r = sd_bus_set_address(a, arg_bus_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set address to connect to: %m");
r = sd_bus_negotiate_fds(a, is_unix);
- if (r < 0) {
- log_error_errno(r, "Failed to set FD negotiation: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set FD negotiation: %m");
r = sd_bus_start(a);
- if (r < 0) {
- log_error_errno(r, "Failed to start bus client: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to start bus client: %m");
r = sd_bus_get_bus_id(a, &server_id);
- if (r < 0) {
- log_error_errno(r, "Failed to get server ID: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get server ID: %m");
r = sd_bus_new(&b);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate bus: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate bus: %m");
r = sd_bus_set_fd(b, in_fd, out_fd);
- if (r < 0) {
- log_error_errno(r, "Failed to set fds: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set fds: %m");
r = sd_bus_set_server(b, 1, server_id);
- if (r < 0) {
- log_error_errno(r, "Failed to set server mode: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set server mode: %m");
r = sd_bus_negotiate_fds(b, is_unix);
- if (r < 0) {
- log_error_errno(r, "Failed to set FD negotiation: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set FD negotiation: %m");
r = sd_bus_set_anonymous(b, true);
- if (r < 0) {
- log_error_errno(r, "Failed to set anonymous authentication: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set anonymous authentication: %m");
r = sd_bus_start(b);
- if (r < 0) {
- log_error_errno(r, "Failed to start bus client: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to start bus client: %m");
for (;;) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
@@ -180,74 +174,51 @@ int main(int argc, char *argv[]) {
struct timespec _ts, *ts;
r = sd_bus_process(a, &m);
- if (r < 0) {
- log_error_errno(r, "Failed to process bus a: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to process bus a: %m");
if (m) {
r = sd_bus_send(b, m, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to send message: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to send message: %m");
}
if (r > 0)
continue;
r = sd_bus_process(b, &m);
- if (r < 0) {
+ if (r < 0)
/* treat 'connection reset by peer' as clean exit condition */
- if (r == -ECONNRESET)
- r = 0;
-
- goto finish;
- }
+ return r == -ECONNRESET ? 0 : r;
if (m) {
r = sd_bus_send(a, m, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to send message: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to send message: %m");
}
if (r > 0)
continue;
fd = sd_bus_get_fd(a);
- if (fd < 0) {
- r = fd;
- log_error_errno(r, "Failed to get fd: %m");
- goto finish;
- }
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to get fd: %m");
events_a = sd_bus_get_events(a);
- if (events_a < 0) {
- r = events_a;
- log_error_errno(r, "Failed to get events mask: %m");
- goto finish;
- }
+ if (events_a < 0)
+ return log_error_errno(events_a, "Failed to get events mask: %m");
r = sd_bus_get_timeout(a, &timeout_a);
- if (r < 0) {
- log_error_errno(r, "Failed to get timeout: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get timeout: %m");
events_b = sd_bus_get_events(b);
- if (events_b < 0) {
- r = events_b;
- log_error_errno(r, "Failed to get events mask: %m");
- goto finish;
- }
+ if (events_b < 0)
+ return log_error_errno(events_b, "Failed to get events mask: %m");
r = sd_bus_get_timeout(b, &timeout_b);
- if (r < 0) {
- log_error_errno(r, "Failed to get timeout: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get timeout: %m");
t = timeout_a;
if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
@@ -275,12 +246,11 @@ int main(int argc, char *argv[]) {
r = ppoll(p, ELEMENTSOF(p), ts, NULL);
}
- if (r < 0) {
- log_error_errno(errno, "ppoll() failed: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(errno, "ppoll() failed: %m");
}
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/sulogin-shell/sulogin-shell.c b/src/sulogin-shell/sulogin-shell.c
index d0e5a89f1f..6d65efbb9e 100644
--- a/src/sulogin-shell/sulogin-shell.c
+++ b/src/sulogin-shell/sulogin-shell.c
@@ -9,6 +9,7 @@
#include "bus-util.h"
#include "bus-error.h"
#include "def.h"
+#include "env-util.h"
#include "log.h"
#include "process-util.h"
#include "sd-bus.h"
@@ -59,16 +60,16 @@ static int start_default_target(sd_bus *bus) {
"ss", "default.target", "isolate");
if (r < 0)
- log_error("Failed to start default target: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to start default target: %s", bus_error_message(&error, r));
- return r;
+ return 0;
}
static int fork_wait(const char* const cmdline[]) {
pid_t pid;
int r;
- r = safe_fork("(sulogin)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ r = safe_fork("(sulogin)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {
@@ -89,16 +90,22 @@ static void print_mode(const char* mode) {
}
int main(int argc, char *argv[]) {
- static const char* const sulogin_cmdline[] = {SULOGIN, NULL};
+ const char* sulogin_cmdline[] = {
+ SULOGIN,
+ NULL, /* --force */
+ NULL
+ };
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
print_mode(argc > 1 ? argv[1] : "");
+ if (getenv_bool("SYSTEMD_SULOGIN_FORCE") > 0)
+ /* allows passwordless logins if root account is locked. */
+ sulogin_cmdline[1] = "--force";
+
(void) fork_wait(sulogin_cmdline);
r = bus_connect_system_systemd(&bus);
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c
index 0151f7dabe..c67d790323 100644
--- a/src/sysctl/sysctl.c
+++ b/src/sysctl/sysctl.c
@@ -14,17 +14,20 @@
#include "fileio.h"
#include "hashmap.h"
#include "log.h"
+#include "main-func.h"
#include "pager.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "string-util.h"
#include "strv.h"
#include "sysctl-util.h"
-#include "terminal-util.h"
#include "util.h"
static char **arg_prefixes = NULL;
static bool arg_cat_config = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
+
+STATIC_DESTRUCTOR_REGISTER(arg_prefixes, strv_freep);
static int apply_all(OrderedHashmap *sysctl_options) {
char *property, *value;
@@ -112,7 +115,7 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign
value = strchr(p, '=');
if (!value) {
- log_error("Line is not an assignment at '%s:%u': %s", path, c, value);
+ log_error("Line is not an assignment at '%s:%u': %s", path, c, p);
if (r == 0)
r = -EINVAL;
@@ -160,7 +163,14 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign
return r;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-sysctl.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
"Applies kernel sysctl settings.\n\n"
" -h --help Show this help\n"
@@ -168,7 +178,12 @@ static void help(void) {
" --cat-config Show configuration files\n"
" --prefix=PATH Only apply rules with the specified prefix\n"
" --no-pager Do not pipe output into a pager\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -199,8 +214,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -232,7 +246,7 @@ static int parse_argv(int argc, char *argv[]) {
}
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case '?':
@@ -242,33 +256,28 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (arg_cat_config && argc > optind) {
- log_error("Positional arguments are not allowed with --cat-config");
- return -EINVAL;
- }
+ if (arg_cat_config && argc > optind)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Positional arguments are not allowed with --cat-config");
return 1;
}
-int main(int argc, char *argv[]) {
- OrderedHashmap *sysctl_options = NULL;
- int r = 0, k;
+static int run(int argc, char *argv[]) {
+ _cleanup_(ordered_hashmap_free_free_freep) OrderedHashmap *sysctl_options = NULL;
+ int r, k;
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
sysctl_options = ordered_hashmap_new(&path_hash_ops);
- if (!sysctl_options) {
- r = log_oom();
- goto finish;
- }
+ if (!sysctl_options)
+ return log_oom();
r = 0;
@@ -285,16 +294,13 @@ int main(int argc, char *argv[]) {
char **f;
r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("sysctl.d"));
- if (r < 0) {
- log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
if (arg_cat_config) {
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
- r = cat_files(NULL, files, 0);
- goto finish;
+ return cat_files(NULL, files, 0);
}
STRV_FOREACH(f, files) {
@@ -308,11 +314,7 @@ int main(int argc, char *argv[]) {
if (k < 0 && r == 0)
r = k;
-finish:
- pager_close();
-
- ordered_hashmap_free_free_free(sysctl_options);
- strv_free(arg_prefixes);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c
index 0f77042f48..77e265d710 100644
--- a/src/system-update-generator/system-update-generator.c
+++ b/src/system-update-generator/system-update-generator.c
@@ -4,6 +4,7 @@
#include <unistd.h>
#include "fs-util.h"
+#include "generator.h"
#include "log.h"
#include "proc-cmdline.h"
#include "special.h"
@@ -14,7 +15,7 @@
* Implements the logic described in systemd.offline-updates(7).
*/
-static const char *arg_dest = "/tmp";
+static const char *arg_dest = NULL;
static int generate_symlink(void) {
const char *p = NULL;
@@ -50,31 +51,20 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
return 0;
}
-int main(int argc, char *argv[]) {
- int r, k;
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+ int r;
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1)
- arg_dest = argv[2];
-
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
+ assert_se(arg_dest = dest_early);
r = generate_symlink();
+ if (r < 0)
+ return r;
- if (r > 0) {
- k = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
- if (k < 0)
- log_warning_errno(k, "Failed to parse kernel command line, ignoring: %m");
- }
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index f072ad0c31..629f9cb505 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2013 Marc-Antoine Perennou
-***/
#include <errno.h>
#include <fcntl.h>
@@ -18,6 +15,7 @@
#include "sd-bus.h"
#include "sd-daemon.h"
+#include "sd-event.h"
#include "sd-login.h"
#include "alloc-util.h"
@@ -36,7 +34,6 @@
#include "escape.h"
#include "exit-status.h"
#include "fd-util.h"
-#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "glob-util.h"
@@ -50,11 +47,14 @@
#include "log.h"
#include "logs-show.h"
#include "macro.h"
+#include "main-func.h"
#include "mkdir.h"
#include "pager.h"
#include "parse-util.h"
#include "path-lookup.h"
#include "path-util.h"
+#include "pretty-print.h"
+#include "proc-cmdline.h"
#include "process-util.h"
#include "reboot-util.h"
#include "rlimit-util.h"
@@ -69,6 +69,7 @@
#include "string-table.h"
#include "strv.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "unit-def.h"
#include "unit-name.h"
#include "user-util.h"
@@ -112,7 +113,7 @@ static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
static bool arg_wait = false;
static bool arg_no_block = false;
static bool arg_no_legend = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static bool arg_no_wtmp = false;
static bool arg_no_sync = false;
static bool arg_no_wall = false;
@@ -315,25 +316,24 @@ static bool install_client_side(void) {
return false;
}
-static int compare_unit_info(const void *a, const void *b) {
- const UnitInfo *u = a, *v = b;
+static int compare_unit_info(const UnitInfo *a, const UnitInfo *b) {
const char *d1, *d2;
int r;
/* First, order by machine */
- if (!u->machine && v->machine)
+ if (!a->machine && b->machine)
return -1;
- if (u->machine && !v->machine)
+ if (a->machine && !b->machine)
return 1;
- if (u->machine && v->machine) {
- r = strcasecmp(u->machine, v->machine);
+ if (a->machine && b->machine) {
+ r = strcasecmp(a->machine, b->machine);
if (r != 0)
return r;
}
/* Second, order by unit type */
- d1 = strrchr(u->id, '.');
- d2 = strrchr(v->id, '.');
+ d1 = strrchr(a->id, '.');
+ d2 = strrchr(b->id, '.');
if (d1 && d2) {
r = strcasecmp(d1, d2);
if (r != 0)
@@ -341,7 +341,7 @@ static int compare_unit_info(const void *a, const void *b) {
}
/* Third, order by name */
- return strcasecmp(u->id, v->id);
+ return strcasecmp(a->id, b->id);
}
static const char* unit_type_suffix(const char *name) {
@@ -391,6 +391,7 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
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");
@@ -476,7 +477,7 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
printf("%-*.*s%s\n",
desc_len,
- !arg_full && arg_no_pager ? (int) desc_len : -1,
+ full ? -1 : (int) desc_len,
"DESCRIPTION",
ansi_normal());
}
@@ -522,7 +523,7 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
}
if (circle_len > 0)
- printf("%s%s%s ", on_circle, circle ? special_glyph(BLACK_CIRCLE) : " ", off_circle);
+ printf("%s%s%s ", on_circle, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_circle);
printf("%s%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s",
on_underline,
@@ -534,7 +535,7 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
printf("%-*.*s%s\n",
desc_len,
- !arg_full && arg_no_pager ? (int) desc_len : -1,
+ full ? -1 : (int) desc_len,
u->description,
off_underline);
}
@@ -749,13 +750,13 @@ static int list_units(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
if (r < 0)
return r;
- qsort_safe(unit_infos, r, sizeof(UnitInfo), compare_unit_info);
+ typesafe_qsort(unit_infos, r, compare_unit_info);
return output_units_list(unit_infos, r);
}
@@ -850,7 +851,7 @@ struct socket_info {
};
static int socket_info_compare(const struct socket_info *a, const struct socket_info *b) {
- int o;
+ int r;
assert(a);
assert(b);
@@ -860,16 +861,16 @@ static int socket_info_compare(const struct socket_info *a, const struct socket_
if (a->machine && !b->machine)
return 1;
if (a->machine && b->machine) {
- o = strcasecmp(a->machine, b->machine);
- if (o != 0)
- return o;
+ r = strcasecmp(a->machine, b->machine);
+ if (r != 0)
+ return r;
}
- o = strcmp(a->path, b->path);
- if (o == 0)
- o = strcmp(a->type, b->type);
+ r = strcmp(a->path, b->path);
+ if (r == 0)
+ r = strcmp(a->type, b->type);
- return o;
+ return r;
}
static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
@@ -961,7 +962,7 @@ static int list_sockets(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
if (n < 0)
@@ -1005,8 +1006,7 @@ static int list_sockets(int argc, char *argv[], void *userdata) {
listening = triggered = NULL; /* avoid cleanup */
}
- qsort_safe(socket_infos, cs, sizeof(struct socket_info),
- (__compar_fn_t) socket_info_compare);
+ typesafe_qsort(socket_infos, cs, socket_info_compare);
output_sockets_list(socket_infos, cs);
@@ -1099,7 +1099,7 @@ struct timer_info {
};
static int timer_info_compare(const struct timer_info *a, const struct timer_info *b) {
- int o;
+ int r;
assert(a);
assert(b);
@@ -1109,15 +1109,14 @@ static int timer_info_compare(const struct timer_info *a, const struct timer_inf
if (a->machine && !b->machine)
return 1;
if (a->machine && b->machine) {
- o = strcasecmp(a->machine, b->machine);
- if (o != 0)
- return o;
+ r = strcasecmp(a->machine, b->machine);
+ if (r != 0)
+ return r;
}
- if (a->next_elapse < b->next_elapse)
- return -1;
- if (a->next_elapse > b->next_elapse)
- return 1;
+ r = CMP(a->next_elapse, b->next_elapse);
+ if (r != 0)
+ return r;
return strcmp(a->id, b->id);
}
@@ -1268,7 +1267,7 @@ static int list_timers(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
if (n < 0)
@@ -1310,8 +1309,7 @@ static int list_timers(int argc, char *argv[], void *userdata) {
};
}
- qsort_safe(timer_infos, c, sizeof(struct timer_info),
- (__compar_fn_t) timer_info_compare);
+ typesafe_qsort(timer_infos, c, timer_info_compare);
output_timers_list(timer_infos, c);
@@ -1322,12 +1320,11 @@ static int list_timers(int argc, char *argv[], void *userdata) {
return r;
}
-static int compare_unit_file_list(const void *a, const void *b) {
+static int compare_unit_file_list(const UnitFileList *a, const UnitFileList *b) {
const char *d1, *d2;
- const UnitFileList *u = a, *v = b;
- d1 = strrchr(u->path, '.');
- d2 = strrchr(v->path, '.');
+ d1 = strrchr(a->path, '.');
+ d2 = strrchr(b->path, '.');
if (d1 && d2) {
int r;
@@ -1337,7 +1334,7 @@ static int compare_unit_file_list(const void *a, const void *b) {
return r;
}
- return strcasecmp(basename(u->path), basename(v->path));
+ return strcasecmp(basename(a->path), basename(b->path));
}
static bool output_show_unit_file(const UnitFileList *u, char **states, char **patterns) {
@@ -1555,9 +1552,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
return bus_log_parse_error(r);
}
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
- qsort_safe(units, c, sizeof(UnitFileList), compare_unit_file_list);
+ typesafe_qsort(units, c, compare_unit_file_list);
output_unit_file_list(units, c);
if (install_client_side())
@@ -1567,7 +1564,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
return 0;
}
-static int list_dependencies_print(const char *name, int level, unsigned int branches, bool last) {
+static int list_dependencies_print(const char *name, int level, unsigned branches, bool last) {
_cleanup_free_ char *n = NULL;
size_t max_len = MAX(columns(),20u);
size_t len = 0;
@@ -1581,7 +1578,7 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra
printf("%s...\n",max_len % 2 ? "" : " ");
return 0;
}
- printf("%s", special_glyph(branches & (1 << i) ? TREE_VERTICAL : TREE_SPACE));
+ printf("%s", special_glyph(branches & (1 << i) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
}
len += 2;
@@ -1590,7 +1587,7 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra
return 0;
}
- printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH));
+ printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH));
}
if (arg_full) {
@@ -1679,9 +1676,7 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha
return 0;
}
-static int list_dependencies_compare(const void *_a, const void *_b) {
- const char **a = (const char**) _a, **b = (const char**) _b;
-
+static int list_dependencies_compare(char * const *a, char * const *b) {
if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET)
return 1;
if (unit_name_to_type(*a) != UNIT_TARGET && unit_name_to_type(*b) == UNIT_TARGET)
@@ -1695,7 +1690,7 @@ static int list_dependencies_one(
const char *name,
int level,
char ***units,
- unsigned int branches) {
+ unsigned branches) {
_cleanup_strv_free_ char **deps = NULL;
char **c;
@@ -1713,7 +1708,7 @@ static int list_dependencies_one(
if (r < 0)
return r;
- qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
+ typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
STRV_FOREACH(c, deps) {
if (strv_contains(*units, *c)) {
@@ -1751,7 +1746,7 @@ static int list_dependencies_one(
break;
}
- printf("%s%s%s ", on, special_glyph(BLACK_CIRCLE), ansi_normal());
+ printf("%s%s%s ", on, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), ansi_normal());
}
r = list_dependencies_print(*c, level, branches, c[1] == NULL);
@@ -1791,7 +1786,7 @@ static int list_dependencies(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
puts(u);
@@ -1838,13 +1833,14 @@ static void free_machines_list(struct machine_info *machine_infos, int n) {
free(machine_infos);
}
-static int compare_machine_info(const void *a, const void *b) {
- const struct machine_info *u = a, *v = b;
+static int compare_machine_info(const struct machine_info *a, const struct machine_info *b) {
+ int r;
- if (u->is_host != v->is_host)
- return u->is_host > v->is_host ? -1 : 1;
+ r = CMP(b->is_host, a->is_host);
+ if (r != 0)
+ return r;
- return strcasecmp(u->name, v->name);
+ return strcasecmp(a->name, b->name);
}
static int get_machine_properties(sd_bus *bus, struct machine_info *mi) {
@@ -1996,7 +1992,7 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n)
on_failed = off_failed = "";
if (circle_len > 0)
- printf("%s%s%s ", on_state, circle ? special_glyph(BLACK_CIRCLE) : " ", off_state);
+ printf("%s%s%s ", on_state, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_state);
if (m->is_host)
printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
@@ -2030,9 +2026,9 @@ static int list_machines(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
- qsort_safe(machine_infos, r, sizeof(struct machine_info), compare_machine_info);
+ typesafe_qsort(machine_infos, r, compare_machine_info);
output_machines_list(machine_infos, r);
free_machines_list(machine_infos, r);
@@ -2202,7 +2198,7 @@ static void output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned
return;
}
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
id_len = STRLEN("JOB");
unit_len = STRLEN("UNIT");
@@ -2317,7 +2313,7 @@ static int list_jobs(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
output_jobs_list(bus, jobs, c, skipped);
return 0;
@@ -2416,7 +2412,7 @@ static void warn_unit_file_changed(const char *name) {
arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
}
-static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **unit_path) {
+static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **ret_unit_path) {
char **p;
assert(lp);
@@ -2426,7 +2422,7 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un
_cleanup_free_ char *path = NULL, *lpath = NULL;
int r;
- path = path_join(NULL, *p, unit_name);
+ path = path_join(*p, unit_name);
if (!path)
return log_oom();
@@ -2436,44 +2432,60 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un
if (r == -ENOMEM)
return log_oom();
if (r < 0)
- return log_error_errno(r, "Failed to access path '%s': %m", path);
+ return log_error_errno(r, "Failed to access path \"%s\": %m", path);
- if (unit_path)
- *unit_path = TAKE_PTR(lpath);
+ if (ret_unit_path)
+ *ret_unit_path = TAKE_PTR(lpath);
return 1;
}
+ if (ret_unit_path)
+ *ret_unit_path = NULL;
+
return 0;
}
static int unit_find_template_path(
const char *unit_name,
LookupPaths *lp,
- char **fragment_path,
- char **template) {
+ char **ret_fragment_path,
+ char **ret_template) {
- _cleanup_free_ char *_template = NULL;
+ _cleanup_free_ char *t = NULL, *f = NULL;
int r;
/* Returns 1 if a fragment was found, 0 if not found, negative on error. */
- r = unit_file_find_path(lp, unit_name, fragment_path);
- if (r != 0)
- return r; /* error or found a real unit */
+ r = unit_file_find_path(lp, unit_name, &f);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ if (ret_fragment_path)
+ *ret_fragment_path = TAKE_PTR(f);
+ if (ret_template)
+ *ret_template = NULL;
+ return r; /* found a real unit */
+ }
+
+ r = unit_name_template(unit_name, &t);
+ if (r == -EINVAL) {
+ if (ret_fragment_path)
+ *ret_fragment_path = NULL;
+ if (ret_template)
+ *ret_template = NULL;
- r = unit_name_template(unit_name, &_template);
- if (r == -EINVAL)
return 0; /* not a template, does not exist */
+ }
if (r < 0)
return log_error_errno(r, "Failed to determine template name: %m");
- r = unit_file_find_path(lp, _template, fragment_path);
+ r = unit_file_find_path(lp, t, ret_fragment_path);
if (r < 0)
return r;
- if (template)
- *template = TAKE_PTR(_template);
+ if (ret_template)
+ *ret_template = r > 0 ? TAKE_PTR(t) : NULL;
return r;
}
@@ -2482,28 +2494,34 @@ static int unit_find_paths(
sd_bus *bus,
const char *unit_name,
LookupPaths *lp,
- char **fragment_path,
- char ***dropin_paths) {
+ bool force_client_side,
+ char **ret_fragment_path,
+ char ***ret_dropin_paths) {
- _cleanup_free_ char *path = NULL;
_cleanup_strv_free_ char **dropins = NULL;
+ _cleanup_free_ char *path = NULL;
int r;
/**
- * Finds where the unit is defined on disk. Returns 0 if the unit
- * is not found. Returns 1 if it is found, and sets
- * - the path to the unit in *path, if it exists on disk,
- * - and a strv of existing drop-ins in *dropins,
- * if the arg is not NULL and any dropins were found.
+ * Finds where the unit is defined on disk. Returns 0 if the unit is not found. Returns 1 if it is found, and
+ * sets:
+ * - the path to the unit in *ret_frament_path, if it exists on disk,
+ * - and a strv of existing drop-ins in *ret_dropin_paths, if the arg is not NULL and any dropins were found.
+ *
+ * Returns -ERFKILL if the unit is masked, and -EKEYREJECTED if the unit file could not be loaded for some
+ * reason (the latter only applies if we are going through the service manager)
*/
assert(unit_name);
- assert(fragment_path);
+ assert(ret_fragment_path);
assert(lp);
- if (!install_client_side() && !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
+ /* Go via the bus to acquire the path, unless we are explicitly told not to, or when the unit name is a template */
+ if (!force_client_side &&
+ !install_client_side() &&
+ !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *unit = NULL;
+ _cleanup_free_ char *load_state = NULL, *unit = NULL;
unit = unit_dbus_path_from_name(unit_name);
if (!unit)
@@ -2514,13 +2532,33 @@ static int unit_find_paths(
"org.freedesktop.systemd1",
unit,
"org.freedesktop.systemd1.Unit",
+ "LoadState",
+ &error,
+ &load_state);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get LoadState: %s", bus_error_message(&error, r));
+
+ if (streq(load_state, "masked"))
+ return -ERFKILL;
+ if (streq(load_state, "not-found")) {
+ r = 0;
+ goto not_found;
+ }
+ if (!streq(load_state, "loaded"))
+ return -EKEYREJECTED;
+
+ r = sd_bus_get_property_string(
+ bus,
+ "org.freedesktop.systemd1",
+ unit,
+ "org.freedesktop.systemd1.Unit",
"FragmentPath",
&error,
&path);
if (r < 0)
return log_error_errno(r, "Failed to get FragmentPath: %s", bus_error_message(&error, r));
- if (dropin_paths) {
+ if (ret_dropin_paths) {
r = sd_bus_get_property_strv(
bus,
"org.freedesktop.systemd1",
@@ -2543,13 +2581,16 @@ static int unit_find_paths(
r = unit_find_template_path(unit_name, lp, &path, &template);
if (r < 0)
return r;
+ if (r > 0) {
+ if (null_or_empty_path(path))
+ /* The template is masked. Let's cut the process short. */
+ return -ERFKILL;
- if (r > 0)
/* We found the unit file. If we followed symlinks, this name might be
* different then the unit_name with started with. Look for dropins matching
* that "final" name. */
r = set_put(names, basename(path));
- else if (!template)
+ } else if (!template)
/* No unit file, let's look for dropins matching the original name.
* systemd has fairly complicated rules (based on unit type and provenience),
* which units are allowed not to have the main unit file. We err on the
@@ -2563,25 +2604,29 @@ static int unit_find_paths(
if (r < 0)
return log_error_errno(r, "Failed to add unit name: %m");
- if (dropin_paths) {
- r = unit_file_find_dropin_conf_paths(arg_root, lp->search_path,
- NULL, names, &dropins);
+ if (ret_dropin_paths) {
+ r = unit_file_find_dropin_conf_paths(arg_root, lp->search_path, NULL, names, &dropins);
if (r < 0)
return r;
}
}
- r = 0;
+ r = 0;
if (!isempty(path)) {
- *fragment_path = TAKE_PTR(path);
+ *ret_fragment_path = TAKE_PTR(path);
r = 1;
- }
+ } else
+ *ret_fragment_path = NULL;
- if (dropin_paths && !strv_isempty(dropins)) {
- *dropin_paths = TAKE_PTR(dropins);
- r = 1;
+ if (ret_dropin_paths) {
+ if (!strv_isempty(dropins)) {
+ *ret_dropin_paths = TAKE_PTR(dropins);
+ r = 1;
+ } else
+ *ret_dropin_paths = NULL;
}
+
not_found:
if (r == 0 && !arg_force)
log_error("No files found for %s.", unit_name);
@@ -2623,10 +2668,24 @@ static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *ac
return 0;
}
-static int unit_is_masked(sd_bus *bus, const char *name) {
+static int unit_is_masked(sd_bus *bus, LookupPaths *lp, const char *name) {
_cleanup_free_ char *load_state = NULL;
int r;
+ if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
+ _cleanup_free_ char *path = NULL;
+
+ /* A template cannot be loaded, but it can be still masked, so
+ * we need to use a different method. */
+
+ r = unit_file_find_path(lp, name, &path);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return false;
+ return null_or_empty_path(path);
+ }
+
r = unit_load_state(bus, name, &load_state);
if (r < 0)
return r;
@@ -2636,7 +2695,7 @@ static int unit_is_masked(sd_bus *bus, const char *name) {
static int check_triggering_units(sd_bus *bus, const char *name) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *n = NULL, *path = NULL;
+ _cleanup_free_ char *n = NULL, *path = NULL, *load_state = NULL;
_cleanup_strv_free_ char **triggered_by = NULL;
bool print_warning_label = true;
UnitActiveState active_state;
@@ -2647,9 +2706,12 @@ static int check_triggering_units(sd_bus *bus, const char *name) {
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
- r = unit_is_masked(bus, n);
- if (r != 0)
- return r < 0 ? r : 0;
+ r = unit_load_state(bus, name, &load_state);
+ if (r < 0)
+ return r;
+
+ if (streq(load_state, "masked"))
+ return 0;
path = unit_dbus_path_from_name(n);
if (!path)
@@ -2737,63 +2799,87 @@ static void wait_context_free(WaitContext *c) {
}
static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ const char *path, *interface, *active_state = NULL, *job_path = NULL;
WaitContext *c = userdata;
- const char *path;
+ bool is_failed;
int r;
+ /* Called whenever we get a PropertiesChanged signal. Checks if ActiveState changed to inactive/failed.
+ *
+ * Signal parameters: (s interface, a{sv} changed_properties, as invalidated_properties) */
+
path = sd_bus_message_get_path(m);
if (!set_contains(c->unit_paths, path))
return 0;
- /* Check if ActiveState changed to inactive/failed */
- /* (s interface, a{sv} changed_properties, as invalidated_properties) */
- r = sd_bus_message_skip(m, "s");
+ r = sd_bus_message_read(m, "s", &interface);
if (r < 0)
return bus_log_parse_error(r);
+ if (!streq(interface, "org.freedesktop.systemd1.Unit")) /* ActiveState is on the Unit interface */
+ return 0;
+
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
if (r < 0)
return bus_log_parse_error(r);
- while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+ for (;;) {
const char *s;
- r = sd_bus_message_read(m, "s", &s);
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
if (r < 0)
return bus_log_parse_error(r);
+ if (r == 0) /* end of array */
+ break;
- if (streq(s, "ActiveState")) {
- bool is_failed;
+ r = sd_bus_message_read(m, "s", &s); /* Property name */
+ if (r < 0)
+ return bus_log_parse_error(r);
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "s");
+ if (streq(s, "ActiveState")) {
+ r = sd_bus_message_read(m, "v", "s", &active_state);
if (r < 0)
return bus_log_parse_error(r);
- r = sd_bus_message_read(m, "s", &s);
+ if (job_path) /* Found everything we need */
+ break;
+
+ } else if (streq(s, "Job")) {
+ uint32_t job_id;
+
+ r = sd_bus_message_read(m, "v", "(uo)", &job_id, &job_path);
if (r < 0)
return bus_log_parse_error(r);
- is_failed = streq(s, "failed");
- if (streq(s, "inactive") || is_failed) {
- log_debug("%s became %s, dropping from --wait tracking", path, s);
- free(set_remove(c->unit_paths, path));
- c->any_failed = c->any_failed || is_failed;
- } else
- log_debug("ActiveState on %s changed to %s", path, s);
+ /* There's still a job pending for this unit, let's ignore this for now, and return right-away. */
+ if (job_id != 0)
+ return 0;
+
+ if (active_state) /* Found everything we need */
+ break;
- break; /* no need to dissect the rest of the message */
} else {
- /* other property */
- r = sd_bus_message_skip(m, "v");
+ r = sd_bus_message_skip(m, "v"); /* Other property */
if (r < 0)
return bus_log_parse_error(r);
}
+
r = sd_bus_message_exit_container(m);
if (r < 0)
return bus_log_parse_error(r);
}
- if (r < 0)
- return bus_log_parse_error(r);
+
+ /* If this didn't contain the ActiveState property we can't do anything */
+ if (!active_state)
+ return 0;
+
+ is_failed = streq(active_state, "failed");
+ if (streq(active_state, "inactive") || is_failed) {
+ log_debug("%s became %s, dropping from --wait tracking", path, active_state);
+ free(set_remove(c->unit_paths, path));
+ c->any_failed = c->any_failed || is_failed;
+ } else
+ log_debug("ActiveState on %s changed to %s", path, active_state);
if (set_isempty(c->unit_paths))
sd_event_exit(c->event, EXIT_SUCCESS);
@@ -3002,12 +3088,13 @@ static enum action verb_to_action(const char *verb) {
static int start_unit(int argc, char *argv[], void *userdata) {
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+ _cleanup_(wait_context_free) WaitContext wait_context = {};
const char *method, *mode, *one_name, *suffix = NULL;
+ _cleanup_free_ char **stopped_units = NULL; /* Do not use _cleanup_strv_free_ */
_cleanup_strv_free_ char **names = NULL;
+ int r, ret = EXIT_SUCCESS;
sd_bus *bus;
- _cleanup_(wait_context_free) WaitContext wait_context = {};
char **name;
- int r = 0;
if (arg_wait && !STR_IN_SET(argv[0], "start", "restart")) {
log_error("--wait may only be used with the 'start' or 'restart' commands.");
@@ -3054,9 +3141,11 @@ static int start_unit(int argc, char *argv[], void *userdata) {
one_name = action_table[arg_action].target;
}
- if (one_name)
- names = strv_new(one_name, NULL);
- else {
+ if (one_name) {
+ names = strv_new(one_name);
+ if (!names)
+ return log_oom();
+ } else {
r = expand_names(bus, strv_skip(argv, 1), suffix, &names);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
@@ -3094,16 +3183,21 @@ static int start_unit(int argc, char *argv[], void *userdata) {
STRV_FOREACH(name, names) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int q;
- q = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL);
- if (r >= 0 && q < 0)
- r = translate_bus_error_to_exit_status(q, &error);
+ r = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL);
+ if (ret == EXIT_SUCCESS && r < 0)
+ ret = translate_bus_error_to_exit_status(r, &error);
+
+ if (r >= 0 && streq(method, "StopUnit")) {
+ r = strv_push(&stopped_units, *name);
+ if (r < 0)
+ return log_oom();
+ }
}
if (!arg_no_block) {
- int q, arg_count = 0;
const char* extra_args[4] = {};
+ int arg_count = 0;
if (arg_scope != UNIT_FILE_SYSTEM)
extra_args[arg_count++] = "--user";
@@ -3117,27 +3211,26 @@ static int start_unit(int argc, char *argv[], void *userdata) {
extra_args[arg_count++] = arg_host;
}
- q = bus_wait_for_jobs(w, arg_quiet, extra_args);
- if (q < 0)
- return q;
+ r = bus_wait_for_jobs(w, arg_quiet, extra_args);
+ if (r < 0)
+ return r;
/* When stopping units, warn if they can still be triggered by
* another active unit (socket, path, timer) */
- if (!arg_quiet && streq(method, "StopUnit"))
- STRV_FOREACH(name, names)
- check_triggering_units(bus, *name);
+ if (!arg_quiet)
+ STRV_FOREACH(name, stopped_units)
+ (void) check_triggering_units(bus, *name);
}
- if (r >= 0 && arg_wait) {
- int q;
- q = sd_event_loop(wait_context.event);
- if (q < 0)
- return log_error_errno(q, "Failed to run event loop: %m");
+ if (ret == EXIT_SUCCESS && arg_wait && !set_isempty(wait_context.unit_paths)) {
+ r = sd_event_loop(wait_context.event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
if (wait_context.any_failed)
- r = EXIT_FAILURE;
+ ret = EXIT_FAILURE;
}
- return r;
+ return ret;
}
#if ENABLE_LOGIND
@@ -3178,7 +3271,7 @@ static int logind_set_wall_message(void) {
#endif
/* Ask systemd-logind, which might grant access to unprivileged users
- * through PolicyKit */
+ * through polkit */
static int logind_reboot(enum action a) {
#if ENABLE_LOGIND
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -3439,30 +3532,21 @@ static int load_kexec_kernel(void) {
if (access(KEXEC, X_OK) < 0)
return log_error_errno(errno, KEXEC" is not available: %m");
- r = find_esp_and_warn(arg_esp_path, false, &where, NULL, NULL, NULL, NULL);
- if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn about this case */
+ r = find_default_boot_entry(arg_esp_path, &where, &config, &e);
+ if (r == -ENOKEY) /* find_default_boot_entry() doesn't warn about this case */
return log_error_errno(r, "Cannot find the ESP partition mount point.");
- if (r < 0) /* But it logs about all these cases, hence don't log here again */
- return r;
-
- r = boot_entries_load_config(where, &config);
if (r < 0)
- return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", where);
-
- if (config.default_entry < 0) {
- log_error("No entry suitable as default, refusing to guess.");
- return -ENOENT;
- }
- e = &config.entries[config.default_entry];
+ /* But it logs about all these cases, hence don't log here again */
+ return r;
if (strv_length(e->initrd) > 1) {
log_error("Boot entry specifies multiple initrds, which is not supported currently.");
return -EINVAL;
}
- kernel = path_join(NULL, where, e->kernel);
+ kernel = path_join(where, e->kernel);
if (!strv_isempty(e->initrd))
- initrd = path_join(NULL, where, *e->initrd);
+ initrd = path_join(where, *e->initrd);
options = strv_join(e->options, " ");
if (!options)
return log_oom();
@@ -3476,7 +3560,7 @@ static int load_kexec_kernel(void) {
if (arg_dry_run)
return 0;
- r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {
@@ -3629,11 +3713,10 @@ static int start_special(int argc, char *argv[], void *userdata) {
static int start_system_special(int argc, char *argv[], void *userdata) {
/* Like start_special above, but raises an error when running in user mode */
- if (arg_scope != UNIT_FILE_SYSTEM) {
- log_error("Bad action for %s mode.",
- arg_scope == UNIT_FILE_GLOBAL ? "--global" : "--user");
- return -EINVAL;
- }
+ if (arg_scope != UNIT_FILE_SYSTEM)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Bad action for %s mode.",
+ arg_scope == UNIT_FILE_GLOBAL ? "--global" : "--user");
return start_special(argc, argv, userdata);
}
@@ -3673,13 +3756,20 @@ static int check_unit_generic(int code, const UnitActiveState good_states[], int
}
static int check_unit_active(int argc, char *argv[], void *userdata) {
- const UnitActiveState states[] = { UNIT_ACTIVE, UNIT_RELOADING };
+ static const UnitActiveState states[] = {
+ UNIT_ACTIVE,
+ UNIT_RELOADING,
+ };
+
/* According to LSB: 3, "program is not running" */
return check_unit_generic(EXIT_PROGRAM_NOT_RUNNING, states, ELEMENTSOF(states), strv_skip(argv, 1));
}
static int check_unit_failed(int argc, char *argv[], void *userdata) {
- const UnitActiveState states[] = { UNIT_FAILED };
+ static const UnitActiveState states[] = {
+ UNIT_FAILED,
+ };
+
return check_unit_generic(EXIT_PROGRAM_DEAD_AND_PID_EXISTS, states, ELEMENTSOF(states), strv_skip(argv, 1));
}
@@ -3904,6 +3994,7 @@ typedef struct UnitStatusInfo {
/* CGroup */
uint64_t memory_current;
+ uint64_t memory_min;
uint64_t memory_low;
uint64_t memory_high;
uint64_t memory_max;
@@ -3966,7 +4057,7 @@ static void print_status_info(
} else
active_on = active_off = "";
- printf("%s%s%s %s", active_on, special_glyph(BLACK_CIRCLE), active_off, strna(i->id));
+ printf("%s%s%s %s", active_on, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), active_off, strna(i->id));
if (i->description && !streq_ptr(i->id, i->description))
printf(" - %s", i->description);
@@ -4029,7 +4120,7 @@ static void print_status_info(
printf("%s\n"
" %s", dir,
- special_glyph(TREE_RIGHT));
+ special_glyph(SPECIAL_GLYPH_TREE_RIGHT));
}
last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir));
@@ -4108,7 +4199,7 @@ static void print_status_info(
LIST_FOREACH(conditions, c, i->conditions)
if (c->tristate < 0)
printf(" %s %s=%s%s%s was not met\n",
- --n ? special_glyph(TREE_BRANCH) : special_glyph(TREE_RIGHT),
+ --n ? special_glyph(SPECIAL_GLYPH_TREE_BRANCH) : special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
c->name,
c->trigger ? "|" : "",
c->negate ? "!" : "",
@@ -4283,12 +4374,17 @@ static void print_status_info(
printf(" Memory: %s", format_bytes(buf, sizeof(buf), i->memory_current));
- if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX ||
- i->memory_max != CGROUP_LIMIT_MAX || i->memory_swap_max != CGROUP_LIMIT_MAX ||
+ if (i->memory_min > 0 || i->memory_low > 0 ||
+ i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
+ i->memory_swap_max != CGROUP_LIMIT_MAX ||
i->memory_limit != CGROUP_LIMIT_MAX) {
const char *prefix = "";
printf(" (");
+ if (i->memory_min > 0) {
+ printf("%smin: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_min));
+ prefix = " ";
+ }
if (i->memory_low > 0) {
printf("%slow: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_low));
prefix = " ";
@@ -4514,6 +4610,7 @@ static int map_asserts(sd_bus *bus, const char *member, sd_bus_message *m, sd_bu
static int map_exec(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
_cleanup_free_ ExecStatusInfo *info = NULL;
+ ExecStatusInfo *last;
UnitStatusInfo *i = userdata;
int r;
@@ -4525,13 +4622,16 @@ static int map_exec(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_e
if (!info)
return -ENOMEM;
+ LIST_FIND_TAIL(exec, i->exec, last);
+
while ((r = exec_status_info_deserialize(m, info)) > 0) {
info->name = strdup(member);
if (!info->name)
return -ENOMEM;
- LIST_PREPEND(exec, i->exec, info);
+ LIST_INSERT_AFTER(exec, i->exec, last, info);
+ last = info;
info = new0(ExecStatusInfo, 1);
if (!info)
@@ -4547,15 +4647,7 @@ static int map_exec(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_e
return 0;
}
-#define print_prop(name, fmt, ...) \
- do { \
- if (arg_value) \
- printf(fmt "\n", __VA_ARGS__); \
- else \
- printf("%s=" fmt "\n", name, __VA_ARGS__); \
- } while (0)
-
-static int print_property(const char *name, sd_bus_message *m, bool value, bool all) {
+static int print_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) {
char bus_type;
const char *contents;
int r;
@@ -4582,9 +4674,9 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
return bus_log_parse_error(r);
if (u > 0)
- print_prop(name, "%"PRIu32, u);
+ bus_print_property_value(name, expected_value, value, "%"PRIu32, u);
else if (all)
- print_prop(name, "%s", "");
+ bus_print_property_value(name, expected_value, value, "%s", "");
return 1;
@@ -4596,7 +4688,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
return bus_log_parse_error(r);
if (all || !isempty(s))
- print_prop(name, "%s", s);
+ bus_print_property_value(name, expected_value, value, "%s", s);
return 1;
@@ -4608,7 +4700,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
return bus_log_parse_error(r);
if (all || !isempty(a) || !isempty(b))
- print_prop(name, "%s \"%s\"", strempty(a), strempty(b));
+ bus_print_property_value(name, expected_value, value, "%s \"%s\"", strempty(a), strempty(b));
return 1;
} else if (streq_ptr(name, "SystemCallFilter")) {
@@ -4670,7 +4762,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(sb)", &path, &ignore)) > 0)
- print_prop(name, "%s (ignore_errors=%s)", path, yes_no(ignore));
+ bus_print_property_value(name, expected_value, value, "%s (ignore_errors=%s)", path, yes_no(ignore));
if (r < 0)
return bus_log_parse_error(r);
@@ -4689,7 +4781,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0)
- print_prop(name, "%s (%s)", path, type);
+ bus_print_property_value(name, expected_value, value, "%s (%s)", path, type);
if (r < 0)
return bus_log_parse_error(r);
@@ -4707,7 +4799,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0)
- print_prop(name, "%s (%s)", path, type);
+ bus_print_property_value(name, expected_value, value, "%s (%s)", path, type);
if (r < 0)
return bus_log_parse_error(r);
@@ -4728,9 +4820,9 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
while ((r = sd_bus_message_read(m, "(stt)", &base, &v, &next_elapse)) > 0) {
char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
- print_prop(name, "{ %s=%s ; next_elapse=%s }", base,
- format_timespan(timespan1, sizeof(timespan1), v, 0),
- format_timespan(timespan2, sizeof(timespan2), next_elapse, 0));
+ bus_print_property_value(name, expected_value, value, "{ %s=%s ; next_elapse=%s }", base,
+ format_timespan(timespan1, sizeof(timespan1), v, 0),
+ format_timespan(timespan2, sizeof(timespan2), next_elapse, 0));
}
if (r < 0)
return bus_log_parse_error(r);
@@ -4752,8 +4844,8 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
while ((r = sd_bus_message_read(m, "(sst)", &base, &spec, &next_elapse)) > 0) {
char timestamp[FORMAT_TIMESTAMP_MAX];
- print_prop(name, "{ %s=%s ; next_elapse=%s }", base, spec,
- format_timestamp(timestamp, sizeof(timestamp), next_elapse));
+ bus_print_property_value(name, expected_value, value, "{ %s=%s ; next_elapse=%s }", base, spec,
+ format_timestamp(timestamp, sizeof(timestamp), next_elapse));
}
if (r < 0)
return bus_log_parse_error(r);
@@ -4777,18 +4869,18 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
tt = strv_join(info.argv, " ");
- print_prop(name,
- "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
- strna(info.path),
- strna(tt),
- yes_no(info.ignore),
- strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
- strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
- info.pid,
- sigchld_code_to_string(info.code),
- info.status,
- info.code == CLD_EXITED ? "" : "/",
- strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
+ bus_print_property_value(name, expected_value, value,
+ "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
+ strna(info.path),
+ strna(tt),
+ yes_no(info.ignore),
+ strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+ strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+ info.pid,
+ sigchld_code_to_string(info.code),
+ info.status,
+ info.code == CLD_EXITED ? "" : "/",
+ strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
free(info.path);
strv_free(info.argv);
@@ -4809,7 +4901,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(ss)", &path, &rwm)) > 0)
- print_prop(name, "%s %s", strna(path), strna(rwm));
+ bus_print_property_value(name, expected_value, value, "%s %s", strna(path), strna(rwm));
if (r < 0)
return bus_log_parse_error(r);
@@ -4829,7 +4921,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(st)", &path, &weight)) > 0)
- print_prop(name, "%s %"PRIu64, strna(path), weight);
+ bus_print_property_value(name, expected_value, value, "%s %"PRIu64, strna(path), weight);
if (r < 0)
return bus_log_parse_error(r);
@@ -4850,7 +4942,29 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(st)", &path, &bandwidth)) > 0)
- print_prop(name, "%s %"PRIu64, strna(path), bandwidth);
+ bus_print_property_value(name, expected_value, value, "%s %"PRIu64, strna(path), bandwidth);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 1;
+
+ } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN &&
+ streq(name, "IODeviceLatencyTargetUSec")) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ const char *path;
+ uint64_t target;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(st)", &path, &target)) > 0)
+ bus_print_property_value(name, expected_value, value, "%s %s", strna(path),
+ format_timespan(ts, sizeof(ts), target, 1));
if (r < 0)
return bus_log_parse_error(r);
@@ -4874,7 +4988,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool
if (n < 0)
return log_oom();
- print_prop(name, "%s", h);
+ bus_print_property_value(name, expected_value, value, "%s", h);
return 1;
}
@@ -4893,7 +5007,7 @@ typedef enum SystemctlShowMode{
_SYSTEMCTL_SHOW_MODE_INVALID = -1,
} SystemctlShowMode;
-static const char* const systemctl_show_mode_table[] = {
+static const char* const systemctl_show_mode_table[_SYSTEMCTL_SHOW_MODE_MAX] = {
[SYSTEMCTL_SHOW_PROPERTIES] = "show",
[SYSTEMCTL_SHOW_STATUS] = "status",
[SYSTEMCTL_SHOW_HELP] = "help",
@@ -4964,6 +5078,7 @@ static int show_one(
{ "Where", "s", NULL, offsetof(UnitStatusInfo, where) },
{ "What", "s", NULL, offsetof(UnitStatusInfo, what) },
{ "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) },
+ { "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) },
{ "MemoryLow", "t", NULL, offsetof(UnitStatusInfo, memory_low) },
{ "MemoryHigh", "t", NULL, offsetof(UnitStatusInfo, memory_high) },
{ "MemoryMax", "t", NULL, offsetof(UnitStatusInfo, memory_max) },
@@ -5111,11 +5226,11 @@ static int show_all(
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
c = (unsigned) r;
- qsort_safe(unit_infos, c, sizeof(UnitInfo), compare_unit_info);
+ typesafe_qsort(unit_infos, c, compare_unit_info);
for (u = unit_infos; u < unit_infos + c; u++) {
_cleanup_free_ char *p = NULL;
@@ -5169,7 +5284,7 @@ static int show_system_status(sd_bus *bus) {
off = ansi_normal();
}
- printf("%s%s%s %s\n", on, special_glyph(BLACK_CIRCLE), off, arg_host ? arg_host : hn);
+ printf("%s%s%s %s\n", on, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), off, arg_host ? arg_host : hn);
printf(" State: %s%s%s\n",
on, strna(mi.state), off);
@@ -5209,27 +5324,19 @@ static int show(int argc, char *argv[], void *userdata) {
assert(argv);
show_mode = systemctl_show_mode_from_string(argv[0]);
- if (show_mode < 0) {
- log_error("Invalid argument.");
- return -EINVAL;
- }
+ if (show_mode < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid argument.");
- if (show_mode == SYSTEMCTL_SHOW_HELP && argc <= 1) {
- log_error("This command expects one or more unit names. Did you mean --help?");
- return -EINVAL;
- }
+ if (show_mode == SYSTEMCTL_SHOW_HELP && argc <= 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "This command expects one or more unit names. Did you mean --help?");
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
-
- if (show_mode == SYSTEMCTL_SHOW_STATUS)
- /* Increase max number of open files to 16K if we can, we
- * might needs this when browsing journal files, which might
- * be split up into many files. */
- setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384));
+ (void) pager_open(arg_pager_flags);
/* If no argument is specified inspect the manager itself */
if (show_mode == SYSTEMCTL_SHOW_PROPERTIES && argc <= 1)
@@ -5334,16 +5441,30 @@ static int cat(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
STRV_FOREACH(name, names) {
_cleanup_free_ char *fragment_path = NULL;
_cleanup_strv_free_ char **dropin_paths = NULL;
- r = unit_find_paths(bus, *name, &lp, &fragment_path, &dropin_paths);
+ r = unit_find_paths(bus, *name, &lp, false, &fragment_path, &dropin_paths);
+ if (r == -ERFKILL) {
+ printf("%s# Unit %s is masked%s.\n",
+ ansi_highlight_magenta(),
+ *name,
+ ansi_normal());
+ continue;
+ }
+ if (r == -EKEYREJECTED) {
+ printf("%s# Unit %s could not be loaded.%s\n",
+ ansi_highlight_magenta(),
+ *name,
+ ansi_normal());
+ continue;
+ }
if (r < 0)
return r;
- else if (r == 0)
+ if (r == 0)
return -ENOENT;
if (first)
@@ -5580,10 +5701,9 @@ static int print_variable(const char *s) {
_cleanup_free_ char *esc = NULL;
sep = strchr(s, '=');
- if (!sep) {
- log_error("Invalid environment block");
- return -EUCLEAN;
- }
+ if (!sep)
+ return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN),
+ "Invalid environment block");
esc = shell_maybe_quote(sep + 1, ESCAPE_POSIX);
if (!esc)
@@ -5604,7 +5724,7 @@ static int show_environment(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
r = sd_bus_get_property(
bus,
@@ -5659,9 +5779,7 @@ static int switch_root(int argc, char *argv[], void *userdata) {
if (argc >= 3)
init = argv[2];
else {
- r = parse_env_file(NULL, "/proc/cmdline", WHITESPACE,
- "init", &cmdline_init,
- NULL);
+ r = proc_cmdline_get_key("init", 0, &cmdline_init);
if (r < 0)
log_debug_errno(r, "Failed to parse /proc/cmdline: %m");
@@ -5915,7 +6033,7 @@ static int enable_sysv_units(const char *verb, char **args) {
if (!arg_quiet)
log_info("Executing: %s", l);
- j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (j < 0)
return j;
if (j == 0) {
@@ -6000,15 +6118,15 @@ static int normalize_filenames(char **names) {
if (!path_is_absolute(*u)) {
char* normalized_path;
- if (!isempty(arg_root)) {
- log_error("Non-absolute paths are not allowed when --root is used: %s", *u);
- return -EINVAL;
- }
+ if (!isempty(arg_root))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Non-absolute paths are not allowed when --root is used: %s",
+ *u);
- if (!strchr(*u,'/')) {
- log_error("Link argument does contain at least one directory separator: %s", *u);
- return -EINVAL;
- }
+ if (!strchr(*u,'/'))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Link argument does contain at least one directory separator: %s",
+ *u);
r = path_make_absolute_cwd(*u, &normalized_path);
if (r < 0)
@@ -6259,18 +6377,20 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
}
if (carries_install_info == 0 && !ignore_carries_install_info)
- log_warning("The unit files have no installation config (WantedBy, RequiredBy, Also, Alias\n"
- "settings in the [Install] section, and DefaultInstance for template units).\n"
- "This means they are not meant to be enabled using systemctl.\n"
- "Possible reasons for having this kind of units are:\n"
- "1) A unit may be statically enabled by being symlinked from another unit's\n"
- " .wants/ or .requires/ directory.\n"
- "2) A unit's purpose may be to act as a helper for some other unit which has\n"
- " a requirement dependency on it.\n"
- "3) A unit may be started when needed via activation (socket, path, timer,\n"
- " D-Bus, udev, scripted systemctl call, ...).\n"
- "4) In case of template units, the unit is meant to be enabled with some\n"
- " instance name specified.");
+ log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, Also=,\n"
+ "Alias= settings in the [Install] section, and DefaultInstance= for template\n"
+ "units). This means they are not meant to be enabled using systemctl.\n"
+ " \n" /* trick: the space is needed so that the line does not get stripped from output */
+ "Possible reasons for having this kind of units are:\n"
+ "%1$s A unit may be statically enabled by being symlinked from another unit's\n"
+ " .wants/ or .requires/ directory.\n"
+ "%1$s A unit's purpose may be to act as a helper for some other unit which has\n"
+ " a requirement dependency on it.\n"
+ "%1$s A unit may be started when needed via activation (socket, path, timer,\n"
+ " D-Bus, udev, scripted systemctl call, ...).\n"
+ "%1$s In case of template units, the unit is meant to be enabled with some\n"
+ " instance name specified.",
+ special_glyph(SPECIAL_GLYPH_BULLET));
if (arg_now && STR_IN_SET(argv[0], "enable", "disable", "mask")) {
sd_bus *bus;
@@ -6583,7 +6703,29 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
return enabled ? EXIT_SUCCESS : EXIT_FAILURE;
}
+static int match_startup_finished(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ char **state = userdata;
+ int r;
+
+ assert(state);
+
+ r = sd_bus_get_property_string(
+ sd_bus_message_get_bus(m),
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "SystemState",
+ NULL,
+ state);
+
+ sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), r);
+ return 0;
+}
+
static int is_system_running(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_startup_finished = NULL;
+ _cleanup_(sd_event_unrefp) sd_event* event = NULL;
_cleanup_free_ char *state = NULL;
sd_bus *bus;
int r;
@@ -6598,18 +6740,49 @@ static int is_system_running(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
+ if (arg_wait) {
+ r = sd_event_default(&event);
+ if (r >= 0)
+ r = sd_bus_attach_event(bus, event, 0);
+ if (r >= 0)
+ r = sd_bus_match_signal_async(
+ bus,
+ &slot_startup_finished,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartupFinished",
+ match_startup_finished, NULL, &state);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to request match for StartupFinished: %m");
+ arg_wait = false;
+ }
+ }
+
r = sd_bus_get_property_string(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"SystemState",
- NULL,
+ &error,
&state);
if (r < 0) {
+ log_warning_errno(r, "Failed to query system state: %s", bus_error_message(&error, r));
+
if (!arg_quiet)
puts("unknown");
- return 0;
+ return EXIT_FAILURE;
+ }
+
+ if (arg_wait && STR_IN_SET(state, "initializing", "starting")) {
+ r = sd_event_loop(event);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to get property from event loop: %m");
+ if (!arg_quiet)
+ puts("unknown");
+ return EXIT_FAILURE;
+ }
}
if (!arg_quiet)
@@ -6670,10 +6843,10 @@ static int get_file_to_edit(
}
if (arg_runtime) {
- if (access(path, F_OK) >= 0) {
- log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path);
- return -EEXIST;
- }
+ if (access(path, F_OK) >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.",
+ run, path);
*ret_path = TAKE_PTR(run);
} else
@@ -6689,7 +6862,8 @@ static int unit_file_create_new(
char **ret_new_path,
char **ret_tmp_path) {
- char *tmp_new_path, *tmp_tmp_path, *ending;
+ _cleanup_free_ char *new_path = NULL, *tmp_path = NULL;
+ const char *ending;
int r;
assert(unit_name);
@@ -6697,18 +6871,16 @@ static int unit_file_create_new(
assert(ret_tmp_path);
ending = strjoina(unit_name, suffix);
- r = get_file_to_edit(paths, ending, &tmp_new_path);
+ r = get_file_to_edit(paths, ending, &new_path);
if (r < 0)
return r;
- r = create_edit_temp_file(tmp_new_path, tmp_new_path, &tmp_tmp_path);
- if (r < 0) {
- free(tmp_new_path);
+ r = create_edit_temp_file(new_path, new_path, &tmp_path);
+ if (r < 0)
return r;
- }
- *ret_new_path = tmp_new_path;
- *ret_tmp_path = tmp_tmp_path;
+ *ret_new_path = TAKE_PTR(new_path);
+ *ret_tmp_path = TAKE_PTR(tmp_path);
return 0;
}
@@ -6720,7 +6892,7 @@ static int unit_file_create_copy(
char **ret_new_path,
char **ret_tmp_path) {
- char *tmp_new_path, *tmp_tmp_path;
+ _cleanup_free_ char *new_path = NULL, *tmp_path = NULL;
int r;
assert(fragment_path);
@@ -6728,33 +6900,26 @@ static int unit_file_create_copy(
assert(ret_new_path);
assert(ret_tmp_path);
- r = get_file_to_edit(paths, unit_name, &tmp_new_path);
+ r = get_file_to_edit(paths, unit_name, &new_path);
if (r < 0)
return r;
- if (!path_equal(fragment_path, tmp_new_path) && access(tmp_new_path, F_OK) == 0) {
+ if (!path_equal(fragment_path, new_path) && access(new_path, F_OK) >= 0) {
char response;
- r = ask_char(&response, "yn", "\"%s\" already exists. Overwrite with \"%s\"? [(y)es, (n)o] ", tmp_new_path, fragment_path);
- if (r < 0) {
- free(tmp_new_path);
+ r = ask_char(&response, "yn", "\"%s\" already exists. Overwrite with \"%s\"? [(y)es, (n)o] ", new_path, fragment_path);
+ if (r < 0)
return r;
- }
- if (response != 'y') {
- log_warning("%s ignored", unit_name);
- free(tmp_new_path);
- return -EKEYREJECTED;
- }
+ if (response != 'y')
+ return log_warning_errno(SYNTHETIC_ERRNO(EKEYREJECTED), "%s skipped.", unit_name);
}
- r = create_edit_temp_file(tmp_new_path, fragment_path, &tmp_tmp_path);
- if (r < 0) {
- free(tmp_new_path);
+ r = create_edit_temp_file(new_path, fragment_path, &tmp_path);
+ if (r < 0)
return r;
- }
- *ret_new_path = tmp_new_path;
- *ret_tmp_path = tmp_tmp_path;
+ *ret_new_path = TAKE_PTR(new_path);
+ *ret_tmp_path = TAKE_PTR(tmp_path);
return 0;
}
@@ -6764,15 +6929,13 @@ static int run_editor(char **paths) {
assert(paths);
- r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG|FORK_WAIT, NULL);
if (r < 0)
return r;
if (r == 0) {
- const char **args;
- char *editor, **editor_args = NULL;
- char **tmp_path, **original_path, *p;
- size_t n_editor_args = 0, i = 1;
- size_t argc;
+ char **editor_args = NULL, **tmp_path, **original_path, *p;
+ size_t n_editor_args = 0, i = 1, argc;
+ const char **args, *editor;
argc = strv_length(paths)/2 + 1;
@@ -6795,6 +6958,7 @@ static int run_editor(char **paths) {
n_editor_args = strv_length(editor_args);
argc += n_editor_args - 1;
}
+
args = newa(const char*, argc + 1);
if (n_editor_args > 0) {
@@ -6803,10 +6967,8 @@ static int run_editor(char **paths) {
args[i] = editor_args[i];
}
- STRV_FOREACH_PAIR(original_path, tmp_path, paths) {
- args[i] = *tmp_path;
- i++;
- }
+ STRV_FOREACH_PAIR(original_path, tmp_path, paths)
+ args[i++] = *tmp_path;
args[i] = NULL;
if (n_editor_args > 0)
@@ -6848,7 +7010,15 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
_cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL, *tmp_name = NULL;
const char *unit_name;
- r = unit_find_paths(bus, *name, &lp, &path, NULL);
+ r = unit_find_paths(bus, *name, &lp, false, &path, NULL);
+ if (r == -EKEYREJECTED) {
+ /* If loading of the unit failed server side complete, then the server won't tell us the unit
+ * file path. In that case, find the file client side. */
+ log_debug_errno(r, "Unit '%s' was not loaded correctly, retrying client-side.", *name);
+ r = unit_find_paths(bus, *name, &lp, true, &path, NULL);
+ }
+ if (r == -ERFKILL)
+ return log_error_errno(r, "Unit '%s' masked, cannot edit.", *name);
if (r < 0)
return r;
@@ -6856,10 +7026,10 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
assert(!path);
if (!arg_force) {
- log_error("Run 'systemctl edit%s --force %s' to create a new unit.",
- arg_scope == UNIT_FILE_GLOBAL ? " --global" :
- arg_scope == UNIT_FILE_USER ? " --user" : "",
- *name);
+ log_info("Run 'systemctl edit%s --force --full %s' to create a new unit.",
+ arg_scope == UNIT_FILE_GLOBAL ? " --global" :
+ arg_scope == UNIT_FILE_USER ? " --user" : "",
+ *name);
return -ENOENT;
}
@@ -6899,6 +7069,7 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
r = strv_push_pair(paths, new_path, tmp_path);
if (r < 0)
return log_oom();
+
new_path = tmp_path = NULL;
}
@@ -6906,6 +7077,7 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
}
static int edit(int argc, char *argv[], void *userdata) {
+ _cleanup_(lookup_paths_free) LookupPaths lp = {};
_cleanup_strv_free_ char **names = NULL;
_cleanup_strv_free_ char **paths = NULL;
char **original, **tmp;
@@ -6922,6 +7094,10 @@ static int edit(int argc, char *argv[], void *userdata) {
return -EINVAL;
}
+ r = lookup_paths_init(&lp, arg_scope, 0, arg_root);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine unit paths: %m");
+
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return r;
@@ -6931,10 +7107,9 @@ static int edit(int argc, char *argv[], void *userdata) {
return log_error_errno(r, "Failed to expand names: %m");
STRV_FOREACH(tmp, names) {
- r = unit_is_masked(bus, *tmp);
+ r = unit_is_masked(bus, &lp, *tmp);
if (r < 0)
return r;
-
if (r > 0) {
log_error("Cannot edit %s: unit is masked.", *tmp);
return -EINVAL;
@@ -6995,8 +7170,15 @@ end:
return r;
}
-static void systemctl_help(void) {
- (void) pager_open(arg_no_pager, false);
+static int systemctl_help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ (void) pager_open(arg_pager_flags);
+
+ r = terminal_urlify_man("systemctl", "1", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Query or send control commands to the systemd manager.\n\n"
@@ -7030,6 +7212,7 @@ static void systemctl_help(void) {
" --dry-run Only print what would be done\n"
" -q --quiet Suppress output\n"
" --wait For (re)start, wait until service stopped again\n"
+ " For is-system-running, wait until startup is completed\n"
" --no-block Do not wait until operation finished\n"
" --no-wall Don't send wall message before halt/power-off/reboot\n"
" --no-reload Don't reload daemon after en-/dis-abling unit files\n"
@@ -7133,11 +7316,23 @@ static void systemctl_help(void) {
" hibernate Hibernate the system\n"
" hybrid-sleep Hibernate and suspend the system\n"
" suspend-then-hibernate Suspend the system, wake after a period of\n"
- " time and put it into hibernate\n",
- program_invocation_short_name);
+ " time and put it into hibernate\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
-static void halt_help(void) {
+static int halt_help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("halt", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]%s\n\n"
"%s the system.\n\n"
" --help Show this help\n"
@@ -7147,15 +7342,27 @@ static void halt_help(void) {
" -f --force Force immediate halt/power-off/reboot\n"
" -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
" -d --no-wtmp Don't write wtmp record\n"
- " --no-wall Don't send wall message before halt/power-off/reboot\n",
- program_invocation_short_name,
- arg_action == ACTION_REBOOT ? " [ARG]" : "",
- arg_action == ACTION_REBOOT ? "Reboot" :
- arg_action == ACTION_POWEROFF ? "Power off" :
- "Halt");
+ " --no-wall Don't send wall message before halt/power-off/reboot\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , arg_action == ACTION_REBOOT ? " [ARG]" : "",
+ arg_action == ACTION_REBOOT ? "Reboot" :
+ arg_action == ACTION_POWEROFF ? "Power off" :
+ "Halt"
+ , link
+ );
+
+ return 0;
}
-static void shutdown_help(void) {
+static int shutdown_help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("shutdown", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] [TIME] [WALL...]\n\n"
"Shut down the system.\n\n"
" --help Show this help\n"
@@ -7165,11 +7372,23 @@ static void shutdown_help(void) {
" -h Equivalent to --poweroff, overridden by --halt\n"
" -k Don't halt/power-off/reboot, just send warnings\n"
" --no-wall Don't send wall message before halt/power-off/reboot\n"
- " -c Cancel a pending shutdown\n",
- program_invocation_short_name);
+ " -c Cancel a pending shutdown\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
-static void telinit_help(void) {
+static int telinit_help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("telinit", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] {COMMAND}\n\n"
"Send control commands to the init daemon.\n\n"
" --help Show this help\n"
@@ -7180,15 +7399,32 @@ static void telinit_help(void) {
" 2, 3, 4, 5 Start runlevelX.target unit\n"
" 1, s, S Enter rescue mode\n"
" q, Q Reload init daemon configuration\n"
- " u, U Reexecute init daemon\n",
- program_invocation_short_name);
+ " u, U Reexecute init daemon\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
-static void runlevel_help(void) {
+static int runlevel_help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("runlevel", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"Prints the previous and current runlevel of the init system.\n\n"
- " --help Show this help\n",
- program_invocation_short_name);
+ " --help Show this help\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static void help_types(void) {
@@ -7345,22 +7581,20 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
/* we default to allowing interactive authorization only in systemctl (not in the legacy commands) */
arg_ask_password = true;
- while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir.::", options, NULL)) >= 0)
switch (c) {
case 'h':
- systemctl_help();
- return 0;
+ return systemctl_help();
case ARG_VERSION:
return version();
case 't': {
- if (isempty(optarg)) {
- log_error("--type= requires arguments.");
- return -EINVAL;
- }
+ if (isempty(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--type= requires arguments.");
for (p = optarg;;) {
_cleanup_free_ char *type = NULL;
@@ -7395,8 +7629,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
log_error("Unknown unit type or load state '%s'.", type);
- log_info("Use -t help to see a list of allowed values.");
- return -EINVAL;
+ return log_info_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Use -t help to see a list of allowed values.");
}
break;
@@ -7500,7 +7734,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_WALL:
@@ -7550,10 +7784,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
arg_signal = signal_from_string(optarg);
- if (arg_signal < 0) {
- log_error("Failed to parse signal string %s.", optarg);
- return -EINVAL;
- }
+ if (arg_signal < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse signal string %s.",
+ optarg);
break;
case ARG_NO_ASK_PASSWORD:
@@ -7575,10 +7809,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
case 'n':
- if (safe_atou(optarg, &arg_lines) < 0) {
- log_error("Failed to parse lines '%s'", optarg);
- return -EINVAL;
- }
+ if (safe_atou(optarg, &arg_lines) < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse lines '%s'",
+ optarg);
break;
case 'o':
@@ -7588,10 +7822,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
arg_output = output_mode_from_string(optarg);
- if (arg_output < 0) {
- log_error("Unknown output '%s'.", optarg);
- return -EINVAL;
- }
+ if (arg_output < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown output '%s'.",
+ optarg);
break;
case 'i':
@@ -7607,10 +7841,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
case ARG_STATE: {
- if (isempty(optarg)) {
- log_error("--state= requires arguments.");
- return -EINVAL;
- }
+ if (isempty(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--state= requires arguments.");
for (p = optarg;;) {
_cleanup_free_ char *s = NULL;
@@ -7635,10 +7868,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
case 'r':
- if (geteuid() != 0) {
- log_error("--recursive requires root privileges.");
- return -EPERM;
- }
+ if (geteuid() != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "--recursive requires root privileges.");
arg_recursive = true;
break;
@@ -7650,10 +7882,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
arg_preset_mode = unit_file_preset_mode_from_string(optarg);
- if (arg_preset_mode < 0) {
- log_error("Failed to parse preset mode: %s.", optarg);
- return -EINVAL;
- }
+ if (arg_preset_mode < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse preset mode: %s.", optarg);
break;
@@ -7666,6 +7897,14 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
return log_oom();
break;
+ case '.':
+ /* Output an error mimicking getopt, and print a hint afterwards */
+ log_error("%s: invalid option -- '.'", program_invocation_name);
+ log_notice("Hint: to specify units starting with a dash, use \"--\":\n"
+ " %s [OPTIONS...] {COMMAND} -- -.%s ...",
+ program_invocation_name, optarg ?: "mount");
+ _fallthrough_;
+
case '?':
return -EINVAL;
@@ -7673,20 +7912,13 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (arg_transport != BUS_TRANSPORT_LOCAL && arg_scope != UNIT_FILE_SYSTEM) {
- log_error("Cannot access user instance remotely.");
- return -EINVAL;
- }
-
- if (arg_wait && arg_no_block) {
- log_error("--wait may not be combined with --no-block.");
- return -EINVAL;
- }
+ if (arg_transport != BUS_TRANSPORT_LOCAL && arg_scope != UNIT_FILE_SYSTEM)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot access user instance remotely.");
- if (arg_runtime && STRPTR_IN_SET(argv[optind], "disable", "unmask", "preset", "preset-all")) {
- log_error("--runtime cannot be used with %s", argv[optind]);
- return -EINVAL;
- }
+ if (arg_wait && arg_no_block)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--wait may not be combined with --no-block.");
return 1;
}
@@ -7725,8 +7957,7 @@ static int halt_parse_argv(int argc, char *argv[]) {
switch (c) {
case ARG_HELP:
- halt_help();
- return 0;
+ return halt_help();
case ARG_HALT:
arg_action = ACTION_HALT;
@@ -7777,10 +8008,9 @@ static int halt_parse_argv(int argc, char *argv[]) {
r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL);
if (r < 0)
return r;
- } else if (optind < argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
+ } else if (optind < argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many arguments.");
return 1;
}
@@ -7823,7 +8053,8 @@ static int parse_shutdown_time_spec(const char *t, usec_t *_u) {
tm.tm_min = (int) minute;
tm.tm_sec = 0;
- assert_se(s = mktime(&tm));
+ s = mktime(&tm);
+ assert(s >= 0);
*_u = (usec_t) s * USEC_PER_SEC;
@@ -7860,8 +8091,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
switch (c) {
case ARG_HELP:
- shutdown_help();
- return 0;
+ return shutdown_help();
case 'H':
arg_action = ACTION_HALT;
@@ -7981,8 +8211,7 @@ static int telinit_parse_argv(int argc, char *argv[]) {
switch (c) {
case ARG_HELP:
- telinit_help();
- return 0;
+ return telinit_help();
case ARG_NO_WALL:
arg_no_wall = true;
@@ -7995,29 +8224,26 @@ static int telinit_parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind >= argc) {
- log_error("%s: required argument missing.", program_invocation_short_name);
- return -EINVAL;
- }
+ if (optind >= argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: required argument missing.",
+ program_invocation_short_name);
- if (optind + 1 < argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
+ if (optind + 1 < argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many arguments.");
- if (strlen(argv[optind]) != 1) {
- log_error("Expected single character argument.");
- return -EINVAL;
- }
+ if (strlen(argv[optind]) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Expected single character argument.");
for (i = 0; i < ELEMENTSOF(table); i++)
if (table[i].from == argv[optind][0])
break;
- if (i >= ELEMENTSOF(table)) {
- log_error("Unknown command '%s'.", argv[optind]);
- return -EINVAL;
- }
+ if (i >= ELEMENTSOF(table))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown command '%s'.", argv[optind]);
arg_action = table[i].to;
@@ -8045,8 +8271,7 @@ static int runlevel_parse_argv(int argc, char *argv[]) {
switch (c) {
case ARG_HELP:
- runlevel_help();
- return 0;
+ return runlevel_help();
case '?':
return -EINVAL;
@@ -8055,10 +8280,9 @@ static int runlevel_parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind < argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
+ if (optind < argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many arguments.");
return 1;
}
@@ -8110,10 +8334,11 @@ static int parse_argv(int argc, char *argv[]) {
/* Hmm, so some other init system is running, we need to forward this request to
* it. For now we simply guess that it is Upstart. */
+ (void) rlimit_nofile_safe();
execv(TELINIT, argv);
- log_error("Couldn't find an alternative telinit implementation to spawn.");
- return -EIO;
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Couldn't find an alternative telinit implementation to spawn.");
}
} else if (strstr(program_invocation_short_name, "runlevel")) {
@@ -8284,13 +8509,13 @@ static int start_with_fallback(void) {
if (talk_initctl() > 0)
return 0;
- log_error("Failed to talk to init daemon.");
- return -EIO;
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to talk to init daemon.");
}
static int halt_now(enum action a) {
/* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
- * synce'd explicitly in advance. */
+ * synced explicitly in advance. */
if (!arg_no_sync && !arg_dry_run)
(void) sync();
@@ -8394,8 +8619,10 @@ static int halt_main(void) {
if (r < 0)
return r;
- if (arg_when > 0)
- return logind_schedule_shutdown();
+ /* Delayed shutdown requested, and was successful */
+ if (arg_when > 0 && logind_schedule_shutdown() == 0)
+ return 0;
+ /* no delay, or logind failed or is not at all available */
if (geteuid() != 0) {
if (arg_dry_run || arg_force > 0) {
@@ -8404,7 +8631,7 @@ static int halt_main(void) {
}
/* Try logind if we are a normal user and no special
- * mode applies. Maybe PolicyKit allows us to shutdown
+ * mode applies. Maybe polkit allows us to shutdown
* the machine. */
if (IN_SET(arg_action, ACTION_POWEROFF, ACTION_REBOOT, ACTION_HALT)) {
r = logind_reboot(arg_action);
@@ -8492,7 +8719,7 @@ static int logind_cancel_shutdown(void) {
#endif
}
-int main(int argc, char*argv[]) {
+static int run(int argc, char *argv[]) {
int r;
argv_cmdline = argv[0];
@@ -8500,6 +8727,10 @@ int main(int argc, char*argv[]) {
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
+
+ /* The journal merging logic potentially needs a lot of fds. */
+ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
sigbus_install();
/* Explicitly not on_tty() to avoid setting cached value.
@@ -8512,7 +8743,6 @@ int main(int argc, char*argv[]) {
goto finish;
if (arg_action != ACTION_SYSTEMCTL && running_in_chroot() > 0) {
-
if (!arg_quiet)
log_info("Running in chroot, ignoring request.");
r = 0;
@@ -8578,10 +8808,6 @@ int main(int argc, char*argv[]) {
finish:
release_busses();
- pager_close();
- ask_password_agent_close();
- polkit_agent_close();
-
strv_free(arg_types);
strv_free(arg_states);
strv_free(arg_properties);
@@ -8590,6 +8816,8 @@ finish:
free(arg_root);
free(arg_esp_path);
- /* Note that we return r here, not EXIT_SUCCESS, so that we can implement the LSB-like return codes */
- return r < 0 ? EXIT_FAILURE : r;
+ /* Note that we return r here, not 0, so that we can implement the LSB-like return codes */
+ return r;
}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/systemd/_sd-common.h b/src/systemd/_sd-common.h
index 7b54d179eb..05c38008cf 100644
--- a/src/systemd/_sd-common.h
+++ b/src/systemd/_sd-common.h
@@ -3,7 +3,6 @@
#define foosdcommonhfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
@@ -24,24 +23,26 @@
# error "Do not include _sd-common.h directly; it is a private header."
#endif
+typedef void (*_sd_destroy_t)(void *userdata);
+
#ifndef _sd_printf_
# if __GNUC__ >= 4
-# define _sd_printf_(a,b) __attribute__ ((format (printf, a, b)))
+# define _sd_printf_(a,b) __attribute__((__format__(printf, a, b)))
# else
# define _sd_printf_(a,b)
# endif
#endif
#ifndef _sd_sentinel_
-# define _sd_sentinel_ __attribute__((sentinel))
+# define _sd_sentinel_ __attribute__((__sentinel__))
#endif
#ifndef _sd_packed_
-# define _sd_packed_ __attribute__((packed))
+# define _sd_packed_ __attribute__((__packed__))
#endif
#ifndef _sd_pure_
-# define _sd_pure_ __attribute__((pure))
+# define _sd_pure_ __attribute__((__pure__))
#endif
#ifndef _SD_STRINGIFY
diff --git a/src/systemd/meson.build b/src/systemd/meson.build
index 5f84439a58..e0c967efc5 100644
--- a/src/systemd/meson.build
+++ b/src/systemd/meson.build
@@ -5,7 +5,9 @@ _systemd_headers = '''
sd-bus-protocol.h
sd-bus-vtable.h
sd-daemon.h
+ sd-device.h
sd-event.h
+ sd-hwdb.h
sd-id128.h
sd-journal.h
sd-login.h
@@ -15,22 +17,23 @@ _systemd_headers = '''
# https://github.com/mesonbuild/meson/issues/1633
systemd_headers = files(_systemd_headers)
-# sd-device.h
-# sd-hwdb.h
-# sd-dhcp6-client.h
-# sd-dhcp6-lease.h
-# sd-dhcp-client.h
-# sd-dhcp-lease.h
-# sd-dhcp-server.h
-# sd-ipv4acd.h
-# sd-ipv4ll.h
-# sd-lldp.h
-# sd-ndisc.h
-# sd-netlink.h
-# sd-network.h
-# sd-path.h
-# sd-resolve.h
-# sd-utf8.h
+_not_installed_headers = '''
+ sd-dhcp6-client.h
+ sd-dhcp6-lease.h
+ sd-dhcp-client.h
+ sd-dhcp-lease.h
+ sd-dhcp-server.h
+ sd-ipv4acd.h
+ sd-ipv4ll.h
+ sd-lldp.h
+ sd-ndisc.h
+ sd-netlink.h
+ sd-network.h
+ sd-path.h
+ sd-radv.h
+ sd-resolve.h
+ sd-utf8.h
+'''.split()
install_headers(
systemd_headers,
@@ -62,13 +65,15 @@ if cxx.found()
endif
endif
-foreach header : _systemd_headers
+foreach header : _systemd_headers + _not_installed_headers + ['../libudev/libudev.h']
foreach opt : opts
- name = ''.join([header, ':'] + opt)
- test('cc-' + name,
- check_compilation_sh,
- args : cc.cmd_array() + ['-c', '-x'] + opt +
- ['-Werror', '-include',
- join_paths(meson.current_source_dir(), header)])
+ name = ''.join(['cc-', header.split('/')[-1], ':'] + opt)
+ if want_tests != 'false'
+ test(name,
+ check_compilation_sh,
+ args : cc.cmd_array() + ['-c', '-x'] + opt +
+ ['-Werror', '-include',
+ join_paths(meson.current_source_dir(), header)])
+ endif
endforeach
endforeach
diff --git a/src/systemd/sd-bus-protocol.h b/src/systemd/sd-bus-protocol.h
index acff670f7f..a3f4e74359 100644
--- a/src/systemd/sd-bus-protocol.h
+++ b/src/systemd/sd-bus-protocol.h
@@ -3,7 +3,6 @@
#define foosdbusprotocolhfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h
index 1268085498..4350a8c705 100644
--- a/src/systemd/sd-bus-vtable.h
+++ b/src/systemd/sd-bus-vtable.h
@@ -3,7 +3,6 @@
#define foosdbusvtablehfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index 54c4b1ca83..4c1acab9f3 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -3,7 +3,6 @@
#define foosdbushfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
@@ -108,7 +107,7 @@ typedef int (*sd_bus_property_set_t) (sd_bus *bus, const char *path, const char
typedef int (*sd_bus_object_find_t) (sd_bus *bus, const char *path, const char *interface, void *userdata, void **ret_found, sd_bus_error *ret_error);
typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *prefix, void *userdata, char ***ret_nodes, sd_bus_error *ret_error);
typedef int (*sd_bus_track_handler_t) (sd_bus_track *track, void *userdata);
-typedef void (*sd_bus_destroy_t)(void *userdata);
+typedef _sd_destroy_t sd_bus_destroy_t;
#include "sd-bus-protocol.h"
#include "sd-bus-vtable.h"
@@ -155,6 +154,8 @@ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b);
int sd_bus_get_allow_interactive_authorization(sd_bus *bus);
int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b);
int sd_bus_get_exit_on_disconnect(sd_bus *bus);
+int sd_bus_set_close_on_exit(sd_bus *bus, int b);
+int sd_bus_get_close_on_exit(sd_bus *bus);
int sd_bus_set_watch_bind(sd_bus *bus, int b);
int sd_bus_get_watch_bind(sd_bus *bus);
int sd_bus_set_connected_signal(sd_bus *bus, int b);
@@ -206,6 +207,9 @@ sd_event *sd_bus_get_event(sd_bus *bus);
int sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret);
int sd_bus_get_n_queued_write(sd_bus *bus, uint64_t *ret);
+int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec);
+int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret);
+
int sd_bus_add_filter(sd_bus *bus, sd_bus_slot **slot, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata);
@@ -232,7 +236,7 @@ int sd_bus_slot_set_destroy_callback(sd_bus_slot *s, sd_bus_destroy_t callback);
int sd_bus_slot_get_destroy_callback(sd_bus_slot *s, sd_bus_destroy_t *callback);
sd_bus_message* sd_bus_slot_get_current_message(sd_bus_slot *slot);
-sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *bus);
+sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *slot);
void *sd_bus_slot_get_current_userdata(sd_bus_slot *slot);
/* Message object */
@@ -306,6 +310,7 @@ int sd_bus_message_close_container(sd_bus_message *m);
int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all);
int sd_bus_message_read(sd_bus_message *m, const char *types, ...);
+int sd_bus_message_readv(sd_bus_message *m, const char *types, va_list ap);
int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p);
int sd_bus_message_read_array(sd_bus_message *m, char type, const void **ptr, size_t *size);
int sd_bus_message_read_strv(sd_bus_message *m, char ***l); /* free the result! */
@@ -419,6 +424,7 @@ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...)
int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _sd_printf_(3,0);
int sd_bus_error_get_errno(const sd_bus_error *e);
int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e);
+int sd_bus_error_move(sd_bus_error *dest, sd_bus_error *e);
int sd_bus_error_is_set(const sd_bus_error *e);
int sd_bus_error_has_name(const sd_bus_error *e, const char *name);
diff --git a/src/systemd/sd-daemon.h b/src/systemd/sd-daemon.h
index 59ca6c2785..62b0f723c7 100644
--- a/src/systemd/sd-daemon.h
+++ b/src/systemd/sd-daemon.h
@@ -3,7 +3,6 @@
#define foosddaemonhfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h
index 3a51c135d4..3c5c88c56b 100644
--- a/src/systemd/sd-device.h
+++ b/src/systemd/sd-device.h
@@ -21,12 +21,19 @@
#include <sys/sysmacros.h>
#include <sys/types.h>
+#include "sd-event.h"
+
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
typedef struct sd_device sd_device;
typedef struct sd_device_enumerator sd_device_enumerator;
+typedef struct sd_device_monitor sd_device_monitor;
+
+/* callback */
+
+typedef int (*sd_device_monitor_handler_t)(sd_device_monitor *m, sd_device *device, void *userdata);
/* device */
@@ -52,7 +59,7 @@ int sd_device_get_devname(sd_device *device, const char **ret);
int sd_device_get_sysname(sd_device *device, const char **ret);
int sd_device_get_sysnum(sd_device *device, const char **ret);
-int sd_device_get_is_initialized(sd_device *device, int *initialized);
+int sd_device_get_is_initialized(sd_device *device);
int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec);
const char *sd_device_get_tag_first(sd_device *device);
@@ -68,7 +75,7 @@ int sd_device_has_tag(sd_device *device, const char *tag);
int sd_device_get_property_value(sd_device *device, const char *key, const char **value);
int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value);
-int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *value);
+int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value);
/* device enumerator */
@@ -89,8 +96,28 @@ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const c
int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent);
int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator);
+/* device monitor */
+
+int sd_device_monitor_new(sd_device_monitor **ret);
+sd_device_monitor *sd_device_monitor_ref(sd_device_monitor *m);
+sd_device_monitor *sd_device_monitor_unref(sd_device_monitor *m);
+
+int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size);
+int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event);
+int sd_device_monitor_detach_event(sd_device_monitor *m);
+sd_event *sd_device_monitor_get_event(sd_device_monitor *m);
+sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m);
+int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata);
+int sd_device_monitor_stop(sd_device_monitor *m);
+
+int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype);
+int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag);
+int sd_device_monitor_filter_update(sd_device_monitor *m);
+int sd_device_monitor_filter_remove(sd_device_monitor *m);
+
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device, sd_device_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_enumerator, sd_device_enumerator_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_monitor, sd_device_monitor_unref);
_SD_END_DECLARATIONS;
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 931b0e890b..bd0d429df6 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -23,6 +23,7 @@
#include <net/ethernet.h>
#include <netinet/in.h>
#include <sys/types.h>
+#include <stdbool.h>
#include "sd-dhcp-lease.h"
#include "sd-event.h"
@@ -127,15 +128,24 @@ int sd_dhcp_client_set_client_id(
size_t data_len);
int sd_dhcp_client_set_iaid_duid(
sd_dhcp_client *client,
+ bool iaid_set,
uint32_t iaid,
uint16_t duid_type,
const void *duid,
size_t duid_len);
+int sd_dhcp_client_set_iaid_duid_llt(
+ sd_dhcp_client *client,
+ bool iaid_set,
+ uint32_t iaid,
+ uint64_t llt_time);
int sd_dhcp_client_set_duid(
sd_dhcp_client *client,
uint16_t duid_type,
const void *duid,
size_t duid_len);
+int sd_dhcp_client_set_duid_llt(
+ sd_dhcp_client *client,
+ uint64_t llt_time);
int sd_dhcp_client_get_client_id(
sd_dhcp_client *client,
uint8_t *type,
diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h
index 2a60145f5b..4875f10555 100644
--- a/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/sd-dhcp-lease.h
@@ -57,6 +57,7 @@ int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);
int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway);
+int sd_dhcp_route_get_option(sd_dhcp_route *route);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref);
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
index 4f3b2d9e2e..43d38f5c7d 100644
--- a/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/sd-dhcp6-client.h
@@ -21,7 +21,6 @@
#include <inttypes.h>
#include <net/ethernet.h>
-#include <stdbool.h>
#include <sys/types.h>
#include "sd-dhcp6-lease.h"
@@ -102,6 +101,9 @@ int sd_dhcp6_client_set_duid(
uint16_t duid_type,
const void *duid,
size_t duid_len);
+int sd_dhcp6_client_set_duid_llt(
+ sd_dhcp6_client *client,
+ uint64_t llt_time);
int sd_dhcp6_client_set_iaid(
sd_dhcp6_client *client,
uint32_t iaid);
@@ -117,8 +119,15 @@ int sd_dhcp6_client_get_information_request(
int sd_dhcp6_client_set_request_option(
sd_dhcp6_client *client,
uint16_t option);
+int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client,
+ int *delegation);
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
- bool delegation);
+ int delegation);
+int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client,
+ int *request);
+int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
+ int request);
+int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
int sd_dhcp6_client_get_lease(
sd_dhcp6_client *client,
diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h
index 7fcae4ac49..787a12f241 100644
--- a/src/systemd/sd-event.h
+++ b/src/systemd/sd-event.h
@@ -3,7 +3,6 @@
#define foosdeventhfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
@@ -34,7 +33,8 @@
- Supports event source prioritization
- Scales better with a large number of time events because it does not require one timerfd each
- Automatically tries to coalesce timer events system-wide
- - Handles signals and child PIDs
+ - Handles signals, child PIDs, inotify events
+ - Supports systemd-style automatic watchdog event generation
*/
_SD_BEGIN_DECLARATIONS;
@@ -77,7 +77,7 @@ typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si,
typedef void* sd_event_child_handler_t;
#endif
typedef int (*sd_event_inotify_handler_t)(sd_event_source *s, const struct inotify_event *event, void *userdata);
-typedef void (*sd_event_destroy_t)(void *userdata);
+typedef _sd_destroy_t sd_event_destroy_t;
int sd_event_default(sd_event **e);
@@ -143,6 +143,8 @@ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid);
int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret);
int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback);
int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret);
+int sd_event_source_get_floating(sd_event_source *s);
+int sd_event_source_set_floating(sd_event_source *s, int b);
/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);
diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h
index b24fd06f01..f4c05a3683 100644
--- a/src/systemd/sd-id128.h
+++ b/src/systemd/sd-id128.h
@@ -3,7 +3,6 @@
#define foosdid128hfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
@@ -42,18 +41,20 @@ int sd_id128_from_string(const char *s, sd_id128_t *ret);
int sd_id128_randomize(sd_id128_t *ret);
int sd_id128_get_machine(sd_id128_t *ret);
-int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot(sd_id128_t *ret);
int sd_id128_get_invocation(sd_id128_t *ret);
-#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
- ((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
- 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }})
+int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
+int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
{ .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}
+#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
+ ((const sd_id128_t) SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15))
+
+
/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16
* times. It is hence not a good idea to call this macro with an
* expensive function as parameter or an expression with side
@@ -109,7 +110,12 @@ _sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
return a.qwords[0] == 0 && a.qwords[1] == 0;
}
+_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
+ return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF);
+}
+
#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
+#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }})
_SD_END_DECLARATIONS;
diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h
index d34c201293..b4bf3b9176 100644
--- a/src/systemd/sd-journal.h
+++ b/src/systemd/sd-journal.h
@@ -3,7 +3,6 @@
#define foosdjournalhfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h
index d650794cc0..a3e5cd6be6 100644
--- a/src/systemd/sd-lldp.h
+++ b/src/systemd/sd-lldp.h
@@ -109,10 +109,12 @@ typedef struct sd_lldp sd_lldp;
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
typedef enum sd_lldp_event {
- SD_LLDP_EVENT_ADDED = 'a',
- SD_LLDP_EVENT_REMOVED = 'r',
- SD_LLDP_EVENT_UPDATED = 'u',
- SD_LLDP_EVENT_REFRESHED = 'f',
+ SD_LLDP_EVENT_ADDED,
+ SD_LLDP_EVENT_REMOVED,
+ SD_LLDP_EVENT_UPDATED,
+ SD_LLDP_EVENT_REFRESHED,
+ _SD_LLDP_EVENT_MAX,
+ _SD_LLDP_EVENT_INVALID = -1,
} sd_lldp_event;
typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata);
diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h
index a4f70ef7cd..50be5433db 100644
--- a/src/systemd/sd-login.h
+++ b/src/systemd/sd-login.h
@@ -3,7 +3,6 @@
#define foosdloginhfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index 2adfe16062..293db8e8e1 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -3,7 +3,6 @@
#define foosdmessageshfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
@@ -24,12 +23,10 @@
_SD_BEGIN_DECLARATIONS;
-/* Hey! If you add a new message here, you *must* also update the
- * message catalog with an appropriate explanation */
+/* Hey! If you add a new message here, you *must* also update the message catalog with an appropriate explanation */
-/* And if you add a new ID here, make sure to generate a random one
- * with journalctl --new-id128. Do not use any other IDs, and do not
- * count them up manually. */
+/* And if you add a new ID here, make sure to generate a random one with "systemd-id128 new". Do not use any other IDs,
+ * and do not count them up manually. */
#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
#define SD_MESSAGE_JOURNAL_START_STR SD_ID128_MAKE_STR(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
@@ -84,16 +81,19 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
#define SD_MESSAGE_SHUTDOWN_STR SD_ID128_MAKE_STR(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
+/* The messages below are actually about jobs, not really about units, the macros are misleadingly named. Moreover
+ * SD_MESSAGE_UNIT_FAILED is not actually about a failing unit but about a failed start job. A job either finishes with
+ * SD_MESSAGE_UNIT_STARTED or with SD_MESSAGE_UNIT_FAILED hence. */
#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
#define SD_MESSAGE_UNIT_STARTING_STR SD_ID128_MAKE_STR(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
#define SD_MESSAGE_UNIT_STARTED_STR SD_ID128_MAKE_STR(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
+#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
+#define SD_MESSAGE_UNIT_FAILED_STR SD_ID128_MAKE_STR(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
#define SD_MESSAGE_UNIT_STOPPING_STR SD_ID128_MAKE_STR(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
#define SD_MESSAGE_UNIT_STOPPED_STR SD_ID128_MAKE_STR(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
-#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
-#define SD_MESSAGE_UNIT_FAILED_STR SD_ID128_MAKE_STR(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
#define SD_MESSAGE_UNIT_RELOADING_STR SD_ID128_MAKE_STR(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
@@ -106,9 +106,18 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_UNIT_RESOURCES SD_ID128_MAKE(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0)
#define SD_MESSAGE_UNIT_RESOURCES_STR SD_ID128_MAKE_STR(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0)
+#define SD_MESSAGE_UNIT_SUCCESS SD_ID128_MAKE(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48)
+#define SD_MESSAGE_UNIT_SUCCESS_STR SD_ID128_MAKE_STR(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48)
+#define SD_MESSAGE_UNIT_FAILURE_RESULT SD_ID128_MAKE(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c)
+#define SD_MESSAGE_UNIT_FAILURE_RESULT_STR \
+ SD_ID128_MAKE_STR(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c)
+
#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
#define SD_MESSAGE_SPAWN_FAILED_STR SD_ID128_MAKE_STR(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
+#define SD_MESSAGE_UNIT_PROCESS_EXIT SD_ID128_MAKE(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15)
+#define SD_MESSAGE_UNIT_PROCESS_EXIT_STR SD_ID128_MAKE_STR(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15)
+
#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
#define SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR \
SD_ID128_MAKE_STR(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h
index 6b6249ca03..d1bee343a2 100644
--- a/src/systemd/sd-ndisc.h
+++ b/src/systemd/sd-ndisc.h
@@ -55,8 +55,10 @@ typedef struct sd_ndisc sd_ndisc;
typedef struct sd_ndisc_router sd_ndisc_router;
typedef enum sd_ndisc_event {
- SD_NDISC_EVENT_TIMEOUT = 't',
- SD_NDISC_EVENT_ROUTER = 'r',
+ SD_NDISC_EVENT_TIMEOUT,
+ SD_NDISC_EVENT_ROUTER,
+ _SD_NDISC_EVENT_MAX,
+ _SD_NDISC_EVENT_INVALID = -1,
} sd_ndisc_event;
typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata);
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index 51f0fa16b4..30be5b113c 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -18,10 +18,11 @@
***/
#include <inttypes.h>
+#include <linux/neighbour.h>
+#include <linux/rtnetlink.h>
+#include <net/ethernet.h>
#include <netinet/ether.h>
#include <netinet/in.h>
-#include <linux/rtnetlink.h>
-#include <linux/neighbour.h>
#include "sd-event.h"
@@ -32,11 +33,13 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_netlink sd_netlink;
typedef struct sd_genl_socket sd_genl_socket;
typedef struct sd_netlink_message sd_netlink_message;
-typedef enum {SD_GENL_ID_CTRL, SD_GENL_WIREGUARD} sd_genl_family;
+typedef struct sd_netlink_slot sd_netlink_slot;
+typedef enum {SD_GENL_ID_CTRL, SD_GENL_WIREGUARD, SD_GENL_FOU} sd_genl_family;
/* callback */
typedef int (*sd_netlink_message_handler_t)(sd_netlink *nl, sd_netlink_message *m, void *userdata);
+typedef _sd_destroy_t sd_netlink_destroy_t;
/* bus */
int sd_netlink_new_from_netlink(sd_netlink **nl, int fd);
@@ -48,20 +51,21 @@ sd_netlink *sd_netlink_ref(sd_netlink *nl);
sd_netlink *sd_netlink_unref(sd_netlink *nl);
int sd_netlink_send(sd_netlink *nl, sd_netlink_message *message, uint32_t *serial);
-int sd_netlink_call_async(sd_netlink *nl, sd_netlink_message *message,
- sd_netlink_message_handler_t callback,
- void *userdata, uint64_t usec, uint32_t *serial);
-int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial);
+int sd_netlink_call_async(sd_netlink *nl, sd_netlink_slot **ret_slot, sd_netlink_message *message,
+ sd_netlink_message_handler_t callback, sd_netlink_destroy_t destoy_callback,
+ void *userdata, uint64_t usec, const char *description);
int sd_netlink_call(sd_netlink *nl, sd_netlink_message *message, uint64_t timeout,
- sd_netlink_message **reply);
+ sd_netlink_message **reply);
int sd_netlink_get_events(sd_netlink *nl);
int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout);
int sd_netlink_process(sd_netlink *nl, sd_netlink_message **ret);
int sd_netlink_wait(sd_netlink *nl, uint64_t timeout);
-int sd_netlink_add_match(sd_netlink *nl, uint16_t match, sd_netlink_message_handler_t c, void *userdata);
-int sd_netlink_remove_match(sd_netlink *nl, uint16_t match, sd_netlink_message_handler_t c, void *userdata);
+int sd_netlink_add_match(sd_netlink *nl, sd_netlink_slot **ret_slot, uint16_t match,
+ sd_netlink_message_handler_t callback,
+ sd_netlink_destroy_t destroy_callback,
+ void *userdata, const char *description);
int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int64_t priority);
int sd_netlink_detach_event(sd_netlink *nl);
@@ -81,6 +85,7 @@ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type
int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key);
int sd_netlink_message_close_container(sd_netlink_message *m);
+int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data);
int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data);
int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data);
int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data);
@@ -174,14 +179,32 @@ int sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(sd_netlink_message
int sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char *len);
int sd_rtnl_message_routing_policy_rule_set_rtm_type(sd_netlink_message *m, unsigned char type);
int sd_rtnl_message_routing_policy_rule_get_rtm_type(sd_netlink_message *m, unsigned char *type);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref);
+int sd_rtnl_message_routing_policy_rule_set_flags(sd_netlink_message *m, unsigned flags);
+int sd_rtnl_message_routing_policy_rule_get_flags(sd_netlink_message *m, unsigned *flags);
/* 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);
+/* slot */
+sd_netlink_slot *sd_netlink_slot_ref(sd_netlink_slot *nl);
+sd_netlink_slot *sd_netlink_slot_unref(sd_netlink_slot *nl);
+
+sd_netlink *sd_netlink_slot_get_netlink(sd_netlink_slot *slot);
+void *sd_netlink_slot_get_userdata(sd_netlink_slot *slot);
+void *sd_netlink_slot_set_userdata(sd_netlink_slot *slot, void *userdata);
+int sd_netlink_slot_get_destroy_callback(sd_netlink_slot *slot, sd_netlink_destroy_t *callback);
+int sd_netlink_slot_set_destroy_callback(sd_netlink_slot *slot, sd_netlink_destroy_t callback);
+int sd_netlink_slot_get_floating(sd_netlink_slot *slot);
+int sd_netlink_slot_set_floating(sd_netlink_slot *slot, int b);
+int sd_netlink_slot_get_description(sd_netlink_slot *slot, const char **description);
+int sd_netlink_slot_set_description(sd_netlink_slot *slot, const char *description);
+
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_slot, sd_netlink_slot_unref);
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
index c8b7226bca..cc6bca9f5e 100644
--- a/src/systemd/sd-network.h
+++ b/src/systemd/sd-network.h
@@ -151,6 +151,9 @@ int sd_network_link_get_search_domains(int ifindex, char ***domains);
/* Get the route DNS domain names for a given link. */
int sd_network_link_get_route_domains(int ifindex, char ***domains);
+/* Get whether this link shall be used as 'default route' for DNS queries */
+int sd_network_link_get_dns_default_route(int ifindex);
+
/* Get the carrier interface indexes to which current link is bound to. */
int sd_network_link_get_carrier_bound_to(int ifindex, int **ifindexes);
diff --git a/src/systemd/sd-path.h b/src/systemd/sd-path.h
index 65178b63a1..16379876eb 100644
--- a/src/systemd/sd-path.h
+++ b/src/systemd/sd-path.h
@@ -3,7 +3,6 @@
#define foosdpathhfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
@@ -26,7 +25,7 @@ _SD_BEGIN_DECLARATIONS;
enum {
/* Temporary files */
- SD_PATH_TEMPORARY = 0x0ULL,
+ SD_PATH_TEMPORARY,
SD_PATH_TEMPORARY_LARGE,
/* Vendor supplied data */
diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h
index 53c507d88c..93861b9d24 100644
--- a/src/systemd/sd-radv.h
+++ b/src/systemd/sd-radv.h
@@ -22,14 +22,11 @@
#include <inttypes.h>
#include <net/ethernet.h>
#include <netinet/in.h>
-#include <stdbool.h>
#include <sys/types.h>
-#include "sd-ndisc.h"
-
-#include "sd-event.h"
-
#include "_sd-common.h"
+#include "sd-event.h"
+#include "sd-ndisc.h"
_SD_BEGIN_DECLARATIONS;
@@ -61,7 +58,7 @@ int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime);
int sd_radv_set_managed_information(sd_radv *ra, int managed);
int sd_radv_set_other_information(sd_radv *ra, int other);
int sd_radv_set_preference(sd_radv *ra, unsigned preference);
-int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic);
+int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, int dynamic);
sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, const struct in6_addr *prefix,
unsigned char prefixlen);
int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
diff --git a/src/systemd/sd-resolve.h b/src/systemd/sd-resolve.h
index 4c6499815a..d78e8db257 100644
--- a/src/systemd/sd-resolve.h
+++ b/src/systemd/sd-resolve.h
@@ -3,7 +3,6 @@
#define foosdresolvehfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
@@ -18,6 +17,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+/* 'struct addrinfo' needs _GNU_SOURCE */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
#include <inttypes.h>
#include <netdb.h>
#include <sys/socket.h>
@@ -38,6 +42,7 @@ typedef struct sd_resolve_query sd_resolve_query;
/* A callback on completion */
typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata);
typedef int (*sd_resolve_getnameinfo_handler_t)(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata);
+typedef _sd_destroy_t sd_resolve_destroy_t;
enum {
SD_RESOLVE_GET_HOST = 1 << 0,
@@ -96,14 +101,18 @@ int sd_resolve_getaddrinfo(sd_resolve *resolve, sd_resolve_query **q, const char
* if you want to query the hostname (resp. the service name). */
int sd_resolve_getnameinfo(sd_resolve *resolve, sd_resolve_query **q, const struct sockaddr *sa, socklen_t salen, int flags, uint64_t get, sd_resolve_getnameinfo_handler_t callback, void *userdata);
-sd_resolve_query *sd_resolve_query_ref(sd_resolve_query* q);
-sd_resolve_query *sd_resolve_query_unref(sd_resolve_query* q);
+sd_resolve_query *sd_resolve_query_ref(sd_resolve_query *q);
+sd_resolve_query *sd_resolve_query_unref(sd_resolve_query *q);
/* Returns non-zero when the query operation specified by q has been completed. */
-int sd_resolve_query_is_done(sd_resolve_query*q);
+int sd_resolve_query_is_done(sd_resolve_query *q);
void *sd_resolve_query_get_userdata(sd_resolve_query *q);
void *sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata);
+int sd_resolve_query_get_destroy_callback(sd_resolve_query *q, sd_resolve_destroy_t *destroy_callback);
+int sd_resolve_query_set_destroy_callback(sd_resolve_query *q, sd_resolve_destroy_t destroy_callback);
+int sd_resolve_query_get_floating(sd_resolve_query *q);
+int sd_resolve_query_set_floating(sd_resolve_query *q, int b);
sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q);
diff --git a/src/systemd/sd-utf8.h b/src/systemd/sd-utf8.h
index 7f2af65049..151b423d51 100644
--- a/src/systemd/sd-utf8.h
+++ b/src/systemd/sd-utf8.h
@@ -3,7 +3,6 @@
#define foosdutf8hfoo
/***
-
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 33959d3c11..df28bcfd72 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -8,18 +8,21 @@
#include "copy.h"
#include "def.h"
#include "fd-util.h"
-#include "fileio-label.h"
+#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "hashmap.h"
+#include "main-func.h"
#include "pager.h"
#include "path-util.h"
+#include "pretty-print.h"
+#include "set.h"
#include "selinux-util.h"
#include "smack-util.h"
#include "specifier.h"
#include "string-util.h"
#include "strv.h"
-#include "terminal-util.h"
+#include "tmpfile-util-label.h"
#include "uid-range.h"
#include "user-util.h"
#include "utf8.h"
@@ -62,19 +65,34 @@ static char *arg_root = NULL;
static bool arg_cat_config = false;
static const char *arg_replace = NULL;
static bool arg_inline = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static OrderedHashmap *users = NULL, *groups = NULL;
static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL;
static OrderedHashmap *members = NULL;
-static Hashmap *database_uid = NULL, *database_user = NULL;
-static Hashmap *database_gid = NULL, *database_group = NULL;
+static Hashmap *database_by_uid = NULL, *database_by_username = NULL;
+static Hashmap *database_by_gid = NULL, *database_by_groupname = NULL;
+static Set *database_users = NULL, *database_groups = NULL;
static uid_t search_uid = UID_INVALID;
static UidRange *uid_range = NULL;
static unsigned n_uid_range = 0;
+STATIC_DESTRUCTOR_REGISTER(groups, ordered_hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(users, ordered_hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(members, ordered_hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(todo_uids, ordered_hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(todo_gids, ordered_hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(database_by_uid, hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(database_by_username, hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(database_users, set_free_freep);
+STATIC_DESTRUCTOR_REGISTER(database_by_gid, hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(database_by_groupname, hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(database_groups, set_free_freep);
+STATIC_DESTRUCTOR_REGISTER(uid_range, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+
static int load_user_database(void) {
_cleanup_fclose_ FILE *f = NULL;
const char *passwd_path;
@@ -86,11 +104,15 @@ static int load_user_database(void) {
if (!f)
return errno == ENOENT ? 0 : -errno;
- r = hashmap_ensure_allocated(&database_user, &string_hash_ops);
+ r = hashmap_ensure_allocated(&database_by_username, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_allocated(&database_by_uid, NULL);
if (r < 0)
return r;
- r = hashmap_ensure_allocated(&database_uid, NULL);
+ r = set_ensure_allocated(&database_users, NULL);
if (r < 0)
return r;
@@ -102,21 +124,19 @@ static int load_user_database(void) {
if (!n)
return -ENOMEM;
- k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
- if (k < 0 && k != -EEXIST) {
+ k = set_put(database_users, n);
+ if (k < 0) {
free(n);
return k;
}
- q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
- if (q < 0 && q != -EEXIST) {
- if (k <= 0)
- free(n);
- return q;
- }
+ k = hashmap_put(database_by_username, n, UID_TO_PTR(pw->pw_uid));
+ if (k < 0 && k != -EEXIST)
+ return k;
- if (k <= 0 && q <= 0)
- free(n);
+ q = hashmap_put(database_by_uid, UID_TO_PTR(pw->pw_uid), n);
+ if (q < 0 && q != -EEXIST)
+ return q;
}
return r;
}
@@ -132,16 +152,19 @@ static int load_group_database(void) {
if (!f)
return errno == ENOENT ? 0 : -errno;
- r = hashmap_ensure_allocated(&database_group, &string_hash_ops);
+ r = hashmap_ensure_allocated(&database_by_groupname, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_allocated(&database_by_gid, NULL);
if (r < 0)
return r;
- r = hashmap_ensure_allocated(&database_gid, NULL);
+ r = set_ensure_allocated(&database_groups, NULL);
if (r < 0)
return r;
- errno = 0;
- while ((gr = fgetgrent(f))) {
+ while ((r = fgetgrent_sane(f, &gr)) > 0) {
char *n;
int k, q;
@@ -149,28 +172,21 @@ static int load_group_database(void) {
if (!n)
return -ENOMEM;
- k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
- if (k < 0 && k != -EEXIST) {
+ k = set_put(database_groups, n);
+ if (k < 0) {
free(n);
return k;
}
- q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
- if (q < 0 && q != -EEXIST) {
- if (k <= 0)
- free(n);
- return q;
- }
-
- if (k <= 0 && q <= 0)
- free(n);
+ k = hashmap_put(database_by_groupname, n, GID_TO_PTR(gr->gr_gid));
+ if (k < 0 && k != -EEXIST)
+ return k;
- errno = 0;
+ q = hashmap_put(database_by_gid, GID_TO_PTR(gr->gr_gid), n);
+ if (q < 0 && q != -EEXIST)
+ return q;
}
- if (!IN_SET(errno, 0, ENOENT))
- return -errno;
-
- return 0;
+ return r;
}
static int make_backup(const char *target, const char *x) {
@@ -822,11 +838,11 @@ static int uid_is_ok(uid_t uid, const char *name, bool check_with_gid) {
}
/* Let's check the files directly */
- if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
+ if (hashmap_contains(database_by_uid, UID_TO_PTR(uid)))
return 0;
if (check_with_gid) {
- n = hashmap_get(database_gid, GID_TO_PTR(uid));
+ n = hashmap_get(database_by_gid, GID_TO_PTR(uid));
if (n && !streq(n, name))
return 0;
}
@@ -929,7 +945,7 @@ static int add_user(Item *i) {
assert(i);
/* Check the database directly */
- z = hashmap_get(database_user, i->name);
+ z = hashmap_get(database_by_username, i->name);
if (z) {
log_debug("User %s already exists.", i->name);
i->uid = PTR_TO_UID(z);
@@ -1046,10 +1062,10 @@ static int gid_is_ok(gid_t gid) {
if (ordered_hashmap_get(todo_uids, UID_TO_PTR(gid)))
return 0;
- if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
+ if (hashmap_contains(database_by_gid, GID_TO_PTR(gid)))
return 0;
- if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
+ if (hashmap_contains(database_by_uid, UID_TO_PTR(gid)))
return 0;
if (!arg_root) {
@@ -1078,7 +1094,7 @@ static int add_group(Item *i) {
assert(i);
/* Check the database directly */
- z = hashmap_get(database_group, i->name);
+ z = hashmap_get(database_by_groupname, i->name);
if (z) {
log_debug("Group %s already exists.", i->name);
i->gid = PTR_TO_GID(z);
@@ -1112,10 +1128,10 @@ static int add_group(Item *i) {
* r > 0: means the gid does not exist -> fail
* r == 0: means the gid exists -> nothing more to do.
*/
- if (r > 0) {
- log_error("Failed to create %s: please create GID %d", i->name, i->gid);
- return -EINVAL;
- }
+ if (r > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to create %s: please create GID %d",
+ i->name, i->gid);
if (r == 0)
return 0;
}
@@ -1227,10 +1243,9 @@ static int process_item(Item *i) {
}
}
-static void item_free(Item *i) {
-
+static Item* item_free(Item *i) {
if (!i)
- return;
+ return NULL;
free(i->name);
free(i->uid_path);
@@ -1238,10 +1253,11 @@ static void item_free(Item *i) {
free(i->description);
free(i->home);
free(i->shell);
- free(i);
+ return mfree(i);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_hash_ops, char, string_hash_func, string_compare_func, Item, item_free);
static int add_implicit(void) {
char *g, **l;
@@ -1256,7 +1272,7 @@ static int add_implicit(void) {
if (!ordered_hashmap_get(users, *m)) {
_cleanup_(item_freep) Item *j = NULL;
- r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&users, &item_hash_ops);
if (r < 0)
return log_oom();
@@ -1281,7 +1297,7 @@ static int add_implicit(void) {
ordered_hashmap_get(groups, g))) {
_cleanup_(item_freep) Item *j = NULL;
- r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&groups, &item_hash_ops);
if (r < 0)
return log_oom();
@@ -1346,6 +1362,8 @@ static bool item_equal(Item *a, Item *b) {
return true;
}
+DEFINE_PRIVATE_HASH_OPS_FULL(members_hash_ops, char, string_hash_func, string_compare_func, free, char*, strv_free);
+
static int parse_line(const char *fname, unsigned line, const char *buffer) {
static const Specifier specifier_table[] = {
@@ -1536,7 +1554,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
- r = ordered_hashmap_ensure_allocated(&members, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&members, &members_hash_ops);
if (r < 0)
return log_oom();
@@ -1578,7 +1596,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
- r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&users, &item_hash_ops);
if (r < 0)
return log_oom();
@@ -1629,7 +1647,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
- r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&groups, &item_hash_ops);
if (r < 0)
return log_oom();
@@ -1681,7 +1699,6 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
static int read_config_file(const char *fn, bool ignore_enoent) {
_cleanup_fclose_ FILE *rf = NULL;
FILE *f = NULL;
- char line[LINE_MAX];
unsigned v = 0;
int r = 0;
@@ -1701,10 +1718,17 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
f = rf;
}
- FOREACH_LINE(line, f, break) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
char *l;
int k;
+ k = read_line(f, LONG_LINE_MAX, &line);
+ if (k < 0)
+ return log_error_errno(k, "Failed to read '%s': %m", fn);
+ if (k == 0)
+ break;
+
v++;
l = strstrip(line);
@@ -1725,27 +1749,6 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
return r;
}
-static void free_database(Hashmap *by_name, Hashmap *by_id) {
- char *name;
-
- for (;;) {
- name = hashmap_first(by_id);
- if (!name)
- break;
-
- hashmap_remove(by_name, name);
-
- hashmap_steal_first_key(by_id);
- free(name);
- }
-
- while ((name = hashmap_steal_first_key(by_name)))
- free(name);
-
- hashmap_free(by_name);
- hashmap_free(by_id);
-}
-
static int cat_config(void) {
_cleanup_strv_free_ char **files = NULL;
int r;
@@ -1754,12 +1757,19 @@ static int cat_config(void) {
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
return cat_files(NULL, files, 0);
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-sysusers.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
"Creates system user accounts.\n\n"
" -h --help Show this help\n"
@@ -1769,7 +1779,12 @@ static void help(void) {
" --replace=PATH Treat arguments as replacement for PATH\n"
" --inline Treat arguments as configuration lines\n"
" --no-pager Do not pipe output into a pager\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -1804,8 +1819,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -1822,10 +1836,9 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_REPLACE:
if (!path_is_absolute(optarg) ||
- !endswith(optarg, ".conf")) {
- log_error("The argument to --replace= must an absolute path to a config file");
- return -EINVAL;
- }
+ !endswith(optarg, ".conf"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "The argument to --replace= must an absolute path to a config file");
arg_replace = optarg;
break;
@@ -1835,7 +1848,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case '?':
@@ -1845,15 +1858,13 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (arg_replace && arg_cat_config) {
- log_error("Option --replace= is not supported with --cat-config");
- return -EINVAL;
- }
+ if (arg_replace && arg_cat_config)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --replace= is not supported with --cat-config");
- if (arg_replace && optind >= argc) {
- log_error("When --replace= is given, some configuration items must be specified");
- return -EINVAL;
- }
+ if (arg_replace && optind >= argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "When --replace= is given, some configuration items must be specified");
return 1;
}
@@ -1905,33 +1916,26 @@ static int read_config_files(char **args) {
return 0;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_close_ int lock = -1;
Iterator iterator;
- int r;
Item *i;
- char *n;
+ int r;
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
- if (arg_cat_config) {
- r = cat_config();
- goto finish;
- }
+ if (arg_cat_config)
+ return cat_config();
umask(0022);
r = mac_selinux_init();
- if (r < 0) {
- log_error_errno(r, "SELinux setup failed: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "SELinux setup failed: %m");
/* If command line arguments are specified along with --replace, read all
* configuration files and insert the positional arguments at the specified
@@ -1944,48 +1948,38 @@ int main(int argc, char *argv[]) {
else
r = parse_arguments(argv + optind);
if (r < 0)
- goto finish;
+ return r;
/* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
* whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
* nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
* synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
* /etc. */
- if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0) {
- r = log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
- goto finish;
- }
+ if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0)
+ return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
if (!uid_range) {
/* Default to default range of 1..SYSTEM_UID_MAX */
r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
- if (r < 0) {
- log_oom();
- goto finish;
- }
+ if (r < 0)
+ return log_oom();
}
r = add_implicit();
if (r < 0)
- goto finish;
+ return r;
lock = take_etc_passwd_lock(arg_root);
- if (lock < 0) {
- log_error_errno(lock, "Failed to take /etc/passwd lock: %m");
- goto finish;
- }
+ if (lock < 0)
+ return log_error_errno(lock, "Failed to take /etc/passwd lock: %m");
r = load_user_database();
- if (r < 0) {
- log_error_errno(r, "Failed to load user database: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to load user database: %m");
r = load_group_database();
- if (r < 0) {
- log_error_errno(r, "Failed to read group database: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read group database: %m");
ORDERED_HASHMAP_FOREACH(i, groups, iterator)
(void) process_item(i);
@@ -1995,29 +1989,9 @@ int main(int argc, char *argv[]) {
r = write_files();
if (r < 0)
- log_error_errno(r, "Failed to write files: %m");
+ return log_error_errno(r, "Failed to write files: %m");
-finish:
- pager_close();
-
- ordered_hashmap_free_with_destructor(groups, item_free);
- ordered_hashmap_free_with_destructor(users, item_free);
-
- while ((n = ordered_hashmap_first_key(members))) {
- strv_free(ordered_hashmap_steal_first(members));
- free(n);
- }
- ordered_hashmap_free(members);
-
- ordered_hashmap_free(todo_uids);
- ordered_hashmap_free(todo_gids);
-
- free_database(database_user, database_uid);
- free_database(database_group, database_gid);
-
- free(uid_range);
-
- free(arg_root);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index 2b1e267c08..514b6e0169 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -14,6 +14,7 @@
#include "hexdecoct.h"
#include "install.h"
#include "log.h"
+#include "main-func.h"
#include "mkdir.h"
#include "path-lookup.h"
#include "path-util.h"
@@ -41,7 +42,7 @@ static const struct {
* means they are shut down anyway at system power off if running. */
};
-static const char *arg_dest = "/tmp";
+static const char *arg_dest = NULL;
typedef struct SysvStub {
char *name;
@@ -432,7 +433,6 @@ static int load_sysv(SysvStub *s) {
_cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
char *description;
bool supports_reload = false;
- char l[LINE_MAX];
assert(s);
@@ -446,9 +446,16 @@ static int load_sysv(SysvStub *s) {
log_debug("Loading SysV script %s", s->path);
- FOREACH_LINE(l, f, goto fail) {
+ for (;;) {
+ _cleanup_free_ char *l = NULL;
char *t;
+ r = read_line(f, LONG_LINE_MAX, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read configuration file '%s': %m", s->path);
+ if (r == 0)
+ break;
+
line++;
t = strstrip(l);
@@ -456,7 +463,7 @@ static int load_sysv(SysvStub *s) {
/* Try to figure out whether this init script supports
* the reload operation. This heuristic looks for
* "Usage" lines which include the reload option. */
- if ( state == USAGE_CONTINUATION ||
+ if (state == USAGE_CONTINUATION ||
(state == NORMAL && strcasestr(t, "usage"))) {
if (usage_contains_reload(t)) {
supports_reload = true;
@@ -644,9 +651,6 @@ static int load_sysv(SysvStub *s) {
s->loaded = true;
return 0;
-
-fail:
- return log_error_errno(errno, "Failed to read configuration file '%s': %m", s->path);
}
static int fix_order(SysvStub *s, Hashmap *all_services) {
@@ -713,7 +717,7 @@ static int acquire_search_path(const char *def, const char *envvar, char ***ret)
if (strv_isempty(l)) {
strv_free(l);
- l = strv_new(def, NULL);
+ l = strv_new(def);
if (!l)
return log_oom();
}
@@ -913,47 +917,30 @@ finish:
return r;
}
-int main(int argc, char *argv[]) {
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
_cleanup_(free_sysvstub_hashmapp) Hashmap *all_services = NULL;
_cleanup_(lookup_paths_free) LookupPaths lp = {};
SysvStub *service;
Iterator j;
int r;
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1)
- arg_dest = argv[3];
-
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
+ assert_se(arg_dest = dest_late);
r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to find lookup paths: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to find lookup paths: %m");
all_services = hashmap_new(&string_hash_ops);
- if (!all_services) {
- r = log_oom();
- goto finish;
- }
+ if (!all_services)
+ return log_oom();
r = enumerate_sysv(&lp, all_services);
if (r < 0)
- goto finish;
+ return r;
r = set_dependencies_from_rcnd(&lp, all_services);
if (r < 0)
- goto finish;
+ return r;
HASHMAP_FOREACH(service, all_services, j)
(void) load_sysv(service);
@@ -963,8 +950,7 @@ int main(int argc, char *argv[]) {
(void) generate_unit_file(service);
}
- r = 0;
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/test/meson.build b/src/test/meson.build
index 7da7e3a22c..ea049a6fba 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -6,7 +6,8 @@ test_hashmap_ordered_c = custom_target(
input : [awkscript, 'test-hashmap-plain.c'],
output : 'test-hashmap-ordered.c',
command : [awk, '-f', '@INPUT0@', '@INPUT1@'],
- capture : true)
+ capture : true,
+ build_by_default : want_tests != 'false')
test_include_dir = include_directories('.')
@@ -26,14 +27,16 @@ test_libsystemd_sym_c = custom_target(
input : [libsystemd_sym_path] + systemd_headers,
output : 'test-libsystemd-sym.c',
command : [generate_sym_test_py, libsystemd_sym_path] + systemd_headers,
- capture : true)
+ capture : true,
+ build_by_default : want_tests != 'false')
test_libudev_sym_c = custom_target(
'test-libudev-sym.c',
input : [libudev_sym_path, libudev_h_path],
output : 'test-libudev-sym.c',
command : [generate_sym_test_py, '@INPUT0@', '@INPUT1@'],
- capture : true)
+ capture : true,
+ build_by_default : want_tests != 'false')
test_dlopen_c = files('test-dlopen.c')
@@ -60,6 +63,16 @@ tests += [
libmount,
libblkid]],
+ [['src/test/test-emergency-action.c'],
+ [libcore,
+ libshared],
+ []],
+
+ [['src/test/test-chown-rec.c'],
+ [libcore,
+ libshared],
+ []],
+
[['src/test/test-job-type.c'],
[libcore,
libshared],
@@ -81,6 +94,17 @@ tests += [
libblkid],
'', 'manual'],
+ [['src/test/test-nscd-flush.c'],
+ [libcore,
+ libshared],
+ [threads,
+ librt,
+ libseccomp,
+ libselinux,
+ libmount,
+ libblkid],
+ '', 'manual'],
+
[['src/test/test-loopback.c'],
[libcore,
libshared],
@@ -135,10 +159,18 @@ tests += [
libmount,
libblkid]],
+ [['src/test/test-serialize.c'],
+ [],
+ []],
+
[['src/test/test-utf8.c'],
[],
[]],
+ [['src/test/test-dev-setup.c'],
+ [],
+ []],
+
[['src/test/test-capability.c'],
[],
[libcap]],
@@ -156,6 +188,10 @@ tests += [
[],
[]],
+ [['src/test/test-static-destruct.c'],
+ [],
+ []],
+
[['src/test/test-sigbus.c'],
[],
[]],
@@ -188,10 +224,18 @@ tests += [
[],
[]],
+ [['src/test/test-json.c'],
+ [],
+ []],
+
[['src/test/test-mount-util.c'],
[],
[]],
+ [['src/test/test-mountpoint-util.c'],
+ [],
+ []],
+
[['src/test/test-exec-util.c'],
[],
[]],
@@ -284,6 +328,10 @@ tests += [
[],
[]],
+ [['src/test/test-pretty-print.c'],
+ [],
+ []],
+
[['src/test/test-uid-range.c'],
[],
[]],
@@ -392,6 +440,10 @@ tests += [
[],
[]],
+ [['src/test/test-set-disable-mempool.c'],
+ [],
+ [threads]],
+
[['src/test/test-bitmap.c'],
[],
[]],
@@ -609,6 +661,11 @@ tests += [
[],
[]],
+ [['src/test/test-ip-protocol-list.c',
+ shared_generated_gperf_headers],
+ [],
+ []],
+
[['src/test/test-journal-importer.c'],
[],
[]],
@@ -627,7 +684,7 @@ tests += [
libblkid,
libkmod,
libacl],
- '', 'manual'],
+ '', 'manual', '-DLOG_REALM=LOG_REALM_UDEV'],
[['src/test/test-id128.c'],
[],
@@ -645,7 +702,7 @@ tests += [
[['src/test/test-nss.c'],
[],
[libdl],
- '', 'manual'],
+ 'ENABLE_NSS', 'manual'],
[['src/test/test-umount.c',
'src/core/mount-setup.c',
@@ -658,6 +715,10 @@ tests += [
[['src/test/test-bus-util.c'],
[],
[]],
+
+ [['src/test/test-sd-hwdb.c'],
+ [],
+ []],
]
############################################################
@@ -756,8 +817,7 @@ tests += [
libshared],
[threads,
libxz,
- liblz4],
- '', '', '-DCATALOG_DIR="@0@"'.format(build_catalog_dir)],
+ liblz4]],
[['src/journal/test-compress.c'],
[libjournal_core,
@@ -782,6 +842,10 @@ tests += [
############################################################
tests += [
+ [['src/libsystemd/sd-bus/test-bus-address.c'],
+ [],
+ [threads]],
+
[['src/libsystemd/sd-bus/test-bus-marshal.c'],
[],
[threads,
@@ -870,6 +934,27 @@ tests += [
[['src/libsystemd/sd-login/test-login.c'],
[],
[]],
+
+ [['src/libsystemd/sd-device/test-sd-device.c'],
+ [],
+ []],
+
+ [['src/libsystemd/sd-device/test-sd-device-thread.c'],
+ [libbasic,
+ libshared_static,
+ libsystemd],
+ [threads]],
+
+ [['src/libsystemd/sd-device/test-udev-device-thread.c'],
+ [libbasic,
+ libshared_static,
+ libudev],
+ [threads]],
+
+ [['src/libsystemd/sd-device/test-sd-device-monitor.c'],
+ [],
+ []],
+
]
if cxx.found()
diff --git a/src/test/test-acl-util.c b/src/test/test-acl-util.c
index 81eb40444b..df879747f5 100644
--- a/src/test/test-acl-util.c
+++ b/src/test/test-acl-util.c
@@ -7,8 +7,8 @@
#include "acl-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "string-util.h"
+#include "tmpfile-util.h"
#include "user-util.h"
static void test_add_acls_for_user(void) {
@@ -32,7 +32,7 @@ static void test_add_acls_for_user(void) {
if (getuid() == 0) {
const char *nobody = NOBODY_USER_NAME;
- r = get_user_creds(&nobody, &uid, NULL, NULL, NULL);
+ r = get_user_creds(&nobody, &uid, NULL, NULL, NULL, 0);
if (r < 0)
uid = 0;
} else
diff --git a/src/test/test-af-list.c b/src/test/test-af-list.c
index 8806744edb..c8ef3295b5 100644
--- a/src/test/test-af-list.c
+++ b/src/test/test-af-list.c
@@ -16,7 +16,7 @@ static const struct af_name* lookup_af(register const char *str, register GPERF_
int main(int argc, const char *argv[]) {
- unsigned int i;
+ unsigned i;
for (i = 0; i < ELEMENTSOF(af_names); i++) {
if (af_names[i]) {
@@ -27,8 +27,8 @@ int main(int argc, const char *argv[]) {
assert_se(af_to_name(af_max()) == NULL);
assert_se(af_to_name(-1) == NULL);
- assert_se(af_from_name("huddlduddl") == AF_UNSPEC);
- assert_se(af_from_name("") == AF_UNSPEC);
+ assert_se(af_from_name("huddlduddl") == -EINVAL);
+ assert_se(af_from_name("") == -EINVAL);
return 0;
}
diff --git a/src/test/test-architecture.c b/src/test/test-architecture.c
index 6bc0a28a42..8c43bfc750 100644
--- a/src/test/test-architecture.c
+++ b/src/test/test-architecture.c
@@ -2,6 +2,7 @@
#include "architecture.h"
#include "log.h"
+#include "tests.h"
#include "util.h"
#include "virt.h"
@@ -9,6 +10,8 @@ int main(int argc, char *argv[]) {
int a, v;
const char *p;
+ test_setup_logging(LOG_INFO);
+
assert_se(architecture_from_string("") < 0);
assert_se(architecture_from_string(NULL) < 0);
assert_se(architecture_from_string("hoge") < 0);
@@ -18,7 +21,7 @@ int main(int argc, char *argv[]) {
v = detect_virtualization();
if (IN_SET(v, -EPERM, -EACCES))
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("Cannot detect virtualization");
assert_se(v >= 0);
diff --git a/src/test/test-arphrd-list.c b/src/test/test-arphrd-list.c
index 10cd9ebab9..3005fc1b69 100644
--- a/src/test/test-arphrd-list.c
+++ b/src/test/test-arphrd-list.c
@@ -4,8 +4,8 @@
#include <string.h>
#include "macro.h"
+#include "missing_network.h"
#include "string-util.h"
-#include "util.h"
_unused_ \
static const struct arphrd_name* lookup_arphrd(register const char *str, register GPERF_LEN_TYPE len);
@@ -16,7 +16,7 @@ static const struct arphrd_name* lookup_arphrd(register const char *str, registe
int main(int argc, const char *argv[]) {
- unsigned int i;
+ unsigned i;
for (i = 1; i < ELEMENTSOF(arphrd_names); i++) {
if (arphrd_names[i]) {
@@ -27,8 +27,8 @@ int main(int argc, const char *argv[]) {
assert_se(arphrd_to_name(arphrd_max()) == NULL);
assert_se(arphrd_to_name(0) == NULL);
- assert_se(arphrd_from_name("huddlduddl") == 0);
- assert_se(arphrd_from_name("") == 0);
+ assert_se(arphrd_from_name("huddlduddl") == -EINVAL);
+ assert_se(arphrd_from_name("") == -EINVAL);
return 0;
}
diff --git a/src/test/test-ask-password-api.c b/src/test/test-ask-password-api.c
index ffd6da80fe..23b06be19b 100644
--- a/src/test/test-ask-password-api.c
+++ b/src/test/test-ask-password-api.c
@@ -3,15 +3,17 @@
#include "alloc-util.h"
#include "ask-password-api.h"
#include "log.h"
+#include "strv.h"
static void ask_password(void) {
int r;
- _cleanup_free_ char *ret;
+ _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);
+ log_info("Got %s", *ret);
}
int main(int argc, char **argv) {
diff --git a/src/test/test-async.c b/src/test/test-async.c
index 7c7dfe62da..4f5307889e 100644
--- a/src/test/test-async.c
+++ b/src/test/test-async.c
@@ -3,8 +3,8 @@
#include <unistd.h>
#include "async.h"
-#include "fileio.h"
#include "macro.h"
+#include "tmpfile-util.h"
#include "util.h"
static bool test_async = false;
@@ -32,7 +32,7 @@ int main(int argc, char *argv[]) {
assert_se(fcntl(fd, F_GETFD) == -1);
assert_se(test_async);
- unlink(name);
+ (void) unlink(name);
return 0;
}
diff --git a/src/test/test-barrier.c b/src/test/test-barrier.c
index d2afd92f63..6ae84cd6fc 100644
--- a/src/test/test-barrier.c
+++ b/src/test/test-barrier.c
@@ -16,6 +16,7 @@
#include "barrier.h"
#include "util.h"
+#include "tests.h"
/* 20ms to test deadlocks; All timings use multiples of this constant as
* alarm/sleep timers. If this timeout is too small for slow machines to perform
@@ -419,18 +420,10 @@ TEST_BARRIER(test_barrier_pending_exit,
TEST_BARRIER_WAIT_SUCCESS(pid2));
int main(int argc, char *argv[]) {
- /*
- * This test uses real-time alarms and sleeps to test for CPU races
- * explicitly. This is highly fragile if your system is under load. We
- * already increased the BASE_TIME value to make the tests more robust,
- * but that just makes the test take significantly longer. Hence,
- * disable the test by default, so it will not break CI.
- */
- if (argc < 2)
- return EXIT_TEST_SKIP;
+ test_setup_logging(LOG_INFO);
- log_parse_environment();
- log_open();
+ if (!slow_tests_enabled())
+ return log_tests_skipped("slow tests are disabled");
test_barrier_sync();
test_barrier_wait_next();
diff --git a/src/test/test-boot-timestamps.c b/src/test/test-boot-timestamps.c
index ef39304b9f..a79e0cf16b 100644
--- a/src/test/test-boot-timestamps.c
+++ b/src/test/test-boot-timestamps.c
@@ -4,22 +4,19 @@
#include "boot-timestamps.h"
#include "efivars.h"
#include "log.h"
+#include "tests.h"
#include "util.h"
static int test_acpi_fpdt(void) {
- usec_t loader_start;
- usec_t loader_exit;
- char ts_start[FORMAT_TIMESPAN_MAX];
- char ts_exit[FORMAT_TIMESPAN_MAX];
- char ts_span[FORMAT_TIMESPAN_MAX];
+ char ts_start[FORMAT_TIMESPAN_MAX], ts_exit[FORMAT_TIMESPAN_MAX], ts_span[FORMAT_TIMESPAN_MAX];
+ usec_t loader_start, loader_exit;
int r;
r = acpi_get_boot_usec(&loader_start, &loader_exit);
if (r < 0) {
bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES) || r == -ENODATA;
- log_full_errno(ok ? LOG_DEBUG : LOG_ERR,
- r, "Failed to read ACPI FPDT: %m");
+ log_full_errno(ok ? LOG_DEBUG : LOG_ERR, r, "Failed to read ACPI FPDT: %m");
return ok ? 0 : r;
}
@@ -31,19 +28,15 @@ static int test_acpi_fpdt(void) {
}
static int test_efi_loader(void) {
- usec_t loader_start;
- usec_t loader_exit;
- char ts_start[FORMAT_TIMESPAN_MAX];
- char ts_exit[FORMAT_TIMESPAN_MAX];
- char ts_span[FORMAT_TIMESPAN_MAX];
+ char ts_start[FORMAT_TIMESPAN_MAX], ts_exit[FORMAT_TIMESPAN_MAX], ts_span[FORMAT_TIMESPAN_MAX];
+ usec_t loader_start, loader_exit;
int r;
r = efi_loader_get_boot_usec(&loader_start, &loader_exit);
if (r < 0) {
- bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES);
+ bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES) || r == -EOPNOTSUPP;
- log_full_errno(ok ? LOG_DEBUG : LOG_ERR,
- r, "Failed to read EFI loader data: %m");
+ log_full_errno(ok ? LOG_DEBUG : LOG_ERR, r, "Failed to read EFI loader data: %m");
return ok ? 0 : r;
}
@@ -56,17 +49,16 @@ static int test_efi_loader(void) {
static int test_boot_timestamps(void) {
char s[MAX(FORMAT_TIMESPAN_MAX, FORMAT_TIMESTAMP_MAX)];
- int r;
dual_timestamp fw, l, k;
+ int r;
dual_timestamp_from_monotonic(&k, 0);
r = boot_timestamps(NULL, &fw, &l);
if (r < 0) {
- bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES);
+ bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES) || r == -EOPNOTSUPP;
- log_full_errno(ok ? LOG_DEBUG : LOG_ERR,
- r, "Failed to read variables: %m");
+ log_full_errno(ok ? LOG_DEBUG : LOG_ERR, r, "Failed to read variables: %m");
return ok ? 0 : r;
}
@@ -81,8 +73,7 @@ static int test_boot_timestamps(void) {
int main(int argc, char* argv[]) {
int p, q, r;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
+ test_setup_logging(LOG_DEBUG);
p = test_acpi_fpdt();
assert(p >= 0);
@@ -91,5 +82,8 @@ int main(int argc, char* argv[]) {
r = test_boot_timestamps();
assert(r >= 0);
- return (p > 0 || q > 0 || r >> 0) ? EXIT_SUCCESS : EXIT_TEST_SKIP;
+ if (p == 0 && q == 0 && r == 0)
+ return log_tests_skipped("access to firmware variables not possible");
+
+ return EXIT_SUCCESS;
}
diff --git a/src/test/test-bpf.c b/src/test/test-bpf.c
index 4d89bd46d3..ea5f0f5bc6 100644
--- a/src/test/test-bpf.c
+++ b/src/test/test-bpf.c
@@ -28,17 +28,13 @@ int main(int argc, char *argv[]) {
char log_buf[65535];
int r;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
r = enter_cgroup_subroot();
- if (r == -ENOMEDIUM) {
- log_notice("cgroupfs not available, skipping tests");
- return EXIT_TEST_SKIP;
- }
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
- assert_se(set_unit_path(get_testdata_dir("")) >= 0);
+ assert_se(set_unit_path(get_testdata_dir()) >= 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p);
@@ -47,16 +43,12 @@ int main(int argc, char *argv[]) {
r = bpf_program_add_instructions(p, exit_insn, ELEMENTSOF(exit_insn));
assert(r == 0);
- if (getuid() != 0) {
- log_notice("Not running as root, skipping kernel related tests.");
- return EXIT_TEST_SKIP;
- }
+ if (getuid() != 0)
+ return log_tests_skipped("not running as root");
r = bpf_firewall_supported();
- if (r == BPF_FIREWALL_UNSUPPORTED) {
- log_notice("BPF firewalling not supported, skipping");
- return EXIT_TEST_SKIP;
- }
+ if (r == BPF_FIREWALL_UNSUPPORTED)
+ return log_tests_skipped("BPF firewalling not supported");
assert_se(r > 0);
if (r == BPF_FIREWALL_SUPPORTED_WITH_MULTI)
@@ -71,7 +63,7 @@ int main(int argc, char *argv[]) {
/* The simple tests suceeded. Now let's try full unit-based use-case. */
- assert_se(manager_new(UNIT_FILE_USER, true, &m) >= 0);
+ assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
assert_se(manager_startup(m, NULL, NULL) >= 0);
assert_se(u = unit_new(m, sizeof(Service)));
@@ -110,9 +102,8 @@ int main(int argc, char *argv[]) {
unit_dump(u, stdout, NULL);
r = bpf_firewall_compile(u);
- if (IN_SET(r, -ENOTTY, -ENOSYS, -EPERM ))
- /* Kernel doesn't support the necessary bpf bits, or masked out via seccomp? */
- return EXIT_TEST_SKIP;
+ if (IN_SET(r, -ENOTTY, -ENOSYS, -EPERM))
+ return log_tests_skipped("Kernel doesn't support the necessary bpf bits (masked out via seccomp?)");
assert_se(r >= 0);
assert(u->ip_bpf_ingress);
diff --git a/src/test/test-bus-util.c b/src/test/test-bus-util.c
index 791b3928fe..a536608ce0 100644
--- a/src/test/test-bus-util.c
+++ b/src/test/test-bus-util.c
@@ -2,38 +2,7 @@
#include "bus-util.h"
#include "log.h"
-
-static void test_name_async(unsigned n_messages) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- int r;
- unsigned i;
-
- log_info("/* %s (%u) */", __func__, n_messages);
-
- r = bus_open_system_watch_bind_with_description(&bus, "test-bus");
- if (r < 0) {
- log_error_errno(r, "Failed to connect to bus: %m");
- return;
- }
-
- r = bus_request_name_async_may_reload_dbus(bus, NULL, "org.freedesktop.systemd.test-bus-util", 0, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to request name: %m");
- return;
- }
-
- for (i = 0; i < n_messages; i++) {
- r = sd_bus_process(bus, NULL);
- log_debug("stage %u: sd_bus_process returned %d", i, r);
- if (r < 0) {
- log_notice_errno(r, "Processing failed: %m");
- return;
- }
-
- if (r > 0 && i + 1 < n_messages)
- (void) sd_bus_wait(bus, USEC_PER_SEC / 3);
- }
-}
+#include "tests.h"
static int callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
return 1;
@@ -78,12 +47,8 @@ static void test_destroy_callback(void) {
}
int main(int argc, char **argv) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
- test_name_async(0);
- test_name_async(20);
test_destroy_callback();
return 0;
diff --git a/src/test/test-capability.c b/src/test/test-capability.c
index af6d808b6d..dae85f2f91 100644
--- a/src/test/test-capability.c
+++ b/src/test/test-capability.c
@@ -14,13 +14,19 @@
#include "fileio.h"
#include "macro.h"
#include "parse-util.h"
+#include "tests.h"
#include "util.h"
static uid_t test_uid = -1;
static gid_t test_gid = -1;
+#if HAS_FEATURE_ADDRESS_SANITIZER
+/* Keep CAP_SYS_PTRACE when running under Address Sanitizer */
+static const uint64_t test_flags = UINT64_C(1) << CAP_SYS_PTRACE;
+#else
/* We keep CAP_DAC_OVERRIDE to avoid errors with gcov when doing test coverage */
-static uint64_t test_flags = 1ULL << CAP_DAC_OVERRIDE;
+static const uint64_t test_flags = UINT64_C(1) << CAP_DAC_OVERRIDE;
+#endif
/* verify cap_last_cap() against /proc/sys/kernel/cap_last_cap */
static void test_last_cap_file(void) {
@@ -91,10 +97,9 @@ static int setup_tests(bool *run_ambient) {
int r;
nobody = getpwnam(NOBODY_USER_NAME);
- if (!nobody) {
- log_error_errno(errno, "Could not find nobody user: %m");
- return -EXIT_TEST_SKIP;
- }
+ if (!nobody)
+ return log_error_errno(errno, "Could not find nobody user: %m");
+
test_uid = nobody->pw_uid;
test_gid = nobody->pw_gid;
@@ -180,8 +185,6 @@ static void test_update_inherited_set(void) {
caps = cap_get_proc();
assert_se(caps);
- assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
- assert(fv == CAP_CLEAR);
set = (UINT64_C(1) << CAP_CHOWN);
@@ -197,12 +200,6 @@ static void test_set_ambient_caps(void) {
uint64_t set = 0;
cap_flag_value_t fv;
- caps = cap_get_proc();
- assert_se(caps);
- assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
- assert(fv == CAP_CLEAR);
- cap_free(caps);
-
assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
set = (UINT64_C(1) << CAP_CHOWN);
@@ -218,23 +215,20 @@ static void test_set_ambient_caps(void) {
}
int main(int argc, char *argv[]) {
- int r;
bool run_ambient;
+ test_setup_logging(LOG_INFO);
+
test_last_cap_file();
test_last_cap_probe();
- log_parse_environment();
- log_open();
-
log_info("have ambient caps: %s", yes_no(ambient_capabilities_supported()));
if (getuid() != 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("not running as root");
- r = setup_tests(&run_ambient);
- if (r < 0)
- return -r;
+ if (setup_tests(&run_ambient) < 0)
+ return log_tests_skipped("setup failed");
show_capabilities();
diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
index d65959edf1..7f6c0c2772 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -1,10 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2013 David Strauss
-***/
#include <stdio.h>
+#include "cgroup.h"
+#include "cgroup-util.h"
#include "macro.h"
#include "manager.h"
#include "rm-rf.h"
@@ -13,26 +12,41 @@
#include "tests.h"
#include "unit.h"
+#define ASSERT_CGROUP_MASK(got, expected) \
+ log_cgroup_mask(got, expected); \
+ assert_se(got == expected)
+
+#define ASSERT_CGROUP_MASK_JOINED(got, expected) ASSERT_CGROUP_MASK(got, CGROUP_MASK_EXTEND_JOINED(expected))
+
+static void log_cgroup_mask(CGroupMask got, CGroupMask expected) {
+ _cleanup_free_ char *e_store = NULL, *g_store = NULL;
+
+ assert_se(cg_mask_to_string(expected, &e_store) >= 0);
+ log_info("Expected mask: %s\n", e_store);
+ assert_se(cg_mask_to_string(got, &g_store) >= 0);
+ log_info("Got mask: %s\n", g_store);
+}
+
static int test_cgroup_mask(void) {
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
- Unit *son, *daughter, *parent, *root, *grandchild, *parent_deep;
+ Unit *son, *daughter, *parent, *root, *grandchild, *parent_deep, *nomem_parent, *nomem_leaf;
int r;
+ CGroupMask cpu_accounting_mask = get_cpu_accounting_mask();
r = enter_cgroup_subroot();
- if (r == -ENOMEDIUM) {
- puts("Skipping test: cgroupfs not available");
- return EXIT_TEST_SKIP;
- }
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
/* Prepare the manager. */
- assert_se(set_unit_path(get_testdata_dir("")) >= 0);
+ assert_se(set_unit_path(get_testdata_dir()) >= 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m);
if (IN_SET(r, -EPERM, -EACCES)) {
- puts("manager_new: Permission denied. Skipping test.");
- return EXIT_TEST_SKIP;
+ log_error_errno(r, "manager_new: %m");
+ return log_tests_skipped("cannot create manager");
}
+
assert_se(r >= 0);
/* Turn off all kinds of default accouning, so that we can
@@ -54,43 +68,55 @@ static int test_cgroup_mask(void) {
assert_se(manager_load_startable_unit_or_warn(m, "daughter.service", NULL, &daughter) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "grandchild.service", NULL, &grandchild) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "parent-deep.slice", NULL, &parent_deep) >= 0);
+ assert_se(manager_load_startable_unit_or_warn(m, "nomem.slice", NULL, &nomem_parent) >= 0);
+ assert_se(manager_load_startable_unit_or_warn(m, "nomemleaf.service", NULL, &nomem_leaf) >= 0);
assert_se(UNIT_DEREF(son->slice) == parent);
assert_se(UNIT_DEREF(daughter->slice) == parent);
assert_se(UNIT_DEREF(parent_deep->slice) == parent);
assert_se(UNIT_DEREF(grandchild->slice) == parent_deep);
+ assert_se(UNIT_DEREF(nomem_leaf->slice) == nomem_parent);
root = UNIT_DEREF(parent->slice);
+ assert_se(UNIT_DEREF(nomem_parent->slice) == root);
/* Verify per-unit cgroups settings. */
- assert_se(unit_get_own_mask(son) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT));
- assert_se(unit_get_own_mask(daughter) == 0);
- assert_se(unit_get_own_mask(grandchild) == 0);
- assert_se(unit_get_own_mask(parent_deep) == CGROUP_MASK_MEMORY);
- assert_se(unit_get_own_mask(parent) == (CGROUP_MASK_IO | CGROUP_MASK_BLKIO));
- assert_se(unit_get_own_mask(root) == 0);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(son), CGROUP_MASK_CPU);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(daughter), cpu_accounting_mask);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(grandchild), 0);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(parent_deep), CGROUP_MASK_MEMORY);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(parent), (CGROUP_MASK_IO | CGROUP_MASK_BLKIO));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(nomem_parent), 0);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(nomem_leaf), (CGROUP_MASK_IO | CGROUP_MASK_BLKIO));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(root), 0);
/* Verify aggregation of member masks */
- assert_se(unit_get_members_mask(son) == 0);
- assert_se(unit_get_members_mask(daughter) == 0);
- assert_se(unit_get_members_mask(grandchild) == 0);
- assert_se(unit_get_members_mask(parent_deep) == 0);
- assert_se(unit_get_members_mask(parent) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY));
- assert_se(unit_get_members_mask(root) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(son), 0);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(daughter), 0);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(grandchild), 0);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(parent_deep), 0);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(parent), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_MEMORY));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(nomem_parent), (CGROUP_MASK_IO | CGROUP_MASK_BLKIO));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(nomem_leaf), 0);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(root), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
/* Verify aggregation of sibling masks. */
- assert_se(unit_get_siblings_mask(son) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY));
- assert_se(unit_get_siblings_mask(daughter) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY));
- assert_se(unit_get_siblings_mask(grandchild) == 0);
- assert_se(unit_get_siblings_mask(parent_deep) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY));
- assert_se(unit_get_siblings_mask(parent) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
- assert_se(unit_get_siblings_mask(root) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(son), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_MEMORY));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(daughter), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_MEMORY));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(grandchild), 0);
+ ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(parent_deep), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_MEMORY));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(parent), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(nomem_parent), (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(nomem_leaf), (CGROUP_MASK_IO | CGROUP_MASK_BLKIO));
+ ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(root), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
/* Verify aggregation of target masks. */
- assert_se(unit_get_target_mask(son) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported));
- assert_se(unit_get_target_mask(daughter) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported));
- assert_se(unit_get_target_mask(grandchild) == 0);
- assert_se(unit_get_target_mask(parent_deep) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported));
- assert_se(unit_get_target_mask(parent) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported));
- assert_se(unit_get_target_mask(root) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported));
+ ASSERT_CGROUP_MASK(unit_get_target_mask(son), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_MEMORY) & m->cgroup_supported));
+ ASSERT_CGROUP_MASK(unit_get_target_mask(daughter), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_MEMORY) & m->cgroup_supported));
+ ASSERT_CGROUP_MASK(unit_get_target_mask(grandchild), 0);
+ ASSERT_CGROUP_MASK(unit_get_target_mask(parent_deep), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_MEMORY) & m->cgroup_supported));
+ ASSERT_CGROUP_MASK(unit_get_target_mask(parent), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported));
+ ASSERT_CGROUP_MASK(unit_get_target_mask(nomem_parent), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO) & m->cgroup_supported));
+ ASSERT_CGROUP_MASK(unit_get_target_mask(nomem_leaf), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_IO | CGROUP_MASK_BLKIO) & m->cgroup_supported));
+ ASSERT_CGROUP_MASK(unit_get_target_mask(root), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported));
return 0;
}
@@ -104,7 +130,7 @@ static void test_cg_mask_to_string_one(CGroupMask mask, const char *t) {
static void test_cg_mask_to_string(void) {
test_cg_mask_to_string_one(0, NULL);
- test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct io blkio memory devices pids");
+ test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct io blkio memory devices pids bpf-firewall bpf-devices");
test_cg_mask_to_string_one(CGROUP_MASK_CPU, "cpu");
test_cg_mask_to_string_one(CGROUP_MASK_CPUACCT, "cpuacct");
test_cg_mask_to_string_one(CGROUP_MASK_IO, "io");
@@ -120,13 +146,12 @@ static void test_cg_mask_to_string(void) {
}
int main(int argc, char* argv[]) {
- int rc = 0;
+ int rc = EXIT_SUCCESS;
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
- TEST_REQ_RUNNING_SYSTEMD(rc = test_cgroup_mask());
test_cg_mask_to_string();
+ TEST_REQ_RUNNING_SYSTEMD(rc = test_cgroup_mask());
return rc;
}
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
index d49356315e..a3239d73f5 100644
--- a/src/test/test-cgroup-util.c
+++ b/src/test/test-cgroup-util.c
@@ -14,6 +14,7 @@
#include "string-util.h"
#include "strv.h"
#include "test-helper.h"
+#include "tests.h"
#include "user-util.h"
#include "util.h"
@@ -296,7 +297,7 @@ static void test_shift_path(void) {
test_shift_path_one("/foobar/waldo", "/", "/foobar/waldo");
test_shift_path_one("/foobar/waldo", "", "/foobar/waldo");
test_shift_path_one("/foobar/waldo", "/foobar", "/waldo");
- test_shift_path_one("/foobar/waldo", "/fuckfuck", "/foobar/waldo");
+ test_shift_path_one("/foobar/waldo", "/hogehoge", "/foobar/waldo");
}
static void test_mask_supported(void) {
@@ -368,6 +369,17 @@ static void test_is_wanted(void) {
"systemd.unified_cgroup_hierarchy=0 "
"systemd.legacy_systemd_cgroup_controller=0", 1) >= 0);
test_is_wanted_print(false);
+
+ /* cgroup_no_v1=all implies unified cgroup hierarchy, unless otherwise
+ * explicitly specified. */
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "cgroup_no_v1=all", 1) >= 0);
+ test_is_wanted_print(false);
+
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "cgroup_no_v1=all "
+ "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
+ test_is_wanted_print(false);
}
static void test_cg_tests(void) {
@@ -447,9 +459,7 @@ static void test_cg_get_keyed_attribute(void) {
}
int main(void) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_path_decode_unit();
test_path_get_unit();
diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c
index 9211c996ac..5cdfd2dc54 100644
--- a/src/test/test-cgroup.c
+++ b/src/test/test-cgroup.c
@@ -9,7 +9,7 @@
#include "string-util.h"
#include "util.h"
-int main(int argc, char*argv[]) {
+int main(int argc, char *argv[]) {
char *path;
char *c, *p;
diff --git a/src/test/test-chown-rec.c b/src/test/test-chown-rec.c
new file mode 100644
index 0000000000..305d44f568
--- /dev/null
+++ b/src/test/test-chown-rec.c
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/xattr.h>
+
+#include "alloc-util.h"
+#include "chown-recursive.h"
+#include "log.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+
+static const uint8_t acl[] = {
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x07, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x07, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x07, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x05, 0x00,
+ 0xff, 0xff, 0xff, 0xff,
+};
+
+static const uint8_t default_acl[] = {
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x04, 0x00, 0x07, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x07, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x07, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x05, 0x00,
+ 0xff, 0xff, 0xff, 0xff,
+};
+
+static bool has_xattr(const char *p) {
+ char buffer[sizeof(acl) * 4];
+
+ if (lgetxattr(p, "system.posix_acl_access", buffer, sizeof(buffer)) < 0) {
+ if (IN_SET(errno, EOPNOTSUPP, ENOTTY, ENODATA, ENOSYS))
+ return false;
+ }
+
+ return true;
+}
+
+static void test_chown_recursive(void) {
+ _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+ struct stat st;
+ const char *p;
+
+ umask(022);
+ assert_se(mkdtemp_malloc(NULL, &t) >= 0);
+
+ p = strjoina(t, "/dir");
+ assert_se(mkdir(p, 0777) >= 0);
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISDIR(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0755);
+ assert_se(st.st_uid == 0);
+ assert_se(st.st_gid == 0);
+ assert_se(!has_xattr(p));
+
+ p = strjoina(t, "/dir/symlink");
+ assert_se(symlink("../../", p) >= 0);
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISLNK(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0777);
+ assert_se(st.st_uid == 0);
+ assert_se(st.st_gid == 0);
+ assert_se(!has_xattr(p));
+
+ p = strjoina(t, "/dir/reg");
+ assert_se(mknod(p, S_IFREG|0777, 0) >= 0);
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISREG(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0755);
+ assert_se(st.st_uid == 0);
+ assert_se(st.st_gid == 0);
+ assert_se(!has_xattr(p));
+
+ p = strjoina(t, "/dir/sock");
+ assert_se(mknod(p, S_IFSOCK|0777, 0) >= 0);
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISSOCK(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0755);
+ assert_se(st.st_uid == 0);
+ assert_se(st.st_gid == 0);
+ assert_se(!has_xattr(p));
+
+ p = strjoina(t, "/dir/fifo");
+ assert_se(mknod(p, S_IFIFO|0777, 0) >= 0);
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISFIFO(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0755);
+ assert_se(st.st_uid == 0);
+ assert_se(st.st_gid == 0);
+ assert_se(!has_xattr(p));
+
+ /* We now apply an xattr to the dir, and check it again */
+ p = strjoina(t, "/dir");
+ assert_se(setxattr(p, "system.posix_acl_access", acl, sizeof(acl), 0) >= 0);
+ assert_se(setxattr(p, "system.posix_acl_default", default_acl, sizeof(default_acl), 0) >= 0);
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISDIR(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0775); /* acl change changed the mode too */
+ assert_se(st.st_uid == 0);
+ assert_se(st.st_gid == 0);
+ assert_se(has_xattr(p));
+
+ assert_se(path_chown_recursive(t, 1, 2) >= 0);
+
+ p = strjoina(t, "/dir");
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISDIR(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0775);
+ assert_se(st.st_uid == 1);
+ assert_se(st.st_gid == 2);
+ assert_se(!has_xattr(p));
+
+ p = strjoina(t, "/dir/symlink");
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISLNK(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0777);
+ assert_se(st.st_uid == 1);
+ assert_se(st.st_gid == 2);
+ assert_se(!has_xattr(p));
+
+ p = strjoina(t, "/dir/reg");
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISREG(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0755);
+ assert_se(st.st_uid == 1);
+ assert_se(st.st_gid == 2);
+ assert_se(!has_xattr(p));
+
+ p = strjoina(t, "/dir/sock");
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISSOCK(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0755);
+ assert_se(st.st_uid == 1);
+ assert_se(st.st_gid == 2);
+ assert_se(!has_xattr(p));
+
+ p = strjoina(t, "/dir/fifo");
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISFIFO(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0755);
+ assert_se(st.st_uid == 1);
+ assert_se(st.st_gid == 2);
+ assert_se(!has_xattr(p));
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_DEBUG);
+
+ if (geteuid() != 0)
+ return log_tests_skipped("not running as root");
+
+ test_chown_recursive();
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/test-clock.c b/src/test/test-clock.c
index 50e9b7756f..018e679b45 100644
--- a/src/test/test-clock.c
+++ b/src/test/test-clock.c
@@ -9,12 +9,13 @@
#include "clock-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "log.h"
#include "macro.h"
+#include "tmpfile-util.h"
static void test_clock_is_localtime(void) {
- char adjtime[] = "/tmp/test-adjtime.XXXXXX";
- int fd = -1;
+ _cleanup_(unlink_tempfilep) char adjtime[] = "/tmp/test-adjtime.XXXXXX";
_cleanup_fclose_ FILE* f = NULL;
static const struct scenario {
@@ -41,22 +42,17 @@ static void test_clock_is_localtime(void) {
/* without an adjtime file we default to UTC */
assert_se(clock_is_localtime("/nonexisting/adjtime") == 0);
- fd = mkostemp_safe(adjtime);
- assert_se(fd >= 0);
+ assert_se(fmkostemp_safe(adjtime, "w", &f) == 0);
log_info("adjtime test file: %s", adjtime);
- f = fdopen(fd, "w");
- assert_se(f);
for (size_t i = 0; i < ELEMENTSOF(scenarios); ++i) {
log_info("scenario #%zu:, expected result %i", i, scenarios[i].expected_result);
log_info("%s", scenarios[i].contents);
rewind(f);
- ftruncate(fd, 0);
+ ftruncate(fileno(f), 0);
assert_se(write_string_stream(f, scenarios[i].contents, WRITE_STRING_FILE_AVOID_NEWLINE) == 0);
assert_se(clock_is_localtime(adjtime) == scenarios[i].expected_result);
}
-
- unlink(adjtime);
}
/* Test with the real /etc/adjtime */
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index 7ce6ee80ea..5c2d00af88 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -26,6 +26,7 @@
#include "strv.h"
#include "tomoyo-util.h"
#include "user-util.h"
+#include "tests.h"
#include "util.h"
#include "virt.h"
@@ -113,7 +114,7 @@ static void test_condition_test_path(void) {
condition_free(condition);
}
-static int test_condition_test_control_group_controller(void) {
+static void test_condition_test_control_group_controller(void) {
Condition *condition;
CGroupMask system_mask;
CGroupController controller;
@@ -123,7 +124,7 @@ static int test_condition_test_control_group_controller(void) {
r = cg_unified_flush();
if (r < 0) {
log_notice_errno(r, "Skipping ConditionControlGroupController tests: %m");
- return EXIT_TEST_SKIP;
+ return;
}
/* Invalid controllers are ignored */
@@ -180,8 +181,6 @@ static int test_condition_test_control_group_controller(void) {
assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
-
- return EXIT_SUCCESS;
}
static void test_condition_test_ac_power(void) {
@@ -675,9 +674,7 @@ static void test_condition_test_group(void) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_condition_test_path();
test_condition_test_ac_power();
diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c
index 2ec2dfc261..9fd8b6b590 100644
--- a/src/test/test-conf-files.c
+++ b/src/test/test-conf-files.c
@@ -13,9 +13,11 @@
#include "macro.h"
#include "mkdir.h"
#include "parse-util.h"
+#include "path-util.h"
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
#include "user-util.h"
#include "util.h"
@@ -42,7 +44,7 @@ static void test_conf_files_list(bool use_root) {
_cleanup_strv_free_ char **found_files = NULL, **found_files2 = NULL;
const char *root_dir, *search_1, *search_2, *expect_a, *expect_b, *expect_c, *mask;
- log_debug("/* %s */", __func__);
+ log_debug("/* %s(%s) */", __func__, yes_no(use_root));
setup_test_dir(tmp_dir,
"/dir1/a.conf",
@@ -92,12 +94,68 @@ static void test_conf_files_list(bool use_root) {
assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
}
+static void test_conf_files_insert(const char *root) {
+ _cleanup_strv_free_ char **s = NULL;
+
+ log_info("/* %s root=%s */", __func__, strempty(root));
+
+ char **dirs = STRV_MAKE("/dir1", "/dir2", "/dir3");
+
+ _cleanup_free_ const char
+ *foo1 = prefix_root(root, "/dir1/foo.conf"),
+ *foo2 = prefix_root(root, "/dir2/foo.conf"),
+ *bar2 = prefix_root(root, "/dir2/bar.conf"),
+ *zzz3 = prefix_root(root, "/dir3/zzz.conf"),
+ *whatever = prefix_root(root, "/whatever.conf");
+
+ assert_se(conf_files_insert(&s, root, dirs, "/dir2/foo.conf") == 0);
+ assert_se(strv_equal(s, STRV_MAKE(foo2)));
+
+ /* The same file again, https://github.com/systemd/systemd/issues/11124 */
+ assert_se(conf_files_insert(&s, root, dirs, "/dir2/foo.conf") == 0);
+ assert_se(strv_equal(s, STRV_MAKE(foo2)));
+
+ /* Lower priority → new entry is ignored */
+ assert_se(conf_files_insert(&s, root, dirs, "/dir3/foo.conf") == 0);
+ assert_se(strv_equal(s, STRV_MAKE(foo2)));
+
+ /* Higher priority → new entry replaces */
+ assert_se(conf_files_insert(&s, root, dirs, "/dir1/foo.conf") == 0);
+ assert_se(strv_equal(s, STRV_MAKE(foo1)));
+
+ /* Earlier basename */
+ assert_se(conf_files_insert(&s, root, dirs, "/dir2/bar.conf") == 0);
+ assert_se(strv_equal(s, STRV_MAKE(bar2, foo1)));
+
+ /* Later basename */
+ assert_se(conf_files_insert(&s, root, dirs, "/dir3/zzz.conf") == 0);
+ assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, zzz3)));
+
+ /* All lower priority → all ignored */
+ assert_se(conf_files_insert(&s, root, dirs, "/dir3/zzz.conf") == 0);
+ assert_se(conf_files_insert(&s, root, dirs, "/dir2/bar.conf") == 0);
+ assert_se(conf_files_insert(&s, root, dirs, "/dir3/bar.conf") == 0);
+ assert_se(conf_files_insert(&s, root, dirs, "/dir2/foo.conf") == 0);
+ assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, zzz3)));
+
+ /* Two entries that don't match any of the directories, but match basename */
+ assert_se(conf_files_insert(&s, root, dirs, "/dir4/zzz.conf") == 0);
+ assert_se(conf_files_insert(&s, root, dirs, "/zzz.conf") == 0);
+ assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, zzz3)));
+
+ /* An entry that doesn't match any of the directories, no match at all */
+ assert_se(conf_files_insert(&s, root, dirs, "/whatever.conf") == 0);
+ assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, whatever, zzz3)));
+}
+
int main(int argc, char **argv) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_conf_files_list(false);
test_conf_files_list(true);
+ test_conf_files_insert(NULL);
+ test_conf_files_insert("/root");
+ test_conf_files_insert("/root/");
+
return 0;
}
diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c
index ff951d12f4..2921338f62 100644
--- a/src/test/test-conf-parser.c
+++ b/src/test/test-conf-parser.c
@@ -2,12 +2,12 @@
#include "conf-parser.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "log.h"
#include "macro.h"
#include "string-util.h"
#include "strv.h"
+#include "tmpfile-util.h"
#include "util.h"
static void test_config_parse_path_one(const char *rvalue, const char *expected) {
@@ -210,45 +210,6 @@ static void test_config_parse_iec_uint64(void) {
assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4.5M", &offset, NULL) == 0);
}
-static void test_config_parse_join_controllers(void) {
- int r;
- _cleanup_(strv_free_freep) char ***c = NULL;
- char ***c2;
-
- /* Test normal operation */
- r = config_parse_join_controllers(NULL, "example.conf", 11, "Section", 10, "JoinControllers", 0, "cpu,cpuacct net_cls,netprio", &c, NULL);
- assert_se(r == 0);
- assert_se(c);
- assert_se(strv_length(c[0]) == 2);
- assert_se(strv_equal(c[0], STRV_MAKE("cpu", "cpuacct")));
- assert_se(strv_length(c[1]) == 2);
- assert_se(strv_equal(c[1], STRV_MAKE("net_cls", "netprio")));
- assert_se(c[2] == NULL);
-
- /* Test special case of no mounted controllers */
- r = config_parse_join_controllers(NULL, "example.conf", 12, "Section", 10, "JoinControllers", 0, "", &c, NULL);
- assert_se(r == 0);
- assert_se(c);
- assert_se(strv_equal(c[0], STRV_MAKE_EMPTY));
- assert_se(c[1] == NULL);
-
- /* Test merging of overlapping lists */
- r = config_parse_join_controllers(NULL, "example.conf", 13, "Section", 10, "JoinControllers", 0, "a,b b,c", &c, NULL);
- assert_se(r == 0);
- assert_se(c);
- assert_se(strv_length(c[0]) == 3);
- assert_se(strv_contains(c[0], "a"));
- assert_se(strv_contains(c[0], "b"));
- assert_se(strv_contains(c[0], "c"));
- assert_se(c[1] == NULL);
-
- /* Test ignoring of bad lines */
- c2 = c;
- r = config_parse_join_controllers(NULL, "example.conf", 14, "Section", 10, "JoinControllers", 0, "a,\"b ", &c, NULL);
- assert_se(r < 0);
- assert_se(c == c2);
-}
-
#define x10(x) x x x x x x x x x x
#define x100(x) x10(x10(x))
#define x1000(x) x10(x100(x))
@@ -275,6 +236,18 @@ static const char* const config_file[] = {
"3\n",
"[Section]\n"
+ "#hogehoge\\\n" /* continuation is ignored in comment */
+ "setting1=1\\\n" /* normal continuation */
+ "2\\\n"
+ "3\n",
+
+ "[Section]\n"
+ "setting1=1\\\n" /* normal continuation */
+ "#hogehoge\\\n" /* commented out line in continuation is ignored */
+ "2\\\n"
+ "3\n",
+
+ "[Section]\n"
"setting1=1\\\n" /* continuation with extra trailing backslash at the end */
"2\\\n"
"3\\\n",
@@ -311,9 +284,9 @@ static const char* const config_file[] = {
static void test_config_parse(unsigned i, const char *s) {
_cleanup_(unlink_tempfilep) char name[] = "/tmp/test-conf-parser.XXXXXX";
- int fd, r;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *setting1 = NULL;
+ int r;
const ConfigTableItem items[] = {
{ "Section", "setting1", config_parse_string, 0, &setting1},
@@ -322,12 +295,9 @@ static void test_config_parse(unsigned i, const char *s) {
log_info("== %s[%i] ==", __func__, i);
- fd = mkostemp_safe(name);
- assert_se(fd >= 0);
- assert_se((size_t) write(fd, s, strlen(s)) == strlen(s));
-
- assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(f = fdopen(fd, "r"));
+ assert_se(fmkostemp_safe(name, "r+", &f) == 0);
+ assert_se(fwrite(s, strlen(s), 1, f) == 1);
+ rewind(f);
/*
int config_parse(const char *unit,
@@ -353,27 +323,27 @@ static void test_config_parse(unsigned i, const char *s) {
assert_se(streq(setting1, "1"));
break;
- case 4 ... 5:
+ case 4 ... 7:
assert_se(r == 0);
assert_se(streq(setting1, "1 2 3"));
break;
- case 6:
+ case 8:
assert_se(r == 0);
assert_se(streq(setting1, "1\\\\ \\\\2"));
break;
- case 7:
+ case 9:
assert_se(r == 0);
assert_se(streq(setting1, x1000("ABCD")));
break;
- case 8 ... 9:
+ case 10 ... 11:
assert_se(r == 0);
assert_se(streq(setting1, x1000("ABCD") " foobar"));
break;
- case 10 ... 11:
+ case 12 ... 13:
assert_se(r == -ENOBUFS);
assert_se(setting1 == NULL);
break;
@@ -398,7 +368,6 @@ int main(int argc, char **argv) {
test_config_parse_sec();
test_config_parse_nsec();
test_config_parse_iec_uint64();
- test_config_parse_join_controllers();
for (i = 0; i < ELEMENTSOF(config_file); i++)
test_config_parse(i, config_file[i]);
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index 2e8d251ac1..b17a1c53fe 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -14,6 +14,8 @@
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
@@ -254,7 +256,7 @@ static void test_copy_atomic(void) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
test_copy_file();
test_copy_file_fd();
diff --git a/src/test/test-daemon.c b/src/test/test-daemon.c
index c8a83f0fc9..a4b96da045 100644
--- a/src/test/test-daemon.c
+++ b/src/test/test-daemon.c
@@ -7,7 +7,7 @@
#include "parse-util.h"
#include "strv.h"
-int main(int argc, char*argv[]) {
+int main(int argc, char *argv[]) {
_cleanup_strv_free_ char **l = NULL;
int n, i;
usec_t duration = USEC_PER_SEC / 10;
diff --git a/src/test/test-date.c b/src/test/test-date.c
index 99b6f2eb9e..cba51e225c 100644
--- a/src/test/test-date.c
+++ b/src/test/test-date.c
@@ -4,6 +4,7 @@
#include "alloc-util.h"
#include "string-util.h"
+#include "tests.h"
#include "util.h"
static void test_should_pass(const char *p) {
@@ -66,9 +67,7 @@ static void test_one_noutc(const char *p) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_one("17:41");
test_one("18:42:44");
diff --git a/src/test/test-dev-setup.c b/src/test/test-dev-setup.c
new file mode 100644
index 0000000000..9414ea6c3e
--- /dev/null
+++ b/src/test/test-dev-setup.c
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "capability-util.h"
+#include "dev-setup.h"
+#include "fs-util.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "tmpfile-util.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
+ const char *f;
+ struct stat st;
+
+ if (have_effective_cap(CAP_DAC_OVERRIDE) <= 0)
+ return EXIT_TEST_SKIP;
+
+ assert_se(mkdtemp_malloc("/tmp/test-dev-setupXXXXXX", &p) >= 0);
+
+ f = prefix_roota(p, "/run");
+ assert_se(mkdir(f, 0755) >= 0);
+
+ assert_se(make_inaccessible_nodes(p, 1, 1) >= 0);
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/reg");
+ assert_se(stat(f, &st) >= 0);
+ assert_se(S_ISREG(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/dir");
+ assert_se(stat(f, &st) >= 0);
+ assert_se(S_ISDIR(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/fifo");
+ assert_se(stat(f, &st) >= 0);
+ assert_se(S_ISFIFO(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/sock");
+ assert_se(stat(f, &st) >= 0);
+ assert_se(S_ISSOCK(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/chr");
+ if (stat(f, &st) < 0)
+ assert_se(errno == ENOENT);
+ else {
+ assert_se(S_ISCHR(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+ }
+
+ f = prefix_roota(p, "/run/systemd/inaccessible/blk");
+ if (stat(f, &st) < 0)
+ assert_se(errno == ENOENT);
+ else {
+ assert_se(S_ISBLK(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0000);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/test-device-nodes.c b/src/test/test-device-nodes.c
index 02496a121d..ad8d9ace16 100644
--- a/src/test/test-device-nodes.c
+++ b/src/test/test-device-nodes.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2013 Dave Reisner
-***/
#include <sys/types.h>
diff --git a/src/test/test-dissect-image.c b/src/test/test-dissect-image.c
index a0a909baf0..7b32e8373f 100644
--- a/src/test/test-dissect-image.c
+++ b/src/test/test-dissect-image.c
@@ -7,13 +7,14 @@
#include "log.h"
#include "loop-util.h"
#include "string-util.h"
+#include "tests.h"
int main(int argc, char *argv[]) {
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
int r, i;
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
if (argc < 2) {
log_error("Requires one command line argument.");
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index 8ed7dbd1e6..ead5311705 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -1,45 +1,70 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- ***/
#include "alloc-util.h"
#include "dns-domain.h"
#include "macro.h"
#include "string-util.h"
+#include "tests.h"
-static void test_dns_label_unescape_one(const char *what, const char *expect, size_t buffer_sz, int ret) {
+static void test_dns_label_unescape_one(const char *what, const char *expect, size_t buffer_sz, int ret, int ret_ldh) {
char buffer[buffer_sz];
int r;
+ const char *w = what;
- r = dns_label_unescape(&what, buffer, buffer_sz);
+ log_info("%s, %s, %zu, →%d/%d", what, expect, buffer_sz, ret, ret_ldh);
+
+ r = dns_label_unescape(&w, buffer, buffer_sz, 0);
assert_se(r == ret);
+ if (r >= 0)
+ assert_se(streq(buffer, expect));
- if (r < 0)
- return;
+ w = what;
+ r = dns_label_unescape(&w, buffer, buffer_sz, DNS_LABEL_LDH);
+ assert_se(r == ret_ldh);
+ if (r >= 0)
+ assert_se(streq(buffer, expect));
- assert_se(streq(buffer, expect));
+ w = what;
+ r = dns_label_unescape(&w, buffer, buffer_sz, DNS_LABEL_NO_ESCAPES);
+ const int ret_noe = strchr(what, '\\') ? -EINVAL : ret;
+ assert_se(r == ret_noe);
+ if (r >= 0)
+ assert_se(streq(buffer, expect));
}
static void test_dns_label_unescape(void) {
- test_dns_label_unescape_one("hallo", "hallo", 6, 5);
- test_dns_label_unescape_one("hallo", "hallo", 4, -ENOBUFS);
- test_dns_label_unescape_one("", "", 10, 0);
- test_dns_label_unescape_one("hallo\\.foobar", "hallo.foobar", 20, 12);
- test_dns_label_unescape_one("hallo.foobar", "hallo", 10, 5);
- test_dns_label_unescape_one("hallo\n.foobar", "hallo", 20, -EINVAL);
- test_dns_label_unescape_one("hallo\\", "hallo", 20, -EINVAL);
- test_dns_label_unescape_one("hallo\\032 ", "hallo ", 20, 7);
- test_dns_label_unescape_one(".", "", 20, 0);
- test_dns_label_unescape_one("..", "", 20, -EINVAL);
- test_dns_label_unescape_one(".foobar", "", 20, -EINVAL);
- test_dns_label_unescape_one("foobar.", "foobar", 20, 6);
- test_dns_label_unescape_one("foobar..", "foobar", 20, -EINVAL);
+ log_info("/* %s */", __func__);
+
+ test_dns_label_unescape_one("hallo", "hallo", 6, 5, 5);
+ test_dns_label_unescape_one("hallo", "hallo", 4, -ENOBUFS, -ENOBUFS);
+ test_dns_label_unescape_one("", "", 10, 0, 0);
+ test_dns_label_unescape_one("hallo\\.foobar", "hallo.foobar", 20, 12, -EINVAL);
+ test_dns_label_unescape_one("hallo.foobar", "hallo", 10, 5, 5);
+ test_dns_label_unescape_one("hallo\n.foobar", "hallo", 20, -EINVAL, -EINVAL);
+ test_dns_label_unescape_one("hallo\\", "hallo", 20, -EINVAL, -EINVAL);
+ test_dns_label_unescape_one("hallo\\032 ", "hallo ", 20, 7, -EINVAL);
+ test_dns_label_unescape_one(".", "", 20, 0, 0);
+ test_dns_label_unescape_one("..", "", 20, -EINVAL, -EINVAL);
+ test_dns_label_unescape_one(".foobar", "", 20, -EINVAL, -EINVAL);
+ test_dns_label_unescape_one("foobar.", "foobar", 20, 6, 6);
+ test_dns_label_unescape_one("foobar..", "foobar", 20, -EINVAL, -EINVAL);
+ test_dns_label_unescape_one("foo-bar", "foo-bar", 20, 7, 7);
+ test_dns_label_unescape_one("foo-", "foo-", 20, 4, -EINVAL);
+ test_dns_label_unescape_one("-foo", "-foo", 20, 4, -EINVAL);
+ test_dns_label_unescape_one("-foo-", "-foo-", 20, 5, -EINVAL);
+ test_dns_label_unescape_one("foo-.", "foo-", 20, 4, -EINVAL);
+ test_dns_label_unescape_one("foo.-", "foo", 20, 3, 3);
+ test_dns_label_unescape_one("foo\\032", "foo ", 20, 4, -EINVAL);
+ test_dns_label_unescape_one("foo\\045", "foo-", 20, 4, -EINVAL);
+ test_dns_label_unescape_one("głąb", "głąb", 20, 6, -EINVAL);
}
static void test_dns_name_to_wire_format_one(const char *what, const char *expect, size_t buffer_sz, int ret) {
uint8_t buffer[buffer_sz];
int r;
+ log_info("%s, %s, %zu, →%d", what, expect, buffer_sz, ret);
+
r = dns_name_to_wire_format(what, buffer, buffer_sz, false);
assert_se(r == ret);
@@ -81,6 +106,8 @@ static void test_dns_name_to_wire_format(void) {
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
3, 'a', '1', '2', 0 };
+ log_info("/* %s */", __func__);
+
test_dns_name_to_wire_format_one("", out0, sizeof(out0), sizeof(out0));
test_dns_name_to_wire_format_one("foo", out1, sizeof(out1), sizeof(out1));
@@ -101,6 +128,8 @@ static void test_dns_label_unescape_suffix_one(const char *what, const char *exp
const char *label;
int r;
+ log_info("%s, %s, %s, %zu, %d, %d", what, expect1, expect2, buffer_sz, ret1, ret2);
+
label = what + strlen(what);
r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
@@ -115,6 +144,8 @@ static void test_dns_label_unescape_suffix_one(const char *what, const char *exp
}
static void test_dns_label_unescape_suffix(void) {
+ log_info("/* %s */", __func__);
+
test_dns_label_unescape_suffix_one("hallo", "hallo", "", 6, 5, 0);
test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOBUFS, -ENOBUFS);
test_dns_label_unescape_suffix_one("", "", "", 10, 0, 0);
@@ -140,6 +171,8 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex
_cleanup_free_ char *t = NULL;
int r;
+ log_info("%s, %zu, %s, →%d", what, l, expect, ret);
+
r = dns_label_escape_new(what, l, &t);
assert_se(r == ret);
@@ -150,6 +183,8 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex
}
static void test_dns_label_escape(void) {
+ log_info("/* %s */", __func__);
+
test_dns_label_escape_one("", 0, NULL, -EINVAL);
test_dns_label_escape_one("hallo", 5, "hallo", 5);
test_dns_label_escape_one("hallo", 6, "hallo\\000", 9);
@@ -160,7 +195,7 @@ static void test_dns_name_normalize_one(const char *what, const char *expect, in
_cleanup_free_ char *t = NULL;
int r;
- r = dns_name_normalize(what, &t);
+ r = dns_name_normalize(what, 0, &t);
assert_se(r == ret);
if (r < 0)
@@ -321,7 +356,7 @@ static void test_dns_name_reverse(void) {
static void test_dns_name_concat_one(const char *a, const char *b, int r, const char *result) {
_cleanup_free_ char *p = NULL;
- assert_se(dns_name_concat(a, b, &p) == r);
+ assert_se(dns_name_concat(a, b, 0, &p) == r);
assert_se(streq_ptr(p, result));
}
@@ -340,46 +375,68 @@ static void test_dns_name_concat(void) {
test_dns_name_concat_one(NULL, "foo", 0, "foo");
}
-static void test_dns_name_is_valid_one(const char *s, int ret) {
+static void test_dns_name_is_valid_one(const char *s, int ret, int ret_ldh) {
+ log_info("%s, →%d", s, ret);
+
assert_se(dns_name_is_valid(s) == ret);
+ assert_se(dns_name_is_valid_ldh(s) == ret_ldh);
}
static void test_dns_name_is_valid(void) {
- test_dns_name_is_valid_one("foo", 1);
- test_dns_name_is_valid_one("foo.", 1);
- test_dns_name_is_valid_one("foo..", 0);
- test_dns_name_is_valid_one("Foo", 1);
- test_dns_name_is_valid_one("foo.bar", 1);
- test_dns_name_is_valid_one("foo.bar.baz", 1);
- test_dns_name_is_valid_one("", 1);
- test_dns_name_is_valid_one("foo..bar", 0);
- test_dns_name_is_valid_one(".foo.bar", 0);
- test_dns_name_is_valid_one("foo.bar.", 1);
- test_dns_name_is_valid_one("foo.bar..", 0);
- test_dns_name_is_valid_one("\\zbar", 0);
- test_dns_name_is_valid_one("ä", 1);
- test_dns_name_is_valid_one("\n", 0);
+ log_info("/* %s */", __func__);
+
+ test_dns_name_is_valid_one("foo", 1, 1);
+ test_dns_name_is_valid_one("foo.", 1, 1);
+ test_dns_name_is_valid_one("foo..", 0, 0);
+ test_dns_name_is_valid_one("Foo", 1, 1);
+ test_dns_name_is_valid_one("foo.bar", 1, 1);
+ test_dns_name_is_valid_one("foo.bar.baz", 1, 1);
+ test_dns_name_is_valid_one("", 1, 1);
+ test_dns_name_is_valid_one("foo..bar", 0, 0);
+ test_dns_name_is_valid_one(".foo.bar", 0, 0);
+ test_dns_name_is_valid_one("foo.bar.", 1, 1);
+ test_dns_name_is_valid_one("foo.bar..", 0, 0);
+ test_dns_name_is_valid_one("\\zbar", 0, 0);
+ test_dns_name_is_valid_one("ä", 1, 0);
+ test_dns_name_is_valid_one("\n", 0, 0);
+
+ test_dns_name_is_valid_one("dash-", 1, 0);
+ test_dns_name_is_valid_one("-dash", 1, 0);
+ test_dns_name_is_valid_one("dash-dash", 1, 1);
+ test_dns_name_is_valid_one("foo.dash-", 1, 0);
+ test_dns_name_is_valid_one("foo.-dash", 1, 0);
+ test_dns_name_is_valid_one("foo.dash-dash", 1, 1);
+ test_dns_name_is_valid_one("foo.dash-.bar", 1, 0);
+ test_dns_name_is_valid_one("foo.-dash.bar", 1, 0);
+ test_dns_name_is_valid_one("foo.dash-dash.bar", 1, 1);
+ test_dns_name_is_valid_one("dash-.bar", 1, 0);
+ test_dns_name_is_valid_one("-dash.bar", 1, 0);
+ test_dns_name_is_valid_one("dash-dash.bar", 1, 1);
+ test_dns_name_is_valid_one("-.bar", 1, 0);
+ test_dns_name_is_valid_one("foo.-", 1, 0);
/* 256 characters */
- test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345", 0);
+ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345", 0, 0);
/* 255 characters */
- test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a1234", 0);
+ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a1234", 0, 0);
/* 254 characters */
- test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a123", 0);
+ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a123", 0, 0);
/* 253 characters */
- test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", 1);
+ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", 1, 1);
/* label of 64 chars length */
- test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a123", 0);
+ test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a123", 0, 0);
/* label of 63 chars length */
- test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a12", 1);
+ test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a12", 1, 1);
}
static void test_dns_service_name_is_valid(void) {
+ log_info("/* %s */", __func__);
+
assert_se(dns_service_name_is_valid("Lennart's Compüter"));
assert_se(dns_service_name_is_valid("piff.paff"));
@@ -391,6 +448,7 @@ static void test_dns_service_name_is_valid(void) {
}
static void test_dns_srv_type_is_valid(void) {
+ log_info("/* %s */", __func__);
assert_se(dns_srv_type_is_valid("_http._tcp"));
assert_se(dns_srv_type_is_valid("_foo-bar._tcp"));
@@ -414,6 +472,7 @@ static void test_dns_srv_type_is_valid(void) {
}
static void test_dnssd_srv_type_is_valid(void) {
+ log_info("/* %s */", __func__);
assert_se(dnssd_srv_type_is_valid("_http._tcp"));
assert_se(dnssd_srv_type_is_valid("_foo-bar._tcp"));
@@ -440,6 +499,8 @@ static void test_dnssd_srv_type_is_valid(void) {
static void test_dns_service_join_one(const char *a, const char *b, const char *c, int r, const char *d) {
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL;
+ log_info("%s, %s, %s, →%d, %s", a, b, c, r, d);
+
assert_se(dns_service_join(a, b, c, &t) == r);
assert_se(streq_ptr(t, d));
@@ -453,6 +514,8 @@ static void test_dns_service_join_one(const char *a, const char *b, const char *
}
static void test_dns_service_join(void) {
+ log_info("/* %s */", __func__);
+
test_dns_service_join_one("", "", "", -EINVAL, NULL);
test_dns_service_join_one("", "_http._tcp", "", -EINVAL, NULL);
test_dns_service_join_one("", "_http._tcp", "foo", -EINVAL, NULL);
@@ -470,6 +533,8 @@ static void test_dns_service_join(void) {
static void test_dns_service_split_one(const char *joined, const char *a, const char *b, const char *c, int r) {
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL;
+ log_info("%s, %s, %s, %s, →%d", joined, a, b, c, r);
+
assert_se(dns_service_split(joined, &x, &y, &z) == r);
assert_se(streq_ptr(x, a));
assert_se(streq_ptr(y, b));
@@ -486,6 +551,8 @@ static void test_dns_service_split_one(const char *joined, const char *a, const
}
static void test_dns_service_split(void) {
+ log_info("/* %s */", __func__);
+
test_dns_service_split_one("", NULL, NULL, ".", 0);
test_dns_service_split_one("foo", NULL, NULL, "foo", 0);
test_dns_service_split_one("foo.bar", NULL, NULL, "foo.bar", 0);
@@ -498,11 +565,15 @@ static void test_dns_service_split(void) {
static void test_dns_name_change_suffix_one(const char *name, const char *old_suffix, const char *new_suffix, int r, const char *result) {
_cleanup_free_ char *s = NULL;
+ log_info("%s, %s, %s, →%s", name, old_suffix, new_suffix, result);
+
assert_se(dns_name_change_suffix(name, old_suffix, new_suffix, &s) == r);
assert_se(streq_ptr(s, result));
}
static void test_dns_name_change_suffix(void) {
+ log_info("/* %s */", __func__);
+
test_dns_name_change_suffix_one("foo.bar", "bar", "waldo", 1, "foo.waldo");
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "foo.bar.waldi.quux", "piff.paff", 1, "piff.paff");
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "bar.waldi.quux", "piff.paff", 1, "foo.piff.paff");
@@ -517,11 +588,15 @@ static void test_dns_name_change_suffix(void) {
static void test_dns_name_suffix_one(const char *name, unsigned n_labels, const char *result, int ret) {
const char *p = NULL;
+ log_info("%s, %d, →%s, %d", name, n_labels, result, ret);
+
assert_se(ret == dns_name_suffix(name, n_labels, &p));
assert_se(streq_ptr(p, result));
}
static void test_dns_name_suffix(void) {
+ log_info("/* %s */", __func__);
+
test_dns_name_suffix_one("foo.bar", 2, "foo.bar", 0);
test_dns_name_suffix_one("foo.bar", 1, "bar", 1);
test_dns_name_suffix_one("foo.bar", 0, "", 2);
@@ -539,10 +614,14 @@ static void test_dns_name_suffix(void) {
}
static void test_dns_name_count_labels_one(const char *name, int n) {
+ log_info("%s, →%d", name, n);
+
assert_se(dns_name_count_labels(name) == n);
}
static void test_dns_name_count_labels(void) {
+ log_info("/* %s */", __func__);
+
test_dns_name_count_labels_one("foo.bar.quux.", 3);
test_dns_name_count_labels_one("foo.bar.quux", 3);
test_dns_name_count_labels_one("foo.bar.", 2);
@@ -555,10 +634,14 @@ static void test_dns_name_count_labels(void) {
}
static void test_dns_name_equal_skip_one(const char *a, unsigned n_labels, const char *b, int ret) {
+ log_info("%s, %u, %s, →%d", a, n_labels, b, ret);
+
assert_se(dns_name_equal_skip(a, n_labels, b) == ret);
}
static void test_dns_name_equal_skip(void) {
+ log_info("/* %s */", __func__);
+
test_dns_name_equal_skip_one("foo", 0, "bar", 0);
test_dns_name_equal_skip_one("foo", 0, "foo", 1);
test_dns_name_equal_skip_one("foo", 1, "foo", 0);
@@ -586,6 +669,8 @@ static void test_dns_name_equal_skip(void) {
}
static void test_dns_name_compare_func(void) {
+ log_info("/* %s */", __func__);
+
assert_se(dns_name_compare_func("", "") == 0);
assert_se(dns_name_compare_func("", ".") == 0);
assert_se(dns_name_compare_func(".", "") == 0);
@@ -601,11 +686,15 @@ static void test_dns_name_compare_func(void) {
static void test_dns_name_common_suffix_one(const char *a, const char *b, const char *result) {
const char *c;
+ log_info("%s, %s, →%s", a, b, result);
+
assert_se(dns_name_common_suffix(a, b, &c) >= 0);
assert_se(streq(c, result));
}
static void test_dns_name_common_suffix(void) {
+ log_info("/* %s */", __func__);
+
test_dns_name_common_suffix_one("", "", "");
test_dns_name_common_suffix_one("foo", "", "");
test_dns_name_common_suffix_one("", "foo", "");
@@ -640,6 +729,7 @@ static void test_dns_name_apply_idna(void) {
#else
const int ret = 0;
#endif
+ log_info("/* %s */", __func__);
/* IDNA2008 forbids names with hyphens in third and fourth positions
* (https://tools.ietf.org/html/rfc5891#section-4.2.3.1).
@@ -677,6 +767,8 @@ static void test_dns_name_apply_idna(void) {
}
static void test_dns_name_is_valid_or_address(void) {
+ log_info("/* %s */", __func__);
+
assert_se(dns_name_is_valid_or_address(NULL) == 0);
assert_se(dns_name_is_valid_or_address("") == 0);
assert_se(dns_name_is_valid_or_address("foobar") > 0);
@@ -689,9 +781,7 @@ static void test_dns_name_is_valid_or_address(void) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_dns_label_unescape();
test_dns_label_unescape_suffix();
diff --git a/src/test/test-emergency-action.c b/src/test/test-emergency-action.c
new file mode 100644
index 0000000000..8ce28ed9f5
--- /dev/null
+++ b/src/test/test-emergency-action.c
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "emergency-action.h"
+#include "tests.h"
+
+static void test_parse_emergency_action(void) {
+ EmergencyAction x;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(parse_emergency_action("none", false, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_NONE);
+ assert_se(parse_emergency_action("reboot", false, &x) == -EOPNOTSUPP);
+ assert_se(parse_emergency_action("reboot-force", false, &x) == -EOPNOTSUPP);
+ assert_se(parse_emergency_action("reboot-immediate", false, &x) == -EOPNOTSUPP);
+ assert_se(parse_emergency_action("poweroff", false, &x) == -EOPNOTSUPP);
+ assert_se(parse_emergency_action("poweroff-force", false, &x) == -EOPNOTSUPP);
+ assert_se(parse_emergency_action("poweroff-immediate", false, &x) == -EOPNOTSUPP);
+ assert_se(x == EMERGENCY_ACTION_NONE);
+ assert_se(parse_emergency_action("exit", false, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_EXIT);
+ assert_se(parse_emergency_action("exit-force", false, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_EXIT_FORCE);
+ assert_se(parse_emergency_action("exit-forcee", false, &x) == -EINVAL);
+
+ assert_se(parse_emergency_action("none", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_NONE);
+ assert_se(parse_emergency_action("reboot", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_REBOOT);
+ assert_se(parse_emergency_action("reboot-force", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_REBOOT_FORCE);
+ assert_se(parse_emergency_action("reboot-immediate", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_REBOOT_IMMEDIATE);
+ assert_se(parse_emergency_action("poweroff", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_POWEROFF);
+ assert_se(parse_emergency_action("poweroff-force", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_POWEROFF_FORCE);
+ assert_se(parse_emergency_action("poweroff-immediate", true, &x) == 0);
+ assert_se(parse_emergency_action("exit", true, &x) == 0);
+ assert_se(parse_emergency_action("exit-force", true, &x) == 0);
+ assert_se(parse_emergency_action("exit-forcee", true, &x) == -EINVAL);
+ assert_se(x == EMERGENCY_ACTION_EXIT_FORCE);
+}
+
+int main(int argc, char **argv) {
+ test_setup_logging(LOG_INFO);
+
+ test_parse_emergency_action();
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/test-engine.c b/src/test/test-engine.c
index d072a15cb1..0673d36b62 100644
--- a/src/test/test-engine.c
+++ b/src/test/test-engine.c
@@ -18,24 +18,18 @@ int main(int argc, char *argv[]) {
Job *j;
int r;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
r = enter_cgroup_subroot();
- if (r == -ENOMEDIUM) {
- log_notice_errno(r, "Skipping test: cgroupfs not available");
- return EXIT_TEST_SKIP;
- }
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
/* prepare the test */
- assert_se(set_unit_path(get_testdata_dir("")) >= 0);
+ assert_se(set_unit_path(get_testdata_dir()) >= 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m);
- if (MANAGER_SKIP_TEST(r)) {
- log_notice_errno(r, "Skipping test: manager_new: %m");
- return EXIT_TEST_SKIP;
- }
+ if (MANAGER_SKIP_TEST(r))
+ return log_tests_skipped_errno(r, "manager_new");
assert_se(r >= 0);
assert_se(manager_startup(m, NULL, NULL) >= 0);
diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c
index e645d4968e..f57a26021c 100644
--- a/src/test/test-env-util.c
+++ b/src/test/test-env-util.c
@@ -5,6 +5,8 @@
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
+#include "serialize.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
@@ -12,13 +14,13 @@
static void test_strv_env_delete(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL, **d = NULL;
- a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF", NULL);
+ a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF");
assert_se(a);
- b = strv_new("PIEP", "FOO", NULL);
+ b = strv_new("PIEP", "FOO");
assert_se(b);
- c = strv_new("SCHLUMPF", NULL);
+ c = strv_new("SCHLUMPF");
assert_se(c);
d = strv_env_delete(a, 2, b, c);
@@ -42,7 +44,7 @@ static void test_strv_env_get(void) {
static void test_strv_env_unset(void) {
_cleanup_strv_free_ char **l = NULL;
- l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES", NULL);
+ l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES");
assert_se(l);
assert_se(strv_env_unset(l, "SCHLUMPF") == l);
@@ -55,7 +57,7 @@ static void test_strv_env_unset(void) {
static void test_strv_env_set(void) {
_cleanup_strv_free_ char **l = NULL, **r = NULL;
- l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES", NULL);
+ l = strv_new("PIEP", "SCHLUMPF=SMURFF", "NANANANA=YES");
assert_se(l);
r = strv_env_set(l, "WALDO=WALDO");
@@ -71,10 +73,10 @@ static void test_strv_env_set(void) {
static void test_strv_env_merge(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL, **r = NULL;
- a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF", NULL);
+ a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF");
assert_se(a);
- b = strv_new("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES", NULL);
+ b = strv_new("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES");
assert_se(b);
r = strv_env_merge(2, a, b);
@@ -247,8 +249,7 @@ static void test_env_clean(void) {
"xyz\n=xyz",
"xyz=xyz\n",
"another=one",
- "another=final one",
- NULL);
+ "another=final one");
assert_se(e);
assert_se(!strv_env_is_valid(e));
assert_se(strv_env_clean(e) == e);
@@ -303,61 +304,6 @@ static void test_env_assignment_is_valid(void) {
assert_se(!env_assignment_is_valid("głąb=printf \"\x1b]0;<mock-chroot>\x07<mock-chroot>\""));
}
-static void test_deserialize_environment(void) {
- _cleanup_strv_free_ char **env = strv_new("A=1", NULL);
-
- assert_se(deserialize_environment(&env, "env=B=2") >= 0);
- assert_se(deserialize_environment(&env, "env=FOO%%=a\\177b\\nc\\td e") >= 0);
-
- assert_se(strv_equal(env, STRV_MAKE("A=1", "B=2", "FOO%%=a\177b\nc\td e")));
-
- assert_se(deserialize_environment(&env, "env=foo\\") < 0);
- assert_se(deserialize_environment(&env, "env=bar\\_baz") < 0);
-}
-
-static void test_serialize_environment(void) {
- char fn[] = "/tmp/test-env-util.XXXXXXX";
- int fd, r;
- _cleanup_fclose_ FILE *f = NULL;
-
- _cleanup_strv_free_ char **env = strv_new("A=1",
- "B=2",
- "C=ąęółń",
- "D=D=a\\x0Ab",
- "FOO%%=a\177b\nc\td e",
- NULL);
- _cleanup_strv_free_ char **env2 = NULL;
-
- fd = mkostemp_safe(fn);
- assert_se(fd >= 0);
-
- assert_se(f = fdopen(fd, "r+"));
-
- assert_se(serialize_environment(f, env) == 0);
- assert_se(fflush_and_check(f) == 0);
-
- rewind(f);
-
- for (;;) {
- char line[LINE_MAX];
- const char *l;
-
- if (!fgets(line, sizeof line, f))
- break;
-
- char_array_0(line);
- l = strstrip(line);
-
- r = deserialize_environment(&env2, l);
- assert_se(r == 1);
- }
- assert_se(feof(f));
-
- assert_se(strv_equal(env, env2));
-
- unlink(fn);
-}
-
int main(int argc, char *argv[]) {
test_strv_env_delete();
test_strv_env_get();
@@ -374,8 +320,6 @@ int main(int argc, char *argv[]) {
test_env_name_is_valid();
test_env_value_is_valid();
test_env_assignment_is_valid();
- test_deserialize_environment();
- test_serialize_environment();
return 0;
}
diff --git a/src/test/test-escape.c b/src/test/test-escape.c
index 650a9a058d..4ee4aa974d 100644
--- a/src/test/test-escape.c
+++ b/src/test/test-escape.c
@@ -3,6 +3,7 @@
#include "alloc-util.h"
#include "escape.h"
#include "macro.h"
+#include "tests.h"
static void test_cescape(void) {
_cleanup_free_ char *escaped;
@@ -119,9 +120,7 @@ static void test_shell_maybe_quote(void) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_cescape();
test_cunescape();
diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c
index cfc8b5f88e..21a4538d74 100644
--- a/src/test/test-exec-util.c
+++ b/src/test/test-exec-util.c
@@ -16,9 +16,11 @@
#include "fs-util.h"
#include "log.h"
#include "macro.h"
+#include "path-util.h"
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
static int here = 0, here2 = 0, here3 = 0;
void *ignore_stdout_args[] = {&here, &here2, &here3};
@@ -115,9 +117,9 @@ static void test_execute_directory(bool gather_stdout) {
assert_se(chmod(mask2e, 0755) == 0);
if (gather_stdout)
- execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL);
else
- execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL);
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL);
assert_se(chdir(template_lo) == 0);
assert_se(access("it_works", F_OK) >= 0);
@@ -182,7 +184,7 @@ static void test_execution_order(void) {
assert_se(chmod(override, 0755) == 0);
assert_se(chmod(masked, 0755) == 0);
- execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
+ execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL);
assert_se(read_full_file(output, &contents, NULL) >= 0);
assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n"));
@@ -264,7 +266,7 @@ static void test_stdout_gathering(void) {
assert_se(chmod(name2, 0755) == 0);
assert_se(chmod(name3, 0755) == 0);
- r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL);
+ r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL, NULL);
assert_se(r >= 0);
log_info("got: %s", output);
@@ -275,7 +277,7 @@ static void test_stdout_gathering(void) {
static void test_environment_gathering(void) {
char template[] = "/tmp/test-exec-util.XXXXXXX", **p;
const char *dirs[] = {template, NULL};
- const char *name, *name2, *name3;
+ const char *name, *name2, *name3, *old;
int r;
char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
@@ -321,7 +323,33 @@ static void test_environment_gathering(void) {
assert_se(chmod(name2, 0755) == 0);
assert_se(chmod(name3, 0755) == 0);
- r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
+ /* When booting in containers or without initramfs there might not be
+ * any PATH in the environ and if there is no PATH /bin/sh built-in
+ * PATH may leak and override systemd's DEFAULT_PATH which is not
+ * good. Force our own PATH in environment, to prevent expansion of sh
+ * built-in $PATH */
+ old = getenv("PATH");
+ r = setenv("PATH", "no-sh-built-in-path", 1);
+ assert_se(r >= 0);
+
+ r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, NULL);
+ assert_se(r >= 0);
+
+ STRV_FOREACH(p, env)
+ log_info("got env: \"%s\"", *p);
+
+ assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
+ assert_se(streq(strv_env_get(env, "B"), "12"));
+ assert_se(streq(strv_env_get(env, "C"), "001"));
+ assert_se(streq(strv_env_get(env, "PATH"), "no-sh-built-in-path:/no/such/file"));
+
+ /* now retest with "default" path passed in, as created by
+ * manager_default_environment */
+ env = strv_free(env);
+ env = strv_new("PATH=" DEFAULT_PATH);
+ assert_se(env);
+
+ r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, env);
assert_se(r >= 0);
STRV_FOREACH(p, env)
@@ -330,13 +358,14 @@ static void test_environment_gathering(void) {
assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
assert_se(streq(strv_env_get(env, "B"), "12"));
assert_se(streq(strv_env_get(env, "C"), "001"));
- assert_se(endswith(strv_env_get(env, "PATH"), ":/no/such/file"));
+ assert_se(streq(strv_env_get(env, "PATH"), DEFAULT_PATH ":/no/such/file"));
+
+ /* reset environ PATH */
+ (void) setenv("PATH", old, 1);
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_execute_directory(true);
test_execute_directory(false);
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index fa8efdddd2..2115061add 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -28,6 +28,8 @@
#include "util.h"
#include "virt.h"
+static bool can_unshare;
+
typedef void (*test_function_t)(Manager *m);
static void check(Manager *m, Unit *unit, int status_expected, int code_expected) {
@@ -52,6 +54,9 @@ static void check(Manager *m, Unit *unit, int status_expected, int code_expected
n = now(CLOCK_MONOTONIC);
if (ts + timeout < n) {
log_error("Test timeout when testing %s", unit->id);
+ r = unit_kill(unit, KILL_ALL, SIGKILL, NULL);
+ if (r < 0)
+ log_error_errno(r, "Failed to kill %s: %m", unit->id);
exit(EXIT_FAILURE);
}
}
@@ -105,6 +110,25 @@ invalid:
return false;
}
+static bool check_user_has_group_with_same_name(const char *name) {
+ struct passwd *p;
+ struct group *g;
+
+ assert(name);
+
+ p = getpwnam(name);
+ if (!p ||
+ !streq(p->pw_name, name))
+ return false;
+
+ g = getgrgid(p->pw_gid);
+ if (!g ||
+ !streq(g->gr_name, name))
+ return false;
+
+ return true;
+}
+
static bool is_inaccessible_available(void) {
char *p;
@@ -129,7 +153,7 @@ static void test(Manager *m, const char *unit_name, int status_expected, int cod
assert_se(unit_name);
assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0);
+ assert_se(unit_start(unit) >= 0);
check(m, unit, status_expected, code_expected);
}
@@ -137,7 +161,7 @@ static void test_exec_bindpaths(Manager *m) {
assert_se(mkdir_p("/tmp/test-exec-bindpaths", 0755) >= 0);
assert_se(mkdir_p("/tmp/test-exec-bindreadonlypaths", 0755) >= 0);
- test(m, "exec-bindpaths.service", 0, CLD_EXITED);
+ test(m, "exec-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
(void) rm_rf("/tmp/test-exec-bindpaths", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/tmp/test-exec-bindreadonlypaths", REMOVE_ROOT|REMOVE_PHYSICAL);
@@ -208,7 +232,7 @@ static void test_exec_ignoresigpipe(Manager *m) {
static void test_exec_privatetmp(Manager *m) {
assert_se(touch("/tmp/test-exec_privatetmp") >= 0);
- test(m, "exec-privatetmp-yes.service", 0, CLD_EXITED);
+ test(m, "exec-privatetmp-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-privatetmp-no.service", 0, CLD_EXITED);
unlink("/tmp/test-exec_privatetmp");
@@ -226,9 +250,9 @@ static void test_exec_privatedevices(Manager *m) {
return;
}
- test(m, "exec-privatedevices-yes.service", 0, CLD_EXITED);
+ test(m, "exec-privatedevices-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-privatedevices-no.service", 0, CLD_EXITED);
- test(m, "exec-privatedevices-disabled-by-prefix.service", 0, CLD_EXITED);
+ test(m, "exec-privatedevices-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
/* We use capsh to test if the capabilities are
* properly set, so be sure that it exists */
@@ -264,21 +288,21 @@ static void test_exec_protectkernelmodules(Manager *m) {
test(m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED);
test(m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED);
- test(m, "exec-protectkernelmodules-yes-mount-propagation.service", 0, CLD_EXITED);
+ test(m, "exec-protectkernelmodules-yes-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_readonlypaths(Manager *m) {
- test(m, "exec-readonlypaths-simple.service", 0, CLD_EXITED);
+ test(m, "exec-readonlypaths-simple.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
if (path_is_read_only_fs("/var") > 0) {
log_notice("Directory /var is readonly, skipping remaining tests in %s", __func__);
return;
}
- test(m, "exec-readonlypaths.service", 0, CLD_EXITED);
- test(m, "exec-readonlypaths-mount-propagation.service", 0, CLD_EXITED);
- test(m, "exec-readonlypaths-with-bindpaths.service", 0, CLD_EXITED);
+ test(m, "exec-readonlypaths.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
+ test(m, "exec-readonlypaths-with-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-readonlypaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_readwritepaths(Manager *m) {
@@ -288,7 +312,7 @@ static void test_exec_readwritepaths(Manager *m) {
return;
}
- test(m, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED);
+ test(m, "exec-readwritepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_inaccessiblepaths(Manager *m) {
@@ -298,26 +322,28 @@ static void test_exec_inaccessiblepaths(Manager *m) {
return;
}
- test(m, "exec-inaccessiblepaths-proc.service", 0, CLD_EXITED);
+ test(m, "exec-inaccessiblepaths-proc.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
if (path_is_read_only_fs("/") > 0) {
log_notice("Root directory is readonly, skipping remaining tests in %s", __func__);
return;
}
- test(m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED);
+ test(m, "exec-inaccessiblepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_temporaryfilesystem(Manager *m) {
- test(m, "exec-temporaryfilesystem-options.service", 0, CLD_EXITED);
- test(m, "exec-temporaryfilesystem-ro.service", 0, CLD_EXITED);
- test(m, "exec-temporaryfilesystem-rw.service", 0, CLD_EXITED);
- test(m, "exec-temporaryfilesystem-usr.service", 0, CLD_EXITED);
+ test(m, "exec-temporaryfilesystem-options.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-temporaryfilesystem-ro.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-temporaryfilesystem-rw.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-temporaryfilesystem-usr.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
}
static void test_exec_systemcallfilter(Manager *m) {
#if HAVE_SECCOMP
+ int r;
+
if (!is_seccomp_available()) {
log_notice("Seccomp not available, skipping %s", __func__);
return;
@@ -327,18 +353,34 @@ static void test_exec_systemcallfilter(Manager *m) {
test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED);
test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED);
test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED);
+
+ r = find_binary("python3", NULL);
+ if (r < 0) {
+ log_notice_errno(r, "Skipping remaining tests in %s, could not find python3 binary: %m", __func__);
+ return;
+ }
+
test(m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED);
test(m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED);
+ test(m, "exec-systemcallfilter-with-errno-multi.service", errno_from_name("EILSEQ"), CLD_EXITED);
#endif
}
static void test_exec_systemcallerrornumber(Manager *m) {
#if HAVE_SECCOMP
+ int r;
+
if (!is_seccomp_available()) {
log_notice("Seccomp not available, skipping %s", __func__);
return;
}
+ r = find_binary("python3", NULL);
+ if (r < 0) {
+ log_notice_errno(r, "Skipping %s, could not find python3 binary: %m", __func__);
+ return;
+ }
+
test(m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED);
test(m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED);
#endif
@@ -351,13 +393,13 @@ static void test_exec_restrictnamespaces(Manager *m) {
return;
}
- test(m, "exec-restrictnamespaces-no.service", 0, CLD_EXITED);
+ test(m, "exec-restrictnamespaces-no.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED);
- test(m, "exec-restrictnamespaces-mnt.service", 0, CLD_EXITED);
+ test(m, "exec-restrictnamespaces-mnt.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-restrictnamespaces-mnt-blacklist.service", 1, CLD_EXITED);
- test(m, "exec-restrictnamespaces-merge-and.service", 0, CLD_EXITED);
- test(m, "exec-restrictnamespaces-merge-or.service", 0, CLD_EXITED);
- test(m, "exec-restrictnamespaces-merge-all.service", 0, CLD_EXITED);
+ test(m, "exec-restrictnamespaces-merge-and.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
+ test(m, "exec-restrictnamespaces-merge-or.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
+ test(m, "exec-restrictnamespaces-merge-all.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
#endif
}
@@ -426,10 +468,15 @@ static void test_exec_supplementarygroups(Manager *m) {
}
static void test_exec_dynamicuser(Manager *m) {
- test(m, "exec-dynamicuser-fixeduser.service", 0, CLD_EXITED);
- test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", 0, CLD_EXITED);
- test(m, "exec-dynamicuser-supplementarygroups.service", 0, CLD_EXITED);
- test(m, "exec-dynamicuser-statedir.service", 0, CLD_EXITED);
+
+ test(m, "exec-dynamicuser-fixeduser.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ if (check_user_has_group_with_same_name("adm"))
+ test(m, "exec-dynamicuser-fixeduser-adm.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ if (check_user_has_group_with_same_name("games"))
+ test(m, "exec-dynamicuser-fixeduser-games.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-dynamicuser-supplementarygroups.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-dynamicuser-statedir.service", can_unshare ? 0 : EXIT_NAMESPACE, 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);
@@ -437,7 +484,7 @@ static void test_exec_dynamicuser(Manager *m) {
(void) rm_rf("/var/lib/private/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
- test(m, "exec-dynamicuser-statedir-migrate-step2.service", 0, CLD_EXITED);
+ test(m, "exec-dynamicuser-statedir-migrate-step2.service", can_unshare ? 0 : EXIT_NAMESPACE, 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);
@@ -595,19 +642,29 @@ static void test_exec_privatenetwork(Manager *m) {
return;
}
- test(m, "exec-privatenetwork-yes.service", 0, CLD_EXITED);
+ test(m, "exec-privatenetwork-yes.service", can_unshare ? 0 : EXIT_NETWORK, CLD_EXITED);
}
static void test_exec_oomscoreadjust(Manager *m) {
test(m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED);
+
+ if (detect_container() > 0) {
+ log_notice("Testing in container, skipping remaining tests in %s", __func__);
+ return;
+ }
test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED);
}
static void test_exec_ioschedulingclass(Manager *m) {
test(m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED);
test(m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED);
- test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED);
test(m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED);
+
+ if (detect_container() > 0) {
+ log_notice("Testing in container, skipping remaining tests in %s", __func__);
+ return;
+ }
+ test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED);
}
static void test_exec_unsetenvironment(Manager *m) {
@@ -625,6 +682,14 @@ static void test_exec_standardinput(Manager *m) {
test(m, "exec-standardinput-file.service", 0, CLD_EXITED);
}
+static void test_exec_standardoutput(Manager *m) {
+ test(m, "exec-standardoutput-file.service", 0, CLD_EXITED);
+}
+
+static void test_exec_standardoutput_append(Manager *m) {
+ test(m, "exec-standardoutput-append.service", 0, CLD_EXITED);
+}
+
static int run_tests(UnitFileScope scope, const test_function_t *tests) {
const test_function_t *test = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
@@ -633,10 +698,8 @@ static int run_tests(UnitFileScope scope, const test_function_t *tests) {
assert_se(tests);
r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
- if (MANAGER_SKIP_TEST(r)) {
- log_notice_errno(r, "Skipping test: manager_new: %m");
- return EXIT_TEST_SKIP;
- }
+ if (MANAGER_SKIP_TEST(r))
+ return log_tests_skipped_errno(r, "manager_new");
assert_se(r >= 0);
assert_se(manager_startup(m, NULL, NULL) >= 0);
@@ -648,6 +711,8 @@ static int run_tests(UnitFileScope scope, const test_function_t *tests) {
int main(int argc, char *argv[]) {
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
+ _cleanup_free_ char *test_execute_path = NULL;
+ _cleanup_hashmap_free_ Hashmap *s = NULL;
static const test_function_t user_tests[] = {
test_exec_basic,
test_exec_ambientcapabilities,
@@ -672,6 +737,8 @@ int main(int argc, char *argv[]) {
test_exec_restrictnamespaces,
test_exec_runtimedirectory,
test_exec_standardinput,
+ test_exec_standardoutput,
+ test_exec_standardoutput_append,
test_exec_supplementarygroups,
test_exec_systemcallerrornumber,
test_exec_systemcallfilter,
@@ -690,28 +757,32 @@ int main(int argc, char *argv[]) {
};
int r;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
+
+#if HAS_FEATURE_ADDRESS_SANITIZER
+ if (is_run_on_travis_ci()) {
+ log_notice("Running on TravisCI under ASan, skipping, see https://github.com/systemd/systemd/issues/10696");
+ return EXIT_TEST_SKIP;
+ }
+#endif
(void) unsetenv("USER");
(void) unsetenv("LOGNAME");
(void) unsetenv("SHELL");
+ can_unshare = have_namespaces();
+
/* It is needed otherwise cgroup creation fails */
- if (getuid() != 0) {
- puts("Skipping test: not root");
- return EXIT_TEST_SKIP;
- }
+ if (getuid() != 0)
+ return log_tests_skipped("not root");
r = enter_cgroup_subroot();
- if (r == -ENOMEDIUM) {
- puts("Skipping test: cgroupfs not available");
- return EXIT_TEST_SKIP;
- }
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
assert_se(runtime_dir = setup_fake_runtime_dir());
- assert_se(set_unit_path(get_testdata_dir("/test-execute")) >= 0);
+ test_execute_path = path_join(get_testdata_dir(), "test-execute");
+ assert_se(set_unit_path(test_execute_path) >= 0);
/* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test
* cases, otherwise (and if they are present in the environment),
@@ -727,5 +798,33 @@ int main(int argc, char *argv[]) {
if (r != 0)
return r;
+ r = run_tests(UNIT_FILE_SYSTEM, system_tests);
+ if (r != 0)
+ return r;
+
+#if HAVE_SECCOMP
+ /* The following tests are for 1beab8b0d0ff2d7d1436b52d4a0c3d56dc908962. */
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping unshare() filtered tests.");
+ return 0;
+ }
+
+ assert_se(s = hashmap_new(NULL));
+ r = seccomp_syscall_resolve_name("unshare");
+ assert_se(r != __NR_SCMP_ERROR);
+ assert_se(hashmap_put(s, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)) >= 0);
+ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true) >= 0);
+ assert_se(unshare(CLONE_NEWNS) < 0);
+ assert_se(errno == EOPNOTSUPP);
+
+ can_unshare = false;
+
+ r = run_tests(UNIT_FILE_USER, user_tests);
+ if (r != 0)
+ return r;
+
return run_tests(UNIT_FILE_SYSTEM, system_tests);
+#else
+ return 0;
+#endif
}
diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c
index a04403d748..7a0a2ad7d8 100644
--- a/src/test/test-fd-util.c
+++ b/src/test/test-fd-util.c
@@ -10,7 +10,10 @@
#include "path-util.h"
#include "process-util.h"
#include "random-util.h"
+#include "serialize.h"
#include "string-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "util.h"
static void test_close_many(void) {
@@ -316,7 +319,7 @@ static void test_read_nr_open(void) {
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
test_close_many();
test_close_nointr();
diff --git a/src/test/test-fdset.c b/src/test/test-fdset.c
index 7d68a6b966..fb9d397569 100644
--- a/src/test/test-fdset.c
+++ b/src/test/test-fdset.c
@@ -5,8 +5,8 @@
#include "fd-util.h"
#include "fdset.h"
-#include "fileio.h"
#include "macro.h"
+#include "tmpfile-util.h"
#include "util.h"
static void test_fdset_new_fill(void) {
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
index 14ba075144..bf918c1d1e 100644
--- a/src/test/test-fileio.c
+++ b/src/test/test-fileio.c
@@ -6,7 +6,7 @@
#include "alloc-util.h"
#include "ctype.h"
-#include "def.h"
+#include "env-file.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -16,30 +16,23 @@
#include "process-util.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "util.h"
static void test_parse_env_file(void) {
_cleanup_(unlink_tempfilep) char
t[] = "/tmp/test-fileio-in-XXXXXX",
p[] = "/tmp/test-fileio-out-XXXXXX";
- int fd, r;
FILE *f;
_cleanup_free_ char *one = NULL, *two = NULL, *three = NULL, *four = NULL, *five = NULL,
*six = NULL, *seven = NULL, *eight = NULL, *nine = NULL, *ten = NULL;
_cleanup_strv_free_ char **a = NULL, **b = NULL;
char **i;
unsigned k;
+ int r;
- fd = mkostemp_safe(p);
- assert_se(fd >= 0);
- close(fd);
-
- fd = mkostemp_safe(t);
- assert_se(fd >= 0);
-
- f = fdopen(fd, "w");
- assert_se(f);
-
+ assert_se(fmkostemp_safe(t, "w", &f) == 0);
fputs("one=BAR \n"
"# comment\n"
" # comment \n"
@@ -63,7 +56,7 @@ static void test_parse_env_file(void) {
fflush(f);
fclose(f);
- r = load_env_file(NULL, t, NULL, &a);
+ r = load_env_file(NULL, t, &a);
assert_se(r >= 0);
STRV_FOREACH(i, a)
@@ -90,7 +83,7 @@ static void test_parse_env_file(void) {
}
r = parse_env_file(
- NULL, t, NULL,
+ NULL, t,
"one", &one,
"two", &two,
"three", &three,
@@ -100,8 +93,7 @@ static void test_parse_env_file(void) {
"seven", &seven,
"eight", &eight,
"export nine", &nine,
- "ten", &ten,
- NULL);
+ "ten", &ten);
assert_se(r >= 0);
@@ -127,10 +119,16 @@ static void test_parse_env_file(void) {
assert_se(streq(nine, "nineval"));
assert_se(ten == NULL);
+ {
+ /* prepare a temporary file to write the environment to */
+ _cleanup_close_ int fd = mkostemp_safe(p);
+ assert_se(fd >= 0);
+ }
+
r = write_env_file(p, a);
assert_se(r >= 0);
- r = load_env_file(NULL, p, NULL, &b);
+ r = load_env_file(NULL, p, &b);
assert_se(r >= 0);
}
@@ -138,21 +136,12 @@ static void test_parse_multiline_env_file(void) {
_cleanup_(unlink_tempfilep) char
t[] = "/tmp/test-fileio-in-XXXXXX",
p[] = "/tmp/test-fileio-out-XXXXXX";
- int fd, r;
FILE *f;
_cleanup_strv_free_ char **a = NULL, **b = NULL;
char **i;
+ int r;
- fd = mkostemp_safe(p);
- assert_se(fd >= 0);
- close(fd);
-
- fd = mkostemp_safe(t);
- assert_se(fd >= 0);
-
- f = fdopen(fd, "w");
- assert_se(f);
-
+ assert_se(fmkostemp_safe(t, "w", &f) == 0);
fputs("one=BAR\\\n"
" VAR\\\n"
"\tGAR\n"
@@ -168,7 +157,7 @@ static void test_parse_multiline_env_file(void) {
fflush(f);
fclose(f);
- r = load_env_file(NULL, t, NULL, &a);
+ r = load_env_file(NULL, t, &a);
assert_se(r >= 0);
STRV_FOREACH(i, a)
@@ -179,28 +168,28 @@ static void test_parse_multiline_env_file(void) {
assert_se(streq_ptr(a[2], "tri=bar var \tgar "));
assert_se(a[3] == NULL);
+ {
+ _cleanup_close_ int fd = mkostemp_safe(p);
+ assert_se(fd >= 0);
+ }
+
r = write_env_file(p, a);
assert_se(r >= 0);
- r = load_env_file(NULL, p, NULL, &b);
+ r = load_env_file(NULL, p, &b);
assert_se(r >= 0);
}
static void test_merge_env_file(void) {
_cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
- int fd, r;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **a = NULL;
char **i;
+ int r;
- fd = mkostemp_safe(t);
- assert_se(fd >= 0);
-
+ assert_se(fmkostemp_safe(t, "w", &f) == 0);
log_info("/* %s (%s) */", __func__, t);
- f = fdopen(fd, "w");
- assert_se(f);
-
r = write_string_stream(f,
"one=1 \n"
"twelve=${one}2\n"
@@ -257,19 +246,14 @@ static void test_merge_env_file(void) {
static void test_merge_env_file_invalid(void) {
_cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
- int fd, r;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **a = NULL;
char **i;
+ int r;
- fd = mkostemp_safe(t);
- assert_se(fd >= 0);
-
+ assert_se(fmkostemp_safe(t, "w", &f) == 0);
log_info("/* %s (%s) */", __func__, t);
- f = fdopen(fd, "w");
- assert_se(f);
-
r = write_string_stream(f,
"unset one \n"
"unset one= \n"
@@ -296,16 +280,11 @@ static void test_merge_env_file_invalid(void) {
static void test_executable_is_script(void) {
_cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
- int fd, r;
_cleanup_fclose_ FILE *f = NULL;
char *command;
+ int r;
- fd = mkostemp_safe(t);
- assert_se(fd >= 0);
-
- f = fdopen(fd, "w");
- assert_se(f);
-
+ assert_se(fmkostemp_safe(t, "w", &f) == 0);
fputs("#! /bin/script -a -b \ngoo goo", f);
fflush(f);
@@ -488,7 +467,7 @@ static void test_load_env_file_pairs(void) {
f = fdopen(fd, "r");
assert_se(f);
- r = load_env_file_pairs(f, fn, NULL, &l);
+ r = load_env_file_pairs(f, fn, &l);
assert_se(r >= 0);
assert_se(strv_length(l) == 14);
@@ -631,8 +610,43 @@ static void test_tempfn(void) {
free(ret);
}
+static const char chars[] =
+ "Aąę„â€\n루\377";
+
+static void test_fgetc(void) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char c;
+
+ f = fmemopen((void*) chars, sizeof(chars), "re");
+ assert_se(f);
+
+ for (unsigned i = 0; i < sizeof(chars); i++) {
+ assert_se(safe_fgetc(f, &c) == 1);
+ assert_se(c == chars[i]);
+
+ /* EOF is -1, and hence we can't push value 255 in this way */
+ assert_se(ungetc(c, f) != EOF || c == EOF);
+ assert_se(c == EOF || safe_fgetc(f, &c) == 1);
+ assert_se(c == chars[i]);
+
+ /* But it works when we push it properly cast */
+ assert_se(ungetc((unsigned char) c, f) != EOF);
+ assert_se(safe_fgetc(f, &c) == 1);
+ assert_se(c == chars[i]);
+ }
+
+ assert_se(safe_fgetc(f, &c) == 0);
+}
+
static const char buffer[] =
"Some test data\n"
+ "루Non-ascii chars: ąę„â€\n"
+ "terminators\r\n"
+ "and even more\n\r"
+ "now the same with a NUL\n\0"
+ "and more\r\0"
+ "and even more\r\n\0"
+ "and yet even more\n\r\0"
"With newlines, and a NUL byte\0"
"\n"
"an empty line\n"
@@ -645,6 +659,27 @@ static void test_read_line_one_file(FILE *f) {
assert_se(read_line(f, (size_t) -1, &line) == 15 && streq(line, "Some test data"));
line = mfree(line);
+ assert_se(read_line(f, (size_t) -1, &line) > 0 && streq(line, "루Non-ascii chars: ąę„â€"));
+ line = mfree(line);
+
+ assert_se(read_line(f, (size_t) -1, &line) == 13 && streq(line, "terminators"));
+ line = mfree(line);
+
+ assert_se(read_line(f, (size_t) -1, &line) == 15 && streq(line, "and even more"));
+ line = mfree(line);
+
+ assert_se(read_line(f, (size_t) -1, &line) == 25 && streq(line, "now the same with a NUL"));
+ line = mfree(line);
+
+ assert_se(read_line(f, (size_t) -1, &line) == 10 && streq(line, "and more"));
+ line = mfree(line);
+
+ assert_se(read_line(f, (size_t) -1, &line) == 16 && streq(line, "and even more"));
+ line = mfree(line);
+
+ assert_se(read_line(f, (size_t) -1, &line) == 20 && streq(line, "and yet even more"));
+ line = mfree(line);
+
assert_se(read_line(f, 1024, &line) == 30 && streq(line, "With newlines, and a NUL byte"));
line = mfree(line);
@@ -661,10 +696,7 @@ static void test_read_line_one_file(FILE *f) {
/* read_line() stopped when it hit the limit, that means when we continue reading we'll read at the first
* character after the previous limit. Let's make use of tha to continue our test. */
- assert_se(read_line(f, 1024, &line) == 61 && streq(line, "line that is supposed to be truncated, because it is so long"));
- line = mfree(line);
-
- assert_se(read_line(f, 1024, &line) == 1 && streq(line, ""));
+ assert_se(read_line(f, 1024, &line) == 62 && streq(line, "line that is supposed to be truncated, because it is so long"));
line = mfree(line);
assert_se(read_line(f, 1024, &line) == 0 && streq(line, ""));
@@ -709,10 +741,77 @@ static void test_read_line3(void) {
assert_se(read_line(f, LINE_MAX, NULL) == 0);
}
+static void test_read_line4(void) {
+ static const struct {
+ size_t length;
+ const char *string;
+ } eof_endings[] = {
+ /* Each of these will be followed by EOF and should generate the one same single string */
+ { 3, "foo" },
+ { 4, "foo\n" },
+ { 4, "foo\r" },
+ { 4, "foo\0" },
+ { 5, "foo\n\0" },
+ { 5, "foo\r\0" },
+ { 5, "foo\r\n" },
+ { 5, "foo\n\r" },
+ { 6, "foo\r\n\0" },
+ { 6, "foo\n\r\0" },
+ };
+
+ size_t i;
+ int r;
+
+ for (i = 0; i < ELEMENTSOF(eof_endings); i++) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *s = NULL;
+
+ assert_se(f = fmemopen((void*) eof_endings[i].string, eof_endings[i].length, "r"));
+
+ r = read_line(f, (size_t) -1, &s);
+ assert_se((size_t) r == eof_endings[i].length);
+ assert_se(streq_ptr(s, "foo"));
+
+ assert_se(read_line(f, (size_t) -1, NULL) == 0); /* Ensure we hit EOF */
+ }
+}
+
+static void test_read_nul_string(void) {
+ static const char test[] = "string nr. 1\0"
+ "string nr. 2\n\0"
+ "\377empty string follows\0"
+ "\0"
+ "final string\n is empty\0"
+ "\0";
+
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *s = NULL;
+
+ assert_se(f = fmemopen((void*) test, sizeof(test)-1, "r"));
+
+ assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 13 && streq_ptr(s, "string nr. 1"));
+ s = mfree(s);
+
+ assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 14 && streq_ptr(s, "string nr. 2\n"));
+ s = mfree(s);
+
+ assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 22 && streq_ptr(s, "\377empty string follows"));
+ s = mfree(s);
+
+ assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 1 && streq_ptr(s, ""));
+ s = mfree(s);
+
+ assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 23 && streq_ptr(s, "final string\n is empty"));
+ s = mfree(s);
+
+ assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 1 && streq_ptr(s, ""));
+ s = mfree(s);
+
+ assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 0 && streq_ptr(s, ""));
+}
+
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_parse_env_file();
test_parse_multiline_env_file();
@@ -730,9 +829,12 @@ int main(int argc, char *argv[]) {
test_search_and_fopen_nulstr();
test_writing_tmpfile();
test_tempfn();
+ test_fgetc();
test_read_line();
test_read_line2();
test_read_line3();
+ test_read_line4();
+ test_read_nul_string();
return 0;
}
diff --git a/src/test/test-firewall-util.c b/src/test/test-firewall-util.c
index 1b62590b49..1788e8d1ca 100644
--- a/src/test/test-firewall-util.c
+++ b/src/test/test-firewall-util.c
@@ -2,12 +2,13 @@
#include "firewall-util.h"
#include "log.h"
+#include "tests.h"
#define MAKE_IN_ADDR_UNION(a,b,c,d) (union in_addr_union) { .in.s_addr = htobe32((uint32_t) (a) << 24 | (uint32_t) (b) << 16 | (uint32_t) (c) << 8 | (uint32_t) (d))}
int main(int argc, char *argv[]) {
int r;
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
r = fw_add_masquerade(true, AF_INET, 0, NULL, 0, "foobar", NULL, 0);
if (r < 0)
diff --git a/src/test/test-format-table.c b/src/test/test-format-table.c
index adcc414161..5bede5c75b 100644
--- a/src/test/test-format-table.c
+++ b/src/test/test-format-table.c
@@ -5,6 +5,30 @@
#include "string-util.h"
#include "time-util.h"
+static void test_issue_9549(void) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ _cleanup_free_ char *formatted = NULL;
+
+ assert_se(table = table_new("name", "type", "ro", "usage", "created", "modified"));
+ assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(3), 100) >= 0);
+ assert_se(table_add_many(table,
+ TABLE_STRING, "foooo",
+ TABLE_STRING, "raw",
+ TABLE_BOOLEAN, false,
+ TABLE_SIZE, (uint64_t) (673.7*1024*1024),
+ TABLE_STRING, "Wed 2018-07-11 00:10:33 JST",
+ TABLE_STRING, "Wed 2018-07-11 00:16:00 JST") >= 0);
+
+ table_set_width(table, 75);
+ assert_se(table_format(table, &formatted) >= 0);
+
+ printf("%s\n", formatted);
+ assert_se(streq(formatted,
+ "NAME TYPE RO USAGE CREATED MODIFIED \n"
+ "foooo raw no 673.6M Wed 2018-07-11 00:10:33 J… Wed 2018-07-11 00:16:00 JST\n"
+ ));
+}
+
int main(int argc, char *argv[]) {
_cleanup_(table_unrefp) Table *t = NULL;
@@ -12,7 +36,7 @@ int main(int argc, char *argv[]) {
assert_se(setenv("COLUMNS", "40", 1) >= 0);
- assert_se(t = table_new("ONE", "TWO", "THREE"));
+ assert_se(t = table_new("one", "two", "three"));
assert_se(table_set_align_percent(t, TABLE_HEADER_CELL(2), 100) >= 0);
@@ -135,5 +159,7 @@ int main(int argc, char *argv[]) {
" yes xxx yes xxx xxx \n"
"5min 5min \n"));
+ test_issue_9549();
+
return 0;
}
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index fc650b513e..b3a4b1749c 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -4,8 +4,6 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "id128-util.h"
#include "macro.h"
@@ -15,22 +13,35 @@
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
+#include "virt.h"
+
+static const char *arg_test_dir = NULL;
static void test_chase_symlinks(void) {
_cleanup_free_ char *result = NULL;
- char temp[] = "/tmp/test-chase.XXXXXX";
+ char *temp;
const char *top, *p, *pslash, *q, *qslash;
+ struct stat st;
int r, pfd;
+ log_info("/* %s */", __func__);
+
+ temp = strjoina(arg_test_dir ?: "/tmp", "/test-chase.XXXXXX");
assert_se(mkdtemp(temp));
top = strjoina(temp, "/top");
assert_se(mkdir(top, 0700) >= 0);
p = strjoina(top, "/dot");
- assert_se(symlink(".", p) >= 0);
+ if (symlink(".", p) < 0) {
+ assert_se(IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM));
+ log_tests_skipped_errno(errno, "symlink() not possible");
+ goto cleanup;
+ };
p = strjoina(top, "/dotdot");
assert_se(symlink("..", p) >= 0);
@@ -237,11 +248,11 @@ static void test_chase_symlinks(void) {
assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
assert_se(chown(q, 0, 0) >= 0);
- assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM);
+ assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -ENOLINK);
assert_se(rmdir(q) >= 0);
assert_se(symlink("/etc/passwd", q) >= 0);
- assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM);
+ assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -ENOLINK);
assert_se(chown(p, 0, 0) >= 0);
assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
@@ -266,6 +277,30 @@ static void test_chase_symlinks(void) {
assert_se(sd_id128_equal(a, b));
}
+ /* Test CHASE_NOFOLLOW */
+
+ p = strjoina(temp, "/target");
+ q = strjoina(temp, "/symlink");
+ assert_se(symlink(p, q) >= 0);
+ pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
+ assert_se(pfd > 0);
+ assert_se(path_equal(result, q));
+ assert_se(fstat(pfd, &st) >= 0);
+ assert_se(S_ISLNK(st.st_mode));
+ result = mfree(result);
+
+ /* s1 -> s2 -> nonexistent */
+ q = strjoina(temp, "/s1");
+ assert_se(symlink("s2", q) >= 0);
+ p = strjoina(temp, "/s2");
+ assert_se(symlink("nonexistent", p) >= 0);
+ pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
+ assert_se(pfd > 0);
+ assert_se(path_equal(result, q));
+ assert_se(fstat(pfd, &st) >= 0);
+ assert_se(S_ISLNK(st.st_mode));
+ result = mfree(result);
+
/* Test CHASE_ONE */
p = strjoina(temp, "/start");
@@ -309,13 +344,17 @@ static void test_chase_symlinks(void) {
assert_se(streq("/usr", result));
result = mfree(result);
+ cleanup:
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
static void test_unlink_noerrno(void) {
- char name[] = "/tmp/test-close_nointr.XXXXXX";
+ char *name;
int fd;
+ log_info("/* %s */", __func__);
+
+ name = strjoina(arg_test_dir ?: "/tmp", "/test-close_nointr.XXXXXX");
fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(close_nointr(fd) >= 0);
@@ -331,32 +370,37 @@ static void test_unlink_noerrno(void) {
}
static void test_readlink_and_make_absolute(void) {
- char tempdir[] = "/tmp/test-readlink_and_make_absolute";
- char name[] = "/tmp/test-readlink_and_make_absolute/original";
- char name2[] = "test-readlink_and_make_absolute/original";
- char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias";
- char *r = NULL;
- _cleanup_free_ char *pwd = NULL;
+ const char *tempdir, *name, *name2, *name_alias;
+ _cleanup_free_ char *r1 = NULL, *r2 = NULL, *pwd = NULL;
+
+ log_info("/* %s */", __func__);
+
+ tempdir = strjoina(arg_test_dir ?: "/tmp", "/test-readlink_and_make_absolute");
+ name = strjoina(tempdir, "/original");
+ name2 = "test-readlink_and_make_absolute/original";
+ name_alias = strjoina(arg_test_dir ?: "/tmp", "/test-readlink_and_make_absolute-alias");
assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), MKDIR_WARN_MODE) >= 0);
assert_se(touch(name) >= 0);
- assert_se(symlink(name, name_alias) >= 0);
- assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
- assert_se(streq(r, name));
- free(r);
- assert_se(unlink(name_alias) >= 0);
+ if (symlink(name, name_alias) < 0) {
+ assert_se(IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM));
+ log_tests_skipped_errno(errno, "symlink() not possible");
+ } else {
+ assert_se(readlink_and_make_absolute(name_alias, &r1) >= 0);
+ assert_se(streq(r1, name));
+ assert_se(unlink(name_alias) >= 0);
- assert_se(safe_getcwd(&pwd) >= 0);
+ assert_se(safe_getcwd(&pwd) >= 0);
- assert_se(chdir(tempdir) >= 0);
- assert_se(symlink(name2, name_alias) >= 0);
- assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
- assert_se(streq(r, name));
- free(r);
- assert_se(unlink(name_alias) >= 0);
+ assert_se(chdir(tempdir) >= 0);
+ assert_se(symlink(name2, name_alias) >= 0);
+ assert_se(readlink_and_make_absolute(name_alias, &r2) >= 0);
+ assert_se(streq(r2, name));
+ assert_se(unlink(name_alias) >= 0);
- assert_se(chdir(pwd) >= 0);
+ assert_se(chdir(pwd) >= 0);
+ }
assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
@@ -364,7 +408,7 @@ static void test_readlink_and_make_absolute(void) {
static void test_get_files_in_directory(void) {
_cleanup_strv_free_ char **l = NULL, **t = NULL;
- assert_se(get_files_in_directory("/tmp", &l) >= 0);
+ assert_se(get_files_in_directory(arg_test_dir ?: "/tmp", &l) >= 0);
assert_se(get_files_in_directory(".", &t) >= 0);
assert_se(get_files_in_directory(".", NULL) >= 0);
}
@@ -373,6 +417,8 @@ static void test_var_tmp(void) {
_cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
const char *tmp_dir = NULL, *t;
+ log_info("/* %s */", __func__);
+
t = getenv("TMPDIR");
if (t) {
tmpdir_backup = strdup(t);
@@ -427,6 +473,8 @@ static void test_var_tmp(void) {
}
static void test_dot_or_dot_dot(void) {
+ log_info("/* %s */", __func__);
+
assert_se(!dot_or_dot_dot(NULL));
assert_se(!dot_or_dot_dot(""));
assert_se(!dot_or_dot_dot("xxx"));
@@ -439,8 +487,12 @@ static void test_dot_or_dot_dot(void) {
static void test_access_fd(void) {
_cleanup_(rmdir_and_freep) char *p = NULL;
_cleanup_close_ int fd = -1;
+ const char *a;
+
+ log_info("/* %s */", __func__);
- assert_se(mkdtemp_malloc("/tmp/access-fd.XXXXXX", &p) >= 0);
+ a = strjoina(arg_test_dir ?: "/tmp", "/access-fd.XXXXXX");
+ assert_se(mkdtemp_malloc(a, &p) >= 0);
fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
assert_se(fd >= 0);
@@ -468,16 +520,26 @@ static void test_touch_file(void) {
struct stat st;
const char *a;
usec_t test_mtime;
+ int r;
+
+ log_info("/* %s */", __func__);
test_uid = geteuid() == 0 ? 65534 : getuid();
test_gid = geteuid() == 0 ? 65534 : getgid();
test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK);
- assert_se(mkdtemp_malloc("/dev/shm/touch-file-XXXXXX", &p) >= 0);
+ a = strjoina(arg_test_dir ?: "/dev/shm", "/touch-file-XXXXXX");
+ assert_se(mkdtemp_malloc(a, &p) >= 0);
a = strjoina(p, "/regular");
- assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
+ r = touch_file(a, false, test_mtime, test_uid, test_gid, 0640);
+ if (r < 0) {
+ assert_se(IN_SET(r, -EINVAL, -ENOSYS, -ENOTTY, -EPERM));
+ log_tests_skipped_errno(errno, "touch_file() not possible");
+ return;
+ }
+
assert_se(lstat(a, &st) >= 0);
assert_se(st.st_uid == test_uid);
assert_se(st.st_gid == test_gid);
@@ -517,7 +579,12 @@ static void test_touch_file(void) {
if (geteuid() == 0) {
a = strjoina(p, "/cdev");
- assert_se(mknod(a, 0775 | S_IFCHR, makedev(0, 0)) >= 0);
+ r = mknod(a, 0775 | S_IFCHR, makedev(0, 0));
+ if (r < 0 && errno == EPERM && detect_container() > 0) {
+ log_notice("Running in unprivileged container? Skipping remaining tests in %s", __func__);
+ return;
+ }
+ assert_se(r >= 0);
assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
assert_se(lstat(a, &st) >= 0);
assert_se(st.st_uid == test_uid);
@@ -553,7 +620,9 @@ static void test_unlinkat_deallocate(void) {
_cleanup_close_ int fd = -1;
struct stat st;
- assert_se(tempfn_random_child(NULL, "unlink-deallocation", &p) >= 0);
+ log_info("/* %s */", __func__);
+
+ assert_se(tempfn_random_child(arg_test_dir, "unlink-deallocation", &p) >= 0);
fd = open(p, O_WRONLY|O_CLOEXEC|O_CREAT|O_EXCL, 0600);
assert_se(fd >= 0);
@@ -568,7 +637,8 @@ static void test_unlinkat_deallocate(void) {
assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0);
assert_se(fstat(fd, &st) >= 0);
- assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6 (it worked) or 0 (we had to resort to truncation) */
+ assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6
+ (it worked) or 0 (we had to resort to truncation) */
assert_se(st.st_blocks == 0);
assert_se(st.st_nlink == 0);
}
@@ -576,13 +646,87 @@ static void test_unlinkat_deallocate(void) {
static void test_fsync_directory_of_file(void) {
_cleanup_close_ int fd = -1;
- fd = open_tmpfile_unlinkable(NULL, O_RDWR);
+ log_info("/* %s */", __func__);
+
+ fd = open_tmpfile_unlinkable(arg_test_dir, O_RDWR);
assert_se(fd >= 0);
assert_se(fsync_directory_of_file(fd) >= 0);
}
+static void test_rename_noreplace(void) {
+ static const char* const table[] = {
+ "/reg",
+ "/dir",
+ "/fifo",
+ "/socket",
+ "/symlink",
+ NULL
+ };
+
+ _cleanup_(rm_rf_physical_and_freep) char *z = NULL;
+ const char *j = NULL;
+ char **a, **b;
+
+ log_info("/* %s */", __func__);
+
+ if (arg_test_dir)
+ j = strjoina(arg_test_dir, "/testXXXXXX");
+ assert_se(mkdtemp_malloc(j, &z) >= 0);
+
+ j = strjoina(z, table[0]);
+ assert_se(touch(j) >= 0);
+
+ j = strjoina(z, table[1]);
+ assert_se(mkdir(j, 0777) >= 0);
+
+ j = strjoina(z, table[2]);
+ (void) mkfifo(j, 0777);
+
+ j = strjoina(z, table[3]);
+ (void) mknod(j, S_IFSOCK | 0777, 0);
+
+ j = strjoina(z, table[4]);
+ (void) symlink("foobar", j);
+
+ STRV_FOREACH(a, (char**) table) {
+ _cleanup_free_ char *x = NULL, *y = NULL;
+
+ x = strjoin(z, *a);
+ assert_se(x);
+
+ if (access(x, F_OK) < 0) {
+ assert_se(errno == ENOENT);
+ continue;
+ }
+
+ STRV_FOREACH(b, (char**) table) {
+ _cleanup_free_ char *w = NULL;
+
+ w = strjoin(w, *b);
+ assert_se(w);
+
+ if (access(w, F_OK) < 0) {
+ assert_se(errno == ENOENT);
+ continue;
+ }
+
+ assert_se(rename_noreplace(AT_FDCWD, w, AT_FDCWD, y) == -EEXIST);
+ }
+
+ y = strjoin(z, "/somethingelse");
+ assert_se(y);
+
+ assert_se(rename_noreplace(AT_FDCWD, x, AT_FDCWD, y) >= 0);
+ assert_se(rename_noreplace(AT_FDCWD, y, AT_FDCWD, x) >= 0);
+ }
+}
+
int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_INFO);
+
+ arg_test_dir = argv[1];
+
test_unlink_noerrno();
test_get_files_in_directory();
test_readlink_and_make_absolute();
@@ -593,6 +737,7 @@ int main(int argc, char *argv[]) {
test_touch_file();
test_unlinkat_deallocate();
test_fsync_directory_of_file();
+ test_rename_noreplace();
return 0;
}
diff --git a/src/test/test-glob-util.c b/src/test/test-glob-util.c
index d78d6223c0..b4f41445fe 100644
--- a/src/test/test-glob-util.c
+++ b/src/test/test-glob-util.c
@@ -7,11 +7,11 @@
#include "alloc-util.h"
#include "dirent-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "glob-util.h"
#include "macro.h"
#include "rm-rf.h"
+#include "tmpfile-util.h"
static void test_glob_exists(void) {
char name[] = "/tmp/test-glob_exists.XXXXXX";
diff --git a/src/test/test-hash.c b/src/test/test-hash.c
index f5bc131846..44d1044bf3 100644
--- a/src/test/test-hash.c
+++ b/src/test/test-hash.c
@@ -7,23 +7,22 @@
#include "log.h"
#include "string-util.h"
#include "khash.h"
+#include "tests.h"
int main(int argc, char *argv[]) {
_cleanup_(khash_unrefp) khash *h = NULL, *copy = NULL;
_cleanup_free_ char *s = NULL;
int r;
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(khash_new(&h, NULL) == -EINVAL);
assert_se(khash_new(&h, "") == -EINVAL);
r = khash_supported();
assert_se(r >= 0);
- if (r == 0) {
- puts("khash not supported on this kernel, skipping");
- return EXIT_TEST_SKIP;
- }
+ if (r == 0)
+ return log_tests_skipped("khash not supported on this kernel");
assert_se(khash_new(&h, "foobar") == -EOPNOTSUPP); /* undefined hash function */
diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c
index b695d4ee35..5376aa84c4 100644
--- a/src/test/test-hashmap-plain.c
+++ b/src/test/test-hashmap-plain.c
@@ -1,25 +1,21 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2013 Daniel Buch
-***/
#include "alloc-util.h"
-#include "env-util.h"
#include "hashmap.h"
#include "log.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
#include "util.h"
-static bool arg_slow = false;
-
void test_hashmap_funcs(void);
static void test_hashmap_replace(void) {
Hashmap *m;
char *val1, *val2, *val3, *val4, *val5, *r;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
@@ -59,7 +55,7 @@ static void test_hashmap_copy(void) {
Hashmap *m, *copy;
char *val1, *val2, *val3, *val4, *r;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val1 = strdup("val1");
assert_se(val1);
@@ -97,7 +93,7 @@ static void test_hashmap_get_strv(void) {
char **strv;
char *val1, *val2, *val3, *val4;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val1 = strdup("val1");
assert_se(val1);
@@ -135,7 +131,7 @@ static void test_hashmap_move_one(void) {
Hashmap *m, *n;
char *val1, *val2, *val3, *val4, *r;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val1 = strdup("val1");
assert_se(val1);
@@ -176,7 +172,7 @@ static void test_hashmap_move(void) {
Hashmap *m, *n;
char *val1, *val2, *val3, *val4, *r;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val1 = strdup("val1");
assert_se(val1);
@@ -220,7 +216,7 @@ static void test_hashmap_update(void) {
Hashmap *m;
char *val1, *val2, *r;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
val1 = strdup("old_value");
@@ -252,7 +248,7 @@ static void test_hashmap_put(void) {
void *val2 = (void*) "val 2";
_cleanup_free_ char* key1 = NULL;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
assert_se(hashmap_ensure_allocated(&m, &string_hash_ops) >= 0);
assert_se(m);
@@ -272,7 +268,7 @@ static void test_hashmap_remove(void) {
_cleanup_hashmap_free_ Hashmap *m = NULL;
char *r;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
r = hashmap_remove(NULL, "key 1");
assert_se(r == NULL);
@@ -302,7 +298,7 @@ static void test_hashmap_remove2(void) {
char val2[] = "val 2";
void *r, *r2;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
r = hashmap_remove2(NULL, "key 1", &r2);
assert_se(r == NULL);
@@ -334,7 +330,7 @@ static void test_hashmap_remove_value(void) {
char val1[] = "val 1";
char val2[] = "val 2";
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
r = hashmap_remove_value(NULL, "key 1", val1);
assert_se(r == NULL);
@@ -368,7 +364,7 @@ static void test_hashmap_remove_and_put(void) {
int valid;
char *r;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
assert_se(m);
@@ -404,7 +400,7 @@ static void test_hashmap_remove_and_replace(void) {
void *r;
int i, j;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&trivial_hash_ops);
assert_se(m);
@@ -457,7 +453,7 @@ static void test_hashmap_ensure_allocated(void) {
Hashmap *m;
int valid_hashmap;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
@@ -480,7 +476,7 @@ static void test_hashmap_foreach_key(void) {
"key 3\0"
"key 4\0";
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
@@ -512,7 +508,7 @@ static void test_hashmap_foreach(void) {
char *val1, *val2, *val3, *val4, *s;
unsigned count;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val1 = strdup("my val1");
assert_se(val1);
@@ -564,7 +560,7 @@ static void test_hashmap_merge(void) {
Hashmap *n;
char *val1, *val2, *val3, *val4, *r;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val1 = strdup("my val1");
assert_se(val1);
@@ -599,7 +595,7 @@ static void test_hashmap_contains(void) {
Hashmap *m;
char *val1;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val1 = strdup("my val");
assert_se(val1);
@@ -621,7 +617,7 @@ static void test_hashmap_isempty(void) {
Hashmap *m;
char *val1;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val1 = strdup("my val");
assert_se(val1);
@@ -640,7 +636,7 @@ static void test_hashmap_size(void) {
Hashmap *m;
char *val1, *val2, *val3, *val4;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val1 = strdup("my val");
assert_se(val1);
@@ -672,7 +668,7 @@ static void test_hashmap_get(void) {
char *r;
char *val;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val = strdup("my val");
assert_se(val);
@@ -701,7 +697,7 @@ static void test_hashmap_get2(void) {
char key_orig[] = "Key 1";
void *key_copy;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
val = strdup("my val");
assert_se(val);
@@ -742,17 +738,22 @@ static void test_hashmap_many(void) {
Hashmap *h;
unsigned i, j;
void *v, *k;
+ bool slow = slow_tests_enabled();
const struct {
+ const char *title;
const struct hash_ops *ops;
unsigned n_entries;
} tests[] = {
- { .ops = NULL, .n_entries = arg_slow ? 1 << 20 : 240 },
- { .ops = &crippled_hashmap_ops, .n_entries = arg_slow ? 1 << 14 : 140 },
+ { "trivial_hashmap_ops", NULL, slow ? 1 << 20 : 240 },
+ { "crippled_hashmap_ops", &crippled_hashmap_ops, slow ? 1 << 14 : 140 },
};
- log_info("%s (%s)", __func__, arg_slow ? "slow" : "fast");
+ log_info("/* %s (%s) */", __func__, slow ? "slow" : "fast");
for (j = 0; j < ELEMENTSOF(tests); j++) {
+ usec_t ts = now(CLOCK_MONOTONIC), n;
+ char b[FORMAT_TIMESPAN_MAX];
+
assert_se(h = hashmap_new(tests[j].ops));
for (i = 1; i < tests[j].n_entries*3; i+=3) {
@@ -763,7 +764,8 @@ static void test_hashmap_many(void) {
for (i = 1; i < tests[j].n_entries*3; i++)
assert_se(hashmap_contains(h, UINT_TO_PTR(i)) == (i % 3 == 1));
- log_info("%u <= %u * 0.8 = %g", hashmap_size(h), hashmap_buckets(h), hashmap_buckets(h) * 0.8);
+ log_info("%s %u <= %u * 0.8 = %g",
+ tests[j].title, hashmap_size(h), hashmap_buckets(h), hashmap_buckets(h) * 0.8);
assert_se(hashmap_size(h) <= hashmap_buckets(h) * 0.8);
assert_se(hashmap_size(h) == tests[j].n_entries);
@@ -775,13 +777,63 @@ static void test_hashmap_many(void) {
}
hashmap_free(h);
+
+ n = now(CLOCK_MONOTONIC);
+ log_info("test took %s", format_timespan(b, sizeof b, n - ts, 0));
+ }
+}
+
+extern unsigned custom_counter;
+extern const struct hash_ops boring_hash_ops, custom_hash_ops;
+
+static void test_hashmap_free(void) {
+ Hashmap *h;
+ bool slow = slow_tests_enabled();
+ usec_t ts, n;
+ char b[FORMAT_TIMESPAN_MAX];
+ unsigned n_entries = slow ? 1 << 20 : 240;
+
+ const struct {
+ const char *title;
+ const struct hash_ops *ops;
+ unsigned expect_counter;
+ } tests[] = {
+ { "string_hash_ops", &boring_hash_ops, 2 * n_entries},
+ { "custom_free_hash_ops", &custom_hash_ops, 0 },
+ };
+
+ log_info("/* %s (%s, %u entries) */", __func__, slow ? "slow" : "fast", n_entries);
+
+ for (unsigned j = 0; j < ELEMENTSOF(tests); j++) {
+ ts = now(CLOCK_MONOTONIC);
+ assert_se(h = hashmap_new(tests[j].ops));
+
+ custom_counter = 0;
+ for (unsigned i = 0; i < n_entries; i++) {
+ char s[DECIMAL_STR_MAX(unsigned)];
+ char *k, *v;
+
+ xsprintf(s, "%u", i);
+ assert_se(k = strdup(s));
+ assert_se(v = strdup(s));
+ custom_counter += 2;
+
+ assert_se(hashmap_put(h, k, v) >= 0);
+ }
+
+ hashmap_free(h);
+
+ n = now(CLOCK_MONOTONIC);
+ log_info("%s test took %s", tests[j].title, format_timespan(b, sizeof b, n - ts, 0));
+
+ assert_se(custom_counter == tests[j].expect_counter);
}
}
static void test_hashmap_first(void) {
_cleanup_hashmap_free_ Hashmap *m = NULL;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
assert_se(m);
@@ -800,7 +852,7 @@ static void test_hashmap_first(void) {
static void test_hashmap_first_key(void) {
_cleanup_hashmap_free_ Hashmap *m = NULL;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
assert_se(m);
@@ -819,7 +871,7 @@ static void test_hashmap_first_key(void) {
static void test_hashmap_steal_first_key(void) {
_cleanup_hashmap_free_ Hashmap *m = NULL;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
assert_se(m);
@@ -836,7 +888,7 @@ static void test_hashmap_steal_first(void) {
int seen[3] = {};
char *val;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
assert_se(m);
@@ -856,7 +908,7 @@ static void test_hashmap_steal_first(void) {
static void test_hashmap_clear_free_free(void) {
_cleanup_hashmap_free_ Hashmap *m = NULL;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
assert_se(m);
@@ -867,12 +919,49 @@ static void test_hashmap_clear_free_free(void) {
hashmap_clear_free_free(m);
assert_se(hashmap_isempty(m));
+
+ assert_se(hashmap_put(m, strdup("key 1"), strdup("value 1")) == 1);
+ assert_se(hashmap_put(m, strdup("key 2"), strdup("value 2")) == 1);
+ assert_se(hashmap_put(m, strdup("key 3"), strdup("value 3")) == 1);
+
+ hashmap_clear_free_free(m);
+ assert_se(hashmap_isempty(m));
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(test_hash_ops_key, char, string_hash_func, string_compare_func, free);
+DEFINE_PRIVATE_HASH_OPS_FULL(test_hash_ops_full, char, string_hash_func, string_compare_func, free, char, free);
+
+static void test_hashmap_clear_free_with_destructor(void) {
+ _cleanup_hashmap_free_ Hashmap *m = NULL;
+
+ log_info("/* %s */", __func__);
+
+ m = hashmap_new(&test_hash_ops_key);
+ assert_se(m);
+
+ assert_se(hashmap_put(m, strdup("key 1"), NULL) == 1);
+ assert_se(hashmap_put(m, strdup("key 2"), NULL) == 1);
+ assert_se(hashmap_put(m, strdup("key 3"), NULL) == 1);
+
+ hashmap_clear_free(m);
+ assert_se(hashmap_isempty(m));
+ m = hashmap_free(m);
+
+ m = hashmap_new(&test_hash_ops_full);
+ assert_se(m);
+
+ assert_se(hashmap_put(m, strdup("key 1"), strdup("value 1")) == 1);
+ assert_se(hashmap_put(m, strdup("key 2"), strdup("value 2")) == 1);
+ assert_se(hashmap_put(m, strdup("key 3"), strdup("value 3")) == 1);
+
+ hashmap_clear_free(m);
+ assert_se(hashmap_isempty(m));
}
static void test_hashmap_reserve(void) {
_cleanup_hashmap_free_ Hashmap *m = NULL;
- log_info("%s", __func__);
+ log_info("/* %s */", __func__);
m = hashmap_new(&string_hash_ops);
@@ -889,14 +978,9 @@ static void test_hashmap_reserve(void) {
}
void test_hashmap_funcs(void) {
- int r;
-
log_parse_environment();
log_open();
- r = getenv_bool("SYSTEMD_SLOW_TESTS");
- arg_slow = r >= 0 ? r : SYSTEMD_SLOW_TESTS_DEFAULT;
-
test_hashmap_copy();
test_hashmap_get_strv();
test_hashmap_move_one();
@@ -919,10 +1003,12 @@ void test_hashmap_funcs(void) {
test_hashmap_get2();
test_hashmap_size();
test_hashmap_many();
+ test_hashmap_free();
test_hashmap_first();
test_hashmap_first_key();
test_hashmap_steal_first_key();
test_hashmap_steal_first();
test_hashmap_clear_free_free();
+ test_hashmap_clear_free_with_destructor();
test_hashmap_reserve();
}
diff --git a/src/test/test-hashmap.c b/src/test/test-hashmap.c
index b319fa6ba9..ee4c0e66db 100644
--- a/src/test/test-hashmap.c
+++ b/src/test/test-hashmap.c
@@ -1,11 +1,17 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2013 Daniel Buch
-***/
#include "hashmap.h"
#include "util.h"
+unsigned custom_counter = 0;
+static void custom_destruct(void* p) {
+ custom_counter--;
+ free(p);
+}
+
+DEFINE_HASH_OPS_FULL(boring_hash_ops, char, string_hash_func, string_compare_func, free, char, free);
+DEFINE_HASH_OPS_FULL(custom_hash_ops, char, string_hash_func, string_compare_func, custom_destruct, char, custom_destruct);
+
void test_hashmap_funcs(void);
void test_ordered_hashmap_funcs(void);
@@ -13,6 +19,8 @@ static void test_ordered_hashmap_next(void) {
_cleanup_ordered_hashmap_free_ OrderedHashmap *m = NULL;
int i;
+ log_info("/* %s */", __func__);
+
assert_se(m = ordered_hashmap_new(NULL));
for (i = -2; i <= 2; i++)
assert_se(ordered_hashmap_put(m, INT_TO_PTR(i), INT_TO_PTR(i+10)) == 1);
@@ -35,6 +43,8 @@ static void test_hashmap_free_with_destructor(void) {
struct Item items[4] = {};
unsigned i;
+ log_info("/* %s */", __func__);
+
assert_se(m = hashmap_new(NULL));
for (i = 0; i < ELEMENTSOF(items) - 1; i++)
assert_se(hashmap_put(m, INT_TO_PTR(i), items + i) == 1);
@@ -90,6 +100,8 @@ static void test_iterated_cache(void) {
Hashmap *m;
IteratedCache *c;
+ log_info("/* %s */", __func__);
+
assert_se(m = hashmap_new(NULL));
assert_se(c = hashmap_iterated_cache_new(m));
compare_cache(m, c);
@@ -125,6 +137,8 @@ static void test_iterated_cache(void) {
static void test_path_hashmap(void) {
_cleanup_hashmap_free_ Hashmap *h = NULL;
+ log_info("/* %s */", __func__);
+
assert_se(h = hashmap_new(&path_hash_ops));
assert_se(hashmap_put(h, "foo", INT_TO_PTR(1)) >= 0);
diff --git a/src/test/test-helper.c b/src/test/test-helper.c
index 9cf9972380..5b79d12f07 100644
--- a/src/test/test-helper.c
+++ b/src/test/test-helper.c
@@ -4,6 +4,7 @@
#include "random-util.h"
#include "alloc-util.h"
#include "cgroup-util.h"
+#include "string-util.h"
int enter_cgroup_subroot(void) {
_cleanup_free_ char *cgroup_root = NULL, *cgroup_subroot = NULL;
@@ -27,3 +28,8 @@ int enter_cgroup_subroot(void) {
return cg_attach_everywhere(supported, cgroup_subroot, 0, NULL, NULL);
}
+
+/* https://docs.travis-ci.com/user/environment-variables#default-environment-variables */
+bool is_run_on_travis_ci(void) {
+ return streq_ptr(getenv("TRAVIS"), "true");
+}
diff --git a/src/test/test-helper.h b/src/test/test-helper.h
index 3e8ccd9049..77af40d555 100644
--- a/src/test/test-helper.h
+++ b/src/test/test-helper.h
@@ -27,3 +27,5 @@
)
int enter_cgroup_subroot(void);
+
+bool is_run_on_travis_ci(void);
diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c
index da9f3008bb..6e9b94b933 100644
--- a/src/test/test-hexdecoct.c
+++ b/src/test/test-hexdecoct.c
@@ -84,7 +84,7 @@ static void test_unhexmem_one(const char *s, size_t l, int retval) {
l = strlen(s);
assert_se(hex = hexmem(mem, len));
- answer = strndupa(s, l);
+ answer = strndupa(strempty(s), l);
assert_se(streq(delete_chars(answer, WHITESPACE), hex));
}
}
diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c
index 194a640eee..4126a24ceb 100644
--- a/src/test/test-hostname-util.c
+++ b/src/test/test-hostname-util.c
@@ -4,6 +4,7 @@
#include "fileio.h"
#include "hostname-util.h"
#include "string-util.h"
+#include "tmpfile-util.h"
#include "util.h"
static void test_hostname_is_valid(void) {
@@ -52,6 +53,12 @@ static void test_hostname_cleanup(void) {
assert_se(streq(hostname_cleanup(s), "foobar.com"));
s = strdupa("foobar.com.");
assert_se(streq(hostname_cleanup(s), "foobar.com"));
+ s = strdupa("foo-bar.-com-.");
+ assert_se(streq(hostname_cleanup(s), "foo-bar.com"));
+ s = strdupa("foo-bar-.-com-.");
+ assert_se(streq(hostname_cleanup(s), "foo-bar--com"));
+ s = strdupa("--foo-bar.-com");
+ assert_se(streq(hostname_cleanup(s), "foo-bar.com"));
s = strdupa("fooBAR");
assert_se(streq(hostname_cleanup(s), "fooBAR"));
s = strdupa("fooBAR.com");
@@ -78,6 +85,8 @@ static void test_hostname_cleanup(void) {
assert_se(streq(hostname_cleanup(s), "foo.bar"));
s = strdupa("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
assert_se(streq(hostname_cleanup(s), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
+ s = strdupa("xxxx........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+ assert_se(streq(hostname_cleanup(s), "xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
}
static void test_read_etc_hostname(void) {
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
index e3d07a69f3..ec50e057a3 100644
--- a/src/test/test-id128.c
+++ b/src/test/test-id128.c
@@ -7,10 +7,10 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "id128-util.h"
#include "macro.h"
#include "string-util.h"
+#include "tmpfile-util.h"
#include "util.h"
#define ID128_WALDI SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10)
diff --git a/src/test/test-in-addr-util.c b/src/test/test-in-addr-util.c
index 5b6e87bf5a..75c3e305c3 100644
--- a/src/test/test-in-addr-util.c
+++ b/src/test/test-in-addr-util.c
@@ -4,12 +4,12 @@
#include "in-addr-util.h"
-static void test_in_addr_prefix_from_string(const char *p, int family, int ret, const union in_addr_union *u, unsigned char prefixlen) {
+static void test_in_addr_prefix_from_string(const char *p, int family, int ret, const union in_addr_union *u, unsigned char prefixlen, bool use_default) {
union in_addr_union q;
unsigned char l;
int r;
- r = in_addr_prefix_from_string(p, family, &q, &l);
+ r = in_addr_prefix_from_string_internal(p, use_default, family, &q, &l);
assert_se(r == ret);
if (r >= 0) {
@@ -18,7 +18,7 @@ static void test_in_addr_prefix_from_string(const char *p, int family, int ret,
assert_se(in_addr_equal(family, &q, u));
assert_se(l == prefixlen);
- r = in_addr_prefix_from_string_auto(p, &f, &q, &l);
+ r = in_addr_prefix_from_string_auto_internal(p, use_default, &f, &q, &l);
assert_se(r >= 0);
assert_se(f == family);
@@ -28,31 +28,57 @@ static void test_in_addr_prefix_from_string(const char *p, int family, int ret,
}
int main(int argc, char *argv[]) {
- test_in_addr_prefix_from_string("", AF_INET, -EINVAL, NULL, 0);
- test_in_addr_prefix_from_string("/", AF_INET, -EINVAL, NULL, 0);
- test_in_addr_prefix_from_string("/8", AF_INET, -EINVAL, NULL, 0);
- test_in_addr_prefix_from_string("1.2.3.4", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32);
- test_in_addr_prefix_from_string("1.2.3.4/0", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 0);
- test_in_addr_prefix_from_string("1.2.3.4/1", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 1);
- test_in_addr_prefix_from_string("1.2.3.4/2", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 2);
- test_in_addr_prefix_from_string("1.2.3.4/32", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32);
- test_in_addr_prefix_from_string("1.2.3.4/33", AF_INET, -ERANGE, NULL, 0);
- test_in_addr_prefix_from_string("1.2.3.4/-1", AF_INET, -ERANGE, NULL, 0);
- test_in_addr_prefix_from_string("::1", AF_INET, -EINVAL, NULL, 0);
-
- test_in_addr_prefix_from_string("", AF_INET6, -EINVAL, NULL, 0);
- test_in_addr_prefix_from_string("/", AF_INET6, -EINVAL, NULL, 0);
- test_in_addr_prefix_from_string("/8", AF_INET6, -EINVAL, NULL, 0);
- test_in_addr_prefix_from_string("::1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128);
- test_in_addr_prefix_from_string("::1/0", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0);
- test_in_addr_prefix_from_string("::1/1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 1);
- test_in_addr_prefix_from_string("::1/2", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 2);
- test_in_addr_prefix_from_string("::1/32", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 32);
- test_in_addr_prefix_from_string("::1/33", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 33);
- test_in_addr_prefix_from_string("::1/64", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 64);
- test_in_addr_prefix_from_string("::1/128", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128);
- test_in_addr_prefix_from_string("::1/129", AF_INET6, -ERANGE, NULL, 0);
- test_in_addr_prefix_from_string("::1/-1", AF_INET6, -ERANGE, NULL, 0);
+ test_in_addr_prefix_from_string("", AF_INET, -EINVAL, NULL, 0, false);
+ test_in_addr_prefix_from_string("/", AF_INET, -EINVAL, NULL, 0, false);
+ test_in_addr_prefix_from_string("/8", AF_INET, -EINVAL, NULL, 0, false);
+ test_in_addr_prefix_from_string("1.2.3.4", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32, false);
+ test_in_addr_prefix_from_string("1.2.3.4/0", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 0, false);
+ test_in_addr_prefix_from_string("1.2.3.4/1", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 1, false);
+ test_in_addr_prefix_from_string("1.2.3.4/2", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 2, false);
+ test_in_addr_prefix_from_string("1.2.3.4/32", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32, false);
+ test_in_addr_prefix_from_string("1.2.3.4/33", AF_INET, -ERANGE, NULL, 0, false);
+ test_in_addr_prefix_from_string("1.2.3.4/-1", AF_INET, -ERANGE, NULL, 0, false);
+ test_in_addr_prefix_from_string("::1", AF_INET, -EINVAL, NULL, 0, false);
+
+ test_in_addr_prefix_from_string("", AF_INET6, -EINVAL, NULL, 0, false);
+ test_in_addr_prefix_from_string("/", AF_INET6, -EINVAL, NULL, 0, false);
+ test_in_addr_prefix_from_string("/8", AF_INET6, -EINVAL, NULL, 0, false);
+ test_in_addr_prefix_from_string("::1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, false);
+ test_in_addr_prefix_from_string("::1/0", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0, false);
+ test_in_addr_prefix_from_string("::1/1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 1, false);
+ test_in_addr_prefix_from_string("::1/2", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 2, false);
+ test_in_addr_prefix_from_string("::1/32", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 32, false);
+ test_in_addr_prefix_from_string("::1/33", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 33, false);
+ test_in_addr_prefix_from_string("::1/64", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 64, false);
+ test_in_addr_prefix_from_string("::1/128", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, false);
+ test_in_addr_prefix_from_string("::1/129", AF_INET6, -ERANGE, NULL, 0, false);
+ test_in_addr_prefix_from_string("::1/-1", AF_INET6, -ERANGE, NULL, 0, false);
+
+ test_in_addr_prefix_from_string("", AF_INET, -EINVAL, NULL, 0, true);
+ test_in_addr_prefix_from_string("/", AF_INET, -EINVAL, NULL, 0, true);
+ test_in_addr_prefix_from_string("/8", AF_INET, -EINVAL, NULL, 0, true);
+ test_in_addr_prefix_from_string("1.2.3.4", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 8, true);
+ test_in_addr_prefix_from_string("1.2.3.4/0", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 0, true);
+ test_in_addr_prefix_from_string("1.2.3.4/1", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 1, true);
+ test_in_addr_prefix_from_string("1.2.3.4/2", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 2, true);
+ test_in_addr_prefix_from_string("1.2.3.4/32", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32, true);
+ test_in_addr_prefix_from_string("1.2.3.4/33", AF_INET, -ERANGE, NULL, 0, true);
+ test_in_addr_prefix_from_string("1.2.3.4/-1", AF_INET, -ERANGE, NULL, 0, true);
+ test_in_addr_prefix_from_string("::1", AF_INET, -EINVAL, NULL, 0, true);
+
+ test_in_addr_prefix_from_string("", AF_INET6, -EINVAL, NULL, 0, true);
+ test_in_addr_prefix_from_string("/", AF_INET6, -EINVAL, NULL, 0, true);
+ test_in_addr_prefix_from_string("/8", AF_INET6, -EINVAL, NULL, 0, true);
+ test_in_addr_prefix_from_string("::1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0, true);
+ test_in_addr_prefix_from_string("::1/0", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0, true);
+ test_in_addr_prefix_from_string("::1/1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 1, true);
+ test_in_addr_prefix_from_string("::1/2", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 2, true);
+ test_in_addr_prefix_from_string("::1/32", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 32, true);
+ test_in_addr_prefix_from_string("::1/33", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 33, true);
+ test_in_addr_prefix_from_string("::1/64", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 64, true);
+ test_in_addr_prefix_from_string("::1/128", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, true);
+ test_in_addr_prefix_from_string("::1/129", AF_INET6, -ERANGE, NULL, 0, true);
+ test_in_addr_prefix_from_string("::1/-1", AF_INET6, -ERANGE, NULL, 0, true);
return 0;
}
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
index 15dd3c6966..73ea68f372 100644
--- a/src/test/test-install-root.c
+++ b/src/test/test-install-root.c
@@ -7,6 +7,7 @@
#include "rm-rf.h"
#include "special.h"
#include "string-util.h"
+#include "tests.h"
static void test_basic_mask_and_enable(const char *root) {
const char *p;
@@ -14,7 +15,7 @@ static void test_basic_mask_and_enable(const char *root) {
UnitFileChange *changes = NULL;
size_t n_changes = 0;
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT);
@@ -861,7 +862,7 @@ static void test_with_dropin(const char *root) {
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
- assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-4a.service"), &changes, &n_changes) == 1);
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-4a.service"), &changes, &n_changes) == 2);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(n_changes == 2);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
@@ -983,6 +984,63 @@ static void test_with_dropin_template(const char *root) {
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
}
+static void test_preset_multiple_instances(const char *root) {
+ UnitFileChange *changes = NULL;
+ size_t n_changes = 0;
+ const char *p;
+ UnitFileState state;
+
+ /* Set up template service files and preset file */
+ p = strjoina(root, "/usr/lib/systemd/system/foo@.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "DefaultInstance=def\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
+ assert_se(write_string_file(p,
+ "enable foo@.service bar0 bar1 bartest\n"
+ "enable emptylist@.service\n" /* This line ensures the old functionality for templated unit still works */
+ "disable *\n" , WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ /* Preset a single instantiated unit specified in the list */
+ assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ /* Check for preset-all case, only instances on the list should be enabled, not including the default instance */
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
+ assert_se(n_changes > 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+
+ unit_file_changes_free(changes, n_changes);
+}
+
int main(int argc, char *argv[]) {
char root[] = "/tmp/rootXXXXXX";
const char *p;
@@ -1012,6 +1070,7 @@ int main(int argc, char *argv[]) {
test_indirect(root);
test_preset_and_list(root);
test_preset_order(root);
+ test_preset_multiple_instances(root);
test_revert(root);
test_static_instance(root);
test_with_dropin(root);
diff --git a/src/test/test-install.c b/src/test/test-install.c
index 7dfc7e4272..62daaccd62 100644
--- a/src/test/test-install.c
+++ b/src/test/test-install.c
@@ -4,6 +4,7 @@
#include <string.h>
#include "install.h"
+#include "tests.h"
static void dump_changes(UnitFileChange *c, unsigned n) {
unsigned i;
@@ -29,8 +30,7 @@ int main(int argc, char* argv[]) {
size_t n_changes = 0;
UnitFileState state = 0;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
+ test_setup_logging(LOG_DEBUG);
h = hashmap_new(&string_hash_ops);
r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL);
diff --git a/src/test/test-ip-protocol-list.c b/src/test/test-ip-protocol-list.c
new file mode 100644
index 0000000000..79390e5289
--- /dev/null
+++ b/src/test/test-ip-protocol-list.c
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <netinet/in.h>
+
+#include "macro.h"
+#include "ip-protocol-list.h"
+#include "stdio-util.h"
+#include "string-util.h"
+
+static void test_int(int i) {
+ char str[DECIMAL_STR_MAX(int)];
+
+ assert_se(ip_protocol_from_name(ip_protocol_to_name(i)) == i);
+
+ xsprintf(str, "%i", i);
+ assert_se(ip_protocol_from_name(ip_protocol_to_name(parse_ip_protocol(str))) == i);
+}
+
+static void test_int_fail(int i) {
+ char str[DECIMAL_STR_MAX(int)];
+
+ assert_se(!ip_protocol_to_name(i));
+
+ xsprintf(str, "%i", i);
+ assert_se(parse_ip_protocol(str) == -EINVAL);
+}
+
+static void test_str(const char *s) {
+ assert_se(streq(ip_protocol_to_name(ip_protocol_from_name(s)), s));
+ assert_se(streq(ip_protocol_to_name(parse_ip_protocol(s)), s));
+}
+
+static void test_str_fail(const char *s) {
+ assert_se(ip_protocol_from_name(s) == -EINVAL);
+ assert_se(parse_ip_protocol(s) == -EINVAL);
+}
+
+static void test_parse_ip_protocol(const char *s, int expected) {
+ assert_se(parse_ip_protocol(s) == expected);
+}
+
+int main(int argc, const char *argv[]) {
+ test_int(IPPROTO_TCP);
+ test_int(IPPROTO_DCCP);
+ test_int_fail(-1);
+ test_int_fail(1024 * 1024);
+
+ test_str("sctp");
+ test_str("udp");
+ test_str_fail("hoge");
+ test_str_fail("-1");
+ test_str_fail("1000000000");
+
+ test_parse_ip_protocol("sctp", IPPROTO_SCTP);
+ test_parse_ip_protocol("ScTp", IPPROTO_SCTP);
+ test_parse_ip_protocol("ip", IPPROTO_IP);
+ test_parse_ip_protocol("", IPPROTO_IP);
+ test_parse_ip_protocol("1", 1);
+ test_parse_ip_protocol("0", 0);
+ test_parse_ip_protocol("-10", -EINVAL);
+ test_parse_ip_protocol("100000000", -EINVAL);
+
+ return 0;
+}
diff --git a/src/test/test-ipcrm.c b/src/test/test-ipcrm.c
index 106c29951e..4b658a0bdb 100644
--- a/src/test/test-ipcrm.c
+++ b/src/test/test-ipcrm.c
@@ -2,6 +2,7 @@
#include "clean-ipc.h"
#include "user-util.h"
+#include "tests.h"
#include "util.h"
int main(int argc, char *argv[]) {
@@ -9,11 +10,14 @@ int main(int argc, char *argv[]) {
int r;
const char* name = argv[1] ?: NOBODY_USER_NAME;
- r = get_user_creds(&name, &uid, NULL, NULL, NULL);
+ test_setup_logging(LOG_INFO);
+
+ r = get_user_creds(&name, &uid, NULL, NULL, NULL, 0);
+ if (r == -ESRCH)
+ return log_tests_skipped("Failed to resolve user");
if (r < 0) {
- log_full_errno(r == -ESRCH ? LOG_NOTICE : LOG_ERR,
- r, "Failed to resolve \"%s\": %m", name);
- return r == -ESRCH ? EXIT_TEST_SKIP : EXIT_FAILURE;
+ log_error_errno(r, "Failed to resolve \"%s\": %m", name);
+ return EXIT_FAILURE;
}
r = clean_ipc_by_uid(uid);
diff --git a/src/test/test-job-type.c b/src/test/test-job-type.c
index dc5f9eae83..d51e0d94fd 100644
--- a/src/test/test-job-type.c
+++ b/src/test/test-job-type.c
@@ -6,7 +6,7 @@
#include "service.h"
#include "unit.h"
-int main(int argc, char*argv[]) {
+int main(int argc, char *argv[]) {
JobType a, b, c, ab, bc, ab_c, bc_a, a_bc;
const ServiceState test_states[] = { SERVICE_DEAD, SERVICE_RUNNING };
unsigned i;
diff --git a/src/test/test-journal-importer.c b/src/test/test-journal-importer.c
index 56bf6a1296..cddbfa7022 100644
--- a/src/test/test-journal-importer.c
+++ b/src/test/test-journal-importer.c
@@ -4,8 +4,10 @@
#include <sys/stat.h>
#include <fcntl.h>
+#include "alloc-util.h"
#include "log.h"
#include "journal-importer.h"
+#include "path-util.h"
#include "string-util.h"
#include "tests.h"
@@ -20,9 +22,11 @@ static void assert_iovec_entry(const struct iovec *iovec, const char* content) {
static void test_basic_parsing(void) {
_cleanup_(journal_importer_cleanup) JournalImporter imp = {};
+ _cleanup_free_ char *journal_data_path = NULL;
int r;
- imp.fd = open(get_testdata_dir("/journal-data/journal-1.txt"), O_RDONLY|O_CLOEXEC);
+ journal_data_path = path_join(get_testdata_dir(), "journal-data/journal-1.txt");
+ imp.fd = open(journal_data_path, O_RDONLY|O_CLOEXEC);
assert_se(imp.fd >= 0);
do
@@ -49,9 +53,11 @@ static void test_basic_parsing(void) {
static void test_bad_input(void) {
_cleanup_(journal_importer_cleanup) JournalImporter imp = {};
+ _cleanup_free_ char *journal_data_path = NULL;
int r;
- imp.fd = open(get_testdata_dir("/journal-data/journal-2.txt"), O_RDONLY|O_CLOEXEC);
+ journal_data_path = path_join(get_testdata_dir(), "journal-data/journal-2.txt");
+ imp.fd = open(journal_data_path, O_RDONLY|O_CLOEXEC);
assert_se(imp.fd >= 0);
do
@@ -63,8 +69,7 @@ static void test_bad_input(void) {
}
int main(int argc, char **argv) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
+ test_setup_logging(LOG_DEBUG);
test_basic_parsing();
test_bad_input();
diff --git a/src/test/test-json.c b/src/test/test-json.c
new file mode 100644
index 0000000000..5aa4d19dbe
--- /dev/null
+++ b/src/test/test-json.c
@@ -0,0 +1,462 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <math.h>
+#if HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "json-internal.h"
+#include "json.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
+
+static void test_tokenizer(const char *data, ...) {
+ unsigned line = 0, column = 0;
+ void *state = NULL;
+ va_list ap;
+
+ va_start(ap, data);
+
+ for (;;) {
+ unsigned token_line, token_column;
+ _cleanup_free_ char *str = NULL;
+ JsonValue v = JSON_VALUE_NULL;
+ int t, tt;
+
+ t = json_tokenize(&data, &str, &v, &token_line, &token_column, &state, &line, &column);
+ tt = va_arg(ap, int);
+
+ assert_se(t == tt);
+
+ if (t == JSON_TOKEN_END || t < 0)
+ break;
+
+ else if (t == JSON_TOKEN_STRING) {
+ const char *nn;
+
+ nn = va_arg(ap, const char *);
+ assert_se(streq_ptr(nn, str));
+
+ } else if (t == JSON_TOKEN_REAL) {
+ long double d;
+
+ d = va_arg(ap, long double);
+
+#if HAVE_VALGRIND_VALGRIND_H
+ if (!RUNNING_ON_VALGRIND)
+#endif
+ /* Valgrind doesn't support long double calculations and automatically downgrades to 80bit:
+ * http://www.valgrind.org/docs/manual/manual-core.html#manual-core.limits */
+ assert_se(fabsl(d - v.real) < 0.001L);
+
+ } else if (t == JSON_TOKEN_INTEGER) {
+ intmax_t i;
+
+ i = va_arg(ap, intmax_t);
+ assert_se(i == v.integer);
+
+ } else if (t == JSON_TOKEN_UNSIGNED) {
+ uintmax_t u;
+
+ u = va_arg(ap, uintmax_t);
+ assert_se(u == v.unsig);
+
+ } else if (t == JSON_TOKEN_BOOLEAN) {
+ bool b;
+
+ b = va_arg(ap, int);
+ assert_se(b == v.boolean);
+ }
+ }
+
+ va_end(ap);
+}
+
+typedef void (*Test)(JsonVariant *);
+
+static void test_variant(const char *data, Test test) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ r = json_parse(data, &v, NULL, NULL);
+ assert_se(r == 0);
+ assert_se(v);
+
+ r = json_variant_format(v, 0, &s);
+ assert_se(r >= 0);
+ assert_se(s);
+
+ log_info("formatted normally: %s\n", s);
+
+ r = json_parse(data, &w, NULL, NULL);
+ assert_se(r == 0);
+ assert_se(w);
+ assert_se(json_variant_has_type(v, json_variant_type(w)));
+ assert_se(json_variant_has_type(w, json_variant_type(v)));
+ assert_se(json_variant_equal(v, w));
+
+ s = mfree(s);
+ w = json_variant_unref(w);
+
+ r = json_variant_format(v, JSON_FORMAT_PRETTY, &s);
+ assert_se(r >= 0);
+ assert_se(s);
+
+ log_info("formatted prettily:\n%s", s);
+
+ r = json_parse(data, &w, NULL, NULL);
+ assert_se(r == 0);
+ assert_se(w);
+
+ assert_se(json_variant_has_type(v, json_variant_type(w)));
+ assert_se(json_variant_has_type(w, json_variant_type(v)));
+ assert_se(json_variant_equal(v, w));
+
+ s = mfree(s);
+ r = json_variant_format(v, JSON_FORMAT_COLOR, &s);
+ assert_se(r >= 0);
+ assert_se(s);
+ printf("Normal with color: %s\n", s);
+
+ s = mfree(s);
+ r = json_variant_format(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, &s);
+ assert_se(r >= 0);
+ assert_se(s);
+ printf("Pretty with color:\n%s\n", s);
+
+ if (test)
+ test(v);
+}
+
+static void test_1(JsonVariant *v) {
+ JsonVariant *p, *q;
+ unsigned i;
+
+ /* 3 keys + 3 values */
+ assert_se(json_variant_elements(v) == 6);
+
+ /* has k */
+ p = json_variant_by_key(v, "k");
+ assert_se(p && json_variant_type(p) == JSON_VARIANT_STRING);
+
+ /* k equals v */
+ assert_se(streq(json_variant_string(p), "v"));
+
+ /* has foo */
+ p = json_variant_by_key(v, "foo");
+ assert_se(p && json_variant_type(p) == JSON_VARIANT_ARRAY && json_variant_elements(p) == 3);
+
+ /* check foo[0] = 1, foo[1] = 2, foo[2] = 3 */
+ for (i = 0; i < 3; ++i) {
+ q = json_variant_by_index(p, i);
+ assert_se(q && json_variant_type(q) == JSON_VARIANT_UNSIGNED && json_variant_unsigned(q) == (i+1));
+ assert_se(q && json_variant_has_type(q, JSON_VARIANT_INTEGER) && json_variant_integer(q) == (i+1));
+ }
+
+ /* has bar */
+ p = json_variant_by_key(v, "bar");
+ assert_se(p && json_variant_type(p) == JSON_VARIANT_OBJECT && json_variant_elements(p) == 2);
+
+ /* zap is null */
+ q = json_variant_by_key(p, "zap");
+ assert_se(q && json_variant_type(q) == JSON_VARIANT_NULL);
+}
+
+static void test_2(JsonVariant *v) {
+ JsonVariant *p, *q;
+
+ /* 2 keys + 2 values */
+ assert_se(json_variant_elements(v) == 4);
+
+ /* has mutant */
+ p = json_variant_by_key(v, "mutant");
+ assert_se(p && json_variant_type(p) == JSON_VARIANT_ARRAY && json_variant_elements(p) == 4);
+
+ /* mutant[0] == 1 */
+ q = json_variant_by_index(p, 0);
+ assert_se(q && json_variant_type(q) == JSON_VARIANT_UNSIGNED && json_variant_unsigned(q) == 1);
+ assert_se(q && json_variant_has_type(q, JSON_VARIANT_INTEGER) && json_variant_integer(q) == 1);
+
+ /* mutant[1] == null */
+ q = json_variant_by_index(p, 1);
+ assert_se(q && json_variant_type(q) == JSON_VARIANT_NULL);
+
+ /* mutant[2] == "1" */
+ q = json_variant_by_index(p, 2);
+ assert_se(q && json_variant_type(q) == JSON_VARIANT_STRING && streq(json_variant_string(q), "1"));
+
+ /* mutant[3] == JSON_VARIANT_OBJECT */
+ q = json_variant_by_index(p, 3);
+ assert_se(q && json_variant_type(q) == JSON_VARIANT_OBJECT && json_variant_elements(q) == 2);
+
+ /* has 1 */
+ p = json_variant_by_key(q, "1");
+ assert_se(p && json_variant_type(p) == JSON_VARIANT_ARRAY && json_variant_elements(p) == 2);
+
+ /* "1"[0] == 1 */
+ q = json_variant_by_index(p, 0);
+ assert_se(q && json_variant_type(q) == JSON_VARIANT_UNSIGNED && json_variant_unsigned(q) == 1);
+ assert_se(q && json_variant_has_type(q, JSON_VARIANT_INTEGER) && json_variant_integer(q) == 1);
+
+ /* "1"[1] == "1" */
+ q = json_variant_by_index(p, 1);
+ assert_se(q && json_variant_type(q) == JSON_VARIANT_STRING && streq(json_variant_string(q), "1"));
+
+ /* has thisisaverylongproperty */
+ p = json_variant_by_key(v, "thisisaverylongproperty");
+ assert_se(p && json_variant_type(p) == JSON_VARIANT_REAL && fabsl(json_variant_real(p) - 1.27) < 0.001);
+}
+
+
+static void test_zeroes(JsonVariant *v) {
+ size_t i;
+
+ /* Make sure zero is how we expect it. */
+
+ assert_se(json_variant_elements(v) == 13);
+
+ for (i = 0; i < json_variant_elements(v); i++) {
+ JsonVariant *w;
+ size_t j;
+
+ assert_se(w = json_variant_by_index(v, i));
+
+ assert_se(json_variant_integer(w) == 0);
+ assert_se(json_variant_unsigned(w) == 0U);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+ assert_se(json_variant_real(w) == 0.0L);
+#pragma GCC diagnostic pop
+
+ assert_se(json_variant_is_integer(w));
+ assert_se(json_variant_is_unsigned(w));
+ assert_se(json_variant_is_real(w));
+ assert_se(json_variant_is_number(w));
+
+ assert_se(!json_variant_is_negative(w));
+
+ assert_se(IN_SET(json_variant_type(w), JSON_VARIANT_INTEGER, JSON_VARIANT_UNSIGNED, JSON_VARIANT_REAL));
+
+ for (j = 0; j < json_variant_elements(v); j++) {
+ JsonVariant *q;
+
+ assert_se(q = json_variant_by_index(v, j));
+
+ assert_se(json_variant_equal(w, q));
+ }
+ }
+}
+
+static void test_build(void) {
+ _cleanup_(json_variant_unrefp) JsonVariant *a = NULL, *b = NULL;
+ _cleanup_free_ char *s = NULL, *t = NULL;
+
+ assert_se(json_build(&a, JSON_BUILD_STRING("hallo")) >= 0);
+ assert_se(json_build(&b, JSON_BUILD_LITERAL(" \"hallo\" ")) >= 0);
+ assert_se(json_variant_equal(a, b));
+
+ b = json_variant_unref(b);
+
+ assert_se(json_build(&b, JSON_BUILD_VARIANT(a)) >= 0);
+ assert_se(json_variant_equal(a, b));
+
+ b = json_variant_unref(b);
+ assert_se(json_build(&b, JSON_BUILD_STRING("pief")) >= 0);
+ assert_se(!json_variant_equal(a, b));
+
+ a = json_variant_unref(a);
+ b = json_variant_unref(b);
+
+ assert_se(json_build(&a, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("one", JSON_BUILD_INTEGER(7)),
+ JSON_BUILD_PAIR("two", JSON_BUILD_REAL(2.0)),
+ JSON_BUILD_PAIR("three", JSON_BUILD_INTEGER(0)))) >= 0);
+
+ assert_se(json_build(&b, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("two", JSON_BUILD_INTEGER(2)),
+ JSON_BUILD_PAIR("three", JSON_BUILD_REAL(0)),
+ JSON_BUILD_PAIR("one", JSON_BUILD_REAL(7)))) >= 0);
+
+ assert_se(json_variant_equal(a, b));
+
+ a = json_variant_unref(a);
+ b = json_variant_unref(b);
+
+ assert_se(json_build(&a, JSON_BUILD_ARRAY(JSON_BUILD_OBJECT(JSON_BUILD_PAIR("x", JSON_BUILD_BOOLEAN(true)),
+ JSON_BUILD_PAIR("y", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("this", JSON_BUILD_NULL)))),
+ JSON_BUILD_VARIANT(NULL),
+ JSON_BUILD_LITERAL(NULL),
+ JSON_BUILD_STRING(NULL),
+ JSON_BUILD_NULL,
+ JSON_BUILD_INTEGER(77),
+ JSON_BUILD_ARRAY(JSON_BUILD_VARIANT(JSON_VARIANT_STRING_CONST("foobar")), JSON_BUILD_VARIANT(JSON_VARIANT_STRING_CONST("zzz"))),
+ JSON_BUILD_STRV(STRV_MAKE("one", "two", "three", "four")))) >= 0);
+
+ assert_se(json_variant_format(a, 0, &s) >= 0);
+ log_info("GOT: %s\n", s);
+ assert_se(json_parse(s, &b, NULL, NULL) >= 0);
+ assert_se(json_variant_equal(a, b));
+
+ a = json_variant_unref(a);
+ b = json_variant_unref(b);
+
+ assert_se(json_build(&a, JSON_BUILD_REAL(M_PIl)) >= 0);
+
+ s = mfree(s);
+ assert_se(json_variant_format(a, 0, &s) >= 0);
+ log_info("GOT: %s\n", s);
+ assert_se(json_parse(s, &b, NULL, NULL) >= 0);
+ assert_se(json_variant_format(b, 0, &t) >= 0);
+ log_info("GOT: %s\n", t);
+
+ assert_se(streq(s, t));
+
+ a = json_variant_unref(a);
+ b = json_variant_unref(b);
+
+ assert_se(json_build(&a, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("x", JSON_BUILD_STRING("y")),
+ JSON_BUILD_PAIR("z", JSON_BUILD_STRING("a")),
+ JSON_BUILD_PAIR("b", JSON_BUILD_STRING("c"))
+ )) >= 0);
+
+ assert_se(json_build(&b, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("x", JSON_BUILD_STRING("y")),
+ JSON_BUILD_PAIR_CONDITION(false, "p", JSON_BUILD_STRING("q")),
+ JSON_BUILD_PAIR_CONDITION(true, "z", JSON_BUILD_STRING("a")),
+ JSON_BUILD_PAIR_CONDITION(false, "j", JSON_BUILD_ARRAY(JSON_BUILD_STRING("k"), JSON_BUILD_STRING("u"), JSON_BUILD_STRING("i"))),
+ JSON_BUILD_PAIR("b", JSON_BUILD_STRING("c"))
+ )) >= 0);
+
+ assert_se(json_variant_equal(a, b));
+}
+
+static void test_source(void) {
+ static const char data[] =
+ "\n"
+ "\n"
+ "{\n"
+ "\"foo\" : \"bar\", \n"
+ "\"qüüx\" : [ 1, 2, 3,\n"
+ "4,\n"
+ "5 ],\n"
+ "\"miep\" : { \"hallo\" : 1 },\n"
+ "\n"
+ "\"zzzzzz\" \n"
+ ":\n"
+ "[ true, \n"
+ "false, 7.5, {} ]\n"
+ "}\n";
+
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ printf("--- original begin ---\n"
+ "%s"
+ "--- original end ---\n", data);
+
+ assert_se(f = fmemopen((void*) data, strlen(data), "r"));
+
+ assert_se(json_parse_file(f, "waldo", &v, NULL, NULL) >= 0);
+
+ printf("--- non-pretty begin ---\n");
+ json_variant_dump(v, 0, stdout, NULL);
+ printf("\n--- non-pretty end ---\n");
+
+ printf("--- pretty begin ---\n");
+ json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, stdout, NULL);
+ printf("--- pretty end ---\n");
+}
+
+static void test_depth(void) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ unsigned i;
+ int r;
+
+ v = JSON_VARIANT_STRING_CONST("start");
+
+ /* Let's verify that the maximum depth checks work */
+
+ for (i = 0;; i++) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+
+ assert_se(i <= UINT16_MAX);
+ if (i & 1)
+ r = json_variant_new_array(&w, &v, 1);
+ else
+ r = json_variant_new_object(&w, (JsonVariant*[]) { JSON_VARIANT_STRING_CONST("key"), v }, 2);
+ if (r == -ELNRNG) {
+ log_info("max depth at %u", i);
+ break;
+ }
+
+ assert_se(r >= 0);
+
+ json_variant_unref(v);
+ v = TAKE_PTR(w);
+ }
+
+ json_variant_dump(v, 0, stdout, NULL);
+ fputs("\n", stdout);
+}
+
+int main(int argc, char *argv[]) {
+
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+ log_open();
+
+ test_tokenizer("x", -EINVAL);
+ test_tokenizer("", JSON_TOKEN_END);
+ test_tokenizer(" ", JSON_TOKEN_END);
+ test_tokenizer("0", JSON_TOKEN_UNSIGNED, (uintmax_t) 0, JSON_TOKEN_END);
+ test_tokenizer("-0", JSON_TOKEN_INTEGER, (intmax_t) 0, JSON_TOKEN_END);
+ test_tokenizer("1234", JSON_TOKEN_UNSIGNED, (uintmax_t) 1234, JSON_TOKEN_END);
+ test_tokenizer("-1234", JSON_TOKEN_INTEGER, (intmax_t) -1234, JSON_TOKEN_END);
+ test_tokenizer("18446744073709551615", JSON_TOKEN_UNSIGNED, (uintmax_t) UINT64_MAX, JSON_TOKEN_END);
+ test_tokenizer("-9223372036854775808", JSON_TOKEN_INTEGER, (intmax_t) INT64_MIN, JSON_TOKEN_END);
+ test_tokenizer("18446744073709551616", JSON_TOKEN_REAL, (long double) 18446744073709551616.0L, JSON_TOKEN_END);
+ test_tokenizer("-9223372036854775809", JSON_TOKEN_REAL, (long double) -9223372036854775809.0L, JSON_TOKEN_END);
+ test_tokenizer("-1234", JSON_TOKEN_INTEGER, (intmax_t) -1234, JSON_TOKEN_END);
+ test_tokenizer("3.141", JSON_TOKEN_REAL, (long double) 3.141, JSON_TOKEN_END);
+ test_tokenizer("0.0", JSON_TOKEN_REAL, (long double) 0.0, JSON_TOKEN_END);
+ test_tokenizer("7e3", JSON_TOKEN_REAL, (long double) 7e3, JSON_TOKEN_END);
+ test_tokenizer("-7e-3", JSON_TOKEN_REAL, (long double) -7e-3, JSON_TOKEN_END);
+ test_tokenizer("true", JSON_TOKEN_BOOLEAN, true, JSON_TOKEN_END);
+ test_tokenizer("false", JSON_TOKEN_BOOLEAN, false, JSON_TOKEN_END);
+ test_tokenizer("null", JSON_TOKEN_NULL, JSON_TOKEN_END);
+ test_tokenizer("{}", JSON_TOKEN_OBJECT_OPEN, JSON_TOKEN_OBJECT_CLOSE, JSON_TOKEN_END);
+ test_tokenizer("\t {\n} \n", JSON_TOKEN_OBJECT_OPEN, JSON_TOKEN_OBJECT_CLOSE, JSON_TOKEN_END);
+ test_tokenizer("[]", JSON_TOKEN_ARRAY_OPEN, JSON_TOKEN_ARRAY_CLOSE, JSON_TOKEN_END);
+ test_tokenizer("\t [] \n\n", JSON_TOKEN_ARRAY_OPEN, JSON_TOKEN_ARRAY_CLOSE, JSON_TOKEN_END);
+ test_tokenizer("\"\"", JSON_TOKEN_STRING, "", JSON_TOKEN_END);
+ test_tokenizer("\"foo\"", JSON_TOKEN_STRING, "foo", JSON_TOKEN_END);
+ test_tokenizer("\"foo\\nfoo\"", JSON_TOKEN_STRING, "foo\nfoo", JSON_TOKEN_END);
+ test_tokenizer("{\"foo\" : \"bar\"}", JSON_TOKEN_OBJECT_OPEN, JSON_TOKEN_STRING, "foo", JSON_TOKEN_COLON, JSON_TOKEN_STRING, "bar", JSON_TOKEN_OBJECT_CLOSE, JSON_TOKEN_END);
+ test_tokenizer("{\"foo\" : [true, false]}", JSON_TOKEN_OBJECT_OPEN, JSON_TOKEN_STRING, "foo", JSON_TOKEN_COLON, JSON_TOKEN_ARRAY_OPEN, JSON_TOKEN_BOOLEAN, true, JSON_TOKEN_COMMA, JSON_TOKEN_BOOLEAN, false, JSON_TOKEN_ARRAY_CLOSE, JSON_TOKEN_OBJECT_CLOSE, JSON_TOKEN_END);
+ test_tokenizer("\"\xef\xbf\xbd\"", JSON_TOKEN_STRING, "\xef\xbf\xbd", JSON_TOKEN_END);
+ test_tokenizer("\"\\ufffd\"", JSON_TOKEN_STRING, "\xef\xbf\xbd", JSON_TOKEN_END);
+ test_tokenizer("\"\\uf\"", -EINVAL);
+ test_tokenizer("\"\\ud800a\"", -EINVAL);
+ test_tokenizer("\"\\udc00\\udc00\"", -EINVAL);
+ test_tokenizer("\"\\ud801\\udc37\"", JSON_TOKEN_STRING, "\xf0\x90\x90\xb7", JSON_TOKEN_END);
+
+ test_tokenizer("[1, 2, -3]", JSON_TOKEN_ARRAY_OPEN, JSON_TOKEN_UNSIGNED, (uintmax_t) 1, JSON_TOKEN_COMMA, JSON_TOKEN_UNSIGNED, (uintmax_t) 2, JSON_TOKEN_COMMA, JSON_TOKEN_INTEGER, (intmax_t) -3, JSON_TOKEN_ARRAY_CLOSE, JSON_TOKEN_END);
+
+ test_variant("{\"k\": \"v\", \"foo\": [1, 2, 3], \"bar\": {\"zap\": null}}", test_1);
+ test_variant("{\"mutant\": [1, null, \"1\", {\"1\": [1, \"1\"]}], \"thisisaverylongproperty\": 1.27}", test_2);
+ test_variant("{\"foo\" : \"\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFFFFF\\\"\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFF\\uDBFF\\uDFFFF\\uDBFF\\uDFFF\\uDBFF\\uDFFF\\uDBFF\\uDFFF\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFFFF\\\"\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFF\\uDBFF\\uDFFF\"}", NULL);
+
+ test_variant("[ 0, -0, 0.0, -0.0, 0.000, -0.000, 0e0, -0e0, 0e+0, -0e-0, 0e-0, -0e000, 0e+000 ]", test_zeroes);
+
+ test_build();
+
+ test_source();
+
+ test_depth();
+
+ return 0;
+}
diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c
index 68399bdb9e..10bf365035 100644
--- a/src/test/test-libudev.c
+++ b/src/test/test-libudev.c
@@ -1,18 +1,18 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <unistd.h>
-#include "libudev.h"
-
+#include "alloc-util.h"
#include "fd-util.h"
+#include "libudev-list-internal.h"
+#include "libudev-util.h"
#include "log.h"
#include "stdio-util.h"
#include "string-util.h"
-#include "udev-util.h"
-#include "util.h"
static void print_device(struct udev_device *device) {
const char *str;
@@ -331,6 +331,138 @@ static void test_hwdb(struct udev *udev, const char *modalias) {
assert_se(hwdb == NULL);
}
+static void test_util_replace_whitespace_one_len(const char *str, size_t len, const char *expected) {
+ _cleanup_free_ char *result = NULL;
+ int r;
+
+ result = new(char, len + 1);
+ assert_se(result);
+ r = util_replace_whitespace(str, result, len);
+ assert_se((size_t) r == strlen(expected));
+ assert_se(streq(result, expected));
+}
+
+static void test_util_replace_whitespace_one(const char *str, const char *expected) {
+ test_util_replace_whitespace_one_len(str, strlen(str), expected);
+}
+
+static void test_util_replace_whitespace(void) {
+ test_util_replace_whitespace_one("hogehoge", "hogehoge");
+ test_util_replace_whitespace_one("hoge hoge", "hoge_hoge");
+ test_util_replace_whitespace_one(" hoge hoge ", "hoge_hoge");
+ test_util_replace_whitespace_one(" ", "");
+ test_util_replace_whitespace_one("hoge ", "hoge");
+
+ test_util_replace_whitespace_one_len("hoge hoge ", 9, "hoge_hoge");
+ test_util_replace_whitespace_one_len("hoge hoge ", 8, "hoge_hog");
+ test_util_replace_whitespace_one_len("hoge hoge ", 7, "hoge_ho");
+ test_util_replace_whitespace_one_len("hoge hoge ", 6, "hoge_h");
+ test_util_replace_whitespace_one_len("hoge hoge ", 5, "hoge");
+ test_util_replace_whitespace_one_len("hoge hoge ", 4, "hoge");
+ test_util_replace_whitespace_one_len("hoge hoge ", 3, "hog");
+ test_util_replace_whitespace_one_len("hoge hoge ", 2, "ho");
+ test_util_replace_whitespace_one_len("hoge hoge ", 1, "h");
+ test_util_replace_whitespace_one_len("hoge hoge ", 0, "");
+
+ test_util_replace_whitespace_one_len(" hoge hoge ", 9, "hoge_hoge");
+ test_util_replace_whitespace_one_len(" hoge hoge ", 8, "hoge_hog");
+ test_util_replace_whitespace_one_len(" hoge hoge ", 7, "hoge_ho");
+ test_util_replace_whitespace_one_len(" hoge hoge ", 6, "hoge_h");
+ test_util_replace_whitespace_one_len(" hoge hoge ", 5, "hoge");
+ test_util_replace_whitespace_one_len(" hoge hoge ", 4, "hoge");
+ test_util_replace_whitespace_one_len(" hoge hoge ", 3, "hog");
+ test_util_replace_whitespace_one_len(" hoge hoge ", 2, "ho");
+ test_util_replace_whitespace_one_len(" hoge hoge ", 1, "h");
+ test_util_replace_whitespace_one_len(" hoge hoge ", 0, "");
+}
+
+static void test_util_resolve_subsys_kernel_one(const char *str, bool read_value, int retval, const char *expected) {
+ char result[UTIL_PATH_SIZE];
+ int r;
+
+ r = util_resolve_subsys_kernel(str, result, sizeof(result), read_value);
+ assert_se(r == retval);
+ if (r >= 0)
+ assert_se(streq(result, expected));
+}
+
+static void test_util_resolve_subsys_kernel(void) {
+ test_util_resolve_subsys_kernel_one("hoge", false, -EINVAL, NULL);
+ test_util_resolve_subsys_kernel_one("[hoge", false, -EINVAL, NULL);
+ test_util_resolve_subsys_kernel_one("[hoge/foo", false, -EINVAL, NULL);
+ test_util_resolve_subsys_kernel_one("[hoge/]", false, -ENODEV, NULL);
+
+ test_util_resolve_subsys_kernel_one("[net/lo]", false, 0, "/sys/devices/virtual/net/lo");
+ test_util_resolve_subsys_kernel_one("[net/lo]/", false, 0, "/sys/devices/virtual/net/lo");
+ test_util_resolve_subsys_kernel_one("[net/lo]hoge", false, 0, "/sys/devices/virtual/net/lo/hoge");
+ test_util_resolve_subsys_kernel_one("[net/lo]/hoge", false, 0, "/sys/devices/virtual/net/lo/hoge");
+
+ test_util_resolve_subsys_kernel_one("[net/lo]", true, -EINVAL, NULL);
+ test_util_resolve_subsys_kernel_one("[net/lo]/", true, -EINVAL, NULL);
+ test_util_resolve_subsys_kernel_one("[net/lo]hoge", true, 0, "");
+ test_util_resolve_subsys_kernel_one("[net/lo]/hoge", true, 0, "");
+ test_util_resolve_subsys_kernel_one("[net/lo]address", true, 0, "00:00:00:00:00:00");
+ test_util_resolve_subsys_kernel_one("[net/lo]/address", true, 0, "00:00:00:00:00:00");
+}
+
+static void test_list(void) {
+ struct udev_list list = {};
+ struct udev_list_entry *e;
+
+ /* empty list */
+ udev_list_init(&list, false);
+ assert_se(!udev_list_get_entry(&list));
+
+ /* unique == false */
+ udev_list_init(&list, false);
+ assert_se(udev_list_entry_add(&list, "aaa", "hoge"));
+ assert_se(udev_list_entry_add(&list, "aaa", "hogehoge"));
+ assert_se(udev_list_entry_add(&list, "bbb", "foo"));
+ e = udev_list_get_entry(&list);
+ assert_se(e);
+ assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
+ assert_se(streq_ptr(udev_list_entry_get_value(e), "hoge"));
+ e = udev_list_entry_get_next(e);
+ assert_se(e);
+ assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
+ assert_se(streq_ptr(udev_list_entry_get_value(e), "hogehoge"));
+ e = udev_list_entry_get_next(e);
+ assert_se(e);
+ assert_se(streq_ptr(udev_list_entry_get_name(e), "bbb"));
+ assert_se(streq_ptr(udev_list_entry_get_value(e), "foo"));
+ assert_se(!udev_list_entry_get_next(e));
+
+ assert_se(!udev_list_entry_get_by_name(e, "aaa"));
+ assert_se(!udev_list_entry_get_by_name(e, "bbb"));
+ assert_se(!udev_list_entry_get_by_name(e, "ccc"));
+ udev_list_cleanup(&list);
+
+ /* unique == true */
+ udev_list_init(&list, true);
+ assert_se(udev_list_entry_add(&list, "aaa", "hoge"));
+ assert_se(udev_list_entry_add(&list, "aaa", "hogehoge"));
+ assert_se(udev_list_entry_add(&list, "bbb", "foo"));
+ e = udev_list_get_entry(&list);
+ assert_se(e);
+ assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
+ assert_se(streq_ptr(udev_list_entry_get_value(e), "hogehoge"));
+ e = udev_list_entry_get_next(e);
+ assert_se(streq_ptr(udev_list_entry_get_name(e), "bbb"));
+ assert_se(streq_ptr(udev_list_entry_get_value(e), "foo"));
+ assert_se(!udev_list_entry_get_next(e));
+
+ e = udev_list_entry_get_by_name(e, "bbb");
+ assert_se(e);
+ assert_se(streq_ptr(udev_list_entry_get_name(e), "bbb"));
+ assert_se(streq_ptr(udev_list_entry_get_value(e), "foo"));
+ e = udev_list_entry_get_by_name(e, "aaa");
+ assert_se(e);
+ assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
+ assert_se(streq_ptr(udev_list_entry_get_value(e), "hogehoge"));
+ assert_se(!udev_list_entry_get_by_name(e, "ccc"));
+ udev_list_cleanup(&list);
+}
+
int main(int argc, char *argv[]) {
_cleanup_(udev_unrefp) struct udev *udev = NULL;
bool arg_monitor = false;
@@ -399,7 +531,6 @@ int main(int argc, char *argv[]) {
test_device_subsys_name(udev, "subsystem", "pci");
test_device_subsys_name(udev, "drivers", "scsi:sd");
test_device_subsys_name(udev, "module", "printk");
-
test_device_parents(udev, syspath);
test_enumerate(udev, subsystem);
@@ -411,5 +542,10 @@ int main(int argc, char *argv[]) {
if (arg_monitor)
test_monitor(udev);
+ test_util_replace_whitespace();
+ test_util_resolve_subsys_kernel();
+
+ test_list();
+
return EXIT_SUCCESS;
}
diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c
index 8ffae8ca03..c6f8c1fb4f 100644
--- a/src/test/test-locale-util.c
+++ b/src/test/test-locale-util.c
@@ -65,21 +65,32 @@ static void test_keymaps(void) {
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
static void dump_special_glyphs(void) {
- assert_cc(ELLIPSIS + 1 == _SPECIAL_GLYPH_MAX);
+ assert_cc(SPECIAL_GLYPH_DEPRESSED_SMILEY + 1 == _SPECIAL_GLYPH_MAX);
log_info("/* %s */", __func__);
log_info("is_locale_utf8: %s", yes_no(is_locale_utf8()));
- dump_glyph(TREE_VERTICAL);
- dump_glyph(TREE_BRANCH);
- dump_glyph(TREE_RIGHT);
- dump_glyph(TREE_SPACE);
- dump_glyph(TRIANGULAR_BULLET);
- dump_glyph(BLACK_CIRCLE);
- dump_glyph(ARROW);
- dump_glyph(MDASH);
- dump_glyph(ELLIPSIS);
+ dump_glyph(SPECIAL_GLYPH_TREE_VERTICAL);
+ dump_glyph(SPECIAL_GLYPH_TREE_BRANCH);
+ dump_glyph(SPECIAL_GLYPH_TREE_RIGHT);
+ dump_glyph(SPECIAL_GLYPH_TREE_SPACE);
+ dump_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET);
+ dump_glyph(SPECIAL_GLYPH_BLACK_CIRCLE);
+ dump_glyph(SPECIAL_GLYPH_BULLET);
+ dump_glyph(SPECIAL_GLYPH_ARROW);
+ dump_glyph(SPECIAL_GLYPH_MDASH);
+ dump_glyph(SPECIAL_GLYPH_ELLIPSIS);
+ dump_glyph(SPECIAL_GLYPH_MU);
+ dump_glyph(SPECIAL_GLYPH_CHECK_MARK);
+ dump_glyph(SPECIAL_GLYPH_CROSS_MARK);
+ dump_glyph(SPECIAL_GLYPH_ECSTATIC_SMILEY);
+ dump_glyph(SPECIAL_GLYPH_HAPPY_SMILEY);
+ dump_glyph(SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY);
+ dump_glyph(SPECIAL_GLYPH_NEUTRAL_SMILEY);
+ dump_glyph(SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY);
+ dump_glyph(SPECIAL_GLYPH_UNHAPPY_SMILEY);
+ dump_glyph(SPECIAL_GLYPH_DEPRESSED_SMILEY);
}
int main(int argc, char *argv[]) {
diff --git a/src/test/test-log.c b/src/test/test-log.c
index c09f40c356..1e010c08fb 100644
--- a/src/test/test-log.c
+++ b/src/test/test-log.c
@@ -17,20 +17,27 @@ assert_cc((LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, LOG_LOCAL3 | LOG_DEBUG) & LOG
assert_cc((LOG_REALM_PLUS_LEVEL(LOG_REALM_UDEV, LOG_USER | LOG_INFO) & LOG_PRIMASK)
== LOG_INFO);
+assert_cc(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(EINVAL)));
+assert_cc(!IS_SYNTHETIC_ERRNO(EINVAL));
+assert_cc(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(0)));
+assert_cc(!IS_SYNTHETIC_ERRNO(0));
+
#define X10(x) x x x x x x x x x x
#define X100(x) X10(X10(x))
#define X1000(x) X100(X10(x))
-static void test_log_console(void) {
+static void test_log_struct(void) {
log_struct(LOG_INFO,
- "MESSAGE=Waldo PID="PID_FMT, getpid_cached(),
+ "MESSAGE=Waldo PID="PID_FMT" (no errno)", getpid_cached(),
"SERVICE=piepapo");
-}
-static void test_log_journal(void) {
- log_struct(LOG_INFO,
- "MESSAGE=Foobar PID="PID_FMT, getpid_cached(),
- "SERVICE=foobar");
+ log_struct_errno(LOG_INFO, EILSEQ,
+ "MESSAGE=Waldo PID="PID_FMT": %m (normal)", getpid_cached(),
+ "SERVICE=piepapo");
+
+ log_struct_errno(LOG_INFO, SYNTHETIC_ERRNO(EILSEQ),
+ "MESSAGE=Waldo PID="PID_FMT": %m (synthetic)", getpid_cached(),
+ "SERVICE=piepapo");
log_struct(LOG_INFO,
"MESSAGE=Foobar PID="PID_FMT, getpid_cached(),
@@ -59,10 +66,11 @@ int main(int argc, char* argv[]) {
log_set_target(target);
log_open();
- test_log_console();
- test_log_journal();
+ test_log_struct();
test_long_lines();
}
+ assert_se(log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), "foo") == -EUCLEAN);
+
return 0;
}
diff --git a/src/test/test-loopback.c b/src/test/test-loopback.c
index eaea9e4c76..89b760fae4 100644
--- a/src/test/test-loopback.c
+++ b/src/test/test-loopback.c
@@ -5,13 +5,12 @@
#include "log.h"
#include "loopback-setup.h"
+#include "tests.h"
int main(int argc, char* argv[]) {
int r;
- log_open();
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
+ test_setup_logging(LOG_DEBUG);
r = loopback_setup();
if (r < 0)
diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c
index c10e1681fb..6986405dc6 100644
--- a/src/test/test-mount-util.c
+++ b/src/test/test-mount-util.c
@@ -3,241 +3,9 @@
#include <sys/mount.h>
#include "alloc-util.h"
-#include "def.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "hashmap.h"
-#include "log.h"
-#include "log.h"
#include "mount-util.h"
-#include "path-util.h"
-#include "rm-rf.h"
#include "string-util.h"
-
-static void test_mount_propagation_flags(const char *name, int ret, unsigned long expected) {
- long unsigned flags;
-
- assert_se(mount_propagation_flags_from_string(name, &flags) == ret);
-
- if (ret >= 0) {
- const char *c;
-
- assert_se(flags == expected);
-
- c = mount_propagation_flags_to_string(flags);
- if (isempty(name))
- assert_se(isempty(c));
- else
- assert_se(streq(c, name));
- }
-}
-
-static void test_mnt_id(void) {
- _cleanup_fclose_ FILE *f = NULL;
- Hashmap *h;
- Iterator i;
- char *p;
- void *k;
- int r;
-
- assert_se(f = fopen("/proc/self/mountinfo", "re"));
- assert_se(h = hashmap_new(&trivial_hash_ops));
-
- for (;;) {
- _cleanup_free_ char *line = NULL, *path = NULL;
- int mnt_id;
-
- r = read_line(f, LONG_LINE_MAX, &line);
- if (r == 0)
- break;
- assert_se(r > 0);
-
- assert_se(sscanf(line, "%i %*s %*s %*s %ms", &mnt_id, &path) == 2);
-
- assert_se(hashmap_put(h, INT_TO_PTR(mnt_id), path) >= 0);
- path = NULL;
- }
-
- HASHMAP_FOREACH_KEY(p, k, h, i) {
- int mnt_id = PTR_TO_INT(k), mnt_id2;
-
- r = path_get_mnt_id(p, &mnt_id2);
- if (r < 0) {
- log_debug_errno(r, "Failed to get the mnt id of %s: %m\n", p);
- continue;
- }
-
- log_debug("mnt id of %s is %i\n", p, mnt_id2);
-
- if (mnt_id == mnt_id2)
- continue;
-
- /* The ids don't match? If so, then there are two mounts on the same path, let's check if that's really
- * the case */
- assert_se(path_equal_ptr(hashmap_get(h, INT_TO_PTR(mnt_id2)), p));
- }
-
- hashmap_free_free(h);
-}
-
-static void test_path_is_mount_point(void) {
- int fd;
- char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
- _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
- _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
- _cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
-
- assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/", NULL, 0) > 0);
- assert_se(path_is_mount_point("//", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("//", NULL, 0) > 0);
-
- assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
- assert_se(path_is_mount_point("/proc/", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/proc/", NULL, 0) > 0);
-
- assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
- assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
-
- assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
- assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/sys/", NULL, 0) > 0);
-
- /* we'll create a hierarchy of different kinds of dir/file/link
- * layouts:
- *
- * <tmp>/file1, <tmp>/file2
- * <tmp>/link1 -> file1, <tmp>/link2 -> file2
- * <tmp>/dir1/
- * <tmp>/dir1/file
- * <tmp>/dirlink1 -> dir1
- * <tmp>/dirlink1file -> dirlink1/file
- * <tmp>/dir2/
- * <tmp>/dir2/file
- */
-
- /* file mountpoints */
- assert_se(mkdtemp(tmp_dir) != NULL);
- file1 = path_join(NULL, tmp_dir, "file1");
- assert_se(file1);
- file2 = path_join(NULL, tmp_dir, "file2");
- assert_se(file2);
- fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
- assert_se(fd > 0);
- close(fd);
- fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
- assert_se(fd > 0);
- close(fd);
- link1 = path_join(NULL, tmp_dir, "link1");
- assert_se(link1);
- assert_se(symlink("file1", link1) == 0);
- link2 = path_join(NULL, tmp_dir, "link2");
- assert_se(link1);
- assert_se(symlink("file2", link2) == 0);
-
- assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(file1, NULL, 0) == 0);
- assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(link1, NULL, 0) == 0);
-
- /* directory mountpoints */
- dir1 = path_join(NULL, tmp_dir, "dir1");
- assert_se(dir1);
- assert_se(mkdir(dir1, 0755) == 0);
- dirlink1 = path_join(NULL, tmp_dir, "dirlink1");
- assert_se(dirlink1);
- assert_se(symlink("dir1", dirlink1) == 0);
- dirlink1file = path_join(NULL, tmp_dir, "dirlink1file");
- assert_se(dirlink1file);
- assert_se(symlink("dirlink1/file", dirlink1file) == 0);
- dir2 = path_join(NULL, tmp_dir, "dir2");
- assert_se(dir2);
- assert_se(mkdir(dir2, 0755) == 0);
-
- assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
- assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
-
- /* file in subdirectory mountpoints */
- dir1file = path_join(NULL, dir1, "file");
- assert_se(dir1file);
- fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
- assert_se(fd > 0);
- close(fd);
-
- assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
- assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
- assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
-
- /* these tests will only work as root */
- if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
- int rf, rt, rdf, rdt, rlf, rlt, rl1f, rl1t;
- const char *file2d;
-
- /* files */
- /* capture results in vars, to avoid dangling mounts on failure */
- log_info("%s: %s", __func__, file2);
- rf = path_is_mount_point(file2, NULL, 0);
- rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
-
- file2d = strjoina(file2, "/");
- log_info("%s: %s", __func__, file2d);
- rdf = path_is_mount_point(file2d, NULL, 0);
- rdt = path_is_mount_point(file2d, NULL, AT_SYMLINK_FOLLOW);
-
- log_info("%s: %s", __func__, link2);
- rlf = path_is_mount_point(link2, NULL, 0);
- rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
-
- assert_se(umount(file2) == 0);
-
- assert_se(rf == 1);
- assert_se(rt == 1);
- assert_se(rdf == -ENOTDIR);
- assert_se(rdt == -ENOTDIR);
- assert_se(rlf == 0);
- assert_se(rlt == 1);
-
- /* dirs */
- dir2file = path_join(NULL, dir2, "file");
- assert_se(dir2file);
- fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
- assert_se(fd > 0);
- close(fd);
-
- assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
-
- log_info("%s: %s", __func__, dir1);
- rf = path_is_mount_point(dir1, NULL, 0);
- rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
- log_info("%s: %s", __func__, dirlink1);
- rlf = path_is_mount_point(dirlink1, NULL, 0);
- rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
- log_info("%s: %s", __func__, dirlink1file);
- /* its parent is a mount point, but not /file itself */
- rl1f = path_is_mount_point(dirlink1file, NULL, 0);
- rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
-
- assert_se(umount(dir1) == 0);
-
- assert_se(rf == 1);
- assert_se(rt == 1);
- assert_se(rlf == 0);
- assert_se(rlt == 1);
- assert_se(rl1f == 0);
- assert_se(rl1t == 0);
-
- } else
- printf("Skipping bind mount file test: %m\n");
-
- assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
-}
+#include "tests.h"
static void test_mount_option_mangle(void) {
char *opts = NULL;
@@ -294,19 +62,8 @@ static void test_mount_option_mangle(void) {
}
int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_DEBUG);
- log_set_max_level(LOG_DEBUG);
-
- test_mount_propagation_flags("shared", 0, MS_SHARED);
- test_mount_propagation_flags("slave", 0, MS_SLAVE);
- test_mount_propagation_flags("private", 0, MS_PRIVATE);
- test_mount_propagation_flags(NULL, 0, 0);
- test_mount_propagation_flags("", 0, 0);
- test_mount_propagation_flags("xxxx", -EINVAL, 0);
- test_mount_propagation_flags(" ", -EINVAL, 0);
-
- test_mnt_id();
- test_path_is_mount_point();
test_mount_option_mangle();
return 0;
diff --git a/src/test/test-mountpoint-util.c b/src/test/test-mountpoint-util.c
new file mode 100644
index 0000000000..6d8bee0d63
--- /dev/null
+++ b/src/test/test-mountpoint-util.c
@@ -0,0 +1,268 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/mount.h>
+
+#include "alloc-util.h"
+#include "def.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "log.h"
+#include "log.h"
+#include "mountpoint-util.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "tests.h"
+
+static void test_mount_propagation_flags(const char *name, int ret, unsigned long expected) {
+ long unsigned flags;
+
+ log_info("/* %s(%s) */", __func__, name);
+
+ assert_se(mount_propagation_flags_from_string(name, &flags) == ret);
+
+ if (ret >= 0) {
+ const char *c;
+
+ assert_se(flags == expected);
+
+ c = mount_propagation_flags_to_string(flags);
+ if (isempty(name))
+ assert_se(isempty(c));
+ else
+ assert_se(streq(c, name));
+ }
+}
+
+static void test_mnt_id(void) {
+ _cleanup_fclose_ FILE *f = NULL;
+ Hashmap *h;
+ Iterator i;
+ char *p;
+ void *k;
+ int r;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(f = fopen("/proc/self/mountinfo", "re"));
+ assert_se(h = hashmap_new(&trivial_hash_ops));
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL, *path = NULL;
+ int mnt_id;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r == 0)
+ break;
+ assert_se(r > 0);
+
+ assert_se(sscanf(line, "%i %*s %*s %*s %ms", &mnt_id, &path) == 2);
+
+ log_debug("mountinfo: %s → %i", path, mnt_id);
+
+ assert_se(hashmap_put(h, INT_TO_PTR(mnt_id), path) >= 0);
+ path = NULL;
+ }
+
+ HASHMAP_FOREACH_KEY(p, k, h, i) {
+ int mnt_id = PTR_TO_INT(k), mnt_id2;
+
+ r = path_get_mnt_id(p, &mnt_id2);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to get the mnt id of %s: %m\n", p);
+ continue;
+ }
+
+ log_debug("mnt ids of %s are %i, %i\n", p, mnt_id, mnt_id2);
+
+ if (mnt_id == mnt_id2)
+ continue;
+
+ /* The ids don't match? If so, then there are two mounts on the same path, let's check if
+ * that's really the case */
+ char *t = hashmap_get(h, INT_TO_PTR(mnt_id2));
+ log_debug("the other path for mnt id %i is %s\n", mnt_id2, t);
+ assert_se(path_equal(p, t));
+ }
+
+ hashmap_free_free(h);
+}
+
+static void test_path_is_mount_point(void) {
+ int fd;
+ char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
+ _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
+ _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
+ _cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/", NULL, 0) > 0);
+ assert_se(path_is_mount_point("//", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("//", NULL, 0) > 0);
+
+ assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
+ assert_se(path_is_mount_point("/proc/", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/proc/", NULL, 0) > 0);
+
+ assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
+ assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
+
+ assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
+ assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/sys/", NULL, 0) > 0);
+
+ /* we'll create a hierarchy of different kinds of dir/file/link
+ * layouts:
+ *
+ * <tmp>/file1, <tmp>/file2
+ * <tmp>/link1 -> file1, <tmp>/link2 -> file2
+ * <tmp>/dir1/
+ * <tmp>/dir1/file
+ * <tmp>/dirlink1 -> dir1
+ * <tmp>/dirlink1file -> dirlink1/file
+ * <tmp>/dir2/
+ * <tmp>/dir2/file
+ */
+
+ /* file mountpoints */
+ assert_se(mkdtemp(tmp_dir) != NULL);
+ file1 = path_join(tmp_dir, "file1");
+ assert_se(file1);
+ file2 = path_join(tmp_dir, "file2");
+ assert_se(file2);
+ fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+ fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+ link1 = path_join(tmp_dir, "link1");
+ assert_se(link1);
+ assert_se(symlink("file1", link1) == 0);
+ link2 = path_join(tmp_dir, "link2");
+ assert_se(link1);
+ assert_se(symlink("file2", link2) == 0);
+
+ assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(file1, NULL, 0) == 0);
+ assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(link1, NULL, 0) == 0);
+
+ /* directory mountpoints */
+ dir1 = path_join(tmp_dir, "dir1");
+ assert_se(dir1);
+ assert_se(mkdir(dir1, 0755) == 0);
+ dirlink1 = path_join(tmp_dir, "dirlink1");
+ assert_se(dirlink1);
+ assert_se(symlink("dir1", dirlink1) == 0);
+ dirlink1file = path_join(tmp_dir, "dirlink1file");
+ assert_se(dirlink1file);
+ assert_se(symlink("dirlink1/file", dirlink1file) == 0);
+ dir2 = path_join(tmp_dir, "dir2");
+ assert_se(dir2);
+ assert_se(mkdir(dir2, 0755) == 0);
+
+ assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
+ assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
+
+ /* file in subdirectory mountpoints */
+ dir1file = path_join(dir1, "file");
+ assert_se(dir1file);
+ fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+
+ assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
+ assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
+
+ /* these tests will only work as root */
+ if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
+ int rf, rt, rdf, rdt, rlf, rlt, rl1f, rl1t;
+ const char *file2d;
+
+ /* files */
+ /* capture results in vars, to avoid dangling mounts on failure */
+ log_info("%s: %s", __func__, file2);
+ rf = path_is_mount_point(file2, NULL, 0);
+ rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
+
+ file2d = strjoina(file2, "/");
+ log_info("%s: %s", __func__, file2d);
+ rdf = path_is_mount_point(file2d, NULL, 0);
+ rdt = path_is_mount_point(file2d, NULL, AT_SYMLINK_FOLLOW);
+
+ log_info("%s: %s", __func__, link2);
+ rlf = path_is_mount_point(link2, NULL, 0);
+ rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
+
+ assert_se(umount(file2) == 0);
+
+ assert_se(rf == 1);
+ assert_se(rt == 1);
+ assert_se(rdf == -ENOTDIR);
+ assert_se(rdt == -ENOTDIR);
+ assert_se(rlf == 0);
+ assert_se(rlt == 1);
+
+ /* dirs */
+ dir2file = path_join(dir2, "file");
+ assert_se(dir2file);
+ fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+
+ assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
+
+ log_info("%s: %s", __func__, dir1);
+ rf = path_is_mount_point(dir1, NULL, 0);
+ rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
+ log_info("%s: %s", __func__, dirlink1);
+ rlf = path_is_mount_point(dirlink1, NULL, 0);
+ rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
+ log_info("%s: %s", __func__, dirlink1file);
+ /* its parent is a mount point, but not /file itself */
+ rl1f = path_is_mount_point(dirlink1file, NULL, 0);
+ rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
+
+ assert_se(umount(dir1) == 0);
+
+ assert_se(rf == 1);
+ assert_se(rt == 1);
+ assert_se(rlf == 0);
+ assert_se(rlt == 1);
+ assert_se(rl1f == 0);
+ assert_se(rl1t == 0);
+
+ } else
+ printf("Skipping bind mount file test: %m\n");
+
+ assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_DEBUG);
+
+ test_mount_propagation_flags("shared", 0, MS_SHARED);
+ test_mount_propagation_flags("slave", 0, MS_SLAVE);
+ test_mount_propagation_flags("private", 0, MS_PRIVATE);
+ test_mount_propagation_flags(NULL, 0, 0);
+ test_mount_propagation_flags("", 0, 0);
+ test_mount_propagation_flags("xxxx", -EINVAL, 0);
+ test_mount_propagation_flags(" ", -EINVAL, 0);
+
+ test_mnt_id();
+ test_path_is_mount_point();
+
+ return 0;
+}
diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c
index b202739719..cc2efecfd2 100644
--- a/src/test/test-namespace.c
+++ b/src/test/test-namespace.c
@@ -7,6 +7,7 @@
#include "namespace.h"
#include "process-util.h"
#include "string-util.h"
+#include "tests.h"
#include "util.h"
static void test_tmpdir(const char *id, const char *A, const char *B) {
@@ -46,16 +47,14 @@ static void test_tmpdir(const char *id, const char *A, const char *B) {
assert_se(rmdir(b) >= 0);
}
-static void test_netns(void) {
+static int test_netns(void) {
_cleanup_close_pair_ int s[2] = { -1, -1 };
pid_t pid1, pid2, pid3;
int r, n = 0;
siginfo_t si;
- if (geteuid() > 0) {
- log_info("Skipping test: not root");
- exit(EXIT_TEST_SKIP);
- }
+ if (geteuid() > 0)
+ return log_tests_skipped("not root");
assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, s) >= 0);
@@ -102,6 +101,7 @@ static void test_netns(void) {
n += si.si_status;
assert_se(n == 1);
+ return EXIT_SUCCESS;
}
int main(int argc, char *argv[]) {
@@ -109,8 +109,12 @@ int main(int argc, char *argv[]) {
char boot_id[SD_ID128_STRING_MAX];
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *zz = NULL;
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_INFO);
+
+ if (!have_namespaces()) {
+ log_tests_skipped("Don't have namespace support");
+ return EXIT_TEST_SKIP;
+ }
assert_se(sd_id128_get_boot(&bid) >= 0);
sd_id128_to_string(bid, boot_id);
@@ -128,7 +132,5 @@ int main(int argc, char *argv[]) {
test_tmpdir("sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device", z, zz);
- test_netns();
-
- return 0;
+ return test_netns();
}
diff --git a/src/test/test-netlink-manual.c b/src/test/test-netlink-manual.c
index eed610b27a..1ebe8d1972 100644
--- a/src/test/test-netlink-manual.c
+++ b/src/test/test-netlink-manual.c
@@ -10,6 +10,7 @@
#include "macro.h"
#include "module-util.h"
+#include "tests.h"
#include "util.h"
static int load_module(const char *mod_name) {
@@ -47,10 +48,14 @@ static int test_tunnel_configure(sd_netlink *rtnl) {
/* skip test if module cannot be loaded */
r = load_module("ipip");
if (r < 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped_errno(r, "failed to load module 'ipip'");
+
+ r = load_module("sit");
+ if (r < 0)
+ return log_tests_skipped_errno(r, "failed to load module 'sit'");
if (getuid() != 0)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("not root");
/* IPIP tunnel */
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0) >= 0);
@@ -76,10 +81,6 @@ static int test_tunnel_configure(sd_netlink *rtnl) {
assert_se((m = sd_netlink_message_unref(m)) == NULL);
- r = load_module("sit");
- if (r < 0)
- return EXIT_TEST_SKIP;
-
/* sit */
assert_se(sd_rtnl_message_new_link(rtnl, &n, RTM_NEWLINK, 0) >= 0);
assert_se(n);
@@ -113,6 +114,8 @@ int main(int argc, char *argv[]) {
sd_netlink *rtnl;
int r;
+ test_setup_logging(LOG_INFO);
+
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(rtnl);
diff --git a/src/test/test-ns.c b/src/test/test-ns.c
index 4ab70f2306..d3dbb54ca1 100644
--- a/src/test/test-ns.c
+++ b/src/test/test-ns.c
@@ -6,6 +6,7 @@
#include "log.h"
#include "namespace.h"
+#include "tests.h"
int main(int argc, char *argv[]) {
const char * const writable[] = {
@@ -43,7 +44,7 @@ int main(int argc, char *argv[]) {
char tmp_dir[] = "/tmp/systemd-private-XXXXXX",
var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX";
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(tmp_dir));
assert_se(mkdtemp(var_tmp_dir));
diff --git a/src/test/test-nscd-flush.c b/src/test/test-nscd-flush.c
new file mode 100644
index 0000000000..97c219596d
--- /dev/null
+++ b/src/test/test-nscd-flush.c
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "main-func.h"
+#include "nscd-flush.h"
+#include "strv.h"
+#include "tests.h"
+
+static int run(int argc, char *argv[]) {
+ int r;
+
+ test_setup_logging(LOG_DEBUG);
+
+ r = nscd_flush_cache(STRV_MAKE("group", "passwd", "hosts"));
+ if (r < 0)
+ return log_error_errno(r, "Failed to flush NSCD cache");
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/test/test-nss.c b/src/test/test-nss.c
index 9e543e7557..20aa6cf01f 100644
--- a/src/test/test-nss.c
+++ b/src/test/test-nss.c
@@ -1,22 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <dlfcn.h>
-#include <stdlib.h>
#include <net/if.h>
+#include <stdlib.h>
+#include "af-list.h"
+#include "alloc-util.h"
+#include "errno-list.h"
+#include "hexdecoct.h"
+#include "hostname-util.h"
+#include "in-addr-util.h"
+#include "local-addresses.h"
#include "log.h"
#include "nss-util.h"
#include "path-util.h"
-#include "string-util.h"
-#include "alloc-util.h"
-#include "in-addr-util.h"
-#include "hexdecoct.h"
-#include "af-list.h"
#include "stdio-util.h"
+#include "string-util.h"
#include "strv.h"
-#include "errno-list.h"
-#include "hostname-util.h"
-#include "local-addresses.h"
static const char* nss_status_to_string(enum nss_status status, char *buf, size_t buf_len) {
switch (status) {
@@ -51,14 +51,12 @@ static const char* af_to_string(int family, char *buf, size_t buf_len) {
}
static void* open_handle(const char* dir, const char* module, int flags) {
- const char *path;
+ const char *path = NULL;
void *handle;
- if (dir) {
+ if (dir)
path = strjoina(dir, "/libnss_", module, ".so.2");
- if (access(path, F_OK) < 0)
- path = strjoina(dir, "/.libs/libnss_", module, ".so.2");
- } else
+ if (!path || access(path, F_OK) < 0)
path = strjoina("libnss_", module, ".so.2");
handle = dlopen(path, flags);
@@ -397,9 +395,7 @@ static int test_one_module(const char* dir,
log_info("======== %s ========", module);
- handle = open_handle(streq(module, "dns") ? NULL : dir,
- module,
- RTLD_LAZY|RTLD_NODELETE);
+ handle = open_handle(dir, module, RTLD_LAZY|RTLD_NODELETE);
if (!handle)
return -EINVAL;
@@ -428,20 +424,19 @@ static int parse_argv(int argc, char **argv,
size_t n_allocated = 0;
if (argc > 1)
- modules = strv_new(argv[1], NULL);
+ modules = strv_new(argv[1]);
else
modules = strv_new(
-#if ENABLE_MYHOSTNAME
+#if ENABLE_NSS_MYHOSTNAME
"myhostname",
#endif
-#if ENABLE_RESOLVE
+#if ENABLE_NSS_RESOLVE
"resolve",
#endif
-#if ENABLE_MACHINED
+#if ENABLE_NSS_MYMACHINES
"mymachines",
#endif
- "dns",
- NULL);
+ "dns");
if (!modules)
return -ENOMEM;
@@ -472,7 +467,7 @@ static int parse_argv(int argc, char **argv,
if (!hostname)
return -ENOMEM;
- names = strv_new("localhost", "_gateway", "foo_no_such_host", hostname, NULL);
+ names = strv_new("localhost", "_gateway", "foo_no_such_host", hostname);
if (!names)
return -ENOMEM;
diff --git a/src/test/test-os-util.c b/src/test/test-os-util.c
index 8d8b52d7f6..c215a2e99e 100644
--- a/src/test/test-os-util.c
+++ b/src/test/test-os-util.c
@@ -4,6 +4,7 @@
#include "log.h"
#include "os-util.h"
+#include "tests.h"
static void test_path_is_os_tree(void) {
assert_se(path_is_os_tree("/") > 0);
@@ -12,9 +13,7 @@ static void test_path_is_os_tree(void) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_path_is_os_tree();
diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c
index e9aef5e882..d732f402f0 100644
--- a/src/test/test-parse-util.c
+++ b/src/test/test-parse-util.c
@@ -726,10 +726,12 @@ static void test_parse_dev(void) {
assert_se(parse_dev("5", &dev) == -EINVAL);
assert_se(parse_dev("5:", &dev) == -EINVAL);
assert_se(parse_dev(":5", &dev) == -EINVAL);
+ assert_se(parse_dev("-1:-1", &dev) == -EINVAL);
#if SIZEOF_DEV_T < 8
assert_se(parse_dev("4294967295:4294967295", &dev) == -EINVAL);
#endif
assert_se(parse_dev("8:11", &dev) >= 0 && major(dev) == 8 && minor(dev) == 11);
+ assert_se(parse_dev("0:0", &dev) >= 0 && major(dev) == 0 && minor(dev) == 0);
}
static void test_parse_errno(void) {
diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c
index 892293cb10..f208559358 100644
--- a/src/test/test-path-lookup.c
+++ b/src/test/test-path-lookup.c
@@ -8,6 +8,7 @@
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
static void test_paths(UnitFileScope scope) {
char template[] = "/tmp/test-path-lookup.XXXXXXX";
@@ -40,6 +41,8 @@ static void test_user_and_global_paths(void) {
unsigned k = 0;
assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0);
+ assert_se(unsetenv("XDG_DATA_DIRS") == 0);
+ assert_se(unsetenv("XDG_CONFIG_DIRS") == 0);
assert_se(lookup_paths_init(&lp_global, UNIT_FILE_GLOBAL, 0, NULL) == 0);
assert_se(lookup_paths_init(&lp_user, UNIT_FILE_USER, 0, NULL) == 0);
@@ -76,9 +79,7 @@ static void print_generator_binary_paths(UnitFileScope scope) {
}
int main(int argc, char **argv) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_paths(UNIT_FILE_SYSTEM);
test_paths(UNIT_FILE_USER);
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 35b27bcedd..8854a94f6c 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -6,12 +6,13 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "macro.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
#include "util.h"
#define test_path_compare(a, b, result) { \
@@ -80,10 +81,10 @@ static void test_path(void) {
test_path_simplify("///.//", "/.", "/");
test_path_simplify("///.//.///", "/./.", "/");
test_path_simplify("////.././///../.", "/.././../.", "/../..");
- test_path_simplify(".", ".", "");
- test_path_simplify("./", ".", "");
- test_path_simplify(".///.//./.", "./././.", "");
- test_path_simplify(".///.//././/", "./././.", "");
+ test_path_simplify(".", ".", ".");
+ test_path_simplify("./", ".", ".");
+ test_path_simplify(".///.//./.", "./././.", ".");
+ test_path_simplify(".///.//././/", "./././.", ".");
test_path_simplify("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/.",
"/./aaa/././.bbb/../c./d.dd/..eeee/.",
"/aaa/.bbb/../c./d.dd/..eeee");
@@ -231,23 +232,47 @@ static void test_prefixes(void) {
static void test_path_join(void) {
-#define test_join(root, path, rest, expected) { \
+#define test_join(expected, ...) { \
_cleanup_free_ char *z = NULL; \
- z = path_join(root, path, rest); \
+ z = path_join(__VA_ARGS__); \
+ log_debug("got \"%s\", expected \"%s\"", z, expected); \
assert_se(streq(z, expected)); \
}
- test_join("/root", "/a/b", "/c", "/root/a/b/c");
- test_join("/root", "a/b", "c", "/root/a/b/c");
- test_join("/root", "/a/b", "c", "/root/a/b/c");
- test_join("/root", "/", "c", "/root/c");
- test_join("/root", "/", NULL, "/root/");
-
- test_join(NULL, "/a/b", "/c", "/a/b/c");
- test_join(NULL, "a/b", "c", "a/b/c");
- test_join(NULL, "/a/b", "c", "/a/b/c");
- test_join(NULL, "/", "c", "/c");
- test_join(NULL, "/", NULL, "/");
+ test_join("/root/a/b/c", "/root", "/a/b", "/c");
+ test_join("/root/a/b/c", "/root", "a/b", "c");
+ test_join("/root/a/b/c", "/root", "/a/b", "c");
+ test_join("/root/c", "/root", "/", "c");
+ test_join("/root/", "/root", "/", NULL);
+
+ test_join("/a/b/c", "", "/a/b", "/c");
+ test_join("a/b/c", "", "a/b", "c");
+ test_join("/a/b/c", "", "/a/b", "c");
+ test_join("/c", "", "/", "c");
+ test_join("/", "", "/", NULL);
+
+ test_join("/a/b/c", NULL, "/a/b", "/c");
+ test_join("a/b/c", NULL, "a/b", "c");
+ test_join("/a/b/c", NULL, "/a/b", "c");
+ test_join("/c", NULL, "/", "c");
+ test_join("/", NULL, "/", NULL);
+
+ test_join("", "", NULL);
+ test_join("", NULL, "");
+ test_join("", NULL, NULL);
+
+ test_join("foo/bar", "foo", "bar");
+ test_join("foo/bar", "", "foo", "bar");
+ test_join("foo/bar", NULL, "foo", NULL, "bar");
+ test_join("foo/bar", "", "foo", "", "bar", "");
+ test_join("foo/bar", "", "", "", "", "foo", "", "", "", "bar", "", "", "");
+
+ test_join("//foo///bar//", "", "/", "", "/foo/", "", "/", "", "/bar/", "", "/", "");
+ test_join("/foo/bar/", "/", "foo", "/", "bar", "/");
+ test_join("foo/bar/baz", "foo", "bar", "baz");
+ test_join("foo/bar/baz", "foo/", "bar", "/baz");
+ test_join("foo//bar//baz", "foo/", "/bar/", "/baz");
+ test_join("//foo////bar////baz//", "//foo/", "///bar/", "///baz//");
}
static void test_fsck_exists(void) {
@@ -292,7 +317,7 @@ static void test_strv_resolve(void) {
assert_se(mkdtemp(tmp_dir) != NULL);
- search_dirs = strv_new("/dir1", "/dir2", "/dir3", NULL);
+ search_dirs = strv_new("/dir1", "/dir2", "/dir3");
assert_se(search_dirs);
STRV_FOREACH(d, search_dirs) {
char *p = strappend(tmp_dir, *d);
@@ -405,6 +430,7 @@ static void test_file_in_same_dir(void) {
}
static void test_last_path_component(void) {
+ assert_se(last_path_component(NULL) == NULL);
assert_se(streq(last_path_component("a/b/c"), "c"));
assert_se(streq(last_path_component("a/b/c/"), "c/"));
assert_se(streq(last_path_component("/"), "/"));
@@ -423,6 +449,45 @@ static void test_last_path_component(void) {
assert_se(streq(last_path_component("/a/"), "a/"));
}
+static void test_path_extract_filename_one(const char *input, const char *output, int ret) {
+ _cleanup_free_ char *k = NULL;
+ int r;
+
+ r = path_extract_filename(input, &k);
+ log_info("%s → %s/%s [expected: %s/%s]", strnull(input), strnull(k), strerror(-r), strnull(output), strerror(-ret));
+ assert_se(streq_ptr(k, output));
+ assert_se(r == ret);
+}
+
+static void test_path_extract_filename(void) {
+ test_path_extract_filename_one(NULL, NULL, -EINVAL);
+ test_path_extract_filename_one("a/b/c", "c", 0);
+ test_path_extract_filename_one("a/b/c/", "c", 0);
+ test_path_extract_filename_one("/", NULL, -EINVAL);
+ test_path_extract_filename_one("//", NULL, -EINVAL);
+ test_path_extract_filename_one("///", NULL, -EINVAL);
+ test_path_extract_filename_one(".", NULL, -EINVAL);
+ test_path_extract_filename_one("./.", NULL, -EINVAL);
+ test_path_extract_filename_one("././", NULL, -EINVAL);
+ test_path_extract_filename_one("././/", NULL, -EINVAL);
+ test_path_extract_filename_one("/foo/a", "a", 0);
+ test_path_extract_filename_one("/foo/a/", "a", 0);
+ test_path_extract_filename_one("", NULL, -EINVAL);
+ test_path_extract_filename_one("a", "a", 0);
+ test_path_extract_filename_one("a/", "a", 0);
+ test_path_extract_filename_one("/a", "a", 0);
+ test_path_extract_filename_one("/a/", "a", 0);
+ test_path_extract_filename_one("/////////////a/////////////", "a", 0);
+ test_path_extract_filename_one("xx/.", NULL, -EINVAL);
+ test_path_extract_filename_one("xx/..", NULL, -EINVAL);
+ test_path_extract_filename_one("..", NULL, -EINVAL);
+ test_path_extract_filename_one("/..", NULL, -EINVAL);
+ test_path_extract_filename_one("../", NULL, -EINVAL);
+ test_path_extract_filename_one(".", NULL, -EINVAL);
+ test_path_extract_filename_one("/.", NULL, -EINVAL);
+ test_path_extract_filename_one("./", NULL, -EINVAL);
+}
+
static void test_filename_is_valid(void) {
char foo[FILENAME_MAX+2];
int i;
@@ -505,10 +570,29 @@ static void test_empty_or_root(void) {
assert_se(!empty_or_root("//yy//"));
}
+static void test_path_startswith_set(void) {
+
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/bar", "/zzz"), ""));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/", "/zzz"), "bar"));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo", "/zzz"), "bar"));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/", "/zzz"), "foo/bar"));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "", "/zzz"), NULL));
+
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/bar", "/zzz"), NULL));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/", "/zzz"), "bar2"));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo", "/zzz"), "bar2"));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/", "/zzz"), "foo/bar2"));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "", "/zzz"), NULL));
+
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/bar", "/zzz"), NULL));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/", "/zzz"), NULL));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo", "/zzz"), NULL));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/", "/zzz"), "foo2/bar"));
+ assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "", "/zzz"), NULL));
+}
+
int main(int argc, char **argv) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_path();
test_path_equal_root();
@@ -522,10 +606,12 @@ int main(int argc, char **argv) {
test_prefix_root();
test_file_in_same_dir();
test_last_path_component();
+ test_path_extract_filename();
test_filename_is_valid();
test_hidden_or_backup_file();
test_skip_dev_prefix();
test_empty_or_root();
+ test_path_startswith_set();
test_systemd_installation_has_version(argv[1]); /* NULL is OK */
diff --git a/src/test/test-path.c b/src/test/test-path.c
index 209eb2e366..07a0e413ee 100644
--- a/src/test/test-path.c
+++ b/src/test/test-path.c
@@ -12,6 +12,7 @@
#include "macro.h"
#include "manager.h"
#include "mkdir.h"
+#include "path-util.h"
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
@@ -32,16 +33,12 @@ static int setup_test(Manager **m) {
assert_se(m);
r = enter_cgroup_subroot();
- if (r == -ENOMEDIUM) {
- log_notice_errno(r, "Skipping test: cgroupfs not available");
- return -EXIT_TEST_SKIP;
- }
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &tmp);
- if (MANAGER_SKIP_TEST(r)) {
- log_notice_errno(r, "Skipping test: manager_new: %m");
- return -EXIT_TEST_SKIP;
- }
+ if (MANAGER_SKIP_TEST(r))
+ return log_tests_skipped_errno(r, "manager_new");
assert_se(r >= 0);
assert_se(manager_startup(tmp, NULL, NULL) >= 0);
@@ -106,7 +103,7 @@ static void check_stop_unlink(Manager *m, Unit *unit, const char *test_path, con
}
}
- assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0);
+ assert_se(unit_stop(unit) >= 0);
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
}
@@ -117,7 +114,7 @@ static void test_path_exists(Manager *m) {
assert_se(m);
assert_se(manager_load_startable_unit_or_warn(m, "path-exists.path", NULL, &unit) >= 0);
- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0);
+ assert_se(unit_start(unit) >= 0);
assert_se(touch(test_path) >= 0);
@@ -130,7 +127,7 @@ static void test_path_existsglob(Manager *m) {
assert_se(m);
assert_se(manager_load_startable_unit_or_warn(m, "path-existsglob.path", NULL, &unit) >= 0);
- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0);
+ assert_se(unit_start(unit) >= 0);
assert_se(touch(test_path) >= 0);
@@ -147,7 +144,7 @@ static void test_path_changed(Manager *m) {
assert_se(touch(test_path) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "path-changed.path", NULL, &unit) >= 0);
- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0);
+ assert_se(unit_start(unit) >= 0);
f = fopen(test_path, "w");
assert_se(f);
@@ -166,7 +163,7 @@ static void test_path_modified(Manager *m) {
assert_se(touch(test_path) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "path-modified.path", NULL, &unit) >= 0);
- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0);
+ assert_se(unit_start(unit) >= 0);
f = fopen(test_path, "w");
assert_se(f);
@@ -182,7 +179,7 @@ static void test_path_unit(Manager *m) {
assert_se(m);
assert_se(manager_load_startable_unit_or_warn(m, "path-unit.path", NULL, &unit) >= 0);
- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0);
+ assert_se(unit_start(unit) >= 0);
assert_se(touch(test_path) >= 0);
@@ -198,7 +195,7 @@ static void test_path_directorynotempty(Manager *m) {
assert_se(access(test_path, F_OK) < 0);
assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0);
- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0);
+ assert_se(unit_start(unit) >= 0);
/* MakeDirectory default to no */
assert_se(access(test_path, F_OK) < 0);
@@ -219,7 +216,7 @@ static void test_path_makedirectory_directorymode(Manager *m) {
assert_se(access(test_path, F_OK) < 0);
assert_se(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit) >= 0);
- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0);
+ assert_se(unit_start(unit) >= 0);
/* Check if the directory has been created */
assert_se(access(test_path, F_OK) >= 0);
@@ -230,7 +227,7 @@ static void test_path_makedirectory_directorymode(Manager *m) {
assert_se((s.st_mode & S_IRWXG) == 0040);
assert_se((s.st_mode & S_IRWXO) == 0004);
- assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0);
+ assert_se(unit_stop(unit) >= 0);
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
}
@@ -247,15 +244,16 @@ int main(int argc, char *argv[]) {
};
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
+ _cleanup_free_ char *test_path = NULL;
const test_function_t *test = NULL;
Manager *m = NULL;
umask(022);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_INFO);
- assert_se(set_unit_path(get_testdata_dir("/test-path")) >= 0);
+ test_path = path_join(get_testdata_dir(), "test-path");
+ assert_se(set_unit_path(test_path) >= 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
for (test = tests; test && *test; test++) {
@@ -263,8 +261,8 @@ int main(int argc, char *argv[]) {
/* We create a clean environment for each test */
r = setup_test(&m);
- if (r < 0)
- return -r;
+ if (r != 0)
+ return r;
(*test)(m);
diff --git a/src/test/test-pretty-print.c b/src/test/test-pretty-print.c
new file mode 100644
index 0000000000..53ec512ec3
--- /dev/null
+++ b/src/test/test-pretty-print.c
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "macro.h"
+#include "pretty-print.h"
+#include "strv.h"
+#include "tests.h"
+
+static void test_terminal_urlify(void) {
+ _cleanup_free_ char *formatted = NULL;
+
+ assert_se(terminal_urlify("https://www.freedesktop.org/wiki/Software/systemd/", "systemd homepage", &formatted) >= 0);
+ printf("Hey, considere visiting the %s right now! It is very good!\n", formatted);
+
+ formatted = mfree(formatted);
+
+ assert_se(terminal_urlify_path("/etc/fstab", "this link to your /etc/fstab", &formatted) >= 0);
+ printf("Or click on %s to have a look at it!\n", formatted);
+}
+
+static void test_cat_files(void) {
+ assert_se(cat_files("/no/such/file", NULL, 0) == -ENOENT);
+ assert_se(cat_files("/no/such/file", NULL, CAT_FLAGS_MAIN_FILE_OPTIONAL) == 0);
+
+ if (access("/etc/fstab", R_OK) >= 0)
+ assert_se(cat_files("/etc/fstab", STRV_MAKE("/etc/fstab", "/etc/fstab"), 0) == 0);
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_INFO);
+
+ test_terminal_urlify();
+ test_cat_files();
+
+ print_separator();
+
+ return 0;
+}
diff --git a/src/test/test-prioq.c b/src/test/test-prioq.c
index 89c41d8ce7..bc5fdd15b2 100644
--- a/src/test/test-prioq.c
+++ b/src/test/test-prioq.c
@@ -10,40 +10,30 @@
#define SET_SIZE 1024*4
-static int unsigned_compare(const void *a, const void *b) {
- const unsigned *x = a, *y = b;
-
- if (*x < *y)
- return -1;
-
- if (*x > *y)
- return 1;
-
- return 0;
+static int unsigned_compare(const unsigned *a, const unsigned *b) {
+ return CMP(*a, *b);
}
static void test_unsigned(void) {
- unsigned buffer[SET_SIZE], i;
- Prioq *q;
+ _cleanup_(prioq_freep) Prioq *q = NULL;
+ unsigned buffer[SET_SIZE], i, u, n;
srand(0);
- q = prioq_new(trivial_compare_func);
- assert_se(q);
+ assert_se(q = prioq_new(trivial_compare_func));
for (i = 0; i < ELEMENTSOF(buffer); i++) {
- unsigned u;
-
u = (unsigned) rand();
buffer[i] = u;
assert_se(prioq_put(q, UINT_TO_PTR(u), NULL) >= 0);
+
+ n = prioq_size(q);
+ assert_se(prioq_remove(q, UINT_TO_PTR(u), &n) == 0);
}
- qsort(buffer, ELEMENTSOF(buffer), sizeof(buffer[0]), unsigned_compare);
+ typesafe_qsort(buffer, ELEMENTSOF(buffer), unsigned_compare);
for (i = 0; i < ELEMENTSOF(buffer); i++) {
- unsigned u;
-
assert_se(prioq_size(q) == ELEMENTSOF(buffer) - i);
u = PTR_TO_UINT(prioq_pop(q));
@@ -51,7 +41,6 @@ static void test_unsigned(void) {
}
assert_se(prioq_isempty(q));
- prioq_free(q);
}
struct test {
@@ -59,90 +48,59 @@ struct test {
unsigned idx;
};
-static int test_compare(const void *a, const void *b) {
- const struct test *x = a, *y = b;
-
- if (x->value < y->value)
- return -1;
-
- if (x->value > y->value)
- return 1;
-
- return 0;
+static int test_compare(const struct test *x, const struct test *y) {
+ return CMP(x->value, y->value);
}
-static void test_hash(const void *a, struct siphash *state) {
- const struct test *x = a;
-
+static void test_hash(const struct test *x, struct siphash *state) {
siphash24_compress(&x->value, sizeof(x->value), state);
}
-static const struct hash_ops test_hash_ops = {
- .hash = test_hash,
- .compare = test_compare
-};
+DEFINE_PRIVATE_HASH_OPS(test_hash_ops, struct test, test_hash, test_compare);
static void test_struct(void) {
- Prioq *q;
- Set *s;
+ _cleanup_(prioq_freep) Prioq *q = NULL;
+ _cleanup_(set_freep) Set *s = NULL;
unsigned previous = 0, i;
- int r;
+ struct test *t;
srand(0);
- q = prioq_new(test_compare);
- assert_se(q);
-
- s = set_new(&test_hash_ops);
- assert_se(s);
+ assert_se(q = prioq_new((compare_func_t) test_compare));
+ assert_se(s = set_new(&test_hash_ops));
for (i = 0; i < SET_SIZE; i++) {
- struct test *t;
-
- t = new0(struct test, 1);
- assert_se(t);
+ assert_se(t = new0(struct test, 1));
t->value = (unsigned) rand();
- r = prioq_put(q, t, &t->idx);
- assert_se(r >= 0);
+ assert_se(prioq_put(q, t, &t->idx) >= 0);
- if (i % 4 == 0) {
- r = set_consume(s, t);
- assert_se(r >= 0);
- }
+ if (i % 4 == 0)
+ assert_se(set_consume(s, t) >= 0);
}
- for (;;) {
- struct test *t;
-
- t = set_steal_first(s);
- if (!t)
- break;
-
- r = prioq_remove(q, t, &t->idx);
- assert_se(r > 0);
+ while ((t = set_steal_first(s))) {
+ assert_se(prioq_remove(q, t, &t->idx) == 1);
+ assert_se(prioq_remove(q, t, &t->idx) == 0);
+ assert_se(prioq_remove(q, t, NULL) == 0);
free(t);
}
for (i = 0; i < SET_SIZE * 3 / 4; i++) {
- struct test *t;
-
assert_se(prioq_size(q) == (SET_SIZE * 3 / 4) - i);
- t = prioq_pop(q);
- assert_se(t);
-
+ assert_se(t = prioq_pop(q));
+ assert_se(prioq_remove(q, t, &t->idx) == 0);
+ assert_se(prioq_remove(q, t, NULL) == 0);
assert_se(previous <= t->value);
+
previous = t->value;
free(t);
}
assert_se(prioq_isempty(q));
- prioq_free(q);
-
assert_se(set_isempty(s));
- set_free(s);
}
int main(int argc, char* argv[]) {
diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c
index 8f77e084b6..b0fc217d51 100644
--- a/src/test/test-proc-cmdline.c
+++ b/src/test/test-proc-cmdline.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
+#include "env-util.h"
#include "log.h"
#include "macro.h"
#include "proc-cmdline.h"
@@ -19,29 +20,79 @@ static int parse_item(const char *key, const char *value, void *data) {
}
static void test_proc_cmdline_parse(void) {
- assert_se(proc_cmdline_parse(parse_item, &obj, true) >= 0);
+ log_info("/* %s */", __func__);
+
+ assert_se(proc_cmdline_parse(parse_item, &obj, PROC_CMDLINE_STRIP_RD_PREFIX) >= 0);
}
-static void test_runlevel_to_target(void) {
- in_initrd_force(false);
- assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
- assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET));
- assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL));
+static void test_proc_cmdline_override(void) {
+ log_info("/* %s */", __func__);
- in_initrd_force(true);
- assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
- assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("3"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
+ assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"") == 0);
+
+ /* Test if the override works */
+ _cleanup_free_ char *line = NULL, *value = NULL;
+ assert_se(proc_cmdline(&line) >= 0);
+
+ /* Test if parsing makes uses of the override */
+ assert_se(streq(line, "foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\""));
+ assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
+ value = mfree(value);
+
+ assert_se(proc_cmdline_get_key("some_arg_with_space", 0, &value) > 0 && streq_ptr(value, "foo bar"));
+ value = mfree(value);
+
+ assert_se(proc_cmdline_get_key("and_one_more", 0, &value) > 0 && streq_ptr(value, "zzz aaa"));
+ value = mfree(value);
+}
+
+static int parse_item_given(const char *key, const char *value, void *data) {
+ assert_se(key);
+ assert_se(data);
+
+ bool *strip = data;
+
+ log_info("%s: option <%s> = <%s>", __func__, key, strna(value));
+ if (proc_cmdline_key_streq(key, "foo_bar"))
+ assert_se(streq(value, "quux"));
+ else if (proc_cmdline_key_streq(key, "wuff-piep"))
+ assert_se(streq(value, "tuet "));
+ else if (proc_cmdline_key_streq(key, "space"))
+ assert_se(streq(value, "x y z"));
+ else if (proc_cmdline_key_streq(key, "miepf"))
+ assert_se(streq(value, "uuu"));
+ else if (in_initrd() && *strip && proc_cmdline_key_streq(key, "zumm"))
+ assert_se(!value);
+ else if (in_initrd() && !*strip && proc_cmdline_key_streq(key, "rd.zumm"))
+ assert_se(!value);
+ else
+ assert_not_reached("Bad key!");
+
+ return 0;
+}
+
+static void test_proc_cmdline_given(bool flip_initrd) {
+ log_info("/* %s (flip: %s) */", __func__, yes_no(flip_initrd));
+
+ if (flip_initrd)
+ in_initrd_force(!in_initrd());
+
+ bool t = true, f = false;
+ assert_se(proc_cmdline_parse_given("foo_bar=quux wuff-piep=\"tuet \" rd.zumm space='x y z' miepf=\"uuu\"",
+ parse_item_given, &t, PROC_CMDLINE_STRIP_RD_PREFIX) >= 0);
+
+ assert_se(proc_cmdline_parse_given("foo_bar=quux wuff-piep=\"tuet \" rd.zumm space='x y z' miepf=\"uuu\"",
+ parse_item_given, &f, 0) >= 0);
+
+ if (flip_initrd)
+ in_initrd_force(!in_initrd());
}
static void test_proc_cmdline_get_key(void) {
_cleanup_free_ char *value = NULL;
- putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm");
+ log_info("/* %s */", __func__);
+ assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm spaaace='ö ü ß' ticks=\"''\"") == 0);
assert_se(proc_cmdline_get_key("", 0, &value) == -EINVAL);
assert_se(proc_cmdline_get_key("abc", 0, NULL) == 0);
@@ -73,12 +124,18 @@ static void test_proc_cmdline_get_key(void) {
assert_se(proc_cmdline_get_key("zumm", 0, &value) == 0 && value == NULL);
assert_se(proc_cmdline_get_key("zumm", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && value == NULL);
assert_se(proc_cmdline_get_key("zumm", 0, NULL) > 0);
+
+ assert_se(proc_cmdline_get_key("spaaace", 0, &value) > 0 && streq_ptr(value, "ö ü ß"));
+ value = mfree(value);
+
+ assert_se(proc_cmdline_get_key("ticks", 0, &value) > 0 && streq_ptr(value, "''"));
}
static void test_proc_cmdline_get_bool(void) {
bool value = false;
- putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep");
+ log_info("/* %s */", __func__);
+ assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep") == 0);
assert_se(proc_cmdline_get_bool("", &value) == -EINVAL);
assert_se(proc_cmdline_get_bool("abc", &value) == 0 && value == false);
@@ -93,7 +150,30 @@ static void test_proc_cmdline_get_bool(void) {
assert_se(proc_cmdline_get_bool("quux", &value) == -EINVAL && value == false);
}
+static void test_proc_cmdline_get_key_many(void) {
+ _cleanup_free_ char *value1 = NULL, *value2 = NULL, *value3 = NULL, *value4 = NULL, *value5 = NULL, *value6 = NULL;
+
+ log_info("/* %s */", __func__);
+ assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm SPACE='one two' doubleticks=\" aaa aaa \"") == 0);
+
+ assert_se(proc_cmdline_get_key_many(0,
+ "wuff-piep", &value3,
+ "foo_bar", &value1,
+ "idontexist", &value2,
+ "zumm", &value4,
+ "SPACE", &value5,
+ "doubleticks", &value6) == 4);
+
+ assert_se(streq_ptr(value1, "quux"));
+ assert_se(!value2);
+ assert_se(streq_ptr(value3, "tuet"));
+ assert_se(!value4);
+ assert_se(streq_ptr(value5, "one two"));
+ assert_se(streq_ptr(value6, " aaa aaa "));
+}
+
static void test_proc_cmdline_key_streq(void) {
+ log_info("/* %s */", __func__);
assert_se(proc_cmdline_key_streq("", ""));
assert_se(proc_cmdline_key_streq("a", "a"));
@@ -110,6 +190,7 @@ static void test_proc_cmdline_key_streq(void) {
}
static void test_proc_cmdline_key_startswith(void) {
+ log_info("/* %s */", __func__);
assert_se(proc_cmdline_key_startswith("", ""));
assert_se(proc_cmdline_key_startswith("x", ""));
@@ -124,15 +205,38 @@ static void test_proc_cmdline_key_startswith(void) {
assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx"));
}
+static void test_runlevel_to_target(void) {
+ log_info("/* %s */", __func__);
+
+ in_initrd_force(false);
+ assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
+ assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET));
+ assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL));
+
+ in_initrd_force(true);
+ assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
+ assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("3"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
+}
+
int main(void) {
log_parse_environment();
log_open();
test_proc_cmdline_parse();
+ test_proc_cmdline_override();
+ test_proc_cmdline_given(false);
+ /* Repeat the same thing, but now flip our ininitrdness */
+ test_proc_cmdline_given(true);
test_proc_cmdline_key_streq();
test_proc_cmdline_key_startswith();
test_proc_cmdline_get_key();
test_proc_cmdline_get_bool();
+ test_proc_cmdline_get_key_many();
test_runlevel_to_target();
return 0;
diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c
index fd4d17408d..5c87db08f5 100644
--- a/src/test/test-process-util.c
+++ b/src/test/test-process-util.c
@@ -17,6 +17,7 @@
#include "fd-util.h"
#include "log.h"
#include "macro.h"
+#include "missing.h"
#include "parse-util.h"
#include "process-util.h"
#include "signal-util.h"
@@ -24,6 +25,7 @@
#include "string-util.h"
#include "terminal-util.h"
#include "test-helper.h"
+#include "tests.h"
#include "util.h"
#include "virt.h"
@@ -180,15 +182,24 @@ static void test_get_process_cmdline_harder(void) {
_cleanup_free_ char *line = NULL;
pid_t pid;
- if (geteuid() != 0)
+ if (geteuid() != 0) {
+ log_info("Skipping %s: not root", __func__);
return;
+ }
+
+ if (!have_namespaces()) {
+ log_notice("Testing without namespaces, skipping %s", __func__);
+ return;
+ }
#if HAVE_VALGRIND_VALGRIND_H
/* valgrind patches open(/proc//cmdline)
* so, test_get_process_cmdline_harder fails always
* See https://github.com/systemd/systemd/pull/3555#issuecomment-226564908 */
- if (RUNNING_ON_VALGRIND)
+ if (RUNNING_ON_VALGRIND) {
+ log_info("Skipping %s: running on valgrind", __func__);
return;
+ }
#endif
pid = fork();
@@ -206,15 +217,21 @@ static void test_get_process_cmdline_harder(void) {
assert_se(pid == 0);
assert_se(unshare(CLONE_NEWNS) >= 0);
- assert_se(mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) >= 0);
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
+ log_warning_errno(errno, "mount(..., \"/\", MS_SLAVE|MS_REC, ...) failed: %m");
+ assert_se(IN_SET(errno, EPERM, EACCES));
+ return;
+ }
fd = mkostemp(path, O_CLOEXEC);
assert_se(fd >= 0);
+ /* Note that we don't unmount the following bind-mount at the end of the test because the kernel
+ * will clear up its /proc/PID/ hierarchy automatically as soon as the test stops. */
if (mount(path, "/proc/self/cmdline", "bind", MS_BIND, NULL) < 0) {
/* This happens under selinux… Abort the test in this case. */
log_warning_errno(errno, "mount(..., \"/proc/self/cmdline\", \"bind\", ...) failed: %m");
- assert(errno == EACCES);
+ assert_se(IN_SET(errno, EPERM, EACCES));
return;
}
@@ -394,12 +411,17 @@ static void test_rename_process_now(const char *p, int ret) {
log_info("comm = <%s>", comm);
assert_se(strneq(comm, p, TASK_COMM_LEN-1));
- assert_se(get_process_cmdline(0, 0, false, &cmdline) >= 0);
+ r = get_process_cmdline(0, 0, false, &cmdline);
+ assert_se(r >= 0);
/* we cannot expect cmdline to be renamed properly without privileges */
if (geteuid() == 0) {
- log_info("cmdline = <%s>", cmdline);
- assert_se(strneq(p, cmdline, STRLEN("test-process-util")));
- assert_se(startswith(p, cmdline));
+ if (r == 0 && detect_container() > 0)
+ log_info("cmdline = <%s> (not verified, Running in unprivileged container?)", cmdline);
+ else {
+ log_info("cmdline = <%s>", cmdline);
+ assert_se(strneq(p, cmdline, STRLEN("test-process-util")));
+ assert_se(startswith(p, cmdline));
+ }
} else
log_info("cmdline = <%s> (not verified)", cmdline);
}
@@ -581,9 +603,7 @@ static void test_ioprio_class_from_to_string(void) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
saved_argc = argc;
saved_argv = argv;
diff --git a/src/test/test-random-util.c b/src/test/test-random-util.c
index a4760b54a3..94c431f7e6 100644
--- a/src/test/test-random-util.c
+++ b/src/test/test-random-util.c
@@ -3,15 +3,16 @@
#include "hexdecoct.h"
#include "random-util.h"
#include "log.h"
+#include "tests.h"
-static void test_acquire_random_bytes(bool high_quality_required) {
+static void test_genuine_random_bytes(RandomFlags flags) {
uint8_t buf[16] = {};
unsigned i;
log_info("/* %s */", __func__);
for (i = 1; i < sizeof buf; i++) {
- assert_se(acquire_random_bytes(buf, i, high_quality_required) == 0);
+ assert_se(genuine_random_bytes(buf, i, flags) == 0);
if (i + 1 < sizeof buf)
assert_se(buf[i] == 0);
@@ -19,14 +20,14 @@ static void test_acquire_random_bytes(bool high_quality_required) {
}
}
-static void test_pseudorandom_bytes(void) {
+static void test_pseudo_random_bytes(void) {
uint8_t buf[16] = {};
unsigned i;
log_info("/* %s */", __func__);
for (i = 1; i < sizeof buf; i++) {
- pseudorandom_bytes(buf, i);
+ pseudo_random_bytes(buf, i);
if (i + 1 < sizeof buf)
assert_se(buf[i] == 0);
@@ -34,15 +35,33 @@ static void test_pseudorandom_bytes(void) {
}
}
+static void test_rdrand(void) {
+ int r, i;
+
+ for (i = 0; i < 10; i++) {
+ unsigned long x = 0;
+
+ r = rdrand(&x);
+ if (r < 0) {
+ log_error_errno(r, "RDRAND failed: %m");
+ return;
+ }
+
+ printf("%lx\n", x);
+ }
+}
+
int main(int argc, char **argv) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
+
+ test_genuine_random_bytes(RANDOM_EXTEND_WITH_PSEUDO);
+ test_genuine_random_bytes(0);
+ test_genuine_random_bytes(RANDOM_BLOCK);
+ test_genuine_random_bytes(RANDOM_ALLOW_RDRAND);
- test_acquire_random_bytes(false);
- test_acquire_random_bytes(true);
+ test_pseudo_random_bytes();
- test_pseudorandom_bytes();
+ test_rdrand();
return 0;
}
diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c
index 15701b2712..771719a267 100644
--- a/src/test/test-rlimit-util.c
+++ b/src/test/test-rlimit-util.c
@@ -5,6 +5,7 @@
#include "alloc-util.h"
#include "capability-util.h"
#include "macro.h"
+#include "missing.h"
#include "rlimit-util.h"
#include "string-util.h"
#include "util.h"
diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c
index c986284155..1aa178182b 100644
--- a/src/test/test-sched-prio.c
+++ b/src/test/test-sched-prio.c
@@ -19,20 +19,18 @@ int main(int argc, char *argv[]) {
Service *ser;
int r;
+ test_setup_logging(LOG_INFO);
+
r = enter_cgroup_subroot();
- if (r == -ENOMEDIUM) {
- log_notice_errno(r, "Skipping test: cgroupfs not available");
- return EXIT_TEST_SKIP;
- }
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
/* prepare the test */
- assert_se(set_unit_path(get_testdata_dir("")) >= 0);
+ assert_se(set_unit_path(get_testdata_dir()) >= 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m);
- if (MANAGER_SKIP_TEST(r)) {
- log_notice_errno(r, "Skipping test: manager_new: %m");
- return EXIT_TEST_SKIP;
- }
+ if (MANAGER_SKIP_TEST(r))
+ return log_tests_skipped_errno(r, "manager_new");
assert_se(r >= 0);
assert_se(manager_startup(m, NULL, NULL) >= 0);
diff --git a/src/test/test-sd-hwdb.c b/src/test/test-sd-hwdb.c
new file mode 100644
index 0000000000..17ca6a0e27
--- /dev/null
+++ b/src/test/test-sd-hwdb.c
@@ -0,0 +1,74 @@
+#include "sd-hwdb.h"
+
+#include "alloc-util.h"
+#include "errno.h"
+#include "tests.h"
+
+static int test_failed_enumerate(void) {
+ _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
+ const char *key, *value;
+ int r;
+
+ log_info("/* %s */", __func__);
+
+ r = sd_hwdb_new(&hwdb);
+ if (r == -ENOENT)
+ return r;
+ assert_se(r == 0);
+
+ assert_se(sd_hwdb_seek(hwdb, "no-such-modalias-should-exist") == 0);
+
+ assert_se(sd_hwdb_enumerate(hwdb, &key, &value) == 0);
+ assert_se(sd_hwdb_enumerate(hwdb, &key, NULL) == -EINVAL);
+ assert_se(sd_hwdb_enumerate(hwdb, NULL, &value) == -EINVAL);
+
+ return 0;
+}
+
+#define DELL_MODALIAS \
+ "evdev:atkbd:dmi:bvnXXX:bvrYYY:bdZZZ:svnDellXXX:pnYYY"
+
+static void test_basic_enumerate(void) {
+ _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
+ const char *key, *value;
+ size_t len1 = 0, len2 = 0;
+ int r;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(sd_hwdb_new(&hwdb) == 0);
+
+ assert_se(sd_hwdb_seek(hwdb, DELL_MODALIAS) == 0);
+
+ for (;;) {
+ r = sd_hwdb_enumerate(hwdb, &key, &value);
+ assert(IN_SET(r, 0, 1));
+ if (r == 0)
+ break;
+ assert(key);
+ assert(value);
+ log_debug("A: \"%s\" → \"%s\"", key, value);
+ len1 += strlen(key) + strlen(value);
+ }
+
+ SD_HWDB_FOREACH_PROPERTY(hwdb, DELL_MODALIAS, key, value) {
+ log_debug("B: \"%s\" → \"%s\"", key, value);
+ len2 += strlen(key) + strlen(value);
+ }
+
+ assert_se(len1 == len2);
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ test_setup_logging(LOG_DEBUG);
+
+ r = test_failed_enumerate();
+ if (r < 0)
+ return log_tests_skipped_errno(r, "cannot open hwdb");
+
+ test_basic_enumerate();
+
+ return 0;
+}
diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c
index d82cb5c1c5..fbfeedd536 100644
--- a/src/test/test-seccomp.c
+++ b/src/test/test-seccomp.c
@@ -20,6 +20,7 @@
#include "seccomp-util.h"
#include "set.h"
#include "string-util.h"
+#include "tests.h"
#include "util.h"
#include "virt.h"
@@ -35,6 +36,8 @@ static void test_seccomp_arch_to_string(void) {
uint32_t a, b;
const char *name;
+ log_info("/* %s */", __func__);
+
a = seccomp_arch_native();
assert_se(a > 0);
name = seccomp_arch_to_string(a);
@@ -46,6 +49,8 @@ static void test_seccomp_arch_to_string(void) {
static void test_architecture_table(void) {
const char *n, *n2;
+ log_info("/* %s */", __func__);
+
NULSTR_FOREACH(n,
"native\0"
"x86\0"
@@ -74,6 +79,8 @@ static void test_architecture_table(void) {
}
static void test_syscall_filter_set_find(void) {
+ log_info("/* %s */", __func__);
+
assert_se(!syscall_filter_set_find(NULL));
assert_se(!syscall_filter_set_find(""));
assert_se(!syscall_filter_set_find("quux"));
@@ -88,10 +95,16 @@ static void test_filter_sets(void) {
unsigned i;
int r;
- if (!is_seccomp_available())
+ log_info("/* %s */", __func__);
+
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping %s", __func__);
return;
- if (geteuid() != 0)
+ }
+ if (geteuid() != 0) {
+ log_notice("Not root, skipping %s", __func__);
return;
+ }
for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
pid_t pid;
@@ -104,11 +117,11 @@ static void test_filter_sets(void) {
if (pid == 0) { /* Child? */
int fd;
- /* if we look at the default set (or one that includes it), whitelist instead of blacklist */
+ /* If we look at the default set (or one that includes it), whitelist instead of blacklist */
if (IN_SET(i, SYSCALL_FILTER_SET_DEFAULT, SYSCALL_FILTER_SET_SYSTEM_SERVICE))
- r = seccomp_load_syscall_filter_set(SCMP_ACT_ERRNO(EUCLEAN), syscall_filter_sets + i, SCMP_ACT_ALLOW);
+ r = seccomp_load_syscall_filter_set(SCMP_ACT_ERRNO(EUCLEAN), syscall_filter_sets + i, SCMP_ACT_ALLOW, true);
else
- r = seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + i, SCMP_ACT_ERRNO(EUCLEAN));
+ r = seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + i, SCMP_ACT_ERRNO(EUCLEAN), true);
if (r < 0)
_exit(EXIT_FAILURE);
@@ -128,11 +141,50 @@ static void test_filter_sets(void) {
}
}
+static void test_filter_sets_ordered(void) {
+ size_t i;
+
+ log_info("/* %s */", __func__);
+
+ /* Ensure "@default" always remains at the beginning of the list */
+ assert_se(SYSCALL_FILTER_SET_DEFAULT == 0);
+ assert_se(streq(syscall_filter_sets[0].name, "@default"));
+
+ for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
+ const char *k, *p = NULL;
+
+ /* Make sure each group has a description */
+ assert_se(!isempty(syscall_filter_sets[0].help));
+
+ /* Make sure the groups are ordered alphabetically, except for the first entry */
+ assert_se(i < 2 || strcmp(syscall_filter_sets[i-1].name, syscall_filter_sets[i].name) < 0);
+
+ NULSTR_FOREACH(k, syscall_filter_sets[i].value) {
+
+ /* Ensure each syscall list is in itself ordered, but groups before names */
+ assert_se(!p ||
+ (*p == '@' && *k != '@') ||
+ (((*p == '@' && *k == '@') ||
+ (*p != '@' && *k != '@')) &&
+ strcmp(p, k) < 0));
+
+ p = k;
+ }
+ }
+}
+
static void test_restrict_namespace(void) {
char *s = NULL;
unsigned long ul;
pid_t pid;
+ if (!have_namespaces()) {
+ log_notice("Testing without namespaces, skipping %s", __func__);
+ return;
+ }
+
+ log_info("/* %s */", __func__);
+
assert_se(namespace_flags_to_string(0, &s) == 0 && streq(s, ""));
s = mfree(s);
assert_se(namespace_flags_to_string(CLONE_NEWNS, &s) == 0 && streq(s, "mnt"));
@@ -160,10 +212,14 @@ static void test_restrict_namespace(void) {
assert_se(namespace_flags_from_string(s, &ul) == 0 && ul == NAMESPACE_FLAGS_ALL);
s = mfree(s);
- if (!is_seccomp_available())
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping remaining tests in %s", __func__);
return;
- if (geteuid() != 0)
+ }
+ if (geteuid() != 0) {
+ log_notice("Not root, skipping remaining tests in %s", __func__);
return;
+ }
pid = fork();
assert_se(pid >= 0);
@@ -223,13 +279,22 @@ static void test_restrict_namespace(void) {
static void test_protect_sysctl(void) {
pid_t pid;
- if (!is_seccomp_available())
+ log_info("/* %s */", __func__);
+
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping %s", __func__);
return;
- if (geteuid() != 0)
+ }
+ if (geteuid() != 0) {
+ log_notice("Not root, skipping %s", __func__);
return;
+ }
- if (detect_container() > 0) /* in containers _sysctl() is likely missing anyway */
+ /* in containers _sysctl() is likely missing anyway */
+ if (detect_container() > 0) {
+ log_notice("Testing in container, skipping %s", __func__);
return;
+ }
pid = fork();
assert_se(pid >= 0);
@@ -256,10 +321,16 @@ static void test_protect_sysctl(void) {
static void test_restrict_address_families(void) {
pid_t pid;
- if (!is_seccomp_available())
+ log_info("/* %s */", __func__);
+
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping %s", __func__);
return;
- if (geteuid() != 0)
+ }
+ if (geteuid() != 0) {
+ log_notice("Not root, skipping %s", __func__);
return;
+ }
pid = fork();
assert_se(pid >= 0);
@@ -339,13 +410,22 @@ static void test_restrict_address_families(void) {
static void test_restrict_realtime(void) {
pid_t pid;
- if (!is_seccomp_available())
+ log_info("/* %s */", __func__);
+
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping %s", __func__);
return;
- if (geteuid() != 0)
+ }
+ if (geteuid() != 0) {
+ log_notice("Not root, skipping %s", __func__);
return;
+ }
- if (detect_container() > 0) /* in containers RT privs are likely missing anyway */
+ /* in containers RT privs are likely missing anyway */
+ if (detect_container() > 0) {
+ log_notice("Testing in container, skipping %s", __func__);
return;
+ }
pid = fork();
assert_se(pid >= 0);
@@ -377,10 +457,16 @@ static void test_restrict_realtime(void) {
static void test_memory_deny_write_execute_mmap(void) {
pid_t pid;
- if (!is_seccomp_available())
+ log_info("/* %s */", __func__);
+
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping %s", __func__);
return;
- if (geteuid() != 0)
+ }
+ if (geteuid() != 0) {
+ log_notice("Not root, skipping %s", __func__);
return;
+ }
pid = fork();
assert_se(pid >= 0);
@@ -421,10 +507,16 @@ static void test_memory_deny_write_execute_shmat(void) {
int shmid;
pid_t pid;
- if (!is_seccomp_available())
+ log_info("/* %s */", __func__);
+
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping %s", __func__);
return;
- if (geteuid() != 0)
+ }
+ if (geteuid() != 0) {
+ log_notice("Not root, skipping %s", __func__);
return;
+ }
shmid = shmget(IPC_PRIVATE, page_size(), 0);
assert_se(shmid >= 0);
@@ -467,10 +559,16 @@ static void test_memory_deny_write_execute_shmat(void) {
static void test_restrict_archs(void) {
pid_t pid;
- if (!is_seccomp_available())
+ log_info("/* %s */", __func__);
+
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping %s", __func__);
return;
- if (geteuid() != 0)
+ }
+ if (geteuid() != 0) {
+ log_notice("Not root, skipping %s", __func__);
return;
+ }
pid = fork();
assert_se(pid >= 0);
@@ -501,10 +599,16 @@ static void test_restrict_archs(void) {
static void test_load_syscall_filter_set_raw(void) {
pid_t pid;
- if (!is_seccomp_available())
+ log_info("/* %s */", __func__);
+
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping %s", __func__);
return;
- if (geteuid() != 0)
+ }
+ if (geteuid() != 0) {
+ log_notice("Not root, skipping %s", __func__);
return;
+ }
pid = fork();
assert_se(pid >= 0);
@@ -515,7 +619,7 @@ static void test_load_syscall_filter_set_raw(void) {
assert_se(access("/", F_OK) >= 0);
assert_se(poll(NULL, 0, 0) == 0);
- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, NULL, SCMP_ACT_KILL) >= 0);
+ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, NULL, SCMP_ACT_KILL, true) >= 0);
assert_se(access("/", F_OK) >= 0);
assert_se(poll(NULL, 0, 0) == 0);
@@ -526,7 +630,7 @@ static void test_load_syscall_filter_set_raw(void) {
assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_faccessat + 1), INT_TO_PTR(-1)) >= 0);
#endif
- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0);
+ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN), true) >= 0);
assert_se(access("/", F_OK) < 0);
assert_se(errno == EUCLEAN);
@@ -542,7 +646,7 @@ static void test_load_syscall_filter_set_raw(void) {
assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_faccessat + 1), INT_TO_PTR(EILSEQ)) >= 0);
#endif
- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0);
+ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN), true) >= 0);
assert_se(access("/", F_OK) < 0);
assert_se(errno == EILSEQ);
@@ -558,7 +662,7 @@ static void test_load_syscall_filter_set_raw(void) {
assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_ppoll + 1), INT_TO_PTR(-1)) >= 0);
#endif
- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0);
+ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH), true) >= 0);
assert_se(access("/", F_OK) < 0);
assert_se(errno == EILSEQ);
@@ -575,7 +679,7 @@ static void test_load_syscall_filter_set_raw(void) {
assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_ppoll + 1), INT_TO_PTR(EILSEQ)) >= 0);
#endif
- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0);
+ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH), true) >= 0);
assert_se(access("/", F_OK) < 0);
assert_se(errno == EILSEQ);
@@ -593,10 +697,16 @@ static void test_lock_personality(void) {
unsigned long current;
pid_t pid;
- if (!is_seccomp_available())
+ log_info("/* %s */", __func__);
+
+ if (!is_seccomp_available()) {
+ log_notice("Seccomp not available, skipping %s", __func__);
return;
- if (geteuid() != 0)
+ }
+ if (geteuid() != 0) {
+ log_notice("Not root, skipping %s", __func__);
return;
+ }
assert_se(opinionated_personality(&current) >= 0);
@@ -636,44 +746,14 @@ static void test_lock_personality(void) {
assert_se(wait_for_terminate_and_check("lockpersonalityseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
}
-static void test_filter_sets_ordered(void) {
- size_t i;
-
- /* Ensure "@default" always remains at the beginning of the list */
- assert_se(SYSCALL_FILTER_SET_DEFAULT == 0);
- assert_se(streq(syscall_filter_sets[0].name, "@default"));
-
- for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
- const char *k, *p = NULL;
-
- /* Make sure each group has a description */
- assert_se(!isempty(syscall_filter_sets[0].help));
-
- /* Make sure the groups are ordered alphabetically, except for the first entry */
- assert_se(i < 2 || strcmp(syscall_filter_sets[i-1].name, syscall_filter_sets[i].name) < 0);
-
- NULSTR_FOREACH(k, syscall_filter_sets[i].value) {
-
- /* Ensure each syscall list is in itself ordered, but groups before names */
- assert_se(!p ||
- (*p == '@' && *k != '@') ||
- (((*p == '@' && *k == '@') ||
- (*p != '@' && *k != '@')) &&
- strcmp(p, k) < 0));
-
- p = k;
- }
- }
-}
-
int main(int argc, char *argv[]) {
-
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
test_seccomp_arch_to_string();
test_architecture_table();
test_syscall_filter_set_find();
test_filter_sets();
+ test_filter_sets_ordered();
test_restrict_namespace();
test_protect_sysctl();
test_restrict_address_families();
@@ -683,7 +763,6 @@ int main(int argc, char *argv[]) {
test_restrict_archs();
test_load_syscall_filter_set_raw();
test_lock_personality();
- test_filter_sets_ordered();
return 0;
}
diff --git a/src/test/test-selinux.c b/src/test/test-selinux.c
index 6caeb843f3..59b4f71946 100644
--- a/src/test/test-selinux.c
+++ b/src/test/test-selinux.c
@@ -7,6 +7,7 @@
#include "log.h"
#include "selinux-util.h"
#include "string-util.h"
+#include "tests.h"
#include "time-util.h"
#include "util.h"
@@ -92,8 +93,7 @@ int main(int argc, char **argv) {
if (argc >= 2)
path = argv[1];
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
+ test_setup_logging(LOG_DEBUG);
test_testing();
test_loading();
diff --git a/src/test/test-serialize.c b/src/test/test-serialize.c
new file mode 100644
index 0000000000..a57d5db2b1
--- /dev/null
+++ b/src/test/test-serialize.c
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "log.h"
+#include "serialize.h"
+#include "strv.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+
+char long_string[LONG_LINE_MAX+1];
+
+static void test_serialize_item(void) {
+ _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-serialize.XXXXXX";
+ _cleanup_fclose_ FILE *f = NULL;
+
+ assert_se(fmkostemp_safe(fn, "r+", &f) == 0);
+ log_info("/* %s (%s) */", __func__, fn);
+
+ assert_se(serialize_item(f, "a", NULL) == 0);
+ assert_se(serialize_item(f, "a", "bbb") == 1);
+ assert_se(serialize_item(f, "a", "bbb") == 1);
+ assert_se(serialize_item(f, "a", long_string) == -EINVAL);
+ assert_se(serialize_item(f, long_string, "a") == -EINVAL);
+ assert_se(serialize_item(f, long_string, long_string) == -EINVAL);
+
+ rewind(f);
+
+ _cleanup_free_ char *line1 = NULL, *line2 = NULL, *line3 = NULL;
+ assert_se(read_line(f, LONG_LINE_MAX, &line1) > 0);
+ assert_se(streq(line1, "a=bbb"));
+ assert_se(read_line(f, LONG_LINE_MAX, &line2) > 0);
+ assert_se(streq(line2, "a=bbb"));
+ assert_se(read_line(f, LONG_LINE_MAX, &line3) == 0);
+ assert_se(streq(line3, ""));
+}
+
+static void test_serialize_item_escaped(void) {
+ _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-serialize.XXXXXX";
+ _cleanup_fclose_ FILE *f = NULL;
+
+ assert_se(fmkostemp_safe(fn, "r+", &f) == 0);
+ log_info("/* %s (%s) */", __func__, fn);
+
+ assert_se(serialize_item_escaped(f, "a", NULL) == 0);
+ assert_se(serialize_item_escaped(f, "a", "bbb") == 1);
+ assert_se(serialize_item_escaped(f, "a", "bbb") == 1);
+ assert_se(serialize_item_escaped(f, "a", long_string) == -EINVAL);
+ assert_se(serialize_item_escaped(f, long_string, "a") == -EINVAL);
+ assert_se(serialize_item_escaped(f, long_string, long_string) == -EINVAL);
+
+ rewind(f);
+
+ _cleanup_free_ char *line1 = NULL, *line2 = NULL, *line3 = NULL;
+ assert_se(read_line(f, LONG_LINE_MAX, &line1) > 0);
+ assert_se(streq(line1, "a=bbb"));
+ assert_se(read_line(f, LONG_LINE_MAX, &line2) > 0);
+ assert_se(streq(line2, "a=bbb"));
+ assert_se(read_line(f, LONG_LINE_MAX, &line3) == 0);
+ assert_se(streq(line3, ""));
+}
+
+static void test_serialize_usec(void) {
+ _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-serialize.XXXXXX";
+ _cleanup_fclose_ FILE *f = NULL;
+
+ assert_se(fmkostemp_safe(fn, "r+", &f) == 0);
+ log_info("/* %s (%s) */", __func__, fn);
+
+ assert_se(serialize_usec(f, "usec1", USEC_INFINITY) == 0);
+ assert_se(serialize_usec(f, "usec2", 0) == 1);
+ assert_se(serialize_usec(f, "usec3", USEC_INFINITY-1) == 1);
+
+ rewind(f);
+
+ _cleanup_free_ char *line1 = NULL, *line2 = NULL;
+ usec_t x;
+
+ assert_se(read_line(f, LONG_LINE_MAX, &line1) > 0);
+ assert_se(streq(line1, "usec2=0"));
+ assert_se(deserialize_usec(line1 + 6, &x) == 0);
+ assert_se(x == 0);
+
+ assert_se(read_line(f, LONG_LINE_MAX, &line2) > 0);
+ assert_se(startswith(line2, "usec3="));
+ assert_se(deserialize_usec(line2 + 6, &x) == 0);
+ assert_se(x == USEC_INFINITY-1);
+}
+
+static void test_serialize_strv(void) {
+ _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-serialize.XXXXXX";
+ _cleanup_fclose_ FILE *f = NULL;
+
+ char **strv = STRV_MAKE("a", "b", "foo foo",
+ "nasty1 \"",
+ "\"nasty2 ",
+ "nasty3 '",
+ "\"nasty4 \"",
+ "nasty5\n",
+ "\nnasty5\nfoo=bar",
+ "\nnasty5\nfoo=bar");
+
+ assert_se(fmkostemp_safe(fn, "r+", &f) == 0);
+ log_info("/* %s (%s) */", __func__, fn);
+
+ assert_se(serialize_strv(f, "strv1", NULL) == 0);
+ assert_se(serialize_strv(f, "strv2", STRV_MAKE_EMPTY) == 0);
+ assert_se(serialize_strv(f, "strv3", strv) == 1);
+ assert_se(serialize_strv(f, "strv4", STRV_MAKE(long_string)) == -EINVAL);
+
+ rewind(f);
+
+ _cleanup_strv_free_ char **strv2 = NULL;
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ int r;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r == 0)
+ break;
+ assert_se(r > 0);
+
+ const char *t = startswith(line, "strv3=");
+ assert_se(t);
+
+ char *un;
+ assert_se(cunescape(t, 0, &un) >= 0);
+ assert_se(strv_consume(&strv2, un) >= 0);
+ }
+
+ assert_se(strv_equal(strv, strv2));
+}
+
+static void test_deserialize_environment(void) {
+ _cleanup_strv_free_ char **env;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(env = strv_new("A=1"));
+
+ assert_se(deserialize_environment("B=2", &env) >= 0);
+ assert_se(deserialize_environment("FOO%%=a\\177b\\nc\\td e", &env) >= 0);
+
+ assert_se(strv_equal(env, STRV_MAKE("A=1", "B=2", "FOO%%=a\177b\nc\td e")));
+
+ assert_se(deserialize_environment("foo\\", &env) < 0);
+ assert_se(deserialize_environment("bar\\_baz", &env) < 0);
+}
+
+static void test_serialize_environment(void) {
+ _cleanup_strv_free_ char **env = NULL, **env2 = NULL;
+ _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-env-util.XXXXXXX";
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert_se(fmkostemp_safe(fn, "r+", &f) == 0);
+ log_info("/* %s (%s) */", __func__, fn);
+
+ assert_se(env = strv_new("A=1",
+ "B=2",
+ "C=ąęółń",
+ "D=D=a\\x0Ab",
+ "FOO%%=a\177b\nc\td e"));
+
+ assert_se(serialize_strv(f, "env", env) == 1);
+ assert_se(fflush_and_check(f) == 0);
+
+ rewind(f);
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ const char *l;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ assert_se(r >= 0);
+
+ if (r == 0)
+ break;
+
+ l = strstrip(line);
+
+ assert_se(startswith(l, "env="));
+
+ r = deserialize_environment(l+4, &env2);
+ assert_se(r >= 0);
+ }
+ assert_se(feof(f));
+
+ assert_se(strv_equal(env, env2));
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_INFO);
+
+ memset(long_string, 'x', sizeof(long_string)-1);
+ char_array_0(long_string);
+
+ test_serialize_item();
+ test_serialize_item_escaped();
+ test_serialize_usec();
+ test_serialize_strv();
+ test_deserialize_environment();
+ test_serialize_environment();
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/test-set-disable-mempool.c b/src/test/test-set-disable-mempool.c
new file mode 100644
index 0000000000..aea83d2679
--- /dev/null
+++ b/src/test/test-set-disable-mempool.c
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <pthread.h>
+
+#include "process-util.h"
+#include "set.h"
+#include "tests.h"
+
+#define NUM 100
+
+static void* thread(void *p) {
+ Set **s = p;
+
+ assert_se(s);
+ assert_se(*s);
+
+ assert_se(!is_main_thread());
+ assert_se(set_size(*s) == NUM);
+ *s = set_free(*s);
+
+ return NULL;
+}
+
+static void test_one(const char *val) {
+ pthread_t t;
+ int x[NUM] = {};
+ unsigned i;
+ Set *s;
+
+ log_info("Testing with SYSTEMD_MEMPOOL=%s", val);
+ assert_se(setenv("SYSTEMD_MEMPOOL", val, true) == 0);
+ assert_se(is_main_thread());
+
+ assert_se(s = set_new(NULL));
+ for (i = 0; i < NUM; i++)
+ assert_se(set_put(s, &x[i]));
+
+ assert_se(pthread_create(&t, NULL, thread, &s) == 0);
+ assert_se(pthread_join(t, NULL) == 0);
+
+ assert_se(!s);
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_DEBUG);
+
+ test_one("0");
+ /* The value $SYSTEMD_MEMPOOL= is cached. So the following
+ * test should also succeed. */
+ test_one("1");
+
+ return 0;
+}
diff --git a/src/test/test-set.c b/src/test/test-set.c
index 6307403e4c..340edeb65f 100644
--- a/src/test/test-set.c
+++ b/src/test/test-set.c
@@ -45,6 +45,24 @@ static void test_set_free_with_destructor(void) {
assert_se(items[3].seen == 0);
}
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_hash_ops, void, trivial_hash_func, trivial_compare_func, Item, item_seen);
+
+static void test_set_free_with_hash_ops(void) {
+ Set *m;
+ struct Item items[4] = {};
+ unsigned i;
+
+ assert_se(m = set_new(&item_hash_ops));
+ for (i = 0; i < ELEMENTSOF(items) - 1; i++)
+ assert_se(set_put(m, items + i) == 1);
+
+ m = set_free(m);
+ assert_se(items[0].seen == 1);
+ assert_se(items[1].seen == 1);
+ assert_se(items[2].seen == 1);
+ assert_se(items[3].seen == 0);
+}
+
static void test_set_put(void) {
_cleanup_set_free_ Set *m = NULL;
@@ -64,6 +82,7 @@ static void test_set_put(void) {
int main(int argc, const char *argv[]) {
test_set_steal_first();
test_set_free_with_destructor();
+ test_set_free_with_hash_ops();
test_set_put();
return 0;
diff --git a/src/test/test-sigbus.c b/src/test/test-sigbus.c
index c9343364d4..d2666dd1d8 100644
--- a/src/test/test-sigbus.c
+++ b/src/test/test-sigbus.c
@@ -2,12 +2,14 @@
#include <sys/mman.h>
+#if HAVE_VALGRIND_VALGRIND_H
+# include <valgrind/valgrind.h>
+#endif
+
#include "fd-util.h"
#include "sigbus.h"
+#include "tests.h"
#include "util.h"
-#if HAVE_VALGRIND_VALGRIND_H
-#include <valgrind/valgrind.h>
-#endif
int main(int argc, char *argv[]) {
_cleanup_close_ int fd = -1;
@@ -15,14 +17,16 @@ int main(int argc, char *argv[]) {
void *addr = NULL;
uint8_t *p;
+ test_setup_logging(LOG_INFO);
+
+#if HAS_FEATURE_ADDRESS_SANITIZER
+ return log_tests_skipped("address-sanitizer is enabled");
+#endif
#if HAVE_VALGRIND_VALGRIND_H
if (RUNNING_ON_VALGRIND)
- return EXIT_TEST_SKIP;
+ return log_tests_skipped("This test cannot run on valgrind");
#endif
-#ifdef __SANITIZE_ADDRESS__
- return EXIT_TEST_SKIP;
-#endif
sigbus_install();
assert_se(sigbus_pop(&addr) == 0);
diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c
index 5286442e26..2a6d5e765a 100644
--- a/src/test/test-sleep.c
+++ b/src/test/test-sleep.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <inttypes.h>
#include <linux/fiemap.h>
#include <stdio.h>
@@ -7,13 +8,16 @@
#include "log.h"
#include "sleep-config.h"
#include "strv.h"
+#include "tests.h"
#include "util.h"
static void test_parse_sleep_config(void) {
const char *verb;
+ log_info("/* %s */", __func__);
+
FOREACH_STRING(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate")
- assert_se(parse_sleep_config(verb, NULL, NULL, NULL) == 0);
+ assert_se(parse_sleep_config(verb, NULL, NULL, NULL, NULL) == 0);
}
static int test_fiemap(const char *path) {
@@ -21,42 +25,44 @@ static int test_fiemap(const char *path) {
_cleanup_close_ int fd = -1;
int r;
+ log_info("/* %s */", __func__);
+
fd = open(path, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
return log_error_errno(errno, "failed to open %s: %m", path);
r = read_fiemap(fd, &fiemap);
- if (r == -EOPNOTSUPP) {
- log_info("Skipping test, not supported");
- exit(EXIT_TEST_SKIP);
- }
+ if (r == -EOPNOTSUPP)
+ exit(log_tests_skipped("Not supported"));
if (r < 0)
return log_error_errno(r, "Unable to read extent map for '%s': %m", path);
log_info("extent map information for %s:", path);
- log_info("\t start: %llu", fiemap->fm_start);
- log_info("\t length: %llu", fiemap->fm_length);
- log_info("\t flags: %u", fiemap->fm_flags);
- log_info("\t number of mapped extents: %u", fiemap->fm_mapped_extents);
- log_info("\t extent count: %u", fiemap->fm_extent_count);
+ log_info("\t start: %" PRIu64, (uint64_t) fiemap->fm_start);
+ log_info("\t length: %" PRIu64, (uint64_t) fiemap->fm_length);
+ log_info("\t flags: %" PRIu32, fiemap->fm_flags);
+ log_info("\t number of mapped extents: %" PRIu32, fiemap->fm_mapped_extents);
+ log_info("\t extent count: %" PRIu32, fiemap->fm_extent_count);
if (fiemap->fm_extent_count > 0)
- log_info("\t first extent location: %llu",
- fiemap->fm_extents[0].fe_physical / page_size());
+ log_info("\t first extent location: %" PRIu64,
+ (uint64_t) (fiemap->fm_extents[0].fe_physical / page_size()));
return 0;
}
static void test_sleep(void) {
_cleanup_strv_free_ char
- **standby = strv_new("standby", NULL),
- **mem = strv_new("mem", NULL),
- **disk = strv_new("disk", NULL),
- **suspend = strv_new("suspend", NULL),
- **reboot = strv_new("reboot", NULL),
- **platform = strv_new("platform", NULL),
- **shutdown = strv_new("shutdown", NULL),
- **freez = strv_new("freeze", NULL);
+ **standby = strv_new("standby"),
+ **mem = strv_new("mem"),
+ **disk = strv_new("disk"),
+ **suspend = strv_new("suspend"),
+ **reboot = strv_new("reboot"),
+ **platform = strv_new("platform"),
+ **shutdown = strv_new("shutdown"),
+ **freez = strv_new("freeze");
int r;
- log_info("/* configuration */");
+ log_info("/* %s */", __func__);
+
+ log_info("/= configuration =/");
log_info("Standby configured: %s", yes_no(can_sleep_state(standby) > 0));
log_info("Suspend configured: %s", yes_no(can_sleep_state(mem) > 0));
log_info("Hibernate configured: %s", yes_no(can_sleep_state(disk) > 0));
@@ -66,7 +72,7 @@ static void test_sleep(void) {
log_info("Hibernate+Shutdown configured: %s", yes_no(can_sleep_disk(shutdown) > 0));
log_info("Freeze configured: %s", yes_no(can_sleep_state(freez) > 0));
- log_info("/* running system */");
+ log_info("/= running system =/");
r = can_sleep("suspend");
log_info("Suspend configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r));
r = can_sleep("hibernate");
@@ -80,8 +86,7 @@ static void test_sleep(void) {
int main(int argc, char* argv[]) {
int i, r = 0, k;
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_INFO);
if (getuid() != 0)
log_warning("This program is unlikely to work for unprivileged users");
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index ac2ea52a5c..07ff487834 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -6,16 +6,24 @@
#include "alloc-util.h"
#include "async.h"
+#include "escape.h"
+#include "exit-status.h"
#include "fd-util.h"
#include "in-addr-util.h"
+#include "io-util.h"
#include "log.h"
#include "macro.h"
+#include "missing_network.h"
#include "process-util.h"
#include "socket-util.h"
#include "string-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "util.h"
static void test_ifname_valid(void) {
+ log_info("/* %s */", __func__);
+
assert(ifname_valid("foo"));
assert(ifname_valid("eth0"));
@@ -38,61 +46,121 @@ static void test_ifname_valid(void) {
assert(!ifname_valid("xxxxxxxxxxxxxxxx"));
}
-static void test_socket_address_parse(void) {
+static void test_socket_address_parse_one(const char *in, int ret, int family, const char *expected) {
SocketAddress a;
+ _cleanup_free_ char *out = NULL;
+ int r;
+
+ r = socket_address_parse(&a, in);
+ if (r >= 0)
+ assert_se(socket_address_print(&a, &out) >= 0);
+
+ log_info("\"%s\" → %s → \"%s\" (expect \"%s\")", in,
+ r >= 0 ? "✓" : "✗", empty_to_dash(out), r >= 0 ? expected ?: in : "-");
+ assert_se(r == ret);
+ if (r >= 0) {
+ assert_se(a.sockaddr.sa.sa_family == family);
+ assert_se(streq(out, expected ?: in));
+ }
+}
+
+#define SUN_PATH_LEN (sizeof(((struct sockaddr_un){}).sun_path))
+assert_cc(sizeof(((struct sockaddr_un){}).sun_path) == 108);
- assert_se(socket_address_parse(&a, "junk") < 0);
- assert_se(socket_address_parse(&a, "192.168.1.1") < 0);
- assert_se(socket_address_parse(&a, ".168.1.1") < 0);
- assert_se(socket_address_parse(&a, "989.168.1.1") < 0);
- assert_se(socket_address_parse(&a, "192.168.1.1:65536") < 0);
- assert_se(socket_address_parse(&a, "192.168.1.1:0") < 0);
- assert_se(socket_address_parse(&a, "0") < 0);
- assert_se(socket_address_parse(&a, "65536") < 0);
+static void test_socket_address_parse(void) {
+ log_info("/* %s */", __func__);
+
+ test_socket_address_parse_one("junk", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("192.168.1.1", -EINVAL, 0, NULL);
+ test_socket_address_parse_one(".168.1.1", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("989.168.1.1", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("192.168.1.1:65536", -ERANGE, 0, NULL);
+ test_socket_address_parse_one("192.168.1.1:0", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("0", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("65536", -ERANGE, 0, NULL);
- assert_se(socket_address_parse(&a, "65535") >= 0);
+ const int default_family = socket_ipv6_is_supported() ? AF_INET6 : AF_INET;
+
+ test_socket_address_parse_one("65535", 0, default_family, "[::]:65535");
/* The checks below will pass even if ipv6 is disabled in
* kernel. The underlying glibc's inet_pton() is just a string
* parser and doesn't make any syscalls. */
- assert_se(socket_address_parse(&a, "[::1]") < 0);
- assert_se(socket_address_parse(&a, "[::1]8888") < 0);
- assert_se(socket_address_parse(&a, "::1") < 0);
- assert_se(socket_address_parse(&a, "[::1]:0") < 0);
- assert_se(socket_address_parse(&a, "[::1]:65536") < 0);
- assert_se(socket_address_parse(&a, "[a:b:1]:8888") < 0);
+ test_socket_address_parse_one("[::1]", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("[::1]8888", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("::1", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("[::1]:0", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("[::1]:65536", -ERANGE, 0, NULL);
+ test_socket_address_parse_one("[a:b:1]:8888", -EINVAL, 0, NULL);
- assert_se(socket_address_parse(&a, "8888") >= 0);
- assert_se(a.sockaddr.sa.sa_family == (socket_ipv6_is_supported() ? AF_INET6 : AF_INET));
+ test_socket_address_parse_one("8888", 0, default_family, "[::]:8888");
+ test_socket_address_parse_one("[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888", 0, AF_INET6,
+ "[2001:db8:0:85a3::ac1f:8001]:8888");
+ test_socket_address_parse_one("[::1]:8888", 0, AF_INET6, NULL);
+ test_socket_address_parse_one("192.168.1.254:8888", 0, AF_INET, NULL);
+ test_socket_address_parse_one("/foo/bar", 0, AF_UNIX, NULL);
+ test_socket_address_parse_one("/", 0, AF_UNIX, NULL);
+ test_socket_address_parse_one("@abstract", 0, AF_UNIX, NULL);
- assert_se(socket_address_parse(&a, "[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_INET6);
+ {
+ char aaa[SUN_PATH_LEN + 1] = "@";
- assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_INET6);
+ memset(aaa + 1, 'a', SUN_PATH_LEN - 1);
+ char_array_0(aaa);
- assert_se(socket_address_parse(&a, "192.168.1.254:8888") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_INET);
+ test_socket_address_parse_one(aaa, -EINVAL, 0, NULL);
- assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_UNIX);
+ aaa[SUN_PATH_LEN - 1] = '\0';
+ test_socket_address_parse_one(aaa, 0, AF_UNIX, NULL);
+ }
- assert_se(socket_address_parse(&a, "@abstract") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_UNIX);
+ test_socket_address_parse_one("vsock:2:1234", 0, AF_VSOCK, NULL);
+ test_socket_address_parse_one("vsock::1234", 0, AF_VSOCK, NULL);
+ test_socket_address_parse_one("vsock:2:1234x", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("vsock:2x:1234", -EINVAL, 0, NULL);
+ test_socket_address_parse_one("vsock:2", -EINVAL, 0, NULL);
+}
- assert_se(socket_address_parse(&a, "vsock::1234") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_VSOCK);
- assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
- assert_se(a.sockaddr.sa.sa_family == AF_VSOCK);
- assert_se(socket_address_parse(&a, "vsock:2:1234x") < 0);
- assert_se(socket_address_parse(&a, "vsock:2x:1234") < 0);
- assert_se(socket_address_parse(&a, "vsock:2") < 0);
+static void test_socket_print_unix_one(const char *in, size_t len_in, const char *expected) {
+ _cleanup_free_ char *out = NULL, *c = NULL;
+
+ SocketAddress a = { .sockaddr = { .un = { .sun_family = AF_UNIX } },
+ .size = offsetof(struct sockaddr_un, sun_path) + len_in,
+ .type = SOCK_STREAM,
+ };
+ memcpy(a.sockaddr.un.sun_path, in, len_in);
+
+ assert_se(socket_address_print(&a, &out) >= 0);
+ assert_se(c = cescape(in));
+ log_info("\"%s\" → \"%s\" (expect \"%s\")", in, out, expected);
+ assert_se(streq(out, expected));
+}
+
+static void test_socket_print_unix(void) {
+ log_info("/* %s */", __func__);
+
+ /* Some additional tests for abstract addresses which we don't parse */
+
+ test_socket_print_unix_one("\0\0\0\0", 4, "@\\000\\000\\000");
+ test_socket_print_unix_one("@abs", 5, "@abs");
+ test_socket_print_unix_one("\n", 2, "\\n");
+ test_socket_print_unix_one("", 1, "<unnamed>");
+ test_socket_print_unix_one("\0", 1, "<unnamed>");
+ test_socket_print_unix_one("\0_________________________there's 108 characters in this string_____________________________________________", 108,
+ "@_________________________there\\'s 108 characters in this string_____________________________________________");
+ test_socket_print_unix_one("////////////////////////////////////////////////////////////////////////////////////////////////////////////", 108,
+ "////////////////////////////////////////////////////////////////////////////////////////////////////////////");
+ test_socket_print_unix_one("////////////////////////////////////////////////////////////////////////////////////////////////////////////", 109,
+ "////////////////////////////////////////////////////////////////////////////////////////////////////////////");
+ test_socket_print_unix_one("\0\a\b\n\255", 6, "@\\a\\b\\n\\255\\000");
}
static void test_socket_address_parse_netlink(void) {
SocketAddress a;
+ log_info("/* %s */", __func__);
+
assert_se(socket_address_parse_netlink(&a, "junk") < 0);
assert_se(socket_address_parse_netlink(&a, "") < 0);
@@ -116,8 +184,9 @@ static void test_socket_address_parse_netlink(void) {
}
static void test_socket_address_equal(void) {
- SocketAddress a;
- SocketAddress b;
+ SocketAddress a, b;
+
+ log_info("/* %s */", __func__);
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_parse(&b, "192.168.1.1:888") >= 0);
@@ -167,6 +236,8 @@ static void test_socket_address_equal(void) {
static void test_socket_address_get_path(void) {
SocketAddress a;
+ log_info("/* %s */", __func__);
+
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(!socket_address_get_path(&a));
@@ -186,6 +257,8 @@ static void test_socket_address_get_path(void) {
static void test_socket_address_is(void) {
SocketAddress a;
+ log_info("/* %s */", __func__);
+
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_is(&a, "192.168.1.1:8888", SOCK_STREAM));
assert_se(!socket_address_is(&a, "route", SOCK_STREAM));
@@ -195,6 +268,8 @@ static void test_socket_address_is(void) {
static void test_socket_address_is_netlink(void) {
SocketAddress a;
+ log_info("/* %s */", __func__);
+
assert_se(socket_address_parse_netlink(&a, "route 10") >= 0);
assert_se(socket_address_is_netlink(&a, "route 10"));
assert_se(!socket_address_is_netlink(&a, "192.168.1.1:8888"));
@@ -202,9 +277,10 @@ static void test_socket_address_is_netlink(void) {
}
static void test_in_addr_is_null(void) {
-
union in_addr_union i = {};
+ log_info("/* %s */", __func__);
+
assert_se(in_addr_is_null(AF_INET, &i) == true);
assert_se(in_addr_is_null(AF_INET6, &i) == true);
@@ -225,6 +301,7 @@ static void test_in_addr_prefix_intersect_one(unsigned f, const char *a, unsigne
}
static void test_in_addr_prefix_intersect(void) {
+ log_info("/* %s */", __func__);
test_in_addr_prefix_intersect_one(AF_INET, "255.255.255.255", 32, "255.255.255.254", 32, 0);
test_in_addr_prefix_intersect_one(AF_INET, "255.255.255.255", 0, "255.255.255.255", 32, 1);
@@ -266,6 +343,7 @@ static void test_in_addr_prefix_next_one(unsigned f, const char *before, unsigne
}
static void test_in_addr_prefix_next(void) {
+ log_info("/* %s */", __func__);
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 24, "192.168.1.0");
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 16, "192.169.0.0");
@@ -285,7 +363,6 @@ static void test_in_addr_prefix_next(void) {
test_in_addr_prefix_next_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, NULL);
test_in_addr_prefix_next_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00", 120, NULL);
-
}
static void test_in_addr_to_string_one(int f, const char *addr) {
@@ -299,6 +376,8 @@ static void test_in_addr_to_string_one(int f, const char *addr) {
}
static void test_in_addr_to_string(void) {
+ log_info("/* %s */", __func__);
+
test_in_addr_to_string_one(AF_INET, "192.168.0.1");
test_in_addr_to_string_one(AF_INET, "10.11.12.13");
test_in_addr_to_string_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
@@ -323,6 +402,8 @@ static void test_in_addr_ifindex_to_string_one(int f, const char *a, int ifindex
}
static void test_in_addr_ifindex_to_string(void) {
+ log_info("/* %s */", __func__);
+
test_in_addr_ifindex_to_string_one(AF_INET, "192.168.0.1", 7, "192.168.0.1");
test_in_addr_ifindex_to_string_one(AF_INET, "10.11.12.13", 9, "10.11.12.13");
test_in_addr_ifindex_to_string_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 10, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
@@ -338,6 +419,7 @@ static void test_in_addr_ifindex_from_string_auto(void) {
int family, ifindex;
union in_addr_union ua;
+ log_info("/* %s */", __func__);
/* Most in_addr_ifindex_from_string_auto() invocations have already been tested above, but let's test some more */
assert_se(in_addr_ifindex_from_string_auto("fe80::17", &family, &ua, &ifindex) >= 0);
@@ -381,6 +463,9 @@ static void test_sockaddr_equal(void) {
.vm.svm_port = 0,
.vm.svm_cid = VMADDR_CID_ANY,
};
+
+ log_info("/* %s */", __func__);
+
assert_se(sockaddr_equal(&a, &a));
assert_se(sockaddr_equal(&a, &b));
assert_se(sockaddr_equal(&d, &d));
@@ -391,6 +476,8 @@ static void test_sockaddr_equal(void) {
}
static void test_sockaddr_un_len(void) {
+ log_info("/* %s */", __func__);
+
static const struct sockaddr_un fs = {
.sun_family = AF_UNIX,
.sun_path = "/foo/bar/waldo",
@@ -401,7 +488,7 @@ static void test_sockaddr_un_len(void) {
.sun_path = "\0foobar",
};
- assert_se(SOCKADDR_UN_LEN(fs) == offsetof(struct sockaddr_un, sun_path) + strlen(fs.sun_path));
+ assert_se(SOCKADDR_UN_LEN(fs) == offsetof(struct sockaddr_un, sun_path) + strlen(fs.sun_path) + 1);
assert_se(SOCKADDR_UN_LEN(abstract) == offsetof(struct sockaddr_un, sun_path) + 1 + strlen(abstract.sun_path + 1));
}
@@ -409,6 +496,8 @@ static void test_in_addr_is_multicast(void) {
union in_addr_union a, b;
int f;
+ log_info("/* %s */", __func__);
+
assert_se(in_addr_from_string_auto("192.168.3.11", &f, &a) >= 0);
assert_se(in_addr_is_multicast(f, &a) == 0);
@@ -425,13 +514,14 @@ static void test_in_addr_is_multicast(void) {
static void test_getpeercred_getpeergroups(void) {
int r;
+ log_info("/* %s */", __func__);
+
r = safe_fork("(getpeercred)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
assert_se(r >= 0);
if (r == 0) {
static const gid_t gids[] = { 3, 4, 5, 6, 7 };
gid_t *test_gids;
- _cleanup_free_ gid_t *peer_groups = NULL;
size_t n_test_gids;
uid_t test_uid;
gid_t test_gid;
@@ -472,25 +562,245 @@ static void test_getpeercred_getpeergroups(void) {
assert_se(ucred.gid == test_gid);
assert_se(ucred.pid == getpid_cached());
- r = getpeergroups(pair[0], &peer_groups);
- assert_se(r >= 0 || IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT));
+ {
+ _cleanup_free_ gid_t *peer_groups = NULL;
- if (r >= 0) {
- assert_se((size_t) r == n_test_gids);
- assert_se(memcmp(peer_groups, test_gids, sizeof(gid_t) * n_test_gids) == 0);
+ r = getpeergroups(pair[0], &peer_groups);
+ assert_se(r >= 0 || IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT));
+
+ if (r >= 0) {
+ assert_se((size_t) r == n_test_gids);
+ assert_se(memcmp(peer_groups, test_gids, sizeof(gid_t) * n_test_gids) == 0);
+ }
}
safe_close_pair(pair);
+ _exit(EXIT_SUCCESS);
}
}
-int main(int argc, char *argv[]) {
+static void test_passfd_read(void) {
+ static const char file_contents[] = "test contents for passfd";
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ int r;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
+
+ r = safe_fork("(passfd_read)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ assert_se(r >= 0);
+
+ if (r == 0) {
+ /* Child */
+ char tmpfile[] = "/tmp/test-socket-util-passfd-read-XXXXXX";
+ _cleanup_close_ int tmpfd = -1;
+
+ pair[0] = safe_close(pair[0]);
+
+ tmpfd = mkostemp_safe(tmpfile);
+ assert_se(tmpfd >= 0);
+ assert_se(write(tmpfd, file_contents, strlen(file_contents)) == (ssize_t) strlen(file_contents));
+ tmpfd = safe_close(tmpfd);
+
+ tmpfd = open(tmpfile, O_RDONLY);
+ assert_se(tmpfd >= 0);
+ assert_se(unlink(tmpfile) == 0);
+
+ assert_se(send_one_fd(pair[1], tmpfd, MSG_DONTWAIT) == 0);
+ _exit(EXIT_SUCCESS);
+ }
+
+ /* Parent */
+ char buf[64];
+ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1);
+ _cleanup_close_ int fd = -1;
+
+ pair[1] = safe_close(pair[1]);
+
+ assert_se(receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd) == 0);
+
+ assert_se(fd >= 0);
+ r = read(fd, buf, sizeof(buf)-1);
+ assert_se(r >= 0);
+ buf[r] = 0;
+ assert_se(streq(buf, file_contents));
+}
+
+static void test_passfd_contents_read(void) {
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ static const char file_contents[] = "test contents in the file";
+ static const char wire_contents[] = "test contents on the wire";
+ int r;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
+
+ r = safe_fork("(passfd_contents_read)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ assert_se(r >= 0);
+
+ if (r == 0) {
+ /* Child */
+ struct iovec iov = IOVEC_INIT_STRING(wire_contents);
+ char tmpfile[] = "/tmp/test-socket-util-passfd-contents-read-XXXXXX";
+ _cleanup_close_ int tmpfd = -1;
+
+ pair[0] = safe_close(pair[0]);
+
+ tmpfd = mkostemp_safe(tmpfile);
+ assert_se(tmpfd >= 0);
+ assert_se(write(tmpfd, file_contents, strlen(file_contents)) == (ssize_t) strlen(file_contents));
+ tmpfd = safe_close(tmpfd);
+
+ tmpfd = open(tmpfile, O_RDONLY);
+ assert_se(tmpfd >= 0);
+ assert_se(unlink(tmpfile) == 0);
+
+ assert_se(send_one_fd_iov(pair[1], tmpfd, &iov, 1, MSG_DONTWAIT) > 0);
+ _exit(EXIT_SUCCESS);
+ }
+
+ /* Parent */
+ char buf[64];
+ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1);
+ _cleanup_close_ int fd = -1;
+ ssize_t k;
+
+ pair[1] = safe_close(pair[1]);
+
+ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd);
+ assert_se(k > 0);
+ buf[k] = 0;
+ assert_se(streq(buf, wire_contents));
+
+ assert_se(fd >= 0);
+ r = read(fd, buf, sizeof(buf)-1);
+ assert_se(r >= 0);
+ buf[r] = 0;
+ assert_se(streq(buf, file_contents));
+}
+
+static void test_receive_nopassfd(void) {
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ static const char wire_contents[] = "no fd passed here";
+ int r;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
+
+ r = safe_fork("(receive_nopassfd)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ assert_se(r >= 0);
+
+ if (r == 0) {
+ /* Child */
+ struct iovec iov = IOVEC_INIT_STRING(wire_contents);
+
+ pair[0] = safe_close(pair[0]);
+
+ assert_se(send_one_fd_iov(pair[1], -1, &iov, 1, MSG_DONTWAIT) > 0);
+ _exit(EXIT_SUCCESS);
+ }
+
+ /* Parent */
+ char buf[64];
+ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1);
+ int fd = -999;
+ ssize_t k;
+
+ pair[1] = safe_close(pair[1]);
+
+ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd);
+ assert_se(k > 0);
+ buf[k] = 0;
+ assert_se(streq(buf, wire_contents));
+
+ /* no fd passed here, confirm it was reset */
+ assert_se(fd == -1);
+}
+
+static void test_send_nodata_nofd(void) {
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ int r;
- log_set_max_level(LOG_DEBUG);
+ log_info("/* %s */", __func__);
+
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
+
+ r = safe_fork("(send_nodata_nofd)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ assert_se(r >= 0);
+
+ if (r == 0) {
+ /* Child */
+ pair[0] = safe_close(pair[0]);
+
+ assert_se(send_one_fd_iov(pair[1], -1, NULL, 0, MSG_DONTWAIT) == -EINVAL);
+ _exit(EXIT_SUCCESS);
+ }
+
+ /* Parent */
+ char buf[64];
+ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1);
+ int fd = -999;
+ ssize_t k;
+
+ pair[1] = safe_close(pair[1]);
+
+ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd);
+ /* recvmsg() will return errno EAGAIN if nothing was sent */
+ assert_se(k == -EAGAIN);
+
+ /* receive_one_fd_iov returned error, so confirm &fd wasn't touched */
+ assert_se(fd == -999);
+}
+
+static void test_send_emptydata(void) {
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ int r;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
+
+ r = safe_fork("(send_emptydata)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ assert_se(r >= 0);
+
+ if (r == 0) {
+ /* Child */
+ struct iovec iov = IOVEC_INIT_STRING(""); /* zero-length iov */
+ assert_se(iov.iov_len == 0);
+
+ pair[0] = safe_close(pair[0]);
+
+ /* This will succeed, since iov is set. */
+ assert_se(send_one_fd_iov(pair[1], -1, &iov, 1, MSG_DONTWAIT) == 0);
+ _exit(EXIT_SUCCESS);
+ }
+
+ /* Parent */
+ char buf[64];
+ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1);
+ int fd = -999;
+ ssize_t k;
+
+ pair[1] = safe_close(pair[1]);
+
+ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd);
+ /* receive_one_fd_iov() returns -EIO if an fd is not found and no data was returned. */
+ assert_se(k == -EIO);
+
+ /* receive_one_fd_iov returned error, so confirm &fd wasn't touched */
+ assert_se(fd == -999);
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_DEBUG);
test_ifname_valid();
test_socket_address_parse();
+ test_socket_print_unix();
test_socket_address_parse_netlink();
test_socket_address_equal();
test_socket_address_get_path();
@@ -512,5 +822,11 @@ int main(int argc, char *argv[]) {
test_getpeercred_getpeergroups();
+ test_passfd_read();
+ test_passfd_contents_read();
+ test_receive_nopassfd();
+ test_send_nodata_nofd();
+ test_send_emptydata();
+
return 0;
}
diff --git a/src/test/test-specifier.c b/src/test/test-specifier.c
index 9c7c352b51..a0ffdf6cb6 100644
--- a/src/test/test-specifier.c
+++ b/src/test/test-specifier.c
@@ -5,6 +5,7 @@
#include "specifier.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
static void test_specifier_escape_one(const char *a, const char *b) {
_cleanup_free_ char *x = NULL;
@@ -39,7 +40,7 @@ static void test_specifier_escape_strv(void) {
}
int main(int argc, char *argv[]) {
- log_set_max_level(LOG_DEBUG);
+ test_setup_logging(LOG_DEBUG);
test_specifier_escape();
test_specifier_escape_strv();
diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c
index 43f56a6c20..d16fdd90d1 100644
--- a/src/test/test-stat-util.c
+++ b/src/test/test-stat-util.c
@@ -6,11 +6,12 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "macro.h"
#include "missing.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "path-util.h"
#include "stat-util.h"
+#include "tmpfile-util.h"
static void test_files_same(void) {
_cleanup_close_ int fd = -1;
@@ -67,11 +68,101 @@ static void test_path_is_temporary_fs(void) {
assert_se(path_is_temporary_fs("/i-dont-exist") == -ENOENT);
}
+static void test_fd_is_network_ns(void) {
+ _cleanup_close_ int fd = -1;
+ assert_se(fd_is_network_ns(STDIN_FILENO) == 0);
+ assert_se(fd_is_network_ns(STDERR_FILENO) == 0);
+ assert_se(fd_is_network_ns(STDOUT_FILENO) == 0);
+
+ assert_se((fd = open("/proc/self/ns/mnt", O_CLOEXEC|O_RDONLY)) >= 0);
+ assert_se(IN_SET(fd_is_network_ns(fd), 0, -EUCLEAN));
+ fd = safe_close(fd);
+
+ assert_se((fd = open("/proc/self/ns/net", O_CLOEXEC|O_RDONLY)) >= 0);
+ assert_se(IN_SET(fd_is_network_ns(fd), 1, -EUCLEAN));
+}
+
+static void test_device_major_minor_valid(void) {
+ /* on glibc dev_t is 64bit, even though in the kernel it is only 32bit */
+ assert_cc(sizeof(dev_t) == sizeof(uint64_t));
+
+ assert_se(DEVICE_MAJOR_VALID(0U));
+ assert_se(DEVICE_MINOR_VALID(0U));
+
+ assert_se(DEVICE_MAJOR_VALID(1U));
+ assert_se(DEVICE_MINOR_VALID(1U));
+
+ assert_se(!DEVICE_MAJOR_VALID(-1U));
+ assert_se(!DEVICE_MINOR_VALID(-1U));
+
+ assert_se(DEVICE_MAJOR_VALID(1U << 10));
+ assert_se(DEVICE_MINOR_VALID(1U << 10));
+
+ assert_se(DEVICE_MAJOR_VALID((1U << 12) - 1));
+ assert_se(DEVICE_MINOR_VALID((1U << 20) - 1));
+
+ assert_se(!DEVICE_MAJOR_VALID((1U << 12)));
+ assert_se(!DEVICE_MINOR_VALID((1U << 20)));
+
+ assert_se(!DEVICE_MAJOR_VALID(1U << 25));
+ assert_se(!DEVICE_MINOR_VALID(1U << 25));
+
+ assert_se(!DEVICE_MAJOR_VALID(UINT32_MAX));
+ assert_se(!DEVICE_MINOR_VALID(UINT32_MAX));
+
+ assert_se(!DEVICE_MAJOR_VALID(UINT64_MAX));
+ assert_se(!DEVICE_MINOR_VALID(UINT64_MAX));
+
+ assert_se(DEVICE_MAJOR_VALID(major(0)));
+ assert_se(DEVICE_MINOR_VALID(minor(0)));
+}
+
+static void test_device_path_make_canonical_one(const char *path) {
+ _cleanup_free_ char *resolved = NULL, *raw = NULL;
+ struct stat st;
+ dev_t devno;
+ mode_t mode;
+ int r;
+
+ assert_se(stat(path, &st) >= 0);
+ r = device_path_make_canonical(st.st_mode, st.st_rdev, &resolved);
+ if (r == -ENOENT) /* maybe /dev/char/x:y and /dev/block/x:y are missing in this test environment, because we
+ * run in a container or so? */
+ return;
+
+ assert_se(r >= 0);
+ assert_se(path_equal(path, resolved));
+
+ assert_se(device_path_make_major_minor(st.st_mode, st.st_rdev, &raw) >= 0);
+ assert_se(device_path_parse_major_minor(raw, &mode, &devno) >= 0);
+
+ assert_se(st.st_rdev == devno);
+ assert_se((st.st_mode & S_IFMT) == (mode & S_IFMT));
+}
+
+static void test_device_path_make_canonical(void) {
+
+ test_device_path_make_canonical_one("/dev/null");
+ test_device_path_make_canonical_one("/dev/zero");
+ test_device_path_make_canonical_one("/dev/full");
+ test_device_path_make_canonical_one("/dev/random");
+ test_device_path_make_canonical_one("/dev/urandom");
+ test_device_path_make_canonical_one("/dev/tty");
+
+ if (is_device_node("/run/systemd/inaccessible/chr") > 0) {
+ test_device_path_make_canonical_one("/run/systemd/inaccessible/chr");
+ test_device_path_make_canonical_one("/run/systemd/inaccessible/blk");
+ }
+}
+
int main(int argc, char *argv[]) {
test_files_same();
test_is_symlink();
test_path_is_fs_type();
test_path_is_temporary_fs();
+ test_fd_is_network_ns();
+ test_device_major_minor_valid();
+ test_device_path_make_canonical();
return 0;
}
diff --git a/src/test/test-static-destruct.c b/src/test/test-static-destruct.c
new file mode 100644
index 0000000000..eb0523d87a
--- /dev/null
+++ b/src/test/test-static-destruct.c
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "static-destruct.h"
+#include "tests.h"
+
+static int foo = 0;
+static int bar = 0;
+static int baz = 0;
+static char* memory = NULL;
+
+static void test_destroy(int *b) {
+ (*b)++;
+}
+
+STATIC_DESTRUCTOR_REGISTER(foo, test_destroy);
+STATIC_DESTRUCTOR_REGISTER(bar, test_destroy);
+STATIC_DESTRUCTOR_REGISTER(bar, test_destroy);
+STATIC_DESTRUCTOR_REGISTER(baz, test_destroy);
+STATIC_DESTRUCTOR_REGISTER(baz, test_destroy);
+STATIC_DESTRUCTOR_REGISTER(baz, test_destroy);
+STATIC_DESTRUCTOR_REGISTER(memory, freep);
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_INFO);
+
+ assert_se(memory = strdup("hallo"));
+
+ assert_se(foo == 0 && bar == 0 && baz == 0);
+ static_destruct();
+ assert_se(foo == 1 && bar == 2 && baz == 3);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c
index 3e72ce2c0a..8c1f91d4ef 100644
--- a/src/test/test-string-util.c
+++ b/src/test/test-string-util.c
@@ -5,6 +5,7 @@
#include "macro.h"
#include "string-util.h"
#include "strv.h"
+#include "tests.h"
#include "utf8.h"
static void test_string_erase(void) {
@@ -30,6 +31,64 @@ static void test_string_erase(void) {
assert_se(x[9] == '\0');
}
+static void test_free_and_strndup_one(char **t, const char *src, size_t l, const char *expected, bool change) {
+ int r;
+
+ log_debug("%s: \"%s\", \"%s\", %zd (expect \"%s\", %s)",
+ __func__, strnull(*t), strnull(src), l, strnull(expected), yes_no(change));
+
+ r = free_and_strndup(t, src, l);
+ assert_se(streq_ptr(*t, expected));
+ assert_se(r == change); /* check that change occurs only when necessary */
+}
+
+static void test_free_and_strndup(void) {
+ static const struct test_case {
+ const char *src;
+ size_t len;
+ const char *expected;
+ } cases[] = {
+ {"abc", 0, ""},
+ {"abc", 0, ""},
+ {"abc", 1, "a"},
+ {"abc", 2, "ab"},
+ {"abc", 3, "abc"},
+ {"abc", 4, "abc"},
+ {"abc", 5, "abc"},
+ {"abc", 5, "abc"},
+ {"abc", 4, "abc"},
+ {"abc", 3, "abc"},
+ {"abc", 2, "ab"},
+ {"abc", 1, "a"},
+ {"abc", 0, ""},
+
+ {"", 0, ""},
+ {"", 1, ""},
+ {"", 2, ""},
+ {"", 0, ""},
+ {"", 1, ""},
+ {"", 2, ""},
+ {"", 2, ""},
+ {"", 1, ""},
+ {"", 0, ""},
+
+ {NULL, 0, NULL},
+
+ {"foo", 3, "foo"},
+ {"foobar", 6, "foobar"},
+ };
+
+ _cleanup_free_ char *t = NULL;
+ const char *prev_expected = t;
+
+ for (unsigned i = 0; i < ELEMENTSOF(cases); i++) {
+ test_free_and_strndup_one(&t,
+ cases[i].src, cases[i].len, cases[i].expected,
+ !streq_ptr(cases[i].expected, prev_expected));
+ prev_expected = t;
+ }
+}
+
static void test_ascii_strcasecmp_n(void) {
assert_se(ascii_strcasecmp_n("", "", 0) == 0);
@@ -496,8 +555,34 @@ static void test_memory_startswith(void) {
assert_se(!memory_startswith("xxx", 4, "xxxx"));
}
+static void test_memory_startswith_no_case(void) {
+ assert_se(streq(memory_startswith_no_case("", 0, ""), ""));
+ assert_se(streq(memory_startswith_no_case("", 1, ""), ""));
+ assert_se(streq(memory_startswith_no_case("x", 2, ""), "x"));
+ assert_se(streq(memory_startswith_no_case("X", 2, ""), "X"));
+ assert_se(!memory_startswith_no_case("", 1, "X"));
+ assert_se(!memory_startswith_no_case("", 1, "xxxxXXXX"));
+ assert_se(streq(memory_startswith_no_case("xxx", 4, "X"), "xx"));
+ assert_se(streq(memory_startswith_no_case("XXX", 4, "x"), "XX"));
+ assert_se(streq(memory_startswith_no_case("XXX", 4, "X"), "XX"));
+ assert_se(streq(memory_startswith_no_case("xxx", 4, "XX"), "x"));
+ assert_se(streq(memory_startswith_no_case("XXX", 4, "xx"), "X"));
+ assert_se(streq(memory_startswith_no_case("XXX", 4, "XX"), "X"));
+ assert_se(streq(memory_startswith_no_case("xxx", 4, "XXX"), ""));
+ assert_se(streq(memory_startswith_no_case("XXX", 4, "xxx"), ""));
+ assert_se(streq(memory_startswith_no_case("XXX", 4, "XXX"), ""));
+
+ assert_se(memory_startswith_no_case((char[2]){'x', 'x'}, 2, "xx"));
+ assert_se(memory_startswith_no_case((char[2]){'x', 'X'}, 2, "xX"));
+ assert_se(memory_startswith_no_case((char[2]){'X', 'x'}, 2, "Xx"));
+ assert_se(memory_startswith_no_case((char[2]){'X', 'X'}, 2, "XX"));
+}
+
int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_DEBUG);
+
test_string_erase();
+ test_free_and_strndup();
test_ascii_strcasecmp_n();
test_ascii_strcasecmp_nn();
test_cellescape();
@@ -525,6 +610,7 @@ int main(int argc, char *argv[]) {
test_first_word();
test_strlen_ptr();
test_memory_startswith();
+ test_memory_startswith_no_case();
return 0;
}
diff --git a/src/test/test-strip-tab-ansi.c b/src/test/test-strip-tab-ansi.c
index 362f862221..fae384ef9b 100644
--- a/src/test/test-strip-tab-ansi.c
+++ b/src/test/test-strip-tab-ansi.c
@@ -3,6 +3,7 @@
#include <stdio.h>
#include "alloc-util.h"
+#include "pretty-print.h"
#include "string-util.h"
#include "terminal-util.h"
#include "util.h"
diff --git a/src/test/test-strv.c b/src/test/test-strv.c
index 1c192239a2..31ef1abb44 100644
--- a/src/test/test-strv.c
+++ b/src/test/test-strv.c
@@ -56,6 +56,20 @@ static void test_strptr_in_set(void) {
assert_se(!STRPTR_IN_SET(NULL, NULL));
}
+static void test_startswith_set(void) {
+ assert_se(!STARTSWITH_SET("foo", "bar", "baz", "waldo"));
+ assert_se(!STARTSWITH_SET("foo", "bar"));
+
+ assert_se(STARTSWITH_SET("abc", "a", "ab", "abc"));
+ assert_se(STARTSWITH_SET("abc", "ax", "ab", "abc"));
+ assert_se(STARTSWITH_SET("abc", "ax", "abx", "abc"));
+ assert_se(!STARTSWITH_SET("abc", "ax", "abx", "abcx"));
+
+ assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "foo", "zzz"), "bar"));
+ assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "", "zzz"), "foobar"));
+ assert_se(streq_ptr(STARTSWITH_SET("", "hhh", "kkk", "zzz", ""), ""));
+}
+
static const char* const input_table_multiple[] = {
"one",
"two",
@@ -63,6 +77,13 @@ static const char* const input_table_multiple[] = {
NULL,
};
+static const char* const input_table_quoted[] = {
+ "one",
+ " two\t three ",
+ " four five",
+ NULL,
+};
+
static const char* const input_table_one[] = {
"one",
NULL,
@@ -144,6 +165,38 @@ static void test_strv_join(void) {
assert_se(streq(w, ""));
}
+static void test_strv_join_prefix(void) {
+ _cleanup_free_ char *p = NULL, *q = NULL, *r = NULL, *s = NULL, *t = NULL, *v = NULL, *w = NULL;
+
+ p = strv_join_prefix((char **)input_table_multiple, ", ", "foo");
+ assert_se(p);
+ assert_se(streq(p, "fooone, footwo, foothree"));
+
+ q = strv_join_prefix((char **)input_table_multiple, ";", "foo");
+ assert_se(q);
+ assert_se(streq(q, "fooone;footwo;foothree"));
+
+ r = strv_join_prefix((char **)input_table_multiple, NULL, "foo");
+ assert_se(r);
+ assert_se(streq(r, "fooone footwo foothree"));
+
+ s = strv_join_prefix((char **)input_table_one, ", ", "foo");
+ assert_se(s);
+ assert_se(streq(s, "fooone"));
+
+ t = strv_join_prefix((char **)input_table_none, ", ", "foo");
+ assert_se(t);
+ assert_se(streq(t, ""));
+
+ v = strv_join_prefix((char **)input_table_two_empties, ", ", "foo");
+ assert_se(v);
+ assert_se(streq(v, "foo, foo"));
+
+ w = strv_join_prefix((char **)input_table_one_empty, ", ", "foo");
+ assert_se(w);
+ assert_se(streq(w, "foo"));
+}
+
static void test_strv_unquote(const char *quoted, char **list) {
_cleanup_strv_free_ char **s;
_cleanup_free_ char *j;
@@ -174,18 +227,127 @@ static void test_invalid_unquote(const char *quoted) {
}
static void test_strv_split(void) {
- char **s;
- unsigned i = 0;
_cleanup_strv_free_ char **l = NULL;
const char str[] = "one,two,three";
l = strv_split(str, ",");
+ assert_se(l);
+ assert_se(strv_equal(l, (char**) input_table_multiple));
+
+ strv_free(l);
+ l = strv_split(" one two\t three", WHITESPACE);
assert_se(l);
+ assert_se(strv_equal(l, (char**) input_table_multiple));
- STRV_FOREACH(s, l) {
- assert_se(streq(*s, input_table_multiple[i++]));
- }
+ strv_free(l);
+
+ /* Setting NULL for separator is equivalent to WHITESPACE */
+ l = strv_split(" one two\t three", NULL);
+ assert_se(l);
+ assert_se(strv_equal(l, (char**) input_table_multiple));
+
+ strv_free(l);
+
+ l = strv_split_full(" one two\t three", NULL, 0);
+ assert_se(l);
+ assert_se(strv_equal(l, (char**) input_table_multiple));
+
+ strv_free(l);
+
+ l = strv_split_full(" 'one' \" two\t three \" ' four five'", NULL, SPLIT_QUOTES);
+ assert_se(l);
+ assert_se(strv_equal(l, (char**) input_table_quoted));
+
+ strv_free(l);
+
+ /* missing last quote ignores the last element. */
+ l = strv_split_full(" 'one' \" two\t three \" ' four five' ' ignored element ", NULL, SPLIT_QUOTES);
+ assert_se(l);
+ assert_se(strv_equal(l, (char**) input_table_quoted));
+
+ strv_free(l);
+
+ /* missing last quote, but the last element is _not_ ignored with SPLIT_RELAX. */
+ l = strv_split_full(" 'one' \" two\t three \" ' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
+ assert_se(l);
+ assert_se(strv_equal(l, (char**) input_table_quoted));
+
+ strv_free(l);
+
+ /* missing separator between */
+ l = strv_split_full(" 'one' \" two\t three \"' four five'", NULL, SPLIT_QUOTES | SPLIT_RELAX);
+ assert_se(l);
+ assert_se(strv_equal(l, (char**) input_table_quoted));
+
+ strv_free(l);
+
+ l = strv_split_full(" 'one' \" two\t three \"' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
+ assert_se(l);
+ assert_se(strv_equal(l, (char**) input_table_quoted));
+}
+
+static void test_strv_split_empty(void) {
+ _cleanup_strv_free_ char **l = NULL;
+
+ l = strv_split("", WHITESPACE);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split("", NULL);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split_full("", NULL, 0);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split_full("", NULL, SPLIT_QUOTES);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split_full("", WHITESPACE, SPLIT_QUOTES);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split_full("", WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split(" ", WHITESPACE);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split(" ", NULL);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split_full(" ", NULL, 0);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split_full(" ", WHITESPACE, SPLIT_QUOTES);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split_full(" ", NULL, SPLIT_QUOTES);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+
+ strv_free(l);
+ l = strv_split_full(" ", NULL, SPLIT_QUOTES | SPLIT_RELAX);
+ assert_se(l);
+ assert_se(strv_isempty(l));
}
static void test_strv_split_extract(void) {
@@ -233,18 +395,18 @@ static void test_strv_split_nulstr(void) {
static void test_strv_parse_nulstr(void) {
_cleanup_strv_free_ char **l = NULL;
- const char nulstr[] = "fuck\0fuck2\0fuck3\0\0fuck5\0\0xxx";
+ const char nulstr[] = "hoge\0hoge2\0hoge3\0\0hoge5\0\0xxx";
l = strv_parse_nulstr(nulstr, sizeof(nulstr)-1);
assert_se(l);
puts("Parse nulstr:");
strv_print(l);
- assert_se(streq(l[0], "fuck"));
- assert_se(streq(l[1], "fuck2"));
- assert_se(streq(l[2], "fuck3"));
+ assert_se(streq(l[0], "hoge"));
+ assert_se(streq(l[1], "hoge2"));
+ assert_se(streq(l[2], "hoge3"));
assert_se(streq(l[3], ""));
- assert_se(streq(l[4], "fuck5"));
+ assert_se(streq(l[4], "hoge5"));
assert_se(streq(l[5], ""));
assert_se(streq(l[6], "xxx"));
}
@@ -293,8 +455,8 @@ static void test_strv_sort(void) {
static void test_strv_extend_strv_concat(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL;
- a = strv_new("without", "suffix", NULL);
- b = strv_new("with", "suffix", NULL);
+ a = strv_new("without", "suffix");
+ b = strv_new("with", "suffix");
assert_se(a);
assert_se(b);
@@ -309,8 +471,8 @@ static void test_strv_extend_strv_concat(void) {
static void test_strv_extend_strv(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL, **n = NULL;
- a = strv_new("abc", "def", "ghi", NULL);
- b = strv_new("jkl", "mno", "abc", "pqr", NULL);
+ a = strv_new("abc", "def", "ghi");
+ b = strv_new("jkl", "mno", "abc", "pqr");
assert_se(a);
assert_se(b);
@@ -335,7 +497,7 @@ static void test_strv_extend_strv(void) {
static void test_strv_extend(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL;
- a = strv_new("test", "test1", NULL);
+ a = strv_new("test", "test1");
assert_se(a);
assert_se(strv_extend(&a, "test2") >= 0);
assert_se(strv_extend(&b, "test3") >= 0);
@@ -349,7 +511,7 @@ static void test_strv_extend(void) {
static void test_strv_extendf(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL;
- a = strv_new("test", "test1", NULL);
+ a = strv_new("test", "test1");
assert_se(a);
assert_se(strv_extendf(&a, "test2 %s %d %s", "foo", 128, "bar") >= 0);
assert_se(strv_extendf(&b, "test3 %s %s %d", "bar", "foo", 128) >= 0);
@@ -365,7 +527,7 @@ static void test_strv_foreach(void) {
unsigned i = 0;
char **check;
- a = strv_new("one", "two", "three", NULL);
+ a = strv_new("one", "two", "three");
assert_se(a);
@@ -379,7 +541,7 @@ static void test_strv_foreach_backwards(void) {
unsigned i = 2;
char **check;
- a = strv_new("one", "two", "three", NULL);
+ a = strv_new("one", "two", "three");
assert_se(a);
@@ -399,8 +561,7 @@ static void test_strv_foreach_pair(void) {
a = strv_new("pair_one", "pair_one",
"pair_two", "pair_two",
- "pair_three", "pair_three",
- NULL);
+ "pair_three", "pair_three");
STRV_FOREACH_PAIR(x, y, a) {
assert_se(streq(*x, *y));
@@ -460,7 +621,7 @@ static void test_strv_insert(void) {
static void test_strv_push_prepend(void) {
_cleanup_strv_free_ char **a = NULL;
- a = strv_new("foo", "bar", "three", NULL);
+ a = strv_new("foo", "bar", "three");
assert_se(strv_push_prepend(&a, strdup("first")) >= 0);
assert_se(streq(a[0], "first"));
@@ -500,11 +661,11 @@ static void test_strv_equal(void) {
_cleanup_strv_free_ char **b = NULL;
_cleanup_strv_free_ char **c = NULL;
- a = strv_new("one", "two", "three", NULL);
+ a = strv_new("one", "two", "three");
assert_se(a);
- b = strv_new("one", "two", "three", NULL);
+ b = strv_new("one", "two", "three");
assert_se(a);
- c = strv_new("one", "two", "three", "four", NULL);
+ c = strv_new("one", "two", "three", "four");
assert_se(a);
assert_se(strv_equal(a, a));
@@ -519,19 +680,19 @@ static void test_strv_equal(void) {
static void test_strv_is_uniq(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL, **d = NULL;
- a = strv_new(NULL, NULL);
+ a = strv_new(NULL);
assert_se(a);
assert_se(strv_is_uniq(a));
- b = strv_new("foo", NULL);
+ b = strv_new("foo");
assert_se(b);
assert_se(strv_is_uniq(b));
- c = strv_new("foo", "bar", NULL);
+ c = strv_new("foo", "bar");
assert_se(c);
assert_se(strv_is_uniq(c));
- d = strv_new("foo", "bar", "waldo", "bar", "piep", NULL);
+ d = strv_new("foo", "bar", "waldo", "bar", "piep");
assert_se(d);
assert_se(!strv_is_uniq(d));
}
@@ -539,26 +700,26 @@ static void test_strv_is_uniq(void) {
static void test_strv_reverse(void) {
_cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL, **d = NULL;
- a = strv_new(NULL, NULL);
+ a = strv_new(NULL);
assert_se(a);
strv_reverse(a);
assert_se(strv_isempty(a));
- b = strv_new("foo", NULL);
+ b = strv_new("foo");
assert_se(b);
strv_reverse(b);
assert_se(streq_ptr(b[0], "foo"));
assert_se(streq_ptr(b[1], NULL));
- c = strv_new("foo", "bar", NULL);
+ c = strv_new("foo", "bar");
assert_se(c);
strv_reverse(c);
assert_se(streq_ptr(c[0], "bar"));
assert_se(streq_ptr(c[1], "foo"));
assert_se(streq_ptr(c[2], NULL));
- d = strv_new("foo", "bar", "waldo", NULL);
+ d = strv_new("foo", "bar", "waldo");
assert_se(d);
strv_reverse(d);
assert_se(streq_ptr(d[0], "waldo"));
@@ -570,7 +731,7 @@ static void test_strv_reverse(void) {
static void test_strv_shell_escape(void) {
_cleanup_strv_free_ char **v = NULL;
- v = strv_new("foo:bar", "bar,baz", "wal\\do", NULL);
+ v = strv_new("foo:bar", "bar,baz", "wal\\do");
assert_se(v);
assert_se(strv_shell_escape(v, ",:"));
assert_se(streq_ptr(v[0], "foo\\:bar"));
@@ -604,7 +765,7 @@ static void test_strv_skip(void) {
static void test_strv_extend_n(void) {
_cleanup_strv_free_ char **v = NULL;
- v = strv_new("foo", "bar", NULL);
+ v = strv_new("foo", "bar");
assert_se(v);
assert_se(strv_extend_n(&v, "waldo", 3) >= 0);
@@ -660,8 +821,8 @@ static void test_strv_free_free(void) {
char ***t;
assert_se(t = new(char**, 3));
- assert_se(t[0] = strv_new("a", "b", NULL));
- assert_se(t[1] = strv_new("c", "d", "e", NULL));
+ assert_se(t[0] = strv_new("a", "b"));
+ assert_se(t[1] = strv_new("c", "d", "e"));
t[2] = NULL;
t = strv_free_free(t);
@@ -691,7 +852,7 @@ static void test_strv_fnmatch(void) {
assert_se(!strv_fnmatch(STRV_MAKE_EMPTY, "a", 0));
- v = strv_new("*\\*", NULL);
+ v = strv_new("*\\*");
assert_se(!strv_fnmatch(v, "\\", 0));
assert_se(strv_fnmatch(v, "\\", FNM_NOESCAPE));
}
@@ -700,6 +861,7 @@ int main(int argc, char *argv[]) {
test_specifier_printf();
test_str_in_set();
test_strptr_in_set();
+ test_startswith_set();
test_strv_foreach();
test_strv_foreach_backwards();
test_strv_foreach_pair();
@@ -707,6 +869,7 @@ int main(int argc, char *argv[]) {
test_strv_find_prefix();
test_strv_find_startswith();
test_strv_join();
+ test_strv_join_prefix();
test_strv_unquote(" foo=bar \"waldo\" zzz ", STRV_MAKE("foo=bar", "waldo", "zzz"));
test_strv_unquote("", STRV_MAKE_EMPTY);
@@ -733,6 +896,7 @@ int main(int argc, char *argv[]) {
test_invalid_unquote("'x'y'g");
test_strv_split();
+ test_strv_split_empty();
test_strv_split_extract();
test_strv_split_newlines();
test_strv_split_nulstr();
diff --git a/src/test/test-systemd-tmpfiles.py b/src/test/test-systemd-tmpfiles.py
index 13a44f3c4a..83a66e8772 100755
--- a/src/test/test-systemd-tmpfiles.py
+++ b/src/test/test-systemd-tmpfiles.py
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
-# SPDX-License-Identifier: LGPL-2.1+
+# SPDX-License-Identifier: LGPL-2.1+
#
-# systemd is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
import os
import sys
@@ -12,6 +12,7 @@ import socket
import subprocess
import tempfile
import pwd
+import grp
try:
from systemd import id128
@@ -95,9 +96,13 @@ def test_valid_specifiers(*, user):
test_content('f {} - - - - %H', '{}'.format(socket.gethostname()), user=user)
test_content('f {} - - - - %v', '{}'.format(os.uname().release), user=user)
test_content('f {} - - - - %U', '{}'.format(os.getuid()), user=user)
+ test_content('f {} - - - - %G', '{}'.format(os.getgid()), user=user)
- user = pwd.getpwuid(os.getuid())
- test_content('f {} - - - - %u', '{}'.format(user.pw_name), user=user)
+ puser = pwd.getpwuid(os.getuid())
+ test_content('f {} - - - - %u', '{}'.format(puser.pw_name), user=user)
+
+ pgroup = grp.getgrgid(os.getgid())
+ test_content('f {} - - - - %g', '{}'.format(pgroup.gr_name), user=user)
# Note that %h is the only specifier in which we look the environment,
# because we check $HOME. Should we even be doing that?
diff --git a/src/test/test-tables.c b/src/test/test-tables.c
index 944104a25a..49268eae22 100644
--- a/src/test/test-tables.c
+++ b/src/test/test-tables.c
@@ -5,6 +5,7 @@
#include "cgroup.h"
#include "compress.h"
#include "condition.h"
+#include "device-internal.h"
#include "device.h"
#include "execute.h"
#include "import-util.h"
@@ -24,6 +25,7 @@
#include "rlimit-util.h"
#include "scope.h"
#include "service.h"
+#include "show-status.h"
#include "slice.h"
#include "socket-util.h"
#include "socket.h"
@@ -47,7 +49,9 @@ int main(int argc, char **argv) {
test_table(collect_mode, COLLECT_MODE);
test_table(condition_result, CONDITION_RESULT);
test_table(condition_type, CONDITION_TYPE);
+ test_table(device_action, DEVICE_ACTION);
test_table(device_state, DEVICE_STATE);
+ test_table(dns_over_tls_mode, DNS_OVER_TLS_MODE);
test_table(dnssec_mode, DNSSEC_MODE);
test_table(emergency_action, EMERGENCY_ACTION);
test_table(exec_directory_type, EXEC_DIRECTORY_TYPE);
@@ -75,6 +79,7 @@ int main(int argc, char **argv) {
test_table(name_policy, NAMEPOLICY);
test_table(namespace_type, NAMESPACE_TYPE);
test_table(notify_access, NOTIFY_ACCESS);
+ test_table(notify_state, NOTIFY_STATE);
test_table(output_mode, OUTPUT_MODE);
test_table(partition_designator, PARTITION_DESIGNATOR);
test_table(path_result, PATH_RESULT);
@@ -91,6 +96,7 @@ int main(int argc, char **argv) {
test_table(service_result, SERVICE_RESULT);
test_table(service_state, SERVICE_STATE);
test_table(service_type, SERVICE_TYPE);
+ test_table(show_status, SHOW_STATUS);
test_table(slice_state, SLICE_STATE);
test_table(socket_address_bind_ipv6_only, SOCKET_ADDRESS_BIND_IPV6_ONLY);
test_table(socket_exec_command, SOCKET_EXEC_COMMAND);
diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c
index 9f27c74139..958d369430 100644
--- a/src/test/test-terminal-util.c
+++ b/src/test/test-terminal-util.c
@@ -5,11 +5,11 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "fileio.h"
-#include "log.h"
#include "macro.h"
#include "strv.h"
#include "terminal-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "util.h"
static void test_default_term_for_tty(void) {
@@ -32,15 +32,11 @@ static void test_read_one_char(void) {
char r;
bool need_nl;
char name[] = "/tmp/test-read_one_char.XXXXXX";
- int fd;
- fd = mkostemp_safe(name);
- assert_se(fd >= 0);
- file = fdopen(fd, "r+");
- assert_se(file);
+ assert(fmkostemp_safe(name, "r+", &file) == 0);
+
assert_se(fputs("c\n", file) >= 0);
rewind(file);
-
assert_se(read_one_char(file, &r, 1000000, &need_nl) >= 0);
assert_se(!need_nl);
assert_se(r == 'c');
@@ -59,36 +55,11 @@ static void test_read_one_char(void) {
unlink(name);
}
-static void test_terminal_urlify(void) {
- _cleanup_free_ char *formatted = NULL;
-
- assert_se(terminal_urlify("https://www.freedesktop.org/wiki/Software/systemd/", "systemd homepage", &formatted) >= 0);
- printf("Hey, considere visiting the %s right now! It is very good!\n", formatted);
-
- formatted = mfree(formatted);
-
- assert_se(terminal_urlify_path("/etc/fstab", "this link to your /etc/fstab", &formatted) >= 0);
- printf("Or click on %s to have a look at it!\n", formatted);
-}
-
-static void test_cat_files(void) {
- assert_se(cat_files("/no/such/file", NULL, 0) == -ENOENT);
- assert_se(cat_files("/no/such/file", NULL, CAT_FLAGS_MAIN_FILE_OPTIONAL) == 0);
-
- if (access("/etc/fstab", R_OK) >= 0)
- assert_se(cat_files("/etc/fstab", STRV_MAKE("/etc/fstab", "/etc/fstab"), 0) == 0);
-}
-
int main(int argc, char *argv[]) {
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_INFO);
test_default_term_for_tty();
test_read_one_char();
- test_terminal_urlify();
- test_cat_files();
-
- print_separator();
return 0;
}
diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c
index 87de8d172c..2ec2ade3f1 100644
--- a/src/test/test-time-util.c
+++ b/src/test/test-time-util.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "random-util.h"
+#include "serialize.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@@ -36,14 +37,37 @@ static void test_parse_sec(void) {
assert_se(u == USEC_INFINITY);
assert_se(parse_sec(" infinity ", &u) >= 0);
assert_se(u == USEC_INFINITY);
+ assert_se(parse_sec("+3.1s", &u) >= 0);
+ assert_se(u == 3100 * USEC_PER_MSEC);
+ assert_se(parse_sec("3.1s.2", &u) >= 0);
+ assert_se(u == 3300 * USEC_PER_MSEC);
+ assert_se(parse_sec("3.1 .2", &u) >= 0);
+ assert_se(u == 3300 * USEC_PER_MSEC);
+ assert_se(parse_sec("3.1 sec .2 sec", &u) >= 0);
+ assert_se(u == 3300 * USEC_PER_MSEC);
+ assert_se(parse_sec("3.1 sec 1.2 sec", &u) >= 0);
+ assert_se(u == 4300 * USEC_PER_MSEC);
assert_se(parse_sec(" xyz ", &u) < 0);
assert_se(parse_sec("", &u) < 0);
assert_se(parse_sec(" . ", &u) < 0);
assert_se(parse_sec(" 5. ", &u) < 0);
assert_se(parse_sec(".s ", &u) < 0);
+ assert_se(parse_sec("-5s ", &u) < 0);
+ assert_se(parse_sec("-0.3s ", &u) < 0);
+ assert_se(parse_sec("-0.0s ", &u) < 0);
+ assert_se(parse_sec("-0.-0s ", &u) < 0);
+ assert_se(parse_sec("0.-0s ", &u) < 0);
+ assert_se(parse_sec("3.-0s ", &u) < 0);
assert_se(parse_sec(" infinity .7", &u) < 0);
assert_se(parse_sec(".3 infinity", &u) < 0);
+ assert_se(parse_sec("3.+1s", &u) < 0);
+ assert_se(parse_sec("3. 1s", &u) < 0);
+ assert_se(parse_sec("3.s", &u) < 0);
+ assert_se(parse_sec("12.34.56", &u) < 0);
+ assert_se(parse_sec("12..34", &u) < 0);
+ assert_se(parse_sec("..1234", &u) < 0);
+ assert_se(parse_sec("1234..", &u) < 0);
}
static void test_parse_sec_fix_0(void) {
@@ -54,7 +78,7 @@ static void test_parse_sec_fix_0(void) {
assert_se(parse_sec_fix_0("5s", &u) >= 0);
assert_se(u == 5 * USEC_PER_SEC);
assert_se(parse_sec_fix_0("0s", &u) >= 0);
- assert_se(u == 0 * USEC_PER_SEC);
+ assert_se(u == USEC_INFINITY);
assert_se(parse_sec_fix_0("0", &u) >= 0);
assert_se(u == USEC_INFINITY);
assert_se(parse_sec_fix_0(" 0", &u) >= 0);
@@ -83,6 +107,9 @@ static void test_parse_time(void) {
assert_se(parse_time("5s", &u, USEC_PER_MSEC) >= 0);
assert_se(u == 5 * USEC_PER_SEC);
+
+ assert_se(parse_time("11111111111111y", &u, 1) == -ERANGE);
+ assert_se(parse_time("1.1111111111111y", &u, 1) >= 0);
}
static void test_parse_nsec(void) {
@@ -112,6 +139,16 @@ static void test_parse_nsec(void) {
assert_se(u == NSEC_INFINITY);
assert_se(parse_nsec(" infinity ", &u) >= 0);
assert_se(u == NSEC_INFINITY);
+ assert_se(parse_nsec("+3.1s", &u) >= 0);
+ assert_se(u == 3100 * NSEC_PER_MSEC);
+ assert_se(parse_nsec("3.1s.2", &u) >= 0);
+ assert_se(u == 3100 * NSEC_PER_MSEC);
+ assert_se(parse_nsec("3.1 .2s", &u) >= 0);
+ assert_se(u == 200 * NSEC_PER_MSEC + 3);
+ assert_se(parse_nsec("3.1 sec .2 sec", &u) >= 0);
+ assert_se(u == 3300 * NSEC_PER_MSEC);
+ assert_se(parse_nsec("3.1 sec 1.2 sec", &u) >= 0);
+ assert_se(u == 4300 * NSEC_PER_MSEC);
assert_se(parse_nsec(" xyz ", &u) < 0);
assert_se(parse_nsec("", &u) < 0);
@@ -120,6 +157,23 @@ static void test_parse_nsec(void) {
assert_se(parse_nsec(".s ", &u) < 0);
assert_se(parse_nsec(" infinity .7", &u) < 0);
assert_se(parse_nsec(".3 infinity", &u) < 0);
+ assert_se(parse_nsec("-5s ", &u) < 0);
+ assert_se(parse_nsec("-0.3s ", &u) < 0);
+ assert_se(parse_nsec("-0.0s ", &u) < 0);
+ assert_se(parse_nsec("-0.-0s ", &u) < 0);
+ assert_se(parse_nsec("0.-0s ", &u) < 0);
+ assert_se(parse_nsec("3.-0s ", &u) < 0);
+ assert_se(parse_nsec(" infinity .7", &u) < 0);
+ assert_se(parse_nsec(".3 infinity", &u) < 0);
+ assert_se(parse_nsec("3.+1s", &u) < 0);
+ assert_se(parse_nsec("3. 1s", &u) < 0);
+ assert_se(parse_nsec("3.s", &u) < 0);
+ assert_se(parse_nsec("12.34.56", &u) < 0);
+ assert_se(parse_nsec("12..34", &u) < 0);
+ assert_se(parse_nsec("..1234", &u) < 0);
+ assert_se(parse_nsec("1234..", &u) < 0);
+ assert_se(parse_nsec("1111111111111y", &u) == -ERANGE);
+ assert_se(parse_nsec("1.111111111111y", &u) >= 0);
}
static void test_format_timespan_one(usec_t x, usec_t accuracy) {
@@ -311,40 +365,40 @@ static void test_format_timestamp_utc(void) {
test_format_timestamp_utc_one(USEC_INFINITY, NULL);
}
-static void test_dual_timestamp_deserialize(void) {
+static void test_deserialize_dual_timestamp(void) {
int r;
dual_timestamp t;
log_info("/* %s */", __func__);
- r = dual_timestamp_deserialize("1234 5678", &t);
+ r = deserialize_dual_timestamp("1234 5678", &t);
assert_se(r == 0);
assert_se(t.realtime == 1234);
assert_se(t.monotonic == 5678);
- r = dual_timestamp_deserialize("1234x 5678", &t);
+ r = deserialize_dual_timestamp("1234x 5678", &t);
assert_se(r == -EINVAL);
- r = dual_timestamp_deserialize("1234 5678y", &t);
+ r = deserialize_dual_timestamp("1234 5678y", &t);
assert_se(r == -EINVAL);
- r = dual_timestamp_deserialize("-1234 5678", &t);
+ r = deserialize_dual_timestamp("-1234 5678", &t);
assert_se(r == -EINVAL);
- r = dual_timestamp_deserialize("1234 -5678", &t);
+ r = deserialize_dual_timestamp("1234 -5678", &t);
assert_se(r == -EINVAL);
/* Check that output wasn't modified. */
assert_se(t.realtime == 1234);
assert_se(t.monotonic == 5678);
- r = dual_timestamp_deserialize("+123 567", &t);
+ r = deserialize_dual_timestamp("+123 567", &t);
assert_se(r == 0);
assert_se(t.realtime == 123);
assert_se(t.monotonic == 567);
/* Check that we get "infinity" on overflow. */
- r = dual_timestamp_deserialize("18446744073709551617 0", &t);
+ r = deserialize_dual_timestamp("18446744073709551617 0", &t);
assert_se(r == 0);
assert_se(t.realtime == USEC_INFINITY);
assert_se(t.monotonic == 0);
@@ -432,7 +486,7 @@ int main(int argc, char *argv[]) {
test_usec_sub_unsigned();
test_format_timestamp();
test_format_timestamp_utc();
- test_dual_timestamp_deserialize();
+ test_deserialize_dual_timestamp();
test_usec_shift_clock();
test_in_utc_timezone();
diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c
index 3817790233..b526871655 100644
--- a/src/test/test-tmpfiles.c
+++ b/src/test/test-tmpfiles.c
@@ -13,6 +13,8 @@
#include "log.h"
#include "process-util.h"
#include "string-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "util.h"
int main(int argc, char** argv) {
@@ -21,8 +23,7 @@ int main(int argc, char** argv) {
const char *p = argv[1] ?: "/tmp";
char *pattern;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
+ test_setup_logging(LOG_DEBUG);
pattern = strjoina(p, "/systemd-test-XXXXXX");
diff --git a/src/test/test-udev.c b/src/test/test-udev.c
index bed51c1270..7a4622b875 100644
--- a/src/test/test-udev.c
+++ b/src/test/test-udev.c
@@ -11,13 +11,16 @@
#include <sys/signalfd.h>
#include <unistd.h>
+#include "device-private.h"
#include "fs-util.h"
#include "log.h"
+#include "main-func.h"
#include "missing.h"
+#include "mkdir.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "string-util.h"
-#include "udev-util.h"
+#include "tests.h"
#include "udev.h"
static int fake_filesystems(void) {
@@ -27,106 +30,102 @@ static int fake_filesystems(void) {
const char *error;
bool ignore_mount_error;
} fakefss[] = {
- { "test/tmpfs/sys", "/sys", "failed to mount test /sys", false },
- { "test/tmpfs/dev", "/dev", "failed to mount test /dev", false },
- { "test/run", "/run", "failed to mount test /run", false },
- { "test/run", "/etc/udev/rules.d", "failed to mount empty /etc/udev/rules.d", true },
- { "test/run", UDEVLIBEXECDIR "/rules.d", "failed to mount empty " UDEVLIBEXECDIR "/rules.d", true },
+ { "test/tmpfs/sys", "/sys", "Failed to mount test /sys", false },
+ { "test/tmpfs/dev", "/dev", "Failed to mount test /dev", false },
+ { "test/run", "/run", "Failed to mount test /run", false },
+ { "test/run", "/etc/udev/rules.d", "Failed to mount empty /etc/udev/rules.d", true },
+ { "test/run", UDEVLIBEXECDIR "/rules.d", "Failed to mount empty " UDEVLIBEXECDIR "/rules.d", true },
};
- unsigned int i;
+ unsigned i;
if (unshare(CLONE_NEWNS) < 0)
- return log_error_errno(errno, "failed to call unshare(): %m");
+ return log_error_errno(errno, "Failed to call unshare(): %m");
- if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0)
- return log_error_errno(errno, "failed to mount / as private: %m");
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
+ return log_error_errno(errno, "Failed to mount / as private: %m");
- for (i = 0; i < ELEMENTSOF(fakefss); i++) {
+ for (i = 0; i < ELEMENTSOF(fakefss); i++)
if (mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL) < 0) {
log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "%s: %m", fakefss[i].error);
if (!fakefss[i].ignore_mount_error)
return -errno;
}
- }
return 0;
}
-int main(int argc, char *argv[]) {
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
- _cleanup_(udev_event_unrefp) struct udev_event *event = NULL;
- _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
- _cleanup_(udev_rules_unrefp) struct udev_rules *rules = NULL;
- char syspath[UTIL_PATH_SIZE];
- const char *devpath;
- const char *action;
- int err;
+static int run(int argc, char *argv[]) {
+ _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
+ _cleanup_(udev_event_freep) UdevEvent *event = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ const char *devpath, *devname, *action;
+ int r;
+
+ test_setup_logging(LOG_INFO);
- log_parse_environment();
- log_open();
+ if (!IN_SET(argc, 2, 3)) {
+ log_error("This program needs one or two arguments, %d given", argc - 1);
+ return -EINVAL;
+ }
- err = fake_filesystems();
- if (err < 0)
- return EXIT_FAILURE;
+ r = fake_filesystems();
+ if (r < 0)
+ return r;
- udev = udev_new();
- if (udev == NULL)
- return EXIT_FAILURE;
+ if (argc == 2) {
+ if (!streq(argv[1], "check")) {
+ log_error("Unknown argument: %s", argv[1]);
+ return -EINVAL;
+ }
+
+ return 0;
+ }
log_debug("version %s", PACKAGE_VERSION);
mac_selinux_init();
action = argv[1];
- if (action == NULL) {
- log_error("action missing");
- goto out;
- }
-
devpath = argv[2];
- if (devpath == NULL) {
- log_error("devpath missing");
- goto out;
- }
- rules = udev_rules_new(udev, 1);
+ assert_se(udev_rules_new(&rules, RESOLVE_NAME_EARLY) == 0);
- strscpyl(syspath, sizeof(syspath), "/sys", devpath, NULL);
- dev = udev_device_new_from_synthetic_event(udev, syspath, action);
- if (dev == NULL) {
- log_debug("unknown device '%s'", devpath);
- goto out;
- }
+ const char *syspath = strjoina("/sys", devpath);
+ r = device_new_from_synthetic_event(&dev, syspath, action);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to open device '%s'", devpath);
- event = udev_event_new(dev);
+ assert_se(event = udev_event_new(dev, 0, NULL));
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0);
/* do what devtmpfs usually provides us */
- if (udev_device_get_devnode(dev) != NULL) {
+ if (sd_device_get_devname(dev, &devname) >= 0) {
+ const char *subsystem;
mode_t mode = 0600;
- if (streq(udev_device_get_subsystem(dev), "block"))
+ if (sd_device_get_subsystem(dev, &subsystem) >= 0 && streq(subsystem, "block"))
mode |= S_IFBLK;
else
mode |= S_IFCHR;
if (!streq(action, "remove")) {
- mkdir_parents_label(udev_device_get_devnode(dev), 0755);
- mknod(udev_device_get_devnode(dev), mode, udev_device_get_devnum(dev));
+ dev_t devnum = makedev(0, 0);
+
+ (void) mkdir_parents_label(devname, 0755);
+ (void) sd_device_get_devnum(dev, &devnum);
+ if (mknod(devname, mode, devnum) < 0)
+ return log_error_errno(errno, "mknod() failed for '%s': %m", devname);
} else {
- unlink(udev_device_get_devnode(dev));
- rmdir_parents(udev_device_get_devnode(dev), "/");
+ if (unlink(devname) < 0)
+ return log_error_errno(errno, "unlink('%s') failed: %m", devname);
+ (void) rmdir_parents(devname, "/");
}
}
- udev_event_execute_rules(event,
- 3 * USEC_PER_SEC, USEC_PER_SEC,
- NULL,
- rules);
- udev_event_execute_run(event,
- 3 * USEC_PER_SEC, USEC_PER_SEC);
-out:
- mac_selinux_finish();
+ udev_event_execute_rules(event, 3 * USEC_PER_SEC, NULL, rules);
+ udev_event_execute_run(event, 3 * USEC_PER_SEC);
- return err ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/test/test-umount.c b/src/test/test-umount.c
index 770d1a73c8..6ab5758ede 100644
--- a/src/test/test-umount.c
+++ b/src/test/test-umount.c
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include "alloc-util.h"
#include "log.h"
+#include "path-util.h"
#include "string-util.h"
#include "tests.h"
#include "umount.h"
@@ -8,10 +10,14 @@
static void test_mount_points_list(const char *fname) {
_cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
+ _cleanup_free_ char *testdata_fname = NULL;
MountPoint *m;
log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/self/mountinfo");
+ if (fname)
+ fname = testdata_fname = path_join(get_testdata_dir(), fname);
+
LIST_HEAD_INIT(mp_list_head);
assert_se(mount_points_list_get(fname, &mp_list_head) >= 0);
@@ -26,10 +32,14 @@ static void test_mount_points_list(const char *fname) {
static void test_swap_list(const char *fname) {
_cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
+ _cleanup_free_ char *testdata_fname = NULL;
MountPoint *m;
log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/swaps");
+ if (fname)
+ fname = testdata_fname = path_join(get_testdata_dir(), fname);
+
LIST_HEAD_INIT(mp_list_head);
assert_se(swap_list_get(fname, &mp_list_head) >= 0);
@@ -43,15 +53,13 @@ static void test_swap_list(const char *fname) {
}
int main(int argc, char **argv) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_mount_points_list(NULL);
- test_mount_points_list(get_testdata_dir("/test-umount/empty.mountinfo"));
- test_mount_points_list(get_testdata_dir("/test-umount/garbled.mountinfo"));
- test_mount_points_list(get_testdata_dir("/test-umount/rhbug-1554943.mountinfo"));
+ test_mount_points_list("/test-umount/empty.mountinfo");
+ test_mount_points_list("/test-umount/garbled.mountinfo");
+ test_mount_points_list("/test-umount/rhbug-1554943.mountinfo");
test_swap_list(NULL);
- test_swap_list(get_testdata_dir("/test-umount/example.swaps"));
+ test_swap_list("/test-umount/example.swaps");
}
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index 09b0179fa1..f5578f9fc2 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -7,12 +7,12 @@
#include <sys/capability.h>
#include <unistd.h>
-#include "alloc-util.h"
#include "all-units.h"
+#include "alloc-util.h"
#include "capability-util.h"
#include "conf-parser.h"
+#include "env-file.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "hashmap.h"
#include "hostname-util.h"
@@ -26,6 +26,7 @@
#include "strv.h"
#include "test-helper.h"
#include "tests.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
@@ -39,11 +40,8 @@ static int test_unit_file_get_set(void) {
assert_se(h);
r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL);
-
- if (IN_SET(r, -EPERM, -EACCES)) {
- log_notice_errno(r, "Skipping test: unit_file_get_list: %m");
- return EXIT_TEST_SKIP;
- }
+ if (IN_SET(r, -EPERM, -EACCES))
+ return log_tests_skipped_errno(r, "unit_file_get_list");
log_full_errno(r == 0 ? LOG_INFO : LOG_ERR, r,
"unit_file_get_list: %m");
@@ -532,9 +530,9 @@ static void test_load_env_file_1(void) {
fd = mkostemp_safe(name);
assert_se(fd >= 0);
- assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1));
+ assert_se(write(fd, env_file_1, strlen(env_file_1)) == strlen(env_file_1));
- r = load_env_file(NULL, name, NULL, &data);
+ r = load_env_file(NULL, name, &data);
assert_se(r == 0);
assert_se(streq(data[0], "a=a"));
assert_se(streq(data[1], "b=bc"));
@@ -554,9 +552,9 @@ static void test_load_env_file_2(void) {
fd = mkostemp_safe(name);
assert_se(fd >= 0);
- assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2));
+ assert_se(write(fd, env_file_2, strlen(env_file_2)) == strlen(env_file_2));
- r = load_env_file(NULL, name, NULL, &data);
+ r = load_env_file(NULL, name, &data);
assert_se(r == 0);
assert_se(streq(data[0], "a=a"));
assert_se(data[1] == NULL);
@@ -571,9 +569,9 @@ static void test_load_env_file_3(void) {
fd = mkostemp_safe(name);
assert_se(fd >= 0);
- assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3));
+ assert_se(write(fd, env_file_3, strlen(env_file_3)) == strlen(env_file_3));
- r = load_env_file(NULL, name, NULL, &data);
+ r = load_env_file(NULL, name, &data);
assert_se(r == 0);
assert_se(data == NULL);
}
@@ -586,9 +584,9 @@ static void test_load_env_file_4(void) {
fd = mkostemp_safe(name);
assert_se(fd >= 0);
- assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4));
+ assert_se(write(fd, env_file_4, strlen(env_file_4)) == strlen(env_file_4));
- r = load_env_file(NULL, name, NULL, &data);
+ r = load_env_file(NULL, name, &data);
assert_se(r == 0);
assert_se(streq(data[0], "HWMON_MODULES=coretemp f71882fg"));
assert_se(streq(data[1], "MODULE_0=coretemp"));
@@ -605,9 +603,9 @@ static void test_load_env_file_5(void) {
fd = mkostemp_safe(name);
assert_se(fd >= 0);
- assert_se(write(fd, env_file_5, sizeof(env_file_5)) == sizeof(env_file_5));
+ assert_se(write(fd, env_file_5, strlen(env_file_5)) == strlen(env_file_5));
- r = load_env_file(NULL, name, NULL, &data);
+ r = load_env_file(NULL, name, &data);
assert_se(r == 0);
assert_se(streq(data[0], "a="));
assert_se(streq(data[1], "b="));
@@ -624,11 +622,13 @@ static void test_install_printf(void) {
UnitFileInstallInfo i3 = { .name = name3, .path = path3, };
UnitFileInstallInfo i4 = { .name = name3, .path = path3, };
- _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL;
+ _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *gid = NULL, *group = NULL, *uid = NULL, *user = NULL;
assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
assert_se(host = gethostname_malloc());
+ assert_se(group = gid_to_name(getgid()));
+ assert_se(asprintf(&gid, UID_FMT, getgid()) >= 0);
assert_se(user = uid_to_name(getuid()));
assert_se(asprintf(&uid, UID_FMT, getuid()) >= 0);
@@ -655,6 +655,8 @@ static void test_install_printf(void) {
expect(i, "%p", "name");
expect(i, "%i", "");
expect(i, "%j", "name");
+ expect(i, "%g", group);
+ expect(i, "%G", gid);
expect(i, "%u", user);
expect(i, "%U", uid);
@@ -662,12 +664,16 @@ static void test_install_printf(void) {
expect(i, "%b", bid);
expect(i, "%H", host);
+ expect(i2, "%g", group);
+ expect(i2, "%G", gid);
expect(i2, "%u", user);
expect(i2, "%U", uid);
expect(i3, "%n", "name@inst.service");
expect(i3, "%N", "name@inst");
expect(i3, "%p", "name");
+ expect(i3, "%g", group);
+ expect(i3, "%G", gid);
expect(i3, "%u", user);
expect(i3, "%U", uid);
@@ -675,6 +681,8 @@ static void test_install_printf(void) {
expect(i3, "%b", bid);
expect(i3, "%H", host);
+ expect(i4, "%g", group);
+ expect(i4, "%G", gid);
expect(i4, "%u", user);
expect(i4, "%U", uid);
}
@@ -896,14 +904,11 @@ int main(int argc, char *argv[]) {
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
int r;
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_INFO);
r = enter_cgroup_subroot();
- if (r == -ENOMEDIUM) {
- log_notice_errno(r, "Skipping test: cgroupfs not available");
- return EXIT_TEST_SKIP;
- }
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
assert_se(runtime_dir = setup_fake_runtime_dir());
diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c
index 2b00ef8cb7..0d3674cff8 100644
--- a/src/test/test-unit-name.c
+++ b/src/test/test-unit-name.c
@@ -191,7 +191,7 @@ static void test_unit_name_mangle(void) {
}
static int test_unit_printf(void) {
- _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL, *shell = NULL, *home = NULL;
+ _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *gid = NULL, *group = NULL, *uid = NULL, *user = NULL, *shell = NULL, *home = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
Unit *u;
int r;
@@ -200,15 +200,15 @@ static int test_unit_printf(void) {
assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
assert_se(host = gethostname_malloc());
assert_se(user = uid_to_name(getuid()));
+ assert_se(group = gid_to_name(getgid()));
assert_se(asprintf(&uid, UID_FMT, getuid()));
+ assert_se(asprintf(&gid, UID_FMT, getgid()));
assert_se(get_home_dir(&home) >= 0);
assert_se(get_shell(&shell) >= 0);
r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
- if (MANAGER_SKIP_TEST(r)) {
- log_notice_errno(r, "Skipping test: manager_new: %m");
- return EXIT_TEST_SKIP;
- }
+ if (MANAGER_SKIP_TEST(r))
+ return log_tests_skipped_errno(r, "manager_new");
assert_se(r == 0);
#define expect(unit, pattern, expected) \
@@ -243,6 +243,8 @@ static int test_unit_printf(void) {
expect(u, "%I", "");
expect(u, "%j", "blah");
expect(u, "%J", "blah");
+ expect(u, "%g", group);
+ expect(u, "%G", gid);
expect(u, "%u", user);
expect(u, "%U", uid);
expect(u, "%h", home);
@@ -265,6 +267,8 @@ static int test_unit_printf(void) {
expect(u, "%I", "foo/foo");
expect(u, "%j", "blah");
expect(u, "%J", "blah");
+ expect(u, "%g", group);
+ expect(u, "%G", gid);
expect(u, "%u", user);
expect(u, "%U", uid);
expect(u, "%h", home);
@@ -811,14 +815,11 @@ int main(int argc, char* argv[]) {
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
int r, rc = 0;
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_INFO);
r = enter_cgroup_subroot();
- if (r == -ENOMEDIUM) {
- log_notice_errno(r, "Skipping test: cgroupfs not available");
- return EXIT_TEST_SKIP;
- }
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
assert_se(runtime_dir = setup_fake_runtime_dir());
diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c
index c1428fab02..801824ad67 100644
--- a/src/test/test-user-util.c
+++ b/src/test/test-user-util.c
@@ -159,7 +159,7 @@ static void test_get_user_creds_one(const char *id, const char *name, uid_t uid,
log_info("/* %s(\"%s\", \"%s\", "UID_FMT", "GID_FMT", \"%s\", \"%s\") */",
__func__, id, name, uid, gid, home, shell);
- r = get_user_creds(&id, &ruid, &rgid, &rhome, &rshell);
+ r = get_user_creds(&id, &ruid, &rgid, &rhome, &rshell, 0);
log_info_errno(r, "got \"%s\", "UID_FMT", "GID_FMT", \"%s\", \"%s\": %m",
id, ruid, rgid, strnull(rhome), strnull(rshell));
if (!synthesize_nobody() && streq(name, NOBODY_USER_NAME)) {
@@ -180,7 +180,7 @@ static void test_get_group_creds_one(const char *id, const char *name, gid_t gid
log_info("/* %s(\"%s\", \"%s\", "GID_FMT") */", __func__, id, name, gid);
- r = get_group_creds(&id, &rgid);
+ r = get_group_creds(&id, &rgid, 0);
log_info_errno(r, "got \"%s\", "GID_FMT": %m", id, rgid);
if (!synthesize_nobody() && streq(name, NOBODY_GROUP_NAME)) {
log_info("(skipping detailed tests because nobody is not synthesized)");
@@ -191,7 +191,7 @@ static void test_get_group_creds_one(const char *id, const char *name, gid_t gid
assert_se(rgid == gid);
}
-int main(int argc, char*argv[]) {
+int main(int argc, char *argv[]) {
test_uid_to_name_one(0, "root");
test_uid_to_name_one(UID_NOBODY, NOBODY_USER_NAME);
test_uid_to_name_one(0xFFFF, "65535");
diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c
index fe3db314a0..9849530ac8 100644
--- a/src/test/test-utf8.c
+++ b/src/test/test-utf8.c
@@ -1,10 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- Copyright © 2013 Dave Reisner
-***/
#include "alloc-util.h"
#include "string-util.h"
+#include "strv.h"
#include "utf8.h"
#include "util.h"
@@ -90,15 +88,25 @@ static void test_utf8_escaping_printable(void) {
}
static void test_utf16_to_utf8(void) {
- char *a = NULL;
- const uint16_t utf16[] = { htole16('a'), htole16(0xd800), htole16('b'), htole16(0xdc00), htole16('c'), htole16(0xd801), htole16(0xdc37) };
- const char utf8[] = { 'a', 'b', 'c', 0xf0, 0x90, 0x90, 0xb7, 0 };
+ const char16_t utf16[] = { htole16('a'), htole16(0xd800), htole16('b'), htole16(0xdc00), htole16('c'), htole16(0xd801), htole16(0xdc37) };
+ static const char utf8[] = { 'a', 'b', 'c', 0xf0, 0x90, 0x90, 0xb7 };
+ _cleanup_free_ char16_t *b = NULL;
+ _cleanup_free_ char *a = NULL;
- a = utf16_to_utf8(utf16, 14);
+ /* Convert UTF-16 to UTF-8, filtering embedded bad chars */
+ a = utf16_to_utf8(utf16, sizeof(utf16));
assert_se(a);
- assert_se(streq(a, utf8));
+ assert_se(memcmp(a, utf8, sizeof(utf8)) == 0);
+
+ /* Convert UTF-8 to UTF-16, and back */
+ b = utf8_to_utf16(utf8, sizeof(utf8));
+ assert_se(b);
free(a);
+ a = utf16_to_utf8(b, char16_strlen(b) * 2);
+ assert_se(a);
+ assert_se(strlen(a) == sizeof(utf8));
+ assert_se(memcmp(a, utf8, sizeof(utf8)) == 0);
}
static void test_utf8_n_codepoints(void) {
@@ -119,6 +127,28 @@ static void test_utf8_console_width(void) {
assert_se(utf8_console_width("\xF1") == (size_t) -1);
}
+static void test_utf8_to_utf16(void) {
+ const char *p;
+
+ FOREACH_STRING(p,
+ "abc",
+ "zażółcić gęślą jaźń",
+ "串",
+ "",
+ "…👊🔪💅") {
+
+ _cleanup_free_ char16_t *a = NULL;
+ _cleanup_free_ char *b = NULL;
+
+ a = utf8_to_utf16(p, strlen(p));
+ assert_se(a);
+
+ b = utf16_to_utf8(a, char16_strlen(a) * 2);
+ assert_se(b);
+ assert_se(streq(p, b));
+ }
+}
+
int main(int argc, char *argv[]) {
test_utf8_is_valid();
test_utf8_is_printable();
@@ -130,6 +160,7 @@ int main(int argc, char *argv[]) {
test_utf16_to_utf8();
test_utf8_n_codepoints();
test_utf8_console_width();
+ test_utf8_to_utf16();
return 0;
}
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 4d3e5c5b94..3c1b5f9b41 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -8,16 +8,20 @@
#include "def.h"
#include "fileio.h"
#include "fs-util.h"
+#include "missing_syscall.h"
#include "parse-util.h"
#include "process-util.h"
#include "raw-clone.h"
#include "rm-rf.h"
#include "string-util.h"
+#include "tests.h"
#include "util.h"
static void test_align_power2(void) {
unsigned long i, p2;
+ log_info("/* %s */", __func__);
+
assert_se(ALIGN_POWER2(0) == 0);
assert_se(ALIGN_POWER2(1) == 1);
assert_se(ALIGN_POWER2(2) == 2);
@@ -53,6 +57,14 @@ static void test_max(void) {
.a = CONST_MAX(10, 100),
};
int d = 0;
+ unsigned long x = 12345;
+ unsigned long y = 54321;
+ const char str[] = "a_string_constant";
+ const unsigned long long arr[] = {9999ULL, 10ULL, 0ULL, 3000ULL, 2000ULL, 1000ULL, 100ULL, 9999999ULL};
+ void *p = (void *)str;
+ void *q = (void *)&str[16];
+
+ log_info("/* %s */", __func__);
assert_cc(sizeof(val1.b) == sizeof(int) * 100);
@@ -80,6 +92,35 @@ static void test_max(void) {
assert_se(LESS_BY(4, 8) == 0);
assert_se(LESS_BY(16, LESS_BY(8, 4)) == 12);
assert_se(LESS_BY(4, LESS_BY(8, 4)) == 0);
+ assert_se(CMP(3, 5) == -1);
+ assert_se(CMP(5, 3) == 1);
+ assert_se(CMP(5, 5) == 0);
+ assert_se(CMP(x, y) == -1);
+ assert_se(CMP(y, x) == 1);
+ assert_se(CMP(x, x) == 0);
+ assert_se(CMP(y, y) == 0);
+ assert_se(CMP(UINT64_MAX, (uint64_t) 0) == 1);
+ assert_se(CMP((uint64_t) 0, UINT64_MAX) == -1);
+ assert_se(CMP(UINT64_MAX, UINT64_MAX) == 0);
+ assert_se(CMP(INT64_MIN, INT64_MAX) == -1);
+ assert_se(CMP(INT64_MAX, INT64_MIN) == 1);
+ assert_se(CMP(INT64_MAX, INT64_MAX) == 0);
+ assert_se(CMP(INT64_MIN, INT64_MIN) == 0);
+ assert_se(CMP(INT64_MAX, (int64_t) 0) == 1);
+ assert_se(CMP((int64_t) 0, INT64_MIN) == 1);
+ assert_se(CMP(INT64_MIN, (int64_t) 0) == -1);
+ assert_se(CMP((int64_t) 0, INT64_MAX) == -1);
+ assert_se(CMP(&str[2], &str[7]) == -1);
+ assert_se(CMP(&str[2], &str[2]) == 0);
+ assert_se(CMP(&str[7], (const char *)str) == 1);
+ assert_se(CMP(str[2], str[7]) == 1);
+ assert_se(CMP(str[7], *str) == 1);
+ assert_se(CMP((const unsigned long long *)arr, &arr[3]) == -1);
+ assert_se(CMP(*arr, arr[3]) == 1);
+ assert_se(CMP(p, q) == -1);
+ assert_se(CMP(q, p) == 1);
+ assert_se(CMP(p, p) == 0);
+ assert_se(CMP(q, q) == 0);
assert_se(CLAMP(-5, 0, 1) == 0);
assert_se(CLAMP(5, 0, 1) == 1);
assert_se(CLAMP(5, -10, 1) == 1);
@@ -100,6 +141,8 @@ static void test_container_of(void) {
uint32_t v2;
} _packed_ myval = { };
+ log_info("/* %s */", __func__);
+
assert_cc(sizeof(myval) == 17);
assert_se(container_of(&myval.v1, struct mytype, v1) == &myval);
assert_se(container_of(&myval.v2, struct mytype, v2) == &myval);
@@ -115,6 +158,8 @@ static void test_container_of(void) {
static void test_div_round_up(void) {
int div;
+ log_info("/* %s */", __func__);
+
/* basic tests */
assert_se(DIV_ROUND_UP(0, 8) == 0);
assert_se(DIV_ROUND_UP(1, 8) == 1);
@@ -146,6 +191,8 @@ static void test_div_round_up(void) {
}
static void test_u64log2(void) {
+ log_info("/* %s */", __func__);
+
assert_se(u64log2(0) == 0);
assert_se(u64log2(8) == 3);
assert_se(u64log2(9) == 3);
@@ -156,6 +203,8 @@ static void test_u64log2(void) {
}
static void test_protect_errno(void) {
+ log_info("/* %s */", __func__);
+
errno = 12;
{
PROTECT_ERRNO;
@@ -165,6 +214,8 @@ static void test_protect_errno(void) {
}
static void test_in_set(void) {
+ log_info("/* %s */", __func__);
+
assert_se(IN_SET(1, 1));
assert_se(IN_SET(1, 1, 2, 3, 4));
assert_se(IN_SET(2, 1, 2, 3, 4));
@@ -175,6 +226,8 @@ static void test_in_set(void) {
}
static void test_log2i(void) {
+ log_info("/* %s */", __func__);
+
assert_se(log2i(1) == 0);
assert_se(log2i(2) == 1);
assert_se(log2i(3) == 1);
@@ -185,9 +238,25 @@ static void test_log2i(void) {
assert_se(log2i(INT_MAX) == sizeof(int)*8-2);
}
+static void test_eqzero(void) {
+ const uint32_t zeros[] = {0, 0, 0};
+ const uint32_t ones[] = {1, 1};
+ const uint32_t mixed[] = {0, 1, 0, 0, 0};
+ const uint8_t longer[] = {[55] = 255};
+
+ log_info("/* %s */", __func__);
+
+ assert_se(eqzero(zeros));
+ assert_se(!eqzero(ones));
+ assert_se(!eqzero(mixed));
+ assert_se(!eqzero(longer));
+}
+
static void test_raw_clone(void) {
pid_t parent, pid, pid2;
+ log_info("/* %s */", __func__);
+
parent = getpid();
log_info("before clone: getpid()→"PID_FMT, parent);
assert_se(raw_getpid() == parent);
@@ -218,6 +287,8 @@ static void test_physical_memory(void) {
uint64_t p;
char buf[FORMAT_BYTES_MAX];
+ log_info("/* %s */", __func__);
+
p = physical_memory();
assert_se(p > 0);
assert_se(p < UINT64_MAX);
@@ -229,6 +300,8 @@ static void test_physical_memory(void) {
static void test_physical_memory_scale(void) {
uint64_t p;
+ log_info("/* %s */", __func__);
+
p = physical_memory();
assert_se(physical_memory_scale(0, 100) == 0);
@@ -263,6 +336,8 @@ static void test_physical_memory_scale(void) {
static void test_system_tasks_max(void) {
uint64_t t;
+ log_info("/* %s */", __func__);
+
t = system_tasks_max();
assert_se(t > 0);
assert_se(t < UINT64_MAX);
@@ -273,6 +348,8 @@ static void test_system_tasks_max(void) {
static void test_system_tasks_max_scale(void) {
uint64_t t;
+ log_info("/* %s */", __func__);
+
t = system_tasks_max();
assert_se(system_tasks_max_scale(0, 100) == 0);
@@ -298,8 +375,7 @@ static void test_system_tasks_max_scale(void) {
}
int main(int argc, char *argv[]) {
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_INFO);
test_align_power2();
test_max();
@@ -309,6 +385,7 @@ int main(int argc, char *argv[]) {
test_protect_errno();
test_in_set();
test_log2i();
+ test_eqzero();
test_raw_clone();
test_physical_memory();
test_physical_memory_scale();
diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c
index cb43b35bc5..2c6ca0a1a2 100644
--- a/src/test/test-watch-pid.c
+++ b/src/test/test-watch-pid.c
@@ -13,25 +13,18 @@ int main(int argc, char *argv[]) {
Unit *a, *b, *c, *u;
int r;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
-
- if (getuid() != 0) {
- log_notice("Not running as root, skipping kernel related tests.");
- return EXIT_TEST_SKIP;
- }
+ test_setup_logging(LOG_DEBUG);
+ if (getuid() != 0)
+ return log_tests_skipped("not root");
r = enter_cgroup_subroot();
- if (r == -ENOMEDIUM) {
- log_notice("cgroupfs not available, skipping tests");
- return EXIT_TEST_SKIP;
- }
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
- assert_se(set_unit_path(get_testdata_dir("")) >= 0);
+ assert_se(set_unit_path(get_testdata_dir()) >= 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
- assert_se(manager_new(UNIT_FILE_USER, true, &m) >= 0);
+ assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
assert_se(manager_startup(m, NULL, NULL) >= 0);
assert_se(a = unit_new(m, sizeof(Service)));
diff --git a/src/test/test-watchdog.c b/src/test/test-watchdog.c
index 2aba3b5a26..ab66d5c49d 100644
--- a/src/test/test-watchdog.c
+++ b/src/test/test-watchdog.c
@@ -3,8 +3,8 @@
#include <string.h>
#include <unistd.h>
-#include "env-util.h"
#include "log.h"
+#include "tests.h"
#include "watchdog.h"
int main(int argc, char *argv[]) {
@@ -13,11 +13,9 @@ int main(int argc, char *argv[]) {
int r;
bool slow;
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
+ test_setup_logging(LOG_DEBUG);
- r = getenv_bool("SYSTEMD_SLOW_TESTS");
- slow = r >= 0 ? r : SYSTEMD_SLOW_TESTS_DEFAULT;
+ slow = slow_tests_enabled();
t = slow ? 10 * USEC_PER_SEC : 1 * USEC_PER_SEC;
count = slow ? 5 : 3;
diff --git a/src/test/test-xattr-util.c b/src/test/test-xattr-util.c
index 72720dccb8..3e6df96c5d 100644
--- a/src/test/test-xattr-util.c
+++ b/src/test/test-xattr-util.c
@@ -8,10 +8,11 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "macro.h"
#include "string-util.h"
+#include "tests.h"
+#include "tmpfile-util.h"
#include "xattr-util.h"
static void test_fgetxattrat_fake(void) {
@@ -78,9 +79,7 @@ static void test_getcrtime(void) {
}
int main(void) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_DEBUG);
test_fgetxattrat_fake();
test_getcrtime();
diff --git a/src/time-wait-sync/time-wait-sync.c b/src/time-wait-sync/time-wait-sync.c
index d268fb0c5a..d02633c9a8 100644
--- a/src/time-wait-sync/time-wait-sync.c
+++ b/src/time-wait-sync/time-wait-sync.c
@@ -1,8 +1,6 @@
/*
* systemd service to wait until kernel realtime clock is synchronized
*
- * Copyright © 2018 Peter A. Bigot
- *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
@@ -24,6 +22,7 @@
#include "fd-util.h"
#include "fs-util.h"
+#include "main-func.h"
#include "missing.h"
#include "signal-util.h"
#include "time-util.h"
@@ -187,61 +186,49 @@ static int clock_state_update(
return r;
}
-int main(int argc, char * argv[]) {
- int r;
+static int run(int argc, char * argv[]) {
_cleanup_(sd_event_unrefp) sd_event *event;
- ClockState state = {
+ _cleanup_(clock_state_release) ClockState state = {
.timerfd_fd = -1,
.inotify_fd = -1,
.run_systemd_wd = -1,
.run_systemd_timesync_wd = -1,
};
+ int r;
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
r = sd_event_default(&event);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate event loop: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to create sigterm event source: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create sigterm event source: %m");
r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to create sigint event source: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create sigint event source: %m");
r = sd_event_set_watchdog(event, true);
- if (r < 0) {
- log_error_errno(r, "Failed to create watchdog event source: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create watchdog event source: %m");
r = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
- if (r < 0) {
- log_error_errno(errno, "Failed to create inotify descriptor: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(errno, "Failed to create inotify descriptor: %m");
+
state.inotify_fd = r;
r = sd_event_add_io(event, &state.inotify_event_source, state.inotify_fd,
EPOLLIN, inotify_handler, &state);
- if (r < 0) {
- log_error_errno(r, "Failed to create notify event source: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create notify event source: %m");
r = inotify_add_watch(state.inotify_fd, "/run/systemd/", IN_CREATE);
- if (r < 0) {
- log_error_errno(errno, "Failed to watch /run/systemd/: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(errno, "Failed to watch /run/systemd/: %m");
+
state.run_systemd_wd = r;
(void) update_notify_run_systemd_timesync(&state);
@@ -259,7 +246,7 @@ int main(int argc, char * argv[]) {
if (state.adjtime_state == TIME_ERROR)
log_info("Exit without adjtimex synchronized.");
- finish:
- clock_state_release(&state);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
index befc8cb723..1e7b26276a 100644
--- a/src/timedate/timedatectl.c
+++ b/src/timedate/timedatectl.c
@@ -11,8 +11,10 @@
#include "bus-error.h"
#include "bus-util.h"
#include "in-addr-util.h"
+#include "main-func.h"
#include "pager.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "spawn-polkit-agent.h"
#include "sparse-endian.h"
#include "string-table.h"
@@ -21,7 +23,7 @@
#include "util.h"
#include "verbs.h"
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static bool arg_ask_password = true;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static char *arg_host = NULL;
@@ -204,9 +206,9 @@ static int set_time(int argc, char **argv, void *userdata) {
NULL,
"xbb", (int64_t) t, relative, interactive);
if (r < 0)
- log_error("Failed to set time: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to set time: %s", bus_error_message(&error, r));
- return r;
+ return 0;
}
static int set_timezone(int argc, char **argv, void *userdata) {
@@ -225,9 +227,9 @@ static int set_timezone(int argc, char **argv, void *userdata) {
NULL,
"sb", argv[1], arg_ask_password);
if (r < 0)
- log_error("Failed to set time zone: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to set time zone: %s", bus_error_message(&error, r));
- return r;
+ return 0;
}
static int set_local_rtc(int argc, char **argv, void *userdata) {
@@ -250,9 +252,9 @@ static int set_local_rtc(int argc, char **argv, void *userdata) {
NULL,
"bbb", b, arg_adjust_system_clock, arg_ask_password);
if (r < 0)
- log_error("Failed to set local RTC: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to set local RTC: %s", bus_error_message(&error, r));
- return r;
+ return 0;
}
static int set_ntp(int argc, char **argv, void *userdata) {
@@ -275,20 +277,35 @@ static int set_ntp(int argc, char **argv, void *userdata) {
NULL,
"bb", b, arg_ask_password);
if (r < 0)
- log_error("Failed to set ntp: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to set ntp: %s", bus_error_message(&error, r));
- return r;
+ return 0;
}
static int list_timezones(int argc, char **argv, void *userdata) {
- _cleanup_strv_free_ char **zones = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
+ char** zones;
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.timedate1",
+ "/org/freedesktop/timedate1",
+ "org.freedesktop.timedate1",
+ "ListTimezones",
+ &error,
+ &reply,
+ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to request list of time zones: %s",
+ bus_error_message(&error, r));
- r = get_timezones(&zones);
+ r = sd_bus_message_read_strv(reply, &zones);
if (r < 0)
- return log_error_errno(r, "Failed to read list of time zones: %m");
+ return bus_log_parse_error(r);
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
strv_print(zones);
return 0;
@@ -436,15 +453,13 @@ static int map_server_address(sd_bus *bus, const char *member, sd_bus_message *m
return 0;
}
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_error("Unknown address family %i", family);
- return -EINVAL;
- }
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown address family %i", family);
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_error("Invalid address size");
- return -EINVAL;
- }
+ if (sz != FAMILY_ADDRESS_SIZE(family))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid address size");
r = in_addr_to_string(family, d, p);
if (r < 0)
@@ -589,15 +604,7 @@ static int show_timesync_status(int argc, char **argv, void *userdata) {
return 0;
}
-#define property(name, fmt, ...) \
- do { \
- if (value) \
- printf(fmt "\n", __VA_ARGS__); \
- else \
- printf("%s=" fmt "\n", name, __VA_ARGS__); \
- } while (0)
-
-static int print_timesync_property(const char *name, sd_bus_message *m, bool value, bool all) {
+static int print_timesync_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) {
char type;
const char *contents;
int r;
@@ -663,7 +670,7 @@ static int print_timesync_property(const char *name, sd_bus_message *m, bool val
return r;
if (arg_all || !isempty(str))
- property(name, "%s", str);
+ bus_print_property_value(name, expected_value, value, "%s", str);
return 1;
}
@@ -694,6 +701,13 @@ static int show_timesync(int argc, char **argv, void *userdata) {
}
static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("timedatectl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] COMMAND ...\n\n"
"Query or change system time and date settings.\n\n"
" -h --help Show this help message\n"
@@ -720,7 +734,10 @@ static int help(void) {
"systemd-timesyncd Commands:\n"
" timesync-status Show status of systemd-timesyncd\n"
" show-timesync Show properties of systemd-timesyncd\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
@@ -789,7 +806,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_MONITOR:
@@ -827,7 +844,6 @@ static int parse_argv(int argc, char *argv[]) {
}
static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
-
static const Verb verbs[] = {
{ "status", VERB_ANY, 1, VERB_DEFAULT, show_status },
{ "show", VERB_ANY, 1, 0, show_properties },
@@ -845,8 +861,8 @@ static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, bus);
}
-int main(int argc, char *argv[]) {
- sd_bus *bus = NULL;
+static int run(int argc, char *argv[]) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
@@ -855,21 +871,13 @@ int main(int argc, char *argv[]) {
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
- if (r < 0) {
- log_error_errno(r, "Failed to create bus connection: %m");
- goto finish;
- }
-
- r = timedatectl_main(bus, argc, argv);
-
-finish:
- /* make sure we terminate the bus connection first, and then close the
- * pager, see issue #3543 for the details. */
- sd_bus_flush_close_unref(bus);
- pager_close();
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return timedatectl_main(bus, argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index 82eb213e95..e16888945c 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -15,11 +15,15 @@
#include "clock-util.h"
#include "def.h"
#include "fileio-label.h"
+#include "fileio.h"
#include "fs-util.h"
#include "hashmap.h"
#include "list.h"
+#include "main-func.h"
+#include "missing_capability.h"
#include "path-util.h"
#include "selinux-util.h"
+#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
#include "unit-def.h"
@@ -35,6 +39,7 @@ typedef struct UnitStatusInfo {
char *load_state;
char *unit_file_state;
char *active_state;
+ char *path;
LIST_FIELDS(struct UnitStatusInfo, units);
} UnitStatusInfo;
@@ -43,6 +48,9 @@ typedef struct Context {
char *zone;
bool local_rtc;
Hashmap *polkit_registry;
+ sd_bus_message *cache;
+
+ sd_bus_slot *slot_job_removed;
LIST_HEAD(UnitStatusInfo, units);
} Context;
@@ -60,16 +68,20 @@ static void unit_status_info_free(UnitStatusInfo *p) {
unit_status_info_clear(p);
free(p->name);
+ free(p->path);
free(p);
}
-static void context_free(Context *c) {
+static void context_clear(Context *c) {
UnitStatusInfo *p;
assert(c);
free(c->zone);
bus_verify_polkit_async_registry_free(c->polkit_registry);
+ sd_bus_message_unref(c->cache);
+
+ sd_bus_slot_unref(c->slot_job_removed);
while ((p = c->units)) {
LIST_REMOVE(units, c->units, p);
@@ -301,18 +313,20 @@ static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m)
{ "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
{}
};
- static sd_bus_message *_m = NULL;
UnitStatusInfo *u;
int r;
assert(c);
assert(bus);
- /* Suppress multiple call of context_update_ntp_status() within single DBus transaction. */
- if (m && m == _m)
- return 0;
+ /* Suppress calling context_update_ntp_status() multiple times within single DBus transaction. */
+ if (m) {
+ if (m == c->cache)
+ return 0;
- _m = m;
+ sd_bus_message_unref(c->cache);
+ c->cache = sd_bus_message_ref(m);
+ }
LIST_FOREACH(units, u, c->units) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -340,18 +354,46 @@ static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m)
return 0;
}
+static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ Context *c = userdata;
+ UnitStatusInfo *u;
+ const char *path;
+ unsigned n = 0;
+ int r;
+
+ assert(c);
+ assert(m);
+
+ r = sd_bus_message_read(m, "uoss", NULL, &path, NULL, NULL);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ return 0;
+ }
+
+ LIST_FOREACH(units, u, c->units)
+ if (streq_ptr(path, u->path))
+ u->path = mfree(u->path);
+ else
+ n += !!u->path;
+
+ if (n == 0) {
+ (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
+
+ c->slot_job_removed = sd_bus_slot_unref(c->slot_job_removed);
+ }
+
+ return 0;
+}
+
static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool start) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ const char *path;
int r;
assert(u);
assert(bus);
assert(error);
- /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
-
- if (streq(u->active_state, "active") == start)
- return 0;
-
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
@@ -359,13 +401,21 @@ static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *erro
"org.freedesktop.systemd1.Manager",
start ? "StartUnit" : "StopUnit",
error,
- NULL,
+ &reply,
"ss",
u->name,
"replace");
if (r < 0)
return r;
+ r = sd_bus_message_read(reply, "o", &path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = free_and_strdup(&u->path, path);
+ if (r < 0)
+ return log_oom();
+
return 0;
}
@@ -417,8 +467,9 @@ static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *
error,
NULL,
NULL);
- if (r < 0)
- return r;
+ if (r < 0)
+ return r;
+
return 0;
}
@@ -557,13 +608,13 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *
if (c->local_rtc) {
struct timespec ts;
- struct tm *tm;
+ struct tm tm;
/* 4. Sync RTC from system clock, with the new delta */
assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
- assert_se(tm = localtime(&ts.tv_sec));
+ assert_se(localtime_r(&ts.tv_sec, &tm));
- r = clock_set_hwclock(tm);
+ r = clock_set_hwclock(&tm);
if (r < 0)
log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
}
@@ -632,9 +683,9 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
/* Sync system clock from RTC; first, initialize the timezone fields of struct tm. */
if (c->local_rtc)
- tm = *localtime(&ts.tv_sec);
+ localtime_r(&ts.tv_sec, &tm);
else
- tm = *gmtime(&ts.tv_sec);
+ gmtime_r(&ts.tv_sec, &tm);
/* Override the main fields of struct tm, but not the timezone fields */
r = clock_get_hwclock(&tm);
@@ -652,15 +703,15 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
}
} else {
- struct tm *tm;
+ struct tm tm;
/* Sync RTC from system clock */
if (c->local_rtc)
- tm = localtime(&ts.tv_sec);
+ localtime_r(&ts.tv_sec, &tm);
else
- tm = gmtime(&ts.tv_sec);
+ gmtime_r(&ts.tv_sec, &tm);
- r = clock_set_hwclock(tm);
+ r = clock_set_hwclock(&tm);
if (r < 0)
log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
}
@@ -679,7 +730,7 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro
int64_t utc;
struct timespec ts;
usec_t start;
- struct tm* tm;
+ struct tm tm;
assert(m);
assert(c);
@@ -748,11 +799,11 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro
/* Sync down to RTC */
if (c->local_rtc)
- tm = localtime(&ts.tv_sec);
+ localtime_r(&ts.tv_sec, &tm);
else
- tm = gmtime(&ts.tv_sec);
+ gmtime_r(&ts.tv_sec, &tm);
- r = clock_set_hwclock(tm);
+ r = clock_set_hwclock(&tm);
if (r < 0)
log_debug_errno(r, "Failed to update hardware clock, ignoring: %m");
@@ -765,6 +816,7 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro
}
static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
sd_bus *bus = sd_bus_message_get_bus(m);
Context *c = userdata;
UnitStatusInfo *u;
@@ -799,6 +851,23 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
if (r == 0)
return 1;
+ /* This method may be called frequently. Forget the previous job if it has not completed yet. */
+ LIST_FOREACH(units, u, c->units)
+ u->path = mfree(u->path);
+
+ if (!c->slot_job_removed) {
+ r = sd_bus_match_signal_async(
+ bus,
+ &slot,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "JobRemoved",
+ match_job_removed, NULL, c);
+ if (r < 0)
+ return r;
+ }
+
if (!enable)
LIST_FOREACH(units, u, c->units) {
if (!streq(u->load_state, "loaded"))
@@ -826,7 +895,7 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
break;
}
- else if (context_ntp_service_is_active(c) <= 0)
+ else
LIST_FOREACH(units, u, c->units) {
if (!streq(u->load_state, "loaded") ||
!streq(u->unit_file_state, "enabled"))
@@ -839,13 +908,36 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
if (r < 0)
return r;
- log_info("Set NTP to %sd", enable_disable(enable));
+ if (slot)
+ c->slot_job_removed = TAKE_PTR(slot);
- (void) sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
+ log_info("Set NTP to %sd", enable_disable(enable));
return sd_bus_reply_method_return(m, NULL);
}
+static int method_list_timezones(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_strv_free_ char **zones = NULL;
+ int r;
+
+ assert(m);
+
+ r = get_timezones(&zones);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to read list of time zones: %m");
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_strv(reply, zones);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
static const sd_bus_vtable timedate_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -859,6 +951,7 @@ static const sd_bus_vtable timedate_vtable[] = {
SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ListTimezones", NULL, "as", method_list_timezones, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
@@ -891,56 +984,54 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
return 0;
}
-int main(int argc, char *argv[]) {
- Context context = {};
+static int run(int argc, char *argv[]) {
+ _cleanup_(context_clear) Context context = {};
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
- if (argc != 1) {
- log_error("This program takes no arguments.");
- r = -EINVAL;
- goto finish;
- }
+ if (argc != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
r = sd_event_default(&event);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate event loop: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
- sd_event_set_watchdog(event, true);
+ (void) sd_event_set_watchdog(event, true);
+
+ r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to install SIGINT handler: %m");
+
+ r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to install SIGTERM handler: %m");
r = connect_bus(&context, event, &bus);
if (r < 0)
- goto finish;
+ return r;
(void) sd_bus_negotiate_timestamp(bus, true);
r = context_read_data(&context);
- if (r < 0) {
- log_error_errno(r, "Failed to read time zone data: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read time zone data: %m");
r = context_parse_ntp_services(&context);
if (r < 0)
- goto finish;
+ return r;
r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- goto finish;
- }
-
-finish:
- context_free(&context);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/timesync/test-timesync.c b/src/timesync/test-timesync.c
index a045eeeec1..bd03a1dd90 100644
--- a/src/timesync/test-timesync.c
+++ b/src/timesync/test-timesync.c
@@ -5,6 +5,7 @@
#include "log.h"
#include "macro.h"
#include "timesyncd-conf.h"
+#include "tests.h"
static void test_manager_parse_string(void) {
/* Make sure that NTP_SERVERS is configured to something
@@ -25,8 +26,7 @@ static void test_manager_parse_string(void) {
}
int main(int argc, char **argv) {
- log_set_max_level(LOG_DEBUG);
- log_parse_environment();
+ test_setup_logging(LOG_DEBUG);
test_manager_parse_string();
diff --git a/src/timesync/timesyncd-bus.c b/src/timesync/timesyncd-bus.c
index f2527f52e4..5a5896f0b7 100644
--- a/src/timesync/timesyncd-bus.c
+++ b/src/timesync/timesyncd-bus.c
@@ -80,6 +80,8 @@ static int property_get_current_server_address(
if (!a)
return sd_bus_message_append(reply, "(iay)", AF_UNSPEC, 0);
+ assert(IN_SET(a->sockaddr.sa.sa_family, AF_INET, AF_INET6));
+
r = sd_bus_message_open_container(reply, 'r', "iay");
if (r < 0)
return r;
@@ -88,7 +90,9 @@ static int property_get_current_server_address(
if (r < 0)
return r;
- r = sd_bus_message_append_array(reply, 'y', &a->sockaddr.in.sin_addr, FAMILY_ADDRESS_SIZE(a->sockaddr.sa.sa_family));
+ r = sd_bus_message_append_array(reply, 'y',
+ a->sockaddr.sa.sa_family == AF_INET ? (void*) &a->sockaddr.in.sin_addr : (void*) &a->sockaddr.in6.sin6_addr,
+ FAMILY_ADDRESS_SIZE(a->sockaddr.sa.sa_family));
if (r < 0)
return r;
@@ -185,7 +189,7 @@ int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to add manager object vtable: %m");
- r = bus_request_name_async_may_reload_dbus(m->bus, NULL, "org.freedesktop.timesync1", 0, NULL);
+ r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.timesync1", 0, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to request name: %m");
diff --git a/src/timesync/timesyncd-conf.h b/src/timesync/timesyncd-conf.h
index ebdb143f0b..bbe27cf2ac 100644
--- a/src/timesync/timesyncd-conf.h
+++ b/src/timesync/timesyncd-conf.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "conf-parser.h"
#include "timesyncd-manager.h"
diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c
index 2b731af9e3..4b0696f3a3 100644
--- a/src/timesync/timesyncd-manager.c
+++ b/src/timesync/timesyncd-manager.c
@@ -23,6 +23,7 @@
#include "missing.h"
#include "network-util.h"
#include "ratelimit.h"
+#include "resolve-private.h"
#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
@@ -320,7 +321,7 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
}
static bool manager_sample_spike_detection(Manager *m, double offset, double delay) {
- unsigned int i, idx_cur, idx_new, idx_min;
+ unsigned i, idx_cur, idx_new, idx_min;
double jitter;
double j;
@@ -471,10 +472,9 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
break;
}
}
- if (!recv_time) {
- log_error("Invalid packet timestamp.");
- return -EINVAL;
- }
+ if (!recv_time)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid packet timestamp.");
if (!m->pending) {
log_debug("Unexpected reply. Ignoring.");
@@ -604,7 +604,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
m->dest_time = *recv_time;
m->spike = spike;
- log_debug("interval/delta/delay/jitter/drift " USEC_FMT "s/%+.3fs/%.3fs/%.3fs/%+"PRI_TIMEX"ppm%s",
+ log_debug("interval/delta/delay/jitter/drift " USEC_FMT "s/%+.3fs/%.3fs/%.3fs/%+"PRIi64"ppm%s",
m->poll_interval_usec / USEC_PER_SEC, offset, delay, m->samples_jitter, m->drift_freq / 65536,
spike ? " (ignored)" : "");
@@ -629,8 +629,6 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
static int manager_listen_setup(Manager *m) {
union sockaddr_union addr = {};
- static const int tos = IPTOS_LOWDELAY;
- static const int on = 1;
int r;
assert(m);
@@ -651,11 +649,11 @@ static int manager_listen_setup(Manager *m) {
if (r < 0)
return -errno;
- r = setsockopt(m->server_socket, SOL_SOCKET, SO_TIMESTAMPNS, &on, sizeof(on));
+ r = setsockopt_int(m->server_socket, SOL_SOCKET, SO_TIMESTAMPNS, true);
if (r < 0)
- return -errno;
+ return r;
- (void) setsockopt(m->server_socket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+ (void) setsockopt_int(m->server_socket, IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY);
return sd_event_add_io(m->event, &m->event_receive, m->server_socket, EPOLLIN, manager_receive_response, m);
}
@@ -727,8 +725,7 @@ void manager_set_server_address(Manager *m, ServerAddress *a) {
}
}
-static int manager_resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) {
- Manager *m = userdata;
+static int manager_resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, Manager *m) {
int r;
assert(q);
@@ -793,7 +790,7 @@ int manager_connect(Manager *m) {
m->event_retry = sd_event_source_unref(m->event_retry);
if (!ratelimit_below(&m->ratelimit)) {
- log_debug("Slowing down attempts to contact servers.");
+ log_debug("Delaying attempts to contact servers.");
r = sd_event_add_time(m->event, &m->event_retry, clock_boottime_or_monotonic(), now(clock_boottime_or_monotonic()) + RETRY_USEC, 0, manager_retry_connect, m);
if (r < 0)
@@ -876,7 +873,7 @@ int manager_connect(Manager *m) {
log_debug("Resolving %s...", m->current_server_name->string);
- r = sd_resolve_getaddrinfo(m->resolve, &m->resolve_query, m->current_server_name->string, "123", &hints, manager_resolve_handler, m);
+ r = resolve_getaddrinfo(m->resolve, &m->resolve_query, m->current_server_name->string, "123", &hints, manager_resolve_handler, NULL, m);
if (r < 0)
return log_error_errno(r, "Failed to create resolver: %m");
@@ -1101,10 +1098,10 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
- sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
- sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+ (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
+ (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
- sd_event_set_watchdog(m->event, true);
+ (void) sd_event_set_watchdog(m->event, true);
r = sd_resolve_default(&m->resolve);
if (r < 0)
diff --git a/src/timesync/timesyncd-manager.h b/src/timesync/timesyncd-manager.h
index d8d97cc1ee..97c4e2ff34 100644
--- a/src/timesync/timesyncd-manager.h
+++ b/src/timesync/timesyncd-manager.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <sys/timex.h>
#include "sd-bus.h"
@@ -72,14 +71,14 @@ struct Manager {
double offset;
double delay;
} samples[8];
- unsigned int samples_idx;
+ unsigned samples_idx;
double samples_jitter;
usec_t max_root_distance_usec;
/* last change */
bool jumped;
bool sync;
- long drift_freq;
+ int64_t drift_freq;
/* watch for time changes */
sd_event_source *event_clock_watch;
diff --git a/src/timesync/timesyncd-server.h b/src/timesync/timesyncd-server.h
index 8972706f20..d30bd2a3ce 100644
--- a/src/timesync/timesyncd-server.h
+++ b/src/timesync/timesyncd-server.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include "list.h"
#include "socket-util.h"
diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index 987cded009..70774d757b 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -5,8 +5,10 @@
#include "capability-util.h"
#include "clock-util.h"
+#include "daemon-util.h"
#include "fd-util.h"
#include "fs-util.h"
+#include "main-func.h"
#include "mkdir.h"
#include "network-util.h"
#include "process-util.h"
@@ -83,62 +85,52 @@ settime:
return 0;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
+ _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
const char *user = "systemd-timesync";
uid_t uid, uid_current;
gid_t gid;
int r;
- log_set_target(LOG_TARGET_AUTO);
log_set_facility(LOG_CRON);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
- if (argc != 1) {
- log_error("This program does not take arguments.");
- r = -EINVAL;
- goto finish;
- }
+ if (argc != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program does not take arguments.");
uid = uid_current = geteuid();
gid = getegid();
if (uid_current == 0) {
- r = get_user_creds(&user, &uid, &gid, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "Cannot resolve user name %s: %m", user);
- goto finish;
- }
+ r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Cannot resolve user name %s: %m", user);
}
r = load_clock_timestamp(uid, gid);
if (r < 0)
- goto finish;
+ return r;
/* Drop privileges, but only if we have been started as root. If we are not running as root we assume all
* privileges are already dropped. */
if (uid_current == 0) {
r = drop_privileges(uid, gid, (1ULL << CAP_SYS_TIME));
if (r < 0)
- goto finish;
+ return log_error_errno(r, "Failed to drop privileges: %m");
}
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
r = manager_new(&m);
- if (r < 0) {
- log_error_errno(r, "Failed to allocate manager: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate manager: %m");
r = manager_connect_bus(m);
- if (r < 0) {
- log_error_errno(r, "Could not connect to bus: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not connect to bus: %m");
if (clock_is_localtime(NULL) > 0) {
log_info("The system is configured to read the RTC time in the local time zone. "
@@ -151,27 +143,24 @@ int main(int argc, char *argv[]) {
log_warning_errno(r, "Failed to parse configuration file: %m");
r = manager_parse_fallback_string(m, NTP_SERVERS);
- if (r < 0) {
- log_error_errno(r, "Failed to parse fallback server strings: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse fallback server strings: %m");
log_debug("systemd-timesyncd running as pid " PID_FMT, getpid_cached());
- sd_notify(false,
- "READY=1\n"
- "STATUS=Daemon is running");
+
+ notify_message = notify_start("READY=1\n"
+ "STATUS=Daemon is running",
+ NOTIFY_STOPPING);
if (network_is_online()) {
r = manager_connect(m);
if (r < 0)
- goto finish;
+ return r;
}
r = sd_event_loop(m->event);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
/* if we got an authoritative time, store it in the file system */
if (m->sync) {
@@ -180,12 +169,9 @@ int main(int argc, char *argv[]) {
log_debug_errno(r, "Failed to touch %s, ignoring: %m", CLOCK_FILE);
}
- sd_event_get_exit_code(m->event, &r);
-
-finish:
- sd_notify(false,
- "STOPPING=1\n"
- "STATUS=Shutting down...");
+ (void) sd_event_get_exit_code(m->event, &r);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 927de35f32..19225f8cfd 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -39,13 +39,15 @@
#include "label.h"
#include "log.h"
#include "macro.h"
+#include "main-func.h"
#include "missing.h"
#include "mkdir.h"
-#include "mount-util.h"
+#include "mountpoint-util.h"
#include "pager.h"
#include "parse-util.h"
#include "path-lookup.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "rm-rf.h"
#include "selinux-util.h"
#include "set.h"
@@ -55,7 +57,6 @@
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
-#include "terminal-util.h"
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
@@ -65,6 +66,12 @@
* properly owned directories beneath /tmp, /var/tmp, /run, which are
* volatile and hence need to be recreated on bootup. */
+typedef enum OperationMask {
+ OPERATION_CREATE = 1 << 0,
+ OPERATION_REMOVE = 1 << 1,
+ OPERATION_CLEAN = 1 << 2,
+} OperationMask;
+
typedef enum ItemType {
/* These ones take file names */
CREATE_FILE = 'f',
@@ -128,17 +135,22 @@ typedef struct Item {
bool force:1;
- bool done:1;
+ bool allow_failure:1;
+
+ OperationMask done;
} Item;
typedef struct ItemArray {
Item *items;
- size_t count;
- size_t size;
+ size_t n_items;
+ size_t allocated;
+
+ struct ItemArray *parent;
+ Set *children;
} ItemArray;
typedef enum DirectoryType {
- DIRECTORY_RUNTIME = 0,
+ DIRECTORY_RUNTIME,
DIRECTORY_STATE,
DIRECTORY_CACHE,
DIRECTORY_LOGS,
@@ -147,11 +159,9 @@ typedef enum DirectoryType {
static bool arg_cat_config = false;
static bool arg_user = false;
-static bool arg_create = false;
-static bool arg_clean = false;
-static bool arg_remove = false;
+static OperationMask arg_operation = 0;
static bool arg_boot = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static char **arg_include_prefixes = NULL;
static char **arg_exclude_prefixes = NULL;
@@ -163,8 +173,15 @@ static char *arg_replace = NULL;
static OrderedHashmap *items = NULL, *globs = NULL;
static Set *unix_sockets = NULL;
-static int specifier_machine_id_safe(char specifier, void *data, void *userdata, char **ret);
-static int specifier_directory(char specifier, void *data, void *userdata, char **ret);
+STATIC_DESTRUCTOR_REGISTER(items, ordered_hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(globs, ordered_hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(unix_sockets, set_free_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_include_prefixes, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+
+static int specifier_machine_id_safe(char specifier, const void *data, const void *userdata, char **ret);
+static int specifier_directory(char specifier, const void *data, const void *userdata, char **ret);
static const Specifier specifier_table[] = {
{ 'm', specifier_machine_id_safe, NULL },
@@ -172,6 +189,8 @@ static const Specifier specifier_table[] = {
{ 'H', specifier_host_name, NULL },
{ 'v', specifier_kernel_release, NULL },
+ { 'g', specifier_group_name, NULL },
+ { 'G', specifier_group_id, NULL },
{ 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
{ 'h', specifier_user_home, NULL },
@@ -185,7 +204,7 @@ static const Specifier specifier_table[] = {
{}
};
-static int specifier_machine_id_safe(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_machine_id_safe(char specifier, const void *data, const void *userdata, char **ret) {
int r;
/* If /etc/machine_id is missing or empty (e.g. in a chroot environment)
@@ -199,7 +218,7 @@ static int specifier_machine_id_safe(char specifier, void *data, void *userdata,
return r;
}
-static int specifier_directory(char specifier, void *data, void *userdata, char **ret) {
+static int specifier_directory(char specifier, const void *data, const void *userdata, char **ret) {
struct table_entry {
uint64_t type;
const char *suffix;
@@ -348,9 +367,9 @@ static struct Item* find_glob(OrderedHashmap *h, const char *match) {
Iterator i;
ORDERED_HASHMAP_FOREACH(j, h, i) {
- unsigned n;
+ size_t n;
- for (n = 0; n < j->count; n++) {
+ for (n = 0; n < j->n_items; n++) {
Item *item = j->items + n;
if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
@@ -361,48 +380,39 @@ static struct Item* find_glob(OrderedHashmap *h, const char *match) {
return NULL;
}
-static void load_unix_sockets(void) {
+static int load_unix_sockets(void) {
+ _cleanup_set_free_free_ Set *sockets = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
if (unix_sockets)
- return;
+ return 0;
/* We maintain a cache of the sockets we found in /proc/net/unix to speed things up a little. */
- unix_sockets = set_new(&path_hash_ops);
- if (!unix_sockets) {
- log_oom();
- return;
- }
+ sockets = set_new(&path_hash_ops);
+ if (!sockets)
+ return log_oom();
f = fopen("/proc/net/unix", "re");
- if (!f) {
- log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
- "Failed to open /proc/net/unix, ignoring: %m");
- goto fail;
- }
+ if (!f)
+ return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to open /proc/net/unix, ignoring: %m");
/* Skip header */
r = read_line(f, LONG_LINE_MAX, NULL);
- if (r < 0) {
- log_warning_errno(r, "Failed to skip /proc/net/unix header line: %m");
- goto fail;
- }
- if (r == 0) {
- log_warning("Premature end of file reading /proc/net/unix.");
- goto fail;
- }
+ if (r < 0)
+ return log_warning_errno(r, "Failed to skip /proc/net/unix header line: %m");
+ if (r == 0)
+ return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Premature end of file reading /proc/net/unix.");
for (;;) {
- _cleanup_free_ char *line = NULL;
- char *p, *s;
+ _cleanup_free_ char *line = NULL, *s = NULL;
+ char *p;
r = read_line(f, LONG_LINE_MAX, &line);
- if (r < 0) {
- log_warning_errno(r, "Failed to read /proc/net/unix line, ignoring: %m");
- goto fail;
- }
+ if (r < 0)
+ return log_warning_errno(r, "Failed to read /proc/net/unix line, ignoring: %m");
if (r == 0) /* EOF */
break;
@@ -422,36 +432,31 @@ static void load_unix_sockets(void) {
continue;
s = strdup(p);
- if (!s) {
- log_oom();
- goto fail;
- }
+ if (!s)
+ return log_oom();
path_simplify(s, false);
- r = set_consume(unix_sockets, s);
- if (r < 0 && r != -EEXIST) {
- log_warning_errno(r, "Failed to add AF_UNIX socket to set, ignoring: %m");
- goto fail;
- }
- }
+ r = set_consume(sockets, s);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return log_warning_errno(r, "Failed to add AF_UNIX socket to set, ignoring: %m");
- return;
+ TAKE_PTR(s);
+ }
-fail:
- unix_sockets = set_free_free(unix_sockets);
+ unix_sockets = TAKE_PTR(sockets);
+ return 1;
}
static bool unix_socket_alive(const char *fn) {
assert(fn);
- load_unix_sockets();
-
- if (unix_sockets)
- return !!set_get(unix_sockets, (char*) fn);
+ if (load_unix_sockets() < 0)
+ return true; /* We don't know, so assume yes */
- /* We don't know, so assume yes */
- return true;
+ return !!set_get(unix_sockets, (char*) fn);
}
static int dir_is_mount_point(DIR *d, const char *subdir) {
@@ -770,25 +775,27 @@ static bool hardlink_vulnerable(const struct stat *st) {
return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks();
}
-static int fd_set_perms(Item *i, int fd, const struct stat *st) {
- _cleanup_free_ char *path = NULL;
- int r;
+static int fd_set_perms(Item *i, int fd, const char *path, const struct stat *st) {
+ struct stat stbuf;
assert(i);
assert(fd);
-
- r = fd_get_path(fd, &path);
- if (r < 0)
- return r;
+ assert(path);
if (!i->mode_set && !i->uid_set && !i->gid_set)
goto shortcut;
- if (hardlink_vulnerable(st)) {
- log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
- return -EPERM;
+ if (!st) {
+ if (fstat(fd, &stbuf) < 0)
+ return log_error_errno(errno, "fstat(%s) failed: %m", path);
+ st = &stbuf;
}
+ if (hardlink_vulnerable(st))
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.",
+ path);
+
if (i->mode_set) {
if (S_ISLNK(st->st_mode))
log_debug("Skipping mode fix for symlink %s.", path);
@@ -835,35 +842,58 @@ shortcut:
return label_fix(path, 0);
}
-static int path_set_perms(Item *i, const char *path) {
- _cleanup_close_ int fd = -1;
- struct stat st;
+static int path_open_parent_safe(const char *path) {
+ _cleanup_free_ char *dn = NULL;
+ int fd;
+
+ if (path_equal(path, "/") || !path_is_normalized(path))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to open parent of '%s': invalid path.",
+ path);
+
+ dn = dirname_malloc(path);
+ if (!dn)
+ return log_oom();
+
+ fd = chase_symlinks(dn, NULL, CHASE_OPEN|CHASE_SAFE|CHASE_WARN, NULL);
+ if (fd < 0 && fd != -ENOLINK)
+ return log_error_errno(fd, "Failed to validate path %s: %m", path);
+
+ return fd;
+}
+
+static int path_open_safe(const char *path) {
+ int fd;
+
+ /* path_open_safe() returns a file descriptor opened with O_PATH after
+ * verifying that the path doesn't contain unsafe transitions, except
+ * for its final component as the function does not follow symlink. */
- assert(i);
assert(path);
- fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
- if (fd < 0) {
- int level = LOG_ERR, r = -errno;
+ if (!path_is_normalized(path))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to open invalid path '%s'.",
+ path);
- /* Option "e" operates only on existing objects. Do not
- * print errors about non-existent files or directories */
- if (i->type == EMPTY_DIRECTORY && errno == ENOENT) {
- level = LOG_DEBUG;
- r = 0;
- }
+ fd = chase_symlinks(path, NULL, CHASE_OPEN|CHASE_SAFE|CHASE_WARN|CHASE_NOFOLLOW, NULL);
+ if (fd < 0 && fd != -ENOLINK)
+ return log_error_errno(fd, "Failed to validate path %s: %m", path);
- log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path);
- return r;
- }
+ return fd;
+}
+
+static int path_set_perms(Item *i, const char *path) {
+ _cleanup_close_ int fd = -1;
- if (fstat(fd, &st) < 0)
- return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
+ assert(i);
+ assert(path);
- if (i->type == EMPTY_DIRECTORY && !S_ISDIR(st.st_mode))
- return log_error_errno(EEXIST, "'%s' already exists and is not a directory. ", path);
+ fd = path_open_safe(path);
+ if (fd < 0)
+ return fd;
- return fd_set_perms(i, fd, &st);
+ return fd_set_perms(i, fd, path, NULL);
}
static int parse_xattrs_from_arg(Item *i) {
@@ -904,18 +934,13 @@ static int parse_xattrs_from_arg(Item *i) {
return 0;
}
-static int fd_set_xattrs(Item *i, int fd, const struct stat *st) {
+static int fd_set_xattrs(Item *i, int fd, const char *path, const struct stat *st) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
- _cleanup_free_ char *path = NULL;
char **name, **value;
- int r;
assert(i);
assert(fd);
-
- r = fd_get_path(fd, &path);
- if (r < 0)
- return r;
+ assert(path);
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
@@ -934,11 +959,11 @@ static int path_set_xattrs(Item *i, const char *path) {
assert(i);
assert(path);
- fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ fd = path_open_safe(path);
if (fd < 0)
- return log_error_errno(errno, "Cannot open '%s': %m", path);
+ return fd;
- return fd_set_xattrs(i, fd, NULL);
+ return fd_set_xattrs(i, fd, path, NULL);
}
static int parse_acls_from_arg(Item *item) {
@@ -954,7 +979,7 @@ static int parse_acls_from_arg(Item *item) {
if (r < 0)
log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument);
#else
- log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
+ log_warning_errno(SYNTHETIC_ERRNO(ENOSYS), "ACLs are not supported. Ignoring");
#endif
return 0;
@@ -1006,25 +1031,27 @@ static int path_set_acl(const char *path, const char *pretty, acl_type_t type, a
}
#endif
-static int fd_set_acls(Item *item, int fd, const struct stat *st) {
+static int fd_set_acls(Item *item, int fd, const char *path, const struct stat *st) {
int r = 0;
#if HAVE_ACL
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
- _cleanup_free_ char *path = NULL;
+ struct stat stbuf;
assert(item);
assert(fd);
- assert(st);
-
- r = fd_get_path(fd, &path);
- if (r < 0)
- return r;
+ assert(path);
- if (hardlink_vulnerable(st)) {
- log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
- return -EPERM;
+ if (!st) {
+ if (fstat(fd, &stbuf) < 0)
+ return log_error_errno(errno, "fstat(%s) failed: %m", path);
+ st = &stbuf;
}
+ if (hardlink_vulnerable(st))
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.",
+ path);
+
if (S_ISLNK(st->st_mode)) {
log_debug("Skipping ACL fix for symlink %s.", path);
return 0;
@@ -1035,7 +1062,8 @@ static int fd_set_acls(Item *item, int fd, const struct stat *st) {
if (item->acl_access)
r = path_set_acl(procfs_path, path, ACL_TYPE_ACCESS, item->acl_access, item->force);
- if (r == 0 && item->acl_default)
+ /* set only default acls to folders */
+ if (r == 0 && item->acl_default && S_ISDIR(st->st_mode))
r = path_set_acl(procfs_path, path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
if (r > 0)
@@ -1052,24 +1080,20 @@ static int fd_set_acls(Item *item, int fd, const struct stat *st) {
static int path_set_acls(Item *item, const char *path) {
int r = 0;
-#ifdef HAVE_ACL
+#if HAVE_ACL
_cleanup_close_ int fd = -1;
- struct stat st;
assert(item);
assert(path);
- fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
+ fd = path_open_safe(path);
if (fd < 0)
- return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path);
-
- if (fstat(fd, &st) < 0)
- return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
+ return fd;
- r = fd_set_acls(item, fd, &st);
- #endif
- return r;
- }
+ r = fd_set_acls(item, fd, path, NULL);
+#endif
+ return r;
+}
#define ATTRIBUTES_ALL \
(FS_NOATIME_FL | \
@@ -1134,10 +1158,10 @@ static int parse_attribute_from_arg(Item *item) {
}
}
- if (isempty(p) && mode != MODE_SET) {
- log_error("Setting file attribute on '%s' needs an attribute specification.", item->path);
- return -EINVAL;
- }
+ if (isempty(p) && mode != MODE_SET)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Setting file attribute on '%s' needs an attribute specification.",
+ item->path);
for (; p && *p ; p++) {
unsigned i, v;
@@ -1146,10 +1170,10 @@ static int parse_attribute_from_arg(Item *item) {
if (*p == attributes[i].character)
break;
- if (i >= ELEMENTSOF(attributes)) {
- log_error("Unknown file attribute '%c' on '%s'.", *p, item->path);
- return -EINVAL;
- }
+ if (i >= ELEMENTSOF(attributes))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown file attribute '%c' on '%s'.",
+ *p, item->path);
v = attributes[i].value;
@@ -1170,26 +1194,32 @@ static int parse_attribute_from_arg(Item *item) {
return 0;
}
-static int fd_set_attribute(Item *item, int fd, const struct stat *st) {
+static int fd_set_attribute(Item *item, int fd, const char *path, const struct stat *st) {
_cleanup_close_ int procfs_fd = -1;
- _cleanup_free_ char *path = NULL;
+ struct stat stbuf;
unsigned f;
int r;
+ assert(item);
+ assert(fd);
+ assert(path);
+
if (!item->attribute_set || item->attribute_mask == 0)
return 0;
- r = fd_get_path(fd, &path);
- if (r < 0)
- return r;
+ if (!st) {
+ if (fstat(fd, &stbuf) < 0)
+ return log_error_errno(errno, "fstat(%s) failed: %m", path);
+ st = &stbuf;
+ }
/* Issuing the file attribute ioctls on device nodes is not
* safe, as that will be delivered to the drivers, not the
* file system containing the device node. */
- if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) {
- log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path);
- return -EINVAL;
- }
+ if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Setting file flags is only supported on regular files and directories, cannot set on '%s'.",
+ path);
f = item->attribute_value & item->attribute_mask;
@@ -1199,13 +1229,13 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) {
procfs_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NOATIME);
if (procfs_fd < 0)
- return -errno;
+ return log_error_errno(procfs_fd, "Failed to re-open '%s': %m", path);
- r = chattr_fd(procfs_fd, f, item->attribute_mask);
+ r = chattr_fd(procfs_fd, f, item->attribute_mask, NULL);
if (r < 0)
log_full_errno(IN_SET(r, -ENOTTY, -EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING,
r,
- "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
+ "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x, ignoring: %m",
path, item->attribute_value, item->attribute_mask);
return 0;
@@ -1213,99 +1243,569 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) {
static int path_set_attribute(Item *item, const char *path) {
_cleanup_close_ int fd = -1;
- struct stat st;
if (!item->attribute_set || item->attribute_mask == 0)
return 0;
- fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ fd = path_open_safe(path);
if (fd < 0)
- return log_error_errno(errno, "Cannot open '%s': %m", path);
+ return fd;
- if (fstat(fd, &st) < 0)
- return log_error_errno(errno, "Cannot stat '%s': %m", path);
-
- return fd_set_attribute(item, fd, &st);
+ return fd_set_attribute(item, fd, path, NULL);
}
static int write_one_file(Item *i, const char *path) {
- _cleanup_close_ int fd = -1;
- int flags, r = 0;
- struct stat st;
+ _cleanup_close_ int fd = -1, dir_fd = -1;
+ char *bn;
+ int r;
assert(i);
assert(path);
+ assert(i->argument);
+ assert(i->type == WRITE_FILE);
+
+ /* Validate the path and keep the fd on the directory for opening the
+ * file so we're sure that it can't be changed behind our back. */
+ dir_fd = path_open_parent_safe(path);
+ if (dir_fd < 0)
+ return dir_fd;
+
+ bn = basename(path);
+
+ /* Follows symlinks */
+ fd = openat(dir_fd, bn, O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
+ if (fd < 0) {
+ if (errno == ENOENT) {
+ log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
+ return 0;
+ }
+ return log_error_errno(errno, "Failed to open file \"%s\": %m", path);
+ }
- flags = i->type == CREATE_FILE ? O_CREAT|O_EXCL|O_NOFOLLOW :
- i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
+ /* 'w' is allowed to write into any kind of files. */
+ log_debug("Writing to \"%s\".", path);
+
+ r = loop_write(fd, i->argument, strlen(i->argument), false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write file \"%s\": %m", path);
+
+ return fd_set_perms(i, fd, path, NULL);
+}
+
+static int create_file(Item *i, const char *path) {
+ _cleanup_close_ int fd = -1, dir_fd = -1;
+ struct stat stbuf, *st = NULL;
+ int r = 0;
+ char *bn;
+
+ assert(i);
+ assert(path);
+ assert(i->type == CREATE_FILE);
+
+ /* 'f' operates on regular files exclusively. */
+
+ /* Validate the path and keep the fd on the directory for opening the
+ * file so we're sure that it can't be changed behind our back. */
+ dir_fd = path_open_parent_safe(path);
+ if (dir_fd < 0)
+ return dir_fd;
+
+ bn = basename(path);
RUN_WITH_UMASK(0000) {
mac_selinux_create_file_prepare(path, S_IFREG);
- fd = open(path, flags|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
+ fd = openat(dir_fd, bn, O_CREAT|O_EXCL|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
mac_selinux_create_file_clear();
}
if (fd < 0) {
- if (i->type == WRITE_FILE && errno == ENOENT) {
- log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
- return 0;
+ /* Even on a read-only filesystem, open(2) returns EEXIST if the
+ * file already exists. It returns EROFS only if it needs to
+ * create the file. */
+ if (errno != EEXIST)
+ return log_error_errno(errno, "Failed to create file %s: %m", path);
+
+ /* Re-open the file. At that point it must exist since open(2)
+ * failed with EEXIST. We still need to check if the perms/mode
+ * need to be changed. For read-only filesystems, we let
+ * fd_set_perms() report the error if the perms need to be
+ * modified. */
+ fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to re-open file %s: %m", path);
+
+ if (fstat(fd, &stbuf) < 0)
+ return log_error_errno(errno, "stat(%s) failed: %m", path);
+
+ if (!S_ISREG(stbuf.st_mode)) {
+ log_error("%s exists and is not a regular file.", path);
+ return -EEXIST;
}
- if (i->type == CREATE_FILE && errno == EEXIST) {
- log_debug_errno(errno, "Not writing to pre-existing file \"%s\": %m", path);
- goto done;
+
+ st = &stbuf;
+ } else {
+
+ log_debug("\"%s\" has been created.", path);
+
+ if (i->argument) {
+ log_debug("Writing to \"%s\".", path);
+
+ r = loop_write(fd, i->argument, strlen(i->argument), false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write file \"%s\": %m", path);
}
+ }
- r = -errno;
- if (!i->argument && errno == EROFS && stat(path, &st) == 0 &&
- (i->type == CREATE_FILE || st.st_size == 0))
- goto check_mode;
+ return fd_set_perms(i, fd, path, st);
+}
+
+static int truncate_file(Item *i, const char *path) {
+ _cleanup_close_ int fd = -1, dir_fd = -1;
+ struct stat stbuf, *st = NULL;
+ bool erofs = false;
+ int r = 0;
+ char *bn;
+
+ assert(i);
+ assert(path);
+ assert(i->type == TRUNCATE_FILE);
+
+ /* We want to operate on regular file exclusively especially since
+ * O_TRUNC is unspecified if the file is neither a regular file nor a
+ * fifo nor a terminal device. Therefore we first open the file and make
+ * sure it's a regular one before truncating it. */
- return log_error_errno(r, "Failed to create file %s: %m", path);
+ /* Validate the path and keep the fd on the directory for opening the
+ * file so we're sure that it can't be changed behind our back. */
+ dir_fd = path_open_parent_safe(path);
+ if (dir_fd < 0)
+ return dir_fd;
+
+ bn = basename(path);
+
+ RUN_WITH_UMASK(0000) {
+ mac_selinux_create_file_prepare(path, S_IFREG);
+ fd = openat(dir_fd, bn, O_CREAT|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
+ mac_selinux_create_file_clear();
}
- if (i->argument) {
- log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path);
+ if (fd < 0) {
+ if (errno != EROFS)
+ return log_error_errno(errno, "Failed to open/create file %s: %m", path);
- r = loop_write(fd, i->argument, strlen(i->argument), false);
- if (r < 0)
- return log_error_errno(r, "Failed to write file \"%s\": %m", path);
- } else
- log_debug("\"%s\" has been created.", path);
+ /* On a read-only filesystem, we don't want to fail if the
+ * target is already empty and the perms are set. So we still
+ * proceed with the sanity checks and let the remaining
+ * operations fail with EROFS if they try to modify the target
+ * file. */
- fd = safe_close(fd);
+ fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode);
+ if (fd < 0) {
+ if (errno == ENOENT) {
+ log_error("Cannot create file %s on a read-only file system.", path);
+ return -EROFS;
+ }
- done:
- if (stat(path, &st) < 0)
+ return log_error_errno(errno, "Failed to re-open file %s: %m", path);
+ }
+
+ erofs = true;
+ }
+
+ if (fstat(fd, &stbuf) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", path);
- check_mode:
- if (!S_ISREG(st.st_mode)) {
- log_error("%s is not a file.", path);
+ if (!S_ISREG(stbuf.st_mode)) {
+ log_error("%s exists and is not a regular file.", path);
return -EEXIST;
}
- r = path_set_perms(i, path);
+ if (stbuf.st_size > 0) {
+ if (ftruncate(fd, 0) < 0) {
+ r = erofs ? -EROFS : -errno;
+ return log_error_errno(r, "Failed to truncate file %s: %m", path);
+ }
+ } else
+ st = &stbuf;
+
+ log_debug("\"%s\" has been created.", path);
+
+ if (i->argument) {
+ log_debug("Writing to \"%s\".", path);
+
+ r = loop_write(fd, i->argument, strlen(i->argument), false);
+ if (r < 0) {
+ r = erofs ? -EROFS : r;
+ return log_error_errno(r, "Failed to write file %s: %m", path);
+ }
+ }
+
+ return fd_set_perms(i, fd, path, st);
+}
+
+static int copy_files(Item *i) {
+ _cleanup_close_ int dfd = -1, fd = -1;
+ char *bn;
+ int r;
+
+ log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
+
+ bn = basename(i->path);
+
+ /* Validate the path and use the returned directory fd for copying the
+ * target so we're sure that the path can't be changed behind our
+ * back. */
+ dfd = path_open_parent_safe(i->path);
+ if (dfd < 0)
+ return dfd;
+
+ r = copy_tree_at(AT_FDCWD, i->argument,
+ dfd, bn,
+ i->uid_set ? i->uid : UID_INVALID,
+ i->gid_set ? i->gid : GID_INVALID,
+ COPY_REFLINK);
+ if (r < 0) {
+ struct stat a, b;
+
+ /* If the target already exists on read-only filesystems, trying
+ * to create the target will not fail with EEXIST but with
+ * EROFS. */
+ if (r == -EROFS && faccessat(dfd, bn, F_OK, AT_SYMLINK_NOFOLLOW) == 0)
+ r = -EEXIST;
+
+ if (r != -EEXIST)
+ return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
+
+ if (stat(i->argument, &a) < 0)
+ return log_error_errno(errno, "stat(%s) failed: %m", i->argument);
+
+ if (fstatat(dfd, bn, &b, AT_SYMLINK_NOFOLLOW) < 0)
+ return log_error_errno(errno, "stat(%s) failed: %m", i->path);
+
+ if ((a.st_mode ^ b.st_mode) & S_IFMT) {
+ log_debug("Can't copy to %s, file exists already and is of different type", i->path);
+ return 0;
+ }
+ }
+
+ fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to openat(%s): %m", i->path);
+
+ return fd_set_perms(i, fd, i->path, NULL);
+}
+
+typedef enum {
+ CREATION_NORMAL,
+ CREATION_EXISTING,
+ CREATION_FORCE,
+ _CREATION_MODE_MAX,
+ _CREATION_MODE_INVALID = -1
+} CreationMode;
+
+static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
+ [CREATION_NORMAL] = "Created",
+ [CREATION_EXISTING] = "Found existing",
+ [CREATION_FORCE] = "Created replacement",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
+
+static int create_directory_or_subvolume(const char *path, mode_t mode, bool subvol, CreationMode *creation) {
+ _cleanup_close_ int pfd = -1;
+ CreationMode c;
+ int r;
+
+ assert(path);
+
+ if (!creation)
+ creation = &c;
+
+ pfd = path_open_parent_safe(path);
+ if (pfd < 0)
+ return pfd;
+
+ if (subvol) {
+ if (btrfs_is_subvol(empty_to_root(arg_root)) <= 0)
+
+ /* Don't create a subvolume unless the root directory is
+ * one, too. We do this under the assumption that if the
+ * root directory is just a plain directory (i.e. very
+ * light-weight), we shouldn't try to split it up into
+ * subvolumes (i.e. more heavy-weight). Thus, chroot()
+ * environments and suchlike will get a full brtfs
+ * subvolume set up below their tree only if they
+ * specifically set up a btrfs subvolume for the root
+ * dir too. */
+
+ subvol = false;
+ else {
+ RUN_WITH_UMASK((~mode) & 0777)
+ r = btrfs_subvol_make_fd(pfd, basename(path));
+ }
+ } else
+ r = 0;
+
+ if (!subvol || r == -ENOTTY)
+ RUN_WITH_UMASK(0000)
+ r = mkdirat_label(pfd, basename(path), mode);
+
+ if (r < 0) {
+ int k;
+
+ if (!IN_SET(r, -EEXIST, -EROFS))
+ return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", path);
+
+ k = is_dir_fd(pfd);
+ if (k == -ENOENT && r == -EROFS)
+ return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", path);
+ if (k < 0)
+ return log_error_errno(k, "Failed to check if %s exists: %m", path);
+ if (!k) {
+ log_warning("\"%s\" already exists and is not a directory.", path);
+ return -EEXIST;
+ }
+
+ *creation = CREATION_EXISTING;
+ } else
+ *creation = CREATION_NORMAL;
+
+ log_debug("%s directory \"%s\".", creation_mode_verb_to_string(*creation), path);
+
+ r = openat(pfd, basename(path), O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (r < 0)
- return r;
+ return log_error_errno(errno, "Failed to open directory '%s': %m", basename(path));
- return 0;
+ return r;
+}
+
+static int create_directory(Item *i, const char *path) {
+ _cleanup_close_ int fd = -1;
+
+ assert(i);
+ assert(IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY));
+
+ fd = create_directory_or_subvolume(path, i->mode, false, NULL);
+ if (fd == -EEXIST)
+ return 0;
+ if (fd < 0)
+ return fd;
+
+ return fd_set_perms(i, fd, path, NULL);
+}
+
+static int create_subvolume(Item *i, const char *path) {
+ _cleanup_close_ int fd = -1;
+ CreationMode creation;
+ int r, q = 0;
+
+ assert(i);
+ assert(IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA));
+
+ fd = create_directory_or_subvolume(path, i->mode, true, &creation);
+ if (fd == -EEXIST)
+ return 0;
+ if (fd < 0)
+ return fd;
+
+ if (creation == CREATION_NORMAL &&
+ IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
+ r = btrfs_subvol_auto_qgroup_fd(fd, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
+ if (r == -ENOTTY)
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
+ else if (r == -EROFS)
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
+ else if (r == -ENOPROTOOPT)
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
+ else if (r < 0)
+ q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
+ else if (r > 0)
+ log_debug("Adjusted quota for subvolume \"%s\".", i->path);
+ else if (r == 0)
+ log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
+ }
+
+ r = fd_set_perms(i, fd, path, NULL);
+ if (q < 0) /* prefer the quota change error from above */
+ return q;
+
+ return r;
+}
+
+static int empty_directory(Item *i, const char *path) {
+ int r;
+
+ assert(i);
+ assert(i->type == EMPTY_DIRECTORY);
+
+ r = is_dir(path, false);
+ if (r == -ENOENT) {
+ /* Option "e" operates only on existing objects. Do not
+ * print errors about non-existent files or directories */
+ log_debug("Skipping missing directory: %s", path);
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "is_dir() failed on path %s: %m", path);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "'%s' already exists and is not a directory.",
+ path);
+
+ return path_set_perms(i, path);
+}
+
+static int create_device(Item *i, mode_t file_type) {
+ _cleanup_close_ int dfd = -1, fd = -1;
+ CreationMode creation;
+ char *bn;
+ int r;
+
+ assert(i);
+ assert(IN_SET(file_type, S_IFBLK, S_IFCHR));
+
+ bn = basename(i->path);
+
+ /* Validate the path and use the returned directory fd for copying the
+ * target so we're sure that the path can't be changed behind our
+ * back. */
+ dfd = path_open_parent_safe(i->path);
+ if (dfd < 0)
+ return dfd;
+
+ RUN_WITH_UMASK(0000) {
+ mac_selinux_create_file_prepare(i->path, file_type);
+ r = mknodat(dfd, bn, i->mode | file_type, i->major_minor);
+ mac_selinux_create_file_clear();
+ }
+
+ if (r < 0) {
+ struct stat st;
+
+ if (errno == EPERM) {
+ log_debug("We lack permissions, possibly because of cgroup configuration; "
+ "skipping creation of device node %s.", i->path);
+ return 0;
+ }
+
+ if (errno != EEXIST)
+ return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
+
+ if (fstatat(dfd, bn, &st, 0) < 0)
+ return log_error_errno(errno, "stat(%s) failed: %m", i->path);
+
+ if ((st.st_mode & S_IFMT) != file_type) {
+
+ if (i->force) {
+
+ RUN_WITH_UMASK(0000) {
+ mac_selinux_create_file_prepare(i->path, file_type);
+ /* FIXME: need to introduce mknodat_atomic() */
+ r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
+ mac_selinux_create_file_clear();
+ }
+
+ if (r < 0)
+ return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
+ creation = CREATION_FORCE;
+ } else {
+ log_debug("%s is not a device node.", i->path);
+ return 0;
+ }
+ } else
+ creation = CREATION_EXISTING;
+ } else
+ creation = CREATION_NORMAL;
+
+ log_debug("%s %s device node \"%s\" %u:%u.",
+ creation_mode_verb_to_string(creation),
+ i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
+ i->path, major(i->mode), minor(i->mode));
+
+ fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to openat(%s): %m", i->path);
+
+ return fd_set_perms(i, fd, i->path, NULL);
}
-typedef int (*action_t)(Item *, const char *);
-typedef int (*fdaction_t)(Item *, int fd, const struct stat *st);
+static int create_fifo(Item *i, const char *path) {
+ _cleanup_close_ int pfd = -1, fd = -1;
+ CreationMode creation;
+ struct stat st;
+ char *bn;
+ int r;
+
+ pfd = path_open_parent_safe(path);
+ if (pfd < 0)
+ return pfd;
+
+ bn = basename(path);
-static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) {
+ RUN_WITH_UMASK(0000) {
+ mac_selinux_create_file_prepare(path, S_IFIFO);
+ r = mkfifoat(pfd, bn, i->mode);
+ mac_selinux_create_file_clear();
+ }
+
+ if (r < 0) {
+ if (errno != EEXIST)
+ return log_error_errno(errno, "Failed to create fifo %s: %m", path);
+
+ if (fstatat(pfd, bn, &st, AT_SYMLINK_NOFOLLOW) < 0)
+ return log_error_errno(errno, "stat(%s) failed: %m", path);
+
+ if (!S_ISFIFO(st.st_mode)) {
+
+ if (i->force) {
+ RUN_WITH_UMASK(0000) {
+ mac_selinux_create_file_prepare(path, S_IFIFO);
+ r = mkfifoat_atomic(pfd, bn, i->mode);
+ mac_selinux_create_file_clear();
+ }
+
+ if (r < 0)
+ return log_error_errno(r, "Failed to create fifo %s: %m", path);
+ creation = CREATION_FORCE;
+ } else {
+ log_warning("\"%s\" already exists and is not a fifo.", path);
+ return 0;
+ }
+ } else
+ creation = CREATION_EXISTING;
+ } else
+ creation = CREATION_NORMAL;
+
+ log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), path);
+
+ fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to openat(%s): %m", path);
+
+ return fd_set_perms(i, fd, i->path, NULL);
+}
+
+typedef int (*action_t)(Item *i, const char *path);
+typedef int (*fdaction_t)(Item *i, int fd, const char *path, const struct stat *st);
+
+static int item_do(Item *i, int fd, const char *path, fdaction_t action) {
+ struct stat st;
int r = 0, q;
assert(i);
+ assert(path);
assert(fd >= 0);
- assert(st);
+
+ if (fstat(fd, &st) < 0) {
+ r = log_error_errno(errno, "fstat() on file failed: %m");
+ goto finish;
+ }
/* This returns the first error we run into, but nevertheless
* tries to go on */
- r = action(i, fd, st);
+ r = action(i, fd, path, &st);
- if (S_ISDIR(st->st_mode)) {
+ if (S_ISDIR(st.st_mode)) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
@@ -1316,23 +1816,31 @@ static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) {
d = opendir(procfs_path);
if (!d) {
- r = r ?: -errno;
+ log_error_errno(errno, "Failed to opendir() '%s': %m", procfs_path);
+ if (r == 0)
+ r = -errno;
goto finish;
}
FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) {
- struct stat de_st;
int de_fd;
if (dot_or_dot_dot(de->d_name))
continue;
de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH);
- if (de_fd >= 0 && fstat(de_fd, &de_st) >= 0)
- /* pass ownership of dirent fd over */
- q = item_do(i, de_fd, &de_st, action);
- else
- q = -errno;
+ if (de_fd < 0)
+ q = log_error_errno(errno, "Failed to open() file '%s': %m", de->d_name);
+ else {
+ _cleanup_free_ char *de_path = NULL;
+
+ de_path = path_join(path, de->d_name);
+ if (!de_path)
+ q = log_oom();
+ else
+ /* Pass ownership of dirent fd over */
+ q = item_do(i, de_fd, de_path, action);
+ }
if (q < 0 && r == 0)
r = q;
@@ -1376,7 +1884,6 @@ static int glob_item_recursively(Item *i, fdaction_t action) {
STRV_FOREACH(fn, g.gl_pathv) {
_cleanup_close_ int fd = -1;
- struct stat st;
/* Make sure we won't trigger/follow file object (such as
* device nodes, automounts, ...) pointed out by 'fn' with
@@ -1385,16 +1892,13 @@ static int glob_item_recursively(Item *i, fdaction_t action) {
fd = open(*fn, O_CLOEXEC|O_NOFOLLOW|O_PATH);
if (fd < 0) {
- r = r ?: -errno;
+ log_error_errno(errno, "Opening '%s' failed: %m", *fn);
+ if (r == 0)
+ r = -errno;
continue;
}
- if (fstat(fd, &st) < 0) {
- r = r ?: -errno;
- continue;
- }
-
- k = item_do(i, fd, &st, action);
+ k = item_do(i, fd, *fn, action);
if (k < 0 && r == 0)
r = k;
@@ -1405,27 +1909,9 @@ static int glob_item_recursively(Item *i, fdaction_t action) {
return r;
}
-typedef enum {
- CREATION_NORMAL,
- CREATION_EXISTING,
- CREATION_FORCE,
- _CREATION_MODE_MAX,
- _CREATION_MODE_INVALID = -1
-} CreationMode;
-
-static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
- [CREATION_NORMAL] = "Created",
- [CREATION_EXISTING] = "Found existing",
- [CREATION_FORCE] = "Created replacement",
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
-
static int create_item(Item *i) {
- struct stat st;
- int r = 0;
- int q = 0;
CreationMode creation;
+ int r = 0;
assert(i);
@@ -1440,50 +1926,30 @@ static int create_item(Item *i) {
return 0;
case CREATE_FILE:
- case TRUNCATE_FILE:
RUN_WITH_UMASK(0000)
(void) mkdir_parents_label(i->path, 0755);
- r = write_one_file(i, i->path);
+ r = create_file(i, i->path);
if (r < 0)
return r;
break;
- case COPY_FILES:
+ case TRUNCATE_FILE:
RUN_WITH_UMASK(0000)
(void) mkdir_parents_label(i->path, 0755);
- log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
- r = copy_tree(i->argument, i->path,
- i->uid_set ? i->uid : UID_INVALID,
- i->gid_set ? i->gid : GID_INVALID,
- COPY_REFLINK);
-
- if (r == -EROFS && stat(i->path, &st) == 0)
- r = -EEXIST;
-
- if (r < 0) {
- struct stat a, b;
-
- if (r != -EEXIST)
- return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
-
- if (stat(i->argument, &a) < 0)
- return log_error_errno(errno, "stat(%s) failed: %m", i->argument);
-
- if (stat(i->path, &b) < 0)
- return log_error_errno(errno, "stat(%s) failed: %m", i->path);
+ r = truncate_file(i, i->path);
+ if (r < 0)
+ return r;
+ break;
- if ((a.st_mode ^ b.st_mode) & S_IFMT) {
- log_debug("Can't copy to %s, file exists already and is of different type", i->path);
- return 0;
- }
- }
+ case COPY_FILES:
+ RUN_WITH_UMASK(0000)
+ (void) mkdir_parents_label(i->path, 0755);
- r = path_set_perms(i, i->path);
+ r = copy_files(i);
if (r < 0)
return r;
-
break;
case WRITE_FILE:
@@ -1495,132 +1961,38 @@ static int create_item(Item *i) {
case CREATE_DIRECTORY:
case TRUNCATE_DIRECTORY:
+ RUN_WITH_UMASK(0000)
+ (void) mkdir_parents_label(i->path, 0755);
+
+ r = create_directory(i, i->path);
+ if (r < 0)
+ return r;
+ break;
+
case CREATE_SUBVOLUME:
case CREATE_SUBVOLUME_INHERIT_QUOTA:
case CREATE_SUBVOLUME_NEW_QUOTA:
RUN_WITH_UMASK(0000)
(void) mkdir_parents_label(i->path, 0755);
- if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) {
-
- if (btrfs_is_subvol(empty_to_root(arg_root)) <= 0)
-
- /* Don't create a subvolume unless the
- * root directory is one, too. We do
- * this under the assumption that if
- * the root directory is just a plain
- * directory (i.e. very light-weight),
- * we shouldn't try to split it up
- * into subvolumes (i.e. more
- * heavy-weight). Thus, chroot()
- * environments and suchlike will get
- * a full brtfs subvolume set up below
- * their tree only if they
- * specifically set up a btrfs
- * subvolume for the root dir too. */
-
- r = -ENOTTY;
- else {
- RUN_WITH_UMASK((~i->mode) & 0777)
- r = btrfs_subvol_make(i->path);
- }
- } else
- r = 0;
-
- if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY)
- RUN_WITH_UMASK(0000)
- r = mkdir_label(i->path, i->mode);
-
- if (r < 0) {
- int k;
-
- if (!IN_SET(r, -EEXIST, -EROFS))
- return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path);
-
- k = is_dir(i->path, false);
- if (k == -ENOENT && r == -EROFS)
- return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path);
- if (k < 0)
- return log_error_errno(k, "Failed to check if %s exists: %m", i->path);
- if (!k) {
- log_warning("\"%s\" already exists and is not a directory.", i->path);
- return 0;
- }
-
- creation = CREATION_EXISTING;
- } else
- creation = CREATION_NORMAL;
-
- log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
-
- if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
- r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
- if (r == -ENOTTY)
- log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
- else if (r == -EROFS)
- log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
- else if (r == -ENOPROTOOPT)
- log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
- else if (r < 0)
- q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
- else if (r > 0)
- log_debug("Adjusted quota for subvolume \"%s\".", i->path);
- else if (r == 0)
- log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
- }
+ r = create_subvolume(i, i->path);
+ if (r < 0)
+ return r;
+ break;
- _fallthrough_;
case EMPTY_DIRECTORY:
- r = glob_item(i, path_set_perms);
- if (q < 0)
- return q;
+ r = glob_item(i, empty_directory);
if (r < 0)
return r;
-
break;
case CREATE_FIFO:
- RUN_WITH_UMASK(0000) {
+ RUN_WITH_UMASK(0000)
(void) mkdir_parents_label(i->path, 0755);
- mac_selinux_create_file_prepare(i->path, S_IFIFO);
- r = mkfifo(i->path, i->mode);
- mac_selinux_create_file_clear();
- }
-
- if (r < 0) {
- if (errno != EEXIST)
- return log_error_errno(errno, "Failed to create fifo %s: %m", i->path);
-
- if (lstat(i->path, &st) < 0)
- return log_error_errno(errno, "stat(%s) failed: %m", i->path);
-
- if (!S_ISFIFO(st.st_mode)) {
-
- if (i->force) {
- RUN_WITH_UMASK(0000) {
- mac_selinux_create_file_prepare(i->path, S_IFIFO);
- r = mkfifo_atomic(i->path, i->mode);
- mac_selinux_create_file_clear();
- }
-
- if (r < 0)
- return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
- creation = CREATION_FORCE;
- } else {
- log_warning("\"%s\" already exists and is not a fifo.", i->path);
- return 0;
- }
- } else
- creation = CREATION_EXISTING;
- } else
- creation = CREATION_NORMAL;
- log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path);
-
- r = path_set_perms(i, i->path);
+ r = create_fifo(i, i->path);
if (r < 0)
return r;
-
break;
case CREATE_SYMLINK: {
@@ -1672,14 +2044,10 @@ static int create_item(Item *i) {
}
case CREATE_BLOCK_DEVICE:
- case CREATE_CHAR_DEVICE: {
- mode_t file_type;
-
+ case CREATE_CHAR_DEVICE:
if (have_effective_cap(CAP_MKNOD) == 0) {
- /* In a container we lack CAP_MKNOD. We
- shouldn't attempt to create the device node in
- that case to avoid noise, and we don't support
- virtualized devices in containers anyway. */
+ /* In a container we lack CAP_MKNOD. We shouldn't attempt to create the device node in that
+ * case to avoid noise, and we don't support virtualized devices in containers anyway. */
log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
return 0;
@@ -1688,60 +2056,11 @@ static int create_item(Item *i) {
RUN_WITH_UMASK(0000)
(void) mkdir_parents_label(i->path, 0755);
- file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
-
- RUN_WITH_UMASK(0000) {
- mac_selinux_create_file_prepare(i->path, file_type);
- r = mknod(i->path, i->mode | file_type, i->major_minor);
- mac_selinux_create_file_clear();
- }
-
- if (r < 0) {
- if (errno == EPERM) {
- log_debug("We lack permissions, possibly because of cgroup configuration; "
- "skipping creation of device node %s.", i->path);
- return 0;
- }
-
- if (errno != EEXIST)
- return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
-
- if (lstat(i->path, &st) < 0)
- return log_error_errno(errno, "stat(%s) failed: %m", i->path);
-
- if ((st.st_mode & S_IFMT) != file_type) {
-
- if (i->force) {
-
- RUN_WITH_UMASK(0000) {
- mac_selinux_create_file_prepare(i->path, file_type);
- r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
- mac_selinux_create_file_clear();
- }
-
- if (r < 0)
- return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
- creation = CREATION_FORCE;
- } else {
- log_debug("%s is not a device node.", i->path);
- return 0;
- }
- } else
- creation = CREATION_EXISTING;
- } else
- creation = CREATION_NORMAL;
-
- log_debug("%s %s device node \"%s\" %u:%u.",
- creation_mode_verb_to_string(creation),
- i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
- i->path, major(i->mode), minor(i->mode));
-
- r = path_set_perms(i, i->path);
+ r = create_device(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
if (r < 0)
return r;
break;
- }
case ADJUST_MODE:
case RELABEL_PATH:
@@ -1809,12 +2128,10 @@ static int remove_item_instance(Item *i, const char *instance) {
break;
- case TRUNCATE_DIRECTORY:
case RECURSIVE_REMOVE_PATH:
- /* FIXME: we probably should use dir_cleanup() here
- * instead of rm_rf() so that 'x' is honoured. */
+ /* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
log_debug("rm -rf \"%s\"", instance);
- r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT|REMOVE_SUBVOLUME : 0) | REMOVE_PHYSICAL);
+ r = rm_rf(instance, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "rm_rf(%s): %m", instance);
@@ -1828,14 +2145,24 @@ static int remove_item_instance(Item *i, const char *instance) {
}
static int remove_item(Item *i) {
+ int r;
+
assert(i);
log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
switch (i->type) {
- case REMOVE_PATH:
case TRUNCATE_DIRECTORY:
+ /* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
+ log_debug("rm -rf \"%s\"", i->path);
+ r = rm_rf(i->path, REMOVE_PHYSICAL);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "rm_rf(%s): %m", i->path);
+
+ return 0;
+
+ case REMOVE_PATH:
case RECURSIVE_REMOVE_PATH:
return glob_item(i, remove_item_instance);
@@ -1875,10 +2202,9 @@ static int clean_item_instance(Item *i, const char* instance) {
if (fstat(dirfd(d), &s) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
- if (!S_ISDIR(s.st_mode)) {
- log_error("%s is not a directory.", i->path);
- return -ENOTDIR;
- }
+ if (!S_ISDIR(s.st_mode))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR),
+ "%s is not a directory.", i->path);
if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
@@ -1917,57 +2243,67 @@ static int clean_item(Item *i) {
}
}
-static int process_item_array(ItemArray *array);
-
-static int process_item(Item *i) {
- int r, q, p, t = 0;
- _cleanup_free_ char *prefix = NULL;
+static int process_item(Item *i, OperationMask operation) {
+ OperationMask todo;
+ int r, q, p;
assert(i);
- if (i->done)
+ todo = operation & ~i->done;
+ if (todo == 0) /* Everything already done? */
return 0;
- i->done = true;
-
- prefix = malloc(strlen(i->path) + 1);
- if (!prefix)
- return log_oom();
-
- PATH_FOREACH_PREFIX(prefix, i->path) {
- ItemArray *j;
+ i->done |= operation;
- j = ordered_hashmap_get(items, prefix);
- if (j) {
- int s;
-
- s = process_item_array(j);
- if (s < 0 && t == 0)
- t = s;
- }
+ r = chase_symlinks(i->path, NULL, CHASE_NO_AUTOFS|CHASE_WARN, NULL);
+ if (r == -EREMOTE) {
+ log_notice_errno(r, "Skipping %s", i->path);
+ return 0;
}
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine whether '%s' is below autofs, ignoring: %m", i->path);
- if (chase_symlinks(i->path, NULL, CHASE_NO_AUTOFS, NULL) == -EREMOTE)
- return t;
+ r = FLAGS_SET(operation, OPERATION_CREATE) ? create_item(i) : 0;
+ /* Failure can only be tolerated for create */
+ if (i->allow_failure)
+ r = 0;
- r = arg_create ? create_item(i) : 0;
- q = arg_remove ? remove_item(i) : 0;
- p = arg_clean ? clean_item(i) : 0;
+ q = FLAGS_SET(operation, OPERATION_REMOVE) ? remove_item(i) : 0;
+ p = FLAGS_SET(operation, OPERATION_CLEAN) ? clean_item(i) : 0;
- return t < 0 ? t :
- r < 0 ? r :
+ return r < 0 ? r :
q < 0 ? q :
p;
}
-static int process_item_array(ItemArray *array) {
- unsigned n;
- int r = 0, k;
+static int process_item_array(ItemArray *array, OperationMask operation) {
+ int r = 0;
+ size_t n;
assert(array);
- for (n = 0; n < array->count; n++) {
- k = process_item(array->items + n);
+ /* Create any parent first. */
+ if (FLAGS_SET(operation, OPERATION_CREATE) && array->parent)
+ r = process_item_array(array->parent, operation & OPERATION_CREATE);
+
+ /* Clean up all children first */
+ if ((operation & (OPERATION_REMOVE|OPERATION_CLEAN)) && !set_isempty(array->children)) {
+ Iterator i;
+ ItemArray *c;
+
+ SET_FOREACH(c, array->children, i) {
+ int k;
+
+ k = process_item_array(c, operation & (OPERATION_REMOVE|OPERATION_CLEAN));
+ if (k < 0 && r == 0)
+ r = k;
+ }
+ }
+
+ for (n = 0; n < array->n_items; n++) {
+ int k;
+
+ k = process_item(array->items + n, operation);
if (k < 0 && r == 0)
r = k;
}
@@ -1987,30 +2323,30 @@ static void item_free_contents(Item *i) {
#endif
}
-static void item_array_free(ItemArray *a) {
- unsigned n;
+static ItemArray* item_array_free(ItemArray *a) {
+ size_t n;
if (!a)
- return;
+ return NULL;
- for (n = 0; n < a->count; n++)
+ for (n = 0; n < a->n_items; n++)
item_free_contents(a->items + n);
+
+ set_free(a->children);
free(a->items);
- free(a);
+ return mfree(a);
}
-static int item_compare(const void *a, const void *b) {
- const Item *x = a, *y = b;
-
+static int item_compare(const Item *a, const Item *b) {
/* Make sure that the ownership taking item is put first, so
* that we first create the node, and then can adjust it */
- if (takes_ownership(x->type) && !takes_ownership(y->type))
+ if (takes_ownership(a->type) && !takes_ownership(b->type))
return -1;
- if (!takes_ownership(x->type) && takes_ownership(y->type))
+ if (!takes_ownership(a->type) && takes_ownership(b->type))
return 1;
- return (int) x->type - (int) y->type;
+ return CMP(a->type, b->type);
}
static bool item_compatible(Item *a, Item *b) {
@@ -2145,8 +2481,7 @@ static int patch_var_run(const char *fname, unsigned line, char **path) {
log_notice("[%s:%u] Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", fname, line, *path, n);
- free(*path);
- *path = n;
+ free_and_replace(*path, n);
return 0;
}
@@ -2158,7 +2493,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
ItemArray *existing;
OrderedHashmap *h;
int r, pos;
- bool force = false, boot = false;
+ bool force = false, boot = false, allow_failure = false;
assert(fname);
assert(line >= 1);
@@ -2203,6 +2538,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
boot = true;
else if (action[pos] == '+' && !force)
force = true;
+ else if (action[pos] == '-' && !allow_failure)
+ allow_failure = true;
else {
*invalid_config = true;
log_error("[%s:%u] Unknown modifiers in command '%s'",
@@ -2219,6 +2556,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
i.type = action[0];
i.force = force;
+ i.allow_failure = allow_failure;
r = specifier_printf(path, specifier_table, NULL, &i.path);
if (r == -ENXIO)
@@ -2289,24 +2627,21 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
break;
case CREATE_CHAR_DEVICE:
- case CREATE_BLOCK_DEVICE: {
- unsigned major, minor;
-
+ case CREATE_BLOCK_DEVICE:
if (!i.argument) {
*invalid_config = true;
log_error("[%s:%u] Device file requires argument.", fname, line);
return -EBADMSG;
}
- if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) {
+ r = parse_dev(i.argument, &i.major_minor);
+ if (r < 0) {
*invalid_config = true;
- log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
+ log_error_errno(r, "[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
return -EBADMSG;
}
- i.major_minor = makedev(major, minor);
break;
- }
case SET_XATTR:
case RECURSIVE_SET_XATTR:
@@ -2380,14 +2715,13 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
if (!p)
return log_oom();
- free(i.path);
- i.path = p;
+ free_and_replace(i.path, p);
}
if (!isempty(user) && !streq(user, "-")) {
const char *u = user;
- r = get_user_creds(&u, &i.uid, NULL, NULL, NULL);
+ r = get_user_creds(&u, &i.uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
if (r < 0) {
*invalid_config = true;
return log_error_errno(r, "[%s:%u] Unknown user '%s'.", fname, line, user);
@@ -2399,7 +2733,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
if (!isempty(group) && !streq(group, "-")) {
const char *g = group;
- r = get_group_creds(&g, &i.gid);
+ r = get_group_creds(&g, &i.gid, USER_CREDS_ALLOW_MISSING);
if (r < 0) {
*invalid_config = true;
log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
@@ -2450,9 +2784,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
existing = ordered_hashmap_get(h, i.path);
if (existing) {
- unsigned n;
+ size_t n;
- for (n = 0; n < existing->count; n++) {
+ for (n = 0; n < existing->n_items; n++) {
if (!item_compatible(existing->items + n, &i)) {
log_notice("[%s:%u] Duplicate line for path \"%s\", ignoring.",
fname, line, i.path);
@@ -2465,19 +2799,21 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
return log_oom();
r = ordered_hashmap_put(h, i.path, existing);
- if (r < 0)
+ if (r < 0) {
+ free(existing);
return log_oom();
+ }
}
- if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1))
+ if (!GREEDY_REALLOC(existing->items, existing->allocated, existing->n_items + 1))
return log_oom();
- memcpy(existing->items + existing->count++, &i, sizeof(i));
+ existing->items[existing->n_items++] = i;
+ i = (struct Item) {};
/* Sort item array, to enforce stable ordering of application */
- qsort_safe(existing->items, existing->count, sizeof(Item), item_compare);
+ typesafe_qsort(existing->items, existing->n_items, item_compare);
- zero(i);
return 0;
}
@@ -2492,7 +2828,14 @@ static int cat_config(char **config_dirs, char **args) {
return cat_files(NULL, files, 0);
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-tmpfiles", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
"Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
" -h --help Show this help\n"
@@ -2508,7 +2851,12 @@ static void help(void) {
" --root=PATH Operate on an alternate filesystem root\n"
" --replace=PATH Treat arguments as replacement for PATH\n"
" --no-pager Do not pipe output into a pager\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -2555,8 +2903,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -2570,15 +2917,15 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CREATE:
- arg_create = true;
+ arg_operation |= OPERATION_CREATE;
break;
case ARG_CLEAN:
- arg_clean = true;
+ arg_operation |= OPERATION_CLEAN;
break;
case ARG_REMOVE:
- arg_remove = true;
+ arg_operation |= OPERATION_REMOVE;
break;
case ARG_BOOT:
@@ -2603,16 +2950,15 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_REPLACE:
if (!path_is_absolute(optarg) ||
- !endswith(optarg, ".conf")) {
- log_error("The argument to --replace= must an absolute path to a config file");
- return -EINVAL;
- }
+ !endswith(optarg, ".conf"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "The argument to --replace= must an absolute path to a config file");
arg_replace = optarg;
break;
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case '?':
@@ -2622,30 +2968,26 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (!arg_clean && !arg_create && !arg_remove && !arg_cat_config) {
- log_error("You need to specify at least one of --clean, --create or --remove.");
- return -EINVAL;
- }
+ if (arg_operation == 0 && !arg_cat_config)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "You need to specify at least one of --clean, --create or --remove.");
- if (arg_replace && arg_cat_config) {
- log_error("Option --replace= is not supported with --cat-config");
- return -EINVAL;
- }
+ if (arg_replace && arg_cat_config)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --replace= is not supported with --cat-config");
- if (arg_replace && optind >= argc) {
- log_error("When --replace= is given, some configuration items must be specified");
- return -EINVAL;
- }
+ if (arg_replace && optind >= argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "When --replace= is given, some configuration items must be specified");
return 1;
}
static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
_cleanup_fclose_ FILE *_f = NULL;
- FILE *f;
- char line[LINE_MAX];
Iterator iterator;
unsigned v = 0;
+ FILE *f;
Item *i;
int r = 0;
@@ -2669,10 +3011,17 @@ static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoe
f = _f;
}
- FOREACH_LINE(line, f, break) {
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ bool invalid_line = false;
char *l;
int k;
- bool invalid_line = false;
+
+ k = read_line(f, LONG_LINE_MAX, &line);
+ if (k < 0)
+ return log_error_errno(k, "Failed to read '%s': %m", fn);
+ if (k == 0)
+ break;
v++;
@@ -2766,33 +3115,74 @@ static int read_config_files(char **config_dirs, char **args, bool *invalid_conf
return 0;
}
-int main(int argc, char *argv[]) {
- int r, k, r_process = 0;
- ItemArray *a;
- Iterator iterator;
+static int link_parent(ItemArray *a) {
+ const char *path;
+ char *prefix;
+ int r;
+
+ assert(a);
+
+ /* Finds the closest "parent" item array for the specified item array. Then registers the specified item array
+ * as child of it, and fills the parent in, linking them both ways. This allows us to later create parents
+ * before their children, and clean up/remove children before their parents. */
+
+ if (a->n_items <= 0)
+ return 0;
+
+ path = a->items[0].path;
+ prefix = alloca(strlen(path) + 1);
+ PATH_FOREACH_PREFIX(prefix, path) {
+ ItemArray *j;
+
+ j = ordered_hashmap_get(items, prefix);
+ if (!j)
+ j = ordered_hashmap_get(globs, prefix);
+ if (j) {
+ r = set_ensure_allocated(&j->children, NULL);
+ if (r < 0)
+ return log_oom();
+
+ r = set_put(j->children, a);
+ if (r < 0)
+ return log_oom();
+
+ a->parent = j;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_array_hash_ops, char, string_hash_func, string_compare_func,
+ ItemArray, item_array_free);
+
+static int run(int argc, char *argv[]) {
_cleanup_strv_free_ char **config_dirs = NULL;
bool invalid_config = false;
+ Iterator iterator;
+ ItemArray *a;
+ enum {
+ PHASE_REMOVE_AND_CLEAN,
+ PHASE_CREATE,
+ _PHASE_MAX
+ } phase;
+ int r, k;
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
if (arg_user) {
r = user_config_paths(&config_dirs);
- if (r < 0) {
- log_error_errno(r, "Failed to initialize configuration directory list: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize configuration directory list: %m");
} else {
config_dirs = strv_split_nulstr(CONF_PATHS_NULSTR("tmpfiles.d"));
- if (!config_dirs) {
- r = log_oom();
- goto finish;
- }
+ if (!config_dirs)
+ return log_oom();
}
if (DEBUG_LOGGING) {
@@ -2804,23 +3194,19 @@ int main(int argc, char *argv[]) {
}
if (arg_cat_config) {
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
- r = cat_config(config_dirs, argv + optind);
- goto finish;
+ return cat_config(config_dirs, argv + optind);
}
umask(0022);
mac_selinux_init();
- items = ordered_hashmap_new(&string_hash_ops);
- globs = ordered_hashmap_new(&string_hash_ops);
-
- if (!items || !globs) {
- r = log_oom();
- goto finish;
- }
+ items = ordered_hashmap_new(&item_array_hash_ops);
+ globs = ordered_hashmap_new(&item_array_hash_ops);
+ if (!items || !globs)
+ return log_oom();
/* If command line arguments are specified along with --replace, read all
* configuration files and insert the positional arguments at the specified
@@ -2833,44 +3219,57 @@ int main(int argc, char *argv[]) {
else
r = parse_arguments(config_dirs, argv + optind, &invalid_config);
if (r < 0)
- goto finish;
+ return r;
- /* The non-globbing ones usually create things, hence we apply
- * them first */
+ /* Let's now link up all child/parent relationships */
ORDERED_HASHMAP_FOREACH(a, items, iterator) {
- k = process_item_array(a);
- if (k < 0 && r_process == 0)
- r_process = k;
+ r = link_parent(a);
+ if (r < 0)
+ return r;
}
-
- /* The globbing ones usually alter things, hence we apply them
- * second. */
ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
- k = process_item_array(a);
- if (k < 0 && r_process == 0)
- r_process = k;
+ r = link_parent(a);
+ if (r < 0)
+ return r;
}
-finish:
- pager_close();
+ /* If multiple operations are requested, let's first run the remove/clean operations, and only then the create
+ * operations. i.e. that we first clean out the platform we then build on. */
+ for (phase = 0; phase < _PHASE_MAX; phase++) {
+ OperationMask op;
- ordered_hashmap_free_with_destructor(items, item_array_free);
- ordered_hashmap_free_with_destructor(globs, item_array_free);
+ if (phase == PHASE_REMOVE_AND_CLEAN)
+ op = arg_operation & (OPERATION_REMOVE|OPERATION_CLEAN);
+ else if (phase == PHASE_CREATE)
+ op = arg_operation & OPERATION_CREATE;
+ else
+ assert_not_reached("unexpected phase");
- free(arg_include_prefixes);
- free(arg_exclude_prefixes);
- free(arg_root);
+ if (op == 0) /* Nothing requested in this phase */
+ continue;
- set_free_free(unix_sockets);
+ /* The non-globbing ones usually create things, hence we apply them first */
+ ORDERED_HASHMAP_FOREACH(a, items, iterator) {
+ k = process_item_array(a, op);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
- mac_selinux_finish();
+ /* The globbing ones usually alter things, hence we apply them second. */
+ ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
+ k = process_item_array(a, op);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
+ }
- if (r < 0 || ERRNO_IS_RESOURCE(-r_process))
- return EXIT_FAILURE;
- else if (invalid_config)
+ if (ERRNO_IS_RESOURCE(-r))
+ return r;
+ if (invalid_config)
return EX_DATAERR;
- else if (r_process < 0)
+ if (r < 0)
return EX_CANTCREAT;
- else
- return EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 40d594896b..fc165ffc01 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -30,8 +30,10 @@
#include "hashmap.h"
#include "io-util.h"
#include "macro.h"
+#include "main-func.h"
#include "mkdir.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "process-util.h"
#include "signal-util.h"
#include "socket-util.h"
@@ -228,21 +230,25 @@ static int ask_password_plymouth(
r = 0;
finish:
- explicit_bzero(buffer, sizeof(buffer));
+ explicit_bzero_safe(buffer, sizeof(buffer));
return r;
}
static int send_passwords(const char *socket_name, char **passwords) {
_cleanup_free_ char *packet = NULL;
_cleanup_close_ int socket_fd = -1;
- union sockaddr_union sa = { .un.sun_family = AF_UNIX };
+ union sockaddr_union sa = {};
size_t packet_length = 1;
char **p, *d;
ssize_t n;
- int r;
+ int r, salen;
assert(socket_name);
+ salen = sockaddr_un_set_path(&sa.un, socket_name);
+ if (salen < 0)
+ return salen;
+
STRV_FOREACH(p, passwords)
packet_length += strlen(*p) + 1;
@@ -262,9 +268,7 @@ static int send_passwords(const char *socket_name, char **passwords) {
goto finish;
}
- strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
-
- n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+ n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, salen);
if (n < 0) {
r = log_debug_errno(errno, "sendto(): %m");
goto finish;
@@ -273,7 +277,7 @@ static int send_passwords(const char *socket_name, char **passwords) {
r = (int) n;
finish:
- explicit_bzero(packet, packet_length);
+ explicit_bzero_safe(packet, packet_length);
return r;
}
@@ -304,10 +308,9 @@ static int parse_password(const char *filename, char **wall) {
if (r < 0)
return r;
- if (!socket_name) {
- log_error("Invalid password file %s", filename);
- return -EBADMSG;
- }
+ if (!socket_name)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Invalid password file %s", filename);
if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after)
return 0;
@@ -323,7 +326,7 @@ static int parse_password(const char *filename, char **wall) {
if (asprintf(&_wall,
"%s%sPassword entry required for \'%s\' (PID %u).\r\n"
- "Please enter password with the systemd-tty-ask-password-agent tool!",
+ "Please enter password with the systemd-tty-ask-password-agent tool:",
strempty(*wall),
*wall ? "\r\n\r\n" : "",
message,
@@ -348,7 +351,6 @@ static int parse_password(const char *filename, char **wall) {
if (arg_plymouth)
r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords);
else {
- char *password = NULL;
int tty_fd = -1;
if (arg_console) {
@@ -366,18 +368,12 @@ static int parse_password(const char *filename, char **wall) {
r = ask_password_tty(tty_fd, message, NULL, not_after,
(echo ? ASK_PASSWORD_ECHO : 0) |
(arg_console ? ASK_PASSWORD_CONSOLE_COLOR : 0),
- filename, &password);
+ filename, &passwords);
if (arg_console) {
tty_fd = safe_close(tty_fd);
release_terminal();
}
-
- if (r >= 0)
- r = strv_push(&passwords, password);
-
- if (r < 0)
- string_free_erase(password);
}
/* If the query went away, that's OK */
@@ -524,8 +520,12 @@ static int watch_passwords(void) {
if (notify < 0)
return log_error_errno(errno, "Failed to allocate directory watch: %m");
- if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0)
- return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m");
+ if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
+ if (errno == ENOSPC)
+ return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: inotify watch limit reached");
+ else
+ return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m");
+ }
assert_se(sigemptyset(&mask) >= 0);
assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
@@ -562,7 +562,14 @@ static int watch_passwords(void) {
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-tty-ask-password-agent", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"Process system password requests.\n\n"
" -h --help Show this help\n"
@@ -572,8 +579,13 @@ static void help(void) {
" --watch Continuously process password requests\n"
" --wall Continuously forward password requests to wall\n"
" --plymouth Ask question with Plymouth instead of on TTY\n"
- " --console Ask question on /dev/console instead of current TTY\n",
- program_invocation_short_name);
+ " --console Ask question on /dev/console instead of current TTY\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -610,8 +622,7 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
@@ -640,10 +651,9 @@ static int parse_argv(int argc, char *argv[]) {
arg_console = true;
if (optarg) {
- if (isempty(optarg)) {
- log_error("Empty console device path is not allowed.");
- return -EINVAL;
- }
+ if (isempty(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Empty console device path is not allowed.");
arg_device = optarg;
}
@@ -656,22 +666,19 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (optind != argc) {
- log_error("%s takes no arguments.", program_invocation_short_name);
- return -EINVAL;
- }
+ if (optind != argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s takes no arguments.", program_invocation_short_name);
if (arg_plymouth || arg_console) {
- if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH)) {
- log_error("Options --query and --watch conflict.");
- return -EINVAL;
- }
+ if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Options --query and --watch conflict.");
- if (arg_plymouth && arg_console) {
- log_error("Options --plymouth and --console conflict.");
- return -EINVAL;
- }
+ if (arg_plymouth && arg_console)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Options --plymouth and --console conflict.");
}
return 1;
@@ -826,42 +833,37 @@ static int ask_on_consoles(int argc, char *argv[]) {
return 0;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
if (arg_console && !arg_device)
/*
- * Spawn for each console device a separate process.
+ * Spawn a separate process for each console device.
*/
- r = ask_on_consoles(argc, argv);
- else {
-
- if (arg_device) {
- /*
- * Later on, a controlling terminal will be acquired,
- * therefore the current process has to become a session
- * leader and should not have a controlling terminal already.
- */
- (void) setsid();
- (void) release_terminal();
- }
+ return ask_on_consoles(argc, argv);
- if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
- r = watch_passwords();
- else
- r = show_passwords();
+ if (arg_device) {
+ /*
+ * Later on, a controlling terminal will be acquired,
+ * therefore the current process has to become a session
+ * leader and should not have a controlling terminal already.
+ */
+ (void) setsid();
+ (void) release_terminal();
}
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
+ return watch_passwords();
+ else
+ return show_passwords();
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c
index e077a699a4..6c2233e430 100644
--- a/src/udev/ata_id/ata_id.c
+++ b/src/udev/ata_id/ata_id.c
@@ -3,7 +3,6 @@
* ata_id - reads product/serial number from ATA drives
*
* Copyright © 2009-2010 David Zeuthen <zeuthen@gmail.com>
- *
*/
#include <ctype.h>
@@ -24,23 +23,21 @@
#include <sys/types.h>
#include <unistd.h>
-#include "libudev.h"
-
#include "fd-util.h"
-#include "libudev-private.h"
+#include "libudev-util.h"
#include "log.h"
#include "udev-util.h"
+#include "util.h"
#define COMMAND_TIMEOUT_MSEC (30 * 1000)
-static int disk_scsi_inquiry_command(int fd,
- void *buf,
- size_t buf_len)
-{
+static int disk_scsi_inquiry_command(
+ int fd,
+ void *buf,
+ size_t buf_len) {
+
uint8_t cdb[6] = {
- /*
- * INQUIRY, see SPC-4 section 6.4
- */
+ /* INQUIRY, see SPC-4 section 6.4 */
[0] = 0x12, /* OPERATION CODE: INQUIRY */
[3] = (buf_len >> 8), /* ALLOCATION LENGTH */
[4] = (buf_len & 0xff),
@@ -102,10 +99,11 @@ static int disk_scsi_inquiry_command(int fd,
return 0;
}
-static int disk_identify_command(int fd,
- void *buf,
- size_t buf_len)
-{
+static int disk_identify_command(
+ int fd,
+ void *buf,
+ size_t buf_len) {
+
uint8_t cdb[12] = {
/*
* ATA Pass-Through 12 byte command, as described in
@@ -172,10 +170,11 @@ static int disk_identify_command(int fd,
return 0;
}
-static int disk_identify_packet_device_command(int fd,
- void *buf,
- size_t buf_len)
-{
+static int disk_identify_packet_device_command(
+ int fd,
+ void *buf,
+ size_t buf_len) {
+
uint8_t cdb[16] = {
/*
* ATA Pass-Through 16 byte command, as described in
@@ -257,13 +256,14 @@ static int disk_identify_packet_device_command(int fd,
*
* Copies the ATA string from @identify located at @offset_words into @dest.
*/
-static void disk_identify_get_string(uint8_t identify[512],
- unsigned int offset_words,
- char *dest,
- size_t dest_len)
-{
- unsigned int c1;
- unsigned int c2;
+static void disk_identify_get_string(
+ uint8_t identify[512],
+ unsigned offset_words,
+ char *dest,
+ size_t dest_len) {
+
+ unsigned c1;
+ unsigned c2;
while (dest_len > 0) {
c1 = identify[offset_words * 2 + 1];
@@ -277,16 +277,15 @@ static void disk_identify_get_string(uint8_t identify[512],
}
}
-static void disk_identify_fixup_string(uint8_t identify[512],
- unsigned int offset_words,
- size_t len)
-{
+static void disk_identify_fixup_string(
+ uint8_t identify[512],
+ unsigned offset_words,
+ size_t len) {
disk_identify_get_string(identify, offset_words,
(char *) identify + offset_words * 2, len);
}
-static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned int offset_words)
-{
+static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned offset_words) {
uint16_t *p;
p = (uint16_t *) identify;
@@ -295,7 +294,6 @@ static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned int offs
/**
* disk_identify:
- * @udev: The libudev context.
* @fd: File descriptor for the block device.
* @out_identify: Return location for IDENTIFY data.
* @out_is_packet_device: Return location for whether returned data is from a IDENTIFY PACKET DEVICE.
@@ -304,17 +302,14 @@ static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned int offs
* device represented by @fd. If successful, then the result will be
* copied into @out_identify and @out_is_packet_device.
*
- * This routine is based on code from libatasmart, Copyright © 2008
- * Lennart Poettering, LGPL v2.1.
+ * This routine is based on code from libatasmart, LGPL v2.1.
*
* Returns: 0 if the data was successfully obtained, otherwise
* non-zero with errno set.
*/
-static int disk_identify(struct udev *udev,
- int fd,
+static int disk_identify(int fd,
uint8_t out_identify[512],
- int *out_is_packet_device)
-{
+ int *out_is_packet_device) {
int ret;
uint8_t inquiry_buf[36];
int peripheral_device_type;
@@ -392,7 +387,6 @@ out:
}
int main(int argc, char *argv[]) {
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
struct hd_driveid id;
union {
uint8_t byte[512];
@@ -418,10 +412,6 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
- udev = udev_new();
- if (udev == NULL)
- return 0;
-
for (;;) {
int option;
@@ -453,7 +443,7 @@ int main(int argc, char *argv[]) {
return 1;
}
- if (disk_identify(udev, fd, identify.byte, &is_packet_device) == 0) {
+ if (disk_identify(fd, identify.byte, &is_packet_device) == 0) {
/*
* fix up only the fields from the IDENTIFY data that we are going to
* use and copy it into the hd_driveid struct for convenience
diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c
index 1aaa263f76..aed24a3b9a 100644
--- a/src/udev/cdrom_id/cdrom_id.c
+++ b/src/udev/cdrom_id/cdrom_id.c
@@ -1,8 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* cdrom_id - optical drive and media information prober
- *
- *
*/
#include <errno.h>
@@ -22,65 +20,64 @@
#include <time.h>
#include <unistd.h>
-#include "libudev.h"
-
-#include "libudev-private.h"
+#include "log.h"
#include "random-util.h"
#include "udev-util.h"
+#include "util.h"
/* device info */
-static unsigned int cd_cd_rom;
-static unsigned int cd_cd_r;
-static unsigned int cd_cd_rw;
-static unsigned int cd_dvd_rom;
-static unsigned int cd_dvd_r;
-static unsigned int cd_dvd_rw;
-static unsigned int cd_dvd_ram;
-static unsigned int cd_dvd_plus_r;
-static unsigned int cd_dvd_plus_rw;
-static unsigned int cd_dvd_plus_r_dl;
-static unsigned int cd_dvd_plus_rw_dl;
-static unsigned int cd_bd;
-static unsigned int cd_bd_r;
-static unsigned int cd_bd_re;
-static unsigned int cd_hddvd;
-static unsigned int cd_hddvd_r;
-static unsigned int cd_hddvd_rw;
-static unsigned int cd_mo;
-static unsigned int cd_mrw;
-static unsigned int cd_mrw_w;
+static unsigned cd_cd_rom;
+static unsigned cd_cd_r;
+static unsigned cd_cd_rw;
+static unsigned cd_dvd_rom;
+static unsigned cd_dvd_r;
+static unsigned cd_dvd_rw;
+static unsigned cd_dvd_ram;
+static unsigned cd_dvd_plus_r;
+static unsigned cd_dvd_plus_rw;
+static unsigned cd_dvd_plus_r_dl;
+static unsigned cd_dvd_plus_rw_dl;
+static unsigned cd_bd;
+static unsigned cd_bd_r;
+static unsigned cd_bd_re;
+static unsigned cd_hddvd;
+static unsigned cd_hddvd_r;
+static unsigned cd_hddvd_rw;
+static unsigned cd_mo;
+static unsigned cd_mrw;
+static unsigned cd_mrw_w;
/* media info */
-static unsigned int cd_media;
-static unsigned int cd_media_cd_rom;
-static unsigned int cd_media_cd_r;
-static unsigned int cd_media_cd_rw;
-static unsigned int cd_media_dvd_rom;
-static unsigned int cd_media_dvd_r;
-static unsigned int cd_media_dvd_rw;
-static unsigned int cd_media_dvd_rw_ro; /* restricted overwrite mode */
-static unsigned int cd_media_dvd_rw_seq; /* sequential mode */
-static unsigned int cd_media_dvd_ram;
-static unsigned int cd_media_dvd_plus_r;
-static unsigned int cd_media_dvd_plus_rw;
-static unsigned int cd_media_dvd_plus_r_dl;
-static unsigned int cd_media_dvd_plus_rw_dl;
-static unsigned int cd_media_bd;
-static unsigned int cd_media_bd_r;
-static unsigned int cd_media_bd_re;
-static unsigned int cd_media_hddvd;
-static unsigned int cd_media_hddvd_r;
-static unsigned int cd_media_hddvd_rw;
-static unsigned int cd_media_mo;
-static unsigned int cd_media_mrw;
-static unsigned int cd_media_mrw_w;
+static unsigned cd_media;
+static unsigned cd_media_cd_rom;
+static unsigned cd_media_cd_r;
+static unsigned cd_media_cd_rw;
+static unsigned cd_media_dvd_rom;
+static unsigned cd_media_dvd_r;
+static unsigned cd_media_dvd_rw;
+static unsigned cd_media_dvd_rw_ro; /* restricted overwrite mode */
+static unsigned cd_media_dvd_rw_seq; /* sequential mode */
+static unsigned cd_media_dvd_ram;
+static unsigned cd_media_dvd_plus_r;
+static unsigned cd_media_dvd_plus_rw;
+static unsigned cd_media_dvd_plus_r_dl;
+static unsigned cd_media_dvd_plus_rw_dl;
+static unsigned cd_media_bd;
+static unsigned cd_media_bd_r;
+static unsigned cd_media_bd_re;
+static unsigned cd_media_hddvd;
+static unsigned cd_media_hddvd_r;
+static unsigned cd_media_hddvd_rw;
+static unsigned cd_media_mo;
+static unsigned cd_media_mrw;
+static unsigned cd_media_mrw_w;
static const char *cd_media_state = NULL;
-static unsigned int cd_media_session_next;
-static unsigned int cd_media_session_count;
-static unsigned int cd_media_track_count;
-static unsigned int cd_media_track_count_data;
-static unsigned int cd_media_track_count_audio;
+static unsigned cd_media_session_next;
+static unsigned cd_media_session_count;
+static unsigned cd_media_track_count;
+static unsigned cd_media_track_count_data;
+static unsigned cd_media_track_count_audio;
static unsigned long long int cd_media_session_last_offset;
#define ERRCODE(s) ((((s)[2] & 0x0F) << 16) | ((s)[12] << 8) | ((s)[13]))
@@ -88,8 +85,7 @@ static unsigned long long int cd_media_session_last_offset;
#define ASC(errcode) (((errcode) >> 8) & 0xFF)
#define ASCQ(errcode) ((errcode) & 0xFF)
-static bool is_mounted(const char *device)
-{
+static bool is_mounted(const char *device) {
struct stat statbuf;
FILE *fp;
int maj, min;
@@ -111,8 +107,7 @@ static bool is_mounted(const char *device)
return mounted;
}
-static void info_scsi_cmd_err(struct udev *udev, const char *cmd, int err)
-{
+static void info_scsi_cmd_err(const char *cmd, int err) {
if (err == -1) {
log_debug("%s failed", cmd);
return;
@@ -129,8 +124,7 @@ struct scsi_cmd {
struct sg_io_hdr sg_io;
};
-static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd)
-{
+static void scsi_cmd_init(struct scsi_cmd *cmd) {
memzero(cmd, sizeof(struct scsi_cmd));
cmd->cgc.quiet = 1;
cmd->cgc.sense = &cmd->_sense.s;
@@ -141,16 +135,14 @@ static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd)
cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO;
}
-static void scsi_cmd_set(struct udev *udev, struct scsi_cmd *cmd, size_t i, unsigned char arg)
-{
+static void scsi_cmd_set(struct scsi_cmd *cmd, size_t i, unsigned char arg) {
cmd->sg_io.cmd_len = i + 1;
cmd->cgc.cmd[i] = arg;
}
#define CHECK_CONDITION 0x01
-static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigned char *buf, size_t bufsize)
-{
+static int scsi_cmd_run(struct scsi_cmd *cmd, int fd, unsigned char *buf, size_t bufsize) {
int ret = 0;
if (bufsize > 0) {
@@ -175,8 +167,7 @@ static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigne
return ret;
}
-static int media_lock(struct udev *udev, int fd, bool lock)
-{
+static int media_lock(int fd, bool lock) {
int err;
/* disable the kernel's lock logic */
@@ -191,25 +182,23 @@ static int media_lock(struct udev *udev, int fd, bool lock)
return err;
}
-static int media_eject(struct udev *udev, int fd)
-{
+static int media_eject(int fd) {
struct scsi_cmd sc;
int err;
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x1b);
- scsi_cmd_set(udev, &sc, 4, 0x02);
- scsi_cmd_set(udev, &sc, 5, 0);
- err = scsi_cmd_run(udev, &sc, fd, NULL, 0);
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x1b);
+ scsi_cmd_set(&sc, 4, 0x02);
+ scsi_cmd_set(&sc, 5, 0);
+ err = scsi_cmd_run(&sc, fd, NULL, 0);
if ((err != 0)) {
- info_scsi_cmd_err(udev, "START_STOP_UNIT", err);
+ info_scsi_cmd_err("START_STOP_UNIT", err);
return -1;
}
return 0;
}
-static int cd_capability_compat(struct udev *udev, int fd)
-{
+static int cd_capability_compat(int fd) {
int capability;
capability = ioctl(fd, CDROM_GET_CAPABILITY, NULL);
@@ -235,8 +224,7 @@ static int cd_capability_compat(struct udev *udev, int fd)
return 0;
}
-static int cd_media_compat(struct udev *udev, int fd)
-{
+static int cd_media_compat(int fd) {
if (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) != CDS_DISC_OK) {
log_debug("CDROM_DRIVE_STATUS != CDS_DISC_OK");
return -1;
@@ -245,19 +233,18 @@ static int cd_media_compat(struct udev *udev, int fd)
return 0;
}
-static int cd_inquiry(struct udev *udev, int fd)
-{
+static int cd_inquiry(int fd) {
struct scsi_cmd sc;
unsigned char inq[128];
int err;
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x12);
- scsi_cmd_set(udev, &sc, 4, 36);
- scsi_cmd_set(udev, &sc, 5, 0);
- err = scsi_cmd_run(udev, &sc, fd, inq, 36);
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x12);
+ scsi_cmd_set(&sc, 4, 36);
+ scsi_cmd_set(&sc, 5, 0);
+ err = scsi_cmd_run(&sc, fd, inq, 36);
if ((err != 0)) {
- info_scsi_cmd_err(udev, "INQUIRY", err);
+ info_scsi_cmd_err("INQUIRY", err);
return -1;
}
@@ -270,8 +257,7 @@ static int cd_inquiry(struct udev *udev, int fd)
return 0;
}
-static void feature_profile_media(struct udev *udev, int cur_profile)
-{
+static void feature_profile_media(int cur_profile) {
switch (cur_profile) {
case 0x03:
case 0x04:
@@ -379,9 +365,8 @@ static void feature_profile_media(struct udev *udev, int cur_profile)
}
}
-static int feature_profiles(struct udev *udev, const unsigned char *profiles, size_t size)
-{
- unsigned int i;
+static int feature_profiles(const unsigned char *profiles, size_t size) {
+ unsigned i;
for (i = 0; i+4 <= size; i += 4) {
int profile;
@@ -469,20 +454,19 @@ static int feature_profiles(struct udev *udev, const unsigned char *profiles, si
}
/* returns 0 if media was detected */
-static int cd_profiles_old_mmc(struct udev *udev, int fd)
-{
+static int cd_profiles_old_mmc(int fd) {
struct scsi_cmd sc;
int err;
unsigned char header[32];
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x51);
- scsi_cmd_set(udev, &sc, 8, sizeof(header));
- scsi_cmd_set(udev, &sc, 9, 0);
- err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header));
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x51);
+ scsi_cmd_set(&sc, 8, sizeof(header));
+ scsi_cmd_set(&sc, 9, 0);
+ err = scsi_cmd_run(&sc, fd, header, sizeof(header));
if ((err != 0)) {
- info_scsi_cmd_err(udev, "READ DISC INFORMATION", err);
+ info_scsi_cmd_err("READ DISC INFORMATION", err);
if (cd_media == 1) {
log_debug("no current profile, but disc is present; assuming CD-ROM");
cd_media_cd_rom = 1;
@@ -511,31 +495,30 @@ static int cd_profiles_old_mmc(struct udev *udev, int fd)
}
/* returns 0 if media was detected */
-static int cd_profiles(struct udev *udev, int fd)
-{
+static int cd_profiles(int fd) {
struct scsi_cmd sc;
unsigned char features[65530];
- unsigned int cur_profile = 0;
- unsigned int len;
- unsigned int i;
+ unsigned cur_profile = 0;
+ unsigned len;
+ unsigned i;
int err;
int ret;
ret = -1;
/* First query the current profile */
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x46);
- scsi_cmd_set(udev, &sc, 8, 8);
- scsi_cmd_set(udev, &sc, 9, 0);
- err = scsi_cmd_run(udev, &sc, fd, features, 8);
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x46);
+ scsi_cmd_set(&sc, 8, 8);
+ scsi_cmd_set(&sc, 9, 0);
+ err = scsi_cmd_run(&sc, fd, features, 8);
if ((err != 0)) {
- info_scsi_cmd_err(udev, "GET CONFIGURATION", err);
+ info_scsi_cmd_err("GET CONFIGURATION", err);
/* handle pre-MMC2 drives which do not support GET CONFIGURATION */
if (SK(err) == 0x5 && IN_SET(ASC(err), 0x20, 0x24)) {
log_debug("drive is pre-MMC2 and does not support 46h get configuration command");
log_debug("trying to work around the problem");
- ret = cd_profiles_old_mmc(udev, fd);
+ ret = cd_profiles_old_mmc(fd);
}
goto out;
}
@@ -543,7 +526,7 @@ static int cd_profiles(struct udev *udev, int fd)
cur_profile = features[6] << 8 | features[7];
if (cur_profile > 0) {
log_debug("current profile 0x%02x", cur_profile);
- feature_profile_media (udev, cur_profile);
+ feature_profile_media(cur_profile);
ret = 0; /* we have media */
} else {
log_debug("no current profile, assuming no media");
@@ -559,14 +542,14 @@ static int cd_profiles(struct udev *udev, int fd)
len = sizeof(features);
/* Now get the full feature buffer */
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x46);
- scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff);
- scsi_cmd_set(udev, &sc, 8, len & 0xff);
- scsi_cmd_set(udev, &sc, 9, 0);
- err = scsi_cmd_run(udev, &sc, fd, features, len);
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x46);
+ scsi_cmd_set(&sc, 7, ( len >> 8 ) & 0xff);
+ scsi_cmd_set(&sc, 8, len & 0xff);
+ scsi_cmd_set(&sc, 9, 0);
+ err = scsi_cmd_run(&sc, fd, features, len);
if ((err != 0)) {
- info_scsi_cmd_err(udev, "GET CONFIGURATION", err);
+ info_scsi_cmd_err("GET CONFIGURATION", err);
return -1;
}
@@ -581,14 +564,14 @@ static int cd_profiles(struct udev *udev, int fd)
/* device features */
for (i = 8; i+4 < len; i += (4 + features[i+3])) {
- unsigned int feature;
+ unsigned feature;
feature = features[i] << 8 | features[i+1];
switch (feature) {
case 0x00:
log_debug("GET CONFIGURATION: feature 'profiles', with %i entries", features[i+3] / 4);
- feature_profiles(udev, &features[i]+4, MIN(features[i+3], len - i - 4));
+ feature_profiles(&features[i]+4, MIN(features[i+3], len - i - 4));
break;
default:
log_debug("GET CONFIGURATION: feature 0x%04x <ignored>, with 0x%02x bytes", feature, features[i+3]);
@@ -599,8 +582,7 @@ out:
return ret;
}
-static int cd_media_info(struct udev *udev, int fd)
-{
+static int cd_media_info(int fd) {
struct scsi_cmd sc;
unsigned char header[32];
static const char *media_status[] = {
@@ -611,13 +593,13 @@ static int cd_media_info(struct udev *udev, int fd)
};
int err;
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x51);
- scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff);
- scsi_cmd_set(udev, &sc, 9, 0);
- err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header));
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x51);
+ scsi_cmd_set(&sc, 8, sizeof(header) & 0xff);
+ scsi_cmd_set(&sc, 9, 0);
+ err = scsi_cmd_run(&sc, fd, header, sizeof(header));
if ((err != 0)) {
- info_scsi_cmd_err(udev, "READ DISC INFORMATION", err);
+ info_scsi_cmd_err("READ DISC INFORMATION", err);
return -1;
};
@@ -649,14 +631,14 @@ static int cd_media_info(struct udev *udev, int fd)
unsigned char dvdstruct[8];
unsigned char format[12];
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0xAD);
- scsi_cmd_set(udev, &sc, 7, 0xC0);
- scsi_cmd_set(udev, &sc, 9, sizeof(dvdstruct));
- scsi_cmd_set(udev, &sc, 11, 0);
- err = scsi_cmd_run(udev, &sc, fd, dvdstruct, sizeof(dvdstruct));
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0xAD);
+ scsi_cmd_set(&sc, 7, 0xC0);
+ scsi_cmd_set(&sc, 9, sizeof(dvdstruct));
+ scsi_cmd_set(&sc, 11, 0);
+ err = scsi_cmd_run(&sc, fd, dvdstruct, sizeof(dvdstruct));
if ((err != 0)) {
- info_scsi_cmd_err(udev, "READ DVD STRUCTURE", err);
+ info_scsi_cmd_err("READ DVD STRUCTURE", err);
return -1;
}
if (dvdstruct[4] & 0x02) {
@@ -666,13 +648,13 @@ static int cd_media_info(struct udev *udev, int fd)
}
/* let's make sure we don't try to read unformatted media */
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x23);
- scsi_cmd_set(udev, &sc, 8, sizeof(format));
- scsi_cmd_set(udev, &sc, 9, 0);
- err = scsi_cmd_run(udev, &sc, fd, format, sizeof(format));
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x23);
+ scsi_cmd_set(&sc, 8, sizeof(format));
+ scsi_cmd_set(&sc, 9, 0);
+ err = scsi_cmd_run(&sc, fd, format, sizeof(format));
if ((err != 0)) {
- info_scsi_cmd_err(udev, "READ DVD FORMAT CAPACITIES", err);
+ info_scsi_cmd_err("READ DVD FORMAT CAPACITIES", err);
return -1;
}
@@ -707,15 +689,15 @@ static int cd_media_info(struct udev *udev, int fd)
* has "blank" status", DVD-RAM was examined earlier) and check
* for ISO and UDF PVDs or a fs superblock presence and do it
* in one ioctl (we need just sectors 0 and 16) */
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x28);
- scsi_cmd_set(udev, &sc, 5, 0);
- scsi_cmd_set(udev, &sc, 8, 32);
- scsi_cmd_set(udev, &sc, 9, 0);
- err = scsi_cmd_run(udev, &sc, fd, buffer, sizeof(buffer));
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x28);
+ scsi_cmd_set(&sc, 5, 0);
+ scsi_cmd_set(&sc, 8, 32);
+ scsi_cmd_set(&sc, 9, 0);
+ err = scsi_cmd_run(&sc, fd, buffer, sizeof(buffer));
if ((err != 0)) {
cd_media = 0;
- info_scsi_cmd_err(udev, "READ FIRST 32 BLOCKS", err);
+ info_scsi_cmd_err("READ FIRST 32 BLOCKS", err);
return -1;
}
@@ -752,23 +734,22 @@ determined:
return 0;
}
-static int cd_media_toc(struct udev *udev, int fd)
-{
+static int cd_media_toc(int fd) {
struct scsi_cmd sc;
unsigned char header[12];
unsigned char toc[65536];
- unsigned int len, i, num_tracks;
+ unsigned len, i, num_tracks;
unsigned char *p;
int err;
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x43);
- scsi_cmd_set(udev, &sc, 6, 1);
- scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff);
- scsi_cmd_set(udev, &sc, 9, 0);
- err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header));
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x43);
+ scsi_cmd_set(&sc, 6, 1);
+ scsi_cmd_set(&sc, 8, sizeof(header) & 0xff);
+ scsi_cmd_set(&sc, 9, 0);
+ err = scsi_cmd_run(&sc, fd, header, sizeof(header));
if ((err != 0)) {
- info_scsi_cmd_err(udev, "READ TOC", err);
+ info_scsi_cmd_err("READ TOC", err);
return -1;
}
@@ -785,15 +766,15 @@ static int cd_media_toc(struct udev *udev, int fd)
if (len < 8)
return 0;
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x43);
- scsi_cmd_set(udev, &sc, 6, header[2]); /* First Track/Session Number */
- scsi_cmd_set(udev, &sc, 7, (len >> 8) & 0xff);
- scsi_cmd_set(udev, &sc, 8, len & 0xff);
- scsi_cmd_set(udev, &sc, 9, 0);
- err = scsi_cmd_run(udev, &sc, fd, toc, len);
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x43);
+ scsi_cmd_set(&sc, 6, header[2]); /* First Track/Session Number */
+ scsi_cmd_set(&sc, 7, (len >> 8) & 0xff);
+ scsi_cmd_set(&sc, 8, len & 0xff);
+ scsi_cmd_set(&sc, 9, 0);
+ err = scsi_cmd_run(&sc, fd, toc, len);
if ((err != 0)) {
- info_scsi_cmd_err(udev, "READ TOC (tracks)", err);
+ info_scsi_cmd_err("READ TOC (tracks)", err);
return -1;
}
@@ -801,8 +782,8 @@ static int cd_media_toc(struct udev *udev, int fd)
* the TOC, but also avoid going beyond the TOC length, just in case
* the last track number is invalidly large */
for (p = toc+4, i = 4; i < len-8 && num_tracks > 0; i += 8, p += 8, --num_tracks) {
- unsigned int block;
- unsigned int is_data_track;
+ unsigned block;
+ unsigned is_data_track;
is_data_track = (p[1] & 0x04) != 0;
@@ -816,14 +797,14 @@ static int cd_media_toc(struct udev *udev, int fd)
cd_media_track_count_audio++;
}
- scsi_cmd_init(udev, &sc);
- scsi_cmd_set(udev, &sc, 0, 0x43);
- scsi_cmd_set(udev, &sc, 2, 1); /* Session Info */
- scsi_cmd_set(udev, &sc, 8, sizeof(header));
- scsi_cmd_set(udev, &sc, 9, 0);
- err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header));
+ scsi_cmd_init(&sc);
+ scsi_cmd_set(&sc, 0, 0x43);
+ scsi_cmd_set(&sc, 2, 1); /* Session Info */
+ scsi_cmd_set(&sc, 8, sizeof(header));
+ scsi_cmd_set(&sc, 9, 0);
+ err = scsi_cmd_run(&sc, fd, header, sizeof(header));
if ((err != 0)) {
- info_scsi_cmd_err(udev, "READ TOC (multi session)", err);
+ info_scsi_cmd_err("READ TOC (multi session)", err);
return -1;
}
len = header[4+4] << 24 | header[4+5] << 16 | header[4+6] << 8 | header[4+7];
@@ -833,7 +814,6 @@ static int cd_media_toc(struct udev *udev, int fd)
}
int main(int argc, char *argv[]) {
- struct udev *udev;
static const struct option options[] = {
{ "lock-media", no_argument, NULL, 'l' },
{ "unlock-media", no_argument, NULL, 'u' },
@@ -855,10 +835,6 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
- udev = udev_new();
- if (udev == NULL)
- goto exit;
-
for (;;) {
int option;
@@ -898,7 +874,6 @@ int main(int argc, char *argv[]) {
node = argv[optind];
if (!node) {
log_error("no device");
- fprintf(stderr, "no device\n");
rc = 1;
goto exit;
}
@@ -916,55 +891,54 @@ int main(int argc, char *argv[]) {
}
if (fd < 0) {
log_debug("unable to open '%s'", node);
- fprintf(stderr, "unable to open '%s'\n", node);
rc = 1;
goto exit;
}
log_debug("probing: '%s'", node);
/* same data as original cdrom_id */
- if (cd_capability_compat(udev, fd) < 0) {
+ if (cd_capability_compat(fd) < 0) {
rc = 1;
goto exit;
}
/* check for media - don't bail if there's no media as we still need to
* to read profiles */
- cd_media_compat(udev, fd);
+ cd_media_compat(fd);
/* check if drive talks MMC */
- if (cd_inquiry(udev, fd) < 0)
+ if (cd_inquiry(fd) < 0)
goto work;
/* read drive and possibly current profile */
- if (cd_profiles(udev, fd) != 0)
+ if (cd_profiles(fd) != 0)
goto work;
/* at this point we are guaranteed to have media in the drive - find out more about it */
/* get session/track info */
- cd_media_toc(udev, fd);
+ cd_media_toc(fd);
/* get writable media state */
- cd_media_info(udev, fd);
+ cd_media_info(fd);
work:
/* lock the media, so we enable eject button events */
if (lock && cd_media) {
log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (lock)");
- media_lock(udev, fd, true);
+ media_lock(fd, true);
}
if (unlock && cd_media) {
log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)");
- media_lock(udev, fd, false);
+ media_lock(fd, false);
}
if (eject) {
log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)");
- media_lock(udev, fd, false);
+ media_lock(fd, false);
log_debug("START_STOP_UNIT (eject)");
- media_eject(udev, fd);
+ media_eject(fd);
}
printf("ID_CDROM=1\n");
@@ -1069,7 +1043,6 @@ work:
exit:
if (fd >= 0)
close(fd);
- udev_unref(udev);
log_close();
return rc;
}
diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c
deleted file mode 100644
index a88dedd002..0000000000
--- a/src/udev/collect/collect.c
+++ /dev/null
@@ -1,479 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * Collect variables across events.
- *
- * usage: collect [--add|--remove] <checkpoint> <id> <idlist>
- *
- * Adds ID <id> to the list governed by <checkpoint>.
- * <id> must be part of the ID list <idlist>.
- * If all IDs given by <idlist> are listed (ie collect has been
- * invoked for each ID in <idlist>) collect returns 0, the
- * number of missing IDs otherwise.
- * A negative number is returned on error.
- *
- * Copyright © 2007, Hannes Reinecke <hare@suse.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- */
-
-#include <errno.h>
-#include <getopt.h>
-#include <stddef.h>
-#include <stdio.h>
-
-#include "alloc-util.h"
-#include "libudev-private.h"
-#include "macro.h"
-#include "stdio-util.h"
-#include "string-util.h"
-#include "udev-util.h"
-
-#define BUFSIZE 16
-#define UDEV_ALARM_TIMEOUT 180
-
-enum collect_state {
- STATE_NONE,
- STATE_OLD,
- STATE_CONFIRMED,
-};
-
-struct _mate {
- struct udev_list_node node;
- char *name;
- enum collect_state state;
-};
-
-static struct udev_list_node bunch;
-static int debug;
-
-/* This can increase dynamically */
-static size_t bufsize = BUFSIZE;
-
-static inline struct _mate *node_to_mate(struct udev_list_node *node)
-{
- return container_of(node, struct _mate, node);
-}
-
-_noreturn_ static void sig_alrm(int signo)
-{
- exit(4);
-}
-
-static void usage(void)
-{
- printf("%s [options] <checkpoint> <id> <idlist>\n\n"
- "Collect variables across events.\n\n"
- " -h --help Print this message\n"
- " -a --add Add ID <id> to the list <idlist>\n"
- " -r --remove Remove ID <id> from the list <idlist>\n"
- " -d --debug Debug to stderr\n\n"
- " Adds ID <id> to the list governed by <checkpoint>.\n"
- " <id> must be part of the list <idlist>.\n"
- " If all IDs given by <idlist> are listed (ie collect has been\n"
- " invoked for each ID in <idlist>) collect returns 0, the\n"
- " number of missing IDs otherwise.\n"
- " On error a negative number is returned.\n\n"
- , program_invocation_short_name);
-}
-
-/*
- * prepare
- *
- * Prepares the database file
- */
-static int prepare(char *dir, char *filename)
-{
- char buf[PATH_MAX];
- int r, fd;
-
- r = mkdir(dir, 0700);
- if (r < 0 && errno != EEXIST)
- return -errno;
-
- snprintf(buf, sizeof buf, "%s/%s", dir, filename);
-
- fd = open(buf, O_RDWR|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
- if (fd < 0)
- fprintf(stderr, "Cannot open %s: %m\n", buf);
-
- if (lockf(fd,F_TLOCK,0) < 0) {
- if (debug)
- fprintf(stderr, "Lock taken, wait for %d seconds\n", UDEV_ALARM_TIMEOUT);
- if (IN_SET(errno, EAGAIN, EACCES)) {
- alarm(UDEV_ALARM_TIMEOUT);
- lockf(fd, F_LOCK, 0);
- if (debug)
- fprintf(stderr, "Acquired lock on %s\n", buf);
- } else {
- if (debug)
- fprintf(stderr, "Could not get lock on %s: %m\n", buf);
- }
- }
-
- return fd;
-}
-
-/*
- * Read checkpoint file
- *
- * Tricky reading this. We allocate a buffer twice as large
- * as we're going to read. Then we read into the upper half
- * of that buffer and start parsing.
- * Once we do _not_ find end-of-work terminator (whitespace
- * character) we move the upper half to the lower half,
- * adjust the read pointer and read the next bit.
- * Quite clever methinks :-)
- * I should become a programmer ...
- *
- * Yes, one could have used fgets() for this. But then we'd
- * have to use freopen etc which I found quite tedious.
- */
-static int checkout(int fd)
-{
- int len;
- _cleanup_free_ char *buf = NULL;
- char *ptr, *word = NULL;
- struct _mate *him;
-
- restart:
- len = bufsize >> 1;
- buf = malloc(bufsize + 1);
- if (!buf)
- return log_oom();
- memset(buf, ' ', bufsize);
- buf[bufsize] = '\0';
-
- ptr = buf + len;
- while ((read(fd, buf + len, len)) > 0) {
- while (ptr && *ptr) {
- word = ptr;
- ptr = strpbrk(word," \n\t\r");
- if (!ptr && word < (buf + len)) {
- bufsize = bufsize << 1;
- if (debug)
- fprintf(stderr, "ID overflow, restarting with size %zu\n", bufsize);
- lseek(fd, 0, SEEK_SET);
- goto restart;
- }
- if (ptr) {
- *ptr = '\0';
- ptr++;
- if (isempty(word))
- continue;
-
- if (debug)
- fprintf(stderr, "Found word %s\n", word);
- him = malloc(sizeof (struct _mate));
- if (!him)
- return log_oom();
- him->name = strdup(word);
- if (!him->name) {
- free(him);
- return log_oom();
- }
- him->state = STATE_OLD;
- udev_list_node_append(&him->node, &bunch);
- word = NULL;
- }
- }
- memcpy(buf, buf + len, len);
- memset(buf + len, ' ', len);
-
- if (!ptr)
- ptr = word;
- ptr -= len;
- }
-
- return 0;
-}
-
-/*
- * invite
- *
- * Adds a new ID 'us' to the internal list,
- * marks it as confirmed.
- */
-static void invite(char *us)
-{
- struct udev_list_node *him_node;
- struct _mate *who = NULL;
-
- if (debug)
- fprintf(stderr, "Adding ID '%s'\n", us);
-
- udev_list_node_foreach(him_node, &bunch) {
- struct _mate *him = node_to_mate(him_node);
-
- if (streq(him->name, us)) {
- him->state = STATE_CONFIRMED;
- who = him;
- }
- }
- if (debug && !who)
- fprintf(stderr, "ID '%s' not in database\n", us);
-
-}
-
-/*
- * reject
- *
- * Marks the ID 'us' as invalid,
- * causing it to be removed when the
- * list is written out.
- */
-static void reject(char *us)
-{
- struct udev_list_node *him_node;
- struct _mate *who = NULL;
-
- if (debug)
- fprintf(stderr, "Removing ID '%s'\n", us);
-
- udev_list_node_foreach(him_node, &bunch) {
- struct _mate *him = node_to_mate(him_node);
-
- if (streq(him->name, us)) {
- him->state = STATE_NONE;
- who = him;
- }
- }
- if (debug && !who)
- fprintf(stderr, "ID '%s' not in database\n", us);
-}
-
-/*
- * kickout
- *
- * Remove all IDs in the internal list which are not part
- * of the list passed via the command line.
- */
-static void kickout(void)
-{
- struct udev_list_node *him_node;
- struct udev_list_node *tmp;
-
- udev_list_node_foreach_safe(him_node, tmp, &bunch) {
- struct _mate *him = node_to_mate(him_node);
-
- if (him->state == STATE_OLD) {
- udev_list_node_remove(&him->node);
- free(him->name);
- free(him);
- }
- }
-}
-
-/*
- * missing
- *
- * Counts all missing IDs in the internal list.
- */
-static int missing(int fd)
-{
- char *buf;
- int ret = 0;
- struct udev_list_node *him_node;
-
- buf = malloc(bufsize);
- if (!buf)
- return log_oom();
-
- udev_list_node_foreach(him_node, &bunch) {
- struct _mate *him = node_to_mate(him_node);
-
- if (him->state == STATE_NONE) {
- ret++;
- } else {
- while (strlen(him->name)+1 >= bufsize) {
- char *tmpbuf;
-
- bufsize = bufsize << 1;
- tmpbuf = realloc(buf, bufsize);
- if (!tmpbuf) {
- free(buf);
- return log_oom();
- }
- buf = tmpbuf;
- }
- snprintf(buf, strlen(him->name)+2, "%s ", him->name);
- if (write(fd, buf, strlen(buf)) < 0) {
- free(buf);
- return -1;
- }
- }
- }
-
- free(buf);
- return ret;
-}
-
-/*
- * everybody
- *
- * Prints out the status of the internal list.
- */
-static void everybody(void)
-{
- struct udev_list_node *him_node;
- const char *state = "";
-
- udev_list_node_foreach(him_node, &bunch) {
- struct _mate *him = node_to_mate(him_node);
-
- switch (him->state) {
- case STATE_NONE:
- state = "none";
- break;
- case STATE_OLD:
- state = "old";
- break;
- case STATE_CONFIRMED:
- state = "confirmed";
- break;
- }
- fprintf(stderr, "ID: %s=%s\n", him->name, state);
- }
-}
-
-int main(int argc, char **argv) {
- static const struct option options[] = {
- { "add", no_argument, NULL, 'a' },
- { "remove", no_argument, NULL, 'r' },
- { "debug", no_argument, NULL, 'd' },
- { "help", no_argument, NULL, 'h' },
- {}
- };
- int argi;
- char *checkpoint, *us;
- int fd;
- int i;
- int ret = EXIT_SUCCESS;
- int prune = 0;
- char tmpdir[UTIL_PATH_SIZE];
-
- log_set_target(LOG_TARGET_AUTO);
- udev_parse_config();
- log_parse_environment();
- log_open();
-
- for (;;) {
- int option;
-
- option = getopt_long(argc, argv, "ardh", options, NULL);
- if (option == -1)
- break;
-
- switch (option) {
- case 'a':
- prune = 0;
- break;
- case 'r':
- prune = 1;
- break;
- case 'd':
- debug = 1;
- break;
- case 'h':
- usage();
- return 0;
- default:
- return 1;
- }
- }
-
- argi = optind;
- if (argi + 2 > argc) {
- printf("Missing parameter(s)\n");
- return 1;
- }
- checkpoint = argv[argi++];
- us = argv[argi++];
-
- if (signal(SIGALRM, sig_alrm) == SIG_ERR) {
- fprintf(stderr, "Cannot set SIGALRM: %m\n");
- return 2;
- }
-
- udev_list_node_init(&bunch);
-
- if (debug)
- fprintf(stderr, "Using checkpoint '%s'\n", checkpoint);
-
- strscpyl(tmpdir, sizeof(tmpdir), "/run/udev/collect", NULL);
- fd = prepare(tmpdir, checkpoint);
- if (fd < 0) {
- ret = 3;
- goto out;
- }
-
- if (checkout(fd) < 0) {
- ret = 2;
- goto out;
- }
-
- for (i = argi; i < argc; i++) {
- struct udev_list_node *him_node;
- struct _mate *who;
-
- who = NULL;
- udev_list_node_foreach(him_node, &bunch) {
- struct _mate *him = node_to_mate(him_node);
-
- if (streq(him->name, argv[i]))
- who = him;
- }
- if (!who) {
- struct _mate *him;
-
- if (debug)
- fprintf(stderr, "ID %s: not in database\n", argv[i]);
- him = new(struct _mate, 1);
- if (!him) {
- ret = ENOMEM;
- goto out;
- }
-
- him->name = strdup(argv[i]);
- if (!him->name) {
- free(him);
- ret = ENOMEM;
- goto out;
- }
-
- him->state = STATE_NONE;
- udev_list_node_append(&him->node, &bunch);
- } else {
- if (debug)
- fprintf(stderr, "ID %s: found in database\n", argv[i]);
- who->state = STATE_CONFIRMED;
- }
- }
-
- if (prune)
- reject(us);
- else
- invite(us);
-
- if (debug) {
- everybody();
- fprintf(stderr, "Prune lists\n");
- }
- kickout();
-
- lseek(fd, 0, SEEK_SET);
- ftruncate(fd, 0);
- ret = missing(fd);
-
- lockf(fd, F_ULOCK, 0);
- close(fd);
-out:
- if (debug)
- everybody();
- if (ret >= 0)
- printf("COLLECT_%s=%d\n", checkpoint, ret);
- return ret;
-}
diff --git a/src/udev/meson.build b/src/udev/meson.build
index 3bcd2bd3d7..e378d9190c 100644
--- a/src/udev/meson.build
+++ b/src/udev/meson.build
@@ -2,14 +2,15 @@
udevadm_sources = files('''
udevadm.c
- udevadm-info.c
+ udevadm.h
udevadm-control.c
- udevadm-monitor.c
udevadm-hwdb.c
+ udevadm-info.c
+ udevadm-monitor.c
udevadm-settle.c
- udevadm-trigger.c
udevadm-test.c
udevadm-test-builtin.c
+ udevadm-trigger.c
udevadm-util.c
udevadm-util.h
'''.split())
@@ -18,12 +19,16 @@ systemd_udevd_sources = files('udevd.c')
libudev_core_sources = '''
udev.h
+ udev-ctrl.c
+ udev-ctrl.h
udev-event.c
- udev-watch.c
udev-node.c
+ udev-node.h
udev-rules.c
- udev-ctrl.c
+ udev-watch.c
+ udev-watch.h
udev-builtin.c
+ udev-builtin.h
udev-builtin-btrfs.c
udev-builtin-hwdb.c
udev-builtin-input_id.c
@@ -49,7 +54,7 @@ endif
if conf.get('HAVE_ACL') == 1
libudev_core_sources += ['udev-builtin-uaccess.c',
logind_acl_c,
- sd_login_c]
+ sd_login_sources]
endif
############################################################
@@ -123,6 +128,7 @@ install_libudev_static = static_library(
shared_sources,
libsystemd_sources,
libudev_sources,
+ disable_mempool_c,
include_directories : includes,
build_by_default : static_libudev != 'false',
install : static_libudev != 'false',
@@ -134,7 +140,7 @@ install_libudev_static = static_library(
libudev = shared_library(
'udev',
- 'udev.h', # pick a header file at random to work around old meson bug
+ disable_mempool_c,
version : libudev_version,
include_directories : includes,
link_args : ['-shared',
@@ -159,7 +165,6 @@ libudev_core = static_library(
foreach prog : [['ata_id/ata_id.c'],
['cdrom_id/cdrom_id.c'],
- ['collect/collect.c'],
['scsi_id/scsi_id.c',
'scsi_id/scsi_id.h',
'scsi_id/scsi_serial.c',
diff --git a/src/udev/mtd_probe/probe_smartmedia.c b/src/udev/mtd_probe/probe_smartmedia.c
index 099809da1b..f8e1b140f5 100644
--- a/src/udev/mtd_probe/probe_smartmedia.c
+++ b/src/udev/mtd_probe/probe_smartmedia.c
@@ -49,19 +49,17 @@ int probe_smart_media(int mtd_fd, mtd_info_t* info) {
if (!cis_buffer)
return log_oom();
- if (info->type != MTD_NANDFLASH) {
- log_debug("Not marked MTD_NANDFLASH.");
- return -EINVAL;
- }
+ if (info->type != MTD_NANDFLASH)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not marked MTD_NANDFLASH.");
sector_size = info->writesize;
block_size = info->erasesize;
size_in_megs = info->size / (1024 * 1024);
- if (!IN_SET(sector_size, SM_SECTOR_SIZE, SM_SMALL_PAGE)) {
- log_debug("Unexpected sector size: %i", sector_size);
- return -EINVAL;
- }
+ if (!IN_SET(sector_size, SM_SECTOR_SIZE, SM_SMALL_PAGE))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unexpected sector size: %i", sector_size);
switch(size_in_megs) {
case 1:
@@ -85,16 +83,14 @@ int probe_smart_media(int mtd_fd, mtd_info_t* info) {
}
}
- if (!cis_found) {
- log_debug("CIS not found");
- return -EINVAL;
- }
+ if (!cis_found)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "CIS not found");
if (memcmp(cis_buffer, cis_signature, sizeof(cis_signature)) != 0 &&
- memcmp(cis_buffer + SM_SMALL_PAGE, cis_signature, sizeof(cis_signature)) != 0) {
- log_debug("CIS signature didn't match");
- return -EINVAL;
- }
+ memcmp(cis_buffer + SM_SMALL_PAGE, cis_signature, sizeof(cis_signature)) != 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "CIS signature didn't match");
printf("MTD_FTL=smartmedia\n");
return 0;
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
index 4bb4216ac8..bc0deaf347 100644
--- a/src/udev/net/ethtool-util.c
+++ b/src/udev/net/ethtool-util.c
@@ -56,6 +56,65 @@ static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
[NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation",
};
+static const char* const ethtool_link_mode_bit_table[] = {
+ [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = "10baset-half",
+ [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = "10baset-full",
+ [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = "100baset-half",
+ [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = "100baset-full",
+ [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = "1000baset-half",
+ [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = "1000baset-full",
+ [ETHTOOL_LINK_MODE_Autoneg_BIT] = "autonegotiation",
+ [ETHTOOL_LINK_MODE_TP_BIT] = "tp",
+ [ETHTOOL_LINK_MODE_AUI_BIT] = "aui",
+ [ETHTOOL_LINK_MODE_MII_BIT] = "mii",
+ [ETHTOOL_LINK_MODE_FIBRE_BIT] = "fibre",
+ [ETHTOOL_LINK_MODE_BNC_BIT] = "bnc",
+ [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = "10000baset-full",
+ [ETHTOOL_LINK_MODE_Pause_BIT] = "pause",
+ [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = "asym-pause",
+ [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = "2500basex-full",
+ [ETHTOOL_LINK_MODE_Backplane_BIT] = "backplane",
+ [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = "1000basekx-full",
+ [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = "10000basekx4-full",
+ [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = "10000basekr-full",
+ [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = "10000baser-fec",
+ [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = "20000basemld2-full",
+ [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = "20000basekr2-full",
+ [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = "40000basekr4-full",
+ [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = "40000basecr4-full",
+ [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = "40000basesr4-full",
+ [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = "40000baselr4-full",
+ [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = "56000basekr4-full",
+ [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = "56000basecr4-full",
+ [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = "56000basesr4-full",
+ [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = "56000baselr4-full",
+ [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = "25000basecr-full",
+ [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = "25000basekr-full",
+ [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = "25000basesr-full",
+ [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = "50000basecr2-full",
+ [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = "50000basekr2-full",
+ [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = "100000basekr4-full",
+ [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = "100000basesr4-full",
+ [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = "100000basecr4-full",
+ [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = "100000baselr4-er4-full",
+ [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = "50000basesr2-full",
+ [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = "1000basex-full",
+ [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = "10000basecr-full",
+ [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = "10000basesr-full",
+ [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = "10000baselr-full",
+ [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = "10000baselrm-full",
+ [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = "10000baseer-full",
+ [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = "2500baset-full",
+ [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = "5000baset-full",
+ [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = "fec-none",
+ [ETHTOOL_LINK_MODE_FEC_RS_BIT] = "fec-rs",
+ [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = "fec-baser",
+};
+/* Make sure the array is large enough to fit all bits */
+assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < ELEMENTSOF(((struct link_config){}).advertise));
+
+DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices);
+
int ethtool_connect(int *ret) {
int fd;
@@ -100,7 +159,7 @@ int ethtool_get_driver(int *fd, const char *ifname, char **ret) {
return 0;
}
-int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex) {
+int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex) {
struct ethtool_cmd ecmd = {
.cmd = ETHTOOL_GSET
};
@@ -302,7 +361,7 @@ static int find_feature_index(struct ethtool_gstrings *strings, const char *feat
return -1;
}
-int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) {
+int ethtool_set_features(int *fd, const char *ifname, int *features) {
_cleanup_free_ struct ethtool_gstrings *strings = NULL;
struct ethtool_sfeatures *sfeatures;
int block, bit, i, r;
@@ -320,7 +379,7 @@ int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) {
if (r < 0)
return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname);
- sfeatures = alloca0(sizeof(struct ethtool_gstrings) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
+ sfeatures = alloca0(sizeof(struct ethtool_sfeatures) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
sfeatures->cmd = ETHTOOL_SFEATURES;
sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
@@ -455,7 +514,7 @@ static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_lin
struct ethtool_link_settings req;
__u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
} ecmd = {};
- unsigned int offset;
+ unsigned offset;
int r;
if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
@@ -501,6 +560,8 @@ static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettin
ecmd.phy_address = u->base.phy_address;
ecmd.autoneg = u->base.autoneg;
ecmd.mdio_support = u->base.mdio_support;
+ ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
+ ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
ifr->ifr_data = (void *) &ecmd;
@@ -513,11 +574,10 @@ static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettin
/* If autonegotiation is disabled, the speed and duplex represent the fixed link
* mode and are writable if the driver supports multiple link modes. If it is
- * enabled then they are read-only. If the link is up they represent the negotiated
+ * enabled then they are read-only. If the link is up they represent the negotiated
* link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
* enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
*/
-
int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link) {
_cleanup_free_ struct ethtool_link_usettings *u = NULL;
struct ifreq ifr = {};
@@ -554,6 +614,12 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *l
u->base.autoneg = link->autonegotiation;
+ if (!eqzero(link->advertise)) {
+ memcpy(&u->link_modes.advertising, link->advertise, sizeof(link->advertise));
+ memzero((uint8_t*) &u->link_modes.advertising + sizeof(link->advertise),
+ ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(link->advertise));
+ }
+
if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
r = set_slinksettings(*fd, &ifr, u);
else
@@ -665,3 +731,55 @@ int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels)
return 0;
}
+
+int config_parse_advertise(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) {
+ link_config *config = data;
+ const char *p;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ /* Empty string resets the value. */
+ zero(config->advertise);
+ return 0;
+ }
+
+ for (p = rvalue;;) {
+ _cleanup_free_ char *w = NULL;
+ enum ethtool_link_mode_bit_indices mode;
+
+ r = extract_first_word(&p, &w, NULL, 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split advertise modes '%s', ignoring: %m", rvalue);
+ break;
+ }
+ if (r == 0)
+ break;
+
+ mode = ethtool_link_mode_bit_from_string(w);
+ if (mode < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse advertise mode, ignoring: %s", w);
+ continue;
+ }
+
+ config->advertise[mode / 32] |= 1UL << (mode % 32);
+ }
+
+ return 0;
+}
diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h
index 064bf4d2bd..e4bc5161d5 100644
--- a/src/udev/net/ethtool-util.h
+++ b/src/udev/net/ethtool-util.h
@@ -1,11 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
#include <macro.h>
#include <linux/ethtool.h>
-#include "missing.h"
+#include "conf-parser.h"
+#include "missing_network.h"
struct link_config;
@@ -55,6 +55,7 @@ typedef enum NetDevPort {
} NetDevPort;
#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
+#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES (4 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
/* layout of the struct passed from/to userland */
struct ethtool_link_usettings {
@@ -82,9 +83,9 @@ typedef struct netdev_channels {
int ethtool_connect(int *ret);
int ethtool_get_driver(int *fd, const char *ifname, char **ret);
-int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex);
+int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex);
int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol);
-int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features);
+int ethtool_set_features(int *fd, const char *ifname, int *features);
int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link);
int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels);
@@ -97,7 +98,11 @@ WakeOnLan wol_from_string(const char *wol) _pure_;
const char *port_to_string(NetDevPort port) _const_;
NetDevPort port_from_string(const char *port) _pure_;
-int config_parse_duplex(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_wol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_port(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_channel(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);
+const char *ethtool_link_mode_bit_to_string(enum ethtool_link_mode_bit_indices val) _const_;
+enum ethtool_link_mode_bit_indices ethtool_link_mode_bit_from_string(const char *str) _pure_;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_duplex);
+CONFIG_PARSER_PROTOTYPE(config_parse_wol);
+CONFIG_PARSER_PROTOTYPE(config_parse_port);
+CONFIG_PARSER_PROTOTYPE(config_parse_channel);
+CONFIG_PARSER_PROTOTYPE(config_parse_advertise);
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index 5640fa0513..2bc18bff56 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -51,3 +51,4 @@ Link.RxChannels, config_parse_channel, 0,
Link.TxChannels, config_parse_channel, 0, 0
Link.OtherChannels, config_parse_channel, 0, 0
Link.CombinedChannels, config_parse_channel, 0, 0
+Link.Advertise, config_parse_advertise, 0, 0
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index cec4f4f779..ac66ffd047 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -2,17 +2,18 @@
#include <netinet/ether.h>
+#include "sd-device.h"
#include "sd-netlink.h"
#include "alloc-util.h"
#include "conf-files.h"
#include "conf-parser.h"
+#include "device-util.h"
#include "ethtool-util.h"
#include "fd-util.h"
-#include "libudev-private.h"
#include "link-config.h"
#include "log.h"
-#include "missing.h"
+#include "missing_network.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "parse-util.h"
@@ -125,6 +126,7 @@ int link_config_ctx_new(link_config_ctx **ret) {
static int load_link(link_config_ctx *ctx, const char *filename) {
_cleanup_(link_config_freep) link_config *link = NULL;
_cleanup_fclose_ FILE *file = NULL;
+ int i;
int r;
assert(ctx);
@@ -153,7 +155,8 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
link->port = _NET_DEV_PORT_INVALID;
link->autonegotiation = -1;
- memset(&link->features, 0xFF, sizeof(link->features));
+ for (i = 0; i < (int)ELEMENTSOF(link->features); i++)
+ link->features[i] = -1;
r = config_parse(NULL, filename, file,
"Match\0Link\0Ethernet\0",
@@ -215,8 +218,7 @@ bool link_config_should_reload(link_config_ctx *ctx) {
return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false);
}
-int link_config_get(link_config_ctx *ctx, struct udev_device *device,
- link_config **ret) {
+int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) {
link_config *link;
assert(ctx);
@@ -224,43 +226,50 @@ int link_config_get(link_config_ctx *ctx, struct udev_device *device,
assert(ret);
LIST_FOREACH(links, link, ctx->links) {
- const char* attr_value;
+ const char *address = NULL, *id_path = NULL, *parent_driver = NULL, *id_net_driver = NULL, *devtype = NULL, *sysname = NULL;
+ sd_device *parent;
- attr_value = udev_device_get_sysattr_value(device, "address");
+ (void) sd_device_get_sysattr_value(device, "address", &address);
+ (void) sd_device_get_property_value(device, "ID_PATH", &id_path);
+ if (sd_device_get_parent(device, &parent) >= 0)
+ (void) sd_device_get_driver(parent, &parent_driver);
+ (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &id_net_driver);
+ (void) sd_device_get_devtype(device, &devtype);
+ (void) sd_device_get_sysname(device, &sysname);
if (net_match_config(link->match_mac, link->match_path, link->match_driver,
link->match_type, link->match_name, link->match_host,
link->match_virt, link->match_kernel_cmdline,
link->match_kernel_version, link->match_arch,
- attr_value ? ether_aton(attr_value) : NULL,
- udev_device_get_property_value(device, "ID_PATH"),
- udev_device_get_driver(udev_device_get_parent(device)),
- udev_device_get_property_value(device, "ID_NET_DRIVER"),
- udev_device_get_devtype(device),
- udev_device_get_sysname(device))) {
+ address ? ether_aton(address) : NULL,
+ id_path,
+ parent_driver,
+ id_net_driver,
+ devtype,
+ sysname)) {
if (link->match_name) {
unsigned char name_assign_type = NET_NAME_UNKNOWN;
+ const char *attr_value;
- attr_value = udev_device_get_sysattr_value(device, "name_assign_type");
- if (attr_value)
+ if (sd_device_get_sysattr_value(device, "name_assign_type", &attr_value) >= 0)
(void) safe_atou8(attr_value, &name_assign_type);
if (name_assign_type == NET_NAME_ENUM) {
log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'",
- link->filename, udev_device_get_sysname(device));
+ link->filename, sysname);
*ret = link;
return 0;
} else if (name_assign_type == NET_NAME_RENAMED) {
log_warning("Config file %s matches device based on renamed interface name '%s', ignoring",
- link->filename, udev_device_get_sysname(device));
+ link->filename, sysname);
continue;
}
}
log_debug("Config file %s applies to device %s",
- link->filename, udev_device_get_sysname(device));
+ link->filename, sysname);
*ret = link;
@@ -273,14 +282,13 @@ int link_config_get(link_config_ctx *ctx, struct udev_device *device,
return -ENOENT;
}
-static bool mac_is_random(struct udev_device *device) {
+static bool mac_is_random(sd_device *device) {
const char *s;
unsigned type;
int r;
/* if we can't get the assign type, assume it is not random */
- s = udev_device_get_sysattr_value(device, "addr_assign_type");
- if (!s)
+ if (sd_device_get_sysattr_value(device, "addr_assign_type", &s) < 0)
return false;
r = safe_atou(s, &type);
@@ -290,14 +298,13 @@ static bool mac_is_random(struct udev_device *device) {
return type == NET_ADDR_RANDOM;
}
-static bool should_rename(struct udev_device *device, bool respect_predictable) {
+static bool should_rename(sd_device *device, bool respect_predictable) {
const char *s;
unsigned type;
int r;
/* if we can't get the assgin type, assume we should rename */
- s = udev_device_get_sysattr_value(device, "name_assign_type");
- if (!s)
+ if (sd_device_get_sysattr_value(device, "name_assign_type", &s) < 0)
return true;
r = safe_atou(s, &type);
@@ -305,23 +312,18 @@ static bool should_rename(struct udev_device *device, bool respect_predictable)
return true;
switch (type) {
- case NET_NAME_USER:
- case NET_NAME_RENAMED:
- /* these were already named by userspace, do not touch again */
- return false;
case NET_NAME_PREDICTABLE:
/* the kernel claims to have given a predictable name */
if (respect_predictable)
return false;
_fallthrough_;
- case NET_NAME_ENUM:
default:
/* the name is known to be bad, or of an unknown type */
return true;
}
}
-static int get_mac(struct udev_device *device, bool want_random,
+static int get_mac(sd_device *device, bool want_random,
struct ether_addr *mac) {
int r;
@@ -346,7 +348,7 @@ static int get_mac(struct udev_device *device, bool want_random,
}
int link_config_apply(link_config_ctx *ctx, link_config *config,
- struct udev_device *device, const char **name) {
+ sd_device *device, const char **name) {
bool respect_predictable = false;
struct ether_addr generated_mac;
struct ether_addr *mac = NULL;
@@ -360,23 +362,30 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
assert(device);
assert(name);
- old_name = udev_device_get_sysname(device);
- if (!old_name)
- return -EINVAL;
+ r = sd_device_get_sysname(device, &old_name);
+ if (r < 0)
+ return r;
r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, config);
if (r < 0) {
if (config->port != _NET_DEV_PORT_INVALID)
- log_warning_errno(r, "Could not set port (%s) of %s: %m", port_to_string(config->port), old_name);
+ log_warning_errno(r, "Could not set port (%s) of %s: %m", port_to_string(config->port), old_name);
- speed = DIV_ROUND_UP(config->speed, 1000000);
- if (r == -EOPNOTSUPP)
- r = ethtool_set_speed(&ctx->ethtool_fd, old_name, speed, config->duplex);
+ if (!eqzero(config->advertise))
+ log_warning_errno(r, "Could not set advertise mode: %m"); /* TODO: include modes in the log message. */
- if (r < 0)
- log_warning_errno(r, "Could not set speed or duplex of %s to %u Mbps (%s): %m",
- old_name, speed, duplex_to_string(config->duplex));
+ if (config->speed) {
+ speed = DIV_ROUND_UP(config->speed, 1000000);
+ if (r == -EOPNOTSUPP) {
+ r = ethtool_set_speed(&ctx->ethtool_fd, old_name, speed, config->duplex);
+ if (r < 0)
+ log_warning_errno(r, "Could not set speed of %s to %u Mbps: %m", old_name, speed);
+ }
+ }
+
+ if (config->duplex !=_DUP_INVALID)
+ log_warning_errno(r, "Could not set duplex of %s to (%s): %m", old_name, duplex_to_string(config->duplex));
}
r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol);
@@ -394,11 +403,9 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
log_warning_errno(r, "Could not set channels of %s: %m", old_name);
}
- ifindex = udev_device_get_ifindex(device);
- if (ifindex <= 0) {
- log_warning("Could not find ifindex");
- return -ENODEV;
- }
+ r = sd_device_get_ifindex(device, &ifindex);
+ if (r < 0)
+ return log_device_warning_errno(device, r, "Could not find ifindex: %m");
if (ctx->enable_name_policy && config->name_policy) {
NamePolicy *policy;
@@ -410,19 +417,19 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
respect_predictable = true;
break;
case NAMEPOLICY_DATABASE:
- new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE");
+ (void) sd_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE", &new_name);
break;
case NAMEPOLICY_ONBOARD:
- new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
+ (void) sd_device_get_property_value(device, "ID_NET_NAME_ONBOARD", &new_name);
break;
case NAMEPOLICY_SLOT:
- new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
+ (void) sd_device_get_property_value(device, "ID_NET_NAME_SLOT", &new_name);
break;
case NAMEPOLICY_PATH:
- new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
+ (void) sd_device_get_property_value(device, "ID_NET_NAME_PATH", &new_name);
break;
case NAMEPOLICY_MAC:
- new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
+ (void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &new_name);
break;
default:
break;
@@ -430,12 +437,8 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
}
}
- if (should_rename(device, respect_predictable)) {
- /* if not set by policy, fall back manually set name */
- if (!new_name)
- new_name = config->name;
- } else
- new_name = NULL;
+ if (!new_name && should_rename(device, respect_predictable))
+ new_name = config->name;
switch (config->mac_policy) {
case MACPOLICY_PERSISTENT:
@@ -474,14 +477,14 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
return 0;
}
-int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) {
+int link_get_driver(link_config_ctx *ctx, sd_device *device, char **ret) {
const char *name;
char *driver = NULL;
int r;
- name = udev_device_get_sysname(device);
- if (!name)
- return -EINVAL;
+ r = sd_device_get_sysname(device, &name);
+ if (r < 0)
+ return r;
r = ethtool_get_driver(&ctx->ethtool_fd, name, &driver);
if (r < 0)
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index 4798bb101c..8204959034 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -1,10 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-
-#include "libudev.h"
+#include "sd-device.h"
#include "condition.h"
+#include "conf-parser.h"
#include "ethtool-util.h"
#include "list.h"
#include "set.h"
@@ -55,9 +55,10 @@ struct link_config {
size_t speed;
Duplex duplex;
int autonegotiation;
+ uint32_t advertise[2];
WakeOnLan wol;
NetDevPort port;
- NetDevFeature features[_NET_DEV_FEAT_MAX];
+ int features[_NET_DEV_FEAT_MAX];
netdev_channels channels;
LIST_FIELDS(link_config, links);
@@ -69,10 +70,9 @@ void link_config_ctx_free(link_config_ctx *ctx);
int link_config_load(link_config_ctx *ctx);
bool link_config_should_reload(link_config_ctx *ctx);
-int link_config_get(link_config_ctx *ctx, struct udev_device *device, struct link_config **ret);
-int link_config_apply(link_config_ctx *ctx, struct link_config *config, struct udev_device *device, const char **name);
-
-int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret);
+int link_config_get(link_config_ctx *ctx, sd_device *device, struct link_config **ret);
+int link_config_apply(link_config_ctx *ctx, struct link_config *config, sd_device *device, const char **name);
+int link_get_driver(link_config_ctx *ctx, sd_device *device, char **ret);
const char *name_policy_to_string(NamePolicy p) _const_;
NamePolicy name_policy_from_string(const char *p) _pure_;
@@ -83,5 +83,5 @@ MACPolicy mac_policy_from_string(const char *p) _pure_;
/* gperf lookup function */
const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
-int config_parse_mac_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_name_policy(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);
+CONFIG_PARSER_PROTOTYPE(config_parse_mac_policy);
+CONFIG_PARSER_PROTOTYPE(config_parse_name_policy);
diff --git a/src/udev/scsi_id/scsi.h b/src/udev/scsi_id/scsi.h
index 8b759a768d..208b3e72ab 100644
--- a/src/udev/scsi_id/scsi.h
+++ b/src/udev/scsi_id/scsi.h
@@ -16,8 +16,8 @@
#include <scsi/scsi.h>
struct scsi_ioctl_command {
- unsigned int inlen; /* excluding scsi command length */
- unsigned int outlen;
+ unsigned inlen; /* excluding scsi command length */
+ unsigned outlen;
unsigned char data[1];
/* on input, scsi command starts here then opt. data */
};
diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c
index 5caab7774f..e94f2946f9 100644
--- a/src/udev/scsi_id/scsi_id.c
+++ b/src/udev/scsi_id/scsi_id.c
@@ -2,7 +2,6 @@
/*
* Copyright © IBM Corp. 2003
* Copyright © SUSE Linux Products GmbH, 2006
- *
*/
#include <ctype.h>
@@ -18,12 +17,12 @@
#include <sys/stat.h>
#include <unistd.h>
-#include "libudev.h"
-
+#include "alloc-util.h"
#include "fd-util.h"
-#include "libudev-private.h"
+#include "libudev-util.h"
#include "scsi_id.h"
#include "string-util.h"
+#include "strxcpyx.h"
#include "udev-util.h"
static const struct option options[] = {
@@ -55,8 +54,7 @@ static char model_enc_str[256];
static char revision_str[16];
static char type_str[16];
-static void set_type(const char *from, char *to, size_t len)
-{
+static void set_type(const char *from, char *to, size_t len) {
int type_num;
char *eptr;
const char *type = "generic";
@@ -101,8 +99,7 @@ static void set_type(const char *from, char *to, size_t len)
* Return a pointer to the NUL terminated string, returns NULL if no
* matches.
*/
-static char *get_value(char **buffer)
-{
+static char *get_value(char **buffer) {
static const char *quote_string = "\"\n";
static const char *comma_string = ",\n";
char *val;
@@ -130,8 +127,7 @@ static char *get_value(char **buffer)
return val;
}
-static int argc_count(char *opts)
-{
+static int argc_count(char *opts) {
int i = 0;
while (*opts != '\0')
if (*opts++ == ' ')
@@ -148,10 +144,8 @@ static int argc_count(char *opts)
*
* vendor and model can end in '\n'.
*/
-static int get_file_options(struct udev *udev,
- const char *vendor, const char *model,
- int *argc, char ***newargv)
-{
+static int get_file_options(const char *vendor, const char *model,
+ int *argc, char ***newargv) {
_cleanup_free_ char *buffer = NULL;
_cleanup_fclose_ FILE *f;
char *buf;
@@ -311,10 +305,8 @@ static void help(void) {
}
-static int set_options(struct udev *udev,
- int argc, char **argv,
- char *maj_min_dev)
-{
+static int set_options(int argc, char **argv,
+ char *maj_min_dev) {
int option;
/*
@@ -400,9 +392,7 @@ static int set_options(struct udev *udev,
return 0;
}
-static int per_dev_options(struct udev *udev,
- struct scsi_id_device *dev_scsi, int *good_bad, int *page_code)
-{
+static int per_dev_options(struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) {
int retval;
int newargc;
char **newargv = NULL;
@@ -411,7 +401,7 @@ static int per_dev_options(struct udev *udev,
*good_bad = all_good;
*page_code = default_page_code;
- retval = get_file_options(udev, vendor_str, model_str, &newargc, &newargv);
+ retval = get_file_options(vendor_str, model_str, &newargc, &newargv);
optind = 1; /* reset this global extern */
while (retval == 0) {
@@ -455,25 +445,24 @@ static int per_dev_options(struct udev *udev,
return retval;
}
-static int set_inq_values(struct udev *udev, struct scsi_id_device *dev_scsi, const char *path)
-{
+static int set_inq_values(struct scsi_id_device *dev_scsi, const char *path) {
int retval;
dev_scsi->use_sg = sg_version;
- retval = scsi_std_inquiry(udev, dev_scsi, path);
+ retval = scsi_std_inquiry(dev_scsi, path);
if (retval)
return retval;
udev_util_encode_string(dev_scsi->vendor, vendor_enc_str, sizeof(vendor_enc_str));
udev_util_encode_string(dev_scsi->model, model_enc_str, sizeof(model_enc_str));
- util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str));
+ util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)-1);
util_replace_chars(vendor_str, NULL);
- util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str));
+ util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)-1);
util_replace_chars(model_str, NULL);
set_type(dev_scsi->type, type_str, sizeof(type_str));
- util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str));
+ util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)-1);
util_replace_chars(revision_str, NULL);
return 0;
}
@@ -482,27 +471,26 @@ static int set_inq_values(struct udev *udev, struct scsi_id_device *dev_scsi, co
* scsi_id: try to get an id, if one is found, printf it to stdout.
* returns a value passed to exit() - 0 if printed an id, else 1.
*/
-static int scsi_id(struct udev *udev, char *maj_min_dev)
-{
+static int scsi_id(char *maj_min_dev) {
struct scsi_id_device dev_scsi = {};
int good_dev;
int page_code;
int retval = 0;
- if (set_inq_values(udev, &dev_scsi, maj_min_dev) < 0) {
+ if (set_inq_values(&dev_scsi, maj_min_dev) < 0) {
retval = 1;
goto out;
}
/* get per device (vendor + model) options from the config file */
- per_dev_options(udev, &dev_scsi, &good_dev, &page_code);
+ per_dev_options(&dev_scsi, &good_dev, &page_code);
if (!good_dev) {
retval = 1;
goto out;
}
/* read serial number from mode pages (no values for optical drives) */
- scsi_get_serial(udev, &dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN);
+ scsi_get_serial(&dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN);
if (export) {
char serial_str[MAX_SERIAL_LEN];
@@ -515,10 +503,10 @@ static int scsi_id(struct udev *udev, char *maj_min_dev)
printf("ID_REVISION=%s\n", revision_str);
printf("ID_TYPE=%s\n", type_str);
if (dev_scsi.serial[0] != '\0') {
- util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str));
+ util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)-1);
util_replace_chars(serial_str, NULL);
printf("ID_SERIAL=%s\n", serial_str);
- util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str));
+ util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)-1);
util_replace_chars(serial_str, NULL);
printf("ID_SERIAL_SHORT=%s\n", serial_str);
}
@@ -545,7 +533,7 @@ static int scsi_id(struct udev *udev, char *maj_min_dev)
if (reformat_serial) {
char serial_str[MAX_SERIAL_LEN];
- util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str));
+ util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)-1);
util_replace_chars(serial_str, NULL);
printf("%s\n", serial_str);
goto out;
@@ -557,7 +545,6 @@ out:
}
int main(int argc, char **argv) {
- _cleanup_(udev_unrefp) struct udev *udev;
int retval = 0;
char maj_min_dev[MAX_PATH_LEN];
int newargc;
@@ -568,14 +555,10 @@ int main(int argc, char **argv) {
log_parse_environment();
log_open();
- udev = udev_new();
- if (udev == NULL)
- goto exit;
-
/*
* Get config file options.
*/
- retval = get_file_options(udev, NULL, NULL, &newargc, &newargv);
+ retval = get_file_options(NULL, NULL, &newargc, &newargv);
if (retval < 0) {
retval = 1;
goto exit;
@@ -583,7 +566,7 @@ int main(int argc, char **argv) {
if (retval == 0) {
assert(newargv);
- if (set_options(udev, newargc, newargv, maj_min_dev) < 0) {
+ if (set_options(newargc, newargv, maj_min_dev) < 0) {
retval = 2;
goto exit;
}
@@ -592,7 +575,7 @@ int main(int argc, char **argv) {
/*
* Get command line options (overriding any config file settings).
*/
- if (set_options(udev, argc, argv, maj_min_dev) < 0)
+ if (set_options(argc, argv, maj_min_dev) < 0)
exit(EXIT_FAILURE);
if (!dev_specified) {
@@ -601,7 +584,7 @@ int main(int argc, char **argv) {
goto exit;
}
- retval = scsi_id(udev, maj_min_dev);
+ retval = scsi_id(maj_min_dev);
exit:
if (newargv) {
diff --git a/src/udev/scsi_id/scsi_id.h b/src/udev/scsi_id/scsi_id.h
index 1222f250ec..70e804fb75 100644
--- a/src/udev/scsi_id/scsi_id.h
+++ b/src/udev/scsi_id/scsi_id.h
@@ -3,7 +3,6 @@
/*
* Copyright © IBM Corp. 2003
- *
*/
#define MAX_PATH_LEN 512
@@ -49,8 +48,8 @@ struct scsi_id_device {
char tgpt_group[8];
};
-int scsi_std_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname);
-int scsi_get_serial(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname,
+int scsi_std_inquiry(struct scsi_id_device *dev_scsi, const char *devname);
+int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname,
int page_code, int len);
/*
diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c
index fd91657a32..c67d047475 100644
--- a/src/udev/scsi_id/scsi_serial.c
+++ b/src/udev/scsi_id/scsi_serial.c
@@ -3,7 +3,6 @@
* Copyright © IBM Corp. 2003
*
* Author: Patrick Mansfield<patmans@us.ibm.com>
- *
*/
#include <errno.h>
@@ -22,13 +21,11 @@
#include <time.h>
#include <unistd.h>
-#include "libudev.h"
-
-#include "libudev-private.h"
#include "random-util.h"
#include "scsi.h"
#include "scsi_id.h"
#include "string-util.h"
+#include "util.h"
/*
* A priority based list of id, naa, and binary/ascii for the identifier
@@ -86,15 +83,12 @@ static const char hex_str[]="0123456789abcdef";
#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */
#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */
-static int do_scsi_page80_inquiry(struct udev *udev,
- struct scsi_id_device *dev_scsi, int fd,
+static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd,
char *serial, char *serial_short, int max_len);
-static int sg_err_category_new(struct udev *udev,
- int scsi_status, int msg_status, int
+static int sg_err_category_new(int scsi_status, int msg_status, int
host_status, int driver_status, const
- unsigned char *sense_buffer, int sb_len)
-{
+ unsigned char *sense_buffer, int sb_len) {
scsi_status &= 0x7e;
/*
@@ -141,35 +135,26 @@ static int sg_err_category_new(struct udev *udev,
return SG_ERR_CAT_OTHER;
}
-static int sg_err_category3(struct udev *udev, struct sg_io_hdr *hp)
-{
- return sg_err_category_new(udev,
- hp->status, hp->msg_status,
+static int sg_err_category3(struct sg_io_hdr *hp) {
+ return sg_err_category_new(hp->status, hp->msg_status,
hp->host_status, hp->driver_status,
hp->sbp, hp->sb_len_wr);
}
-static int sg_err_category4(struct udev *udev, struct sg_io_v4 *hp)
-{
- return sg_err_category_new(udev, hp->device_status, 0,
+static int sg_err_category4(struct sg_io_v4 *hp) {
+ return sg_err_category_new(hp->device_status, 0,
hp->transport_status, hp->driver_status,
(unsigned char *)(uintptr_t)hp->response,
hp->response_len);
}
-static int scsi_dump_sense(struct udev *udev,
- struct scsi_id_device *dev_scsi,
- unsigned char *sense_buffer, int sb_len)
-{
+static int scsi_dump_sense(struct scsi_id_device *dev_scsi,
+ unsigned char *sense_buffer, int sb_len) {
int s;
int code;
int sense_class;
int sense_key;
int asc, ascq;
-#ifdef DUMP_SENSE
- char out_buffer[256];
- int i, j;
-#endif
/*
* Figure out and print the sense key, asc and ascq.
@@ -241,23 +226,10 @@ static int scsi_dump_sense(struct udev *udev,
}
-#ifdef DUMP_SENSE
- for (i = 0, j = 0; (i < s) && (j < 254); i++) {
- out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4];
- out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f];
- out_buffer[j++] = ' ';
- }
- out_buffer[j] = '\0';
- log_debug("%s: sense dump:", dev_scsi->kernel);
- log_debug("%s: %s", dev_scsi->kernel, out_buffer);
-
-#endif
return -1;
}
-static int scsi_dump(struct udev *udev,
- struct scsi_id_device *dev_scsi, struct sg_io_hdr *io)
-{
+static int scsi_dump(struct scsi_id_device *dev_scsi, struct sg_io_hdr *io) {
if (!io->status && !io->host_status && !io->msg_status &&
!io->driver_status) {
/*
@@ -270,14 +242,12 @@ static int scsi_dump(struct udev *udev,
log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x",
dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status);
if (io->status == SCSI_CHECK_CONDITION)
- return scsi_dump_sense(udev, dev_scsi, io->sbp, io->sb_len_wr);
+ return scsi_dump_sense(dev_scsi, io->sbp, io->sb_len_wr);
else
return -1;
}
-static int scsi_dump_v4(struct udev *udev,
- struct scsi_id_device *dev_scsi, struct sg_io_v4 *io)
-{
+static int scsi_dump_v4(struct scsi_id_device *dev_scsi, struct sg_io_v4 *io) {
if (!io->device_status && !io->transport_status &&
!io->driver_status) {
/*
@@ -290,17 +260,15 @@ static int scsi_dump_v4(struct udev *udev,
log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x",
dev_scsi->kernel, io->driver_status, io->transport_status, io->device_status);
if (io->device_status == SCSI_CHECK_CONDITION)
- return scsi_dump_sense(udev, dev_scsi, (unsigned char *)(uintptr_t)io->response,
+ return scsi_dump_sense(dev_scsi, (unsigned char *)(uintptr_t)io->response,
io->response_len);
else
return -1;
}
-static int scsi_inquiry(struct udev *udev,
- struct scsi_id_device *dev_scsi, int fd,
+static int scsi_inquiry(struct scsi_id_device *dev_scsi, int fd,
unsigned char evpd, unsigned char page,
- unsigned char *buf, unsigned int buflen)
-{
+ unsigned char *buf, unsigned buflen) {
unsigned char inq_cmd[INQUIRY_CMDLEN] =
{ INQUIRY_CMD, evpd, page, 0, buflen, 0 };
unsigned char sense[SENSE_BUFF_LEN];
@@ -353,9 +321,9 @@ resend:
}
if (dev_scsi->use_sg == 4)
- retval = sg_err_category4(udev, io_buf);
+ retval = sg_err_category4(io_buf);
else
- retval = sg_err_category3(udev, io_buf);
+ retval = sg_err_category3(io_buf);
switch (retval) {
case SG_ERR_CAT_NOTSUPPORTED:
@@ -368,9 +336,9 @@ resend:
default:
if (dev_scsi->use_sg == 4)
- retval = scsi_dump_v4(udev, dev_scsi, io_buf);
+ retval = scsi_dump_v4(dev_scsi, io_buf);
else
- retval = scsi_dump(udev, dev_scsi, io_buf);
+ retval = scsi_dump(dev_scsi, io_buf);
}
if (!retval) {
@@ -390,14 +358,12 @@ error:
}
/* Get list of supported EVPD pages */
-static int do_scsi_page0_inquiry(struct udev *udev,
- struct scsi_id_device *dev_scsi, int fd,
- unsigned char *buffer, unsigned int len)
-{
+static int do_scsi_page0_inquiry(struct scsi_id_device *dev_scsi, int fd,
+ unsigned char *buffer, unsigned len) {
int retval;
memzero(buffer, len);
- retval = scsi_inquiry(udev, dev_scsi, fd, 1, 0x0, buffer, len);
+ retval = scsi_inquiry(dev_scsi, fd, 1, 0x0, buffer, len);
if (retval < 0)
return 1;
@@ -434,9 +400,7 @@ static int do_scsi_page0_inquiry(struct udev *udev,
* The caller checks that serial is long enough to include the vendor +
* model.
*/
-static int prepend_vendor_model(struct udev *udev,
- struct scsi_id_device *dev_scsi, char *serial)
-{
+static int prepend_vendor_model(struct scsi_id_device *dev_scsi, char *serial) {
int ind;
strncpy(serial, dev_scsi->vendor, VENDOR_LENGTH);
@@ -459,14 +423,12 @@ static int prepend_vendor_model(struct udev *udev,
* check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill
* serial number.
*/
-static int check_fill_0x83_id(struct udev *udev,
- struct scsi_id_device *dev_scsi,
+static int check_fill_0x83_id(struct scsi_id_device *dev_scsi,
unsigned char *page_83,
const struct scsi_id_search_values
*id_search, char *serial, char *serial_short,
int max_len, char *wwn,
- char *wwn_vendor_extension, char *tgpt_group)
-{
+ char *wwn_vendor_extension, char *tgpt_group) {
int i, j, s, len;
/*
@@ -519,9 +481,9 @@ static int check_fill_0x83_id(struct udev *udev,
}
if (id_search->id_type == SCSI_ID_TGTGROUP && tgpt_group != NULL) {
- unsigned int group;
+ unsigned group;
- group = ((unsigned int)page_83[6] << 8) | page_83[7];
+ group = ((unsigned)page_83[6] << 8) | page_83[7];
sprintf(tgpt_group,"%x", group);
return 1;
}
@@ -535,7 +497,7 @@ static int check_fill_0x83_id(struct udev *udev,
* included in the identifier.
*/
if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
- if (prepend_vendor_model(udev, dev_scsi, &serial[1]) < 0)
+ if (prepend_vendor_model(dev_scsi, &serial[1]) < 0)
return 1;
i = 4; /* offset to the start of the identifier */
@@ -570,12 +532,10 @@ static int check_fill_0x83_id(struct udev *udev,
}
/* Extract the raw binary from VPD 0x83 pre-SPC devices */
-static int check_fill_0x83_prespc3(struct udev *udev,
- struct scsi_id_device *dev_scsi,
+static int check_fill_0x83_prespc3(struct scsi_id_device *dev_scsi,
unsigned char *page_83,
const struct scsi_id_search_values
- *id_search, char *serial, char *serial_short, int max_len)
-{
+ *id_search, char *serial, char *serial_short, int max_len) {
int i, j;
serial[0] = hex_str[SCSI_ID_NAA];
@@ -592,21 +552,19 @@ static int check_fill_0x83_prespc3(struct udev *udev,
}
/* Get device identification VPD page */
-static int do_scsi_page83_inquiry(struct udev *udev,
- struct scsi_id_device *dev_scsi, int fd,
+static int do_scsi_page83_inquiry(struct scsi_id_device *dev_scsi, int fd,
char *serial, char *serial_short, int len,
char *unit_serial_number, char *wwn,
- char *wwn_vendor_extension, char *tgpt_group)
-{
+ char *wwn_vendor_extension, char *tgpt_group) {
int retval;
- unsigned int id_ind, j;
+ unsigned id_ind, j;
unsigned char page_83[SCSI_INQ_BUFF_LEN];
/* also pick up the page 80 serial number */
- do_scsi_page80_inquiry(udev, dev_scsi, fd, NULL, unit_serial_number, MAX_SERIAL_LEN);
+ do_scsi_page80_inquiry(dev_scsi, fd, NULL, unit_serial_number, MAX_SERIAL_LEN);
memzero(page_83, SCSI_INQ_BUFF_LEN);
- retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83,
+ retval = scsi_inquiry(dev_scsi, fd, 1, PAGE_83, page_83,
SCSI_INQ_BUFF_LEN);
if (retval < 0)
return 1;
@@ -646,8 +604,7 @@ static int do_scsi_page83_inquiry(struct udev *udev,
*/
if (page_83[6] != 0)
- return check_fill_0x83_prespc3(udev,
- dev_scsi, page_83, id_search_list,
+ return check_fill_0x83_prespc3(dev_scsi, page_83, id_search_list,
serial, serial_short, len);
/*
@@ -661,9 +618,8 @@ static int do_scsi_page83_inquiry(struct udev *udev,
* Examine each descriptor returned. There is normally only
* one or a small number of descriptors.
*/
- for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) {
- retval = check_fill_0x83_id(udev,
- dev_scsi, &page_83[j],
+ for (j = 4; j <= (unsigned)page_83[3] + 3; j += page_83[j + 3] + 4) {
+ retval = check_fill_0x83_id(dev_scsi, &page_83[j],
&id_search_list[id_ind],
serial, serial_short, len,
wwn, wwn_vendor_extension,
@@ -684,16 +640,14 @@ static int do_scsi_page83_inquiry(struct udev *udev,
* Return the hard coded error code value 2 if the page 83 reply is not
* conformant to the SCSI-2 format.
*/
-static int do_scsi_page83_prespc3_inquiry(struct udev *udev,
- struct scsi_id_device *dev_scsi, int fd,
- char *serial, char *serial_short, int len)
-{
+static int do_scsi_page83_prespc3_inquiry(struct scsi_id_device *dev_scsi, int fd,
+ char *serial, char *serial_short, int len) {
int retval;
int i, j;
unsigned char page_83[SCSI_INQ_BUFF_LEN];
memzero(page_83, SCSI_INQ_BUFF_LEN);
- retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN);
+ retval = scsi_inquiry(dev_scsi, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN);
if (retval < 0)
return 1;
@@ -746,10 +700,8 @@ static int do_scsi_page83_prespc3_inquiry(struct udev *udev,
}
/* Get unit serial number VPD page */
-static int do_scsi_page80_inquiry(struct udev *udev,
- struct scsi_id_device *dev_scsi, int fd,
- char *serial, char *serial_short, int max_len)
-{
+static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd,
+ char *serial, char *serial_short, int max_len) {
int retval;
int ser_ind;
int i;
@@ -757,7 +709,7 @@ static int do_scsi_page80_inquiry(struct udev *udev,
unsigned char buf[SCSI_INQ_BUFF_LEN];
memzero(buf, SCSI_INQ_BUFF_LEN);
- retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN);
+ retval = scsi_inquiry(dev_scsi, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN);
if (retval < 0)
return retval;
@@ -779,7 +731,7 @@ static int do_scsi_page80_inquiry(struct udev *udev,
len = buf[3];
if (serial != NULL) {
serial[0] = 'S';
- ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]);
+ ser_ind = prepend_vendor_model(dev_scsi, &serial[1]);
if (ser_ind < 0)
return 1;
ser_ind++; /* for the leading 'S' */
@@ -793,9 +745,7 @@ static int do_scsi_page80_inquiry(struct udev *udev,
return 0;
}
-int scsi_std_inquiry(struct udev *udev,
- struct scsi_id_device *dev_scsi, const char *devname)
-{
+int scsi_std_inquiry(struct scsi_id_device *dev_scsi, const char *devname) {
int fd;
unsigned char buf[SCSI_INQ_BUFF_LEN];
struct stat statbuf;
@@ -816,7 +766,7 @@ int scsi_std_inquiry(struct udev *udev,
minor(statbuf.st_rdev));
memzero(buf, SCSI_INQ_BUFF_LEN);
- err = scsi_inquiry(udev, dev_scsi, fd, 0, 0, buf, SCSI_INQ_BUFF_LEN);
+ err = scsi_inquiry(dev_scsi, fd, 0, 0, buf, SCSI_INQ_BUFF_LEN);
if (err < 0)
goto out;
@@ -834,10 +784,8 @@ out:
return err;
}
-int scsi_get_serial(struct udev *udev,
- struct scsi_id_device *dev_scsi, const char *devname,
- int page_code, int len)
-{
+int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname,
+ int page_code, int len) {
unsigned char page0[SCSI_INQ_BUFF_LEN];
int fd = -1;
int cnt;
@@ -860,7 +808,7 @@ int scsi_get_serial(struct udev *udev,
return 1;
if (page_code == PAGE_80) {
- if (do_scsi_page80_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) {
+ if (do_scsi_page80_inquiry(dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) {
retval = 1;
goto completed;
} else {
@@ -868,7 +816,7 @@ int scsi_get_serial(struct udev *udev,
goto completed;
}
} else if (page_code == PAGE_83) {
- if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) {
+ if (do_scsi_page83_inquiry(dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) {
retval = 1;
goto completed;
} else {
@@ -876,7 +824,7 @@ int scsi_get_serial(struct udev *udev,
goto completed;
}
} else if (page_code == PAGE_83_PRE_SPC3) {
- retval = do_scsi_page83_prespc3_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len);
+ retval = do_scsi_page83_prespc3_inquiry(dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len);
if (retval) {
/*
* Fallback to servicing a SPC-2/3 compliant page 83
@@ -884,7 +832,7 @@ int scsi_get_serial(struct udev *udev,
* conform to pre-SPC3 expectations.
*/
if (retval == 2) {
- if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) {
+ if (do_scsi_page83_inquiry(dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) {
retval = 1;
goto completed;
} else {
@@ -910,7 +858,7 @@ int scsi_get_serial(struct udev *udev,
* Get page 0, the page of the pages. By default, try from best to
* worst of supported pages: 0x83 then 0x80.
*/
- if (do_scsi_page0_inquiry(udev, dev_scsi, fd, page0, SCSI_INQ_BUFF_LEN)) {
+ if (do_scsi_page0_inquiry(dev_scsi, fd, page0, SCSI_INQ_BUFF_LEN)) {
/*
* Don't try anything else. Black list if a specific page
* should be used for this vendor+model, or maybe have an
@@ -922,7 +870,7 @@ int scsi_get_serial(struct udev *udev,
for (ind = 4; ind <= page0[3] + 3; ind++)
if (page0[ind] == PAGE_83)
- if (!do_scsi_page83_inquiry(udev, dev_scsi, fd,
+ if (!do_scsi_page83_inquiry(dev_scsi, fd,
dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) {
/*
* Success
@@ -933,7 +881,7 @@ int scsi_get_serial(struct udev *udev,
for (ind = 4; ind <= page0[3] + 3; ind++)
if (page0[ind] == PAGE_80)
- if (!do_scsi_page80_inquiry(udev, dev_scsi, fd,
+ if (!do_scsi_page80_inquiry(dev_scsi, fd,
dev_scsi->serial, dev_scsi->serial_short, len)) {
/*
* Success
diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c
index 477b7ef61f..df0f95461d 100644
--- a/src/udev/udev-builtin-blkid.c
+++ b/src/udev/udev-builtin-blkid.c
@@ -3,7 +3,6 @@
* probe disks for filesystems and partitions
*
* Copyright © 2011 Karel Zak <kzak@redhat.com>
- *
*/
#include <blkid.h>
@@ -19,14 +18,16 @@
#include "alloc-util.h"
#include "blkid-util.h"
+#include "device-util.h"
#include "efivars.h"
#include "fd-util.h"
#include "gpt.h"
#include "parse-util.h"
#include "string-util.h"
-#include "udev.h"
+#include "strxcpyx.h"
+#include "udev-builtin.h"
-static void print_property(struct udev_device *dev, bool test, const char *name, const char *value) {
+static void print_property(sd_device *dev, bool test, const char *name, const char *value) {
char s[256];
s[0] = '\0';
@@ -94,7 +95,7 @@ static void print_property(struct udev_device *dev, bool test, const char *name,
}
}
-static int find_gpt_root(struct udev_device *dev, blkid_probe pr, bool test) {
+static int find_gpt_root(sd_device *dev, blkid_probe pr, bool test) {
#if defined(GPT_ROOT_NATIVE) && ENABLE_EFI
@@ -182,6 +183,8 @@ static int probe_superblocks(blkid_probe pr) {
struct stat st;
int rc;
+ /* TODO: Return negative errno. */
+
if (fstat(blkid_probe_get_fd(pr), &st))
return -errno;
@@ -210,18 +213,13 @@ static int probe_superblocks(blkid_probe pr) {
return blkid_do_safeprobe(pr);
}
-static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool test) {
- const char *root_partition;
- int64_t offset = 0;
- bool noraid = false;
- _cleanup_close_ int fd = -1;
+static int builtin_blkid(sd_device *dev, int argc, char *argv[], bool test) {
+ const char *devnode, *root_partition = NULL, *data, *name;
_cleanup_(blkid_free_probep) blkid_probe pr = NULL;
- const char *data;
- const char *name;
- int nvals;
- int i;
- int err = 0;
- bool is_gpt = false;
+ bool noraid = false, is_gpt = false;
+ _cleanup_close_ int fd = -1;
+ int64_t offset = 0;
+ int nvals, i, r;
static const struct option options[] = {
{ "offset", required_argument, NULL, 'o' },
@@ -238,13 +236,11 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t
switch (option) {
case 'o':
- err = safe_atoi64(optarg, &offset);
- if (err < 0)
- goto out;
- if (offset < 0) {
- err = -ERANGE;
- goto out;
- }
+ r = safe_atoi64(optarg, &offset);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to parse '%s' as an integer: %m", optarg);
+ if (offset < 0)
+ return log_device_error_errno(dev, -ERANGE, "Invalid offset %"PRIi64": %m", offset);
break;
case 'R':
noraid = true;
@@ -252,9 +248,10 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t
}
}
+ errno = 0;
pr = blkid_new_probe();
if (!pr)
- return EXIT_FAILURE;
+ return log_device_debug_errno(dev, errno > 0 ? errno : ENOMEM, "Failed to create blkid prober: %m");
blkid_probe_set_superblocks_flags(pr,
BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
@@ -264,31 +261,35 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t
if (noraid)
blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID);
- fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC);
- if (fd < 0) {
- err = log_debug_errno(errno, "Failure opening block device %s: %m", udev_device_get_devnode(dev));
- goto out;
- }
+ r = sd_device_get_devname(dev, &devnode);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get device name: %m");
- err = blkid_probe_set_device(pr, fd, offset, 0);
- if (err < 0)
- goto out;
+ fd = open(devnode, O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return log_device_debug_errno(dev, errno, "Failed to open block device %s: %m", devnode);
+
+ errno = 0;
+ r = blkid_probe_set_device(pr, fd, offset, 0);
+ if (r < 0)
+ return log_device_debug_errno(dev, errno > 0 ? errno : ENOMEM, "Failed to set device to blkid prober: %m");
- log_debug("probe %s %sraid offset=%"PRIi64,
- udev_device_get_devnode(dev),
- noraid ? "no" : "", offset);
+ log_device_debug(dev, "Probe %s with %sraid and offset=%"PRIi64, devnode, noraid ? "no" : "", offset);
- err = probe_superblocks(pr);
- if (err < 0)
- goto out;
+ r = probe_superblocks(pr);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to probe superblocks: %m");
- /* If we are a partition then our parent passed on the root
- * partition UUID to us */
- root_partition = udev_device_get_property_value(dev, "ID_PART_GPT_AUTO_ROOT_UUID");
+ /* If the device is a partition then its parent passed the root partition UUID to the device */
+ (void) sd_device_get_property_value(dev, "ID_PART_GPT_AUTO_ROOT_UUID", &root_partition);
+ errno = 0;
nvals = blkid_probe_numof_values(pr);
+ if (nvals < 0)
+ return log_device_debug_errno(dev, errno > 0 ? errno : ENOMEM, "Failed to get number of probed values: %m");
+
for (i = 0; i < nvals; i++) {
- if (blkid_probe_get_value(pr, i, &name, &data, NULL))
+ if (blkid_probe_get_value(pr, i, &name, &data, NULL) < 0)
continue;
print_property(dev, test, name, data);
@@ -298,7 +299,7 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t
is_gpt = true;
/* Is this a partition that matches the root partition
- * property we inherited from our parent? */
+ * property inherited from the parent? */
if (root_partition && streq(name, "PART_ENTRY_UUID") && streq(data, root_partition))
udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT", "1");
}
@@ -306,11 +307,7 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t
if (is_gpt)
find_gpt_root(dev, pr, test);
-out:
- if (err < 0)
- return EXIT_FAILURE;
-
- return EXIT_SUCCESS;
+ return 0;
}
const struct udev_builtin udev_builtin_blkid = {
diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c
index 2e8535598d..85dba3d099 100644
--- a/src/udev/udev-builtin-btrfs.c
+++ b/src/udev/udev-builtin-btrfs.c
@@ -4,34 +4,33 @@
#include <stdlib.h>
#include <sys/ioctl.h>
-#if HAVE_LINUX_BTRFS_H
-#include <linux/btrfs.h>
-#endif
-
+#include "device-util.h"
#include "fd-util.h"
#include "missing.h"
#include "string-util.h"
-#include "udev.h"
+#include "strxcpyx.h"
+#include "udev-builtin.h"
+#include "util.h"
-static int builtin_btrfs(struct udev_device *dev, int argc, char *argv[], bool test) {
+static int builtin_btrfs(sd_device *dev, int argc, char *argv[], bool test) {
struct btrfs_ioctl_vol_args args = {};
_cleanup_close_ int fd = -1;
- int err;
+ int r;
if (argc != 3 || !streq(argv[1], "ready"))
- return EXIT_FAILURE;
+ return log_device_error_errno(dev, EINVAL, "Invalid arguments");
fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC);
if (fd < 0)
- return EXIT_FAILURE;
+ return log_device_debug_errno(dev, errno, "Failed to open /dev/btrfs-control: %m");
strscpy(args.name, sizeof(args.name), argv[2]);
- err = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
- if (err < 0)
- return EXIT_FAILURE;
+ r = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args);
+ if (r < 0)
+ return log_device_debug_errno(dev, errno, "Failed to call BTRFS_IOC_DEVICES_READY: %m");
- udev_builtin_add_property(dev, test, "ID_BTRFS_READY", one_zero(err == 0));
- return EXIT_SUCCESS;
+ udev_builtin_add_property(dev, test, "ID_BTRFS_READY", one_zero(r == 0));
+ return 0;
}
const struct udev_builtin udev_builtin_btrfs = {
diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c
index 396384f6c8..ccad98e892 100644
--- a/src/udev/udev-builtin-hwdb.c
+++ b/src/udev/udev-builtin-hwdb.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <errno.h>
#include <fnmatch.h>
#include <getopt.h>
#include <stdio.h>
@@ -8,20 +9,20 @@
#include "sd-hwdb.h"
#include "alloc-util.h"
+#include "device-util.h"
#include "hwdb-util.h"
#include "parse-util.h"
#include "string-util.h"
-#include "udev-util.h"
-#include "udev.h"
+#include "udev-builtin.h"
static sd_hwdb *hwdb;
-int udev_builtin_hwdb_lookup(struct udev_device *dev,
+int udev_builtin_hwdb_lookup(sd_device *dev,
const char *prefix, const char *modalias,
const char *filter, bool test) {
_cleanup_free_ char *lookup = NULL;
const char *key, *value;
- int n = 0;
+ int n = 0, r;
if (!hwdb)
return -ENOENT;
@@ -37,22 +38,21 @@ int udev_builtin_hwdb_lookup(struct udev_device *dev,
if (filter && fnmatch(filter, key, FNM_NOESCAPE) != 0)
continue;
- if (udev_builtin_add_property(dev, test, key, value) < 0)
- return -ENOMEM;
+ r = udev_builtin_add_property(dev, test, key, value);
+ if (r < 0)
+ return r;
n++;
}
return n;
}
-static const char *modalias_usb(struct udev_device *dev, char *s, size_t size) {
+static const char *modalias_usb(sd_device *dev, char *s, size_t size) {
const char *v, *p;
uint16_t vn, pn;
- v = udev_device_get_sysattr_value(dev, "idVendor");
- if (!v)
+ if (sd_device_get_sysattr_value(dev, "idVendor", &v) < 0)
return NULL;
- p = udev_device_get_sysattr_value(dev, "idProduct");
- if (!p)
+ if (sd_device_get_sysattr_value(dev, "idProduct", &p) < 0)
return NULL;
if (safe_atoux16(v, &vn) < 0)
return NULL;
@@ -62,10 +62,10 @@ static const char *modalias_usb(struct udev_device *dev, char *s, size_t size) {
return s;
}
-static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device *srcdev,
+static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev,
const char *subsystem, const char *prefix,
const char *filter, bool test) {
- struct udev_device *d;
+ sd_device *d;
char s[16];
bool last = false;
int r = 0;
@@ -75,21 +75,21 @@ static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device
if (!srcdev)
srcdev = dev;
- for (d = srcdev; d && !last; d = udev_device_get_parent(d)) {
- const char *dsubsys;
- const char *modalias = NULL;
+ for (d = srcdev; d; ) {
+ const char *dsubsys, *devtype, *modalias = NULL;
- dsubsys = udev_device_get_subsystem(d);
- if (!dsubsys)
- continue;
+ if (sd_device_get_subsystem(d, &dsubsys) < 0)
+ goto next;
/* look only at devices of a specific subsystem */
if (subsystem && !streq(dsubsys, subsystem))
- continue;
+ goto next;
- modalias = udev_device_get_property_value(d, "MODALIAS");
+ (void) sd_device_get_property_value(d, "MODALIAS", &modalias);
- if (streq(dsubsys, "usb") && streq_ptr(udev_device_get_devtype(d), "usb_device")) {
+ if (streq(dsubsys, "usb") &&
+ sd_device_get_devtype(d, &devtype) >= 0 &&
+ streq(devtype, "usb_device")) {
/* if the usb_device does not have a modalias, compose one */
if (!modalias)
modalias = modalias_usb(d, s, sizeof(s));
@@ -99,17 +99,23 @@ static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device
}
if (!modalias)
- continue;
+ goto next;
r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test);
if (r > 0)
break;
+
+ if (last)
+ break;
+next:
+ if (sd_device_get_parent(d, &d) < 0)
+ break;
}
return r;
}
-static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) {
+static int builtin_hwdb(sd_device *dev, int argc, char *argv[], bool test) {
static const struct option options[] = {
{ "filter", required_argument, NULL, 'f' },
{ "device", required_argument, NULL, 'd' },
@@ -121,10 +127,11 @@ static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool te
const char *device = NULL;
const char *subsystem = NULL;
const char *prefix = NULL;
- _cleanup_(udev_device_unrefp) struct udev_device *srcdev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *srcdev = NULL;
+ int r;
if (!hwdb)
- return EXIT_FAILURE;
+ return -EINVAL;
for (;;) {
int option;
@@ -154,25 +161,31 @@ static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool te
/* query a specific key given as argument */
if (argv[optind]) {
- if (udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test) > 0)
- return EXIT_SUCCESS;
- return EXIT_FAILURE;
+ 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");
+ if (r == 0)
+ return log_device_debug_errno(dev, ENOENT, "No entry found from hwdb: %m");
+ return r;
}
/* read data from another device than the device we will store the data */
if (device) {
- srcdev = udev_device_new_from_device_id(udev_device_get_udev(dev), device);
- if (!srcdev)
- return EXIT_FAILURE;
+ r = sd_device_new_from_device_id(&srcdev, device);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to create sd_device object '%s': %m", device);
}
- if (udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test) > 0)
- return EXIT_SUCCESS;
- return EXIT_FAILURE;
+ 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");
+ if (r == 0)
+ return log_device_debug_errno(dev, ENOENT, "No entry found from hwdb: %m");
+ return r;
}
/* called at udev startup and reload */
-static int builtin_hwdb_init(struct udev *udev) {
+static int builtin_hwdb_init(void) {
int r;
if (hwdb)
@@ -186,12 +199,12 @@ static int builtin_hwdb_init(struct udev *udev) {
}
/* called on udev shutdown and reload request */
-static void builtin_hwdb_exit(struct udev *udev) {
+static void builtin_hwdb_exit(void) {
hwdb = sd_hwdb_unref(hwdb);
}
/* called every couple of seconds during event activity; 'true' if config has changed */
-static bool builtin_hwdb_validate(struct udev *udev) {
+static bool builtin_hwdb_validate(void) {
return hwdb_validate(hwdb);
}
diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c
index 680170028b..13e9f0108d 100644
--- a/src/udev/udev-builtin-input_id.c
+++ b/src/udev/udev-builtin-input_id.c
@@ -2,10 +2,8 @@
/*
* expose input properties via udev
*
- * Copyright © 2009 Martin Pitt <martin.pitt@ubuntu.com>
* Portions Copyright © 2004 David Zeuthen, <david@fubar.dk>
* Copyright © 2014 Carlos Garnacho <carlosg@gnome.org>
- *
*/
#include <errno.h>
@@ -17,11 +15,12 @@
#include <linux/limits.h>
#include <linux/input.h>
+#include "device-util.h"
#include "fd-util.h"
#include "missing.h"
#include "stdio-util.h"
#include "string-util.h"
-#include "udev.h"
+#include "udev-builtin.h"
#include "util.h"
/* we must use this kernel-compatible implementation */
@@ -48,7 +47,7 @@ static inline int abs_size_mm(const struct input_absinfo *absinfo) {
return (absinfo->maximum - absinfo->minimum) / absinfo->resolution;
}
-static void extract_info(struct udev_device *dev, const char *devpath, bool test) {
+static void extract_info(sd_device *dev, const char *devpath, bool test) {
char width[DECIMAL_STR_MAX(int)], height[DECIMAL_STR_MAX(int)];
struct input_absinfo xabsinfo = {}, yabsinfo = {};
_cleanup_close_ int fd = -1;
@@ -73,12 +72,11 @@ static void extract_info(struct udev_device *dev, const char *devpath, bool test
/*
* Read a capability attribute and return bitmask.
- * @param dev udev_device
+ * @param dev sd_device
* @param attr sysfs attribute name (e. g. "capabilities/key")
* @param bitmask: Output array which has a sizeof of bitmask_size
*/
-static void get_cap_mask(struct udev_device *dev,
- struct udev_device *pdev, const char* attr,
+static void get_cap_mask(sd_device *pdev, const char* attr,
unsigned long *bitmask, size_t bitmask_size,
bool test) {
const char *v;
@@ -87,20 +85,20 @@ static void get_cap_mask(struct udev_device *dev,
char* word;
unsigned long val;
- v = udev_device_get_sysattr_value(pdev, attr);
- v = strempty(v);
+ if (sd_device_get_sysattr_value(pdev, attr, &v) < 0)
+ v = "";
xsprintf(text, "%s", v);
- log_debug("%s raw kernel attribute: %s", attr, text);
+ log_device_debug(pdev, "%s raw kernel attribute: %s", attr, text);
memzero(bitmask, bitmask_size);
i = 0;
while ((word = strrchr(text, ' ')) != NULL) {
- val = strtoul (word+1, NULL, 16);
- if (i < bitmask_size/sizeof(unsigned long))
+ val = strtoul(word+1, NULL, 16);
+ if (i < bitmask_size / sizeof(unsigned long))
bitmask[i] = val;
else
- log_debug("ignoring %s block %lX which is larger than maximum size", attr, val);
+ log_device_debug(pdev, "Ignoring %s block %lX which is larger than maximum size", attr, val);
*word = '\0';
++i;
}
@@ -108,27 +106,27 @@ static void get_cap_mask(struct udev_device *dev,
if (i < bitmask_size / sizeof(unsigned long))
bitmask[i] = val;
else
- log_debug("ignoring %s block %lX which is larger than maximum size", attr, val);
+ log_device_debug(pdev, "Ignoring %s block %lX which is larger than maximum size", attr, val);
if (test) {
/* printf pattern with the right unsigned long number of hex chars */
xsprintf(text, " bit %%4u: %%0%zulX\n",
2 * sizeof(unsigned long));
- log_debug("%s decoded bit map:", attr);
+ log_device_debug(pdev, "%s decoded bit map:", attr);
val = bitmask_size / sizeof (unsigned long);
/* skip over leading zeros */
while (bitmask[val-1] == 0 && val > 0)
--val;
for (i = 0; i < val; ++i) {
DISABLE_WARNING_FORMAT_NONLITERAL;
- log_debug(text, i * BITS_PER_LONG, bitmask[i]);
+ log_device_debug(pdev, text, i * BITS_PER_LONG, bitmask[i]);
REENABLE_WARNING;
}
}
}
/* pointer devices */
-static bool test_pointers(struct udev_device *dev,
+static bool test_pointers(sd_device *dev,
const unsigned long* bitmask_ev,
const unsigned long* bitmask_abs,
const unsigned long* bitmask_key,
@@ -250,7 +248,7 @@ static bool test_pointers(struct udev_device *dev,
}
/* key like devices */
-static bool test_key(struct udev_device *dev,
+static bool test_key(sd_device *dev,
const unsigned long* bitmask_ev,
const unsigned long* bitmask_key,
bool test) {
@@ -261,7 +259,7 @@ static bool test_key(struct udev_device *dev,
/* do we have any KEY_* capability? */
if (!test_bit(EV_KEY, bitmask_ev)) {
- log_debug("test_key: no EV_KEY capability");
+ log_device_debug(dev, "test_key: no EV_KEY capability");
return false;
}
@@ -269,7 +267,7 @@ static bool test_key(struct udev_device *dev,
found = 0;
for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) {
found |= bitmask_key[i];
- log_debug("test_key: checking bit block %lu for any keys; found=%i", (unsigned long)i*BITS_PER_LONG, found > 0);
+ log_device_debug(dev, "test_key: checking bit block %lu for any keys; found=%i", (unsigned long)i*BITS_PER_LONG, found > 0);
}
/* If there are no keys in the lower block, check the higher blocks */
if (!found) {
@@ -277,7 +275,7 @@ static bool test_key(struct udev_device *dev,
for (block = 0; block < (sizeof(high_key_blocks) / sizeof(struct range)); ++block) {
for (i = high_key_blocks[block].start; i < high_key_blocks[block].end; ++i) {
if (test_bit(i, bitmask_key)) {
- log_debug("test_key: Found key %x in high block", i);
+ log_device_debug(dev, "test_key: Found key %x in high block", i);
found = 1;
break;
}
@@ -301,8 +299,8 @@ static bool test_key(struct udev_device *dev,
return ret;
}
-static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], bool test) {
- struct udev_device *pdev;
+static int builtin_input_id(sd_device *dev, int argc, char *argv[], bool test) {
+ sd_device *pdev;
unsigned long bitmask_ev[NBITS(EV_MAX)];
unsigned long bitmask_abs[NBITS(ABS_MAX)];
unsigned long bitmask_key[NBITS(KEY_MAX)];
@@ -316,19 +314,28 @@ static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], boo
/* walk up the parental chain until we find the real input device; the
* argument is very likely a subdevice of this, like eventN */
- pdev = dev;
- while (pdev != NULL && udev_device_get_sysattr_value(pdev, "capabilities/ev") == NULL)
- pdev = udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL);
+ for (pdev = dev; pdev; ) {
+ const char *s;
+
+ if (sd_device_get_sysattr_value(pdev, "capabilities/ev", &s) >= 0)
+ break;
+
+ if (sd_device_get_parent_with_subsystem_devtype(pdev, "input", NULL, &pdev) >= 0)
+ continue;
+
+ pdev = NULL;
+ break;
+ }
if (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");
- get_cap_mask(dev, pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test);
- get_cap_mask(dev, pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test);
- get_cap_mask(dev, pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test);
- get_cap_mask(dev, pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test);
- get_cap_mask(dev, pdev, "properties", bitmask_props, sizeof(bitmask_props), test);
+ get_cap_mask(pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test);
+ get_cap_mask(pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), 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,
bitmask_key, bitmask_rel,
bitmask_props, test);
@@ -342,12 +349,12 @@ static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], boo
}
- devnode = udev_device_get_devnode(dev);
- sysname = udev_device_get_sysname(dev);
- if (devnode && sysname && startswith(sysname, "event"))
+ if (sd_device_get_devname(dev, &devnode) >= 0 &&
+ sd_device_get_sysname(dev, &sysname) >= 0 &&
+ startswith(sysname, "event"))
extract_info(dev, devnode, test);
- return EXIT_SUCCESS;
+ return 0;
}
const struct udev_builtin udev_builtin_input_id = {
diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c
index 9160a5b5d5..cb49a17c33 100644
--- a/src/udev/udev-builtin-keyboard.c
+++ b/src/udev/udev-builtin-keyboard.c
@@ -1,39 +1,43 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/input.h>
+#include "device-util.h"
#include "fd-util.h"
#include "parse-util.h"
#include "stdio-util.h"
#include "string-util.h"
-#include "udev.h"
+#include "strxcpyx.h"
+#include "udev-builtin.h"
static const struct key_name *keyboard_lookup_key(const char *str, GPERF_LEN_TYPE len);
#include "keyboard-keys-from-name.h"
-static int install_force_release(struct udev_device *dev, const unsigned *release, unsigned release_count) {
- struct udev_device *atkbd;
+static int install_force_release(sd_device *dev, const unsigned *release, unsigned release_count) {
+ sd_device *atkbd;
const char *cur;
char codes[4096];
char *s;
size_t l;
unsigned i;
- int ret;
+ int r;
assert(dev);
assert(release);
- atkbd = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL);
- if (!atkbd)
- return -ENODEV;
+ r = sd_device_get_parent_with_subsystem_devtype(dev, "serio", NULL, &atkbd);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get serio parent: %m");
- cur = udev_device_get_sysattr_value(atkbd, "force_release");
- if (!cur)
- return -ENODEV;
+ r = sd_device_get_sysattr_value(atkbd, "force_release", &cur);
+ if (r < 0)
+ return log_device_error_errno(atkbd, r, "Failed to get force-release attribute: %m");
s = codes;
l = sizeof(codes);
@@ -45,15 +49,15 @@ static int install_force_release(struct udev_device *dev, const unsigned *releas
for (i = 0; i < release_count; i++)
l = strpcpyf(&s, l, ",%u", release[i]);
- log_debug("keyboard: updating force-release list with '%s'", codes);
- ret = udev_device_set_sysattr_value(atkbd, "force_release", codes);
- if (ret < 0)
- log_error_errno(ret, "Error writing force-release attribute: %m");
- return ret;
+ log_device_debug(atkbd, "keyboard: updating force-release list with '%s'", codes);
+ r = sd_device_set_sysattr_value(atkbd, "force_release", codes);
+ if (r < 0)
+ return log_device_error_errno(atkbd, r, "Failed to set force-release attribute: %m");
+
+ return 0;
}
-static void map_keycode(int fd, const char *devnode, int scancode, const char *keycode)
-{
+static int map_keycode(sd_device *dev, int fd, int scancode, const char *keycode) {
struct {
unsigned scan;
unsigned key;
@@ -69,20 +73,20 @@ static void map_keycode(int fd, const char *devnode, int scancode, const char *k
} else {
/* check if it's a numeric code already */
keycode_num = strtoul(keycode, &endptr, 0);
- if (endptr[0] !='\0') {
- log_error("Unknown key identifier '%s'", keycode);
- return;
- }
+ if (endptr[0] !='\0')
+ return log_device_error_errno(dev, EINVAL, "Failed to parse key identifier '%s'", keycode);
}
map.scan = scancode;
map.key = keycode_num;
- log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)",
- map.scan, map.scan, map.key, map.key);
+ log_device_debug(dev, "keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)",
+ map.scan, map.scan, map.key, map.key);
if (ioctl(fd, EVIOCSKEYCODE, &map) < 0)
- log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", devnode, map.scan, map.key);
+ return log_device_error_errno(dev, errno, "Failed to call EVIOCSKEYCODE with scan code 0x%x, and key code %d: %m", map.scan, map.key);
+
+ return 0;
}
static inline char* parse_token(const char *current, int32_t *val_out) {
@@ -105,40 +109,34 @@ static inline char* parse_token(const char *current, int32_t *val_out) {
return next;
}
-static void override_abs(int fd, const char *devnode,
- unsigned evcode, const char *value) {
+static int override_abs(sd_device *dev, int fd, unsigned evcode, const char *value) {
struct input_absinfo absinfo;
- int rc;
char *next;
+ int r;
- rc = ioctl(fd, EVIOCGABS(evcode), &absinfo);
- if (rc < 0) {
- log_error_errno(errno, "Unable to EVIOCGABS device \"%s\"", devnode);
- return;
- }
+ r = ioctl(fd, EVIOCGABS(evcode), &absinfo);
+ if (r < 0)
+ return log_device_error_errno(dev, errno, "Failed to call EVIOCGABS");
next = parse_token(value, &absinfo.minimum);
next = parse_token(next, &absinfo.maximum);
next = parse_token(next, &absinfo.resolution);
next = parse_token(next, &absinfo.fuzz);
next = parse_token(next, &absinfo.flat);
- if (!next) {
- log_error("Unable to parse EV_ABS override '%s' for '%s'", value, devnode);
- return;
- }
+ if (!next)
+ return log_device_error(dev, "Failed to parse EV_ABS override '%s'", value);
+
+ log_device_debug(dev, "keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32,
+ evcode, absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat);
+ r = ioctl(fd, EVIOCSABS(evcode), &absinfo);
+ if (r < 0)
+ return log_device_error_errno(dev, errno, "Failed to call EVIOCSABS");
- log_debug("keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32" for \"%s\"",
- evcode,
- absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat,
- devnode);
- rc = ioctl(fd, EVIOCSABS(evcode), &absinfo);
- if (rc < 0)
- log_error_errno(errno, "Unable to EVIOCSABS device \"%s\"", devnode);
+ return 0;
}
-static void set_trackpoint_sensitivity(struct udev_device *dev, const char *value)
-{
- struct udev_device *pdev;
+static int set_trackpoint_sensitivity(sd_device *dev, const char *value) {
+ sd_device *pdev;
char val_s[DECIMAL_STR_MAX(int)];
int r, val_i;
@@ -146,103 +144,83 @@ static void set_trackpoint_sensitivity(struct udev_device *dev, const char *valu
assert(value);
/* The sensitivity sysfs attr belongs to the serio parent device */
- pdev = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL);
- if (!pdev) {
- log_warning("Failed to get serio parent for '%s'", udev_device_get_devnode(dev));
- return;
- }
+ r = sd_device_get_parent_with_subsystem_devtype(dev, "serio", NULL, &pdev);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get serio parent: %m");
r = safe_atoi(value, &val_i);
- if (r < 0) {
- log_error("Unable to parse POINTINGSTICK_SENSITIVITY '%s' for '%s'", value, udev_device_get_devnode(dev));
- return;
- } else if (val_i < 0 || val_i > 255) {
- log_error("POINTINGSTICK_SENSITIVITY %d outside range [0..255] for '%s' ", val_i, udev_device_get_devnode(dev));
- return;
- }
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to parse POINTINGSTICK_SENSITIVITY '%s': %m", value);
+ else if (val_i < 0 || val_i > 255)
+ return log_device_error_errno(dev, ERANGE, "POINTINGSTICK_SENSITIVITY %d outside range [0..255]", val_i);
xsprintf(val_s, "%d", val_i);
- r = udev_device_set_sysattr_value(pdev, "sensitivity", val_s);
+ r = sd_device_set_sysattr_value(pdev, "sensitivity", val_s);
if (r < 0)
- log_error_errno(r, "Failed to write 'sensitivity' attribute for '%s': %m", udev_device_get_devnode(pdev));
-}
+ return log_device_error_errno(dev, r, "Failed to write 'sensitivity' attribute: %m");
-static int open_device(const char *devnode) {
- int fd;
-
- fd = open(devnode, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
- if (fd < 0)
- return log_error_errno(errno, "Error opening device \"%s\": %m", devnode);
-
- return fd;
+ return 0;
}
-static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) {
- struct udev_list_entry *entry;
+static int builtin_keyboard(sd_device *dev, int argc, char *argv[], bool test) {
unsigned release[1024];
unsigned release_count = 0;
_cleanup_close_ int fd = -1;
- const char *node;
- int has_abs = -1;
+ const char *node, *key, *value;
+ int has_abs = -1, r;
- node = udev_device_get_devnode(dev);
- if (!node) {
- log_error("No device node for \"%s\"", udev_device_get_syspath(dev));
- return EXIT_FAILURE;
- }
+ r = sd_device_get_devname(dev, &node);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get device name: %m");
- udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) {
- const char *key;
+ FOREACH_DEVICE_PROPERTY(dev, key, value) {
char *endptr;
- key = udev_list_entry_get_name(entry);
if (startswith(key, "KEYBOARD_KEY_")) {
- const char *keycode;
+ const char *keycode = value;
unsigned scancode;
/* KEYBOARD_KEY_<hex scan code>=<key identifier string> */
scancode = strtoul(key + 13, &endptr, 16);
if (endptr[0] != '\0') {
- log_warning("Unable to parse scan code from \"%s\"", key);
+ log_device_warning(dev, "Failed to parse scan code from \"%s\", ignoring", key);
continue;
}
- keycode = udev_list_entry_get_value(entry);
-
/* a leading '!' needs a force-release entry */
if (keycode[0] == '!') {
keycode++;
release[release_count] = scancode;
- if (release_count < ELEMENTSOF(release)-1)
+ if (release_count < ELEMENTSOF(release)-1)
release_count++;
if (keycode[0] == '\0')
continue;
}
- if (fd == -1) {
- fd = open_device(node);
+ if (fd < 0) {
+ fd = open(node, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0)
- return EXIT_FAILURE;
+ return log_device_error_errno(dev, errno, "Failed to open device '%s': %m", node);
}
- map_keycode(fd, node, scancode, keycode);
+ (void) map_keycode(dev, fd, scancode, keycode);
} else if (startswith(key, "EVDEV_ABS_")) {
unsigned evcode;
/* EVDEV_ABS_<EV_ABS code>=<min>:<max>:<res>:<fuzz>:<flat> */
evcode = strtoul(key + 10, &endptr, 16);
if (endptr[0] != '\0') {
- log_warning("Unable to parse EV_ABS code from \"%s\"", key);
+ log_device_warning(dev, "Failed to parse EV_ABS code from \"%s\", ignoring", key);
continue;
}
- if (fd == -1) {
- fd = open_device(node);
+ if (fd < 0) {
+ fd = open(node, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0)
- return EXIT_FAILURE;
+ return log_device_error_errno(dev, errno, "Failed to open device '%s': %m", node);
}
if (has_abs == -1) {
@@ -250,29 +228,27 @@ static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], boo
int rc;
rc = ioctl(fd, EVIOCGBIT(0, sizeof(bits)), &bits);
- if (rc < 0) {
- log_error_errno(errno, "Unable to EVIOCGBIT device \"%s\"", node);
- return EXIT_FAILURE;
- }
+ if (rc < 0)
+ return log_device_error_errno(dev, errno, "Failed to set EVIOCGBIT");
has_abs = !!(bits & (1 << EV_ABS));
if (!has_abs)
- log_warning("EVDEV_ABS override set but no EV_ABS present on device \"%s\"", node);
+ log_device_warning(dev, "EVDEV_ABS override set but no EV_ABS present on device");
}
if (!has_abs)
continue;
- override_abs(fd, node, evcode, udev_list_entry_get_value(entry));
+ (void) override_abs(dev, fd, evcode, value);
} else if (streq(key, "POINTINGSTICK_SENSITIVITY"))
- set_trackpoint_sensitivity(dev, udev_list_entry_get_value(entry));
+ (void) set_trackpoint_sensitivity(dev, value);
}
/* install list of force-release codes */
if (release_count > 0)
- install_force_release(dev, release, release_count);
+ (void) install_force_release(dev, release, release_count);
- return EXIT_SUCCESS;
+ return 0;
}
const struct udev_builtin udev_builtin_keyboard = {
diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c
index e24e8e55e2..ce52149b1d 100644
--- a/src/udev/udev-builtin-kmod.c
+++ b/src/udev/udev-builtin-kmod.c
@@ -3,7 +3,6 @@
* load kernel modules
*
* Copyright © 2011 ProFUSION embedded systems
- *
*/
#include <errno.h>
@@ -14,65 +13,32 @@
#include "module-util.h"
#include "string-util.h"
-#include "udev.h"
+#include "udev-builtin.h"
static struct kmod_ctx *ctx = NULL;
-static int load_module(struct udev *udev, const char *alias) {
- _cleanup_(kmod_module_unref_listp) struct kmod_list *list = NULL;
- struct kmod_list *l;
- int err;
-
- err = kmod_module_new_from_lookup(ctx, alias, &list);
- if (err < 0)
- return err;
-
- if (list == NULL)
- log_debug("No module matches '%s'", alias);
-
- kmod_list_foreach(l, list) {
- _cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
-
- mod = kmod_module_get_module(l);
-
- err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL);
- if (err == KMOD_PROBE_APPLY_BLACKLIST)
- log_debug("Module '%s' is blacklisted", kmod_module_get_name(mod));
- else if (err == 0)
- log_debug("Inserted '%s'", kmod_module_get_name(mod));
- else
- log_debug("Failed to insert '%s'", kmod_module_get_name(mod));
- }
-
- return err;
-}
-
_printf_(6,0) static void udev_kmod_log(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) {
log_internalv(priority, 0, file, line, fn, format, args);
}
-static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) {
- struct udev *udev = udev_device_get_udev(dev);
+static int builtin_kmod(sd_device *dev, int argc, char *argv[], bool test) {
int i;
if (!ctx)
return 0;
- if (argc < 3 || !streq(argv[1], "load")) {
- log_error("expect: %s load <module>", argv[0]);
- return EXIT_FAILURE;
- }
+ if (argc < 3 || !streq(argv[1], "load"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: expected: load <module>", argv[0]);
- for (i = 2; argv[i]; i++) {
- log_debug("Execute '%s' '%s'", argv[1], argv[i]);
- load_module(udev, argv[i]);
- }
+ for (i = 2; argv[i]; i++)
+ (void) module_load_and_warn(ctx, argv[i], false);
- return EXIT_SUCCESS;
+ return 0;
}
/* called at udev startup and reload */
-static int builtin_kmod_init(struct udev *udev) {
+static int builtin_kmod_init(void) {
if (ctx)
return 0;
@@ -81,19 +47,19 @@ static int builtin_kmod_init(struct udev *udev) {
return -ENOMEM;
log_debug("Load module index");
- kmod_set_log_fn(ctx, udev_kmod_log, udev);
+ kmod_set_log_fn(ctx, udev_kmod_log, NULL);
kmod_load_resources(ctx);
return 0;
}
/* called on udev shutdown and reload request */
-static void builtin_kmod_exit(struct udev *udev) {
+static void builtin_kmod_exit(void) {
log_debug("Unload module index");
ctx = kmod_unref(ctx);
}
/* called every couple of seconds during event activity; 'true' if config has changed */
-static bool builtin_kmod_validate(struct udev *udev) {
+static bool builtin_kmod_validate(void) {
log_debug("Validate module index");
if (!ctx)
return false;
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 147e04ab8c..0292c4973c 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -11,6 +11,7 @@
*
* Two character prefixes based on the type of interface:
* en — Ethernet
+ * ib — InfiniBand
* sl — serial line IP (slip)
* wl — wlan
* ww — wwan
@@ -34,6 +35,9 @@
* All multi-function PCI devices will carry the [f<function>] number in the
* device name, including the function 0 device.
*
+ * SR-IOV virtual devices are named based on the name of the parent interface,
+ * with a suffix of "v<N>", where <N> is the virtual device number.
+ *
* When using PCI geography, The PCI domain is only prepended when it is not 0.
*
* For USB devices the full chain of port numbers of hubs is composed. If the
@@ -64,6 +68,12 @@
* ID_NET_NAME_MAC=wlx0024d7e31130
* ID_NET_NAME_PATH=wlp3s0
*
+ * PCI IB host adapter with 2 ports:
+ * /sys/devices/pci0000:00/0000:00:03.0/0000:15:00.0/net/ibp21s0f0
+ * ID_NET_NAME_PATH=ibp21s0f0
+ * /sys/devices/pci0000:00/0000:00:03.0/0000:15:00.1/net/ibp21s0f1
+ * ID_NET_NAME_PATH=ibp21s0f1
+ *
* USB built-in 3G modem:
* /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6
* ID_NET_NAME_MAC=wwx028037ec0200
@@ -91,18 +101,63 @@
#include <unistd.h>
#include <linux/pci_regs.h>
+#include "alloc-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "parse-util.h"
+#include "proc-cmdline.h"
#include "stdio-util.h"
#include "string-util.h"
-#include "udev.h"
-#include "udev-util.h"
+#include "strv.h"
+#include "strxcpyx.h"
+#include "udev-builtin.h"
#define ONBOARD_INDEX_MAX (16*1024-1)
+/* So here's the deal: net_id is supposed to be an excercise in providing stable names for network devices. However, we
+ * also want to keep updating the naming scheme used in future versions of net_id. These two goals of course are
+ * contradictory: on one hand we want things to not change and on the other hand we want them to improve. Our way out
+ * of this dilemma is to introduce the "naming scheme" concept: each time we improve the naming logic we define a new
+ * flag for it. Then, we keep a list of schemes, each identified by a name associated with the flags it implements. Via
+ * a kernel command line and environment variable we then allow the user to pick the scheme they want us to follow:
+ * installers could "freeze" the used scheme at the moment of installation this way.
+ *
+ * Developers: each time you tweak the naming logic here, define a new flag below, and condition the tweak with
+ * it. Each time we do a release we'll then add a new scheme entry and include all newly defined flags.
+ *
+ * Note that this is only half a solution to the problem though: not only udev/net_id gets updated all the time, the
+ * kernel gets too. And thus a kernel that previously didn't expose some sysfs attribute we look for might eventually
+ * do, and thus affect our naming scheme too. Thus, enforcing a naming scheme will make interfacing more stable across
+ * OS versions, but not fully stabilize them. */
+typedef enum NamingSchemeFlags {
+ /* First, the individual features */
+ NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a40008b8299529c978ed8e11de8f6*/
+ NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6eab35d1cb9fa73889892702c27be09 */
+ NAMING_INFINIBAND = 1 << 2, /* Use "ib" prefix for infiniband, see 938d30aa98df887797c9e05074a562ddacdcdf5e */
+ NAMING_ZERO_ACPI_INDEX = 1 << 3, /* Allow zero acpi_index field, see d81186ef4f6a888a70f20a1e73a812d6acb9e22f */
+
+ /* And now the masks that combine the features above */
+ NAMING_V238 = 0,
+ NAMING_V239 = NAMING_V238|NAMING_SR_IOV_V|NAMING_NPAR_ARI,
+ NAMING_V240 = NAMING_V239|NAMING_INFINIBAND|NAMING_ZERO_ACPI_INDEX,
+
+ _NAMING_SCHEME_FLAGS_INVALID = -1,
+} NamingSchemeFlags;
+
+typedef struct NamingScheme {
+ const char *name;
+ NamingSchemeFlags flags;
+} NamingScheme;
+
+static const NamingScheme naming_schemes[] = {
+ { "v238", NAMING_V238 },
+ { "v239", NAMING_V239 },
+ { "v240", NAMING_V240 },
+ /* … add more schemes here, as the logic to name devices is updated … */
+};
+
enum netname_type{
NET_UNDEF,
NET_PCI,
@@ -120,7 +175,7 @@ struct netnames {
uint8_t mac[6];
bool mac_valid;
- struct udev_device *pcidev;
+ sd_device *pcidev;
char pci_slot[IFNAMSIZ];
char pci_path[IFNAMSIZ];
char pci_onboard[IFNAMSIZ];
@@ -134,101 +189,172 @@ struct netnames {
};
struct virtfn_info {
- struct udev_device *physfn_pcidev;
+ sd_device *physfn_pcidev;
char suffix[IFNAMSIZ];
};
+static const NamingScheme* naming_scheme_from_name(const char *name) {
+ size_t i;
+
+ if (streq(name, "latest"))
+ return naming_schemes + ELEMENTSOF(naming_schemes) - 1;
+
+ for (i = 0; i < ELEMENTSOF(naming_schemes); i++)
+ if (streq(naming_schemes[i].name, name))
+ return naming_schemes + i;
+
+ return NULL;
+}
+
+static const NamingScheme* naming_scheme(void) {
+ static const NamingScheme *cache = NULL;
+ _cleanup_free_ char *buffer = NULL;
+ const char *e, *k;
+
+ if (cache)
+ return cache;
+
+ /* Acquire setting from the kernel command line */
+ (void) proc_cmdline_get_key("net.naming-scheme", 0, &buffer);
+
+ /* Also acquire it from an env var */
+ e = getenv("NET_NAMING_SCHEME");
+ if (e) {
+ if (*e == ':') {
+ /* If prefixed with ':' the kernel cmdline takes precedence */
+ k = buffer ?: e + 1;
+ } else
+ k = e; /* Otherwise the env var takes precedence */
+ } else
+ k = buffer;
+
+ if (k) {
+ cache = naming_scheme_from_name(k);
+ if (cache) {
+ log_info("Using interface naming scheme '%s'.", cache->name);
+ return cache;
+ }
+
+ log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k);
+ }
+
+ cache = naming_scheme_from_name(DEFAULT_NET_NAMING_SCHEME);
+ assert(cache);
+ log_info("Using default interface naming scheme '%s'.", cache->name);
+
+ return cache;
+}
+
+static bool naming_scheme_has(NamingSchemeFlags flags) {
+ return FLAGS_SET(naming_scheme()->flags, flags);
+}
+
/* skip intermediate virtio devices */
-static struct udev_device *skip_virtio(struct udev_device *dev) {
- struct udev_device *parent = dev;
+static sd_device *skip_virtio(sd_device *dev) {
+ sd_device *parent;
/* there can only ever be one virtio bus per parent device, so we can
- safely ignore any virtio buses. see
- <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
- while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent)))
- parent = udev_device_get_parent(parent);
+ * safely ignore any virtio buses. see
+ * http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html */
+ for (parent = dev; parent; ) {
+ const char *subsystem;
+
+ if (sd_device_get_subsystem(parent, &subsystem) < 0)
+ break;
+
+ if (!streq(subsystem, "virtio"))
+ break;
+
+ if (sd_device_get_parent(parent, &parent) < 0)
+ return NULL;
+ }
+
return parent;
}
-static int get_virtfn_info(struct udev_device *dev, struct netnames *names, struct virtfn_info *vf_info) {
- struct udev *udev;
- const char *physfn_link_file;
+static int get_virtfn_info(sd_device *dev, struct netnames *names, struct virtfn_info *ret) {
+ _cleanup_(sd_device_unrefp) sd_device *physfn_pcidev = NULL;
+ const char *physfn_link_file, *syspath;
_cleanup_free_ char *physfn_pci_syspath = NULL;
_cleanup_free_ char *virtfn_pci_syspath = NULL;
struct dirent *dent;
_cleanup_closedir_ DIR *dir = NULL;
- struct virtfn_info vf_info_local = {};
+ char suffix[IFNAMSIZ];
int r;
- udev = udev_device_get_udev(names->pcidev);
- if (!udev)
- return -ENOENT;
+ assert(dev);
+ assert(names);
+ assert(ret);
+
+ r = sd_device_get_syspath(names->pcidev, &syspath);
+ if (r < 0)
+ return r;
+
/* Check if this is a virtual function. */
- physfn_link_file = strjoina(udev_device_get_syspath(names->pcidev), "/physfn");
+ physfn_link_file = strjoina(syspath, "/physfn");
r = chase_symlinks(physfn_link_file, NULL, 0, &physfn_pci_syspath);
if (r < 0)
return r;
/* Get physical function's pci device. */
- vf_info_local.physfn_pcidev = udev_device_new_from_syspath(udev, physfn_pci_syspath);
- if (!vf_info_local.physfn_pcidev)
- return -ENOENT;
+ r = sd_device_new_from_syspath(&physfn_pcidev, physfn_pci_syspath);
+ if (r < 0)
+ return r;
/* Find the virtual function number by finding the right virtfn link. */
dir = opendir(physfn_pci_syspath);
- if (!dir) {
- r = -errno;
- goto out_unref;
- }
+ if (!dir)
+ return -errno;
+
FOREACH_DIRENT_ALL(dent, dir, break) {
_cleanup_free_ char *virtfn_link_file = NULL;
+
if (!startswith(dent->d_name, "virtfn"))
continue;
+
virtfn_link_file = strjoin(physfn_pci_syspath, "/", dent->d_name);
- if (!virtfn_link_file) {
- r = -ENOMEM;
- goto out_unref;
- }
+ if (!virtfn_link_file)
+ return -ENOMEM;
+
if (chase_symlinks(virtfn_link_file, NULL, 0, &virtfn_pci_syspath) < 0)
continue;
- if (streq(udev_device_get_syspath(names->pcidev), virtfn_pci_syspath)) {
- if (!snprintf_ok(vf_info_local.suffix, sizeof(vf_info_local.suffix), "v%s", &dent->d_name[6])) {
- r = -ENOENT;
- goto out_unref;
- }
+
+ if (streq(syspath, virtfn_pci_syspath)) {
+ if (!snprintf_ok(suffix, sizeof(suffix), "v%s", &dent->d_name[6]))
+ return -ENOENT;
+
break;
}
}
- if (isempty(vf_info_local.suffix)) {
- r = -ENOENT;
- goto out_unref;
- }
- *vf_info = vf_info_local;
- return 0;
+ if (isempty(suffix))
+ return -ENOENT;
+
+ ret->physfn_pcidev = TAKE_PTR(physfn_pcidev);
+ strncpy(ret->suffix, suffix, sizeof(ret->suffix));
-out_unref:
- udev_device_unref(vf_info_local.physfn_pcidev);
- return r;
+ return 0;
}
/* retrieve on-board index number and label from firmware */
-static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
- unsigned dev_port = 0;
+static int dev_pci_onboard(sd_device *dev, struct netnames *names) {
+ unsigned long idx, dev_port = 0;
+ const char *attr, *port_name = NULL;
size_t l;
char *s;
- const char *attr, *port_name;
- int idx;
+ int r;
/* ACPI _DSM — device specific method for naming a PCI or PCI Express device */
- attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index");
- /* SMBIOS type 41 — Onboard Devices Extended Information */
- if (!attr)
- attr = udev_device_get_sysattr_value(names->pcidev, "index");
- if (!attr)
- return -ENOENT;
+ if (sd_device_get_sysattr_value(names->pcidev, "acpi_index", &attr) < 0) {
+ /* SMBIOS type 41 — Onboard Devices Extended Information */
+ r = sd_device_get_sysattr_value(names->pcidev, "index", &attr);
+ if (r < 0)
+ return r;
+ }
- idx = strtoul(attr, NULL, 0);
- if (idx <= 0)
+ r = safe_atolu(attr, &idx);
+ if (r < 0)
+ return r;
+ if (idx == 0 && !naming_scheme_has(NAMING_ZERO_ACPI_INDEX))
return -EINVAL;
/* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for
@@ -239,35 +365,38 @@ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
return -ENOENT;
/* kernel provided port index for multiple ports on a single PCI function */
- attr = udev_device_get_sysattr_value(dev, "dev_port");
- if (attr)
- dev_port = strtol(attr, NULL, 10);
+ if (sd_device_get_sysattr_value(dev, "dev_port", &attr) >= 0)
+ dev_port = strtoul(attr, NULL, 10);
/* kernel provided front panel port name for multiple port PCI device */
- port_name = udev_device_get_sysattr_value(dev, "phys_port_name");
+ (void) sd_device_get_sysattr_value(dev, "phys_port_name", &port_name);
s = names->pci_onboard;
l = sizeof(names->pci_onboard);
- l = strpcpyf(&s, l, "o%d", idx);
+ l = strpcpyf(&s, l, "o%lu", idx);
if (port_name)
l = strpcpyf(&s, l, "n%s", port_name);
else if (dev_port > 0)
- l = strpcpyf(&s, l, "d%d", dev_port);
+ l = strpcpyf(&s, l, "d%lu", dev_port);
if (l == 0)
names->pci_onboard[0] = '\0';
- names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label");
+ if (sd_device_get_sysattr_value(names->pcidev, "label", &names->pci_onboard_label) < 0)
+ names->pci_onboard_label = NULL;
return 0;
}
/* read the 256 bytes PCI configuration space to check the multi-function bit */
-static bool is_pci_multifunction(struct udev_device *dev) {
+static bool is_pci_multifunction(sd_device *dev) {
_cleanup_close_ int fd = -1;
- const char *filename;
+ const char *filename, *syspath;
uint8_t config[64];
- filename = strjoina(udev_device_get_syspath(dev), "/config");
+ if (sd_device_get_syspath(dev, &syspath) < 0)
+ return false;
+
+ filename = strjoina(syspath, "/config");
fd = open(filename, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return false;
@@ -275,43 +404,64 @@ static bool is_pci_multifunction(struct udev_device *dev) {
return false;
/* bit 0-6 header type, bit 7 multi/single function device */
- if ((config[PCI_HEADER_TYPE] & 0x80) != 0)
- return true;
-
- return false;
+ return config[PCI_HEADER_TYPE] & 0x80;
}
-static bool is_pci_ari_enabled(struct udev_device *dev) {
- return streq_ptr(udev_device_get_sysattr_value(dev, "ari_enabled"), "1");
+static bool is_pci_ari_enabled(sd_device *dev) {
+ const char *a;
+
+ if (sd_device_get_sysattr_value(dev, "ari_enabled", &a) < 0)
+ return false;
+
+ return streq(a, "1");
}
-static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
- struct udev *udev = udev_device_get_udev(names->pcidev);
- unsigned domain, bus, slot, func, dev_port = 0, hotplug_slot = 0;
+static int dev_pci_slot(sd_device *dev, struct netnames *names) {
+ unsigned long dev_port = 0;
+ unsigned domain, bus, slot, func, hotplug_slot = 0;
size_t l;
char *s;
- const char *attr, *port_name;
- _cleanup_(udev_device_unrefp) struct udev_device *pci = NULL;
- struct udev_device *hotplug_slot_dev;
+ const char *sysname, *attr, *port_name = NULL, *syspath;
+ _cleanup_(sd_device_unrefp) sd_device *pci = NULL;
+ sd_device *hotplug_slot_dev;
char slots[PATH_MAX];
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *dent;
+ int r;
- if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
+ r = sd_device_get_sysname(names->pcidev, &sysname);
+ if (r < 0)
+ return r;
+
+ if (sscanf(sysname, "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
return -ENOENT;
- if (is_pci_ari_enabled(names->pcidev))
+
+ if (naming_scheme_has(NAMING_NPAR_ARI) &&
+ is_pci_ari_enabled(names->pcidev))
/* ARI devices support up to 256 functions on a single device ("slot"), and interpret the
* traditional 5-bit slot and 3-bit function number as a single 8-bit function number,
* where the slot makes up the upper 5 bits. */
func += slot * 8;
/* kernel provided port index for multiple ports on a single PCI function */
- attr = udev_device_get_sysattr_value(dev, "dev_port");
- if (attr)
- dev_port = strtol(attr, NULL, 10);
+ if (sd_device_get_sysattr_value(dev, "dev_port", &attr) >= 0) {
+ dev_port = strtoul(attr, NULL, 10);
+ /* With older kernels IP-over-InfiniBand network interfaces sometimes erroneously
+ * provide the port number in the 'dev_id' sysfs attribute instead of 'dev_port',
+ * which thus stays initialized as 0. */
+ if (dev_port == 0 &&
+ sd_device_get_sysattr_value(dev, "type", &attr) >= 0) {
+ unsigned long type;
+
+ type = strtoul(attr, NULL, 10);
+ if (type == ARPHRD_INFINIBAND &&
+ sd_device_get_sysattr_value(dev, "dev_id", &attr) >= 0)
+ dev_port = strtoul(attr, NULL, 16);
+ }
+ }
/* kernel provided front panel port name for multiple port PCI device */
- port_name = udev_device_get_sysattr_value(dev, "phys_port_name");
+ (void) sd_device_get_sysattr_value(dev, "phys_port_name", &port_name);
/* compose a name based on the raw kernel's PCI bus, slot numbers */
s = names->pci_path;
@@ -324,16 +474,19 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (port_name)
l = strpcpyf(&s, l, "n%s", port_name);
else if (dev_port > 0)
- l = strpcpyf(&s, l, "d%u", dev_port);
+ l = strpcpyf(&s, l, "d%lu", dev_port);
if (l == 0)
names->pci_path[0] = '\0';
/* ACPI _SUN — slot user number */
- pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci");
- if (!pci)
- return -ENOENT;
+ r = sd_device_new_from_subsystem_sysname(&pci, "subsystem", "pci");
+ if (r < 0)
+ return r;
- if (!snprintf_ok(slots, sizeof slots, "%s/slots", udev_device_get_syspath(pci)))
+ r = sd_device_get_syspath(pci, &syspath);
+ if (r < 0)
+ return r;
+ if (!snprintf_ok(slots, sizeof slots, "%s/slots", syspath))
return -ENAMETOOLONG;
dir = opendir(slots);
@@ -342,31 +495,33 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
hotplug_slot_dev = names->pcidev;
while (hotplug_slot_dev) {
+ if (sd_device_get_sysname(hotplug_slot_dev, &sysname) < 0)
+ continue;
+
FOREACH_DIRENT_ALL(dent, dir, break) {
unsigned i;
- int r;
char str[PATH_MAX];
_cleanup_free_ char *address = NULL;
if (dent->d_name[0] == '.')
continue;
r = safe_atou_full(dent->d_name, 10, &i);
- if (i < 1 || r < 0)
+ if (r < 0 || i <= 0)
continue;
+ /* match slot address with device by stripping the function */
if (snprintf_ok(str, sizeof str, "%s/%s/address", slots, dent->d_name) &&
- read_one_line_file(str, &address) >= 0)
- /* match slot address with device by stripping the function */
- if (startswith(udev_device_get_sysname(hotplug_slot_dev), address))
- hotplug_slot = i;
-
- if (hotplug_slot > 0)
+ read_one_line_file(str, &address) >= 0 &&
+ startswith(sysname, address)) {
+ hotplug_slot = i;
break;
+ }
}
if (hotplug_slot > 0)
break;
+ if (sd_device_get_parent_with_subsystem_devtype(hotplug_slot_dev, "pci", NULL, &hotplug_slot_dev) < 0)
+ break;
rewinddir(dir);
- hotplug_slot_dev = udev_device_get_parent_with_subsystem_devtype(hotplug_slot_dev, "pci", NULL);
}
if (hotplug_slot > 0) {
@@ -380,7 +535,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (port_name)
l = strpcpyf(&s, l, "n%s", port_name);
else if (dev_port > 0)
- l = strpcpyf(&s, l, "d%d", dev_port);
+ l = strpcpyf(&s, l, "d%lu", dev_port);
if (l == 0)
names->pci_slot[0] = '\0';
}
@@ -388,24 +543,30 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
return 0;
}
-static int names_vio(struct udev_device *dev, struct netnames *names) {
- struct udev_device *parent;
+static int names_vio(sd_device *dev, struct netnames *names) {
+ sd_device *parent;
unsigned busid, slotid, ethid;
- const char *syspath;
+ const char *syspath, *subsystem;
+ int r;
/* check if our direct parent is a VIO device with no other bus in-between */
- parent = udev_device_get_parent(dev);
- if (!parent)
- return -ENOENT;
+ r = sd_device_get_parent(dev, &parent);
+ if (r < 0)
+ return r;
- if (!streq_ptr("vio", udev_device_get_subsystem(parent)))
- return -ENOENT;
+ r = sd_device_get_subsystem(parent, &subsystem);
+ if (r < 0)
+ return r;
+ if (!streq("vio", subsystem))
+ return -ENOENT;
/* The devices' $DEVPATH number is tied to (virtual) hardware (slot id
* selected in the HMC), thus this provides a reliable naming (e.g.
* "/devices/vio/30000002/net/eth1"); we ignore the bus number, as
* there should only ever be one bus, and then remove leading zeros. */
- syspath = udev_device_get_syspath(dev);
+ r = sd_device_get_syspath(dev, &syspath);
+ if (r < 0)
+ return r;
if (sscanf(syspath, "/sys/devices/vio/%4x%4x/net/eth%u", &busid, &slotid, &ethid) != 3)
return -EINVAL;
@@ -419,21 +580,28 @@ static int names_vio(struct udev_device *dev, struct netnames *names) {
#define _PLATFORM_PATTERN4 "/sys/devices/platform/%4s%4x:%2x/net/eth%u"
#define _PLATFORM_PATTERN3 "/sys/devices/platform/%3s%4x:%2x/net/eth%u"
-static int names_platform(struct udev_device *dev, struct netnames *names, bool test) {
- struct udev_device *parent;
+static int names_platform(sd_device *dev, struct netnames *names, bool test) {
+ sd_device *parent;
char vendor[5];
unsigned model, instance, ethid;
- const char *syspath, *pattern, *validchars;
+ const char *syspath, *pattern, *validchars, *subsystem;
+ int r;
/* check if our direct parent is a platform device with no other bus in-between */
- parent = udev_device_get_parent(dev);
- if (!parent)
- return -ENOENT;
+ r = sd_device_get_parent(dev, &parent);
+ if (r < 0)
+ return r;
- if (!streq_ptr("platform", udev_device_get_subsystem(parent)))
+ r = sd_device_get_subsystem(parent, &subsystem);
+ if (r < 0)
+ return r;
+
+ if (!streq("platform", subsystem))
return -ENOENT;
- syspath = udev_device_get_syspath(dev);
+ r = sd_device_get_syspath(dev, &syspath);
+ if (r < 0)
+ return r;
/* syspath is too short, to have a valid ACPI instance */
if (strlen(syspath) < sizeof _PLATFORM_TEST)
@@ -469,15 +637,19 @@ static int names_platform(struct udev_device *dev, struct netnames *names, bool
return 0;
}
-static int names_pci(struct udev_device *dev, struct netnames *names) {
- struct udev_device *parent;
+static int names_pci(sd_device *dev, struct netnames *names) {
+ sd_device *parent;
struct netnames vf_names = {};
struct virtfn_info vf_info = {};
+ const char *subsystem;
+ int r;
assert(dev);
assert(names);
- parent = udev_device_get_parent(dev);
+ r = sd_device_get_parent(dev, &parent);
+ if (r < 0)
+ return r;
/* skip virtio subsystem if present */
parent = skip_virtio(parent);
@@ -485,16 +657,18 @@ static int names_pci(struct udev_device *dev, struct netnames *names) {
return -ENOENT;
/* check if our direct parent is a PCI device with no other bus in-between */
- if (streq_ptr("pci", udev_device_get_subsystem(parent))) {
+ if (sd_device_get_subsystem(parent, &subsystem) >= 0 &&
+ streq("pci", subsystem)) {
names->type = NET_PCI;
names->pcidev = parent;
} else {
- names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL);
- if (!names->pcidev)
- return -ENOENT;
+ r = sd_device_get_parent_with_subsystem_devtype(dev, "pci", NULL, &names->pcidev);
+ if (r < 0)
+ return r;
}
- if (get_virtfn_info(dev, names, &vf_info) >= 0) {
+ if (naming_scheme_has(NAMING_SR_IOV_V) &&
+ get_virtfn_info(dev, names, &vf_info) >= 0) {
/* If this is an SR-IOV virtual device, get base name using physical device and add virtfn suffix. */
vf_names.pcidev = vf_info.physfn_pcidev;
dev_pci_onboard(dev, &vf_names);
@@ -511,32 +685,35 @@ static int names_pci(struct udev_device *dev, struct netnames *names) {
if (strlen(vf_names.pci_path) + strlen(vf_info.suffix) < sizeof(names->pci_path))
strscpyl(names->pci_path, sizeof(names->pci_path),
vf_names.pci_path, vf_info.suffix, NULL);
- udev_device_unref(vf_info.physfn_pcidev);
+ sd_device_unref(vf_info.physfn_pcidev);
} else {
dev_pci_onboard(dev, names);
dev_pci_slot(dev, names);
}
+
return 0;
}
-static int names_usb(struct udev_device *dev, struct netnames *names) {
- struct udev_device *usbdev;
- char name[256];
- char *ports;
- char *config;
- char *interf;
+static int names_usb(sd_device *dev, struct netnames *names) {
+ sd_device *usbdev;
+ char name[256], *ports, *config, *interf, *s;
+ const char *sysname;
size_t l;
- char *s;
+ int r;
assert(dev);
assert(names);
- usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
- if (!usbdev)
- return -ENOENT;
+ r = sd_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface", &usbdev);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_sysname(usbdev, &sysname);
+ if (r < 0)
+ return r;
/* get USB port number chain, configuration, interface */
- strscpy(name, sizeof(name), udev_device_get_sysname(usbdev));
+ strscpy(name, sizeof(name), sysname);
s = strchr(name, '-');
if (!s)
return -EINVAL;
@@ -575,19 +752,25 @@ static int names_usb(struct udev_device *dev, struct netnames *names) {
return 0;
}
-static int names_bcma(struct udev_device *dev, struct netnames *names) {
- struct udev_device *bcmadev;
- unsigned int core;
+static int names_bcma(sd_device *dev, struct netnames *names) {
+ sd_device *bcmadev;
+ unsigned core;
+ const char *sysname;
+ int r;
assert(dev);
assert(names);
- bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL);
- if (!bcmadev)
- return -ENOENT;
+ r = sd_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL, &bcmadev);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_sysname(bcmadev, &sysname);
+ if (r < 0)
+ return r;
/* bus num:core num */
- if (sscanf(udev_device_get_sysname(bcmadev), "bcma%*u:%u", &core) != 1)
+ if (sscanf(sysname, "bcma%*u:%u", &core) != 1)
return -EINVAL;
/* suppress the common core == 0 */
if (core > 0)
@@ -597,34 +780,41 @@ static int names_bcma(struct udev_device *dev, struct netnames *names) {
return 0;
}
-static int names_ccw(struct udev_device *dev, struct netnames *names) {
- struct udev_device *cdev;
+static int names_ccw(sd_device *dev, struct netnames *names) {
+ sd_device *cdev;
const char *bus_id, *subsys;
size_t bus_id_len;
size_t bus_id_start;
+ int r;
assert(dev);
assert(names);
/* Retrieve the associated CCW device */
- cdev = udev_device_get_parent(dev);
+ r = sd_device_get_parent(dev, &cdev);
+ if (r < 0)
+ return r;
+
/* skip virtio subsystem if present */
cdev = skip_virtio(cdev);
if (!cdev)
return -ENOENT;
+ r = sd_device_get_subsystem(cdev, &subsys);
+ if (r < 0)
+ return r;
+
/* Network devices are either single or grouped CCW devices */
- subsys = udev_device_get_subsystem(cdev);
- if (!STRPTR_IN_SET(subsys, "ccwgroup", "ccw"))
+ if (!STR_IN_SET(subsys, "ccwgroup", "ccw"))
return -ENOENT;
/* Retrieve bus-ID of the CCW device. The bus-ID uniquely
* identifies the network device on the Linux on System z channel
* subsystem. Note that the bus-ID contains lowercase characters.
*/
- bus_id = udev_device_get_sysname(cdev);
- if (!bus_id)
- return -ENOENT;
+ r = sd_device_get_sysname(cdev, &bus_id);
+ if (r < 0)
+ return r;
/* Check the length of the bus-ID. Rely on that the kernel provides
* a correct bus-ID; alternatively, improve this check and parse and
@@ -649,22 +839,41 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) {
return 0;
}
-static int names_mac(struct udev_device *dev, struct netnames *names) {
+static int names_mac(sd_device *dev, struct netnames *names) {
const char *s;
- unsigned int i;
- unsigned int a1, a2, a3, a4, a5, a6;
+ unsigned long i;
+ unsigned a1, a2, a3, a4, a5, a6;
+ int r;
+
+ /* Some kinds of devices tend to have hardware addresses
+ * that are impossible to use in an iface name.
+ */
+ r = sd_device_get_sysattr_value(dev, "type", &s);
+ if (r < 0)
+ return r;
+
+ i = strtoul(s, NULL, 0);
+ switch (i) {
+ /* The persistent part of a hardware address of an InfiniBand NIC
+ * is 8 bytes long. We cannot fit this much in an iface name.
+ */
+ case ARPHRD_INFINIBAND:
+ return -EINVAL;
+ default:
+ break;
+ }
/* check for NET_ADDR_PERM, skip random MAC addresses */
- s = udev_device_get_sysattr_value(dev, "addr_assign_type");
- if (!s)
- return EXIT_FAILURE;
+ r = sd_device_get_sysattr_value(dev, "addr_assign_type", &s);
+ if (r < 0)
+ return r;
i = strtoul(s, NULL, 0);
if (i != 0)
return 0;
- s = udev_device_get_sysattr_value(dev, "address");
- if (!s)
- return -ENOENT;
+ r = sd_device_get_sysattr_value(dev, "address", &s);
+ if (r < 0)
+ return r;
if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6)
return -EINVAL;
@@ -683,7 +892,7 @@ static int names_mac(struct udev_device *dev, struct netnames *names) {
}
/* IEEE Organizationally Unique Identifier vendor string */
-static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) {
+static int ieee_oui(sd_device *dev, struct netnames *names, bool test) {
char str[32];
if (!names->mac_valid)
@@ -698,24 +907,28 @@ static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test)
return 0;
}
-static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) {
- const char *s;
- const char *p;
- unsigned int i;
- const char *devtype;
- const char *prefix = "en";
+static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) {
+ const char *s, *p, *devtype, *prefix = "en";
struct netnames names = {};
- int err;
+ unsigned long i;
+ int r;
+
+ /* handle only ARPHRD_ETHER, ARPHRD_SLIP and ARPHRD_INFINIBAND devices */
+ r = sd_device_get_sysattr_value(dev, "type", &s);
+ if (r < 0)
+ return r;
- /* handle only ARPHRD_ETHER and ARPHRD_SLIP devices */
- s = udev_device_get_sysattr_value(dev, "type");
- if (!s)
- return EXIT_FAILURE;
i = strtoul(s, NULL, 0);
switch (i) {
case ARPHRD_ETHER:
prefix = "en";
break;
+ case ARPHRD_INFINIBAND:
+ if (naming_scheme_has(NAMING_INFINIBAND))
+ prefix = "ib";
+ else
+ return 0;
+ break;
case ARPHRD_SLIP:
prefix = "sl";
break;
@@ -724,25 +937,26 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
}
/* skip stacked devices, like VLANs, ... */
- s = udev_device_get_sysattr_value(dev, "ifindex");
- if (!s)
- return EXIT_FAILURE;
- p = udev_device_get_sysattr_value(dev, "iflink");
- if (!p)
- return EXIT_FAILURE;
+ r = sd_device_get_sysattr_value(dev, "ifindex", &s);
+ if (r < 0)
+ return r;
+ r = sd_device_get_sysattr_value(dev, "iflink", &p);
+ if (r < 0)
+ return r;
if (!streq(s, p))
return 0;
- devtype = udev_device_get_devtype(dev);
- if (devtype) {
+ if (sd_device_get_devtype(dev, &devtype) >= 0) {
if (streq("wlan", devtype))
prefix = "wl";
else if (streq("wwan", devtype))
prefix = "ww";
}
- err = names_mac(dev, &names);
- if (err >= 0 && names.mac_valid) {
+ udev_builtin_add_property(dev, test, "ID_NET_NAMING_SCHEME", naming_scheme()->name);
+
+ r = names_mac(dev, &names);
+ if (r >= 0 && names.mac_valid) {
char str[IFNAMSIZ];
xsprintf(str, "%sx%02x%02x%02x%02x%02x%02x", prefix,
@@ -754,39 +968,35 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
}
/* get path names for Linux on System z network devices */
- err = names_ccw(dev, &names);
- if (err >= 0 && names.type == NET_CCW) {
+ if (names_ccw(dev, &names) >= 0 && names.type == NET_CCW) {
char str[IFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.ccw_busid))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
- goto out;
+ return 0;
}
/* get ibmveth/ibmvnic slot-based names. */
- err = names_vio(dev, &names);
- if (err >= 0 && names.type == NET_VIO) {
+ if (names_vio(dev, &names) >= 0 && names.type == NET_VIO) {
char str[IFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.vio_slot))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
- goto out;
+ return 0;
}
/* get ACPI path names for ARM64 platform devices */
- err = names_platform(dev, &names, test);
- if (err >= 0 && names.type == NET_PLATFORM) {
+ if (names_platform(dev, &names, test) >= 0 && names.type == NET_PLATFORM) {
char str[IFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.platform_path))
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
- goto out;
+ return 0;
}
/* get PCI based path names, we compose only PCI based paths */
- err = names_pci(dev, &names);
- if (err < 0)
- goto out;
+ if (names_pci(dev, &names) < 0)
+ return 0;
/* plain PCI device */
if (names.type == NET_PCI) {
@@ -807,12 +1017,11 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
if (names.pci_slot[0] &&
snprintf_ok(str, sizeof str, "%s%s", prefix, names.pci_slot))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
- goto out;
+ return 0;
}
/* USB device */
- err = names_usb(dev, &names);
- if (err >= 0 && names.type == NET_USB) {
+ if (names_usb(dev, &names) >= 0 && names.type == NET_USB) {
char str[IFNAMSIZ];
if (names.pci_path[0] &&
@@ -822,12 +1031,11 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
if (names.pci_slot[0] &&
snprintf_ok(str, sizeof str, "%s%s%s", prefix, names.pci_slot, names.usb_ports))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
- goto out;
+ return 0;
}
/* Broadcom bus */
- err = names_bcma(dev, &names);
- if (err >= 0 && names.type == NET_BCMA) {
+ if (names_bcma(dev, &names) >= 0 && names.type == NET_BCMA) {
char str[IFNAMSIZ];
if (names.pci_path[0] &&
@@ -837,10 +1045,10 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
if (names.pci_slot[0] &&
snprintf(str, sizeof str, "%s%s%s", prefix, names.pci_slot, names.bcma_core))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
- goto out;
+ return 0;
}
-out:
- return EXIT_SUCCESS;
+
+ return 0;
}
const struct udev_builtin udev_builtin_net_id = {
diff --git a/src/udev/udev-builtin-net_setup_link.c b/src/udev/udev-builtin-net_setup_link.c
index 8bed6399af..c0d3d4aa01 100644
--- a/src/udev/udev-builtin-net_setup_link.c
+++ b/src/udev/udev-builtin-net_setup_link.c
@@ -1,22 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include "device-util.h"
#include "alloc-util.h"
#include "link-config.h"
#include "log.h"
-#include "udev.h"
+#include "string-util.h"
+#include "udev-builtin.h"
static link_config_ctx *ctx = NULL;
-static int builtin_net_setup_link(struct udev_device *dev, int argc, char **argv, bool test) {
+static int builtin_net_setup_link(sd_device *dev, int argc, char **argv, bool test) {
_cleanup_free_ char *driver = NULL;
const char *name = NULL;
link_config *link;
int r;
- if (argc > 1) {
- log_error("This program takes no arguments.");
- return EXIT_FAILURE;
- }
+ if (argc > 1)
+ return log_device_error_errno(dev, EINVAL, "This program takes no arguments.");
r = link_get_driver(ctx, dev, &driver);
if (r >= 0)
@@ -24,28 +24,25 @@ static int builtin_net_setup_link(struct udev_device *dev, int argc, char **argv
r = link_config_get(ctx, dev, &link);
if (r < 0) {
- if (r == -ENOENT) {
- log_debug("No matching link configuration found.");
- return EXIT_SUCCESS;
- } else {
- log_error_errno(r, "Could not get link config: %m");
- return EXIT_FAILURE;
- }
+ if (r == -ENOENT)
+ return log_device_debug_errno(dev, r, "No matching link configuration found.");
+
+ return log_device_error_errno(dev, r, "Failed to get link config: %m");
}
r = link_config_apply(ctx, link, dev, &name);
if (r < 0)
- log_warning_errno(r, "Could not apply link config to %s, ignoring: %m", udev_device_get_sysname(dev));
+ log_device_warning_errno(dev, r, "Could not apply link config, ignoring: %m");
udev_builtin_add_property(dev, test, "ID_NET_LINK_FILE", link->filename);
if (name)
udev_builtin_add_property(dev, test, "ID_NET_NAME", name);
- return EXIT_SUCCESS;
+ return 0;
}
-static int builtin_net_setup_link_init(struct udev *udev) {
+static int builtin_net_setup_link_init(void) {
int r;
if (ctx)
@@ -63,13 +60,13 @@ static int builtin_net_setup_link_init(struct udev *udev) {
return 0;
}
-static void builtin_net_setup_link_exit(struct udev *udev) {
+static void builtin_net_setup_link_exit(void) {
link_config_ctx_free(ctx);
ctx = NULL;
log_debug("Unloaded link configuration context.");
}
-static bool builtin_net_setup_link_validate(struct udev *udev) {
+static bool builtin_net_setup_link_validate(void) {
log_debug("Check if link configuration needs reloading.");
if (!ctx)
return false;
diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c
index 36f1949c7b..94f2740592 100644
--- a/src/udev/udev-builtin-path_id.c
+++ b/src/udev/udev-builtin-path_id.c
@@ -2,9 +2,7 @@
/*
* compose persistent device path
*
- *
* Logic based on Hannes Reinecke's shell script.
- *
*/
#include <ctype.h>
@@ -20,10 +18,11 @@
#include "alloc-util.h"
#include "dirent-util.h"
#include "fd-util.h"
+#include "libudev-util.h"
#include "string-util.h"
+#include "strv.h"
#include "sysexits.h"
-#include "udev.h"
-#include "udev-util.h"
+#include "udev-builtin.h"
_printf_(2,3)
static void path_prepend(char **path, const char *fmt, ...) {
@@ -57,59 +56,68 @@ static void path_prepend(char **path, const char *fmt, ...) {
** Linux only supports 32 bit luns.
** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
*/
-static void format_lun_number(struct udev_device *dev, char **path) {
- unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10);
+static int format_lun_number(sd_device *dev, char **path) {
+ const char *sysnum;
+ unsigned long lun;
+ int r;
+
+ r = sd_device_get_sysnum(dev, &sysnum);
+ if (r < 0)
+ return r;
+ if (!sysnum)
+ return -ENOENT;
+ lun = strtoul(sysnum, NULL, 10);
if (lun < 256)
/* address method 0, peripheral device addressing with bus id of zero */
path_prepend(path, "lun-%lu", lun);
else
/* handle all other lun addressing methods by using a variant of the original lun format */
path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff);
+
+ return 0;
}
-static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) {
- struct udev_device *parent = dev;
+static sd_device *skip_subsystem(sd_device *dev, const char *subsys) {
+ sd_device *parent;
assert(dev);
assert(subsys);
- while (parent) {
+ for (parent = dev; ; ) {
const char *subsystem;
- subsystem = udev_device_get_subsystem(parent);
- if (!streq_ptr(subsystem, subsys))
+ if (sd_device_get_subsystem(parent, &subsystem) < 0)
+ break;
+
+ if (!streq(subsystem, subsys))
break;
dev = parent;
- parent = udev_device_get_parent(parent);
+ if (sd_device_get_parent(dev, &parent) < 0)
+ break;
}
return dev;
}
-static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) {
- struct udev *udev;
- struct udev_device *targetdev;
- _cleanup_(udev_device_unrefp) struct udev_device *fcdev = NULL;
- const char *port;
+static sd_device *handle_scsi_fibre_channel(sd_device *parent, char **path) {
+ sd_device *targetdev;
+ _cleanup_(sd_device_unrefp) sd_device *fcdev = NULL;
+ const char *port, *sysname;
_cleanup_free_ char *lun = NULL;
assert(parent);
assert(path);
- udev = udev_device_get_udev(parent);
- targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
- if (!targetdev)
+ if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target", &targetdev) < 0)
return NULL;
-
- fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev));
- if (!fcdev)
+ if (sd_device_get_sysname(targetdev, &sysname) < 0)
return NULL;
-
- port = udev_device_get_sysattr_value(fcdev, "port_name");
- if (!port)
+ if (sd_device_new_from_subsystem_sysname(&fcdev, "fc_transport", sysname) < 0)
+ return NULL;
+ if (sd_device_get_sysattr_value(fcdev, "port_name", &port) < 0)
return NULL;
format_lun_number(parent, &lun);
@@ -117,33 +125,24 @@ static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent,
return parent;
}
-static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent, char **path) {
- struct udev *udev;
- struct udev_device *targetdev, *target_parent;
- _cleanup_(udev_device_unrefp) struct udev_device *sasdev = NULL;
- const char *sas_address;
+static sd_device *handle_scsi_sas_wide_port(sd_device *parent, char **path) {
+ sd_device *targetdev, *target_parent;
+ _cleanup_(sd_device_unrefp) sd_device *sasdev = NULL;
+ const char *sas_address, *sysname;
_cleanup_free_ char *lun = NULL;
assert(parent);
assert(path);
- udev = udev_device_get_udev(parent);
-
- targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
- if (!targetdev)
+ if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target", &targetdev) < 0)
return NULL;
-
- target_parent = udev_device_get_parent(targetdev);
- if (!target_parent)
+ if (sd_device_get_parent(targetdev, &target_parent) < 0)
return NULL;
-
- sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device",
- udev_device_get_sysname(target_parent));
- if (!sasdev)
+ if (sd_device_get_sysname(target_parent, &sysname) < 0)
return NULL;
-
- sas_address = udev_device_get_sysattr_value(sasdev, "sas_address");
- if (!sas_address)
+ if (sd_device_new_from_subsystem_sysname(&sasdev, "sas_device", sysname) < 0)
+ return NULL;
+ if (sd_device_get_sysattr_value(sasdev, "sas_address", &sas_address) < 0)
return NULL;
format_lun_number(parent, &lun);
@@ -151,47 +150,35 @@ static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent,
return parent;
}
-static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path)
-{
- struct udev *udev;
- struct udev_device *targetdev, *target_parent, *port, *expander;
- _cleanup_(udev_device_unrefp) struct udev_device
- *target_sasdev = NULL, *expander_sasdev = NULL, *port_sasdev = NULL;
+static sd_device *handle_scsi_sas(sd_device *parent, char **path) {
+ sd_device *targetdev, *target_parent, *port, *expander;
+ _cleanup_(sd_device_unrefp) sd_device *target_sasdev = NULL, *expander_sasdev = NULL, *port_sasdev = NULL;
const char *sas_address = NULL;
const char *phy_id;
- const char *phy_count;
+ const char *phy_count, *sysname;
_cleanup_free_ char *lun = NULL;
assert(parent);
assert(path);
- udev = udev_device_get_udev(parent);
-
- targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
- if (!targetdev)
+ if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target", &targetdev) < 0)
return NULL;
-
- target_parent = udev_device_get_parent(targetdev);
- if (!target_parent)
+ if (sd_device_get_parent(targetdev, &target_parent) < 0)
+ return NULL;
+ if (sd_device_get_sysname(target_parent, &sysname) < 0)
return NULL;
-
/* Get sas device */
- target_sasdev = udev_device_new_from_subsystem_sysname(
- udev, "sas_device", udev_device_get_sysname(target_parent));
- if (!target_sasdev)
+ if (sd_device_new_from_subsystem_sysname(&target_sasdev, "sas_device", sysname) < 0)
return NULL;
-
/* The next parent is sas port */
- port = udev_device_get_parent(target_parent);
- if (!port)
+ if (sd_device_get_parent(target_parent, &port) < 0)
+ return NULL;
+ if (sd_device_get_sysname(port, &sysname) < 0)
return NULL;
-
/* Get port device */
- port_sasdev = udev_device_new_from_subsystem_sysname(
- udev, "sas_port", udev_device_get_sysname(port));
-
- phy_count = udev_device_get_sysattr_value(port_sasdev, "num_phys");
- if (!phy_count)
+ if (sd_device_new_from_subsystem_sysname(&port_sasdev, "sas_port", sysname) < 0)
+ return NULL;
+ if (sd_device_get_sysattr_value(port_sasdev, "num_phys", &phy_count) < 0)
return NULL;
/* Check if we are simple disk */
@@ -199,24 +186,20 @@ static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **pa
return handle_scsi_sas_wide_port(parent, path);
/* Get connected phy */
- phy_id = udev_device_get_sysattr_value(target_sasdev, "phy_identifier");
- if (!phy_id)
+ if (sd_device_get_sysattr_value(target_sasdev, "phy_identifier", &phy_id) < 0)
return NULL;
/* The port's parent is either hba or expander */
- expander = udev_device_get_parent(port);
- if (!expander)
+ if (sd_device_get_parent(port, &expander) < 0)
return NULL;
+ if (sd_device_get_sysname(expander, &sysname) < 0)
+ return NULL;
/* Get expander device */
- expander_sasdev = udev_device_new_from_subsystem_sysname(
- udev, "sas_device", udev_device_get_sysname(expander));
- if (expander_sasdev) {
- /* Get expander's address */
- sas_address = udev_device_get_sysattr_value(expander_sasdev,
- "sas_address");
- if (!sas_address)
- return NULL;
+ if (sd_device_new_from_subsystem_sysname(&expander_sasdev, "sas_device", sysname) >= 0) {
+ /* Get expander's address */
+ if (sd_device_get_sysattr_value(expander_sasdev, "sas_address", &sas_address) < 0)
+ return NULL;
}
format_lun_number(parent, &lun);
@@ -228,46 +211,43 @@ static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **pa
return parent;
}
-static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) {
- struct udev *udev;
- struct udev_device *transportdev;
- _cleanup_(udev_device_unrefp) struct udev_device
- *sessiondev = NULL, *conndev = NULL;
+static sd_device *handle_scsi_iscsi(sd_device *parent, char **path) {
+ sd_device *transportdev;
+ _cleanup_(sd_device_unrefp) sd_device *sessiondev = NULL, *conndev = NULL;
const char *target, *connname, *addr, *port;
_cleanup_free_ char *lun = NULL;
+ const char *sysname, *sysnum;
assert(parent);
assert(path);
- udev = udev_device_get_udev(parent);
-
/* find iscsi session */
- transportdev = parent;
- for (;;) {
- transportdev = udev_device_get_parent(transportdev);
- if (!transportdev)
+ for (transportdev = parent; ; ) {
+
+ if (sd_device_get_parent(transportdev, &transportdev) < 0)
+ return NULL;
+ if (sd_device_get_sysname(transportdev, &sysname) < 0)
return NULL;
- if (startswith(udev_device_get_sysname(transportdev), "session"))
+ if (startswith(sysname, "session"))
break;
}
/* find iscsi session device */
- sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev));
- if (!sessiondev)
+ if (sd_device_new_from_subsystem_sysname(&sessiondev, "iscsi_session", sysname) < 0)
return NULL;
- target = udev_device_get_sysattr_value(sessiondev, "targetname");
- if (!target)
+ if (sd_device_get_sysattr_value(sessiondev, "targetname", &target) < 0)
return NULL;
- connname = strjoina("connection", udev_device_get_sysnum(transportdev), ":0");
- conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname);
- if (!conndev)
+ if (sd_device_get_sysnum(transportdev, &sysnum) < 0 || !sysnum)
+ return NULL;
+ connname = strjoina("connection", sysnum, ":0");
+ if (sd_device_new_from_subsystem_sysname(&conndev, "iscsi_connection", connname) < 0)
return NULL;
- addr = udev_device_get_sysattr_value(conndev, "persistent_address");
- port = udev_device_get_sysattr_value(conndev, "persistent_port");
- if (!addr || !port)
+ if (sd_device_get_sysattr_value(conndev, "persistent_address", &addr) < 0)
+ return NULL;
+ if (sd_device_get_sysattr_value(conndev, "persistent_port", &port) < 0)
return NULL;
format_lun_number(parent, &lun);
@@ -275,39 +255,34 @@ static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **
return parent;
}
-static struct udev_device *handle_scsi_ata(struct udev_device *parent, char **path) {
- struct udev *udev;
- struct udev_device *targetdev, *target_parent;
- _cleanup_(udev_device_unrefp) struct udev_device *atadev = NULL;
- const char *port_no;
+static sd_device *handle_scsi_ata(sd_device *parent, char **path) {
+ sd_device *targetdev, *target_parent;
+ _cleanup_(sd_device_unrefp) sd_device *atadev = NULL;
+ const char *port_no, *sysname;
assert(parent);
assert(path);
- udev = udev_device_get_udev(parent);
-
- targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
- if (!targetdev)
+ if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host", &targetdev) < 0)
return NULL;
- target_parent = udev_device_get_parent(targetdev);
- if (!target_parent)
+ if (sd_device_get_parent(targetdev, &target_parent) < 0)
return NULL;
- atadev = udev_device_new_from_subsystem_sysname(udev, "ata_port", udev_device_get_sysname(target_parent));
- if (!atadev)
+ if (sd_device_get_sysname(target_parent, &sysname) < 0)
+ return NULL;
+ if (sd_device_new_from_subsystem_sysname(&atadev, "ata_port", sysname) < 0)
return NULL;
- port_no = udev_device_get_sysattr_value(atadev, "port_no");
- if (!port_no)
+ if (sd_device_get_sysattr_value(atadev, "port_no", &port_no) < 0)
return NULL;
path_prepend(path, "ata-%s", port_no);
return parent;
}
-static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) {
- struct udev_device *hostdev;
+static sd_device *handle_scsi_default(sd_device *parent, char **path) {
+ sd_device *hostdev;
int host, bus, target, lun;
const char *name, *base, *pos;
_cleanup_closedir_ DIR *dir = NULL;
@@ -317,11 +292,11 @@ static struct udev_device *handle_scsi_default(struct udev_device *parent, char
assert(parent);
assert(path);
- hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
- if (!hostdev)
+ if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host", &hostdev) < 0)
return NULL;
- name = udev_device_get_sysname(parent);
+ if (sd_device_get_sysname(parent, &name) < 0)
+ return NULL;
if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4)
return NULL;
@@ -343,7 +318,8 @@ static struct udev_device *handle_scsi_default(struct udev_device *parent, char
* get into the way of this "I hope it works" logic.
*/
- base = udev_device_get_syspath(hostdev);
+ if (sd_device_get_syspath(hostdev, &base) < 0)
+ return NULL;
pos = strrchr(base, '/');
if (!pos)
return NULL;
@@ -382,9 +358,9 @@ static struct udev_device *handle_scsi_default(struct udev_device *parent, char
return hostdev;
}
-static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char **path, size_t guid_str_len) {
- struct udev_device *hostdev;
- struct udev_device *vmbusdev;
+static sd_device *handle_scsi_hyperv(sd_device *parent, char **path, size_t guid_str_len) {
+ sd_device *hostdev;
+ sd_device *vmbusdev;
const char *guid_str;
_cleanup_free_ char *lun = NULL;
char guid[39];
@@ -394,16 +370,13 @@ static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char *
assert(path);
assert(guid_str_len < sizeof(guid));
- hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
- if (!hostdev)
+ if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host", &hostdev) < 0)
return NULL;
- vmbusdev = udev_device_get_parent(hostdev);
- if (!vmbusdev)
+ if (sd_device_get_parent(hostdev, &vmbusdev) < 0)
return NULL;
- guid_str = udev_device_get_sysattr_value(vmbusdev, "device_id");
- if (!guid_str)
+ if (sd_device_get_sysattr_value(vmbusdev, "device_id", &guid_str) < 0)
return NULL;
if (strlen(guid_str) < guid_str_len || guid_str[0] != '{' || guid_str[guid_str_len-1] != '}')
@@ -421,23 +394,23 @@ static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char *
return parent;
}
-static struct udev_device *handle_scsi(struct udev_device *parent, char **path, bool *supported_parent) {
+static sd_device *handle_scsi(sd_device *parent, char **path, bool *supported_parent) {
const char *devtype, *id, *name;
- devtype = udev_device_get_devtype(parent);
- if (!streq_ptr(devtype, "scsi_device"))
+ if (sd_device_get_devtype(parent, &devtype) < 0 ||
+ !streq(devtype, "scsi_device"))
return parent;
/* firewire */
- id = udev_device_get_sysattr_value(parent, "ieee1394_id");
- if (id) {
+ if (sd_device_get_sysattr_value(parent, "ieee1394_id", &id) >= 0) {
path_prepend(path, "ieee1394-0x%s", id);
*supported_parent = true;
return skip_subsystem(parent, "scsi");
}
/* scsi sysfs does not have a "subsystem" for the transport */
- name = udev_device_get_syspath(parent);
+ if (sd_device_get_syspath(parent, &name) < 0)
+ return NULL;
if (strstr(name, "/rport-")) {
*supported_parent = true;
@@ -465,11 +438,12 @@ static struct udev_device *handle_scsi(struct udev_device *parent, char **path,
return handle_scsi_default(parent, path);
}
-static struct udev_device *handle_cciss(struct udev_device *parent, char **path) {
+static sd_device *handle_cciss(sd_device *parent, char **path) {
const char *str;
- unsigned int controller, disk;
+ unsigned controller, disk;
- str = udev_device_get_sysname(parent);
+ if (sd_device_get_sysname(parent, &str) < 0)
+ return NULL;
if (sscanf(str, "c%ud%u%*s", &controller, &disk) != 2)
return NULL;
@@ -477,30 +451,32 @@ static struct udev_device *handle_cciss(struct udev_device *parent, char **path)
return skip_subsystem(parent, "cciss");
}
-static void handle_scsi_tape(struct udev_device *dev, char **path) {
+static void handle_scsi_tape(sd_device *dev, char **path) {
const char *name;
/* must be the last device in the syspath */
if (*path)
return;
- name = udev_device_get_sysname(dev);
+ if (sd_device_get_sysname(dev, &name) < 0)
+ return;
+
if (startswith(name, "nst") && strchr("lma", name[3]))
path_prepend(path, "nst%c", name[3]);
else if (startswith(name, "st") && strchr("lma", name[2]))
path_prepend(path, "st%c", name[2]);
}
-static struct udev_device *handle_usb(struct udev_device *parent, char **path) {
+static sd_device *handle_usb(sd_device *parent, char **path) {
const char *devtype, *str, *port;
- devtype = udev_device_get_devtype(parent);
- if (!devtype)
+ if (sd_device_get_devtype(parent, &devtype) < 0)
return parent;
if (!STR_IN_SET(devtype, "usb_interface", "usb_device"))
return parent;
- str = udev_device_get_sysname(parent);
+ if (sd_device_get_sysname(parent, &str) < 0)
+ return parent;
port = strchr(str, '-');
if (!port)
return parent;
@@ -510,11 +486,12 @@ static struct udev_device *handle_usb(struct udev_device *parent, char **path) {
return skip_subsystem(parent, "usb");
}
-static struct udev_device *handle_bcma(struct udev_device *parent, char **path) {
+static sd_device *handle_bcma(sd_device *parent, char **path) {
const char *sysname;
- unsigned int core;
+ unsigned core;
- sysname = udev_device_get_sysname(parent);
+ if (sd_device_get_sysname(parent, &sysname) < 0)
+ return NULL;
if (sscanf(sysname, "bcma%*u:%u", &core) != 1)
return NULL;
@@ -523,38 +500,41 @@ static struct udev_device *handle_bcma(struct udev_device *parent, char **path)
}
/* Handle devices of AP bus in System z platform. */
-static struct udev_device *handle_ap(struct udev_device *parent, char **path) {
+static sd_device *handle_ap(sd_device *parent, char **path) {
const char *type, *func;
assert(parent);
assert(path);
- type = udev_device_get_sysattr_value(parent, "type");
- func = udev_device_get_sysattr_value(parent, "ap_functions");
-
- if (type && func)
+ if (sd_device_get_sysattr_value(parent, "type", &type) >= 0 &&
+ sd_device_get_sysattr_value(parent, "ap_functions", &func) >= 0)
path_prepend(path, "ap-%s-%s", type, func);
- else
- path_prepend(path, "ap-%s", udev_device_get_sysname(parent));
+ else {
+ const char *sysname;
+
+ if (sd_device_get_sysname(parent, &sysname) >= 0)
+ path_prepend(path, "ap-%s", sysname);
+ }
return skip_subsystem(parent, "ap");
}
-static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) {
- struct udev_device *parent;
+static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
+ sd_device *parent;
_cleanup_free_ char *path = NULL;
bool supported_transport = false;
bool supported_parent = false;
+ const char *subsystem;
assert(dev);
/* walk up the chain of devices and compose path */
parent = dev;
while (parent) {
- const char *subsys;
+ const char *subsys, *sysname;
- subsys = udev_device_get_subsystem(parent);
- if (!subsys) {
+ if (sd_device_get_subsystem(parent, &subsys) < 0 ||
+ sd_device_get_sysname(parent, &sysname) < 0) {
;
} else if (streq(subsys, "scsi_tape")) {
handle_scsi_tape(parent, &path);
@@ -571,40 +551,44 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
parent = handle_bcma(parent, &path);
supported_transport = true;
} else if (streq(subsys, "serio")) {
- path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent));
- parent = skip_subsystem(parent, "serio");
+ const char *sysnum;
+
+ if (sd_device_get_sysnum(parent, &sysnum) >= 0 && sysnum) {
+ path_prepend(&path, "serio-%s", sysnum);
+ parent = skip_subsystem(parent, "serio");
+ }
} else if (streq(subsys, "pci")) {
- path_prepend(&path, "pci-%s", udev_device_get_sysname(parent));
+ path_prepend(&path, "pci-%s", sysname);
parent = skip_subsystem(parent, "pci");
supported_parent = true;
} else if (streq(subsys, "platform")) {
- path_prepend(&path, "platform-%s", udev_device_get_sysname(parent));
+ path_prepend(&path, "platform-%s", sysname);
parent = skip_subsystem(parent, "platform");
supported_transport = true;
supported_parent = true;
} else if (streq(subsys, "acpi")) {
- path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent));
+ path_prepend(&path, "acpi-%s", sysname);
parent = skip_subsystem(parent, "acpi");
supported_parent = true;
} else if (streq(subsys, "xen")) {
- path_prepend(&path, "xen-%s", udev_device_get_sysname(parent));
+ path_prepend(&path, "xen-%s", sysname);
parent = skip_subsystem(parent, "xen");
supported_parent = true;
} else if (streq(subsys, "virtio")) {
parent = skip_subsystem(parent, "virtio");
supported_transport = true;
} else if (streq(subsys, "scm")) {
- path_prepend(&path, "scm-%s", udev_device_get_sysname(parent));
+ path_prepend(&path, "scm-%s", sysname);
parent = skip_subsystem(parent, "scm");
supported_transport = true;
supported_parent = true;
} else if (streq(subsys, "ccw")) {
- path_prepend(&path, "ccw-%s", udev_device_get_sysname(parent));
+ path_prepend(&path, "ccw-%s", sysname);
parent = skip_subsystem(parent, "ccw");
supported_transport = true;
supported_parent = true;
} else if (streq(subsys, "ccwgroup")) {
- path_prepend(&path, "ccwgroup-%s", udev_device_get_sysname(parent));
+ path_prepend(&path, "ccwgroup-%s", sysname);
parent = skip_subsystem(parent, "ccwgroup");
supported_transport = true;
supported_parent = true;
@@ -613,14 +597,14 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
supported_transport = true;
supported_parent = true;
} else if (streq(subsys, "iucv")) {
- path_prepend(&path, "iucv-%s", udev_device_get_sysname(parent));
+ path_prepend(&path, "iucv-%s", sysname);
parent = skip_subsystem(parent, "iucv");
supported_transport = true;
supported_parent = true;
} else if (streq(subsys, "nvme")) {
- const char *nsid = udev_device_get_sysattr_value(dev, "nsid");
+ const char *nsid;
- if (nsid) {
+ if (sd_device_get_sysattr_value(dev, "nsid", &nsid) >= 0) {
path_prepend(&path, "nvme-%s", nsid);
parent = skip_subsystem(parent, "nvme");
supported_parent = true;
@@ -628,12 +612,14 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
}
}
- if (parent)
- parent = udev_device_get_parent(parent);
+ if (!parent)
+ break;
+ if (sd_device_get_parent(parent, &parent) < 0)
+ break;
}
if (!path)
- return EXIT_FAILURE;
+ return -ENOENT;
/*
* Do not return devices with an unknown parent device type. They
@@ -641,15 +627,17 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
* unique and predictable name.
*/
if (!supported_parent)
- return EXIT_FAILURE;
+ return -ENOENT;
/*
* Do not return block devices without a well-known transport. Some
* devices do not expose their buses and do not provide a unique
* and predictable name that way.
*/
- if (streq_ptr(udev_device_get_subsystem(dev), "block") && !supported_transport)
- return EXIT_FAILURE;
+ if (sd_device_get_subsystem(dev, &subsystem) >= 0 &&
+ streq(subsystem, "block") &&
+ !supported_transport)
+ return -ENOENT;
{
char tag[UTIL_NAME_SIZE];
@@ -685,7 +673,7 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag);
}
- return EXIT_SUCCESS;
+ return 0;
}
const struct udev_builtin udev_builtin_path_id = {
diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c
index b1191ae7ff..10a143ac7f 100644
--- a/src/udev/udev-builtin-uaccess.c
+++ b/src/udev/udev-builtin-uaccess.c
@@ -1,26 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* manage device node user ACL
- *
- *
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/stat.h>
#include "sd-login.h"
+#include "device-util.h"
#include "login-util.h"
#include "logind-acl.h"
-#include "udev.h"
-#include "util.h"
+#include "log.h"
+#include "udev-builtin.h"
-static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool test) {
- int r;
+static int builtin_uaccess(sd_device *dev, int argc, char *argv[], bool test) {
const char *path = NULL, *seat;
bool changed_acl = false;
uid_t uid;
+ int r;
umask(0022);
@@ -28,24 +28,29 @@ static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool
if (!logind_running())
return 0;
- path = udev_device_get_devnode(dev);
- seat = udev_device_get_property_value(dev, "ID_SEAT");
- if (!seat)
+ r = sd_device_get_devname(dev, &path);
+ if (r < 0) {
+ log_device_error_errno(dev, r, "Failed to get device name: %m");
+ goto finish;
+ }
+
+ if (sd_device_get_property_value(dev, "ID_SEAT", &seat) < 0)
seat = "seat0";
r = sd_seat_get_active(seat, NULL, &uid);
- if (IN_SET(r, -ENXIO, -ENODATA)) {
- /* No active session on this seat */
- r = 0;
- goto finish;
- } else if (r < 0) {
- log_error("Failed to determine active user on seat %s.", seat);
+ if (r < 0) {
+ if (IN_SET(r, -ENXIO, -ENODATA))
+ /* No active session on this seat */
+ r = 0;
+ else
+ log_device_error_errno(dev, r, "Failed to determine active user on seat %s: %m", seat);
+
goto finish;
}
r = devnode_acl(path, true, false, 0, true, uid);
if (r < 0) {
- log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL on %s: %m", path);
+ log_device_full(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL: %m");
goto finish;
}
@@ -59,13 +64,13 @@ finish:
/* Better be safe than sorry and reset ACL */
k = devnode_acl(path, true, false, 0, false, 0);
if (k < 0) {
- log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, k, "Failed to apply ACL on %s: %m", path);
+ log_device_full(dev, k == -ENOENT ? LOG_DEBUG : LOG_ERR, k, "Failed to apply ACL: %m");
if (r >= 0)
r = k;
}
}
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
const struct udev_builtin udev_builtin_uaccess = {
diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c
index dcf21a2f44..3525d25048 100644
--- a/src/udev/udev-builtin-usb_id.c
+++ b/src/udev/udev-builtin-usb_id.c
@@ -4,8 +4,6 @@
*
* Copyright (c) 2005 SUSE Linux Products GmbH, Germany
* Author: Hannes Reinecke <hare@suse.de>
- *
- *
*/
#include <ctype.h>
@@ -18,9 +16,12 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "device-util.h"
#include "fd-util.h"
+#include "libudev-util.h"
#include "string-util.h"
-#include "udev.h"
+#include "strxcpyx.h"
+#include "udev-builtin.h"
static void set_usb_iftype(char *to, int if_class_num, size_t len) {
const char *type = "generic";
@@ -136,13 +137,15 @@ static void set_scsi_type(char *to, const char *from, size_t len) {
#define USB_DT_DEVICE 0x01
#define USB_DT_INTERFACE 0x04
-static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len) {
+static int dev_if_packed_info(sd_device *dev, char *ifs_str, size_t len) {
_cleanup_free_ char *filename = NULL;
_cleanup_close_ int fd = -1;
ssize_t size;
unsigned char buf[18 + 65535];
size_t pos = 0;
unsigned strpos = 0;
+ const char *syspath;
+ int r;
struct usb_interface_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -155,12 +158,15 @@ static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len
uint8_t iInterface;
} _packed_;
- if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0)
+ r = sd_device_get_syspath(dev, &syspath);
+ if (r < 0)
+ return r;
+ if (asprintf(&filename, "%s/descriptors", syspath) < 0)
return log_oom();
fd = open(filename, O_RDONLY|O_CLOEXEC);
if (fd < 0)
- return log_debug_errno(errno, "Error opening USB device 'descriptors' file: %m");
+ return log_device_debug_errno(dev, errno, "Failed to open USB device 'descriptors' file: %m");
size = read(fd, buf, sizeof(buf));
if (size < 18 || (size_t) size >= sizeof(buf))
@@ -219,7 +225,7 @@ static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len
* 6.) If the device supplies a serial number, this number
* is concatenated with the identification with an underscore '_'.
*/
-static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool test) {
+static int builtin_usb_id(sd_device *dev, int argc, char *argv[], bool test) {
char vendor_str[64] = "";
char vendor_str_enc[256];
const char *vendor_id;
@@ -235,116 +241,114 @@ static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool
const char *driver = NULL;
char serial[256];
- struct udev_device *dev_interface = NULL;
- struct udev_device *dev_usb = NULL;
+ sd_device *dev_interface, *dev_usb;
const char *if_class, *if_subclass;
int if_class_num;
int protocol = 0;
size_t l;
char *s;
+ const char *syspath, *sysname, *devtype, *interface_syspath;
+ int r;
+
assert(dev);
+ r = sd_device_get_syspath(dev, &syspath);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_sysname(dev, &sysname);
+ if (r < 0)
+ return r;
+
/* shortcut, if we are called directly for a "usb_device" type */
- if (udev_device_get_devtype(dev) != NULL && streq(udev_device_get_devtype(dev), "usb_device")) {
+ if (sd_device_get_devtype(dev, &devtype) >= 0 && streq(devtype, "usb_device")) {
dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str));
dev_usb = dev;
goto fallback;
}
/* usb interface directory */
- dev_interface = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface");
- if (dev_interface == NULL) {
- log_debug("unable to access usb_interface device of '%s'",
- udev_device_get_syspath(dev));
- return EXIT_FAILURE;
- }
+ r = sd_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface", &dev_interface);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to access usb_interface: %m");
- ifnum = udev_device_get_sysattr_value(dev_interface, "bInterfaceNumber");
- driver = udev_device_get_sysattr_value(dev_interface, "driver");
+ r = sd_device_get_syspath(dev_interface, &interface_syspath);
+ if (r < 0)
+ return log_device_debug_errno(dev_interface, r, "Failed to get syspath: %m");
+ (void) sd_device_get_sysattr_value(dev_interface, "bInterfaceNumber", &ifnum);
+ (void) sd_device_get_sysattr_value(dev_interface, "driver", &driver);
- if_class = udev_device_get_sysattr_value(dev_interface, "bInterfaceClass");
- if (!if_class) {
- log_debug("%s: cannot get bInterfaceClass attribute",
- udev_device_get_sysname(dev));
- return EXIT_FAILURE;
- }
+ r = sd_device_get_sysattr_value(dev_interface, "bInterfaceClass", &if_class);
+ if (r < 0)
+ return log_device_debug_errno(dev_interface, r, "Failed to get bInterfaceClass attribute: %m");
if_class_num = strtoul(if_class, NULL, 16);
if (if_class_num == 8) {
/* mass storage */
- if_subclass = udev_device_get_sysattr_value(dev_interface, "bInterfaceSubClass");
- if (if_subclass != NULL)
+ if (sd_device_get_sysattr_value(dev_interface, "bInterfaceSubClass", &if_subclass) >= 0)
protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1);
- } else {
+ } else
set_usb_iftype(type_str, if_class_num, sizeof(type_str)-1);
- }
- log_debug("%s: if_class %d protocol %d",
- udev_device_get_syspath(dev_interface), if_class_num, protocol);
+ log_device_debug(dev_interface, "if_class:%d protocol:%d", if_class_num, protocol);
/* usb device directory */
- dev_usb = udev_device_get_parent_with_subsystem_devtype(dev_interface, "usb", "usb_device");
- if (!dev_usb) {
- log_debug("unable to find parent 'usb' device of '%s'",
- udev_device_get_syspath(dev));
- return EXIT_FAILURE;
- }
+ r = sd_device_get_parent_with_subsystem_devtype(dev_interface, "usb", "usb_device", &dev_usb);
+ if (r < 0)
+ return log_device_debug_errno(dev_interface, r, "Failed to find parent 'usb' device");
/* all interfaces of the device in a single string */
dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str));
/* mass storage : SCSI or ATAPI */
if (IN_SET(protocol, 6, 2)) {
- struct udev_device *dev_scsi;
- const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev;
+ sd_device *dev_scsi;
+ const char *scsi_sysname, *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev;
int host, bus, target, lun;
/* get scsi device */
- dev_scsi = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device");
- if (dev_scsi == NULL) {
- log_debug("unable to find parent 'scsi' device of '%s'",
- udev_device_get_syspath(dev));
+ r = sd_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device", &dev_scsi);
+ if (r < 0) {
+ log_device_debug_errno(dev, r, "Unable to find parent SCSI device");
goto fallback;
}
- if (sscanf(udev_device_get_sysname(dev_scsi), "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) {
- log_debug("invalid scsi device '%s'", udev_device_get_sysname(dev_scsi));
+ if (sd_device_get_sysname(dev_scsi, &scsi_sysname) < 0)
+ goto fallback;
+ if (sscanf(scsi_sysname, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) {
+ log_device_debug(dev_scsi, "Invalid SCSI device");
goto fallback;
}
/* Generic SPC-2 device */
- scsi_vendor = udev_device_get_sysattr_value(dev_scsi, "vendor");
- if (!scsi_vendor) {
- log_debug("%s: cannot get SCSI vendor attribute",
- udev_device_get_sysname(dev_scsi));
+ r = sd_device_get_sysattr_value(dev_scsi, "vendor", &scsi_vendor);
+ if (r < 0) {
+ log_device_debug_errno(dev_scsi, r, "Failed to get SCSI vendor attribute: %m");
goto fallback;
}
udev_util_encode_string(scsi_vendor, vendor_str_enc, sizeof(vendor_str_enc));
util_replace_whitespace(scsi_vendor, vendor_str, sizeof(vendor_str)-1);
util_replace_chars(vendor_str, NULL);
- scsi_model = udev_device_get_sysattr_value(dev_scsi, "model");
- if (!scsi_model) {
- log_debug("%s: cannot get SCSI model attribute",
- udev_device_get_sysname(dev_scsi));
+ r = sd_device_get_sysattr_value(dev_scsi, "model", &scsi_model);
+ if (r < 0) {
+ log_device_debug_errno(dev_scsi, r, "Failed to get SCSI model attribute: %m");
goto fallback;
}
udev_util_encode_string(scsi_model, model_str_enc, sizeof(model_str_enc));
util_replace_whitespace(scsi_model, model_str, sizeof(model_str)-1);
util_replace_chars(model_str, NULL);
- scsi_type = udev_device_get_sysattr_value(dev_scsi, "type");
- if (!scsi_type) {
- log_debug("%s: cannot get SCSI type attribute",
- udev_device_get_sysname(dev_scsi));
+ r = sd_device_get_sysattr_value(dev_scsi, "type", &scsi_type);
+ if (r < 0) {
+ log_device_debug_errno(dev_scsi, r, "Failed to get SCSI type attribute: %m");
goto fallback;
}
set_scsi_type(type_str, scsi_type, sizeof(type_str)-1);
- scsi_rev = udev_device_get_sysattr_value(dev_scsi, "rev");
- if (!scsi_rev) {
- log_debug("%s: cannot get SCSI revision attribute",
- udev_device_get_sysname(dev_scsi));
+ r = sd_device_get_sysattr_value(dev_scsi, "rev", &scsi_rev);
+ if (r < 0) {
+ log_device_debug_errno(dev_scsi, r, "Failed to get SCSI revision attribute: %m");
goto fallback;
}
util_replace_whitespace(scsi_rev, revision_str, sizeof(revision_str)-1);
@@ -358,33 +362,30 @@ static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool
}
fallback:
- vendor_id = udev_device_get_sysattr_value(dev_usb, "idVendor");
- product_id = udev_device_get_sysattr_value(dev_usb, "idProduct");
+ r = sd_device_get_sysattr_value(dev_usb, "idVendor", &vendor_id);
+ if (r < 0)
+ return log_device_debug_errno(dev_usb, r, "Failed to get idVendor attribute: %m");
+
+ r = sd_device_get_sysattr_value(dev_usb, "idProduct", &product_id);
+ if (r < 0)
+ return log_device_debug_errno(dev_usb, r, "Failed to get idProduct attribute: %m");
/* fallback to USB vendor & device */
if (vendor_str[0] == '\0') {
- const char *usb_vendor = NULL;
+ const char *usb_vendor;
- usb_vendor = udev_device_get_sysattr_value(dev_usb, "manufacturer");
- if (!usb_vendor)
+ if (sd_device_get_sysattr_value(dev_usb, "manufacturer", &usb_vendor) < 0)
usb_vendor = vendor_id;
- if (!usb_vendor) {
- log_debug("No USB vendor information available");
- return EXIT_FAILURE;
- }
udev_util_encode_string(usb_vendor, vendor_str_enc, sizeof(vendor_str_enc));
util_replace_whitespace(usb_vendor, vendor_str, sizeof(vendor_str)-1);
util_replace_chars(vendor_str, NULL);
}
if (model_str[0] == '\0') {
- const char *usb_model = NULL;
+ const char *usb_model;
- usb_model = udev_device_get_sysattr_value(dev_usb, "product");
- if (!usb_model)
+ if (sd_device_get_sysattr_value(dev_usb, "product", &usb_model) < 0)
usb_model = product_id;
- if (!usb_model)
- return EXIT_FAILURE;
udev_util_encode_string(usb_model, model_str_enc, sizeof(model_str_enc));
util_replace_whitespace(usb_model, model_str, sizeof(model_str)-1);
util_replace_chars(model_str, NULL);
@@ -393,8 +394,7 @@ fallback:
if (revision_str[0] == '\0') {
const char *usb_rev;
- usb_rev = udev_device_get_sysattr_value(dev_usb, "bcdDevice");
- if (usb_rev) {
+ if (sd_device_get_sysattr_value(dev_usb, "bcdDevice", &usb_rev) >= 0) {
util_replace_whitespace(usb_rev, revision_str, sizeof(revision_str)-1);
util_replace_chars(revision_str, NULL);
}
@@ -403,21 +403,20 @@ fallback:
if (serial_str[0] == '\0') {
const char *usb_serial;
- usb_serial = udev_device_get_sysattr_value(dev_usb, "serial");
- if (usb_serial) {
+ if (sd_device_get_sysattr_value(dev_usb, "serial", &usb_serial) >= 0) {
const unsigned char *p;
/* http://msdn.microsoft.com/en-us/library/windows/hardware/gg487321.aspx */
- for (p = (unsigned char *)usb_serial; *p != '\0'; p++)
+ for (p = (unsigned char *) usb_serial; *p != '\0'; p++)
if (*p < 0x20 || *p > 0x7f || *p == ',') {
usb_serial = NULL;
break;
}
- }
- if (usb_serial) {
- util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1);
- util_replace_chars(serial_str, NULL);
+ if (usb_serial) {
+ util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1);
+ util_replace_chars(serial_str, NULL);
+ }
}
}
@@ -446,11 +445,11 @@ fallback:
udev_builtin_add_property(dev, test, "ID_BUS", "usb");
if (!isempty(packed_if_str))
udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str);
- if (ifnum != NULL)
+ if (ifnum)
udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum);
- if (driver != NULL)
+ if (driver)
udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver);
- return EXIT_SUCCESS;
+ return 0;
}
const struct udev_builtin udev_builtin_usb_id = {
diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c
index 576d83d378..3a61be10ca 100644
--- a/src/udev/udev-builtin.c
+++ b/src/udev/udev-builtin.c
@@ -4,12 +4,15 @@
#include <stdio.h>
#include <string.h>
+#include "device-private.h"
+#include "device-util.h"
#include "string-util.h"
-#include "udev.h"
+#include "strv.h"
+#include "udev-builtin.h"
static bool initialized;
-static const struct udev_builtin *builtins[] = {
+static const struct udev_builtin *builtins[_UDEV_BUILTIN_MAX] = {
#if HAVE_BLKID
[UDEV_BUILTIN_BLKID] = &udev_builtin_blkid,
#endif
@@ -29,50 +32,52 @@ static const struct udev_builtin *builtins[] = {
#endif
};
-void udev_builtin_init(struct udev *udev) {
- unsigned int i;
+void udev_builtin_init(void) {
+ unsigned i;
if (initialized)
return;
- for (i = 0; i < ELEMENTSOF(builtins); i++)
+ for (i = 0; i < _UDEV_BUILTIN_MAX; i++)
if (builtins[i] && builtins[i]->init)
- builtins[i]->init(udev);
+ builtins[i]->init();
initialized = true;
}
-void udev_builtin_exit(struct udev *udev) {
- unsigned int i;
+void udev_builtin_exit(void) {
+ unsigned i;
if (!initialized)
return;
- for (i = 0; i < ELEMENTSOF(builtins); i++)
+ for (i = 0; i < _UDEV_BUILTIN_MAX; i++)
if (builtins[i] && builtins[i]->exit)
- builtins[i]->exit(udev);
+ builtins[i]->exit();
initialized = false;
}
-bool udev_builtin_validate(struct udev *udev) {
- unsigned int i;
+bool udev_builtin_validate(void) {
+ unsigned i;
- for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (builtins[i] && builtins[i]->validate && builtins[i]->validate(udev))
+ for (i = 0; i < _UDEV_BUILTIN_MAX; i++)
+ if (builtins[i] && builtins[i]->validate && builtins[i]->validate())
return true;
return false;
}
-void udev_builtin_list(struct udev *udev) {
- unsigned int i;
+void udev_builtin_list(void) {
+ unsigned i;
- for (i = 0; i < ELEMENTSOF(builtins); i++)
+ for (i = 0; i < _UDEV_BUILTIN_MAX; i++)
if (builtins[i])
fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help);
}
const char *udev_builtin_name(enum udev_builtin_cmd cmd) {
+ assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX);
+
if (!builtins[cmd])
return NULL;
@@ -80,6 +85,8 @@ const char *udev_builtin_name(enum udev_builtin_cmd cmd) {
}
bool udev_builtin_run_once(enum udev_builtin_cmd cmd) {
+ assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX);
+
if (!builtins[cmd])
return false;
@@ -87,39 +94,52 @@ bool udev_builtin_run_once(enum udev_builtin_cmd cmd) {
}
enum udev_builtin_cmd udev_builtin_lookup(const char *command) {
- char name[UTIL_PATH_SIZE];
enum udev_builtin_cmd i;
- char *pos;
-
- strscpy(name, sizeof(name), command);
- pos = strchr(name, ' ');
- if (pos)
- pos[0] = '\0';
- for (i = 0; i < ELEMENTSOF(builtins); i++)
- if (builtins[i] && streq(builtins[i]->name, name))
+ size_t n;
+
+ assert(command);
+
+ command += strspn(command, WHITESPACE);
+ n = strcspn(command, WHITESPACE);
+ for (i = 0; i < _UDEV_BUILTIN_MAX; i++)
+ if (builtins[i] && strneq(builtins[i]->name, command, n))
return i;
- return UDEV_BUILTIN_MAX;
+
+ return _UDEV_BUILTIN_INVALID;
}
-int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) {
- char arg[UTIL_PATH_SIZE];
- int argc;
- char *argv[128];
+int udev_builtin_run(sd_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) {
+ _cleanup_strv_free_ char **argv = NULL;
+
+ assert(dev);
+ assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX);
+ assert(command);
if (!builtins[cmd])
return -EOPNOTSUPP;
+ argv = strv_split_full(command, NULL, SPLIT_QUOTES | SPLIT_RELAX);
+ if (!argv)
+ return -ENOMEM;
+
/* we need '0' here to reset the internal state */
optind = 0;
- strscpy(arg, sizeof(arg), command);
- udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv);
- return builtins[cmd]->cmd(dev, argc, argv, test);
+ return builtins[cmd]->cmd(dev, strv_length(argv), argv, test);
}
-int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) {
- udev_device_add_property(dev, key, val);
+int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val) {
+ int r;
+
+ assert(dev);
+ assert(key);
+
+ r = device_add_property(dev, key, val);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to add property '%s%s%s'",
+ key, val ? "=" : "", strempty(val));
if (test)
printf("%s=%s\n", key, val);
+
return 0;
}
diff --git a/src/udev/udev-builtin.h b/src/udev/udev-builtin.h
new file mode 100644
index 0000000000..e51eefbfb5
--- /dev/null
+++ b/src/udev/udev-builtin.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#pragma once
+
+#include <stdbool.h>
+
+#include "sd-device.h"
+
+enum udev_builtin_cmd {
+#if HAVE_BLKID
+ UDEV_BUILTIN_BLKID,
+#endif
+ UDEV_BUILTIN_BTRFS,
+ UDEV_BUILTIN_HWDB,
+ UDEV_BUILTIN_INPUT_ID,
+ UDEV_BUILTIN_KEYBOARD,
+#if HAVE_KMOD
+ UDEV_BUILTIN_KMOD,
+#endif
+ UDEV_BUILTIN_NET_ID,
+ UDEV_BUILTIN_NET_LINK,
+ UDEV_BUILTIN_PATH_ID,
+ UDEV_BUILTIN_USB_ID,
+#if HAVE_ACL
+ UDEV_BUILTIN_UACCESS,
+#endif
+ _UDEV_BUILTIN_MAX,
+ _UDEV_BUILTIN_INVALID = -1,
+};
+
+struct udev_builtin {
+ const char *name;
+ int (*cmd)(sd_device *dev, int argc, char *argv[], bool test);
+ const char *help;
+ int (*init)(void);
+ void (*exit)(void);
+ bool (*validate)(void);
+ bool run_once;
+};
+
+#if HAVE_BLKID
+extern const struct udev_builtin udev_builtin_blkid;
+#endif
+extern const struct udev_builtin udev_builtin_btrfs;
+extern const struct udev_builtin udev_builtin_hwdb;
+extern const struct udev_builtin udev_builtin_input_id;
+extern const struct udev_builtin udev_builtin_keyboard;
+#if HAVE_KMOD
+extern const struct udev_builtin udev_builtin_kmod;
+#endif
+extern const struct udev_builtin udev_builtin_net_id;
+extern const struct udev_builtin udev_builtin_net_setup_link;
+extern const struct udev_builtin udev_builtin_path_id;
+extern const struct udev_builtin udev_builtin_usb_id;
+#if HAVE_ACL
+extern const struct udev_builtin udev_builtin_uaccess;
+#endif
+
+void udev_builtin_init(void);
+void udev_builtin_exit(void);
+enum udev_builtin_cmd udev_builtin_lookup(const char *command);
+const char *udev_builtin_name(enum udev_builtin_cmd cmd);
+bool udev_builtin_run_once(enum udev_builtin_cmd cmd);
+int udev_builtin_run(sd_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test);
+void udev_builtin_list(void);
+bool udev_builtin_validate(void);
+int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val);
+int udev_builtin_hwdb_lookup(sd_device *dev, const char *prefix, const char *modalias,
+ const char *filter, bool test);
diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c
index efe7297f04..d90ebb7259 100644
--- a/src/udev/udev-ctrl.c
+++ b/src/udev/udev-ctrl.c
@@ -2,7 +2,6 @@
*
* libudev - interface to udev device information
*
- *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -21,8 +20,11 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "format-util.h"
+#include "io-util.h"
#include "socket-util.h"
-#include "udev.h"
+#include "strxcpyx.h"
+#include "udev-ctrl.h"
+#include "util.h"
/* wire protocol magic must match */
#define UDEV_CTRL_MAGIC 0xdead1dea
@@ -41,7 +43,7 @@ enum udev_ctrl_msg_type {
struct udev_ctrl_msg_wire {
char version[16];
- unsigned int magic;
+ unsigned magic;
enum udev_ctrl_msg_type type;
union {
int intval;
@@ -50,14 +52,13 @@ struct udev_ctrl_msg_wire {
};
struct udev_ctrl_msg {
- int refcount;
+ unsigned n_ref;
struct udev_ctrl_connection *conn;
struct udev_ctrl_msg_wire ctrl_msg_wire;
};
struct udev_ctrl {
- int refcount;
- struct udev *udev;
+ unsigned n_ref;
int sock;
union sockaddr_union saddr;
socklen_t addrlen;
@@ -67,26 +68,24 @@ struct udev_ctrl {
};
struct udev_ctrl_connection {
- int refcount;
+ unsigned n_ref;
struct udev_ctrl *uctrl;
int sock;
};
-struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) {
+struct udev_ctrl *udev_ctrl_new_from_fd(int fd) {
struct udev_ctrl *uctrl;
- const int on = 1;
int r;
uctrl = new0(struct udev_ctrl, 1);
- if (uctrl == NULL)
+ if (!uctrl)
return NULL;
- uctrl->refcount = 1;
- uctrl->udev = udev;
+ uctrl->n_ref = 1;
if (fd < 0) {
uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
if (uctrl->sock < 0) {
- log_error_errno(errno, "error getting socket: %m");
+ log_error_errno(errno, "Failed to create socket: %m");
udev_ctrl_unref(uctrl);
return NULL;
}
@@ -99,18 +98,21 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) {
* FIXME: remove it as soon as we can depend on this:
* http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=90c6bd34f884cd9cee21f1d152baf6c18bcac949
*/
- r = setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ r = setsockopt_int(uctrl->sock, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- log_warning_errno(errno, "could not set SO_PASSCRED: %m");
+ log_warning_errno(r, "Failed to set SO_PASSCRED: %m");
+
+ uctrl->saddr.un = (struct sockaddr_un) {
+ .sun_family = AF_UNIX,
+ .sun_path = "/run/udev/control",
+ };
- uctrl->saddr.un.sun_family = AF_LOCAL;
- strscpy(uctrl->saddr.un.sun_path, sizeof(uctrl->saddr.un.sun_path), "/run/udev/control");
uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un);
return uctrl;
}
-struct udev_ctrl *udev_ctrl_new(struct udev *udev) {
- return udev_ctrl_new_from_fd(udev, -1);
+struct udev_ctrl *udev_ctrl_new(void) {
+ return udev_ctrl_new_from_fd(-1);
}
int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
@@ -119,16 +121,16 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
if (!uctrl->bound) {
err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen);
if (err < 0 && errno == EADDRINUSE) {
- unlink(uctrl->saddr.un.sun_path);
+ (void) sockaddr_un_unlink(&uctrl->saddr.un);
err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen);
}
if (err < 0)
- return log_error_errno(errno, "bind failed: %m");
+ return log_error_errno(errno, "Failed to bind socket: %m");
err = listen(uctrl->sock, 0);
if (err < 0)
- return log_error_errno(errno, "listen failed: %m");
+ return log_error_errno(errno, "Failed to listen: %m");
uctrl->bound = true;
uctrl->cleanup_socket = true;
@@ -136,37 +138,26 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
return 0;
}
-struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) {
- return uctrl->udev;
-}
-
-static struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) {
- if (uctrl)
- uctrl->refcount++;
+static struct udev_ctrl *udev_ctrl_free(struct udev_ctrl *uctrl) {
+ assert(uctrl);
- return uctrl;
+ safe_close(uctrl->sock);
+ return mfree(uctrl);
}
-struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) {
- if (uctrl && -- uctrl->refcount == 0) {
- if (uctrl->sock >= 0)
- close(uctrl->sock);
- free(uctrl);
- }
-
- return NULL;
-}
+DEFINE_PRIVATE_TRIVIAL_REF_FUNC(struct udev_ctrl, udev_ctrl);
+DEFINE_TRIVIAL_UNREF_FUNC(struct udev_ctrl, udev_ctrl, udev_ctrl_free);
int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
- if (uctrl == NULL)
+ if (!uctrl)
return 0;
if (uctrl->cleanup_socket)
- unlink(uctrl->saddr.un.sun_path);
+ sockaddr_un_unlink(&uctrl->saddr.un);
return 0;
}
int udev_ctrl_get_fd(struct udev_ctrl *uctrl) {
- if (uctrl == NULL)
+ if (!uctrl)
return -EINVAL;
return uctrl->sock;
}
@@ -174,66 +165,54 @@ int udev_ctrl_get_fd(struct udev_ctrl *uctrl) {
struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) {
struct udev_ctrl_connection *conn;
struct ucred ucred = {};
- const int on = 1;
int r;
conn = new(struct udev_ctrl_connection, 1);
- if (conn == NULL)
+ if (!conn)
return NULL;
- conn->refcount = 1;
+ conn->n_ref = 1;
conn->uctrl = uctrl;
conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
if (conn->sock < 0) {
if (errno != EINTR)
- log_error_errno(errno, "unable to receive ctrl connection: %m");
+ log_error_errno(errno, "Failed to receive ctrl connection: %m");
goto err;
}
/* check peer credential of connection */
r = getpeercred(conn->sock, &ucred);
if (r < 0) {
- log_error_errno(r, "unable to receive credentials of ctrl connection: %m");
+ log_error_errno(r, "Failed to receive credentials of ctrl connection: %m");
goto err;
}
if (ucred.uid > 0) {
- log_error("sender uid="UID_FMT", message ignored", ucred.uid);
+ log_error("Sender uid="UID_FMT", message ignored", ucred.uid);
goto err;
}
/* enable receiving of the sender credentials in the messages */
- r = setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ r = setsockopt_int(conn->sock, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- log_warning_errno(errno, "could not set SO_PASSCRED: %m");
+ log_warning_errno(r, "Failed to set SO_PASSCRED: %m");
udev_ctrl_ref(uctrl);
return conn;
err:
- if (conn->sock >= 0)
- close(conn->sock);
+ safe_close(conn->sock);
return mfree(conn);
}
-struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn) {
- if (conn == NULL)
- return NULL;
- conn->refcount++;
- return conn;
-}
-
-struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) {
- if (conn && -- conn->refcount == 0) {
- if (conn->sock >= 0)
- close(conn->sock);
-
- udev_ctrl_unref(conn->uctrl);
+static struct udev_ctrl_connection *udev_ctrl_connection_free(struct udev_ctrl_connection *conn) {
+ assert(conn);
- free(conn);
- }
-
- return NULL;
+ safe_close(conn->sock);
+ udev_ctrl_unref(conn->uctrl);
+ return mfree(conn);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(struct udev_ctrl_connection, udev_ctrl_connection, udev_ctrl_connection_free);
+
static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout) {
struct udev_ctrl_msg_wire ctrl_msg_wire;
int err = 0;
@@ -243,7 +222,7 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
ctrl_msg_wire.magic = UDEV_CTRL_MAGIC;
ctrl_msg_wire.type = type;
- if (buf != NULL)
+ if (buf)
strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf);
else
ctrl_msg_wire.intval = intval;
@@ -268,7 +247,7 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
pfd[0].fd = uctrl->sock;
pfd[0].events = POLLIN;
r = poll(pfd, 1, timeout * MSEC_PER_SEC);
- if (r < 0) {
+ if (r < 0) {
if (errno == EINTR)
continue;
err = -errno;
@@ -335,9 +314,9 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
struct ucred *cred;
uctrl_msg = new0(struct udev_ctrl_msg, 1);
- if (uctrl_msg == NULL)
+ if (!uctrl_msg)
return NULL;
- uctrl_msg->refcount = 1;
+ uctrl_msg->n_ref = 1;
uctrl_msg->conn = conn;
udev_ctrl_connection_ref(conn);
@@ -350,16 +329,16 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
pfd[0].events = POLLIN;
r = poll(pfd, 1, 10000);
- if (r < 0) {
+ if (r < 0) {
if (errno == EINTR)
continue;
goto err;
} else if (r == 0) {
- log_error("timeout waiting for ctrl message");
+ log_error("Timeout waiting for ctrl message");
goto err;
} else {
if (!(pfd[0].revents & POLLIN)) {
- log_error_errno(errno, "ctrl connection error: %m");
+ log_error("Invalid ctrl connection: %m");
goto err;
}
}
@@ -367,12 +346,11 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
break;
}
- iov.iov_base = &uctrl_msg->ctrl_msg_wire;
- iov.iov_len = sizeof(struct udev_ctrl_msg_wire);
+ iov = IOVEC_MAKE(&uctrl_msg->ctrl_msg_wire, sizeof(struct udev_ctrl_msg_wire));
size = recvmsg(conn->sock, &smsg, 0);
- if (size < 0) {
- log_error_errno(errno, "unable to receive ctrl message: %m");
+ if (size < 0) {
+ log_error_errno(errno, "Failed to receive ctrl message: %m");
goto err;
}
@@ -380,20 +358,20 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
cmsg = CMSG_FIRSTHDR(&smsg);
- if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
- log_error("no sender credentials received, message ignored");
+ if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) {
+ log_error("No sender credentials received, ignoring message");
goto err;
}
cred = (struct ucred *) CMSG_DATA(cmsg);
if (cred->uid != 0) {
- log_error("sender uid="UID_FMT", message ignored", cred->uid);
+ log_error("Sender uid="UID_FMT", ignoring message", cred->uid);
goto err;
}
if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) {
- log_error("message magic 0x%08x doesn't match, ignore it", uctrl_msg->ctrl_msg_wire.magic);
+ log_error("Message magic 0x%08x doesn't match, ignoring", uctrl_msg->ctrl_msg_wire.magic);
goto err;
}
@@ -403,15 +381,15 @@ err:
return NULL;
}
-struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) {
- if (ctrl_msg && -- ctrl_msg->refcount == 0) {
- udev_ctrl_connection_unref(ctrl_msg->conn);
- free(ctrl_msg);
- }
+static struct udev_ctrl_msg *udev_ctrl_msg_free(struct udev_ctrl_msg *ctrl_msg) {
+ assert(ctrl_msg);
- return NULL;
+ udev_ctrl_connection_unref(ctrl_msg->conn);
+ return mfree(ctrl_msg);
}
+DEFINE_TRIVIAL_UNREF_FUNC(struct udev_ctrl_msg, udev_ctrl_msg, udev_ctrl_msg_free);
+
int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg) {
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL)
return ctrl_msg->ctrl_msg_wire.intval;
diff --git a/src/udev/udev-ctrl.h b/src/udev/udev-ctrl.h
new file mode 100644
index 0000000000..87021cb880
--- /dev/null
+++ b/src/udev/udev-ctrl.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#pragma once
+
+#include "macro.h"
+
+struct udev_ctrl;
+struct udev_ctrl *udev_ctrl_new(void);
+struct udev_ctrl *udev_ctrl_new_from_fd(int fd);
+int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl);
+struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl);
+int udev_ctrl_cleanup(struct udev_ctrl *uctrl);
+int udev_ctrl_get_fd(struct udev_ctrl *uctrl);
+int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout);
+int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout);
+int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout);
+
+struct udev_ctrl_connection;
+struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl);
+struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn);
+struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn);
+
+struct udev_ctrl_msg;
+struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn);
+struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg);
+const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_connection*, udev_ctrl_connection_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_msg*, udev_ctrl_msg_unref);
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index fd8406d959..e28d6a5d08 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -1,68 +1,85 @@
/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- *
- */
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <net/if.h>
-#include <poll.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <sys/prctl.h>
-#include <sys/signalfd.h>
-#include <sys/wait.h>
#include <unistd.h>
+#include "sd-event.h"
+
#include "alloc-util.h"
+#include "device-private.h"
+#include "device-util.h"
#include "fd-util.h"
#include "format-util.h"
+#include "libudev-util.h"
#include "netlink-util.h"
+#include "path-util.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "signal-util.h"
+#include "stdio-util.h"
#include "string-util.h"
+#include "strv.h"
+#include "strxcpyx.h"
+#include "udev-builtin.h"
+#include "udev-node.h"
+#include "udev-watch.h"
#include "udev.h"
typedef struct Spawn {
const char *cmd;
pid_t pid;
- usec_t timeout_warn;
- usec_t timeout;
+ usec_t timeout_warn_usec;
+ usec_t timeout_usec;
+ usec_t event_birth_usec;
bool accept_failure;
+ int fd_stdout;
+ int fd_stderr;
+ char *result;
+ size_t result_size;
+ size_t result_len;
} Spawn;
-struct udev_event *udev_event_new(struct udev_device *dev) {
- struct udev *udev = udev_device_get_udev(dev);
- struct udev_event *event;
+UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rtnl) {
+ UdevEvent *event;
+
+ assert(dev);
- event = new0(struct udev_event, 1);
- if (event == NULL)
+ event = new(UdevEvent, 1);
+ if (!event)
return NULL;
- event->dev = dev;
- event->udev = udev;
- udev_list_init(udev, &event->run_list, false);
- udev_list_init(udev, &event->seclabel_list, false);
- event->birth_usec = now(CLOCK_MONOTONIC);
+
+ *event = (UdevEvent) {
+ .dev = sd_device_ref(dev),
+ .birth_usec = now(CLOCK_MONOTONIC),
+ .exec_delay_usec = exec_delay_usec,
+ .rtnl = sd_netlink_ref(rtnl),
+ };
+
return event;
}
-void udev_event_unref(struct udev_event *event) {
- if (event == NULL)
- return;
+UdevEvent *udev_event_free(UdevEvent *event) {
+ if (!event)
+ return NULL;
+
+ sd_device_unref(event->dev);
+ sd_device_unref(event->dev_db_clone);
sd_netlink_unref(event->rtnl);
- udev_list_cleanup(&event->run_list);
- udev_list_cleanup(&event->seclabel_list);
+ hashmap_free_free_key(event->run_list);
+ hashmap_free_free_free(event->seclabel_list);
free(event->program_result);
free(event->name);
- free(event);
+
+ return mfree(event);
}
enum subst_type {
- SUBST_UNKNOWN,
SUBST_DEVNODE,
SUBST_ATTR,
SUBST_ENV,
@@ -81,68 +98,103 @@ enum subst_type {
SUBST_SYS,
};
-static size_t subst_format_var(struct udev_event *event, struct udev_device *dev,
- enum subst_type type, char *attr,
- char *dest, size_t l) {
+struct subst_map_entry {
+ const char *name;
+ const char fmt;
+ enum subst_type type;
+};
+
+static const struct subst_map_entry map[] = {
+ { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE },
+ { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE },
+ { .name = "attr", .fmt = 's', .type = SUBST_ATTR },
+ { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR },
+ { .name = "env", .fmt = 'E', .type = SUBST_ENV },
+ { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL },
+ { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER },
+ { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER },
+ { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH },
+ { .name = "id", .fmt = 'b', .type = SUBST_ID },
+ { .name = "major", .fmt = 'M', .type = SUBST_MAJOR },
+ { .name = "minor", .fmt = 'm', .type = SUBST_MINOR },
+ { .name = "result", .fmt = 'c', .type = SUBST_RESULT },
+ { .name = "parent", .fmt = 'P', .type = SUBST_PARENT },
+ { .name = "name", .fmt = 'D', .type = SUBST_NAME },
+ { .name = "links", .fmt = 'L', .type = SUBST_LINKS },
+ { .name = "root", .fmt = 'r', .type = SUBST_ROOT },
+ { .name = "sys", .fmt = 'S', .type = SUBST_SYS },
+};
+
+static ssize_t subst_format_var(UdevEvent *event,
+ const struct subst_map_entry *entry, char *attr,
+ char *dest, size_t l) {
+ sd_device *parent, *dev = event->dev;
+ const char *val = NULL;
char *s = dest;
+ dev_t devnum;
+ int r;
+
+ assert(entry);
- switch (type) {
+ switch (entry->type) {
case SUBST_DEVPATH:
- l = strpcpy(&s, l, udev_device_get_devpath(dev));
+ r = sd_device_get_devpath(dev, &val);
+ if (r < 0)
+ return r;
+ l = strpcpy(&s, l, val);
break;
case SUBST_KERNEL:
- l = strpcpy(&s, l, udev_device_get_sysname(dev));
+ r = sd_device_get_sysname(dev, &val);
+ if (r < 0)
+ return r;
+ l = strpcpy(&s, l, val);
break;
case SUBST_KERNEL_NUMBER:
- if (udev_device_get_sysnum(dev) == NULL)
- break;
- l = strpcpy(&s, l, udev_device_get_sysnum(dev));
+ r = sd_device_get_sysnum(dev, &val);
+ if (r < 0)
+ return r == -ENOENT ? 0 : r;
+ l = strpcpy(&s, l, val);
break;
case SUBST_ID:
- if (event->dev_parent == NULL)
- break;
- l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent));
- break;
- case SUBST_DRIVER: {
- const char *driver;
-
- if (event->dev_parent == NULL)
- break;
-
- driver = udev_device_get_driver(event->dev_parent);
- if (driver == NULL)
- break;
- l = strpcpy(&s, l, driver);
+ if (!event->dev_parent)
+ return 0;
+ r = sd_device_get_sysname(event->dev_parent, &val);
+ if (r < 0)
+ return r;
+ l = strpcpy(&s, l, val);
break;
- }
- case SUBST_MAJOR: {
- char num[UTIL_PATH_SIZE];
-
- sprintf(num, "%u", major(udev_device_get_devnum(dev)));
- l = strpcpy(&s, l, num);
+ case SUBST_DRIVER:
+ if (!event->dev_parent)
+ return 0;
+ r = sd_device_get_driver(event->dev_parent, &val);
+ if (r < 0)
+ return r == -ENOENT ? 0 : r;
+ l = strpcpy(&s, l, val);
break;
- }
+ case SUBST_MAJOR:
case SUBST_MINOR: {
- char num[UTIL_PATH_SIZE];
+ char buf[DECIMAL_STR_MAX(unsigned)];
- sprintf(num, "%u", minor(udev_device_get_devnum(dev)));
- l = strpcpy(&s, l, num);
+ r = sd_device_get_devnum(dev, &devnum);
+ if (r < 0 && r != -ENOENT)
+ return r;
+ xsprintf(buf, "%u", r < 0 ? 0 : entry->type == SUBST_MAJOR ? major(devnum) : minor(devnum));
+ l = strpcpy(&s, l, buf);
break;
}
case SUBST_RESULT: {
char *rest;
int i;
- if (event->program_result == NULL)
- break;
+ if (!event->program_result)
+ return 0;
+
/* get part of the result string */
i = 0;
- if (attr != NULL)
+ if (attr)
i = strtoul(attr, &rest, 10);
if (i > 0) {
- char result[UTIL_PATH_SIZE];
- char tmp[UTIL_PATH_SIZE];
- char *cpos;
+ char result[UTIL_PATH_SIZE], tmp[UTIL_PATH_SIZE], *cpos;
strscpy(result, sizeof(result), event->program_result);
cpos = result;
@@ -166,88 +218,79 @@ static size_t subst_format_var(struct udev_event *event, struct udev_device *dev
cpos[0] = '\0';
}
l = strpcpy(&s, l, tmp);
- } else {
+ } else
l = strpcpy(&s, l, event->program_result);
- }
break;
}
case SUBST_ATTR: {
- const char *value = NULL;
char vbuf[UTIL_NAME_SIZE];
size_t len;
int count;
- if (attr == NULL) {
- log_error("missing file parameter for attr");
- break;
- }
+ if (!attr)
+ return -EINVAL;
/* try to read the value specified by "[dmi/id]product_name" */
- if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0)
- value = vbuf;
+ if (util_resolve_subsys_kernel(attr, vbuf, sizeof(vbuf), true) == 0)
+ val = vbuf;
/* try to read the attribute the device */
- if (value == NULL)
- value = udev_device_get_sysattr_value(event->dev, attr);
+ if (!val)
+ (void) sd_device_get_sysattr_value(dev, attr, &val);
/* try to read the attribute of the parent device, other matches have selected */
- if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev)
- value = udev_device_get_sysattr_value(event->dev_parent, attr);
+ if (!val && event->dev_parent && event->dev_parent != dev)
+ (void) sd_device_get_sysattr_value(event->dev_parent, attr, &val);
- if (value == NULL)
- break;
+ if (!val)
+ return 0;
/* strip trailing whitespace, and replace unwanted characters */
- if (value != vbuf)
- strscpy(vbuf, sizeof(vbuf), value);
+ if (val != vbuf)
+ strscpy(vbuf, sizeof(vbuf), val);
len = strlen(vbuf);
while (len > 0 && isspace(vbuf[--len]))
vbuf[len] = '\0';
count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
if (count > 0)
- log_debug("%i character(s) replaced" , count);
+ log_device_debug(dev, "%i character(s) replaced", count);
l = strpcpy(&s, l, vbuf);
break;
}
- case SUBST_PARENT: {
- struct udev_device *dev_parent;
- const char *devnode;
-
- dev_parent = udev_device_get_parent(event->dev);
- if (dev_parent == NULL)
- break;
- devnode = udev_device_get_devnode(dev_parent);
- if (devnode != NULL)
- l = strpcpy(&s, l, devnode + STRLEN("/dev/"));
+ case SUBST_PARENT:
+ r = sd_device_get_parent(dev, &parent);
+ if (r < 0)
+ return r == -ENODEV ? 0 : r;
+ r = sd_device_get_devname(parent, &val);
+ if (r < 0)
+ return r == -ENOENT ? 0 : r;
+ l = strpcpy(&s, l, val + STRLEN("/dev/"));
break;
- }
case SUBST_DEVNODE:
- if (udev_device_get_devnode(dev) != NULL)
- l = strpcpy(&s, l, udev_device_get_devnode(dev));
+ r = sd_device_get_devname(dev, &val);
+ if (r < 0)
+ return r == -ENOENT ? 0 : r;
+ l = strpcpy(&s, l, val);
break;
case SUBST_NAME:
- if (event->name != NULL)
+ if (event->name)
l = strpcpy(&s, l, event->name);
- else if (udev_device_get_devnode(dev) != NULL)
- l = strpcpy(&s, l,
- udev_device_get_devnode(dev) + STRLEN("/dev/"));
- else
- l = strpcpy(&s, l, udev_device_get_sysname(dev));
+ else if (sd_device_get_devname(dev, &val) >= 0)
+ l = strpcpy(&s, l, val + STRLEN("/dev/"));
+ else {
+ r = sd_device_get_sysname(dev, &val);
+ if (r < 0)
+ return r;
+ l = strpcpy(&s, l, val);
+ }
break;
- case SUBST_LINKS: {
- struct udev_list_entry *list_entry;
-
- list_entry = udev_device_get_devlinks_list_entry(dev);
- if (list_entry == NULL)
- break;
- l = strpcpy(&s, l,
- udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
- udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
- l = strpcpyl(&s, l, " ",
- udev_list_entry_get_name(list_entry) + STRLEN("/dev/"),
- NULL);
+ case SUBST_LINKS:
+ FOREACH_DEVICE_DEVLINK(dev, val)
+ if (s == dest)
+ l = strpcpy(&s, l, val + STRLEN("/dev/"));
+ else
+ l = strpcpyl(&s, l, " ", val + STRLEN("/dev/"), NULL);
break;
- }
case SUBST_ROOT:
l = strpcpy(&s, l, "/dev");
break;
@@ -255,73 +298,47 @@ static size_t subst_format_var(struct udev_event *event, struct udev_device *dev
l = strpcpy(&s, l, "/sys");
break;
case SUBST_ENV:
- if (attr == NULL) {
- break;
- } else {
- const char *value;
-
- value = udev_device_get_property_value(event->dev, attr);
- if (value == NULL)
- break;
- l = strpcpy(&s, l, value);
- break;
- }
- default:
- log_error("unknown substitution type=%i", type);
+ if (!attr)
+ return 0;
+ r = sd_device_get_property_value(dev, attr, &val);
+ if (r < 0)
+ return r == -ENOENT ? 0 : r;
+ l = strpcpy(&s, l, val);
break;
+ default:
+ assert_not_reached("Unknown format substitution type");
}
return s - dest;
}
-size_t udev_event_apply_format(struct udev_event *event,
- const char *src, char *dest, size_t size,
- bool replace_whitespace) {
- struct udev_device *dev = event->dev;
- static const struct subst_map {
- const char *name;
- const char fmt;
- enum subst_type type;
- } map[] = {
- { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE },
- { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE },
- { .name = "attr", .fmt = 's', .type = SUBST_ATTR },
- { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR },
- { .name = "env", .fmt = 'E', .type = SUBST_ENV },
- { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL },
- { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER },
- { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER },
- { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH },
- { .name = "id", .fmt = 'b', .type = SUBST_ID },
- { .name = "major", .fmt = 'M', .type = SUBST_MAJOR },
- { .name = "minor", .fmt = 'm', .type = SUBST_MINOR },
- { .name = "result", .fmt = 'c', .type = SUBST_RESULT },
- { .name = "parent", .fmt = 'P', .type = SUBST_PARENT },
- { .name = "name", .fmt = 'D', .type = SUBST_NAME },
- { .name = "links", .fmt = 'L', .type = SUBST_LINKS },
- { .name = "root", .fmt = 'r', .type = SUBST_ROOT },
- { .name = "sys", .fmt = 'S', .type = SUBST_SYS },
- };
+ssize_t udev_event_apply_format(UdevEvent *event,
+ const char *src, char *dest, size_t size,
+ bool replace_whitespace) {
const char *from;
char *s;
size_t l;
- assert(dev);
+ assert(event);
+ assert(event->dev);
+ assert(src);
+ assert(dest);
+ assert(size > 0);
from = src;
s = dest;
l = size;
for (;;) {
- enum subst_type type = SUBST_UNKNOWN;
- char attrbuf[UTIL_PATH_SIZE];
- char *attr = NULL;
- size_t subst_len;
+ const struct subst_map_entry *entry = NULL;
+ char attrbuf[UTIL_PATH_SIZE], *attr;
+ bool format_dollar = false;
+ ssize_t subst_len;
while (from[0] != '\0') {
if (from[0] == '$') {
/* substitute named variable */
- unsigned int i;
+ unsigned i;
if (from[1] == '$') {
from++;
@@ -330,14 +347,15 @@ size_t udev_event_apply_format(struct udev_event *event,
for (i = 0; i < ELEMENTSOF(map); i++) {
if (startswith(&from[1], map[i].name)) {
- type = map[i].type;
+ entry = &map[i];
from += strlen(map[i].name)+1;
+ format_dollar = true;
goto subst;
}
}
} else if (from[0] == '%') {
/* substitute format char */
- unsigned int i;
+ unsigned i;
if (from[1] == '%') {
from++;
@@ -346,7 +364,7 @@ size_t udev_event_apply_format(struct udev_event *event,
for (i = 0; i < ELEMENTSOF(map); i++) {
if (from[1] == map[i].fmt) {
- type = map[i].type;
+ entry = &map[i];
from += 2;
goto subst;
}
@@ -366,7 +384,7 @@ copy:
subst:
/* extract possible $format{attr} */
if (from[0] == '{') {
- unsigned int i;
+ unsigned i;
from++;
for (i = 0; from[i] != '}'; i++)
@@ -381,14 +399,21 @@ subst:
attrbuf[i] = '\0';
from += i+1;
attr = attrbuf;
- } else {
+ } else
attr = NULL;
- }
- subst_len = subst_format_var(event, dev, type, attr, s, l);
+ subst_len = subst_format_var(event, entry, attr, s, l);
+ if (subst_len < 0) {
+ if (format_dollar)
+ log_device_warning_errno(event->dev, subst_len, "Failed to substitute variable '$%s', ignoring: %m", entry->name);
+ else
+ log_device_warning_errno(event->dev, subst_len, "Failed to apply format '%%%c', ignoring: %m", entry->fmt);
+
+ continue;
+ }
/* SUBST_RESULT handles spaces itself */
- if (replace_whitespace && type != SUBST_RESULT)
+ if (replace_whitespace && entry->type != SUBST_RESULT)
/* util_replace_whitespace can replace in-place,
* and does nothing if subst_len == 0
*/
@@ -404,210 +429,75 @@ out:
return l;
}
-static int spawn_exec(struct udev_event *event,
- const char *cmd, char *const argv[], char **envp,
- int fd_stdout, int fd_stderr) {
- _cleanup_close_ int fd = -1;
- int r;
-
- /* discard child output or connect to pipe */
- fd = open("/dev/null", O_RDWR);
- if (fd >= 0) {
- r = dup2(fd, STDIN_FILENO);
- if (r < 0)
- log_warning_errno(errno, "redirecting stdin failed: %m");
-
- if (fd_stdout < 0) {
- r = dup2(fd, STDOUT_FILENO);
- if (r < 0)
- log_warning_errno(errno, "redirecting stdout failed: %m");
- }
-
- if (fd_stderr < 0) {
- r = dup2(fd, STDERR_FILENO);
- if (r < 0)
- log_warning_errno(errno, "redirecting stderr failed: %m");
- }
- } else
- log_warning_errno(errno, "open /dev/null failed: %m");
-
- /* connect pipes to std{out,err} */
- if (fd_stdout >= 0) {
- r = dup2(fd_stdout, STDOUT_FILENO);
- if (r < 0)
- log_warning_errno(errno, "redirecting stdout failed: %m");
-
- fd_stdout = safe_close(fd_stdout);
- }
-
- if (fd_stderr >= 0) {
- r = dup2(fd_stderr, STDERR_FILENO);
- if (r < 0)
- log_warning_errno(errno, "redirecting stdout failed: %m");
-
- fd_stderr = safe_close(fd_stderr);
- }
-
- /* terminate child in case parent goes away */
- prctl(PR_SET_PDEATHSIG, SIGTERM);
-
- /* restore sigmask before exec */
- (void) reset_signal_mask();
-
- execve(argv[0], argv, envp);
-
- /* exec failed */
- return log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd);
-}
-
-static void spawn_read(struct udev_event *event,
- usec_t timeout_usec,
- const char *cmd,
- int fd_stdout, int fd_stderr,
- char *result, size_t ressize) {
- _cleanup_close_ int fd_ep = -1;
- struct epoll_event ep_outpipe = {
- .events = EPOLLIN,
- .data.ptr = &fd_stdout,
- };
- struct epoll_event ep_errpipe = {
- .events = EPOLLIN,
- .data.ptr = &fd_stderr,
- };
- size_t respos = 0;
- int r;
+static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Spawn *spawn = userdata;
+ char buf[4096], *p;
+ size_t size;
+ ssize_t l;
- /* read from child if requested */
- if (fd_stdout < 0 && fd_stderr < 0)
- return;
+ assert(spawn);
+ assert(fd == spawn->fd_stdout || fd == spawn->fd_stderr);
+ assert(!spawn->result || spawn->result_len < spawn->result_size);
- fd_ep = epoll_create1(EPOLL_CLOEXEC);
- if (fd_ep < 0) {
- log_error_errno(errno, "error creating epoll fd: %m");
- return;
+ if (fd == spawn->fd_stdout && spawn->result) {
+ p = spawn->result + spawn->result_len;
+ size = spawn->result_size - spawn->result_len;
+ } else {
+ p = buf;
+ size = sizeof(buf);
}
- if (fd_stdout >= 0) {
- r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe);
- if (r < 0) {
- log_error_errno(errno, "fail to add stdout fd to epoll: %m");
- return;
- }
- }
+ l = read(fd, p, size - 1);
+ if (l < 0) {
+ if (errno != EAGAIN)
+ log_error_errno(errno, "Failed to read stdout of '%s': %m", spawn->cmd);
- if (fd_stderr >= 0) {
- r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe);
- if (r < 0) {
- log_error_errno(errno, "fail to add stderr fd to epoll: %m");
- return;
- }
+ return 0;
}
- /* read child output */
- while (fd_stdout >= 0 || fd_stderr >= 0) {
- int timeout;
- int fdcount;
- struct epoll_event ev[4];
- int i;
-
- if (timeout_usec > 0) {
- usec_t age_usec;
-
- age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
- if (age_usec >= timeout_usec) {
- log_error("timeout '%s'", cmd);
- return;
- }
- timeout = ((timeout_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC;
- } else {
- timeout = -1;
- }
-
- fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- log_error_errno(errno, "failed to poll: %m");
- return;
- } else if (fdcount == 0) {
- log_error("timeout '%s'", cmd);
- return;
- }
-
- for (i = 0; i < fdcount; i++) {
- int *fd = (int *)ev[i].data.ptr;
+ p[l] = '\0';
+ if (fd == spawn->fd_stdout && spawn->result)
+ spawn->result_len += l;
- if (*fd < 0)
- continue;
+ /* Log output only if we watch stderr. */
+ if (l > 0 && spawn->fd_stderr >= 0) {
+ _cleanup_strv_free_ char **v = NULL;
+ char **q;
- if (ev[i].events & EPOLLIN) {
- ssize_t count;
- char buf[4096];
+ v = strv_split_newlines(p);
+ if (!v)
+ return 0;
- count = read(*fd, buf, sizeof(buf)-1);
- if (count <= 0)
- continue;
- buf[count] = '\0';
-
- /* store stdout result */
- if (result != NULL && *fd == fd_stdout) {
- if (respos + count < ressize) {
- memcpy(&result[respos], buf, count);
- respos += count;
- } else {
- log_error("'%s' ressize %zu too short", cmd, ressize);
- }
- }
-
- /* log debug output only if we watch stderr */
- if (fd_stderr >= 0) {
- char *pos;
- char *line;
-
- pos = buf;
- while ((line = strsep(&pos, "\n"))) {
- if (pos != NULL || line[0] != '\0')
- log_debug("'%s'(%s) '%s'", cmd, *fd == fd_stdout ? "out" : "err" , line);
- }
- }
- } else if (ev[i].events & EPOLLHUP) {
- r = epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL);
- if (r < 0) {
- log_error_errno(errno, "failed to remove fd from epoll: %m");
- return;
- }
- *fd = -1;
- }
- }
+ STRV_FOREACH(q, v)
+ log_debug("'%s'(%s) '%s'", spawn->cmd,
+ fd == spawn->fd_stdout ? "out" : "err", *q);
}
- /* return the child's stdout string */
- if (result != NULL)
- result[respos] = '\0';
+ return 0;
}
static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
Spawn *spawn = userdata;
- char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
+ char timeout[FORMAT_TIMESPAN_MAX];
assert(spawn);
kill_and_sigcont(spawn->pid, SIGKILL);
- log_error("spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid,
- format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
+ log_error("Spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid,
+ format_timespan(timeout, sizeof(timeout), spawn->timeout_usec, USEC_PER_SEC));
return 1;
}
static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
Spawn *spawn = userdata;
- char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
+ char timeout[FORMAT_TIMESPAN_MAX];
assert(spawn);
- log_warning("spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid,
- format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
+ log_warning("Spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid,
+ format_timespan(timeout, sizeof(timeout), spawn->timeout_warn_usec, USEC_PER_SEC));
return 1;
}
@@ -624,11 +514,10 @@ static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userd
sd_event_exit(sd_event_source_get_event(s), 0);
return 1;
- } else if (spawn->accept_failure)
- log_debug("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
- else
- log_warning("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
+ }
+ log_full(spawn->accept_failure ? LOG_DEBUG : LOG_WARNING,
+ "Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
break;
case CLD_KILLED:
case CLD_DUMPED:
@@ -644,49 +533,52 @@ static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userd
return 1;
}
-static int spawn_wait(struct udev_event *event,
- usec_t timeout_usec,
- usec_t timeout_warn_usec,
- const char *cmd, pid_t pid,
- bool accept_failure) {
- Spawn spawn = {
- .cmd = cmd,
- .pid = pid,
- .accept_failure = accept_failure,
- };
+static int spawn_wait(Spawn *spawn) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
int r, ret;
+ assert(spawn);
+
r = sd_event_new(&e);
if (r < 0)
return r;
- if (timeout_usec > 0) {
+ if (spawn->timeout_usec > 0) {
usec_t usec, age_usec;
usec = now(CLOCK_MONOTONIC);
- age_usec = usec - event->birth_usec;
- if (age_usec < timeout_usec) {
- if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) {
- spawn.timeout_warn = timeout_warn_usec - age_usec;
+ age_usec = usec - spawn->event_birth_usec;
+ if (age_usec < spawn->timeout_usec) {
+ if (spawn->timeout_warn_usec > 0 &&
+ spawn->timeout_warn_usec < spawn->timeout_usec &&
+ spawn->timeout_warn_usec > age_usec) {
+ spawn->timeout_warn_usec -= age_usec;
r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
- usec + spawn.timeout_warn, USEC_PER_SEC,
- on_spawn_timeout_warning, &spawn);
+ usec + spawn->timeout_warn_usec, USEC_PER_SEC,
+ on_spawn_timeout_warning, spawn);
if (r < 0)
return r;
}
- spawn.timeout = timeout_usec - age_usec;
+ spawn->timeout_usec -= age_usec;
r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
- usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn);
+ usec + spawn->timeout_usec, USEC_PER_SEC, on_spawn_timeout, spawn);
if (r < 0)
return r;
}
}
- r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn);
+ r = sd_event_add_io(e, NULL, spawn->fd_stdout, EPOLLIN, on_spawn_io, spawn);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_io(e, NULL, spawn->fd_stderr, EPOLLIN, on_spawn_io, spawn);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_child(e, NULL, spawn->pid, WEXITED, on_spawn_sigchld, spawn);
if (r < 0)
return r;
@@ -701,257 +593,309 @@ static int spawn_wait(struct udev_event *event,
return ret;
}
-int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) {
- int i = 0;
- char *pos;
-
- if (strchr(cmd, ' ') == NULL) {
- argv[i++] = cmd;
- goto out;
- }
-
- pos = cmd;
- while (pos != NULL && pos[0] != '\0') {
- if (IN_SET(pos[0], '\'', '"')) {
- /* do not separate quotes or double quotes */
- char delim[2] = { pos[0], '\0' };
-
- pos++;
- argv[i] = strsep(&pos, delim);
- if (pos != NULL)
- while (pos[0] == ' ')
- pos++;
- } else {
- argv[i] = strsep(&pos, " ");
- if (pos != NULL)
- while (pos[0] == ' ')
- pos++;
- }
- i++;
- }
-out:
- argv[i] = NULL;
- if (argc)
- *argc = i;
- return 0;
-}
-
-int udev_event_spawn(struct udev_event *event,
+int udev_event_spawn(UdevEvent *event,
usec_t timeout_usec,
- usec_t timeout_warn_usec,
bool accept_failure,
const char *cmd,
char *result, size_t ressize) {
- int outpipe[2] = {-1, -1};
- int errpipe[2] = {-1, -1};
+ _cleanup_close_pair_ int outpipe[2] = {-1, -1}, errpipe[2] = {-1, -1};
+ _cleanup_strv_free_ char **argv = NULL;
+ char **envp = NULL;
+ Spawn spawn;
pid_t pid;
- int err = 0;
+ int r;
+
+ assert(event);
+ assert(event->dev);
+ assert(result || ressize == 0);
/* pipes from child to parent */
- if (result != NULL || log_get_max_level() >= LOG_INFO) {
- if (pipe2(outpipe, O_NONBLOCK) != 0) {
- err = log_error_errno(errno, "pipe failed: %m");
- goto out;
- }
- }
- if (log_get_max_level() >= LOG_INFO) {
- if (pipe2(errpipe, O_NONBLOCK) != 0) {
- err = log_error_errno(errno, "pipe failed: %m");
- goto out;
- }
+ if (result || log_get_max_level() >= LOG_INFO)
+ if (pipe2(outpipe, O_NONBLOCK|O_CLOEXEC) != 0)
+ return log_error_errno(errno, "Failed to create pipe for command '%s': %m", cmd);
+
+ if (log_get_max_level() >= LOG_INFO)
+ if (pipe2(errpipe, O_NONBLOCK|O_CLOEXEC) != 0)
+ return log_error_errno(errno, "Failed to create pipe for command '%s': %m", cmd);
+
+ argv = strv_split_full(cmd, NULL, SPLIT_QUOTES|SPLIT_RELAX);
+ if (!argv)
+ return log_oom();
+
+ if (isempty(argv[0]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid command '%s'", cmd);
+
+ /* allow programs in /usr/lib/udev/ to be called without the path */
+ if (!path_is_absolute(argv[0])) {
+ char *program;
+
+ program = path_join(UDEVLIBEXECDIR, argv[0]);
+ if (!program)
+ return log_oom();
+
+ free_and_replace(argv[0], program);
}
- err = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_LOG, &pid);
- if (err < 0)
- goto out;
- if (err == 0) {
- char arg[UTIL_PATH_SIZE];
- char *argv[128];
- char program[UTIL_PATH_SIZE];
-
- /* child closes parent's ends of pipes */
- outpipe[READ_END] = safe_close(outpipe[READ_END]);
- errpipe[READ_END] = safe_close(errpipe[READ_END]);
-
- strscpy(arg, sizeof(arg), cmd);
- udev_build_argv(event->udev, arg, NULL, argv);
-
- /* allow programs in /usr/lib/udev/ to be called without the path */
- if (argv[0][0] != '/') {
- strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL);
- argv[0] = program;
- }
+ r = device_get_properties_strv(event->dev, &envp);
+ if (r < 0)
+ return log_device_error_errno(event->dev, r, "Failed to get device properties");
+
+ log_debug("Starting '%s'", cmd);
- log_debug("starting '%s'", cmd);
+ r = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to fork() to execute command '%s': %m", cmd);
+ if (r == 0) {
+ if (rearrange_stdio(-1, outpipe[WRITE_END], errpipe[WRITE_END]) < 0)
+ _exit(EXIT_FAILURE);
- spawn_exec(event, cmd, argv, udev_device_get_properties_envp(event->dev),
- outpipe[WRITE_END], errpipe[WRITE_END]);
+ (void) close_all_fds(NULL, 0);
+ (void) rlimit_nofile_safe();
- _exit(2);
+ execve(argv[0], argv, envp);
+ _exit(EXIT_FAILURE);
}
/* parent closed child's ends of pipes */
outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]);
errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]);
- spawn_read(event,
- timeout_usec,
- cmd,
- outpipe[READ_END], errpipe[READ_END],
- result, ressize);
+ spawn = (Spawn) {
+ .cmd = cmd,
+ .pid = pid,
+ .accept_failure = accept_failure,
+ .timeout_warn_usec = udev_warn_timeout(timeout_usec),
+ .timeout_usec = timeout_usec,
+ .event_birth_usec = event->birth_usec,
+ .fd_stdout = outpipe[READ_END],
+ .fd_stderr = errpipe[READ_END],
+ .result = result,
+ .result_size = ressize,
+ };
+ r = spawn_wait(&spawn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to wait spawned command '%s': %m", cmd);
- err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure);
+ if (result)
+ result[spawn.result_len] = '\0';
-out:
- if (outpipe[READ_END] >= 0)
- close(outpipe[READ_END]);
- if (outpipe[WRITE_END] >= 0)
- close(outpipe[WRITE_END]);
- if (errpipe[READ_END] >= 0)
- close(errpipe[READ_END]);
- if (errpipe[WRITE_END] >= 0)
- close(errpipe[WRITE_END]);
- return err;
+ return r;
}
-static int rename_netif(struct udev_event *event) {
- struct udev_device *dev = event->dev;
+static int rename_netif(UdevEvent *event) {
+ sd_device *dev = event->dev;
+ const char *action, *oldname;
char name[IFNAMSIZ];
- const char *oldname;
- int r;
+ int ifindex, r;
+
+ if (!event->name)
+ return 0; /* No new name is requested. */
- oldname = udev_device_get_sysname(dev);
+ r = sd_device_get_sysname(dev, &oldname);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get sysname: %m");
+
+ if (streq(event->name, oldname))
+ return 0; /* The interface name is already requested name. */
+
+ r = sd_device_get_property_value(dev, "ACTION", &action);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get property 'ACTION': %m");
+
+ if (!streq(action, "add"))
+ return 0; /* Rename the interface only when it is added. */
+
+ r = sd_device_get_ifindex(dev, &ifindex);
+ if (r == -ENOENT)
+ return 0; /* Device is not a network interface. */
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get ifindex: %m");
strscpy(name, IFNAMSIZ, event->name);
+ r = rtnl_set_link_name(&event->rtnl, ifindex, name);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to rename network interface %i from '%s' to '%s': %m", ifindex, oldname, name);
- r = rtnl_set_link_name(&event->rtnl, udev_device_get_ifindex(dev), name);
+ r = device_rename(dev, event->name);
if (r < 0)
- return log_error_errno(r, "Error changing net interface name '%s' to '%s': %m", oldname, name);
+ return log_warning_errno(r, "Network interface %i is renamed from '%s' to '%s', but could not update sd_device object: %m", ifindex, oldname, name);
- log_debug("renamed network interface '%s' to '%s'", oldname, name);
+ log_device_debug(dev, "Network interface %i is renamed from '%s' to '%s'", ifindex, oldname, name);
- return 0;
+ return 1;
}
-void udev_event_execute_rules(struct udev_event *event,
- usec_t timeout_usec, usec_t timeout_warn_usec,
- struct udev_list *properties_list,
- struct udev_rules *rules) {
- struct udev_device *dev = event->dev;
+static int update_devnode(UdevEvent *event) {
+ sd_device *dev = event->dev;
+ const char *action;
+ bool apply;
+ int r;
- if (udev_device_get_subsystem(dev) == NULL)
- return;
+ r = sd_device_get_devnum(dev, NULL);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get devnum: %m");
- if (streq(udev_device_get_action(dev), "remove")) {
- udev_device_read_db(dev);
- udev_device_tag_index(dev, NULL, false);
- udev_device_delete_db(dev);
+ /* remove/update possible left-over symlinks from old database entry */
+ if (event->dev_db_clone)
+ (void) udev_node_update_old_links(dev, event->dev_db_clone);
- if (major(udev_device_get_devnum(dev)) != 0)
- udev_watch_end(event->udev, dev);
+ if (!event->owner_set) {
+ r = device_get_devnode_uid(dev, &event->uid);
+ if (r < 0 && r != -ENOENT)
+ return log_device_error_errno(dev, r, "Failed to get devnode UID: %m");
+ }
- udev_rules_apply_to_event(rules, event,
- timeout_usec, timeout_warn_usec,
- properties_list);
+ if (!event->group_set) {
+ r = device_get_devnode_gid(dev, &event->gid);
+ if (r < 0 && r != -ENOENT)
+ return log_device_error_errno(dev, r, "Failed to get devnode GID: %m");
+ }
- if (major(udev_device_get_devnum(dev)) != 0)
- udev_node_remove(dev);
- } else {
- event->dev_db = udev_device_clone_with_db(dev);
- if (event->dev_db != NULL) {
- /* disable watch during event processing */
- if (major(udev_device_get_devnum(dev)) != 0)
- udev_watch_end(event->udev, event->dev_db);
-
- if (major(udev_device_get_devnum(dev)) == 0 &&
- streq(udev_device_get_action(dev), "move"))
- udev_device_copy_properties(dev, event->dev_db);
+ if (!event->mode_set) {
+ r = device_get_devnode_mode(dev, &event->mode);
+ if (r < 0 && r != -ENOENT)
+ return log_device_error_errno(dev, r, "Failed to get devnode mode: %m");
+ if (r == -ENOENT) {
+ if (event->gid > 0)
+ /* default 0660 if a group is assigned */
+ event->mode = 0660;
+ else
+ /* default 0600 */
+ event->mode = 0600;
}
+ }
+
+ r = sd_device_get_property_value(dev, "ACTION", &action);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get property 'ACTION': %m");
- udev_rules_apply_to_event(rules, event,
- timeout_usec, timeout_warn_usec,
- properties_list);
+ apply = streq(action, "add") || event->owner_set || event->group_set || event->mode_set;
+ return udev_node_add(dev, apply, event->mode, event->uid, event->gid, event->seclabel_list);
+}
- /* rename a new network interface, if needed */
- if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") &&
- event->name != NULL && !streq(event->name, udev_device_get_sysname(dev))) {
- int r;
+static void event_execute_rules_on_remove(
+ UdevEvent *event,
+ usec_t timeout_usec,
+ Hashmap *properties_list,
+ UdevRules *rules) {
- r = rename_netif(event);
- if (r < 0)
- log_warning_errno(r, "could not rename interface '%d' from '%s' to '%s': %m", udev_device_get_ifindex(dev),
- udev_device_get_sysname(dev), event->name);
- else {
- r = udev_device_rename(dev, event->name);
+ sd_device *dev = event->dev;
+ int r;
+
+ r = device_read_db_internal(dev, true);
+ if (r < 0)
+ log_device_debug_errno(dev, r, "Failed to read database under /run/udev/data/: %m");
+
+ r = device_tag_index(dev, NULL, false);
+ if (r < 0)
+ log_device_debug_errno(dev, r, "Failed to remove corresponding tag files under /run/udev/tag/, ignoring: %m");
+
+ r = device_delete_db(dev);
+ if (r < 0)
+ log_device_debug_errno(dev, r, "Failed to delete database under /run/udev/data/, ignoring: %m");
+
+ if (sd_device_get_devnum(dev, NULL) >= 0)
+ (void) udev_watch_end(dev);
+
+ (void) udev_rules_apply_to_event(rules, event, timeout_usec, properties_list);
+
+ if (sd_device_get_devnum(dev, NULL) >= 0)
+ (void) udev_node_remove(dev);
+}
+
+int udev_event_execute_rules(UdevEvent *event,
+ usec_t timeout_usec,
+ Hashmap *properties_list,
+ UdevRules *rules) {
+ sd_device *dev = event->dev;
+ const char *subsystem, *action;
+ int r;
+
+ assert(event);
+ assert(rules);
+
+ r = sd_device_get_subsystem(dev, &subsystem);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get subsystem: %m");
+
+ r = sd_device_get_property_value(dev, "ACTION", &action);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get property 'ACTION': %m");
+
+ if (streq(action, "remove")) {
+ event_execute_rules_on_remove(event, timeout_usec, properties_list, rules);
+ return 0;
+ }
+
+ r = device_clone_with_db(dev, &event->dev_db_clone);
+ if (r < 0)
+ log_device_debug_errno(dev, r, "Failed to clone sd_device object, ignoring: %m");
+
+ if (event->dev_db_clone) {
+ r = sd_device_get_devnum(dev, NULL);
+ if (r < 0) {
+ if (r != -ENOENT)
+ log_device_debug_errno(dev, r, "Failed to get devnum, ignoring: %m");
+
+ if (streq(action, "move")) {
+ r = device_copy_properties(dev, event->dev_db_clone);
if (r < 0)
- log_warning_errno(r, "renamed interface '%d' from '%s' to '%s', but could not update udev_device: %m",
- udev_device_get_ifindex(dev), udev_device_get_sysname(dev), event->name);
- else
- log_debug("changed devpath to '%s'", udev_device_get_devpath(dev));
+ log_device_debug_errno(dev, r, "Failed to copy properties from cloned device, ignoring: %m");
}
- }
+ } else
+ /* Disable watch during event processing. */
+ (void) udev_watch_end(event->dev_db_clone);
+ }
- if (major(udev_device_get_devnum(dev)) > 0) {
- bool apply;
-
- /* remove/update possible left-over symlinks from old database entry */
- if (event->dev_db != NULL)
- udev_node_update_old_links(dev, event->dev_db);
-
- if (!event->owner_set)
- event->uid = udev_device_get_devnode_uid(dev);
-
- if (!event->group_set)
- event->gid = udev_device_get_devnode_gid(dev);
-
- if (!event->mode_set) {
- if (udev_device_get_devnode_mode(dev) > 0) {
- /* kernel supplied value */
- event->mode = udev_device_get_devnode_mode(dev);
- } else if (event->gid > 0) {
- /* default 0660 if a group is assigned */
- event->mode = 0660;
- } else {
- /* default 0600 */
- event->mode = 0600;
- }
- }
+ (void) udev_rules_apply_to_event(rules, event, timeout_usec, properties_list);
- apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set;
- udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list);
- }
+ (void) rename_netif(event);
+ (void) update_devnode(event);
- /* preserve old, or get new initialization timestamp */
- udev_device_ensure_usec_initialized(event->dev, event->dev_db);
+ /* preserve old, or get new initialization timestamp */
+ r = device_ensure_usec_initialized(dev, event->dev_db_clone);
+ if (r < 0)
+ log_device_debug_errno(dev, r, "Failed to set initialization timestamp, ignoring: %m");
- /* (re)write database file */
- udev_device_tag_index(dev, event->dev_db, true);
- udev_device_update_db(dev);
- udev_device_set_is_initialized(dev);
+ /* (re)write database file */
+ r = device_tag_index(dev, event->dev_db_clone, true);
+ if (r < 0)
+ log_device_debug_errno(dev, r, "Failed to update tags under /run/udev/tag/, ignoring: %m");
- event->dev_db = udev_device_unref(event->dev_db);
- }
+ r = device_update_db(dev);
+ if (r < 0)
+ log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/, ignoring: %m");
+
+ device_set_is_initialized(dev);
+
+ event->dev_db_clone = sd_device_unref(event->dev_db_clone);
+
+ return 0;
}
-void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec) {
- struct udev_list_entry *list_entry;
+void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) {
+ const char *cmd;
+ void *val;
+ Iterator i;
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) {
+ HASHMAP_FOREACH_KEY(val, cmd, event->run_list, i) {
+ enum udev_builtin_cmd builtin_cmd = PTR_TO_INT(val);
char command[UTIL_PATH_SIZE];
- const char *cmd = udev_list_entry_get_name(list_entry);
- enum udev_builtin_cmd builtin_cmd = udev_list_entry_get_num(list_entry);
udev_event_apply_format(event, cmd, command, sizeof(command), false);
- if (builtin_cmd < UDEV_BUILTIN_MAX)
+ if (builtin_cmd >= 0 && builtin_cmd < _UDEV_BUILTIN_MAX)
udev_builtin_run(event->dev, builtin_cmd, command, false);
else {
- if (event->exec_delay > 0) {
+ if (event->exec_delay_usec > 0) {
log_debug("delay execution of '%s'", command);
- sleep(event->exec_delay);
+ (void) usleep(event->exec_delay_usec);
}
- udev_event_spawn(event, timeout_usec, timeout_warn_usec, false, command, NULL, 0);
+ udev_event_spawn(event, timeout_usec, false, command, NULL, 0);
}
}
}
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 333dcae6b9..c11eb8c1ac 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- *
- */
#include <errno.h>
#include <fcntl.h>
@@ -12,350 +9,434 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "alloc-util.h"
#include "device-nodes.h"
+#include "device-private.h"
+#include "device-util.h"
#include "dirent-util.h"
+#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
+#include "libudev-util.h"
+#include "mkdir.h"
+#include "path-util.h"
#include "selinux-util.h"
#include "smack-util.h"
#include "stdio-util.h"
#include "string-util.h"
-#include "udev.h"
+#include "strxcpyx.h"
+#include "udev-node.h"
-static int node_symlink(struct udev_device *dev, const char *node, const char *slink) {
+static int node_symlink(sd_device *dev, const char *node, const char *slink) {
+ _cleanup_free_ char *slink_dirname = NULL, *target = NULL;
+ const char *id_filename, *slink_tmp;
struct stat stats;
- char target[UTIL_PATH_SIZE];
- char *s;
- size_t l;
- char slink_tmp[UTIL_PATH_SIZE + 32];
- int i = 0;
- int tail = 0;
- int err = 0;
+ int r;
+
+ assert(dev);
+ assert(node);
+ assert(slink);
+
+ slink_dirname = dirname_malloc(slink);
+ if (!slink_dirname)
+ return log_oom();
/* use relative link */
- target[0] = '\0';
- while (node[i] && (node[i] == slink[i])) {
- if (node[i] == '/')
- tail = i+1;
- i++;
- }
- s = target;
- l = sizeof(target);
- while (slink[i] != '\0') {
- if (slink[i] == '/')
- l = strpcpy(&s, l, "../");
- i++;
- }
- l = strscpy(s, l, &node[tail]);
- if (l == 0) {
- err = -EINVAL;
- goto exit;
- }
+ r = path_make_relative(slink_dirname, node, &target);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node);
/* preserve link with correct target, do not replace node of other device */
if (lstat(slink, &stats) == 0) {
if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) {
- log_error("conflicting device node '%s' found, link to '%s' will not be created", slink, node);
- goto exit;
+ log_device_error(dev, "Conflicting device node '%s' found, link to '%s' will not be created.", slink, node);
+ return -EOPNOTSUPP;
} else if (S_ISLNK(stats.st_mode)) {
- char buf[UTIL_PATH_SIZE];
- int len;
-
- len = readlink(slink, buf, sizeof(buf));
- if (len > 0 && len < (int)sizeof(buf)) {
- buf[len] = '\0';
- if (streq(target, buf)) {
- log_debug("preserve already existing symlink '%s' to '%s'", slink, target);
- (void) label_fix(slink, LABEL_IGNORE_ENOENT);
- utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW);
- goto exit;
- }
+ _cleanup_free_ char *buf = NULL;
+
+ if (readlink_malloc(slink, &buf) >= 0 &&
+ streq(target, buf)) {
+ log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target);
+ (void) label_fix(slink, LABEL_IGNORE_ENOENT);
+ (void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW);
+ return 0;
}
}
} else {
- log_debug("creating symlink '%s' to '%s'", slink, target);
+ log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target);
do {
- err = mkdir_parents_label(slink, 0755);
- if (!IN_SET(err, 0, -ENOENT))
+ r = mkdir_parents_label(slink, 0755);
+ if (!IN_SET(r, 0, -ENOENT))
break;
mac_selinux_create_file_prepare(slink, S_IFLNK);
- err = symlink(target, slink);
- if (err != 0)
- err = -errno;
+ if (symlink(target, slink) < 0)
+ r = -errno;
mac_selinux_create_file_clear();
- } while (err == -ENOENT);
- if (err == 0)
- goto exit;
+ } while (r == -ENOENT);
+ if (r == 0)
+ return 0;
+ if (r < 0)
+ log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink, target, slink);
}
- log_debug("atomically replace '%s'", slink);
- strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", udev_device_get_id_filename(dev), NULL);
- unlink(slink_tmp);
+ log_device_debug(dev, "Atomically replace '%s'", slink);
+ r = device_get_id_filename(dev, &id_filename);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get id_filename: %m");
+ slink_tmp = strjoina(slink, ".tmp-", id_filename);
+ (void) unlink(slink_tmp);
do {
- err = mkdir_parents_label(slink_tmp, 0755);
- if (!IN_SET(err, 0, -ENOENT))
+ r = mkdir_parents_label(slink_tmp, 0755);
+ if (!IN_SET(r, 0, -ENOENT))
break;
mac_selinux_create_file_prepare(slink_tmp, S_IFLNK);
- err = symlink(target, slink_tmp);
- if (err != 0)
- err = -errno;
+ if (symlink(target, slink_tmp) < 0)
+ r = -errno;
mac_selinux_create_file_clear();
- } while (err == -ENOENT);
- if (err != 0) {
- log_error_errno(errno, "symlink '%s' '%s' failed: %m", target, slink_tmp);
- goto exit;
- }
- err = rename(slink_tmp, slink);
- if (err != 0) {
- log_error_errno(errno, "rename '%s' '%s' failed: %m", slink_tmp, slink);
- unlink(slink_tmp);
+ } while (r == -ENOENT);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target);
+
+ if (rename(slink_tmp, slink) < 0) {
+ r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s' failed: %m", slink_tmp, slink);
+ (void) unlink(slink_tmp);
}
-exit:
- return err;
+
+ return r;
}
/* find device node of device with highest priority */
-static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) {
- struct udev *udev = udev_device_get_udev(dev);
- DIR *dir;
+static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ _cleanup_free_ char *target = NULL;
struct dirent *dent;
- int priority = 0;
- const char *target = NULL;
+ int r, priority = 0;
+
+ assert(!add || dev);
+ assert(stackdir);
+ assert(ret);
if (add) {
- priority = udev_device_get_devlink_priority(dev);
- strscpy(buf, bufsize, udev_device_get_devnode(dev));
- target = buf;
+ const char *devnode;
+
+ r = device_get_devlink_priority(dev, &priority);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_devname(dev, &devnode);
+ if (r < 0)
+ return r;
+
+ target = strdup(devnode);
+ if (!target)
+ return -ENOMEM;
}
dir = opendir(stackdir);
- if (dir == NULL)
- return target;
+ if (!dir) {
+ if (target) {
+ *ret = TAKE_PTR(target);
+ return 0;
+ }
+
+ return -errno;
+ }
+
FOREACH_DIRENT_ALL(dent, dir, break) {
- struct udev_device *dev_db;
+ _cleanup_(sd_device_unrefp) sd_device *dev_db = NULL;
+ const char *devnode, *id_filename;
+ int db_prio = 0;
if (dent->d_name[0] == '\0')
break;
if (dent->d_name[0] == '.')
continue;
- log_debug("found '%s' claiming '%s'", dent->d_name, stackdir);
+ log_device_debug(dev, "Found '%s' claiming '%s'", dent->d_name, stackdir);
+
+ if (device_get_id_filename(dev, &id_filename) < 0)
+ continue;
/* did we find ourself? */
- if (streq(dent->d_name, udev_device_get_id_filename(dev)))
+ if (streq(dent->d_name, id_filename))
continue;
- dev_db = udev_device_new_from_device_id(udev, dent->d_name);
- if (dev_db != NULL) {
- const char *devnode;
-
- devnode = udev_device_get_devnode(dev_db);
- if (devnode != NULL) {
- if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) {
- log_debug("'%s' claims priority %i for '%s'",
- udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir);
- priority = udev_device_get_devlink_priority(dev_db);
- strscpy(buf, bufsize, devnode);
- target = buf;
- }
- }
- udev_device_unref(dev_db);
- }
+ if (sd_device_new_from_device_id(&dev_db, dent->d_name) < 0)
+ continue;
+
+ if (sd_device_get_devname(dev_db, &devnode) < 0)
+ continue;
+
+ if (device_get_devlink_priority(dev_db, &db_prio) < 0)
+ continue;
+
+ if (target && db_prio <= priority)
+ continue;
+
+ log_device_debug(dev_db, "Device claims priority %i for '%s'", db_prio, stackdir);
+
+ r = free_and_strdup(&target, devnode);
+ if (r < 0)
+ return r;
+ priority = db_prio;
}
- closedir(dir);
- return target;
+
+ *ret = TAKE_PTR(target);
+ return 0;
}
/* manage "stack of names" with possibly specified device priorities */
-static void link_update(struct udev_device *dev, const char *slink, bool add) {
- char name_enc[UTIL_PATH_SIZE];
- char filename[UTIL_PATH_SIZE * 2];
- char dirname[UTIL_PATH_SIZE];
- const char *target;
- char buf[UTIL_PATH_SIZE];
+static int link_update(sd_device *dev, const char *slink, bool add) {
+ _cleanup_free_ char *target = NULL, *filename = NULL, *dirname = NULL;
+ char name_enc[PATH_MAX];
+ const char *id_filename;
+ int r;
+
+ assert(dev);
+ assert(slink);
+
+ r = device_get_id_filename(dev, &id_filename);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get id_filename: %m");
util_path_encode(slink + STRLEN("/dev"), name_enc, sizeof(name_enc));
- strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL);
- strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL);
+ dirname = path_join("/run/udev/links/", name_enc);
+ if (!dirname)
+ return log_oom();
+ filename = path_join(dirname, id_filename);
+ if (!filename)
+ return log_oom();
if (!add && unlink(filename) == 0)
- rmdir(dirname);
+ (void) rmdir(dirname);
- target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf));
- if (target == NULL) {
- log_debug("no reference left, remove '%s'", slink);
+ r = link_find_prioritized(dev, add, dirname, &target);
+ if (r < 0) {
+ log_device_debug(dev, "No reference left, removing '%s'", slink);
if (unlink(slink) == 0)
- rmdir_parents(slink, "/");
- } else {
- log_debug("creating link '%s' to '%s'", slink, target);
- node_symlink(dev, target, slink);
- }
-
- if (add) {
- int err;
+ (void) rmdir_parents(slink, "/");
+ } else
+ (void) node_symlink(dev, target, slink);
+ if (add)
do {
- int fd;
+ _cleanup_close_ int fd = -1;
- err = mkdir_parents(filename, 0755);
- if (!IN_SET(err, 0, -ENOENT))
+ r = mkdir_parents(filename, 0755);
+ if (!IN_SET(r, 0, -ENOENT))
break;
fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
- if (fd >= 0)
- close(fd);
- else
- err = -errno;
- } while (err == -ENOENT);
- }
+ if (fd < 0)
+ r = -errno;
+ } while (r == -ENOENT);
+
+ return r;
}
-void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) {
- struct udev_list_entry *list_entry;
+int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
+ const char *name, *devpath;
+ int r;
+
+ assert(dev);
+ assert(dev_old);
+
+ r = sd_device_get_devpath(dev, &devpath);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get devpath: %m");
/* update possible left-over symlinks */
- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev_old)) {
- const char *name = udev_list_entry_get_name(list_entry);
- struct udev_list_entry *list_entry_current;
- int found;
+ FOREACH_DEVICE_DEVLINK(dev_old, name) {
+ const char *name_current;
+ bool found = false;
/* check if old link name still belongs to this device */
- found = 0;
- udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) {
- const char *name_current = udev_list_entry_get_name(list_entry_current);
-
+ FOREACH_DEVICE_DEVLINK(dev, name_current)
if (streq(name, name_current)) {
- found = 1;
+ found = true;
break;
}
- }
+
if (found)
continue;
- log_debug("update old name, '%s' no longer belonging to '%s'",
- name, udev_device_get_devpath(dev));
+ log_device_debug(dev, "Updating old name, '%s' no longer belonging to '%s'",
+ name, devpath);
link_update(dev, name, false);
}
+
+ return 0;
}
-static int node_permissions_apply(struct udev_device *dev, bool apply,
+static int node_permissions_apply(sd_device *dev, bool apply,
mode_t mode, uid_t uid, gid_t gid,
- struct udev_list *seclabel_list) {
- const char *devnode = udev_device_get_devnode(dev);
- dev_t devnum = udev_device_get_devnum(dev);
+ Hashmap *seclabel_list) {
+ const char *devnode, *subsystem, *id_filename = NULL;
struct stat stats;
- struct udev_list_entry *entry;
- int err = 0;
-
- if (streq(udev_device_get_subsystem(dev), "block"))
+ dev_t devnum;
+ int r = 0;
+
+ assert(dev);
+
+ r = sd_device_get_devname(dev, &devnode);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get devname: %m");
+ r = sd_device_get_subsystem(dev, &subsystem);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
+ r = sd_device_get_devnum(dev, &devnum);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get devnum: %m");
+ (void) device_get_id_filename(dev, &id_filename);
+
+ if (streq(subsystem, "block"))
mode |= S_IFBLK;
else
mode |= S_IFCHR;
- if (lstat(devnode, &stats) != 0) {
- err = log_debug_errno(errno, "cannot stat() node '%s' (%m)", devnode);
- goto out;
- }
+ if (lstat(devnode, &stats) < 0)
+ return log_device_debug_errno(dev, errno, "cannot stat() node '%s' (%m)", devnode);
- if (((stats.st_mode & S_IFMT) != (mode & S_IFMT)) || (stats.st_rdev != devnum)) {
- err = -EEXIST;
- log_debug("found node '%s' with non-matching devnum %s, skip handling",
- udev_device_get_devnode(dev), udev_device_get_id_filename(dev));
- goto out;
- }
+ if (((stats.st_mode & S_IFMT) != (mode & S_IFMT)) || (stats.st_rdev != devnum))
+ return log_device_debug_errno(dev, EEXIST, "Found node '%s' with non-matching devnum %s, skip handling",
+ devnode, id_filename);
if (apply) {
- bool selinux = false;
- bool smack = false;
+ bool selinux = false, smack = false;
+ const char *name, *label;
+ Iterator i;
if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) {
- log_debug("set permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid);
- err = chmod(devnode, mode);
- if (err < 0)
- log_warning_errno(errno, "setting mode of %s to %#o failed: %m", devnode, mode);
- err = chown(devnode, uid, gid);
- if (err < 0)
- log_warning_errno(errno, "setting owner of %s to uid=%u, gid=%u failed: %m", devnode, uid, gid);
- } else {
- log_debug("preserve permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid);
- }
+ log_device_debug(dev, "Setting permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid);
+ if (chmod(devnode, mode) < 0)
+ r = log_device_warning_errno(dev, errno, "Failed to set mode of %s to %#o: %m", devnode, mode);
+ if (chown(devnode, uid, gid) < 0)
+ r = log_device_warning_errno(dev, errno, "Failed to set owner of %s to uid=%u, gid=%u: %m", devnode, uid, gid);
+ } else
+ log_device_debug(dev, "Preserve permissions of %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid);
/* apply SECLABEL{$module}=$label */
- udev_list_entry_foreach(entry, udev_list_get_entry(seclabel_list)) {
- const char *name, *label;
- int r;
-
- name = udev_list_entry_get_name(entry);
- label = udev_list_entry_get_value(entry);
+ HASHMAP_FOREACH_KEY(label, name, seclabel_list, i) {
+ int q;
if (streq(name, "selinux")) {
selinux = true;
- r = mac_selinux_apply(devnode, label);
- if (r < 0)
- log_error_errno(r, "SECLABEL: failed to set SELinux label '%s': %m", label);
+ q = mac_selinux_apply(devnode, label);
+ if (q < 0)
+ log_device_error_errno(dev, q, "SECLABEL: failed to set SELinux label '%s': %m", label);
else
- log_debug("SECLABEL: set SELinux label '%s'", label);
+ log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
} else if (streq(name, "smack")) {
smack = true;
- r = mac_smack_apply(devnode, SMACK_ATTR_ACCESS, label);
- if (r < 0)
- log_error_errno(r, "SECLABEL: failed to set SMACK label '%s': %m", label);
+ q = mac_smack_apply(devnode, SMACK_ATTR_ACCESS, label);
+ if (q < 0)
+ log_device_error_errno(dev, q, "SECLABEL: failed to set SMACK label '%s': %m", label);
else
- log_debug("SECLABEL: set SMACK label '%s'", label);
+ log_device_debug(dev, "SECLABEL: set SMACK label '%s'", label);
} else
- log_error("SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
+ log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
}
/* set the defaults */
if (!selinux)
(void) mac_selinux_fix(devnode, LABEL_IGNORE_ENOENT);
if (!smack)
- mac_smack_apply(devnode, SMACK_ATTR_ACCESS, NULL);
+ (void) mac_smack_apply(devnode, SMACK_ATTR_ACCESS, NULL);
}
/* always update timestamp when we re-use the node, like on media change events */
- utimensat(AT_FDCWD, devnode, NULL, 0);
-out:
- return err;
+ (void) utimensat(AT_FDCWD, devnode, NULL, 0);
+
+ return r;
}
-void udev_node_add(struct udev_device *dev, bool apply,
- mode_t mode, uid_t uid, gid_t gid,
- struct udev_list *seclabel_list) {
- char filename[DEV_NUM_PATH_MAX];
- struct udev_list_entry *list_entry;
+static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) {
+ char filename[DEV_NUM_PATH_MAX], *s;
+ const char *subsystem;
+ dev_t devnum;
+ int r;
- log_debug("handling device node '%s', devnum=%s, mode=%#o, uid="UID_FMT", gid="GID_FMT,
- udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid);
+ assert(ret);
- if (node_permissions_apply(dev, apply, mode, uid, gid, seclabel_list) < 0)
- return;
+ r = sd_device_get_subsystem(dev, &subsystem);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_devnum(dev, &devnum);
+ if (r < 0)
+ return r;
- /* always add /dev/{block,char}/$major:$minor */
xsprintf_dev_num_path(filename,
- streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
- udev_device_get_devnum(dev));
- node_symlink(dev, udev_device_get_devnode(dev), filename);
+ streq(subsystem, "block") ? "block" : "char",
+ devnum);
+
+ s = strdup(filename);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+}
+
+int udev_node_add(sd_device *dev, bool apply,
+ mode_t mode, uid_t uid, gid_t gid,
+ Hashmap *seclabel_list) {
+ const char *devnode, *devlink;
+ _cleanup_free_ char *filename = NULL;
+ int r;
+
+ assert(dev);
+
+ r = sd_device_get_devname(dev, &devnode);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get devnode: %m");
+
+ if (DEBUG_LOGGING) {
+ const char *id_filename = NULL;
+
+ (void) device_get_id_filename(dev, &id_filename);
+ log_device_debug(dev, "Handling device node '%s', devnum=%s, mode=%#o, uid="UID_FMT", gid="GID_FMT,
+ devnode, strnull(id_filename), mode, uid, gid);
+ }
+
+ r = node_permissions_apply(dev, apply, mode, uid, gid, seclabel_list);
+ if (r < 0)
+ return r;
+
+ r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get device path: %m");
+
+ /* always add /dev/{block,char}/$major:$minor */
+ (void) node_symlink(dev, devnode, filename);
/* create/update symlinks, add symlinks to name index */
- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev))
- link_update(dev, udev_list_entry_get_name(list_entry), true);
+ FOREACH_DEVICE_DEVLINK(dev, devlink)
+ (void) link_update(dev, devlink, true);
+
+ return 0;
}
-void udev_node_remove(struct udev_device *dev) {
- struct udev_list_entry *list_entry;
- char filename[DEV_NUM_PATH_MAX];
+int udev_node_remove(sd_device *dev) {
+ _cleanup_free_ char *filename = NULL;
+ const char *devlink;
+ int r;
+
+ assert(dev);
/* remove/update symlinks, remove symlinks from name index */
- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev))
- link_update(dev, udev_list_entry_get_name(list_entry), false);
+ FOREACH_DEVICE_DEVLINK(dev, devlink)
+ (void) link_update(dev, devlink, false);
+
+ r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get device path: %m");
/* remove /dev/{block,char}/$major:$minor */
- xsprintf_dev_num_path(filename,
- streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
- udev_device_get_devnum(dev));
- unlink(filename);
+ (void) unlink(filename);
+
+ return 0;
}
diff --git a/src/udev/udev-node.h b/src/udev/udev-node.h
new file mode 100644
index 0000000000..223c8f0e43
--- /dev/null
+++ b/src/udev/udev-node.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#pragma once
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "sd-device.h"
+
+#include "hashmap.h"
+
+int udev_node_add(sd_device *dev, bool apply,
+ mode_t mode, uid_t uid, gid_t gid,
+ Hashmap *seclabel_list);
+int udev_node_remove(sd_device *dev);
+int udev_node_update_old_links(sd_device *dev, sd_device *dev_old);
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index f029395884..53c68d254a 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- *
- */
#include <ctype.h>
#include <errno.h>
@@ -18,11 +15,17 @@
#include "alloc-util.h"
#include "conf-files.h"
+#include "device-private.h"
+#include "device-util.h"
#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
+#include "fileio.h"
#include "fs-util.h"
#include "glob-util.h"
+#include "libudev-util.h"
+#include "mkdir.h"
+#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
#include "stat-util.h"
@@ -30,7 +33,9 @@
#include "strbuf.h"
#include "string-util.h"
#include "strv.h"
+#include "strxcpyx.h"
#include "sysctl-util.h"
+#include "udev-builtin.h"
#include "udev.h"
#include "user-util.h"
#include "util.h"
@@ -38,7 +43,7 @@
#define PREALLOC_TOKEN 2048
struct uid_gid {
- unsigned int name_off;
+ unsigned name_off;
union {
uid_t uid;
gid_t gid;
@@ -52,33 +57,32 @@ static const char* const rules_dirs[] = {
NULL
};
-struct udev_rules {
- struct udev *udev;
+struct UdevRules {
usec_t dirs_ts_usec;
- int resolve_names;
+ ResolveNameTiming resolve_name_timing;
/* every key in the rules file becomes a token */
struct token *tokens;
- unsigned int token_cur;
- unsigned int token_max;
+ unsigned token_cur;
+ unsigned token_max;
/* all key strings are copied and de-duplicated in a single continuous string buffer */
struct strbuf *strbuf;
/* during rule parsing, uid/gid lookup results are cached */
struct uid_gid *uids;
- unsigned int uids_cur;
- unsigned int uids_max;
+ unsigned uids_cur;
+ unsigned uids_max;
struct uid_gid *gids;
- unsigned int gids_cur;
- unsigned int gids_max;
+ unsigned gids_cur;
+ unsigned gids_max;
};
-static char *rules_str(struct udev_rules *rules, unsigned int off) {
+static char *rules_str(UdevRules *rules, unsigned off) {
return rules->strbuf->buf + off;
}
-static unsigned int rules_add_string(struct udev_rules *rules, const char *s) {
+static unsigned rules_add_string(UdevRules *rules, const char *s) {
return strbuf_add_string(rules->strbuf, s, strlen(s));
}
@@ -183,9 +187,9 @@ struct token {
enum token_type type:8;
bool can_set_name:1;
bool has_static_node:1;
- unsigned int unused:6;
+ unsigned unused:6;
unsigned short token_count;
- unsigned int label_off;
+ unsigned label_off;
unsigned short filename_off;
unsigned short filename_line;
} rule;
@@ -195,11 +199,11 @@ struct token {
enum string_glob_type glob:8;
enum string_subst_type subst:4;
enum string_subst_type attrsubst:4;
- unsigned int value_off;
+ unsigned value_off;
union {
- unsigned int attr_off;
- unsigned int rule_goto;
- mode_t mode;
+ unsigned attr_off;
+ unsigned rule_goto;
+ mode_t mode;
uid_t uid;
gid_t gid;
int devlink_prio;
@@ -212,13 +216,13 @@ struct token {
#define MAX_TK 64
struct rule_tmp {
- struct udev_rules *rules;
+ UdevRules *rules;
struct token rule;
struct token token[MAX_TK];
- unsigned int token_cur;
+ unsigned token_cur;
};
-#ifdef DEBUG
+#if ENABLE_DEBUG_UDEV
static const char *operation_str(enum operation_type type) {
static const char *operation_strs[] = {
[OP_UNSET] = "UNSET",
@@ -230,7 +234,7 @@ static const char *operation_str(enum operation_type type) {
[OP_REMOVE] = "remove",
[OP_ASSIGN] = "assign",
[OP_ASSIGN_FINAL] = "assign-final",
-} ;
+ };
return operation_strs[type];
}
@@ -314,7 +318,7 @@ static const char *token_str(enum token_type type) {
return token_strs[type];
}
-static void dump_token(struct udev_rules *rules, struct token *token) {
+static void dump_token(UdevRules *rules, struct token *token) {
enum token_type type = token->type;
enum operation_type op = token->key.op;
enum string_glob_type glob = token->key.glob;
@@ -326,7 +330,7 @@ static void dump_token(struct udev_rules *rules, struct token *token) {
{
const char *tks_ptr = (char *)rules->tokens;
const char *tk_ptr = (char *)token;
- unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token);
+ unsigned idx = (tk_ptr - tks_ptr) / sizeof(struct token);
log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'",
&rules->strbuf->buf[token->rule.filename_off], token->rule.filename_line,
@@ -420,15 +424,15 @@ static void dump_token(struct udev_rules *rules, struct token *token) {
case TK_M_PARENTS_MAX:
case TK_M_MAX:
case TK_UNSET:
- log_debug("unknown type %u", type);
+ log_debug("Unknown token type %u", type);
break;
}
}
-static void dump_rules(struct udev_rules *rules) {
- unsigned int i;
+static void dump_rules(UdevRules *rules) {
+ unsigned i;
- log_debug("dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings",
+ log_debug("Dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings",
rules->token_cur,
rules->token_cur * sizeof(struct token),
rules->strbuf->nodes_count,
@@ -437,15 +441,15 @@ static void dump_rules(struct udev_rules *rules) {
dump_token(rules, &rules->tokens[i]);
}
#else
-static inline void dump_token(struct udev_rules *rules, struct token *token) {}
-static inline void dump_rules(struct udev_rules *rules) {}
-#endif /* DEBUG */
+static inline void dump_token(UdevRules *rules, struct token *token) {}
+static inline void dump_rules(UdevRules *rules) {}
+#endif /* ENABLE_DEBUG_UDEV */
-static int add_token(struct udev_rules *rules, struct token *token) {
+static int add_token(UdevRules *rules, struct token *token) {
/* grow buffer if needed */
if (rules->token_cur+1 >= rules->token_max) {
struct token *tokens;
- unsigned int add;
+ unsigned add;
/* double the buffer size */
add = rules->token_max;
@@ -453,7 +457,7 @@ static int add_token(struct udev_rules *rules, struct token *token) {
add = 8;
tokens = reallocarray(rules->tokens, rules->token_max + add, sizeof(struct token));
- if (tokens == NULL)
+ if (!tokens)
return -1;
rules->tokens = tokens;
rules->token_max += add;
@@ -463,17 +467,17 @@ static int add_token(struct udev_rules *rules, struct token *token) {
return 0;
}
-static void log_unknown_owner(int error, const char *entity, const char *owner) {
+static void log_unknown_owner(sd_device *dev, int error, const char *entity, const char *owner) {
if (IN_SET(abs(error), ENOENT, ESRCH))
- log_error("Specified %s '%s' unknown", entity, owner);
+ log_device_error(dev, "Specified %s '%s' unknown", entity, owner);
else
- log_error_errno(error, "Error resolving %s '%s': %m", entity, owner);
+ log_device_error_errno(dev, error, "Failed to resolve %s '%s': %m", entity, owner);
}
-static uid_t add_uid(struct udev_rules *rules, const char *owner) {
- unsigned int i;
+static uid_t add_uid(UdevRules *rules, const char *owner) {
+ unsigned i;
uid_t uid = 0;
- unsigned int off;
+ unsigned off;
int r;
/* lookup, if we know it already */
@@ -484,14 +488,14 @@ static uid_t add_uid(struct udev_rules *rules, const char *owner) {
return uid;
}
}
- r = get_user_creds(&owner, &uid, NULL, NULL, NULL);
+ r = get_user_creds(&owner, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
if (r < 0)
- log_unknown_owner(r, "user", owner);
+ log_unknown_owner(NULL, r, "user", owner);
/* grow buffer if needed */
if (rules->uids_cur+1 >= rules->uids_max) {
struct uid_gid *uids;
- unsigned int add;
+ unsigned add;
/* double the buffer size */
add = rules->uids_max;
@@ -499,7 +503,7 @@ static uid_t add_uid(struct udev_rules *rules, const char *owner) {
add = 8;
uids = reallocarray(rules->uids, rules->uids_max + add, sizeof(struct uid_gid));
- if (uids == NULL)
+ if (!uids)
return uid;
rules->uids = uids;
rules->uids_max += add;
@@ -513,10 +517,10 @@ static uid_t add_uid(struct udev_rules *rules, const char *owner) {
return uid;
}
-static gid_t add_gid(struct udev_rules *rules, const char *group) {
- unsigned int i;
+static gid_t add_gid(UdevRules *rules, const char *group) {
+ unsigned i;
gid_t gid = 0;
- unsigned int off;
+ unsigned off;
int r;
/* lookup, if we know it already */
@@ -527,14 +531,14 @@ static gid_t add_gid(struct udev_rules *rules, const char *group) {
return gid;
}
}
- r = get_group_creds(&group, &gid);
+ r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING);
if (r < 0)
- log_unknown_owner(r, "group", group);
+ log_unknown_owner(NULL, r, "group", group);
/* grow buffer if needed */
if (rules->gids_cur+1 >= rules->gids_max) {
struct uid_gid *gids;
- unsigned int add;
+ unsigned add;
/* double the buffer size */
add = rules->gids_max;
@@ -542,7 +546,7 @@ static gid_t add_gid(struct udev_rules *rules, const char *group) {
add = 8;
gids = reallocarray(rules->gids, rules->gids_max + add, sizeof(struct uid_gid));
- if (gids == NULL)
+ if (!gids)
return gid;
rules->gids = gids;
rules->gids_max += add;
@@ -556,7 +560,7 @@ static gid_t add_gid(struct udev_rules *rules, const char *group) {
return gid;
}
-static int import_property_from_string(struct udev_device *dev, char *line) {
+static int import_property_from_string(sd_device *dev, char *line) {
char *key;
char *val;
size_t len;
@@ -568,12 +572,12 @@ static int import_property_from_string(struct udev_device *dev, char *line) {
/* comment or empty line */
if (IN_SET(key[0], '#', '\0'))
- return -1;
+ return 0;
/* split key/value */
val = strchr(key, '=');
- if (val == NULL)
- return -1;
+ if (!val)
+ return -EINVAL;
val[0] = '\0';
val++;
@@ -584,7 +588,7 @@ static int import_property_from_string(struct udev_device *dev, char *line) {
/* terminate key */
len = strlen(key);
if (len == 0)
- return -1;
+ return -EINVAL;
while (isspace(key[len-1]))
len--;
key[len] = '\0';
@@ -592,87 +596,91 @@ static int import_property_from_string(struct udev_device *dev, char *line) {
/* terminate value */
len = strlen(val);
if (len == 0)
- return -1;
+ return -EINVAL;
while (isspace(val[len-1]))
len--;
val[len] = '\0';
if (len == 0)
- return -1;
+ return -EINVAL;
/* unquote */
if (IN_SET(val[0], '"', '\'')) {
- if (len == 1 || val[len-1] != val[0]) {
- log_debug("inconsistent quoting: '%s', skip", line);
- return -1;
- }
+ if (len == 1 || val[len-1] != val[0])
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Inconsistent quoting: '%s', skip",
+ line);
val[len-1] = '\0';
val++;
}
- udev_device_add_property(dev, key, val);
-
- return 0;
+ return device_add_property(dev, key, val);
}
-static int import_file_into_properties(struct udev_device *dev, const char *filename) {
- FILE *f;
- char line[UTIL_LINE_SIZE];
+static int import_file_into_properties(sd_device *dev, const char *filename) {
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
f = fopen(filename, "re");
- if (f == NULL)
- return -1;
- while (fgets(line, sizeof(line), f) != NULL)
- import_property_from_string(dev, line);
- fclose(f);
+ if (!f)
+ return -errno;
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ (void) import_property_from_string(dev, line);
+ }
+
return 0;
}
-static int import_program_into_properties(struct udev_event *event,
+static int import_program_into_properties(UdevEvent *event,
usec_t timeout_usec,
- usec_t timeout_warn_usec,
const char *program) {
char result[UTIL_LINE_SIZE];
char *line;
int err;
- err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result));
+ err = udev_event_spawn(event, timeout_usec, true, program, result, sizeof(result));
if (err < 0)
return err;
line = result;
- while (line != NULL) {
+ while (line) {
char *pos;
pos = strchr(line, '\n');
- if (pos != NULL) {
+ if (pos) {
pos[0] = '\0';
pos = &pos[1];
}
- import_property_from_string(event->dev, line);
+ (void) import_property_from_string(event->dev, line);
line = pos;
}
return 0;
}
-static int import_parent_into_properties(struct udev_device *dev, const char *filter) {
- struct udev_device *dev_parent;
- struct udev_list_entry *list_entry;
+static int import_parent_into_properties(sd_device *dev, const char *filter) {
+ const char *key, *val;
+ sd_device *parent;
+ int r;
assert(dev);
assert(filter);
- dev_parent = udev_device_get_parent(dev);
- if (dev_parent == NULL)
- return -1;
-
- udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) {
- const char *key = udev_list_entry_get_name(list_entry);
- const char *val = udev_list_entry_get_value(list_entry);
+ r = sd_device_get_parent(dev, &parent);
+ if (r < 0)
+ return r;
+ FOREACH_DEVICE_PROPERTY(parent, key, val)
if (fnmatch(filter, key, 0) == 0)
- udev_device_add_property(dev, key, val);
- }
+ device_add_property(dev, key, val);
return 0;
}
@@ -688,7 +696,7 @@ static void attr_subst_subdir(char *attr, size_t len) {
tail = pos + 2;
path = strndupa(attr, pos - attr + 1); /* include slash at end */
dir = opendir(path);
- if (dir == NULL)
+ if (!dir)
return;
FOREACH_DIRENT_ALL(dent, dir, break)
@@ -703,13 +711,13 @@ static void attr_subst_subdir(char *attr, size_t len) {
}
}
-static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) {
+static int get_key(char **line, char **key, enum operation_type *op, char **value) {
char *linepos;
char *temp;
unsigned i, j;
linepos = *line;
- if (linepos == NULL || linepos[0] == '\0')
+ if (!linepos || linepos[0] == '\0')
return -1;
/* skip whitespace */
@@ -805,15 +813,15 @@ static int get_key(struct udev *udev, char **line, char **key, enum operation_ty
}
/* extract possible KEY{attr} */
-static const char *get_key_attribute(struct udev *udev, char *str) {
+static const char *get_key_attribute(char *str) {
char *pos;
char *attr;
attr = strchr(str, '{');
- if (attr != NULL) {
+ if (attr) {
attr++;
pos = strchr(attr, '}');
- if (pos == NULL) {
+ if (!pos) {
log_error("Missing closing brace for format");
return NULL;
}
@@ -881,7 +889,7 @@ static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
break;
case TK_M_TEST:
token->key.value_off = rules_add_string(rule_tmp->rules, value);
- if (data != NULL)
+ if (data)
token->key.mode = *(mode_t *)data;
break;
case TK_A_STRING_ESCAPE_NONE:
@@ -915,44 +923,43 @@ static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
assert_not_reached("wrong type");
}
- if (value != NULL && type < TK_M_MAX) {
+ if (value && type < TK_M_MAX) {
/* check if we need to split or call fnmatch() while matching rules */
enum string_glob_type glob;
- int has_split;
- int has_glob;
+ bool has_split, has_glob;
- has_split = (strchr(value, '|') != NULL);
+ has_split = strchr(value, '|');
has_glob = string_is_glob(value);
- if (has_split && has_glob) {
+ if (has_split && has_glob)
glob = GL_SPLIT_GLOB;
- } else if (has_split) {
+ else if (has_split)
glob = GL_SPLIT;
- } else if (has_glob) {
+ else if (has_glob) {
if (streq(value, "?*"))
glob = GL_SOMETHING;
else
glob = GL_GLOB;
- } else {
+ } else
glob = GL_PLAIN;
- }
+
token->key.glob = glob;
}
- if (value != NULL && type > TK_M_MAX) {
+ if (value && type > TK_M_MAX) {
/* check if assigned value has substitution chars */
if (value[0] == '[')
token->key.subst = SB_SUBSYS;
- else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL)
+ else if (strchr(value, '%') || strchr(value, '$'))
token->key.subst = SB_FORMAT;
else
token->key.subst = SB_NONE;
}
- if (attr != NULL) {
+ if (attr) {
/* check if property/attribute name has substitution chars */
if (attr[0] == '[')
token->key.attrsubst = SB_SUBSYS;
- else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL)
+ else if (strchr(attr, '%') || strchr(attr, '$'))
token->key.attrsubst = SB_FORMAT;
else
token->key.attrsubst = SB_NONE;
@@ -963,15 +970,15 @@ static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
rule_tmp->token_cur++;
}
-static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) {
- unsigned int i;
- unsigned int start = 0;
- unsigned int end = rule_tmp->token_cur;
+static int sort_token(UdevRules *rules, struct rule_tmp *rule_tmp) {
+ unsigned i;
+ unsigned start = 0;
+ unsigned end = rule_tmp->token_cur;
for (i = 0; i < rule_tmp->token_cur; i++) {
enum token_type next_val = TK_UNSET;
- unsigned int next_idx = 0;
- unsigned int j;
+ unsigned next_idx = 0;
+ unsigned j;
/* find smallest value */
for (j = start; j < end; j++) {
@@ -997,13 +1004,14 @@ static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) {
return 0;
}
-#define LOG_RULE_ERROR(fmt, ...) log_error("Invalid rule %s:%u: " fmt, filename, lineno, ##__VA_ARGS__)
-#define LOG_RULE_WARNING(fmt, ...) log_warning("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__)
-#define LOG_RULE_DEBUG(fmt, ...) log_debug("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__)
+#define LOG_RULE_FULL(level, fmt, ...) log_full(level, "%s:%u: " fmt, filename, lineno, ##__VA_ARGS__)
+#define LOG_RULE_ERROR(fmt, ...) LOG_RULE_FULL(LOG_ERR, fmt, ##__VA_ARGS__)
+#define LOG_RULE_WARNING(fmt, ...) LOG_RULE_FULL(LOG_WARNING, fmt, ##__VA_ARGS__)
+#define LOG_RULE_DEBUG(fmt, ...) LOG_RULE_FULL(LOG_DEBUG, fmt, ##__VA_ARGS__)
#define LOG_AND_RETURN(fmt, ...) { LOG_RULE_ERROR(fmt, __VA_ARGS__); return; }
-static void add_rule(struct udev_rules *rules, char *line,
- const char *filename, unsigned int filename_off, unsigned int lineno) {
+static void add_rule(UdevRules *rules, char *line,
+ const char *filename, unsigned filename_off, unsigned lineno) {
char *linepos;
const char *attr;
struct rule_tmp rule_tmp = {
@@ -1022,7 +1030,7 @@ static void add_rule(struct udev_rules *rules, char *line,
char *value;
enum operation_type op;
- if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) {
+ if (get_key(&linepos, &key, &op, &value) != 0) {
/* Avoid erroring on trailing whitespace. This is probably rare
* so save the work for the error case instead of always trying
* to strip the trailing whitespace with strstrip(). */
@@ -1036,38 +1044,37 @@ static void add_rule(struct udev_rules *rules, char *line,
_cleanup_free_ char *tmp;
tmp = cescape(buf);
- log_error("invalid key/value pair in file %s on line %u, starting at character %tu ('%s')",
- filename, lineno, linepos - line + 1, tmp);
+ LOG_RULE_ERROR("Invalid key/value pair, starting at character %tu ('%s')", linepos - line + 1, tmp);
if (*linepos == '#')
- log_error("hint: comments can only start at beginning of line");
+ LOG_RULE_ERROR("Hint: comments can only start at beginning of line");
}
break;
}
if (rule_tmp.token_cur >= ELEMENTSOF(rule_tmp.token))
- LOG_AND_RETURN("temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur);
+ LOG_AND_RETURN("Temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur);
if (streq(key, "ACTION")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL);
} else if (streq(key, "DEVPATH")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL);
} else if (streq(key, "KERNEL")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL);
} else if (streq(key, "SUBSYSTEM")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
/* bus, class, subsystem events should all be the same */
if (STR_IN_SET(value, "subsystem", "bus", "class")) {
@@ -1080,18 +1087,17 @@ static void add_rule(struct udev_rules *rules, char *line,
} else if (streq(key, "DRIVER")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL);
} else if (startswith(key, "ATTR{")) {
- attr = get_key_attribute(rules->udev,
- key + STRLEN("ATTR"));
- if (attr == NULL)
- LOG_AND_RETURN("error parsing %s attribute", "ATTR");
+ attr = get_key_attribute(key + STRLEN("ATTR"));
+ if (!attr)
+ LOG_AND_RETURN("Failed to parse %s attribute", "ATTR");
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", "ATTR");
+ LOG_AND_RETURN("Invalid %s operation", "ATTR");
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr);
@@ -1099,13 +1105,12 @@ static void add_rule(struct udev_rules *rules, char *line,
rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr);
} else if (startswith(key, "SYSCTL{")) {
- attr = get_key_attribute(rules->udev,
- key + STRLEN("SYSCTL"));
- if (attr == NULL)
- LOG_AND_RETURN("error parsing %s attribute", "ATTR");
+ attr = get_key_attribute(key + STRLEN("SYSCTL"));
+ if (!attr)
+ LOG_AND_RETURN("Failed to parse %s attribute", "ATTR");
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", "ATTR");
+ LOG_AND_RETURN("Invalid %s operation", "ATTR");
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr);
@@ -1113,63 +1118,60 @@ static void add_rule(struct udev_rules *rules, char *line,
rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr);
} else if (startswith(key, "SECLABEL{")) {
- attr = get_key_attribute(rules->udev,
- key + STRLEN("SECLABEL"));
- if (attr == NULL)
- LOG_AND_RETURN("error parsing %s attribute", "SECLABEL");
+ attr = get_key_attribute(key + STRLEN("SECLABEL"));
+ if (!attr)
+ LOG_AND_RETURN("Failed to parse %s attribute", "SECLABEL");
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", "SECLABEL");
+ LOG_AND_RETURN("Invalid %s operation", "SECLABEL");
rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr);
} else if (streq(key, "KERNELS")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL);
} else if (streq(key, "SUBSYSTEMS")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL);
} else if (streq(key, "DRIVERS")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL);
} else if (startswith(key, "ATTRS{")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", "ATTRS");
+ LOG_AND_RETURN("Invalid %s operation", "ATTRS");
- attr = get_key_attribute(rules->udev,
- key + STRLEN("ATTRS"));
- if (attr == NULL)
- LOG_AND_RETURN("error parsing %s attribute", "ATTRS");
+ attr = get_key_attribute(key + STRLEN("ATTRS"));
+ if (!attr)
+ LOG_AND_RETURN("Failed to parse %s attribute", "ATTRS");
if (startswith(attr, "device/"))
LOG_RULE_WARNING("'device' link may not be available in future kernels; please fix");
- if (strstr(attr, "../") != NULL)
- LOG_RULE_WARNING("direct reference to parent sysfs directory, may break in future kernels; please fix");
+ if (strstr(attr, "../"))
+ LOG_RULE_WARNING("Direct reference to parent sysfs directory, may break in future kernels; please fix");
rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr);
} else if (streq(key, "TAGS")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL);
} else if (startswith(key, "ENV{")) {
- attr = get_key_attribute(rules->udev,
- key + STRLEN("ENV"));
- if (attr == NULL)
- LOG_AND_RETURN("error parsing %s attribute", "ENV");
+ attr = get_key_attribute(key + STRLEN("ENV"));
+ if (!attr)
+ LOG_AND_RETURN("Failed to parse %s attribute", "ENV");
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", "ENV");
+ LOG_AND_RETURN("Invalid %s operation", "ENV");
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr);
@@ -1186,7 +1188,7 @@ static void add_rule(struct udev_rules *rules, char *line,
"DEVLINKS",
"DEVPATH",
"TAGS"))
- LOG_AND_RETURN("invalid ENV attribute, '%s' cannot be set", attr);
+ LOG_AND_RETURN("Invalid ENV attribute, '%s' cannot be set", attr);
rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr);
}
@@ -1199,32 +1201,31 @@ static void add_rule(struct udev_rules *rules, char *line,
} else if (streq(key, "PROGRAM")) {
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL);
} else if (streq(key, "RESULT")) {
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL);
} else if (startswith(key, "IMPORT")) {
- attr = get_key_attribute(rules->udev,
- key + STRLEN("IMPORT"));
- if (attr == NULL) {
- LOG_RULE_WARNING("ignoring IMPORT{} with missing type");
+ attr = get_key_attribute(key + STRLEN("IMPORT"));
+ if (!attr) {
+ LOG_RULE_WARNING("Ignoring IMPORT{} with missing type");
continue;
}
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", "IMPORT");
+ LOG_AND_RETURN("Invalid %s operation", "IMPORT");
if (streq(attr, "program")) {
/* find known built-in command */
if (value[0] != '/') {
const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
- if (cmd < UDEV_BUILTIN_MAX) {
+ if (cmd >= 0) {
LOG_RULE_DEBUG("IMPORT found builtin '%s', replacing", value);
rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
continue;
@@ -1234,7 +1235,7 @@ static void add_rule(struct udev_rules *rules, char *line,
} else if (streq(attr, "builtin")) {
const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
- if (cmd >= UDEV_BUILTIN_MAX)
+ if (cmd < 0)
LOG_RULE_WARNING("IMPORT{builtin} '%s' unknown", value);
else
rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
@@ -1247,59 +1248,57 @@ static void add_rule(struct udev_rules *rules, char *line,
else if (streq(attr, "parent"))
rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL);
else
- LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "IMPORT", attr);
+ LOG_RULE_ERROR("Ignoring unknown %s{} type '%s'", "IMPORT", attr);
} else if (startswith(key, "TEST")) {
mode_t mode = 0;
if (op > OP_MATCH_MAX)
- LOG_AND_RETURN("invalid %s operation", "TEST");
+ LOG_AND_RETURN("Invalid %s operation", "TEST");
- attr = get_key_attribute(rules->udev,
- key + STRLEN("TEST"));
- if (attr != NULL) {
+ attr = get_key_attribute(key + STRLEN("TEST"));
+ if (attr) {
mode = strtol(attr, NULL, 8);
rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode);
} else
rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL);
} else if (startswith(key, "RUN")) {
- attr = get_key_attribute(rules->udev,
- key + STRLEN("RUN"));
- if (attr == NULL)
+ attr = get_key_attribute(key + STRLEN("RUN"));
+ if (!attr)
attr = "program";
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", "RUN");
+ LOG_AND_RETURN("Invalid %s operation", "RUN");
if (streq(attr, "builtin")) {
const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
- if (cmd < UDEV_BUILTIN_MAX)
- rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd);
- else
+ if (cmd < 0)
LOG_RULE_ERROR("RUN{builtin}: '%s' unknown", value);
+ else
+ rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd);
} else if (streq(attr, "program")) {
- const enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX;
+ const enum udev_builtin_cmd cmd = _UDEV_BUILTIN_MAX;
rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd);
} else
- LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "RUN", attr);
+ LOG_RULE_ERROR("Ignoring unknown %s{} type '%s'", "RUN", attr);
} else if (streq(key, "LABEL")) {
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_tmp.rule.rule.label_off = rules_add_string(rules, value);
} else if (streq(key, "GOTO")) {
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL);
} else if (startswith(key, "NAME")) {
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL);
@@ -1318,7 +1317,7 @@ static void add_rule(struct udev_rules *rules, char *line,
} else if (streq(key, "SYMLINK")) {
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
if (op < OP_MATCH_MAX)
rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL);
@@ -1331,15 +1330,15 @@ static void add_rule(struct udev_rules *rules, char *line,
char *endptr;
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
uid = strtoul(value, &endptr, 10);
if (endptr[0] == '\0')
rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
- else if (rules->resolve_names > 0 && strchr("$%", value[0]) == NULL) {
+ else if (rules->resolve_name_timing == RESOLVE_NAME_EARLY && !strchr("$%", value[0])) {
uid = add_uid(rules, value);
rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
- } else if (rules->resolve_names >= 0)
+ } else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER)
rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL);
rule_tmp.rule.rule.can_set_name = true;
@@ -1349,15 +1348,15 @@ static void add_rule(struct udev_rules *rules, char *line,
char *endptr;
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
gid = strtoul(value, &endptr, 10);
if (endptr[0] == '\0')
rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
- else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) {
+ else if ((rules->resolve_name_timing == RESOLVE_NAME_EARLY) && !strchr("$%", value[0])) {
gid = add_gid(rules, value);
rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
- } else if (rules->resolve_names >= 0)
+ } else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER)
rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL);
rule_tmp.rule.rule.can_set_name = true;
@@ -1367,7 +1366,7 @@ static void add_rule(struct udev_rules *rules, char *line,
char *endptr;
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
mode = strtol(value, &endptr, 8);
if (endptr[0] == '\0')
@@ -1380,17 +1379,17 @@ static void add_rule(struct udev_rules *rules, char *line,
const char *pos;
if (op == OP_REMOVE)
- LOG_AND_RETURN("invalid %s operation", key);
+ LOG_AND_RETURN("Invalid %s operation", key);
pos = strstr(value, "link_priority=");
- if (pos != NULL) {
+ if (pos) {
int prio = atoi(pos + STRLEN("link_priority="));
rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio);
}
pos = strstr(value, "string_escape=");
- if (pos != NULL) {
+ if (pos) {
pos += STRLEN("string_escape=");
if (startswith(pos, "none"))
rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL);
@@ -1399,54 +1398,51 @@ static void add_rule(struct udev_rules *rules, char *line,
}
pos = strstr(value, "db_persist");
- if (pos != NULL)
+ if (pos)
rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL);
pos = strstr(value, "nowatch");
- if (pos != NULL) {
- const int off = 0;
-
- rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off);
+ if (pos) {
+ static const int zero = 0;
+ rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &zero);
} else {
+ static const int one = 1;
pos = strstr(value, "watch");
- if (pos != NULL) {
- const int on = 1;
-
- rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on);
- }
+ if (pos)
+ rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &one);
}
pos = strstr(value, "static_node=");
- if (pos != NULL) {
+ if (pos) {
pos += STRLEN("static_node=");
rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, pos, NULL);
rule_tmp.rule.rule.has_static_node = true;
}
} else
- LOG_AND_RETURN("unknown key '%s'", key);
+ LOG_AND_RETURN("Unknown key '%s'", key);
}
/* add rule token and sort tokens */
rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur;
if (add_token(rules, &rule_tmp.rule) != 0 || sort_token(rules, &rule_tmp) != 0)
- LOG_RULE_ERROR("failed to add rule token");
+ LOG_RULE_ERROR("Failed to add rule token");
}
-static int parse_file(struct udev_rules *rules, const char *filename) {
+static int parse_file(UdevRules *rules, const char *filename) {
_cleanup_fclose_ FILE *f = NULL;
- unsigned int first_token;
- unsigned int filename_off;
+ unsigned first_token;
+ unsigned filename_off;
char line[UTIL_LINE_SIZE];
int line_nr = 0;
- unsigned int i;
+ unsigned i;
f = fopen(filename, "re");
if (!f) {
if (errno == ENOENT)
return 0;
- else
- return -errno;
+
+ return -errno;
}
if (null_or_empty_fd(fileno(f))) {
@@ -1458,7 +1454,7 @@ static int parse_file(struct udev_rules *rules, const char *filename) {
first_token = rules->token_cur;
filename_off = rules_add_string(rules, filename);
- while (fgets(line, sizeof(line), f) != NULL) {
+ while (fgets(line, sizeof(line), f)) {
char *key;
size_t len;
@@ -1478,7 +1474,7 @@ static int parse_file(struct udev_rules *rules, const char *filename) {
/* continue reading if backslash+newline is found */
while (line[len-2] == '\\') {
- if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL)
+ if (!fgets(&line[len-2], (sizeof(line)-len)+2, f))
break;
if (strlen(&line[len-2]) < 2)
break;
@@ -1487,7 +1483,7 @@ static int parse_file(struct udev_rules *rules, const char *filename) {
}
if (len+1 >= sizeof(line)) {
- log_error("line too long '%s':%u, ignored", filename, line_nr);
+ log_error("Line too long '%s':%u, ignored", filename, line_nr);
continue;
}
add_rule(rules, key, filename, filename_off, line_nr);
@@ -1497,7 +1493,7 @@ static int parse_file(struct udev_rules *rules, const char *filename) {
for (i = first_token+1; i < rules->token_cur; i++) {
if (rules->tokens[i].type == TK_A_GOTO) {
char *label = rules_str(rules, rules->tokens[i].key.value_off);
- unsigned int j;
+ unsigned j;
for (j = i+1; j < rules->token_cur; j++) {
if (rules->tokens[j].type != TK_RULE)
@@ -1516,37 +1512,37 @@ static int parse_file(struct udev_rules *rules, const char *filename) {
return 0;
}
-struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) {
- struct udev_rules *rules;
- struct udev_list file_list;
- struct token end_token;
- char **files, **f;
+int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) {
+ _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
+ _cleanup_strv_free_ char **files = NULL;
+ char **f;
int r;
- rules = new0(struct udev_rules, 1);
- if (rules == NULL)
- return NULL;
- rules->udev = udev;
- rules->resolve_names = resolve_names;
- udev_list_init(udev, &file_list, true);
+ assert(resolve_name_timing >= 0 && resolve_name_timing < _RESOLVE_NAME_TIMING_MAX);
+
+ rules = new(UdevRules, 1);
+ if (!rules)
+ return -ENOMEM;
+
+ *rules = (UdevRules) {
+ .resolve_name_timing = resolve_name_timing,
+ };
/* init token array and string buffer */
rules->tokens = malloc_multiply(PREALLOC_TOKEN, sizeof(struct token));
- if (rules->tokens == NULL)
- return udev_rules_unref(rules);
+ if (!rules->tokens)
+ return -ENOMEM;
rules->token_max = PREALLOC_TOKEN;
rules->strbuf = strbuf_new();
if (!rules->strbuf)
- return udev_rules_unref(rules);
+ return -ENOMEM;
udev_rules_check_timestamp(rules);
r = conf_files_list_strv(&files, ".rules", NULL, 0, rules_dirs);
- if (r < 0) {
- log_error_errno(r, "failed to enumerate rules files: %m");
- return udev_rules_unref(rules);
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate rules files: %m");
/*
* The offset value in the rules strct is limited; add all
@@ -1558,12 +1554,9 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) {
STRV_FOREACH(f, files)
parse_file(rules, *f);
- strv_free(files);
-
- memzero(&end_token, sizeof(struct token));
- end_token.type = TK_END;
+ struct token end_token = { .type = TK_END };
add_token(rules, &end_token);
- log_debug("rules contain %zu bytes tokens (%u * %zu bytes), %zu bytes strings",
+ log_debug("Rules contain %zu bytes tokens (%u * %zu bytes), %zu bytes strings",
rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len);
/* cleanup temporary strbuf data */
@@ -1581,11 +1574,12 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) {
rules->gids_max = 0;
dump_rules(rules);
- return rules;
+ *ret_rules = TAKE_PTR(rules);
+ return 0;
}
-struct udev_rules *udev_rules_unref(struct udev_rules *rules) {
- if (rules == NULL)
+UdevRules *udev_rules_free(UdevRules *rules) {
+ if (!rules)
return NULL;
free(rules->tokens);
strbuf_cleanup(rules->strbuf);
@@ -1594,19 +1588,19 @@ struct udev_rules *udev_rules_unref(struct udev_rules *rules) {
return mfree(rules);
}
-bool udev_rules_check_timestamp(struct udev_rules *rules) {
+bool udev_rules_check_timestamp(UdevRules *rules) {
if (!rules)
return false;
return paths_check_timestamp(rules_dirs, &rules->dirs_ts_usec, true);
}
-static int match_key(struct udev_rules *rules, struct token *token, const char *val) {
+static int match_key(UdevRules *rules, struct token *token, const char *val) {
char *key_value = rules_str(rules, token->key.value_off);
char *pos;
bool match = false;
- if (val == NULL)
+ if (!val)
val = "";
switch (token->key.glob) {
@@ -1627,7 +1621,7 @@ static int match_key(struct udev_rules *rules, struct token *token, const char *
const char *next;
next = strchr(s, '|');
- if (next != NULL) {
+ if (next) {
size_t matchlen = (size_t)(next - s);
match = (matchlen == len && strneq(s, val, matchlen));
@@ -1647,9 +1641,9 @@ static int match_key(struct udev_rules *rules, struct token *token, const char *
strscpy(value, sizeof(value), rules_str(rules, token->key.value_off));
key_value = value;
- while (key_value != NULL) {
+ while (key_value) {
pos = strchr(key_value, '|');
- if (pos != NULL) {
+ if (pos) {
pos[0] = '\0';
pos = &pos[1];
}
@@ -1674,11 +1668,9 @@ static int match_key(struct udev_rules *rules, struct token *token, const char *
return -1;
}
-static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) {
- const char *name;
- char nbuf[UTIL_NAME_SIZE];
- const char *value;
- char vbuf[UTIL_NAME_SIZE];
+static int match_attr(UdevRules *rules, sd_device *dev, UdevEvent *event, struct token *cur) {
+ char nbuf[UTIL_NAME_SIZE], vbuf[UTIL_NAME_SIZE];
+ const char *name, *value;
size_t len;
name = rules_str(rules, cur->key.attr_off);
@@ -1688,12 +1680,11 @@ static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct
name = nbuf;
_fallthrough_;
case SB_NONE:
- value = udev_device_get_sysattr_value(dev, name);
- if (value == NULL)
+ if (sd_device_get_sysattr_value(dev, name, &value) < 0)
return -1;
break;
case SB_SUBSYS:
- if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0)
+ if (util_resolve_subsys_kernel(name, vbuf, sizeof(vbuf), true) != 0)
return -1;
value = vbuf;
break;
@@ -1728,23 +1719,28 @@ enum escape_type {
ESCAPE_REPLACE,
};
-void udev_rules_apply_to_event(struct udev_rules *rules,
- struct udev_event *event,
- usec_t timeout_usec,
- usec_t timeout_warn_usec,
- struct udev_list *properties_list) {
- struct token *cur;
- struct token *rule;
+int udev_rules_apply_to_event(
+ UdevRules *rules,
+ UdevEvent *event,
+ usec_t timeout_usec,
+ Hashmap *properties_list) {
+ sd_device *dev = event->dev;
enum escape_type esc = ESCAPE_UNSET;
+ struct token *cur, *rule;
+ const char *action, *val;
bool can_set_name;
int r;
- if (rules->tokens == NULL)
- return;
+ if (!rules->tokens)
+ return 0;
+
+ r = sd_device_get_property_value(dev, "ACTION", &action);
+ if (r < 0)
+ return r;
- can_set_name = ((!streq(udev_device_get_action(event->dev), "remove")) &&
- (major(udev_device_get_devnum(event->dev)) > 0 ||
- udev_device_get_ifindex(event->dev) > 0));
+ can_set_name = (!streq(action, "remove") &&
+ (sd_device_get_devnum(dev, NULL) >= 0 ||
+ sd_device_get_ifindex(dev, NULL) >= 0));
/* loop through token list, match, run actions or forward to next rule */
cur = &rules->tokens[0];
@@ -1761,30 +1757,31 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
esc = ESCAPE_UNSET;
break;
case TK_M_ACTION:
- if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0)
+ if (match_key(rules, cur, action) != 0)
goto nomatch;
break;
case TK_M_DEVPATH:
- if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0)
+ if (sd_device_get_devpath(dev, &val) < 0)
+ goto nomatch;
+ if (match_key(rules, cur, val) != 0)
goto nomatch;
break;
case TK_M_KERNEL:
- if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0)
+ if (sd_device_get_sysname(dev, &val) < 0)
+ goto nomatch;
+ if (match_key(rules, cur, val) != 0)
goto nomatch;
break;
case TK_M_DEVLINK: {
- struct udev_list_entry *list_entry;
+ const char *devlink;
bool match = false;
- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) {
- const char *devlink;
-
- devlink = udev_list_entry_get_name(list_entry) + STRLEN("/dev/");
- if (match_key(rules, cur, devlink) == 0) {
+ FOREACH_DEVICE_DEVLINK(dev, devlink)
+ if (match_key(rules, cur, devlink + STRLEN("/dev/")) == 0) {
match = true;
break;
}
- }
+
if (!match)
goto nomatch;
break;
@@ -1795,51 +1792,48 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
break;
case TK_M_ENV: {
const char *key_name = rules_str(rules, cur->key.attr_off);
- const char *value;
-
- value = udev_device_get_property_value(event->dev, key_name);
- /* check global properties */
- if (!value && properties_list) {
- struct udev_list_entry *list_entry;
-
- list_entry = udev_list_get_entry(properties_list);
- list_entry = udev_list_entry_get_by_name(list_entry, key_name);
- if (list_entry != NULL)
- value = udev_list_entry_get_value(list_entry);
+ if (sd_device_get_property_value(dev, key_name, &val) < 0) {
+ /* check global properties */
+ if (properties_list)
+ val = hashmap_get(properties_list, key_name);
+ else
+ val = NULL;
}
- if (!value)
- value = "";
- if (match_key(rules, cur, value))
+ if (match_key(rules, cur, strempty(val)))
goto nomatch;
break;
}
case TK_M_TAG: {
- struct udev_list_entry *list_entry;
bool match = false;
+ const char *tag;
- udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) {
- if (streq(rules_str(rules, cur->key.value_off), udev_list_entry_get_name(list_entry))) {
+ FOREACH_DEVICE_TAG(dev, tag)
+ if (streq(rules_str(rules, cur->key.value_off), tag)) {
match = true;
break;
}
- }
+
if ((!match && (cur->key.op != OP_NOMATCH)) ||
(match && (cur->key.op == OP_NOMATCH)))
goto nomatch;
break;
}
case TK_M_SUBSYSTEM:
- if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0)
+ if (sd_device_get_subsystem(dev, &val) < 0)
+ goto nomatch;
+ if (match_key(rules, cur, val) != 0)
goto nomatch;
break;
case TK_M_DRIVER:
- if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0)
+ if (sd_device_get_driver(dev, &val) < 0)
+ goto nomatch;
+ if (match_key(rules, cur, val) != 0)
goto nomatch;
break;
case TK_M_ATTR:
- if (match_attr(rules, event->dev, event, cur) != 0)
+ if (match_attr(rules, dev, event, cur) != 0)
goto nomatch;
break;
case TK_M_SYSCTL: {
@@ -1872,7 +1866,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
next++;
/* loop over parents */
- event->dev_parent = event->dev;
+ event->dev_parent = dev;
for (;;) {
struct token *key;
@@ -1881,15 +1875,21 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
dump_token(rules, key);
switch(key->type) {
case TK_M_KERNELS:
- if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0)
+ if (sd_device_get_sysname(event->dev_parent, &val) < 0)
+ goto try_parent;
+ if (match_key(rules, key, val) != 0)
goto try_parent;
break;
case TK_M_SUBSYSTEMS:
- if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0)
+ if (sd_device_get_subsystem(event->dev_parent, &val) < 0)
+ goto try_parent;
+ if (match_key(rules, key, val) != 0)
goto try_parent;
break;
case TK_M_DRIVERS:
- if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0)
+ if (sd_device_get_driver(event->dev_parent, &val) < 0)
+ goto try_parent;
+ if (match_key(rules, key, val) != 0)
goto try_parent;
break;
case TK_M_ATTRS:
@@ -1897,7 +1897,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
goto try_parent;
break;
case TK_M_TAGS: {
- bool match = udev_device_has_tag(event->dev_parent, rules_str(rules, cur->key.value_off));
+ bool match = sd_device_has_tag(event->dev_parent, rules_str(rules, cur->key.value_off));
if (match && key->key.op == OP_NOMATCH)
goto try_parent;
@@ -1912,9 +1912,10 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
break;
try_parent:
- event->dev_parent = udev_device_get_parent(event->dev_parent);
- if (event->dev_parent == NULL)
+ if (sd_device_get_parent(event->dev_parent, &event->dev_parent) < 0) {
+ event->dev_parent = NULL;
goto nomatch;
+ }
}
/* move behind our sequence of parent match keys */
cur = next;
@@ -1926,13 +1927,15 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
int match;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false);
- if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) {
+ if (util_resolve_subsys_kernel(filename, filename, sizeof(filename), false) != 0) {
if (filename[0] != '/') {
char tmp[UTIL_PATH_SIZE];
+ if (sd_device_get_syspath(dev, &val) < 0)
+ goto nomatch;
+
strscpy(tmp, sizeof(tmp), filename);
- strscpyl(filename, sizeof(filename),
- udev_device_get_syspath(event->dev), "/", tmp, NULL);
+ strscpyl(filename, sizeof(filename), val, "/", tmp, NULL);
}
}
attr_subst_subdir(filename, sizeof(filename));
@@ -1947,17 +1950,16 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
break;
}
case TK_M_PROGRAM: {
- char program[UTIL_PATH_SIZE];
- char result[UTIL_LINE_SIZE];
+ char program[UTIL_PATH_SIZE], result[UTIL_LINE_SIZE];
event->program_result = mfree(event->program_result);
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program), false);
- log_debug("PROGRAM '%s' %s:%u",
- program,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "PROGRAM '%s' %s:%u",
+ program,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
- if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result)) < 0) {
+ if (udev_event_spawn(event, timeout_usec, true, program, result, sizeof(result)) < 0) {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
} else {
@@ -1967,7 +1969,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
if (IN_SET(esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
if (count > 0)
- log_debug("%i character(s) replaced" , count);
+ log_device_debug(dev, "Replaced %i character(s) from result of '%s'" , count, program);
}
event->program_result = strdup(result);
if (cur->key.op == OP_NOMATCH)
@@ -1979,7 +1981,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
char import[UTIL_PATH_SIZE];
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
- if (import_file_into_properties(event->dev, import) != 0)
+ if (import_file_into_properties(dev, import) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
break;
@@ -1988,12 +1990,12 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
char import[UTIL_PATH_SIZE];
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
- log_debug("IMPORT '%s' %s:%u",
- import,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "IMPORT '%s' %s:%u",
+ import,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
- if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import) != 0)
+ if (import_program_into_properties(event, timeout_usec, import) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
break;
@@ -2004,10 +2006,10 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
if (udev_builtin_run_once(cur->key.builtin_cmd)) {
/* check if we ran already */
if (event->builtin_run & (1 << cur->key.builtin_cmd)) {
- log_debug("IMPORT builtin skip '%s' %s:%u",
- udev_builtin_name(cur->key.builtin_cmd),
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "IMPORT builtin skip '%s' %s:%u",
+ udev_builtin_name(cur->key.builtin_cmd),
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
/* return the result from earlier run */
if (event->builtin_ret & (1 << cur->key.builtin_cmd))
if (cur->key.op != OP_NOMATCH)
@@ -2019,15 +2021,16 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
}
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command), false);
- log_debug("IMPORT builtin '%s' %s:%u",
- udev_builtin_name(cur->key.builtin_cmd),
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "IMPORT builtin '%s' %s:%u",
+ udev_builtin_name(cur->key.builtin_cmd),
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
- if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) {
+ r = udev_builtin_run(dev, cur->key.builtin_cmd, command, false);
+ if (r < 0) {
/* remember failure */
- log_debug("IMPORT builtin '%s' returned non-zero",
- udev_builtin_name(cur->key.builtin_cmd));
+ log_device_debug_errno(dev, r, "IMPORT builtin '%s' fails: %m",
+ udev_builtin_name(cur->key.builtin_cmd));
event->builtin_ret |= (1 << cur->key.builtin_cmd);
if (cur->key.op != OP_NOMATCH)
goto nomatch;
@@ -2035,16 +2038,14 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
break;
}
case TK_M_IMPORT_DB: {
- const char *key = rules_str(rules, cur->key.value_off);
- const char *value;
+ const char *key;
- value = udev_device_get_property_value(event->dev_db, key);
- if (value != NULL)
- udev_device_add_property(event->dev, key, value);
- else {
- if (cur->key.op != OP_NOMATCH)
- goto nomatch;
- }
+ key = rules_str(rules, cur->key.value_off);
+ if (event->dev_db_clone &&
+ sd_device_get_property_value(event->dev_db_clone, key, &val) >= 0)
+ device_add_property(dev, key, val);
+ else if (cur->key.op != OP_NOMATCH)
+ goto nomatch;
break;
}
case TK_M_IMPORT_CMDLINE: {
@@ -2053,18 +2054,17 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
const char *key;
key = rules_str(rules, cur->key.value_off);
-
r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &value);
if (r < 0)
- log_debug_errno(r, "Failed to read %s from /proc/cmdline, ignoring: %m", key);
+ log_device_debug_errno(dev, r, "Failed to read %s from /proc/cmdline, ignoring: %m", key);
else if (r > 0) {
imported = true;
if (value)
- udev_device_add_property(event->dev, key, value);
+ device_add_property(dev, key, value);
else
/* we import simple flags as 'FLAG=1' */
- udev_device_add_property(event->dev, key, "1");
+ device_add_property(dev, key, "1");
}
if (!imported && cur->key.op != OP_NOMATCH)
@@ -2075,7 +2075,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
char import[UTIL_PATH_SIZE];
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
- if (import_parent_into_properties(event->dev, import) != 0)
+ if (import_parent_into_properties(dev, import) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
break;
@@ -2091,7 +2091,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
esc = ESCAPE_REPLACE;
break;
case TK_A_DB_PERSIST:
- udev_device_set_db_persist(event->dev);
+ device_set_db_persist(dev);
break;
case TK_A_INOTIFY_WATCH:
if (event->inotify_watch_final)
@@ -2101,7 +2101,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
event->inotify_watch = cur->key.watch;
break;
case TK_A_DEVLINK_PRIO:
- udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio);
+ device_set_devlink_priority(dev, cur->key.devlink_prio);
break;
case TK_A_OWNER: {
char owner[UTIL_NAME_SIZE];
@@ -2113,15 +2113,15 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
event->owner_final = true;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner), false);
event->owner_set = true;
- r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL);
+ r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
if (r < 0) {
- log_unknown_owner(r, "user", owner);
+ log_unknown_owner(dev, r, "user", owner);
event->uid = 0;
}
- log_debug("OWNER %u %s:%u",
- event->uid,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "OWNER %u %s:%u",
+ event->uid,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
break;
}
case TK_A_GROUP: {
@@ -2134,38 +2134,37 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
event->group_final = true;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group), false);
event->group_set = true;
- r = get_group_creds(&gr, &event->gid);
+ r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING);
if (r < 0) {
- log_unknown_owner(r, "group", group);
+ log_unknown_owner(dev, r, "group", group);
event->gid = 0;
}
- log_debug("GROUP %u %s:%u",
- event->gid,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "GROUP %u %s:%u",
+ event->gid,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
break;
}
case TK_A_MODE: {
char mode_str[UTIL_NAME_SIZE];
mode_t mode;
- char *endptr;
if (event->mode_final)
break;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str), false);
- mode = strtol(mode_str, &endptr, 8);
- if (endptr[0] != '\0') {
- log_error("ignoring invalid mode '%s'", mode_str);
+ r = parse_mode(mode_str, &mode);
+ if (r < 0) {
+ log_device_error_errno(dev, r, "Failed to parse mode '%s': %m", mode_str);
break;
}
if (cur->key.op == OP_ASSIGN_FINAL)
event->mode_final = true;
event->mode_set = true;
event->mode = mode;
- log_debug("MODE %#o %s:%u",
- event->mode,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "MODE %#o %s:%u",
+ event->mode,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
break;
}
case TK_A_OWNER_ID:
@@ -2175,10 +2174,10 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
event->owner_final = true;
event->owner_set = true;
event->uid = cur->key.uid;
- log_debug("OWNER %u %s:%u",
- event->uid,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "OWNER %u %s:%u",
+ event->uid,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
break;
case TK_A_GROUP_ID:
if (event->group_final)
@@ -2187,10 +2186,10 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
event->group_final = true;
event->group_set = true;
event->gid = cur->key.gid;
- log_debug("GROUP %u %s:%u",
- event->gid,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "GROUP %u %s:%u",
+ event->gid,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
break;
case TK_A_MODE_ID:
if (event->mode_final)
@@ -2199,56 +2198,70 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
event->mode_final = true;
event->mode_set = true;
event->mode = cur->key.mode;
- log_debug("MODE %#o %s:%u",
- event->mode,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "MODE %#o %s:%u",
+ event->mode,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
break;
case TK_A_SECLABEL: {
+ _cleanup_free_ char *name = NULL, *label = NULL;
char label_str[UTIL_LINE_SIZE] = {};
- const char *name, *label;
- name = rules_str(rules, cur->key.attr_off);
+ name = strdup(rules_str(rules, cur->key.attr_off));
+ if (!name)
+ return log_oom();
+
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), label_str, sizeof(label_str), false);
- if (label_str[0] != '\0')
- label = label_str;
+ if (!isempty(label_str))
+ label = strdup(label_str);
else
- label = rules_str(rules, cur->key.value_off);
+ label = strdup(rules_str(rules, cur->key.value_off));
+ if (!label)
+ return log_oom();
if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
- udev_list_cleanup(&event->seclabel_list);
- udev_list_entry_add(&event->seclabel_list, name, label);
- log_debug("SECLABEL{%s}='%s' %s:%u",
- name, label,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ hashmap_clear_free_free(event->seclabel_list);
+
+ r = hashmap_ensure_allocated(&event->seclabel_list, NULL);
+ if (r < 0)
+ return log_oom();
+
+ r = hashmap_put(event->seclabel_list, name, label);
+ if (r < 0)
+ return log_oom();
+
+ name = label = NULL;
+
+ log_device_debug(dev, "SECLABEL{%s}='%s' %s:%u",
+ name, label,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
break;
}
case TK_A_ENV: {
- const char *name = rules_str(rules, cur->key.attr_off);
- char *value = rules_str(rules, cur->key.value_off);
char value_new[UTIL_NAME_SIZE];
- const char *value_old = NULL;
+ const char *name, *value_old;
- if (value[0] == '\0') {
+ name = rules_str(rules, cur->key.attr_off);
+ val = rules_str(rules, cur->key.value_off);
+ if (val[0] == '\0') {
if (cur->key.op == OP_ADD)
break;
- udev_device_add_property(event->dev, name, NULL);
+ device_add_property(dev, name, NULL);
break;
}
- if (cur->key.op == OP_ADD)
- value_old = udev_device_get_property_value(event->dev, name);
- if (value_old) {
+ if (cur->key.op == OP_ADD &&
+ sd_device_get_property_value(dev, name, &value_old) >= 0) {
char temp[UTIL_NAME_SIZE];
/* append value separated by space */
- udev_event_apply_format(event, value, temp, sizeof(temp), false);
+ udev_event_apply_format(event, val, temp, sizeof(temp), false);
strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL);
} else
- udev_event_apply_format(event, value, value_new, sizeof(value_new), false);
+ udev_event_apply_format(event, val, value_new, sizeof(value_new), false);
- udev_device_add_property(event->dev, name, value_new);
+ device_add_property(dev, name, value_new);
break;
}
case TK_A_TAG: {
@@ -2257,28 +2270,28 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag), false);
if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
- udev_device_cleanup_tags_list(event->dev);
+ device_cleanup_tags(dev);
for (p = tag; *p != '\0'; p++) {
if ((*p >= 'a' && *p <= 'z') ||
(*p >= 'A' && *p <= 'Z') ||
(*p >= '0' && *p <= '9') ||
IN_SET(*p, '-', '_'))
continue;
- log_error("ignoring invalid tag name '%s'", tag);
+ log_device_error(dev, "Ignoring invalid tag name '%s'", tag);
break;
}
if (cur->key.op == OP_REMOVE)
- udev_device_remove_tag(event->dev, tag);
+ device_remove_tag(dev, tag);
else
- udev_device_add_tag(event->dev, tag);
+ device_add_tag(dev, tag);
break;
}
case TK_A_NAME: {
- const char *name = rules_str(rules, cur->key.value_off);
-
char name_str[UTIL_PATH_SIZE];
+ const char *name;
int count;
+ name = rules_str(rules, cur->key.value_off);
if (event->name_final)
break;
if (cur->key.op == OP_ASSIGN_FINAL)
@@ -2287,40 +2300,38 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
if (IN_SET(esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
count = util_replace_chars(name_str, "/");
if (count > 0)
- log_debug("%i character(s) replaced", count);
+ log_device_debug(dev, "Replaced %i character(s) from result of NAME=\"%s\"", count, name);
}
- if (major(udev_device_get_devnum(event->dev)) &&
- !streq(name_str, udev_device_get_devnode(event->dev) + STRLEN("/dev/"))) {
- log_error("NAME=\"%s\" ignored, kernel device nodes cannot be renamed; please fix it in %s:%u\n",
- name,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ if (sd_device_get_devnum(dev, NULL) >= 0 &&
+ (sd_device_get_devname(dev, &val) < 0 ||
+ !streq(name_str, val + STRLEN("/dev/")))) {
+ log_device_error(dev, "Kernel device nodes cannot be renamed, ignoring NAME=\"%s\"; please fix it in %s:%u\n",
+ name,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
break;
}
- if (free_and_strdup(&event->name, name_str) < 0) {
- log_oom();
- return;
- }
- log_debug("NAME '%s' %s:%u",
- event->name,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ if (free_and_strdup(&event->name, name_str) < 0)
+ return log_oom();
+
+ log_device_debug(dev, "NAME '%s' %s:%u",
+ event->name,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
break;
}
case TK_A_DEVLINK: {
- char temp[UTIL_PATH_SIZE];
- char filename[UTIL_PATH_SIZE];
- char *pos, *next;
+ char temp[UTIL_PATH_SIZE], filename[UTIL_PATH_SIZE], *pos, *next;
int count = 0;
if (event->devlink_final)
break;
- if (major(udev_device_get_devnum(event->dev)) == 0)
+ if (sd_device_get_devnum(dev, NULL) < 0)
break;
if (cur->key.op == OP_ASSIGN_FINAL)
event->devlink_final = true;
if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
- udev_device_cleanup_devlinks_list(event->dev);
+ device_cleanup_devlinks(dev);
/* allow multiple symlinks separated by spaces */
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp), esc != ESCAPE_NONE);
@@ -2329,77 +2340,90 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
else if (esc == ESCAPE_REPLACE)
count = util_replace_chars(temp, "/");
if (count > 0)
- log_debug("%i character(s) replaced" , count);
+ log_device_debug(dev, "Replaced %i character(s) from result of LINK" , count);
pos = temp;
while (isspace(pos[0]))
pos++;
next = strchr(pos, ' ');
- while (next != NULL) {
+ while (next) {
next[0] = '\0';
- log_debug("LINK '%s' %s:%u", pos,
- rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
+ log_device_debug(dev, "LINK '%s' %s:%u", pos,
+ rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
- udev_device_add_devlink(event->dev, filename);
+ device_add_devlink(dev, filename);
while (isspace(next[1]))
next++;
pos = &next[1];
next = strchr(pos, ' ');
}
if (pos[0] != '\0') {
- log_debug("LINK '%s' %s:%u", pos,
- rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
+ log_device_debug(dev, "LINK '%s' %s:%u", pos,
+ rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
- udev_device_add_devlink(event->dev, filename);
+ device_add_devlink(dev, filename);
}
break;
}
case TK_A_ATTR: {
- const char *key_name = rules_str(rules, cur->key.attr_off);
- char attr[UTIL_PATH_SIZE];
- char value[UTIL_NAME_SIZE];
+ char attr[UTIL_PATH_SIZE], value[UTIL_NAME_SIZE];
_cleanup_fclose_ FILE *f = NULL;
+ const char *key_name;
- if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0)
- strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL);
+ key_name = rules_str(rules, cur->key.attr_off);
+ if (util_resolve_subsys_kernel(key_name, attr, sizeof(attr), false) != 0 &&
+ sd_device_get_syspath(dev, &val) >= 0)
+ strscpyl(attr, sizeof(attr), val, "/", key_name, NULL);
attr_subst_subdir(attr, sizeof(attr));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
- log_debug("ATTR '%s' writing '%s' %s:%u", attr, value,
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
+ log_device_debug(dev, "ATTR '%s' writing '%s' %s:%u", attr, value,
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
f = fopen(attr, "we");
- if (f == NULL)
- log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr);
+ if (!f)
+ log_device_error_errno(dev, errno, "Failed to open ATTR{%s} for writing: %m", attr);
else if (fprintf(f, "%s", value) <= 0)
- log_error_errno(errno, "error writing ATTR{%s}: %m", attr);
+ log_device_error_errno(dev, errno, "Failed to write ATTR{%s}: %m", attr);
break;
}
case TK_A_SYSCTL: {
- char filename[UTIL_PATH_SIZE];
- char value[UTIL_NAME_SIZE];
+ char filename[UTIL_PATH_SIZE], value[UTIL_NAME_SIZE];
udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
sysctl_normalize(filename);
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
- log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value,
- rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
+ log_device_debug(dev, "SYSCTL '%s' writing '%s' %s:%u", filename, value,
+ rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
r = sysctl_write(filename, value);
if (r < 0)
- log_error_errno(r, "error writing SYSCTL{%s}='%s': %m", filename, value);
+ log_device_error_errno(dev, r, "Failed to write SYSCTL{%s}='%s': %m", filename, value);
break;
}
case TK_A_RUN_BUILTIN:
case TK_A_RUN_PROGRAM: {
- struct udev_list_entry *entry;
+ _cleanup_free_ char *cmd = NULL;
if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
- udev_list_cleanup(&event->run_list);
- log_debug("RUN '%s' %s:%u",
- rules_str(rules, cur->key.value_off),
- rules_str(rules, rule->rule.filename_off),
- rule->rule.filename_line);
- entry = udev_list_entry_add(&event->run_list, rules_str(rules, cur->key.value_off), NULL);
- udev_list_entry_set_num(entry, cur->key.builtin_cmd);
+ hashmap_clear_free_key(event->run_list);
+
+ r = hashmap_ensure_allocated(&event->run_list, NULL);
+ if (r < 0)
+ return log_oom();
+
+ cmd = strdup(rules_str(rules, cur->key.value_off));
+ if (!cmd)
+ return log_oom();
+
+ r = hashmap_put(event->run_list, cmd, INT_TO_PTR(cur->key.builtin_cmd));
+ if (r < 0)
+ return log_oom();
+
+ cmd = NULL;
+
+ log_device_debug(dev, "RUN '%s' %s:%u",
+ rules_str(rules, cur->key.value_off),
+ rules_str(rules, rule->rule.filename_off),
+ rule->rule.filename_line);
break;
}
case TK_A_GOTO:
@@ -2408,13 +2432,13 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
cur = &rules->tokens[cur->key.rule_goto];
continue;
case TK_END:
- return;
+ return 0;
case TK_M_PARENTS_MIN:
case TK_M_PARENTS_MAX:
case TK_M_MAX:
case TK_UNSET:
- log_error("wrong type %u", cur->type);
+ log_device_error(dev, "Wrong type %u", cur->type);
goto nomatch;
}
@@ -2424,9 +2448,11 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
/* fast-forward to next rule */
cur = rule + rule->rule.token_count;
}
+
+ return 0;
}
-int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
+int udev_rules_apply_static_dev_perms(UdevRules *rules) {
struct token *cur;
struct token *rule;
uid_t uid = 0;
@@ -2438,7 +2464,7 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
_cleanup_free_ char *path = NULL;
int r;
- if (rules->tokens == NULL)
+ if (!rules->tokens)
return 0;
cur = &rules->tokens[0];
@@ -2481,7 +2507,7 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
/* we assure, that the permissions tokens are sorted before the static token */
- if (mode == 0 && uid == 0 && gid == 0 && tags == NULL)
+ if (mode == 0 && uid == 0 && gid == 0 && !tags)
goto next;
strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL);
@@ -2498,14 +2524,14 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) {
strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL);
r = mkdir_p(tags_dir, 0755);
if (r < 0)
- return log_error_errno(r, "failed to create %s: %m", tags_dir);
+ return log_error_errno(r, "Failed to create %s: %m", tags_dir);
unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/.");
strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL);
r = symlink(device_node, tag_symlink);
if (r < 0 && errno != EEXIST)
- return log_error_errno(errno, "failed to create symlink %s -> %s: %m",
+ return log_error_errno(errno, "Failed to create symlink %s -> %s: %m",
tag_symlink, device_node);
}
}
diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c
index 7864f57aa5..68b51d04a3 100644
--- a/src/udev/udev-watch.c
+++ b/src/udev/udev-watch.c
@@ -2,139 +2,176 @@
/*
* Copyright © 2009 Canonical Ltd.
* Copyright © 2009 Scott James Remnant <scott@netsplit.com>
- *
*/
-#include <errno.h>
-#include <stddef.h>
-#include <stdio.h>
#include <sys/inotify.h>
#include <unistd.h>
+#include "alloc-util.h"
+#include "device-private.h"
+#include "device-util.h"
#include "dirent-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
#include "stdio-util.h"
-#include "udev.h"
+#include "udev-watch.h"
static int inotify_fd = -1;
/* inotify descriptor, will be shared with rules directory;
* set to cloexec since we need our children to be able to add
- * watches for us
- */
-int udev_watch_init(struct udev *udev) {
+ * watches for us. */
+int udev_watch_init(void) {
inotify_fd = inotify_init1(IN_CLOEXEC);
if (inotify_fd < 0)
- log_error_errno(errno, "inotify_init failed: %m");
+ return -errno;
+
return inotify_fd;
}
-/* move any old watches directory out of the way, and then restore
- * the watches
- */
-void udev_watch_restore(struct udev *udev) {
+/* Move any old watches directory out of the way, and then restore the watches. */
+int udev_watch_restore(void) {
+ struct dirent *ent;
+ DIR *dir;
+ int r;
+
if (inotify_fd < 0)
- return;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid inotify descriptor.");
- if (rename("/run/udev/watch", "/run/udev/watch.old") == 0) {
- DIR *dir;
- struct dirent *ent;
+ if (rename("/run/udev/watch", "/run/udev/watch.old") < 0) {
+ if (errno != ENOENT)
+ return log_warning_errno(errno, "Failed to move watches directory /run/udev/watch. Old watches will not be restored: %m");
- dir = opendir("/run/udev/watch.old");
- if (dir == NULL) {
- log_error_errno(errno, "unable to open old watches dir /run/udev/watch.old; old watches will not be restored: %m");
- return;
- }
+ return 0;
+ }
- FOREACH_DIRENT_ALL(ent, dir, break) {
- char device[UTIL_PATH_SIZE];
- ssize_t len;
- struct udev_device *dev;
+ dir = opendir("/run/udev/watch.old");
+ if (!dir)
+ return log_warning_errno(errno, "Failed to open old watches directory /run/udev/watch.old. Old watches will not be restored: %m");
- if (ent->d_name[0] == '.')
- continue;
+ FOREACH_DIRENT_ALL(ent, dir, break) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ _cleanup_free_ char *device = NULL;
- len = readlinkat(dirfd(dir), ent->d_name, device, sizeof(device));
- if (len <= 0 || len == (ssize_t)sizeof(device))
- goto unlink;
- device[len] = '\0';
+ if (ent->d_name[0] == '.')
+ continue;
- dev = udev_device_new_from_device_id(udev, device);
- if (dev == NULL)
- goto unlink;
+ r = readlinkat_malloc(dirfd(dir), ent->d_name, &device);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read link '/run/udev/watch.old/%s', ignoring: %m", ent->d_name);
+ goto unlink;
+ }
- log_debug("restoring old watch on '%s'", udev_device_get_devnode(dev));
- udev_watch_begin(udev, dev);
- udev_device_unref(dev);
-unlink:
- (void) unlinkat(dirfd(dir), ent->d_name, 0);
+ r = sd_device_new_from_device_id(&dev, device);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to create sd_device object for '%s', ignoring: %m", device);
+ goto unlink;
}
- closedir(dir);
- rmdir("/run/udev/watch.old");
+ log_device_debug(dev, "Restoring old watch");
+ (void) udev_watch_begin(dev);
+unlink:
+ (void) unlinkat(dirfd(dir), ent->d_name, 0);
+ }
- } else if (errno != ENOENT)
- log_error_errno(errno, "unable to move watches dir /run/udev/watch; old watches will not be restored: %m");
+ (void) closedir(dir);
+ (void) rmdir("/run/udev/watch.old");
+
+ return 0;
}
-void udev_watch_begin(struct udev *udev, struct udev_device *dev) {
- char filename[sizeof("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
- int wd;
- int r;
+int udev_watch_begin(sd_device *dev) {
+ char filename[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
+ const char *devnode, *id_filename;
+ int wd, r;
if (inotify_fd < 0)
- return;
-
- log_debug("adding watch on '%s'", udev_device_get_devnode(dev));
- wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE);
- if (wd < 0) {
- log_error_errno(errno, "inotify_add_watch(%d, %s, %o) failed: %m",
- inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE);
- return;
- }
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid inotify descriptor.");
+
+ r = sd_device_get_devname(dev, &devnode);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get device name: %m");
+
+ log_device_debug(dev, "Adding watch on '%s'", devnode);
+ wd = inotify_add_watch(inotify_fd, devnode, IN_CLOSE_WRITE);
+ if (wd < 0)
+ return log_device_full(dev,
+ errno == ENOENT ? LOG_DEBUG : LOG_ERR,
+ errno,
+ "Failed to add device '%s' to watch: %m", devnode);
+
+ device_set_watch_handle(dev, wd);
xsprintf(filename, "/run/udev/watch/%d", wd);
- mkdir_parents(filename, 0755);
- unlink(filename);
- r = symlink(udev_device_get_id_filename(dev), filename);
+ r = mkdir_parents(filename, 0755);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to create parent directory of '%s': %m", filename);
+ (void) unlink(filename);
+
+ r = device_get_id_filename(dev, &id_filename);
if (r < 0)
- log_error_errno(errno, "Failed to create symlink %s: %m", filename);
+ return log_device_error_errno(dev, r, "Failed to get device id-filename: %m");
- udev_device_set_watch_handle(dev, wd);
+ if (symlink(id_filename, filename) < 0)
+ return log_device_error_errno(dev, errno, "Failed to create symlink %s: %m", filename);
+
+ return 0;
}
-void udev_watch_end(struct udev *udev, struct udev_device *dev) {
- int wd;
- char filename[sizeof("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
+int udev_watch_end(sd_device *dev) {
+ char filename[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
+ int wd, r;
if (inotify_fd < 0)
- return;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid inotify descriptor.");
- wd = udev_device_get_watch_handle(dev);
- if (wd < 0)
- return;
+ r = device_get_watch_handle(dev, &wd);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get watch handle, ignoring: %m");
- log_debug("removing watch on '%s'", udev_device_get_devnode(dev));
- inotify_rm_watch(inotify_fd, wd);
+ log_device_debug(dev, "Removing watch");
+ (void) inotify_rm_watch(inotify_fd, wd);
xsprintf(filename, "/run/udev/watch/%d", wd);
- unlink(filename);
+ (void) unlink(filename);
+
+ device_set_watch_handle(dev, -1);
- udev_device_set_watch_handle(dev, -1);
+ return 0;
}
-struct udev_device *udev_watch_lookup(struct udev *udev, int wd) {
- char filename[sizeof("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
- char device[UTIL_NAME_SIZE];
- ssize_t len;
+int udev_watch_lookup(int wd, sd_device **ret) {
+ char filename[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
+ _cleanup_free_ char *device = NULL;
+ int r;
+
+ assert(ret);
+
+ if (inotify_fd < 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid inotify descriptor.");
- if (inotify_fd < 0 || wd < 0)
- return NULL;
+ if (wd < 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid watch handle.");
xsprintf(filename, "/run/udev/watch/%d", wd);
- len = readlink(filename, device, sizeof(device));
- if (len <= 0 || (size_t)len == sizeof(device))
- return NULL;
- device[len] = '\0';
+ r = readlink_malloc(filename, &device);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read link '%s': %m", filename);
+
+ r = sd_device_new_from_device_id(ret, device);
+ if (r == -ENODEV)
+ return 0;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to create sd_device object for '%s': %m", device);
- return udev_device_new_from_device_id(udev, device);
+ return 1;
}
diff --git a/src/udev/udev-watch.h b/src/udev/udev-watch.h
new file mode 100644
index 0000000000..24a136d261
--- /dev/null
+++ b/src/udev/udev-watch.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#pragma once
+
+#include "sd-device.h"
+
+int udev_watch_init(void);
+int udev_watch_restore(void);
+int udev_watch_begin(sd_device *dev);
+int udev_watch_end(sd_device *dev);
+int udev_watch_lookup(int wd, sd_device **ret);
diff --git a/src/udev/udev.conf b/src/udev/udev.conf
index 0d812d4a65..7deb7715be 100644
--- a/src/udev/udev.conf
+++ b/src/udev/udev.conf
@@ -3,4 +3,8 @@
# udevd is also started in the initrd. When this file is modified you might
# also want to rebuild the initrd, so that it will include the modified configuration.
-#udev_log="info"
+#udev_log=info
+#children_max=
+#exec_delay=
+#event_timeout=180
+#resolve_names=early
diff --git a/src/udev/udev.h b/src/udev/udev.h
index 4596d0ea01..3bc69ff6c4 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -3,39 +3,35 @@
/*
* Copyright © 2003 Greg Kroah-Hartman <greg@kroah.com>
- *
*/
-#include <sys/param.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-
-#include "libudev.h"
+#include "sd-device.h"
#include "sd-netlink.h"
-#include "label.h"
-#include "libudev-private.h"
+#include "hashmap.h"
#include "macro.h"
-#include "strv.h"
+#include "udev-util.h"
#include "util.h"
-struct udev_event {
- struct udev *udev;
- struct udev_device *dev;
- struct udev_device *dev_parent;
- struct udev_device *dev_db;
+#define READ_END 0
+#define WRITE_END 1
+
+typedef struct UdevEvent {
+ sd_device *dev;
+ sd_device *dev_parent;
+ sd_device *dev_db_clone;
char *name;
char *program_result;
mode_t mode;
uid_t uid;
gid_t gid;
- struct udev_list seclabel_list;
- struct udev_list run_list;
- int exec_delay;
+ Hashmap *seclabel_list;
+ Hashmap *run_list;
+ usec_t exec_delay_usec;
usec_t birth_usec;
sd_netlink *rtnl;
- unsigned int builtin_run;
- unsigned int builtin_ret;
+ unsigned builtin_run;
+ unsigned builtin_ret;
bool inotify_watch;
bool inotify_watch_final;
bool group_set;
@@ -47,160 +43,40 @@ struct udev_event {
bool name_final;
bool devlink_final;
bool run_final;
-};
-
-struct udev_watch {
- struct udev_list_node node;
- int handle;
- char *name;
-};
+} UdevEvent;
/* udev-rules.c */
-struct udev_rules;
-struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names);
-struct udev_rules *udev_rules_unref(struct udev_rules *rules);
-bool udev_rules_check_timestamp(struct udev_rules *rules);
-void udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event,
- usec_t timeout_usec, usec_t timeout_warn_usec,
- struct udev_list *properties_list);
-int udev_rules_apply_static_dev_perms(struct udev_rules *rules);
+typedef struct UdevRules UdevRules;
+
+int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing);
+UdevRules *udev_rules_free(UdevRules *rules);
+
+bool udev_rules_check_timestamp(UdevRules *rules);
+int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event,
+ usec_t timeout_usec,
+ Hashmap *properties_list);
+int udev_rules_apply_static_dev_perms(UdevRules *rules);
+
+static inline usec_t udev_warn_timeout(usec_t timeout_usec) {
+ return DIV_ROUND_UP(timeout_usec, 3);
+}
/* udev-event.c */
-struct udev_event *udev_event_new(struct udev_device *dev);
-void udev_event_unref(struct udev_event *event);
-size_t udev_event_apply_format(struct udev_event *event,
- const char *src, char *dest, size_t size,
- bool replace_whitespace);
-int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string,
- char *result, size_t maxsize, int read_value);
-int udev_event_spawn(struct udev_event *event,
+UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rtnl);
+UdevEvent *udev_event_free(UdevEvent *event);
+ssize_t udev_event_apply_format(UdevEvent *event,
+ const char *src, char *dest, size_t size,
+ bool replace_whitespace);
+int udev_event_spawn(UdevEvent *event,
usec_t timeout_usec,
- usec_t timeout_warn_usec,
bool accept_failure,
const char *cmd, char *result, size_t ressize);
-void udev_event_execute_rules(struct udev_event *event,
- usec_t timeout_usec, usec_t timeout_warn_usec,
- struct udev_list *properties_list,
- struct udev_rules *rules);
-void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec);
-int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]);
-
-/* udev-watch.c */
-int udev_watch_init(struct udev *udev);
-void udev_watch_restore(struct udev *udev);
-void udev_watch_begin(struct udev *udev, struct udev_device *dev);
-void udev_watch_end(struct udev *udev, struct udev_device *dev);
-struct udev_device *udev_watch_lookup(struct udev *udev, int wd);
-
-/* udev-node.c */
-void udev_node_add(struct udev_device *dev, bool apply,
- mode_t mode, uid_t uid, gid_t gid,
- struct udev_list *seclabel_list);
-void udev_node_remove(struct udev_device *dev);
-void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old);
-
-/* udev-ctrl.c */
-struct udev_ctrl;
-struct udev_ctrl *udev_ctrl_new(struct udev *udev);
-struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd);
-int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl);
-struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl);
-int udev_ctrl_cleanup(struct udev_ctrl *uctrl);
-struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl);
-int udev_ctrl_get_fd(struct udev_ctrl *uctrl);
-int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout);
-int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout);
-int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout);
-int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout);
-int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout);
-int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout);
-int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout);
-int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout);
-struct udev_ctrl_connection;
-struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl);
-struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn);
-struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn);
-struct udev_ctrl_msg;
-struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn);
-struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg);
-int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg);
-int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg);
-int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg);
-int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg);
-int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg);
-int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg);
-const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg);
-int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg);
-
-/* built-in commands */
-enum udev_builtin_cmd {
-#if HAVE_BLKID
- UDEV_BUILTIN_BLKID,
-#endif
- UDEV_BUILTIN_BTRFS,
- UDEV_BUILTIN_HWDB,
- UDEV_BUILTIN_INPUT_ID,
- UDEV_BUILTIN_KEYBOARD,
-#if HAVE_KMOD
- UDEV_BUILTIN_KMOD,
-#endif
- UDEV_BUILTIN_NET_ID,
- UDEV_BUILTIN_NET_LINK,
- UDEV_BUILTIN_PATH_ID,
- UDEV_BUILTIN_USB_ID,
-#if HAVE_ACL
- UDEV_BUILTIN_UACCESS,
-#endif
- UDEV_BUILTIN_MAX
-};
-struct udev_builtin {
- const char *name;
- int (*cmd)(struct udev_device *dev, int argc, char *argv[], bool test);
- const char *help;
- int (*init)(struct udev *udev);
- void (*exit)(struct udev *udev);
- bool (*validate)(struct udev *udev);
- bool run_once;
-};
-#if HAVE_BLKID
-extern const struct udev_builtin udev_builtin_blkid;
-#endif
-extern const struct udev_builtin udev_builtin_btrfs;
-extern const struct udev_builtin udev_builtin_hwdb;
-extern const struct udev_builtin udev_builtin_input_id;
-extern const struct udev_builtin udev_builtin_keyboard;
-#if HAVE_KMOD
-extern const struct udev_builtin udev_builtin_kmod;
-#endif
-extern const struct udev_builtin udev_builtin_net_id;
-extern const struct udev_builtin udev_builtin_net_setup_link;
-extern const struct udev_builtin udev_builtin_path_id;
-extern const struct udev_builtin udev_builtin_usb_id;
-extern const struct udev_builtin udev_builtin_uaccess;
-void udev_builtin_init(struct udev *udev);
-void udev_builtin_exit(struct udev *udev);
-enum udev_builtin_cmd udev_builtin_lookup(const char *command);
-const char *udev_builtin_name(enum udev_builtin_cmd cmd);
-bool udev_builtin_run_once(enum udev_builtin_cmd cmd);
-int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test);
-void udev_builtin_list(struct udev *udev);
-bool udev_builtin_validate(struct udev *udev);
-int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val);
-int udev_builtin_hwdb_lookup(struct udev_device *dev, const char *prefix, const char *modalias,
- const char *filter, bool test);
+int udev_event_execute_rules(UdevEvent *event,
+ usec_t timeout_usec,
+ Hashmap *properties_list,
+ UdevRules *rules);
+void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec);
-/* udevadm commands */
-struct udevadm_cmd {
- const char *name;
- int (*cmd)(struct udev *udev, int argc, char *argv[]);
- const char *help;
- int debug;
-};
-extern const struct udevadm_cmd udevadm_info;
-extern const struct udevadm_cmd udevadm_trigger;
-extern const struct udevadm_cmd udevadm_settle;
-extern const struct udevadm_cmd udevadm_control;
-extern const struct udevadm_cmd udevadm_monitor;
-extern const struct udevadm_cmd udevadm_hwdb;
-extern const struct udevadm_cmd udevadm_test;
-extern const struct udevadm_cmd udevadm_test_builtin;
+/* Cleanup functions */
+DEFINE_TRIVIAL_CLEANUP_FUNC(UdevEvent*, udev_event_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRules*, udev_rules_free);
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
index a84cc156cb..d9320418cf 100644
--- a/src/udev/udevadm-control.c
+++ b/src/udev/udevadm-control.c
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
@@ -20,13 +19,15 @@
#include <string.h>
#include <unistd.h>
+#include "parse-util.h"
#include "process-util.h"
+#include "syslog-util.h"
#include "time-util.h"
-#include "udev-util.h"
-#include "udev.h"
-#include "udevadm-util.h"
+#include "udevadm.h"
+#include "udev-ctrl.h"
+#include "util.h"
-static void print_help(void) {
+static int help(void) {
printf("%s control OPTION\n\n"
"Control the udev daemon.\n\n"
" -h --help Show this help\n"
@@ -40,12 +41,14 @@ static void print_help(void) {
" -m --children-max=N Maximum number of children\n"
" -t --timeout=SECONDS Maximum time to block for a reply\n"
, program_invocation_short_name);
+
+ return 0;
}
-static int adm_control(struct udev *udev, int argc, char *argv[]) {
+int control_main(int argc, char *argv[], void *userdata) {
_cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
int timeout = 60;
- int rc = 1, c;
+ int c, r;
static const struct option options[] = {
{ "exit", no_argument, NULL, 'e' },
@@ -63,80 +66,70 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
{}
};
- if (must_be_root() < 0)
- return 1;
+ r = must_be_root();
+ if (r < 0)
+ return r;
+
+ if (argc <= 1)
+ log_error("Option missing");
- uctrl = udev_ctrl_new(udev);
- if (uctrl == NULL)
- return 2;
+ uctrl = udev_ctrl_new();
+ if (!uctrl)
+ return -ENOMEM;
while ((c = getopt_long(argc, argv, "el:sSRp:m:t:Vh", options, NULL)) >= 0)
switch (c) {
case 'e':
- if (udev_ctrl_send_exit(uctrl, timeout) < 0)
- rc = 2;
- else
- rc = 0;
+ r = udev_ctrl_send_exit(uctrl, timeout);
+ if (r < 0)
+ return r;
break;
- case 'l': {
- int i;
+ case 'l':
+ r = log_level_from_string(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse log priority '%s': %m", optarg);
- i = util_log_priority(optarg);
- if (i < 0) {
- log_error("invalid number '%s'", optarg);
- return rc;
- }
- if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0)
- rc = 2;
- else
- rc = 0;
+ r = udev_ctrl_send_set_log_level(uctrl, r, timeout);
+ if (r < 0)
+ return r;
break;
- }
case 's':
- if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0)
- rc = 2;
- else
- rc = 0;
+ r = udev_ctrl_send_stop_exec_queue(uctrl, timeout);
+ if (r < 0)
+ return r;
break;
case 'S':
- if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0)
- rc = 2;
- else
- rc = 0;
+ r = udev_ctrl_send_start_exec_queue(uctrl, timeout);
+ if (r < 0)
+ return r;
break;
case 'R':
- if (udev_ctrl_send_reload(uctrl, timeout) < 0)
- rc = 2;
- else
- rc = 0;
+ r = udev_ctrl_send_reload(uctrl, timeout);
+ if (r < 0)
+ return r;
break;
case 'p':
- if (strchr(optarg, '=') == NULL) {
+ if (!strchr(optarg, '=')) {
log_error("expect <KEY>=<value> instead of '%s'", optarg);
- return rc;
+ return -EINVAL;
}
- if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0)
- rc = 2;
- else
- rc = 0;
+ r = udev_ctrl_send_set_env(uctrl, optarg, timeout);
+ if (r < 0)
+ return r;
break;
case 'm': {
- char *endp;
- int i;
+ unsigned i;
- i = strtoul(optarg, &endp, 0);
- if (endp[0] != '\0' || i < 1) {
- log_error("invalid number '%s'", optarg);
- return rc;
- }
- if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0)
- rc = 2;
- else
- rc = 0;
+ r = safe_atou(optarg, &i);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse maximum number of events '%s': %m", optarg);
+
+ r = udev_ctrl_send_set_children_max(uctrl, i, timeout);
+ if (r < 0)
+ return r;
break;
}
case 't': {
- int r, seconds;
usec_t s;
r = parse_sec(optarg, &s);
@@ -144,33 +137,28 @@ static int adm_control(struct udev *udev, int argc, char *argv[]) {
return log_error_errno(r, "Failed to parse timeout value '%s'.", optarg);
if (DIV_ROUND_UP(s, USEC_PER_SEC) > INT_MAX)
- log_error("Timeout value is out of range.");
- else {
- seconds = s != USEC_INFINITY ? (int) DIV_ROUND_UP(s, USEC_PER_SEC) : INT_MAX;
- timeout = seconds;
- rc = 0;
- }
+ log_error("Timeout value is out of range, ignoring.");
+ else
+ timeout = s != USEC_INFINITY ? (int) DIV_ROUND_UP(s, USEC_PER_SEC) : INT_MAX;
break;
}
case 'V':
- print_version();
- rc = 0;
- break;
+ return print_version();
case 'h':
- print_help();
- rc = 0;
- break;
+ return help();
+ case '?':
+ return -EINVAL;
+ default:
+ assert_not_reached("Unknown option.");
}
- if (optind < argc)
+ if (optind < argc) {
log_error("Extraneous argument: %s", argv[optind]);
- else if (optind == 1)
+ return -EINVAL;
+ } else if (optind == 1) {
log_error("Option missing");
- return rc;
-}
+ return -EINVAL;
+ }
-const struct udevadm_cmd udevadm_control = {
- .name = "control",
- .cmd = adm_control,
- .help = "Control the udev daemon",
-};
+ return 0;
+}
diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c
index 02408a4285..7f8960f549 100644
--- a/src/udev/udevadm-hwdb.c
+++ b/src/udev/udevadm-hwdb.c
@@ -1,544 +1,18 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <ctype.h>
#include <getopt.h>
-#include <stdlib.h>
-#include <string.h>
-#include "alloc-util.h"
-#include "conf-files.h"
-#include "fileio.h"
-#include "fs-util.h"
-#include "hwdb-internal.h"
#include "hwdb-util.h"
-#include "label.h"
-#include "mkdir.h"
-#include "strbuf.h"
-#include "string-util.h"
-#include "udev.h"
-#include "udevadm-util.h"
+#include "udevadm.h"
#include "util.h"
-/*
- * Generic udev properties, key/value database based on modalias strings.
- * Uses a Patricia/radix trie to index all matches for efficient lookup.
- */
+static const char *arg_test = NULL;
+static const char *arg_root = NULL;
+static const char *arg_hwdb_bin_dir = NULL;
+static bool arg_update = false;
+static bool arg_strict = false;
-static const char * const conf_file_dirs[] = {
- "/etc/udev/hwdb.d",
- UDEVLIBEXECDIR "/hwdb.d",
- NULL
-};
-
-/* in-memory trie objects */
-struct trie {
- struct trie_node *root;
- struct strbuf *strings;
-
- size_t nodes_count;
- size_t children_count;
- size_t values_count;
-};
-
-struct trie_node {
- /* prefix, common part for all children of this node */
- size_t prefix_off;
-
- /* sorted array of pointers to children nodes */
- struct trie_child_entry *children;
- uint8_t children_count;
-
- /* sorted array of key/value pairs */
- struct trie_value_entry *values;
- size_t values_count;
-};
-
-/* children array item with char (0-255) index */
-struct trie_child_entry {
- uint8_t c;
- struct trie_node *child;
-};
-
-/* value array item with key/value pairs */
-struct trie_value_entry {
- size_t key_off;
- size_t value_off;
-};
-
-static int trie_children_cmp(const void *v1, const void *v2) {
- const struct trie_child_entry *n1 = v1;
- const struct trie_child_entry *n2 = v2;
-
- return n1->c - n2->c;
-}
-
-static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) {
- struct trie_child_entry *child;
-
- /* extend array, add new entry, sort for bisection */
- child = reallocarray(node->children, node->children_count + 1, sizeof(struct trie_child_entry));
- if (!child)
- return -ENOMEM;
-
- node->children = child;
- trie->children_count++;
- node->children[node->children_count].c = c;
- node->children[node->children_count].child = node_child;
- node->children_count++;
- qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
- trie->nodes_count++;
-
- return 0;
-}
-
-static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) {
- struct trie_child_entry *child;
- struct trie_child_entry search;
-
- search.c = c;
- child = bsearch_safe(&search,
- node->children, node->children_count, sizeof(struct trie_child_entry),
- trie_children_cmp);
- if (child)
- return child->child;
- return NULL;
-}
-
-static void trie_node_cleanup(struct trie_node *node) {
- size_t i;
-
- for (i = 0; i < node->children_count; i++)
- trie_node_cleanup(node->children[i].child);
- free(node->children);
- free(node->values);
- free(node);
-}
-
-static int trie_values_cmp(const void *v1, const void *v2, void *arg) {
- const struct trie_value_entry *val1 = v1;
- const struct trie_value_entry *val2 = v2;
- struct trie *trie = arg;
-
- return strcmp(trie->strings->buf + val1->key_off,
- trie->strings->buf + val2->key_off);
-}
-
-static int trie_node_add_value(struct trie *trie, struct trie_node *node,
- const char *key, const char *value) {
- ssize_t k, v;
- struct trie_value_entry *val;
-
- k = strbuf_add_string(trie->strings, key, strlen(key));
- if (k < 0)
- return k;
- v = strbuf_add_string(trie->strings, value, strlen(value));
- if (v < 0)
- return v;
-
- if (node->values_count) {
- struct trie_value_entry search = {
- .key_off = k,
- .value_off = v,
- };
-
- val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
- if (val) {
- /* replace existing earlier key with new value */
- val->value_off = v;
- return 0;
- }
- }
-
- /* extend array, add new entry, sort for bisection */
- val = reallocarray(node->values, node->values_count + 1, sizeof(struct trie_value_entry));
- if (!val)
- return -ENOMEM;
- trie->values_count++;
- node->values = val;
- node->values[node->values_count].key_off = k;
- node->values[node->values_count].value_off = v;
- node->values_count++;
- qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
- return 0;
-}
-
-static int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
- const char *key, const char *value) {
- size_t i = 0;
- int err = 0;
-
- for (;;) {
- size_t p;
- uint8_t c;
- struct trie_node *child;
-
- for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
- _cleanup_free_ char *s = NULL;
- ssize_t off;
- _cleanup_free_ struct trie_node *new_child = NULL;
-
- if (c == search[i + p])
- continue;
-
- /* split node */
- new_child = new0(struct trie_node, 1);
- if (!new_child)
- return -ENOMEM;
-
- /* move values from parent to child */
- new_child->prefix_off = node->prefix_off + p+1;
- new_child->children = node->children;
- new_child->children_count = node->children_count;
- new_child->values = node->values;
- new_child->values_count = node->values_count;
-
- /* update parent; use strdup() because the source gets realloc()d */
- s = strndup(trie->strings->buf + node->prefix_off, p);
- if (!s)
- return -ENOMEM;
-
- off = strbuf_add_string(trie->strings, s, p);
- if (off < 0)
- return off;
-
- node->prefix_off = off;
- node->children = NULL;
- node->children_count = 0;
- node->values = NULL;
- node->values_count = 0;
- err = node_add_child(trie, node, new_child, c);
- if (err)
- return err;
-
- new_child = NULL; /* avoid cleanup */
- break;
- }
- i += p;
-
- c = search[i];
- if (c == '\0')
- return trie_node_add_value(trie, node, key, value);
-
- child = node_lookup(node, c);
- if (!child) {
- ssize_t off;
-
- /* new child */
- child = new0(struct trie_node, 1);
- if (!child)
- return -ENOMEM;
-
- off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
- if (off < 0) {
- free(child);
- return off;
- }
-
- child->prefix_off = off;
- err = node_add_child(trie, node, child, c);
- if (err) {
- free(child);
- return err;
- }
-
- return trie_node_add_value(trie, child, key, value);
- }
-
- node = child;
- i++;
- }
-}
-
-struct trie_f {
- FILE *f;
- struct trie *trie;
- uint64_t strings_off;
-
- uint64_t nodes_count;
- uint64_t children_count;
- uint64_t values_count;
-};
-
-/* calculate the storage space for the nodes, children arrays, value arrays */
-static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) {
- uint64_t i;
-
- for (i = 0; i < node->children_count; i++)
- trie_store_nodes_size(trie, node->children[i].child);
-
- trie->strings_off += sizeof(struct trie_node_f);
- for (i = 0; i < node->children_count; i++)
- trie->strings_off += sizeof(struct trie_child_entry_f);
- for (i = 0; i < node->values_count; i++)
- trie->strings_off += sizeof(struct trie_value_entry_f);
-}
-
-static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
- uint64_t i;
- struct trie_node_f n = {
- .prefix_off = htole64(trie->strings_off + node->prefix_off),
- .children_count = node->children_count,
- .values_count = htole64(node->values_count),
- };
- struct trie_child_entry_f *children = NULL;
- int64_t node_off;
-
- if (node->children_count) {
- children = new0(struct trie_child_entry_f, node->children_count);
- if (!children)
- return -ENOMEM;
- }
-
- /* post-order recursion */
- for (i = 0; i < node->children_count; i++) {
- int64_t child_off;
-
- child_off = trie_store_nodes(trie, node->children[i].child);
- if (child_off < 0) {
- free(children);
- return child_off;
- }
- children[i].c = node->children[i].c;
- children[i].child_off = htole64(child_off);
- }
-
- /* write node */
- node_off = ftello(trie->f);
- fwrite(&n, sizeof(struct trie_node_f), 1, trie->f);
- trie->nodes_count++;
-
- /* append children array */
- if (node->children_count) {
- fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
- trie->children_count += node->children_count;
- free(children);
- }
-
- /* append values array */
- for (i = 0; i < node->values_count; i++) {
- struct trie_value_entry_f v = {
- .key_off = htole64(trie->strings_off + node->values[i].key_off),
- .value_off = htole64(trie->strings_off + node->values[i].value_off),
- };
-
- fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f);
- trie->values_count++;
- }
-
- return node_off;
-}
-
-static int trie_store(struct trie *trie, const char *filename) {
- struct trie_f t = {
- .trie = trie,
- };
- _cleanup_free_ char *filename_tmp = NULL;
- int64_t pos;
- int64_t root_off;
- int64_t size;
- struct trie_header_f h = {
- .signature = HWDB_SIG,
- .tool_version = htole64(atoi(PACKAGE_VERSION)),
- .header_size = htole64(sizeof(struct trie_header_f)),
- .node_size = htole64(sizeof(struct trie_node_f)),
- .child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
- .value_entry_size = htole64(sizeof(struct trie_value_entry_f)),
- };
- int err;
-
- /* calculate size of header, nodes, children entries, value entries */
- t.strings_off = sizeof(struct trie_header_f);
- trie_store_nodes_size(&t, trie->root);
-
- err = fopen_temporary(filename, &t.f, &filename_tmp);
- if (err < 0)
- return err;
- fchmod(fileno(t.f), 0444);
-
- /* write nodes */
- if (fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET) < 0)
- goto error_fclose;
- root_off = trie_store_nodes(&t, trie->root);
- h.nodes_root_off = htole64(root_off);
- pos = ftello(t.f);
- h.nodes_len = htole64(pos - sizeof(struct trie_header_f));
-
- /* write string buffer */
- fwrite(trie->strings->buf, trie->strings->len, 1, t.f);
- h.strings_len = htole64(trie->strings->len);
-
- /* write header */
- size = ftello(t.f);
- h.file_size = htole64(size);
- if (fseeko(t.f, 0, SEEK_SET < 0))
- goto error_fclose;
- fwrite(&h, sizeof(struct trie_header_f), 1, t.f);
-
- if (ferror(t.f))
- goto error_fclose;
- if (fflush(t.f) < 0)
- goto error_fclose;
- if (fsync(fileno(t.f)) < 0)
- goto error_fclose;
- if (rename(filename_tmp, filename) < 0)
- goto error_fclose;
-
- /* write succeeded */
- fclose(t.f);
-
- log_debug("=== trie on-disk ===");
- log_debug("size: %8"PRIi64" bytes", size);
- log_debug("header: %8zu bytes", sizeof(struct trie_header_f));
- log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")",
- t.nodes_count * sizeof(struct trie_node_f), t.nodes_count);
- log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")",
- t.children_count * sizeof(struct trie_child_entry_f), t.children_count);
- log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")",
- t.values_count * sizeof(struct trie_value_entry_f), t.values_count);
- log_debug("string store: %8zu bytes", trie->strings->len);
- log_debug("strings start: %8"PRIu64, t.strings_off);
-
- return 0;
-
- error_fclose:
- err = -errno;
- fclose(t.f);
- unlink(filename_tmp);
- return err;
-}
-
-static int insert_data(struct trie *trie, struct udev_list *match_list,
- char *line, const char *filename) {
- char *value;
- struct udev_list_entry *entry;
-
- value = strchr(line, '=');
- if (!value) {
- log_error("Error, key/value pair expected but got '%s' in '%s':", line, filename);
- return -EINVAL;
- }
-
- value[0] = '\0';
- value++;
-
- /* libudev requires properties to start with a space */
- while (isblank(line[0]) && isblank(line[1]))
- line++;
-
- if (line[0] == '\0' || value[0] == '\0') {
- log_error("Error, empty key or value '%s' in '%s':", line, filename);
- return -EINVAL;
- }
-
- udev_list_entry_foreach(entry, udev_list_get_entry(match_list))
- trie_insert(trie, trie->root, udev_list_entry_get_name(entry), line, value);
-
- return 0;
-}
-
-static int import_file(struct udev *udev, struct trie *trie, const char *filename) {
- enum {
- HW_MATCH,
- HW_DATA,
- HW_NONE,
- } state = HW_NONE;
- FILE *f;
- char line[LINE_MAX];
- struct udev_list match_list;
- int r = 0, err;
-
- udev_list_init(udev, &match_list, false);
-
- f = fopen(filename, "re");
- if (f == NULL)
- return -errno;
-
- while (fgets(line, sizeof(line), f)) {
- size_t len;
- char *pos;
-
- /* comment line */
- if (line[0] == '#')
- continue;
-
- /* strip trailing comment */
- pos = strchr(line, '#');
- if (pos)
- pos[0] = '\0';
-
- /* strip trailing whitespace */
- len = strlen(line);
- while (len > 0 && isspace(line[len-1]))
- len--;
- line[len] = '\0';
-
- switch (state) {
- case HW_NONE:
- if (len == 0)
- break;
-
- if (line[0] == ' ') {
- log_error("Error, MATCH expected but got '%s' in '%s':", line, filename);
- r = -EINVAL;
- break;
- }
-
- /* start of record, first match */
- state = HW_MATCH;
- udev_list_entry_add(&match_list, line, NULL);
- break;
-
- case HW_MATCH:
- if (len == 0) {
- log_error("Error, DATA expected but got empty line in '%s':", filename);
- r = -EINVAL;
- state = HW_NONE;
- udev_list_cleanup(&match_list);
- break;
- }
-
- /* another match */
- if (line[0] != ' ') {
- udev_list_entry_add(&match_list, line, NULL);
- break;
- }
-
- /* first data */
- state = HW_DATA;
- err = insert_data(trie, &match_list, line, filename);
- if (err < 0)
- r = err;
- break;
-
- case HW_DATA:
- /* end of record */
- if (len == 0) {
- state = HW_NONE;
- udev_list_cleanup(&match_list);
- break;
- }
-
- if (line[0] != ' ') {
- log_error("Error, DATA expected but got '%s' in '%s':", line, filename);
- r = -EINVAL;
- state = HW_NONE;
- udev_list_cleanup(&match_list);
- break;
- }
-
- err = insert_data(trie, &match_list, line, filename);
- if (err < 0)
- r = err;
- break;
- };
- }
-
- fclose(f);
- udev_list_cleanup(&match_list);
- return r;
-}
-
-static void help(void) {
+static int help(void) {
printf("%s hwdb [OPTIONS]\n\n"
" -h --help Print this message\n"
" -V --version Print version of the program\n"
@@ -551,9 +25,11 @@ static void help(void) {
"The sub-command 'hwdb' is deprecated, and is left for backwards compatibility.\n"
"Please use systemd-hwdb instead.\n"
, program_invocation_short_name);
+
+ return 0;
}
-static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
+static int parse_argv(int argc, char *argv[]) {
enum {
ARG_USR = 0x100,
};
@@ -568,144 +44,58 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
{ "help", no_argument, NULL, 'h' },
{}
};
- const char *test = NULL;
- const char *root = "";
- const char *hwdb_bin_dir = "/etc/udev";
- bool update = false;
- struct trie *trie = NULL;
- int err, c;
- int rc = EXIT_SUCCESS;
- bool strict = false;
+
+ int c;
while ((c = getopt_long(argc, argv, "ust:r:Vh", options, NULL)) >= 0)
switch(c) {
case 'u':
- update = true;
+ arg_update = true;
break;
case ARG_USR:
- hwdb_bin_dir = UDEVLIBEXECDIR;
+ arg_hwdb_bin_dir = UDEVLIBEXECDIR;
break;
case 's':
- strict = true;
+ arg_strict = true;
break;
case 't':
- test = optarg;
+ arg_test = optarg;
break;
case 'r':
- root = optarg;
+ arg_root = optarg;
break;
case 'V':
- print_version();
- return EXIT_SUCCESS;
+ return print_version();
case 'h':
- help();
- return EXIT_SUCCESS;
+ return help();
case '?':
- return EXIT_FAILURE;
+ return -EINVAL;
default:
assert_not_reached("Unknown option");
}
- if (!update && !test) {
- log_error("Either --update or --test must be used");
- return EXIT_FAILURE;
- }
-
- if (update) {
- char **files, **f;
- _cleanup_free_ char *hwdb_bin = NULL;
-
- trie = new0(struct trie, 1);
- if (!trie) {
- rc = EXIT_FAILURE;
- goto out;
- }
-
- /* string store */
- trie->strings = strbuf_new();
- if (!trie->strings) {
- rc = EXIT_FAILURE;
- goto out;
- }
-
- /* index */
- trie->root = new0(struct trie_node, 1);
- if (!trie->root) {
- rc = EXIT_FAILURE;
- goto out;
- }
- trie->nodes_count++;
-
- err = conf_files_list_strv(&files, ".hwdb", root, 0, conf_file_dirs);
- if (err < 0) {
- log_error_errno(err, "failed to enumerate hwdb files: %m");
- rc = EXIT_FAILURE;
- goto out;
- }
- STRV_FOREACH(f, files) {
- log_debug("reading file '%s'", *f);
- if (import_file(udev, trie, *f) < 0 && strict)
- rc = EXIT_FAILURE;
- }
- strv_free(files);
-
- strbuf_complete(trie->strings);
-
- log_debug("=== trie in-memory ===");
- log_debug("nodes: %8zu bytes (%8zu)",
- trie->nodes_count * sizeof(struct trie_node), trie->nodes_count);
- log_debug("children arrays: %8zu bytes (%8zu)",
- trie->children_count * sizeof(struct trie_child_entry), trie->children_count);
- log_debug("values arrays: %8zu bytes (%8zu)",
- trie->values_count * sizeof(struct trie_value_entry), trie->values_count);
- log_debug("strings: %8zu bytes",
- trie->strings->len);
- log_debug("strings incoming: %8zu bytes (%8zu)",
- trie->strings->in_len, trie->strings->in_count);
- log_debug("strings dedup'ed: %8zu bytes (%8zu)",
- trie->strings->dedup_len, trie->strings->dedup_count);
+ return 1;
+}
- hwdb_bin = strjoin(root, "/", hwdb_bin_dir, "/hwdb.bin");
- if (!hwdb_bin) {
- rc = EXIT_FAILURE;
- goto out;
- }
+int hwdb_main(int argc, char *argv[], void *userdata) {
+ int r;
- mkdir_parents_label(hwdb_bin, 0755);
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
- err = trie_store(trie, hwdb_bin);
- if (err < 0) {
- log_error_errno(err, "Failure writing database %s: %m", hwdb_bin);
- rc = EXIT_FAILURE;
- }
+ if (!arg_update && !arg_test)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Either --update or --test must be used.");
- (void) label_fix(hwdb_bin, 0);
+ if (arg_update) {
+ r = hwdb_update(arg_root, arg_hwdb_bin_dir, arg_strict, true);
+ if (r < 0)
+ return r;
}
- if (test) {
- _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
- int r;
+ if (arg_test)
+ return hwdb_query(arg_test);
- r = sd_hwdb_new(&hwdb);
- if (r >= 0) {
- const char *key, *value;
-
- SD_HWDB_FOREACH_PROPERTY(hwdb, test, key, value)
- printf("%s=%s\n", key, value);
- }
- }
-out:
- if (trie) {
- if (trie->root)
- trie_node_cleanup(trie->root);
- if (trie->strings)
- strbuf_cleanup(trie->strings);
- free(trie);
- }
- return rc;
+ return 0;
}
-
-const struct udevadm_cmd udevadm_hwdb = {
- .name = "hwdb",
- .cmd = adm_hwdb,
-};
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index ff41290478..d141bc74b2 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- *
- */
#include <ctype.h>
#include <errno.h>
@@ -13,13 +10,36 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "sd-device.h"
+
+#include "alloc-util.h"
+#include "device-enumerator-private.h"
+#include "device-private.h"
+#include "device-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "string-util.h"
-#include "udev-util.h"
-#include "udev.h"
+#include "udevadm.h"
#include "udevadm-util.h"
+typedef enum ActionType {
+ ACTION_QUERY,
+ ACTION_ATTRIBUTE_WALK,
+ ACTION_DEVICE_ID_FILE,
+} ActionType;
+
+typedef enum QueryType {
+ QUERY_NAME,
+ QUERY_PATH,
+ QUERY_SYMLINK,
+ QUERY_PROPERTY,
+ QUERY_ALL,
+} QueryType;
+
+static bool arg_root = false;
+static bool arg_export = false;
+static const char *arg_export_prefix = NULL;
+
static bool skip_attribute(const char *name) {
static const char* const skip[] = {
"uevent",
@@ -30,7 +50,7 @@ static bool skip_attribute(const char *name) {
"subsystem",
"module",
};
- unsigned int i;
+ unsigned i;
for (i = 0; i < ELEMENTSOF(skip); i++)
if (streq(name, skip[i]))
@@ -38,22 +58,15 @@ static bool skip_attribute(const char *name) {
return false;
}
-static void print_all_attributes(struct udev_device *device, const char *key) {
- struct udev_list_entry *sysattr;
+static void print_all_attributes(sd_device *device, const char *key) {
+ const char *name, *value;
- udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) {
- const char *name;
- const char *value;
+ FOREACH_DEVICE_PROPERTY(device, name, value) {
size_t len;
- name = udev_list_entry_get_name(sysattr);
if (skip_attribute(name))
continue;
- value = udev_device_get_sysattr_value(device, name);
- if (value == NULL)
- continue;
-
/* skip any values that look like a path */
if (value[0] == '/')
continue;
@@ -67,11 +80,11 @@ static void print_all_attributes(struct udev_device *device, const char *key) {
printf(" %s{%s}==\"%s\"\n", key, name, value);
}
- printf("\n");
+ puts("");
}
-static int print_device_chain(struct udev_device *device) {
- struct udev_device *device_parent;
+static int print_device_chain(sd_device *device) {
+ sd_device *child, *parent;
const char *str;
printf("\n"
@@ -82,63 +95,60 @@ static int print_device_chain(struct udev_device *device) {
"and the attributes from one single parent device.\n"
"\n");
- printf(" looking at device '%s':\n", udev_device_get_devpath(device));
- printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device));
- str = udev_device_get_subsystem(device);
- if (str == NULL)
+ (void) sd_device_get_devpath(device, &str);
+ printf(" looking at device '%s':\n", str);
+ (void) sd_device_get_sysname(device, &str);
+ printf(" KERNEL==\"%s\"\n", str);
+ if (sd_device_get_subsystem(device, &str) < 0)
str = "";
printf(" SUBSYSTEM==\"%s\"\n", str);
- str = udev_device_get_driver(device);
- if (str == NULL)
+ if (sd_device_get_driver(device, &str) < 0)
str = "";
printf(" DRIVER==\"%s\"\n", str);
print_all_attributes(device, "ATTR");
- device_parent = device;
- do {
- device_parent = udev_device_get_parent(device_parent);
- if (device_parent == NULL)
- break;
- printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
- printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
- str = udev_device_get_subsystem(device_parent);
- if (str == NULL)
+ for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
+ (void) sd_device_get_devpath(parent, &str);
+ printf(" looking at parent device '%s':\n", str);
+ (void) sd_device_get_sysname(parent, &str);
+ printf(" KERNELS==\"%s\"\n", str);
+ if (sd_device_get_subsystem(parent, &str) < 0)
str = "";
printf(" SUBSYSTEMS==\"%s\"\n", str);
- str = udev_device_get_driver(device_parent);
- if (str == NULL)
+ if (sd_device_get_driver(parent, &str) < 0)
str = "";
printf(" DRIVERS==\"%s\"\n", str);
- print_all_attributes(device_parent, "ATTRS");
- } while (device_parent != NULL);
+ print_all_attributes(parent, "ATTRS");
+ }
return 0;
}
-static void print_record(struct udev_device *device) {
- const char *str;
+static int print_record(sd_device *device) {
+ const char *str, *val;
int i;
- struct udev_list_entry *list_entry;
- printf("P: %s\n", udev_device_get_devpath(device));
+ (void) sd_device_get_devpath(device, &str);
+ printf("P: %s\n", str);
- str = udev_device_get_devnode(device);
- if (str != NULL)
- printf("N: %s\n", str + STRLEN("/dev/"));
+ if (sd_device_get_devname(device, &str) >= 0) {
+ assert_se(val = path_startswith(str, "/dev/"));
+ printf("N: %s\n", val);
+ }
- i = udev_device_get_devlink_priority(device);
- if (i != 0)
+ if (device_get_devlink_priority(device, &i) >= 0)
printf("L: %i\n", i);
- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
- printf("S: %s\n",
- udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
+ FOREACH_DEVICE_DEVLINK(device, str) {
+ assert_se(val = path_startswith(str, "/dev/"));
+ printf("S: %s\n", val);
+ }
+
+ FOREACH_DEVICE_PROPERTY(device, str, val)
+ printf("E: %s=%s\n", str, val);
- udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
- printf("E: %s=%s\n",
- udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry));
- printf("\n");
+ puts("");
+ return 0;
}
static int stat_device(const char *name, bool export, const char *prefix) {
@@ -148,7 +158,7 @@ static int stat_device(const char *name, bool export, const char *prefix) {
return -errno;
if (export) {
- if (prefix == NULL)
+ if (!prefix)
prefix = "INFO_";
printf("%sMAJOR=%u\n"
"%sMINOR=%u\n",
@@ -159,22 +169,25 @@ static int stat_device(const char *name, bool export, const char *prefix) {
return 0;
}
-static int export_devices(struct udev *udev) {
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *udev_enumerate;
- struct udev_list_entry *list_entry;
+static int export_devices(void) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d;
+ int r;
- udev_enumerate = udev_enumerate_new(udev);
- if (udev_enumerate == NULL)
- return -ENOMEM;
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
- udev_enumerate_scan_devices(udev_enumerate);
- udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
- _cleanup_(udev_device_unrefp) struct udev_device *device;
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
- device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
- if (device != NULL)
- print_record(device);
- }
+ r = device_enumerator_scan_devices(e);
+ if (r < 0)
+ return r;
+
+ FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
+ print_record(d);
return 0;
}
@@ -195,10 +208,10 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
if ((stats.st_mode & mask) != 0)
continue;
if (S_ISDIR(stats.st_mode)) {
- _cleanup_closedir_ DIR *dir2;
+ _cleanup_closedir_ DIR *dir2 = NULL;
dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
- if (dir2 != NULL)
+ if (dir2)
cleanup_dir(dir2, mask, depth-1);
(void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
@@ -207,34 +220,95 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
}
}
-static void cleanup_db(struct udev *udev) {
+static void cleanup_db(void) {
_cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
(void) unlink("/run/udev/queue.bin");
dir1 = opendir("/run/udev/data");
- if (dir1 != NULL)
+ if (dir1)
cleanup_dir(dir1, S_ISVTX, 1);
dir2 = opendir("/run/udev/links");
- if (dir2 != NULL)
+ if (dir2)
cleanup_dir(dir2, 0, 2);
dir3 = opendir("/run/udev/tags");
- if (dir3 != NULL)
+ if (dir3)
cleanup_dir(dir3, 0, 2);
dir4 = opendir("/run/udev/static_node-tags");
- if (dir4 != NULL)
+ if (dir4)
cleanup_dir(dir4, 0, 2);
dir5 = opendir("/run/udev/watch");
- if (dir5 != NULL)
+ if (dir5)
cleanup_dir(dir5, 0, 1);
}
-static void help(void) {
+static int query_device(QueryType query, sd_device* device) {
+ int r;
+
+ assert(device);
+
+ switch(query) {
+ case QUERY_NAME: {
+ const char *node;
+
+ r = sd_device_get_devname(device, &node);
+ if (r < 0)
+ return log_error_errno(r, "No device node found: %m");
+
+ if (!arg_root)
+ assert_se(node = path_startswith(node, "/dev/"));
+ printf("%s\n", node);
+ return 0;
+ }
+
+ case QUERY_SYMLINK: {
+ const char *devlink, *prefix = "";
+
+ FOREACH_DEVICE_DEVLINK(device, devlink) {
+ if (!arg_root)
+ assert_se(devlink = path_startswith(devlink, "/dev/"));
+ printf("%s%s", prefix, devlink);
+ prefix = " ";
+ }
+ puts("");
+ return 0;
+ }
+
+ case QUERY_PATH: {
+ const char *devpath;
+
+ r = sd_device_get_devpath(device, &devpath);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get device path: %m");
+
+ printf("%s\n", devpath);
+ return 0;
+ }
+
+ case QUERY_PROPERTY: {
+ const char *key, *value;
+
+ FOREACH_DEVICE_PROPERTY(device, key, value)
+ if (arg_export)
+ printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
+ else
+ printf("%s=%s\n", key, value);
+ return 0;
+ }
+
+ case QUERY_ALL:
+ return print_record(device);
+ }
+
+ assert_not_reached("unknown query type");
+ return 0;
+}
+static int help(void) {
printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
"Query sysfs or the udev database.\n\n"
" -h --help Print this message\n"
@@ -256,16 +330,14 @@ static void help(void) {
" -e --export-db Export the content of the udev database\n"
" -c --cleanup-db Clean up the udev database\n"
, program_invocation_short_name);
+
+ return 0;
}
-static int uinfo(struct udev *udev, int argc, char *argv[]) {
- _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
- bool root = 0;
- bool export = 0;
- const char *export_prefix = NULL;
- char name[UTIL_PATH_SIZE];
- struct udev_list_entry *list_entry;
- int c;
+int info_main(int argc, char *argv[], void *userdata) {
+ _cleanup_strv_free_ char **devices = NULL;
+ _cleanup_free_ char *name = NULL;
+ int c, r;
static const struct option options[] = {
{ "name", required_argument, NULL, 'n' },
@@ -283,47 +355,26 @@ static int uinfo(struct udev *udev, int argc, char *argv[]) {
{}
};
- enum action_type {
- ACTION_QUERY,
- ACTION_ATTRIBUTE_WALK,
- ACTION_DEVICE_ID_FILE,
- } action = ACTION_QUERY;
-
- enum query_type {
- QUERY_NAME,
- QUERY_PATH,
- QUERY_SYMLINK,
- QUERY_PROPERTY,
- QUERY_ALL,
- } query = QUERY_ALL;
+ ActionType action = ACTION_QUERY;
+ QueryType query = QUERY_ALL;
while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
switch (c) {
- case 'n': {
- if (device != NULL) {
- fprintf(stderr, "device already specified\n");
- return 2;
- }
-
- device = find_device(udev, optarg, "/dev/");
- if (device == NULL) {
- fprintf(stderr, "device node not found\n");
- return 2;
- }
+ case 'n':
+ case 'p': {
+ const char *prefix = c == 'n' ? "/dev/" : "/sys/";
+ char *path;
+
+ path = path_join(path_startswith(optarg, prefix) ? NULL : prefix, optarg);
+ if (!path)
+ return log_oom();
+
+ r = strv_consume(&devices, path);
+ if (r < 0)
+ return log_oom();
break;
}
- case 'p':
- if (device != NULL) {
- fprintf(stderr, "device already specified\n");
- return 2;
- }
- device = find_device(udev, optarg, "/sys");
- if (device == NULL) {
- fprintf(stderr, "syspath not found\n");
- return 2;
- }
- break;
case 'q':
action = ACTION_QUERY;
if (streq(optarg, "property") || streq(optarg, "env"))
@@ -337,135 +388,82 @@ static int uinfo(struct udev *udev, int argc, char *argv[]) {
else if (streq(optarg, "all"))
query = QUERY_ALL;
else {
- fprintf(stderr, "unknown query type\n");
- return 3;
+ log_error("unknown query type");
+ return -EINVAL;
}
break;
case 'r':
- root = true;
+ arg_root = true;
break;
case 'd':
action = ACTION_DEVICE_ID_FILE;
- strscpy(name, sizeof(name), optarg);
+ r = free_and_strdup(&name, optarg);
+ if (r < 0)
+ return log_oom();
break;
case 'a':
action = ACTION_ATTRIBUTE_WALK;
break;
case 'e':
- if (export_devices(udev) < 0)
- return 1;
- return 0;
+ return export_devices();
case 'c':
- cleanup_db(udev);
+ cleanup_db();
return 0;
case 'x':
- export = true;
+ arg_export = true;
break;
case 'P':
- export_prefix = optarg;
+ arg_export_prefix = optarg;
break;
case 'V':
- print_version();
- return 0;
+ return print_version();
case 'h':
- help();
- return 0;
+ return help();
+ case '?':
+ return -EINVAL;
default:
- return 1;
- }
-
- switch (action) {
- case ACTION_QUERY:
- if (!device) {
- if (!argv[optind]) {
- help();
- return 2;
- }
- device = find_device(udev, argv[optind], NULL);
- if (!device) {
- fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
- return 4;
- }
+ assert_not_reached("Unknown option");
}
- switch(query) {
- case QUERY_NAME: {
- const char *node = udev_device_get_devnode(device);
- if (node == NULL) {
- fprintf(stderr, "no device node found\n");
- return 5;
- }
+ if (action == ACTION_DEVICE_ID_FILE) {
+ if (argv[optind])
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Positional arguments are not allowed with -d/--device-id-of-file.");
+ assert(name);
+ return stat_device(name, arg_export, arg_export_prefix);
+ }
- if (root)
- printf("%s\n", udev_device_get_devnode(device));
- else
- printf("%s\n",
- udev_device_get_devnode(device) + STRLEN("/dev/"));
- break;
- }
- case QUERY_SYMLINK:
- list_entry = udev_device_get_devlinks_list_entry(device);
- while (list_entry != NULL) {
- if (root)
- printf("%s", udev_list_entry_get_name(list_entry));
- else
- printf("%s",
- udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
- list_entry = udev_list_entry_get_next(list_entry);
- if (list_entry != NULL)
- printf(" ");
- }
- printf("\n");
- break;
- case QUERY_PATH:
- printf("%s\n", udev_device_get_devpath(device));
- return 0;
- case QUERY_PROPERTY:
- list_entry = udev_device_get_properties_list_entry(device);
- while (list_entry != NULL) {
- if (export)
- printf("%s%s='%s'\n", strempty(export_prefix),
- udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry));
- else
- printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
-
- list_entry = udev_list_entry_get_next(list_entry);
- }
- break;
- case QUERY_ALL:
- print_record(device);
- break;
- default:
- assert_not_reached("unknown query type");
- }
- break;
- case ACTION_ATTRIBUTE_WALK:
- if (!device && argv[optind]) {
- device = find_device(udev, argv[optind], NULL);
- if (!device) {
- fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
- return 4;
- }
- }
- if (!device) {
- fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
- return 4;
- }
- print_device_chain(device);
- break;
- case ACTION_DEVICE_ID_FILE:
- if (stat_device(name, export, export_prefix) != 0)
- return 1;
- break;
+ r = strv_extend_strv(&devices, argv + optind, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to build argument list: %m");
+
+ if (strv_isempty(devices))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "A device name or path is required");
+ if (action == ACTION_ATTRIBUTE_WALK && strv_length(devices) > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Only one device may be specified with -a/--attribute-walk");
+
+ char **p;
+ STRV_FOREACH(p, devices) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+
+ r = find_device(*p, NULL, &device);
+ if (r == -EINVAL)
+ return log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p);
+ if (r < 0)
+ return log_error_errno(r, "Unknown device \"%s\": %m", *p);
+
+ if (action == ACTION_QUERY)
+ r = query_device(query, device);
+ else if (action == ACTION_ATTRIBUTE_WALK)
+ r = print_device_chain(device);
+ else
+ assert_not_reached("Unknown action");
+ if (r < 0)
+ return r;
}
return 0;
}
-
-const struct udevadm_cmd udevadm_info = {
- .name = "info",
- .cmd = uinfo,
- .help = "Query sysfs or the udev database",
-};
diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c
index b1e13553dc..f7737d0790 100644
--- a/src/udev/udevadm-monitor.c
+++ b/src/udev/udevadm-monitor.c
@@ -1,53 +1,101 @@
/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- *
- */
#include <errno.h>
#include <getopt.h>
#include <signal.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <sys/time.h>
-#include <time.h>
+#include "sd-device.h"
+#include "sd-event.h"
+
+#include "alloc-util.h"
+#include "device-monitor-private.h"
+#include "device-util.h"
#include "fd-util.h"
#include "format-util.h"
-#include "udev-util.h"
-#include "udev.h"
-#include "udevadm-util.h"
+#include "hashmap.h"
+#include "set.h"
+#include "signal-util.h"
+#include "string-util.h"
+#include "udevadm.h"
+
+static bool arg_show_property = false;
+static bool arg_print_kernel = false;
+static bool arg_print_udev = false;
+static Set *arg_tag_filter = NULL;
+static Hashmap *arg_subsystem_filter = NULL;
+
+static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
+ const char *action = NULL, *devpath = NULL, *subsystem = NULL;
+ MonitorNetlinkGroup group = PTR_TO_INT(userdata);
+ struct timespec ts;
-static bool udev_exit;
+ assert(device);
+ assert(IN_SET(group, MONITOR_GROUP_UDEV, MONITOR_GROUP_KERNEL));
-static void sig_handler(int signum) {
- if (IN_SET(signum, SIGINT, SIGTERM))
- udev_exit = true;
-}
-
-static void print_device(struct udev_device *device, const char *source, int prop) {
- struct timespec ts;
+ (void) sd_device_get_property_value(device, "ACTION", &action);
+ (void) sd_device_get_devpath(device, &devpath);
+ (void) sd_device_get_subsystem(device, &subsystem);
assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
+
printf("%-6s[%"PRI_TIME".%06"PRI_NSEC"] %-8s %s (%s)\n",
- source,
+ group == MONITOR_GROUP_UDEV ? "UDEV" : "KERNEL",
ts.tv_sec, (nsec_t)ts.tv_nsec/1000,
- udev_device_get_action(device),
- udev_device_get_devpath(device),
- udev_device_get_subsystem(device));
- if (prop) {
- struct udev_list_entry *list_entry;
-
- udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
- printf("%s=%s\n",
- udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry));
+ action, devpath, subsystem);
+
+ if (arg_show_property) {
+ const char *key, *value;
+
+ FOREACH_DEVICE_PROPERTY(device, key, value)
+ printf("%s=%s\n", key, value);
+
printf("\n");
}
+
+ return 0;
+}
+
+static int setup_monitor(MonitorNetlinkGroup sender, sd_event *event, sd_device_monitor **ret) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
+ const char *subsystem, *devtype, *tag;
+ Iterator i;
+ int r;
+
+ r = device_monitor_new_full(&monitor, sender, -1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create netlink socket: %m");
+
+ (void) sd_device_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
+
+ r = sd_device_monitor_attach_event(monitor, event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach event: %m");
+
+ HASHMAP_FOREACH_KEY(devtype, subsystem, arg_subsystem_filter, i) {
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, subsystem, devtype);
+ if (r < 0)
+ return log_error_errno(r, "Failed to apply subsystem filter '%s%s%s': %m",
+ subsystem, devtype ? "/" : "", strempty(devtype));
+ }
+
+ SET_FOREACH(tag, arg_tag_filter, i) {
+ r = sd_device_monitor_filter_add_match_tag(monitor, tag);
+ if (r < 0)
+ return log_error_errno(r, "Failed to apply tag filter '%s': %m", tag);
+ }
+
+ r = sd_device_monitor_start(monitor, device_monitor_handler, INT_TO_PTR(sender));
+ if (r < 0)
+ return log_error_errno(r, "Failed to start device monitor: %m");
+
+ (void) sd_event_source_set_description(sd_device_monitor_get_event_source(monitor),
+ sender == MONITOR_GROUP_UDEV ? "device-monitor-udev" : "device-monitor-kernel");
+
+ *ret = TAKE_PTR(monitor);
+ return 0;
}
-static void help(void) {
+static int help(void) {
printf("%s monitor [OPTIONS]\n\n"
"Listen to kernel and udev events.\n\n"
" -h --help Show this help\n"
@@ -58,23 +106,11 @@ static void help(void) {
" -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n"
" -t --tag-match=TAG Filter events by tag\n"
, program_invocation_short_name);
-}
-static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
- struct sigaction act = {};
- sigset_t mask;
- bool prop = false;
- bool print_kernel = false;
- bool print_udev = false;
- _cleanup_(udev_list_cleanup) struct udev_list subsystem_match_list;
- _cleanup_(udev_list_cleanup) struct udev_list tag_match_list;
- _cleanup_(udev_monitor_unrefp) struct udev_monitor *udev_monitor = NULL;
- _cleanup_(udev_monitor_unrefp) struct udev_monitor *kernel_monitor = NULL;
- _cleanup_close_ int fd_ep = -1;
- int fd_kernel = -1, fd_udev = -1;
- struct epoll_event ep_kernel, ep_udev;
- int c;
+ return 0;
+}
+static int parse_argv(int argc, char *argv[]) {
static const struct option options[] = {
{ "property", no_argument, NULL, 'p' },
{ "environment", no_argument, NULL, 'e' }, /* alias for -p */
@@ -87,188 +123,135 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
{}
};
- udev_list_init(udev, &subsystem_match_list, true);
- udev_list_init(udev, &tag_match_list, true);
+ int r, c;
while ((c = getopt_long(argc, argv, "pekus:t:Vh", options, NULL)) >= 0)
switch (c) {
case 'p':
case 'e':
- prop = true;
+ arg_show_property = true;
break;
case 'k':
- print_kernel = true;
+ arg_print_kernel = true;
break;
case 'u':
- print_udev = true;
- break;
- case 's':
- {
- char subsys[UTIL_NAME_SIZE];
- char *devtype;
-
- strscpy(subsys, sizeof(subsys), optarg);
- devtype = strchr(subsys, '/');
- if (devtype != NULL) {
- devtype[0] = '\0';
- devtype++;
- }
- udev_list_entry_add(&subsystem_match_list, subsys, devtype);
- break;
- }
- case 't':
- udev_list_entry_add(&tag_match_list, optarg, NULL);
+ arg_print_udev = true;
break;
- case 'V':
- print_version();
- return 0;
- case 'h':
- help();
- return 0;
- default:
- return 1;
- }
+ case 's': {
+ _cleanup_free_ char *subsystem = NULL, *devtype = NULL;
+ const char *slash;
- if (!print_kernel && !print_udev) {
- print_kernel = true;
- print_udev = true;
- }
+ slash = strchr(optarg, '/');
+ if (slash) {
+ devtype = strdup(devtype + 1);
+ if (!devtype)
+ return -ENOMEM;
- /* set signal handlers */
- act.sa_handler = sig_handler;
- act.sa_flags = SA_RESTART;
- assert_se(sigaction(SIGINT, &act, NULL) == 0);
- assert_se(sigaction(SIGTERM, &act, NULL) == 0);
- assert_se(sigemptyset(&mask) == 0);
- assert_se(sigaddset(&mask, SIGINT) == 0);
- assert_se(sigaddset(&mask, SIGTERM) == 0);
- assert_se(sigprocmask(SIG_UNBLOCK, &mask, NULL) == 0);
+ subsystem = strndup(optarg, devtype - optarg);
+ } else
+ subsystem = strdup(optarg);
- /* Callers are expecting to see events as they happen: Line buffering */
- setlinebuf(stdout);
+ if (!subsystem)
+ return -ENOMEM;
- fd_ep = epoll_create1(EPOLL_CLOEXEC);
- if (fd_ep < 0) {
- log_error_errno(errno, "error creating epoll fd: %m");
- return 1;
- }
+ r = hashmap_ensure_allocated(&arg_subsystem_filter, NULL);
+ if (r < 0)
+ return r;
- printf("monitor will print the received events for:\n");
- if (print_udev) {
- struct udev_list_entry *entry;
+ r = hashmap_put(arg_subsystem_filter, subsystem, devtype);
+ if (r < 0)
+ return r;
- udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
- if (udev_monitor == NULL) {
- fprintf(stderr, "error: unable to create netlink socket\n");
- return 1;
+ subsystem = devtype = NULL;
+ break;
}
- udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024);
- fd_udev = udev_monitor_get_fd(udev_monitor);
+ case 't': {
+ _cleanup_free_ char *tag = NULL;
- udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) {
- const char *subsys = udev_list_entry_get_name(entry);
- const char *devtype = udev_list_entry_get_value(entry);
+ r = set_ensure_allocated(&arg_tag_filter, &string_hash_ops);
+ if (r < 0)
+ return r;
- if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsys, devtype) < 0)
- fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys);
- }
+ tag = strdup(optarg);
+ if (!tag)
+ return -ENOMEM;
- udev_list_entry_foreach(entry, udev_list_get_entry(&tag_match_list)) {
- const char *tag = udev_list_entry_get_name(entry);
+ r = set_put(arg_tag_filter, tag);
+ if (r < 0)
+ return r;
- if (udev_monitor_filter_add_match_tag(udev_monitor, tag) < 0)
- fprintf(stderr, "error: unable to apply tag filter '%s'\n", tag);
- }
-
- if (udev_monitor_enable_receiving(udev_monitor) < 0) {
- fprintf(stderr, "error: unable to subscribe to udev events\n");
- return 2;
+ tag = NULL;
+ break;
}
-
- memzero(&ep_udev, sizeof(struct epoll_event));
- ep_udev.events = EPOLLIN;
- ep_udev.data.fd = fd_udev;
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
- log_error_errno(errno, "fail to add fd to epoll: %m");
- return 2;
+ case 'V':
+ return print_version();
+ case 'h':
+ return help();
+ case '?':
+ return -EINVAL;
+ default:
+ assert_not_reached("Unknown option.");
}
- printf("UDEV - the event which udev sends out after rule processing\n");
+ if (!arg_print_kernel && !arg_print_udev) {
+ arg_print_kernel = true;
+ arg_print_udev = true;
}
- if (print_kernel) {
- struct udev_list_entry *entry;
+ return 1;
+}
- kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel");
- if (kernel_monitor == NULL) {
- fprintf(stderr, "error: unable to create netlink socket\n");
- return 3;
- }
- udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024);
- fd_kernel = udev_monitor_get_fd(kernel_monitor);
+int monitor_main(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *kernel_monitor = NULL, *udev_monitor = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ int r;
- udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) {
- const char *subsys = udev_list_entry_get_name(entry);
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finalize;
- if (udev_monitor_filter_add_match_subsystem_devtype(kernel_monitor, subsys, NULL) < 0)
- fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys);
- }
+ /* Callers are expecting to see events as they happen: Line buffering */
+ setlinebuf(stdout);
- if (udev_monitor_enable_receiving(kernel_monitor) < 0) {
- fprintf(stderr, "error: unable to subscribe to kernel events\n");
- return 4;
- }
+ r = sd_event_default(&event);
+ if (r < 0) {
+ log_error_errno(r, "Failed to initialize event: %m");
+ goto finalize;
+ }
- memzero(&ep_kernel, sizeof(struct epoll_event));
- ep_kernel.events = EPOLLIN;
- ep_kernel.data.fd = fd_kernel;
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) {
- log_error_errno(errno, "fail to add fd to epoll: %m");
- return 5;
- }
+ assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
+ (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ (void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
+
+ printf("monitor will print the received events for:\n");
+ if (arg_print_udev) {
+ r = setup_monitor(MONITOR_GROUP_UDEV, event, &udev_monitor);
+ if (r < 0)
+ goto finalize;
+
+ printf("UDEV - the event which udev sends out after rule processing\n");
+ }
+
+ if (arg_print_kernel) {
+ r = setup_monitor(MONITOR_GROUP_KERNEL, event, &kernel_monitor);
+ if (r < 0)
+ goto finalize;
printf("KERNEL - the kernel uevent\n");
}
printf("\n");
- while (!udev_exit) {
- int fdcount;
- struct epoll_event ev[4];
- int i;
+ r = sd_event_loop(event);
+ if (r < 0) {
+ log_error_errno(r, "Failed to run event loop: %m");
+ goto finalize;
+ }
- fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
- if (fdcount < 0) {
- if (errno != EINTR)
- fprintf(stderr, "error receiving uevent message: %m\n");
- continue;
- }
+ r = 0;
- for (i = 0; i < fdcount; i++) {
- if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) {
- struct udev_device *device;
-
- device = udev_monitor_receive_device(kernel_monitor);
- if (device == NULL)
- continue;
- print_device(device, "KERNEL", prop);
- udev_device_unref(device);
- } else if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
- struct udev_device *device;
-
- device = udev_monitor_receive_device(udev_monitor);
- if (device == NULL)
- continue;
- print_device(device, "UDEV", prop);
- udev_device_unref(device);
- }
- }
- }
+finalize:
+ hashmap_free_free_free(arg_subsystem_filter);
+ set_free_free(arg_tag_filter);
- return 0;
+ return r;
}
-
-const struct udevadm_cmd udevadm_monitor = {
- .name = "monitor",
- .cmd = adm_monitor,
- .help = "Listen to kernel and udev events",
-};
diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c
index b8d428bcb4..4ae237d430 100644
--- a/src/udev/udevadm-settle.c
+++ b/src/udev/udevadm-settle.c
@@ -2,7 +2,6 @@
/*
* Copyright © 2009 Canonical Ltd.
* Copyright © 2009 Scott James Remnant <scott@netsplit.com>
- *
*/
#include <errno.h>
@@ -14,22 +13,28 @@
#include <string.h>
#include <unistd.h>
-#include "parse-util.h"
-#include "udev.h"
-#include "udevadm-util.h"
+#include "libudev-util.h"
+#include "time-util.h"
+#include "udevadm.h"
+#include "udev-ctrl.h"
#include "util.h"
-static void help(void) {
+static usec_t arg_timeout = 120 * USEC_PER_SEC;
+static const char *arg_exists = NULL;
+
+static int help(void) {
printf("%s settle [OPTIONS]\n\n"
"Wait for pending udev events.\n\n"
" -h --help Show this help\n"
" -V --version Show package version\n"
- " -t --timeout=SECONDS Maximum time to wait for events\n"
+ " -t --timeout=SEC Maximum time to wait for events\n"
" -E --exit-if-exists=FILE Stop waiting if file exists\n"
, program_invocation_short_name);
+
+ return 0;
}
-static int adm_settle(struct udev *udev, int argc, char *argv[]) {
+static int parse_argv(int argc, char *argv[]) {
static const struct option options[] = {
{ "timeout", required_argument, NULL, 't' },
{ "exit-if-exists", required_argument, NULL, 'E' },
@@ -40,117 +45,96 @@ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
{ "quiet", no_argument, NULL, 'q' }, /* removed */
{}
};
- usec_t deadline;
- const char *exists = NULL;
- unsigned int timeout = 120;
- struct pollfd pfd[1] = { {.fd = -1}, };
- int c;
- struct udev_queue *queue;
- int rc = EXIT_FAILURE;
+
+ int c, r;
while ((c = getopt_long(argc, argv, "t:E:Vhs:e:q", options, NULL)) >= 0) {
switch (c) {
-
- case 't': {
- int r;
-
- r = safe_atou(optarg, &timeout);
- if (r < 0) {
- log_error_errno(r, "Invalid timeout value '%s': %m", optarg);
- return EXIT_FAILURE;
- }
+ case 't':
+ r = parse_sec(optarg, &arg_timeout);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timeout value '%s': %m", optarg);
break;
- }
-
case 'E':
- exists = optarg;
+ arg_exists = optarg;
break;
-
case 'V':
- print_version();
- return EXIT_SUCCESS;
-
+ return print_version();
case 'h':
- help();
- return EXIT_SUCCESS;
-
+ return help();
case 's':
case 'e':
case 'q':
- log_info("Option -%c no longer supported.", c);
- return EXIT_FAILURE;
-
+ return log_info_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option -%c no longer supported.",
+ c);
case '?':
- return EXIT_FAILURE;
-
+ return -EINVAL;
default:
- assert_not_reached("Unknown argument");
+ assert_not_reached("Unknown option.");
}
}
- if (optind < argc) {
- fprintf(stderr, "Extraneous argument: '%s'\n", argv[optind]);
- return EXIT_FAILURE;
- }
+ return 1;
+}
- deadline = now(CLOCK_MONOTONIC) + timeout * USEC_PER_SEC;
+int settle_main(int argc, char *argv[], void *userdata) {
+ _cleanup_(udev_queue_unrefp) struct udev_queue *queue = NULL;
+ struct pollfd pfd;
+ usec_t deadline;
+ int r;
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ deadline = now(CLOCK_MONOTONIC) + arg_timeout;
/* guarantee that the udev daemon isn't pre-processing */
if (getuid() == 0) {
- struct udev_ctrl *uctrl;
-
- uctrl = udev_ctrl_new(udev);
- if (uctrl != NULL) {
- if (udev_ctrl_send_ping(uctrl, MAX(5U, timeout)) < 0) {
- log_debug("no connection to daemon");
- udev_ctrl_unref(uctrl);
- return EXIT_SUCCESS;
+ _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
+
+ uctrl = udev_ctrl_new();
+ if (uctrl) {
+ r = udev_ctrl_send_ping(uctrl, MAX(5U, arg_timeout / USEC_PER_SEC));
+ if (r < 0) {
+ log_debug_errno(r, "Failed to connect to udev daemon.");
+ return 0;
}
- udev_ctrl_unref(uctrl);
}
}
- queue = udev_queue_new(udev);
- if (!queue) {
- log_error("unable to get udev queue");
- return EXIT_FAILURE;
- }
+ queue = udev_queue_new(NULL);
+ if (!queue)
+ return log_error_errno(errno, "Failed to get udev queue: %m");
- pfd[0].events = POLLIN;
- pfd[0].fd = udev_queue_get_fd(queue);
- if (pfd[0].fd < 0) {
- log_debug("queue is empty, nothing to watch");
- rc = EXIT_SUCCESS;
- goto out;
+ r = udev_queue_get_fd(queue);
+ if (r < 0) {
+ log_debug_errno(r, "Queue is empty, nothing to watch.");
+ return 0;
}
+ pfd = (struct pollfd) {
+ .events = POLLIN,
+ .fd = r,
+ };
+
for (;;) {
- if (exists && access(exists, F_OK) >= 0) {
- rc = EXIT_SUCCESS;
- break;
- }
+ if (arg_exists && access(arg_exists, F_OK) >= 0)
+ return 0;
/* exit if queue is empty */
- if (udev_queue_get_queue_is_empty(queue)) {
- rc = EXIT_SUCCESS;
- break;
- }
+ if (udev_queue_get_queue_is_empty(queue))
+ return 0;
if (now(CLOCK_MONOTONIC) >= deadline)
- break;
+ return -ETIMEDOUT;
- /* wake up when queue is empty */
- if (poll(pfd, 1, MSEC_PER_SEC) > 0 && pfd[0].revents & POLLIN)
- udev_queue_flush(queue);
+ /* wake up when queue becomes empty */
+ if (poll(&pfd, 1, MSEC_PER_SEC) > 0 && pfd.revents & POLLIN) {
+ r = udev_queue_flush(queue);
+ if (r < 0)
+ return log_error_errno(r, "Failed to flush queue: %m");
+ }
}
-
-out:
- udev_queue_unref(queue);
- return rc;
}
-
-const struct udevadm_cmd udevadm_settle = {
- .name = "settle",
- .cmd = adm_settle,
- .help = "Wait for pending udev events",
-};
diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c
index 0d6cd46bd7..22c3184a01 100644
--- a/src/udev/udevadm-test-builtin.c
+++ b/src/udev/udevadm-test-builtin.c
@@ -1,7 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- *
- */
#include <errno.h>
#include <getopt.h>
@@ -9,12 +6,15 @@
#include <stdio.h>
#include <stdlib.h>
-#include "path-util.h"
-#include "string-util.h"
-#include "udev.h"
+#include "log.h"
+#include "udev-builtin.h"
+#include "udevadm.h"
#include "udevadm-util.h"
-static void help(struct udev *udev) {
+static const char *arg_command = NULL;
+static const char *arg_syspath = NULL;
+
+static int help(void) {
printf("%s test-builtin [OPTIONS] COMMAND DEVPATH\n\n"
"Test a built-in command.\n\n"
" -h --help Print this message\n"
@@ -22,85 +22,76 @@ static void help(struct udev *udev) {
"Commands:\n"
, program_invocation_short_name);
- udev_builtin_list(udev);
+ udev_builtin_list();
+
+ return 0;
}
-static int adm_builtin(struct udev *udev, int argc, char *argv[]) {
+static int parse_argv(int argc, char *argv[]) {
static const struct option options[] = {
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{}
};
- char *command = NULL;
- char *syspath = NULL;
- char filename[UTIL_PATH_SIZE];
- struct udev_device *dev = NULL;
- enum udev_builtin_cmd cmd;
- int rc = EXIT_SUCCESS, c;
+
+ int c;
while ((c = getopt_long(argc, argv, "Vh", options, NULL)) >= 0)
switch (c) {
case 'V':
- print_version();
- goto out;
+ return print_version();
case 'h':
- help(udev);
- goto out;
+ return help();
+ case '?':
+ return -EINVAL;
+ default:
+ assert_not_reached("Unknown option");
}
- command = argv[optind++];
- if (command == NULL) {
- fprintf(stderr, "command missing\n");
- help(udev);
- rc = 2;
- goto out;
- }
+ arg_command = argv[optind++];
+ if (!arg_command)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Command missing.");
- syspath = argv[optind++];
- if (syspath == NULL) {
- fprintf(stderr, "syspath missing\n");
- rc = 3;
- goto out;
- }
+ arg_syspath = argv[optind++];
+ if (!arg_syspath)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "syspath missing.");
- udev_builtin_init(udev);
+ return 1;
+}
- cmd = udev_builtin_lookup(command);
- if (cmd >= UDEV_BUILTIN_MAX) {
- fprintf(stderr, "unknown command '%s'\n", command);
- help(udev);
- rc = 5;
- goto out;
- }
+int builtin_main(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ enum udev_builtin_cmd cmd;
+ int r;
+
+ log_set_max_level(LOG_DEBUG);
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ udev_builtin_init();
- /* add /sys if needed */
- if (!path_startswith(syspath, "/sys"))
- strscpyl(filename, sizeof(filename), "/sys", syspath, NULL);
- else
- strscpy(filename, sizeof(filename), syspath);
- delete_trailing_chars(filename, "/");
-
- dev = udev_device_new_from_syspath(udev, filename);
- if (dev == NULL) {
- fprintf(stderr, "unable to open device '%s'\n\n", filename);
- rc = 4;
- goto out;
+ cmd = udev_builtin_lookup(arg_command);
+ if (cmd < 0) {
+ log_error("Unknown command '%s'", arg_command);
+ r = -EINVAL;
+ goto finish;
}
- rc = udev_builtin_run(dev, cmd, command, true);
- if (rc < 0) {
- fprintf(stderr, "error executing '%s', exit code %i\n\n", command, rc);
- rc = 6;
+ r = find_device(arg_syspath, "/sys", &dev);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open device '%s': %m", arg_syspath);
+ goto finish;
}
-out:
- udev_device_unref(dev);
- udev_builtin_exit(udev);
- return rc;
-}
-const struct udevadm_cmd udevadm_test_builtin = {
- .name = "test-builtin",
- .cmd = adm_builtin,
- .help = "Test a built-in command",
- .debug = true,
-};
+ r = udev_builtin_run(dev, cmd, arg_command, true);
+ if (r < 0)
+ log_debug_errno(r, "Builtin command '%s' fails: %m", arg_command);
+
+finish:
+ udev_builtin_exit();
+ return r;
+}
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index e3d85597a8..54c525e02c 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright © 2003-2004 Greg Kroah-Hartman <greg@kroah.com>
- *
*/
#include <errno.h>
@@ -13,12 +12,22 @@
#include <sys/signalfd.h>
#include <unistd.h>
+#include "sd-device.h"
+
+#include "device-private.h"
+#include "device-util.h"
+#include "libudev-util.h"
#include "string-util.h"
-#include "udev-util.h"
+#include "strxcpyx.h"
+#include "udev-builtin.h"
#include "udev.h"
-#include "udevadm-util.h"
+#include "udevadm.h"
+
+static const char *arg_action = "add";
+static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
+static char arg_syspath[UTIL_PATH_SIZE] = {};
-static void help(void) {
+static int help(void) {
printf("%s test [OPTIONS] DEVPATH\n\n"
"Test an event run.\n\n"
@@ -27,20 +36,11 @@ static void help(void) {
" -a --action=ACTION Set action string\n"
" -N --resolve-names=early|late|never When to resolve names\n"
, program_invocation_short_name);
-}
-static int adm_test(struct udev *udev, int argc, char *argv[]) {
- int resolve_names = 1;
- char filename[UTIL_PATH_SIZE];
- const char *action = "add";
- const char *syspath = NULL;
- struct udev_list_entry *entry;
- _cleanup_(udev_rules_unrefp) struct udev_rules *rules = NULL;
- _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
- _cleanup_(udev_event_unrefp) struct udev_event *event = NULL;
- sigset_t mask, sigmask_orig;
- int rc = 0, c;
+ return 0;
+}
+static int parse_argv(int argc, char *argv[]) {
static const struct option options[] = {
{ "action", required_argument, NULL, 'a' },
{ "resolve-names", required_argument, NULL, 'N' },
@@ -49,105 +49,101 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
{}
};
- log_debug("version %s", PACKAGE_VERSION);
+ int c;
while ((c = getopt_long(argc, argv, "a:N:Vh", options, NULL)) >= 0)
switch (c) {
case 'a':
- action = optarg;
+ arg_action = optarg;
break;
case 'N':
- if (streq (optarg, "early")) {
- resolve_names = 1;
- } else if (streq (optarg, "late")) {
- resolve_names = 0;
- } else if (streq (optarg, "never")) {
- resolve_names = -1;
- } else {
- fprintf(stderr, "resolve-names must be early, late or never\n");
- log_error("resolve-names must be early, late or never");
- exit(EXIT_FAILURE);
- }
+ arg_resolve_name_timing = resolve_name_timing_from_string(optarg);
+ if (arg_resolve_name_timing < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--resolve-names= must be early, late or never");
break;
case 'V':
- print_version();
- exit(EXIT_SUCCESS);
+ return print_version();
case 'h':
- help();
- exit(EXIT_SUCCESS);
+ return help();
case '?':
- exit(EXIT_FAILURE);
+ return -EINVAL;
default:
assert_not_reached("Unknown option");
}
- syspath = argv[optind];
- if (syspath == NULL) {
- fprintf(stderr, "syspath parameter missing\n");
- rc = 2;
- goto out;
- }
+ if (!argv[optind])
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "syspath parameter missing.");
+
+ /* add /sys if needed */
+ if (!startswith(argv[optind], "/sys"))
+ strscpyl(arg_syspath, sizeof(arg_syspath), "/sys", argv[optind], NULL);
+ else
+ strscpy(arg_syspath, sizeof(arg_syspath), argv[optind]);
+
+ return 1;
+}
+
+int test_main(int argc, char *argv[], void *userdata) {
+ _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
+ _cleanup_(udev_event_freep) UdevEvent *event = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ const char *cmd, *key, *value;
+ sigset_t mask, sigmask_orig;
+ Iterator i;
+ void *val;
+ int r;
+
+ log_set_max_level(LOG_DEBUG);
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
printf("This program is for debugging only, it does not run any program\n"
"specified by a RUN key. It may show incorrect results, because\n"
"some values may be different, or not available at a simulation run.\n"
"\n");
- sigprocmask(SIG_SETMASK, NULL, &sigmask_orig);
+ assert_se(sigprocmask(SIG_SETMASK, NULL, &sigmask_orig) >= 0);
- udev_builtin_init(udev);
+ udev_builtin_init();
- rules = udev_rules_new(udev, resolve_names);
- if (rules == NULL) {
- fprintf(stderr, "error reading rules\n");
- rc = 3;
+ r = udev_rules_new(&rules, arg_resolve_name_timing);
+ if (r < 0) {
+ log_error_errno(r, "Failed to read udev rules: %m");
goto out;
}
- /* add /sys if needed */
- if (!startswith(syspath, "/sys"))
- strscpyl(filename, sizeof(filename), "/sys", syspath, NULL);
- else
- strscpy(filename, sizeof(filename), syspath);
- delete_trailing_chars(filename, "/");
-
- dev = udev_device_new_from_synthetic_event(udev, filename, action);
- if (dev == NULL) {
- fprintf(stderr, "unable to open device '%s'\n", filename);
- rc = 4;
+ r = device_new_from_synthetic_event(&dev, arg_syspath, arg_action);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open device '%s': %m", arg_syspath);
goto out;
}
/* don't read info from the db */
- udev_device_set_info_loaded(dev);
+ device_seal(dev);
- event = udev_event_new(dev);
+ event = udev_event_new(dev, 0, NULL);
- sigfillset(&mask);
- sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
+ assert_se(sigfillset(&mask) >= 0);
+ assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0);
- udev_event_execute_rules(event,
- 60 * USEC_PER_SEC, 20 * USEC_PER_SEC,
- NULL,
- rules);
+ udev_event_execute_rules(event, 60 * USEC_PER_SEC, NULL, rules);
- udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev))
- printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
+ FOREACH_DEVICE_PROPERTY(dev, key, value)
+ printf("%s=%s\n", key, value);
- udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) {
+ HASHMAP_FOREACH_KEY(val, cmd, event->run_list, i) {
char program[UTIL_PATH_SIZE];
- udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program), false);
+ udev_event_apply_format(event, cmd, program, sizeof(program), false);
printf("run: '%s'\n", program);
}
+
+ r = 0;
out:
- udev_builtin_exit(udev);
- return rc;
+ udev_builtin_exit();
+ return r;
}
-
-const struct udevadm_cmd udevadm_test = {
- .name = "test",
- .cmd = adm_test,
- .help = "Test an event run",
- .debug = true,
-};
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
index 9c07a51869..f13a08f3f9 100644
--- a/src/udev/udevadm-trigger.c
+++ b/src/udev/udevadm-trigger.c
@@ -1,74 +1,102 @@
/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- *
- */
#include <errno.h>
-#include <fcntl.h>
#include <getopt.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
+#include "sd-device.h"
+#include "sd-event.h"
+
+#include "device-enumerator-private.h"
#include "fd-util.h"
+#include "fileio.h"
+#include "path-util.h"
#include "set.h"
#include "string-util.h"
-#include "udev-util.h"
-#include "udev.h"
+#include "strv.h"
+#include "udevadm.h"
#include "udevadm-util.h"
-#include "util.h"
-static int verbose;
-static int dry_run;
+static bool arg_verbose = false;
+static bool arg_dry_run = false;
-static int exec_list(struct udev_enumerate *udev_enumerate, const char *action, Set *settle_set) {
- struct udev_list_entry *entry;
+static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_set) {
+ sd_device *d;
int r;
- udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) {
- char filename[UTIL_PATH_SIZE];
+ FOREACH_DEVICE_AND_SUBSYSTEM(e, d) {
+ _cleanup_free_ char *filename = NULL;
const char *syspath;
- _cleanup_close_ int fd = -1;
- syspath = udev_list_entry_get_name(entry);
- if (verbose)
+ if (sd_device_get_syspath(d, &syspath) < 0)
+ continue;
+
+ if (arg_verbose)
printf("%s\n", syspath);
- if (dry_run)
+ if (arg_dry_run)
continue;
- strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
- fd = open(filename, O_WRONLY|O_CLOEXEC);
- if (fd < 0)
+ filename = path_join(syspath, "uevent");
+ if (!filename)
+ return log_oom();
+
+ r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to write '%s' to '%s', ignoring: %m", action, filename);
continue;
+ }
if (settle_set) {
r = set_put_strdup(settle_set, syspath);
if (r < 0)
return log_oom();
}
-
- if (write(fd, action, strlen(action)) < 0)
- log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename);
}
return 0;
}
-static const char *keyval(const char *str, const char **val, char *buf, size_t size) {
- char *pos;
+static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *userdata) {
+ Set *settle_set = userdata;
+ const char *syspath;
+
+ assert(dev);
+ assert(settle_set);
+
+ if (sd_device_get_syspath(dev, &syspath) < 0)
+ return 0;
+
+ if (arg_verbose)
+ printf("settle %s\n", syspath);
+
+ if (!set_remove(settle_set, syspath))
+ log_debug("Got epoll event on syspath %s not present in syspath set", syspath);
+
+ if (set_isempty(settle_set))
+ return sd_event_exit(sd_device_monitor_get_event(m), 0);
+
+ return 0;
+}
+
+static char* keyval(const char *str, const char **key, const char **val) {
+ char *buf, *pos;
+
+ buf = strdup(str);
+ if (!buf)
+ return NULL;
- strscpy(buf, size,str);
pos = strchr(buf, '=');
- if (pos != NULL) {
+ if (pos) {
pos[0] = 0;
pos++;
}
+
+ *key = buf;
*val = pos;
+
return buf;
}
-static void help(void) {
+static int help(void) {
printf("%s trigger [OPTIONS] DEVPATH\n\n"
"Request events from the kernel.\n\n"
" -h --help Show this help\n"
@@ -90,9 +118,11 @@ static void help(void) {
" -b --parent-match=NAME Trigger devices with that parent device\n"
" -w --settle Wait for the triggered events to complete\n"
, program_invocation_short_name);
+
+ return 0;
}
-static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
+int trigger_main(int argc, char *argv[], void *userdata) {
enum {
ARG_NAME = 0x100,
};
@@ -121,30 +151,31 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
TYPE_SUBSYSTEMS,
} device_type = TYPE_DEVICES;
const char *action = "change";
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *udev_enumerate = NULL;
- _cleanup_(udev_monitor_unrefp) struct udev_monitor *udev_monitor = NULL;
- _cleanup_close_ int fd_ep = -1;
- int fd_udev = -1;
- struct epoll_event ep_udev;
- bool settle = false;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_set_free_free_ Set *settle_set = NULL;
+ bool settle = false;
int c, r;
- udev_enumerate = udev_enumerate_new(udev);
- if (!udev_enumerate)
- return 1;
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:wVh", options, NULL)) >= 0) {
- const char *key;
- const char *val;
- char buf[UTIL_PATH_SIZE];
+ _cleanup_free_ char *buf = NULL;
+ const char *key, *val;
switch (c) {
case 'v':
- verbose = 1;
+ arg_verbose = true;
break;
case 'n':
- dry_run = 1;
+ arg_dry_run = true;
break;
case 't':
if (streq(optarg, "devices"))
@@ -152,84 +183,73 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
else if (streq(optarg, "subsystems"))
device_type = TYPE_SUBSYSTEMS;
else {
- log_error("unknown type --type=%s", optarg);
- return 2;
+ log_error("Unknown type --type=%s", optarg);
+ return -EINVAL;
}
break;
case 'c':
- if (!STR_IN_SET(optarg, "add", "remove", "change")) {
- log_error("unknown action '%s'", optarg);
- return 2;
- } else
+ if (STR_IN_SET(optarg, "add", "remove", "change"))
action = optarg;
+ else {
+ log_error("Unknown action '%s'", optarg);
+ return -EINVAL;
+ }
break;
case 's':
- r = udev_enumerate_add_match_subsystem(udev_enumerate, optarg);
- if (r < 0) {
- log_error_errno(r, "could not add subsystem match '%s': %m", optarg);
- return 2;
- }
+ r = sd_device_enumerator_add_match_subsystem(e, optarg, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add subsystem match '%s': %m", optarg);
break;
case 'S':
- r = udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg);
- if (r < 0) {
- log_error_errno(r, "could not add negative subsystem match '%s': %m", optarg);
- return 2;
- }
+ r = sd_device_enumerator_add_match_subsystem(e, optarg, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add negative subsystem match '%s': %m", optarg);
break;
case 'a':
- key = keyval(optarg, &val, buf, sizeof(buf));
- r = udev_enumerate_add_match_sysattr(udev_enumerate, key, val);
- if (r < 0) {
- log_error_errno(r, "could not add sysattr match '%s=%s': %m", key, val);
- return 2;
- }
+ buf = keyval(optarg, &key, &val);
+ if (!buf)
+ return log_oom();
+ r = sd_device_enumerator_add_match_sysattr(e, key, val, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add sysattr match '%s=%s': %m", key, val);
break;
case 'A':
- key = keyval(optarg, &val, buf, sizeof(buf));
- r = udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val);
- if (r < 0) {
- log_error_errno(r, "could not add negative sysattr match '%s=%s': %m", key, val);
- return 2;
- }
+ buf = keyval(optarg, &key, &val);
+ if (!buf)
+ return log_oom();
+ r = sd_device_enumerator_add_match_sysattr(e, key, val, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add negative sysattr match '%s=%s': %m", key, val);
break;
case 'p':
- key = keyval(optarg, &val, buf, sizeof(buf));
- r = udev_enumerate_add_match_property(udev_enumerate, key, val);
- if (r < 0) {
- log_error_errno(r, "could not add property match '%s=%s': %m", key, val);
- return 2;
- }
+ buf = keyval(optarg, &key, &val);
+ if (!buf)
+ return log_oom();
+ r = sd_device_enumerator_add_match_property(e, key, val);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add property match '%s=%s': %m", key, val);
break;
case 'g':
- r = udev_enumerate_add_match_tag(udev_enumerate, optarg);
- if (r < 0) {
- log_error_errno(r, "could not add tag match '%s': %m", optarg);
- return 2;
- }
+ r = sd_device_enumerator_add_match_tag(e, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add tag match '%s': %m", optarg);
break;
case 'y':
- r = udev_enumerate_add_match_sysname(udev_enumerate, optarg);
- if (r < 0) {
- log_error_errno(r, "could not add sysname match '%s': %m", optarg);
- return 2;
- }
+ r = sd_device_enumerator_add_match_sysname(e, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add sysname match '%s': %m", optarg);
break;
case 'b': {
- _cleanup_(udev_device_unrefp) struct udev_device *dev;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- dev = find_device(udev, optarg, "/sys");
- if (!dev) {
- log_error("unable to open the device '%s'", optarg);
- return 2;
- }
+ r = find_device(optarg, "/sys", &dev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open the device '%s': %m", optarg);
- r = udev_enumerate_add_match_parent(udev_enumerate, dev);
- if (r < 0) {
- log_error_errno(r, "could not add parent match '%s': %m", optarg);
- return 2;
- }
+ r = sd_device_enumerator_add_match_parent(e, dev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add parent match '%s': %m", optarg);
break;
}
case 'w':
@@ -237,132 +257,86 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
break;
case ARG_NAME: {
- _cleanup_(udev_device_unrefp) struct udev_device *dev;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- dev = find_device(udev, optarg, "/dev/");
- if (!dev) {
- log_error("unable to open the device '%s'", optarg);
- return 2;
- }
+ r = find_device(optarg, "/dev/", &dev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open the device '%s': %m", optarg);
- r = udev_enumerate_add_match_parent(udev_enumerate, dev);
- if (r < 0) {
- log_error_errno(r, "could not add parent match '%s': %m", optarg);
- return 2;
- }
+ r = sd_device_enumerator_add_match_parent(e, dev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add parent match '%s': %m", optarg);
break;
}
case 'V':
- print_version();
- return 0;
+ return print_version();
case 'h':
- help();
- return 0;
+ return help();
case '?':
- return 1;
+ return -EINVAL;
default:
assert_not_reached("Unknown option");
}
}
for (; optind < argc; optind++) {
- _cleanup_(udev_device_unrefp) struct udev_device *dev;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- dev = find_device(udev, argv[optind], NULL);
- if (!dev) {
- log_error("unable to open the device '%s'", argv[optind]);
- return 2;
- }
+ r = find_device(argv[optind], NULL, &dev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open the device '%s': %m", argv[optind]);
- r = udev_enumerate_add_match_parent(udev_enumerate, dev);
- if (r < 0) {
- log_error_errno(r, "could not add tag match '%s': %m", optarg);
- return 2;
- }
+ r = sd_device_enumerator_add_match_parent(e, dev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add parent match '%s': %m", argv[optind]);
}
if (settle) {
- fd_ep = epoll_create1(EPOLL_CLOEXEC);
- if (fd_ep < 0) {
- log_error_errno(errno, "error creating epoll fd: %m");
- return 1;
- }
+ settle_set = set_new(&string_hash_ops);
+ if (!settle_set)
+ return log_oom();
- udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
- if (!udev_monitor) {
- log_error("error: unable to create netlink socket");
- return 3;
- }
- fd_udev = udev_monitor_get_fd(udev_monitor);
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get default event: %m");
- if (udev_monitor_enable_receiving(udev_monitor) < 0) {
- log_error("error: unable to subscribe to udev events");
- return 4;
- }
+ r = sd_device_monitor_new(&m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create device monitor object: %m");
- ep_udev = (struct epoll_event) { .events = EPOLLIN, .data.fd = fd_udev };
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
- log_error_errno(errno, "fail to add fd to epoll: %m");
- return 5;
- }
+ r = sd_device_monitor_attach_event(m, event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach event to device monitor: %m");
- settle_set = set_new(&string_hash_ops);
- if (!settle_set) {
- log_oom();
- return 1;
- }
+ r = sd_device_monitor_start(m, device_monitor_handler, settle_set);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start device monitor: %m");
}
switch (device_type) {
case TYPE_SUBSYSTEMS:
- udev_enumerate_scan_subsystems(udev_enumerate);
+ r = device_enumerator_scan_subsystems(e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to scan subsystems: %m");
break;
case TYPE_DEVICES:
- udev_enumerate_scan_devices(udev_enumerate);
+ r = device_enumerator_scan_devices(e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to scan devices: %m");
break;
default:
- assert_not_reached("device_type");
+ assert_not_reached("Unknown device type");
}
- r = exec_list(udev_enumerate, action, settle_set);
+ r = exec_list(e, action, settle_set);
if (r < 0)
- return 1;
+ return r;
- while (!set_isempty(settle_set)) {
- int fdcount;
- struct epoll_event ev[4];
- int i;
-
- fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
- if (fdcount < 0) {
- if (errno != EINTR)
- log_error_errno(errno, "error receiving uevent message: %m");
- continue;
- }
-
- for (i = 0; i < fdcount; i++) {
- if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
- _cleanup_(udev_device_unrefp) struct udev_device *device;
- const char *syspath = NULL;
-
- device = udev_monitor_receive_device(udev_monitor);
- if (!device)
- continue;
-
- syspath = udev_device_get_syspath(device);
- if (verbose)
- printf("settle %s\n", syspath);
- if (!set_remove(settle_set, syspath))
- log_debug("Got epoll event on syspath %s not present in syspath set", syspath);
- }
- }
+ if (event && !set_isempty(settle_set)) {
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
}
return 0;
}
-
-const struct udevadm_cmd udevadm_trigger = {
- .name = "trigger",
- .cmd = adm_trigger,
- .help = "Request events from the kernel",
-};
diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c
index c570b72b8e..557982b23d 100644
--- a/src/udev/udevadm-util.c
+++ b/src/udev/udevadm-util.c
@@ -1,39 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- *
- */
+#include <errno.h>
+
+#include "alloc-util.h"
+#include "device-private.h"
#include "path-util.h"
-#include "string-util.h"
#include "udevadm-util.h"
+#include "unit-name.h"
-struct udev_device *find_device(struct udev *udev,
- const char *id,
- const char *prefix) {
+int find_device(const char *id, const char *prefix, sd_device **ret) {
+ _cleanup_free_ char *path = NULL;
+ int r;
- assert(udev);
assert(id);
-
- if (prefix && !startswith(id, prefix))
- id = strjoina(prefix, id);
+ assert(ret);
+
+ if (prefix) {
+ if (!path_startswith(id, prefix)) {
+ id = path = path_join(prefix, id);
+ if (!path)
+ return -ENOMEM;
+ }
+ } else {
+ /* In cases where the argument is generic (no prefix specified),
+ * check if the argument looks like a device unit name. */
+ if (unit_name_is_valid(id, UNIT_NAME_PLAIN) &&
+ unit_name_to_type(id) == UNIT_DEVICE) {
+ r = unit_name_to_path(id, &path);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to convert \"%s\" to a device path: %m", id);
+ id = path;
+ }
+ }
+
+ if (path_startswith(id, "/sys/"))
+ return sd_device_new_from_syspath(ret, id);
if (path_startswith(id, "/dev/")) {
- struct stat statbuf;
- char type;
-
- if (stat(id, &statbuf) < 0)
- return NULL;
-
- if (S_ISBLK(statbuf.st_mode))
- type = 'b';
- else if (S_ISCHR(statbuf.st_mode))
- type = 'c';
- else
- return NULL;
-
- return udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
- } else if (path_startswith(id, "/sys/"))
- return udev_device_new_from_syspath(udev, id);
- else
- return NULL;
+ struct stat st;
+
+ if (stat(id, &st) < 0)
+ return -errno;
+
+ return device_new_from_stat_rdev(ret, &st);
+ }
+
+ return -EINVAL;
}
diff --git a/src/udev/udevadm-util.h b/src/udev/udevadm-util.h
index 0b426e09f6..59e89022a0 100644
--- a/src/udev/udevadm-util.h
+++ b/src/udev/udevadm-util.h
@@ -1,15 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
#pragma once
-/*
- */
+#include "sd-device.h"
-#include "udev.h"
-
-struct udev_device *find_device(struct udev *udev,
- const char *id,
- const char *prefix);
-
-static inline void print_version(void) {
- printf("%s\n", PACKAGE_VERSION);
-}
+int find_device(const char *id, const char *prefix, sd_device **ret);
diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
index 0b79d2f91d..1125c54b9f 100644
--- a/src/udev/udevadm.c
+++ b/src/udev/udevadm.c
@@ -1,90 +1,62 @@
/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- *
- */
#include <errno.h>
#include <getopt.h>
#include <stddef.h>
#include <stdio.h>
+#include "alloc-util.h"
+#include "main-func.h"
+#include "pretty-print.h"
#include "selinux-util.h"
#include "string-util.h"
-#include "udev.h"
+#include "udevadm.h"
#include "udev-util.h"
+#include "verbs.h"
+#include "util.h"
+
+static int help(void) {
+ static const char * short_descriptions[][2] = {
+ { "info", "Query sysfs or the udev database" },
+ { "trigger", "Request events from the kernel" },
+ { "settle", "Wait for pending udev events" },
+ { "control", "Control the udev daemon" },
+ { "monitor", "Listen to kernel and udev events" },
+ { "test", "Test an event run" },
+ { "test-builtin", "Test a built-in command" },
+ };
-static int adm_version(struct udev *udev, int argc, char *argv[]) {
- printf("%s\n", PACKAGE_VERSION);
- return 0;
-}
+ _cleanup_free_ char *link = NULL;
+ size_t i;
+ int r;
-static const struct udevadm_cmd udevadm_version = {
- .name = "version",
- .cmd = adm_version,
-};
-
-static int adm_help(struct udev *udev, int argc, char *argv[]);
-
-static const struct udevadm_cmd udevadm_help = {
- .name = "help",
- .cmd = adm_help,
-};
-
-static const struct udevadm_cmd *udevadm_cmds[] = {
- &udevadm_info,
- &udevadm_trigger,
- &udevadm_settle,
- &udevadm_control,
- &udevadm_monitor,
- &udevadm_hwdb,
- &udevadm_test,
- &udevadm_test_builtin,
- &udevadm_version,
- &udevadm_help,
-};
-
-static int adm_help(struct udev *udev, int argc, char *argv[]) {
- unsigned int i;
+ r = terminal_urlify_man("udevadm", "8", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n\n"
"Send control commands or test the device manager.\n\n"
"Commands:\n"
, program_invocation_short_name);
- for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++)
- if (udevadm_cmds[i]->help != NULL)
- printf(" %-12s %s\n", udevadm_cmds[i]->name, udevadm_cmds[i]->help);
- return 0;
-}
+ for (i = 0; i < ELEMENTSOF(short_descriptions); i++)
+ printf(" %-12s %s\n", short_descriptions[i][0], short_descriptions[i][1]);
-static int run_command(struct udev *udev, const struct udevadm_cmd *cmd, int argc, char *argv[]) {
- if (cmd->debug)
- log_set_max_level(LOG_DEBUG);
- log_debug("calling: %s", cmd->name);
- return cmd->cmd(udev, argc, argv);
+ printf("\nSee the %s for details.\n", link);
+ return 0;
}
-int main(int argc, char *argv[]) {
- struct udev *udev;
+static int parse_argv(int argc, char *argv[]) {
static const struct option options[] = {
- { "debug", no_argument, NULL, 'd' },
- { "help", no_argument, NULL, 'h' },
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{}
};
- const char *command;
- unsigned int i;
- int rc = 1, c;
-
- udev_parse_config();
- log_parse_environment();
- log_open();
-
- mac_selinux_init();
+ int c;
- udev = udev_new();
- if (udev == NULL)
- goto out;
+ assert(argc >= 0);
+ assert(argv);
while ((c = getopt_long(argc, argv, "+dhV", options, NULL)) >= 0)
switch (c) {
@@ -94,35 +66,61 @@ int main(int argc, char *argv[]) {
break;
case 'h':
- rc = adm_help(udev, argc, argv);
- goto out;
+ return help();
case 'V':
- rc = adm_version(udev, argc, argv);
- goto out;
+ return print_version();
+
+ case '?':
+ return -EINVAL;
default:
- goto out;
+ assert_not_reached("Unhandled option");
}
- command = argv[optind];
-
- if (command != NULL)
- for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++)
- if (streq(udevadm_cmds[i]->name, command)) {
- argc -= optind;
- argv += optind;
- /* we need '0' here to reset the internal state */
- optind = 0;
- rc = run_command(udev, udevadm_cmds[i], argc, argv);
- goto out;
- }
-
- fprintf(stderr, "%s: missing or unknown command\n", program_invocation_short_name);
- rc = 2;
-out:
- mac_selinux_finish();
- udev_unref(udev);
- log_close();
- return rc;
+ return 1; /* work to do */
+}
+
+static int version_main(int argc, char *argv[], void *userdata) {
+ return print_version();
+}
+
+static int help_main(int argc, char *argv[], void *userdata) {
+ return help();
+}
+
+static int udevadm_main(int argc, char *argv[]) {
+ static const Verb verbs[] = {
+ { "info", VERB_ANY, VERB_ANY, 0, info_main },
+ { "trigger", VERB_ANY, VERB_ANY, 0, trigger_main },
+ { "settle", VERB_ANY, VERB_ANY, 0, settle_main },
+ { "control", VERB_ANY, VERB_ANY, 0, control_main },
+ { "monitor", VERB_ANY, VERB_ANY, 0, monitor_main },
+ { "hwdb", VERB_ANY, VERB_ANY, 0, hwdb_main },
+ { "test", VERB_ANY, VERB_ANY, 0, test_main },
+ { "test-builtin", VERB_ANY, VERB_ANY, 0, builtin_main },
+ { "version", VERB_ANY, VERB_ANY, 0, version_main },
+ { "help", VERB_ANY, VERB_ANY, 0, help_main },
+ {}
+ };
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+static int run(int argc, char *argv[]) {
+ int r;
+
+ udev_parse_config();
+ log_parse_environment();
+ log_open();
+ log_set_max_level_realm(LOG_REALM_SYSTEMD, log_get_max_level());
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ mac_selinux_init();
+ return udevadm_main(argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/udev/udevadm.h b/src/udev/udevadm.h
new file mode 100644
index 0000000000..98f9019a48
--- /dev/null
+++ b/src/udev/udevadm.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#pragma once
+
+#include <stdio.h>
+
+int info_main(int argc, char *argv[], void *userdata);
+int trigger_main(int argc, char *argv[], void *userdata);
+int settle_main(int argc, char *argv[], void *userdata);
+int control_main(int argc, char *argv[], void *userdata);
+int monitor_main(int argc, char *argv[], void *userdata);
+int hwdb_main(int argc, char *argv[], void *userdata);
+int test_main(int argc, char *argv[], void *userdata);
+int builtin_main(int argc, char *argv[], void *userdata);
+
+static inline int print_version(void) {
+ puts(PACKAGE_VERSION);
+ return 0;
+}
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 34f6a95503..fb8724ea87 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -3,7 +3,6 @@
* Copyright © 2004 Chris Friesen <chris_friesen@sympatico.ca>
* Copyright © 2009 Canonical Ltd.
* Copyright © 2009 Scott James Remnant <scott@netsplit.com>
- *
*/
#include <errno.h>
@@ -35,46 +34,59 @@
#include "cgroup-util.h"
#include "cpu-set-util.h"
#include "dev-setup.h"
+#include "device-monitor-private.h"
+#include "device-private.h"
+#include "device-util.h"
+#include "event-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "hashmap.h"
#include "io-util.h"
+#include "libudev-device-internal.h"
#include "list.h"
+#include "main-func.h"
+#include "mkdir.h"
#include "netlink-util.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "proc-cmdline.h"
#include "process-util.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "string-util.h"
-#include "terminal-util.h"
+#include "strv.h"
+#include "strxcpyx.h"
+#include "syslog-util.h"
+#include "udev-builtin.h"
+#include "udev-ctrl.h"
#include "udev-util.h"
+#include "udev-watch.h"
#include "udev.h"
#include "user-util.h"
static bool arg_debug = false;
static int arg_daemonize = false;
-static int arg_resolve_names = 1;
-static unsigned arg_children_max;
-static int arg_exec_delay;
+static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
+static unsigned arg_children_max = 0;
+static usec_t arg_exec_delay_usec = 0;
static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC;
-static usec_t arg_event_timeout_warn_usec = 180 * USEC_PER_SEC / 3;
typedef struct Manager {
- struct udev *udev;
sd_event *event;
Hashmap *workers;
LIST_HEAD(struct event, events);
const char *cgroup;
pid_t pid; /* the process that originally allocated the manager object */
- struct udev_rules *rules;
- struct udev_list properties;
+ UdevRules *rules;
+ Hashmap *properties;
- struct udev_monitor *monitor;
+ sd_netlink *rtnl;
+
+ sd_device_monitor *monitor;
struct udev_ctrl *ctrl;
struct udev_ctrl_connection *ctrl_conn_blocking;
int fd_inotify;
@@ -83,6 +95,7 @@ typedef struct Manager {
sd_event_source *ctrl_event;
sd_event_source *uevent_event;
sd_event_source *inotify_event;
+ sd_event_source *kill_workers_event;
usec_t last_usec;
@@ -97,23 +110,20 @@ enum event_state {
};
struct event {
- LIST_FIELDS(struct event, event);
Manager *manager;
- struct udev *udev;
- struct udev_device *dev;
- struct udev_device *dev_kernel;
struct worker *worker;
enum event_state state;
- unsigned long long int delaying_seqnum;
- unsigned long long int seqnum;
- const char *devpath;
- size_t devpath_len;
- const char *devpath_old;
- dev_t devnum;
- int ifindex;
- bool is_block;
- sd_event_source *timeout_warning;
- sd_event_source *timeout;
+
+ sd_device *dev;
+ sd_device *dev_kernel; /* clone of originally received device */
+
+ uint64_t seqnum;
+ uint64_t delaying_seqnum;
+
+ sd_event_source *timeout_warning_event;
+ sd_event_source *timeout_event;
+
+ LIST_FIELDS(struct event, event);
};
static void event_queue_cleanup(Manager *manager, enum event_state type);
@@ -127,9 +137,8 @@ enum worker_state {
struct worker {
Manager *manager;
- int refcount;
pid_t pid;
- struct udev_monitor *monitor;
+ sd_device_monitor *monitor;
enum worker_state state;
struct event *event;
};
@@ -139,30 +148,26 @@ struct worker_message {
};
static void event_free(struct event *event) {
- int r;
-
if (!event)
return;
+
assert(event->manager);
LIST_REMOVE(event, event->manager->events, event);
- udev_device_unref(event->dev);
- udev_device_unref(event->dev_kernel);
+ sd_device_unref(event->dev);
+ sd_device_unref(event->dev_kernel);
- sd_event_source_unref(event->timeout_warning);
- sd_event_source_unref(event->timeout);
+ sd_event_source_unref(event->timeout_warning_event);
+ sd_event_source_unref(event->timeout_event);
if (event->worker)
event->worker->event = NULL;
- if (LIST_IS_EMPTY(event->manager->events)) {
- /* only clean up the queue from the process that created it */
- if (event->manager->pid == getpid_cached()) {
- r = unlink("/run/udev/queue");
- if (r < 0)
- log_warning_errno(errno, "could not unlink /run/udev/queue: %m");
- }
- }
+ /* only clean up the queue from the process that created it */
+ if (LIST_IS_EMPTY(event->manager->events) &&
+ event->manager->pid == getpid_cached())
+ if (unlink("/run/udev/queue") < 0)
+ log_warning_errno(errno, "Failed to unlink /run/udev/queue: %m");
free(event);
}
@@ -174,7 +179,7 @@ static void worker_free(struct worker *worker) {
assert(worker->manager);
hashmap_remove(worker->manager->workers, PID_TO_PTR(worker->pid));
- udev_monitor_unref(worker->monitor);
+ sd_device_monitor_unref(worker->monitor);
event_free(worker->event);
free(worker);
@@ -192,7 +197,7 @@ static void manager_workers_free(Manager *manager) {
manager->workers = hashmap_free(manager->workers);
}
-static int worker_new(struct worker **ret, Manager *manager, struct udev_monitor *worker_monitor, pid_t pid) {
+static int worker_new(struct worker **ret, Manager *manager, sd_device_monitor *worker_monitor, pid_t pid) {
_cleanup_free_ struct worker *worker = NULL;
int r;
@@ -205,11 +210,10 @@ static int worker_new(struct worker **ret, Manager *manager, struct udev_monitor
if (!worker)
return -ENOMEM;
- worker->refcount = 1;
worker->manager = manager;
/* close monitor, but keep address around */
- udev_monitor_disconnect(worker_monitor);
- worker->monitor = udev_monitor_ref(worker_monitor);
+ device_monitor_disconnect(worker_monitor);
+ worker->monitor = sd_device_monitor_ref(worker_monitor);
worker->pid = pid;
r = hashmap_ensure_allocated(&manager->workers, NULL);
@@ -234,7 +238,7 @@ static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
kill_and_sigcont(event->worker->pid, SIGKILL);
event->worker->state = WORKER_KILLED;
- log_error("seq %llu '%s' killed", udev_device_get_seqnum(event->dev), event->devpath);
+ log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum);
return 1;
}
@@ -243,8 +247,9 @@ static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *use
struct event *event = userdata;
assert(event);
+ assert(event->worker);
- log_warning("seq %llu '%s' is taking a long time", udev_device_get_seqnum(event->dev), event->devpath);
+ log_device_warning(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" is taking a long time", event->worker->pid, event->seqnum);
return 1;
}
@@ -268,34 +273,45 @@ static void worker_attach_event(struct worker *worker, struct event *event) {
assert_se(sd_event_now(e, CLOCK_MONOTONIC, &usec) >= 0);
- (void) sd_event_add_time(e, &event->timeout_warning, CLOCK_MONOTONIC,
- usec + arg_event_timeout_warn_usec, USEC_PER_SEC, on_event_timeout_warning, event);
+ (void) sd_event_add_time(e, &event->timeout_warning_event, CLOCK_MONOTONIC,
+ usec + udev_warn_timeout(arg_event_timeout_usec), USEC_PER_SEC, on_event_timeout_warning, event);
- (void) sd_event_add_time(e, &event->timeout, CLOCK_MONOTONIC,
+ (void) sd_event_add_time(e, &event->timeout_event, CLOCK_MONOTONIC,
usec + arg_event_timeout_usec, USEC_PER_SEC, on_event_timeout, event);
}
-static void manager_free(Manager *manager) {
- if (!manager)
- return;
+static void manager_clear_for_worker(Manager *manager) {
+ assert(manager);
- udev_builtin_exit(manager->udev);
+ manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
+ manager->uevent_event = sd_event_source_unref(manager->uevent_event);
+ manager->inotify_event = sd_event_source_unref(manager->inotify_event);
+ manager->kill_workers_event = sd_event_source_unref(manager->kill_workers_event);
- sd_event_source_unref(manager->ctrl_event);
- sd_event_source_unref(manager->uevent_event);
- sd_event_source_unref(manager->inotify_event);
+ manager->event = sd_event_unref(manager->event);
- udev_unref(manager->udev);
- sd_event_unref(manager->event);
manager_workers_free(manager);
event_queue_cleanup(manager, EVENT_UNDEF);
- udev_monitor_unref(manager->monitor);
- udev_ctrl_unref(manager->ctrl);
- udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
+ manager->monitor = sd_device_monitor_unref(manager->monitor);
+ manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
+ manager->ctrl = udev_ctrl_unref(manager->ctrl);
+
+ manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
+}
+
+static void manager_free(Manager *manager) {
+ if (!manager)
+ return;
+
+ udev_builtin_exit();
+
+ manager_clear_for_worker(manager);
+
+ sd_netlink_unref(manager->rtnl);
- udev_list_cleanup(&manager->properties);
- udev_rules_unref(manager->rules);
+ hashmap_free_free_free(manager->properties);
+ udev_rules_free(manager->rules);
safe_close(manager->fd_inotify);
safe_close_pair(manager->worker_watch);
@@ -311,253 +327,252 @@ static int worker_send_message(int fd) {
return loop_write(fd, &message, sizeof(message), false);
}
-static bool shall_lock_device(struct udev_device *dev) {
- const char *sysname;
+static int worker_lock_block_device(sd_device *dev, int *ret_fd) {
+ _cleanup_close_ int fd = -1;
+ const char *val;
+ int r;
+
+ assert(dev);
+ assert(ret_fd);
+
+ /*
+ * Take a shared lock on the device node; this establishes
+ * a concept of device "ownership" to serialize device
+ * access. External processes holding an exclusive lock will
+ * cause udev to skip the event handling; in the case udev
+ * acquired the lock, the external process can block until
+ * udev has finished its event handling.
+ */
+
+ r = sd_device_get_property_value(dev, "ACTION", &val);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get the value of property 'ACTION': %m");
+
+ if (streq(val, "remove"))
+ return 0;
+
+ r = sd_device_get_subsystem(dev, &val);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
+
+ if (!streq(val, "block"))
+ return 0;
+
+ r = sd_device_get_sysname(dev, &val);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get sysname: %m");
+
+ if (STARTSWITH_SET(val, "dm-", "md", "drbd"))
+ return 0;
+
+ r = sd_device_get_devtype(dev, &val);
+ if (r < 0 && r != -ENOENT)
+ return log_device_debug_errno(dev, r, "Failed to get devtype: %m");
+ if (r >= 0 && streq(val, "partition")) {
+ r = sd_device_get_parent(dev, &dev);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get parent device: %m");
+ }
+
+ r = sd_device_get_devname(dev, &val);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get devname: %m");
- if (!streq_ptr("block", udev_device_get_subsystem(dev)))
- return false;
+ fd = open(val, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
+ if (fd < 0) {
+ log_device_debug_errno(dev, errno, "Failed to open '%s', ignoring: %m", val);
+ return 0;
+ }
+
+ if (flock(fd, LOCK_SH|LOCK_NB) < 0)
+ return log_device_debug_errno(dev, errno, "Failed to flock(%s): %m", val);
- sysname = udev_device_get_sysname(dev);
- return !startswith(sysname, "dm-") &&
- !startswith(sysname, "md") &&
- !startswith(sysname, "drbd");
+ *ret_fd = TAKE_FD(fd);
+ return 1;
}
-static void worker_spawn(Manager *manager, struct event *event) {
- struct udev *udev = event->udev;
- _cleanup_(udev_monitor_unrefp) struct udev_monitor *worker_monitor = NULL;
- pid_t pid;
- int r = 0;
+static int worker_process_device(Manager *manager, sd_device *dev) {
+ _cleanup_(udev_event_freep) UdevEvent *udev_event = NULL;
+ _cleanup_close_ int fd_lock = -1;
+ const char *seqnum;
+ int r;
- /* listen for new events */
- worker_monitor = udev_monitor_new_from_netlink(udev, NULL);
- if (worker_monitor == NULL)
- return;
- /* allow the main daemon netlink address to send devices to the worker */
- udev_monitor_allow_unicast_sender(worker_monitor, manager->monitor);
- r = udev_monitor_enable_receiving(worker_monitor);
+ assert(manager);
+ assert(dev);
+
+ r = sd_device_get_property_value(dev, "SEQNUM", &seqnum);
if (r < 0)
- log_error_errno(r, "worker: could not enable receiving of device: %m");
-
- pid = fork();
- switch (pid) {
- case 0: {
- struct udev_device *dev = NULL;
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
- int fd_monitor;
- _cleanup_close_ int fd_signal = -1, fd_ep = -1;
- struct epoll_event ep_signal = { .events = EPOLLIN };
- struct epoll_event ep_monitor = { .events = EPOLLIN };
- sigset_t mask;
-
- /* take initial device from queue */
- dev = TAKE_PTR(event->dev);
-
- unsetenv("NOTIFY_SOCKET");
-
- manager_workers_free(manager);
- event_queue_cleanup(manager, EVENT_UNDEF);
-
- manager->monitor = udev_monitor_unref(manager->monitor);
- manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
- manager->ctrl = udev_ctrl_unref(manager->ctrl);
- manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
-
- manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
- manager->uevent_event = sd_event_source_unref(manager->uevent_event);
- manager->inotify_event = sd_event_source_unref(manager->inotify_event);
-
- manager->event = sd_event_unref(manager->event);
-
- sigfillset(&mask);
- fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
- if (fd_signal < 0) {
- r = log_error_errno(errno, "error creating signalfd %m");
- goto out;
- }
- ep_signal.data.fd = fd_signal;
+ log_device_debug_errno(dev, r, "Failed to get SEQNUM: %m");
- fd_monitor = udev_monitor_get_fd(worker_monitor);
- ep_monitor.data.fd = fd_monitor;
+ log_device_debug(dev, "Processing device (SEQNUM=%s)", seqnum);
- fd_ep = epoll_create1(EPOLL_CLOEXEC);
- if (fd_ep < 0) {
- r = log_error_errno(errno, "error creating epoll fd: %m");
- goto out;
- }
+ udev_event = udev_event_new(dev, arg_exec_delay_usec, manager->rtnl);
+ if (!udev_event)
+ return -ENOMEM;
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
- epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
- r = log_error_errno(errno, "fail to add fds to epoll: %m");
- goto out;
- }
+ r = worker_lock_block_device(dev, &fd_lock);
+ if (r < 0)
+ return r;
- /* Request TERM signal if parent exits.
- Ignore error, not much we can do in that case. */
- (void) prctl(PR_SET_PDEATHSIG, SIGTERM);
+ /* apply rules, create node, symlinks */
+ udev_event_execute_rules(udev_event, arg_event_timeout_usec, manager->properties, manager->rules);
+ udev_event_execute_run(udev_event, arg_event_timeout_usec);
- /* Reset OOM score, we only protect the main daemon. */
- write_string_file("/proc/self/oom_score_adj", "0", 0);
+ if (!manager->rtnl)
+ /* in case rtnl was initialized */
+ manager->rtnl = sd_netlink_ref(udev_event->rtnl);
- for (;;) {
- struct udev_event *udev_event;
- int fd_lock = -1;
+ /* apply/restore inotify watch */
+ if (udev_event->inotify_watch) {
+ (void) udev_watch_begin(dev);
+ r = device_update_db(dev);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+ }
- assert(dev);
+ log_device_debug(dev, "Device (SEQNUM=%s) processed", seqnum);
- log_debug("seq %llu running", udev_device_get_seqnum(dev));
- udev_event = udev_event_new(dev);
- if (udev_event == NULL) {
- r = -ENOMEM;
- goto out;
- }
+ return 0;
+}
- if (arg_exec_delay > 0)
- udev_event->exec_delay = arg_exec_delay;
-
- /*
- * Take a shared lock on the device node; this establishes
- * a concept of device "ownership" to serialize device
- * access. External processes holding an exclusive lock will
- * cause udev to skip the event handling; in the case udev
- * acquired the lock, the external process can block until
- * udev has finished its event handling.
- */
- if (!streq_ptr(udev_device_get_action(dev), "remove") &&
- shall_lock_device(dev)) {
- struct udev_device *d = dev;
-
- if (streq_ptr("partition", udev_device_get_devtype(d)))
- d = udev_device_get_parent(d);
-
- if (d) {
- fd_lock = open(udev_device_get_devnode(d), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
- if (fd_lock >= 0 && flock(fd_lock, LOCK_SH|LOCK_NB) < 0) {
- log_debug_errno(errno, "Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d));
- fd_lock = safe_close(fd_lock);
- goto skip;
- }
- }
- }
+static int worker_device_monitor_handler(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
+ Manager *manager = userdata;
+ int r;
- /* needed for renaming netifs */
- udev_event->rtnl = rtnl;
+ assert(dev);
+ assert(manager);
- /* apply rules, create node, symlinks */
- udev_event_execute_rules(udev_event,
- arg_event_timeout_usec, arg_event_timeout_warn_usec,
- &manager->properties,
- manager->rules);
+ r = worker_process_device(manager, dev);
+ if (r < 0)
+ log_device_warning_errno(dev, r, "Failed to process device, ignoring: %m");
- udev_event_execute_run(udev_event,
- arg_event_timeout_usec, arg_event_timeout_warn_usec);
+ /* send processed event back to libudev listeners */
+ r = device_monitor_send_device(monitor, NULL, dev);
+ if (r < 0)
+ log_device_warning_errno(dev, r, "Failed to send device, ignoring: %m");
- if (udev_event->rtnl)
- /* in case rtnl was initialized */
- rtnl = sd_netlink_ref(udev_event->rtnl);
+ /* send udevd the result of the event execution */
+ r = worker_send_message(manager->worker_watch[WRITE_END]);
+ if (r < 0)
+ log_device_warning_errno(dev, r, "Failed to send signal to main daemon, ignoring: %m");
- /* apply/restore inotify watch */
- if (udev_event->inotify_watch) {
- udev_watch_begin(udev, dev);
- udev_device_update_db(dev);
- }
+ return 1;
+}
- safe_close(fd_lock);
+static int worker_main(Manager *_manager, sd_device_monitor *monitor, sd_device *first_device) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = first_device;
+ _cleanup_(manager_freep) Manager *manager = _manager;
+ int r, ret;
- /* send processed event back to libudev listeners */
- udev_monitor_send_device(worker_monitor, NULL, dev);
+ assert(manager);
+ assert(monitor);
+ assert(dev);
-skip:
- log_debug("seq %llu processed", udev_device_get_seqnum(dev));
+ unsetenv("NOTIFY_SOCKET");
- /* send udevd the result of the event execution */
- r = worker_send_message(manager->worker_watch[WRITE_END]);
- if (r < 0)
- log_error_errno(r, "failed to send result of seq %llu to main daemon: %m",
- udev_device_get_seqnum(dev));
-
- udev_device_unref(dev);
- dev = NULL;
-
- udev_event_unref(udev_event);
-
- /* wait for more device messages from main udevd, or term signal */
- while (dev == NULL) {
- struct epoll_event ev[4];
- int fdcount;
- int i;
-
- fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- r = log_error_errno(errno, "failed to poll: %m");
- goto out;
- }
-
- for (i = 0; i < fdcount; i++) {
- if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) {
- dev = udev_monitor_receive_device(worker_monitor);
- break;
- } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) {
- struct signalfd_siginfo fdsi;
- ssize_t size;
-
- size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
- if (size != sizeof(struct signalfd_siginfo))
- continue;
- switch (fdsi.ssi_signo) {
- case SIGTERM:
- goto out;
- }
- }
- }
- }
- }
-out:
- udev_device_unref(dev);
- manager_free(manager);
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, -1) >= 0);
+
+ /* Reset OOM score, we only protect the main daemon. */
+ r = set_oom_score_adjust(0);
+ if (r < 0)
+ log_debug_errno(r, "Failed to reset OOM score, ignoring: %m");
+
+ /* Clear unnecessary data in Manager object.*/
+ manager_clear_for_worker(manager);
+
+ r = sd_event_new(&manager->event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGTERM, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set SIGTERM event: %m");
+
+ r = sd_device_monitor_attach_event(monitor, manager->event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach event loop to device monitor: %m");
+
+ r = sd_device_monitor_start(monitor, worker_device_monitor_handler, manager);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start device monitor: %m");
+
+ (void) sd_event_source_set_description(sd_device_monitor_get_event_source(monitor), "worker-device-monitor");
+
+ /* Process first device */
+ (void) worker_device_monitor_handler(monitor, dev, manager);
+
+ r = sd_event_loop(manager->event);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
+
+ r = sd_event_get_exit_code(manager->event, &ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get exit code: %m");
+
+ return ret;
+}
+
+static int worker_spawn(Manager *manager, struct event *event) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *worker_monitor = NULL;
+ struct worker *worker;
+ pid_t pid;
+ int r;
+
+ /* listen for new events */
+ r = device_monitor_new_full(&worker_monitor, MONITOR_GROUP_NONE, -1);
+ if (r < 0)
+ return r;
+
+ /* allow the main daemon netlink address to send devices to the worker */
+ r = device_monitor_allow_unicast_sender(worker_monitor, manager->monitor);
+ if (r < 0)
+ return log_error_errno(r, "Worker: Failed to set unicast sender: %m");
+
+ r = device_monitor_enable_receiving(worker_monitor);
+ if (r < 0)
+ return log_error_errno(r, "Worker: Failed to enable receiving of device: %m");
+
+ r = safe_fork("(worker)", FORK_DEATHSIG, &pid);
+ if (r < 0) {
+ event->state = EVENT_QUEUED;
+ return log_error_errno(r, "Failed to fork() worker: %m");
+ }
+ if (r == 0) {
+ /* Worker process */
+ r = worker_main(manager, worker_monitor, sd_device_ref(event->dev));
log_close();
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
- case -1:
- event->state = EVENT_QUEUED;
- log_error_errno(errno, "fork of child failed: %m");
- break;
- default:
- {
- struct worker *worker;
- r = worker_new(&worker, manager, worker_monitor, pid);
- if (r < 0)
- return;
+ r = worker_new(&worker, manager, worker_monitor, pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create worker object: %m");
- worker_attach_event(worker, event);
+ worker_attach_event(worker, event);
- log_debug("seq %llu forked new worker ["PID_FMT"]", udev_device_get_seqnum(event->dev), pid);
- break;
- }
- }
+ log_device_debug(event->dev, "Worker ["PID_FMT"] is forked for processing SEQNUM=%"PRIu64".", pid, event->seqnum);
+ return 0;
}
static void event_run(Manager *manager, struct event *event) {
struct worker *worker;
Iterator i;
+ int r;
assert(manager);
assert(event);
HASHMAP_FOREACH(worker, manager->workers, i) {
- ssize_t count;
-
if (worker->state != WORKER_IDLE)
continue;
- count = udev_monitor_send_device(manager->monitor, worker->monitor, event->dev);
- if (count < 0) {
- log_error_errno(errno, "worker ["PID_FMT"] did not accept message %zi (%m), kill it",
- worker->pid, count);
- kill(worker->pid, SIGKILL);
+ r = device_monitor_send_device(manager->monitor, worker->monitor, event->dev);
+ if (r < 0) {
+ log_device_error_errno(event->dev, r, "Worker ["PID_FMT"] did not accept message, killing the worker: %m",
+ worker->pid);
+ (void) kill(worker->pid, SIGKILL);
worker->state = WORKER_KILLED;
continue;
}
@@ -567,7 +582,7 @@ static void event_run(Manager *manager, struct event *event) {
if (hashmap_size(manager->workers) >= arg_children_max) {
if (arg_children_max > 1)
- log_debug("maximum number (%i) of children reached", hashmap_size(manager->workers));
+ log_debug("Maximum number (%u) of children reached.", hashmap_size(manager->workers));
return;
}
@@ -575,8 +590,11 @@ static void event_run(Manager *manager, struct event *event) {
worker_spawn(manager, event);
}
-static int event_queue_insert(Manager *manager, struct udev_device *dev) {
+static int event_queue_insert(Manager *manager, sd_device *dev) {
+ _cleanup_(sd_device_unrefp) sd_device *clone = NULL;
struct event *event;
+ const char *val;
+ uint64_t seqnum;
int r;
assert(manager);
@@ -588,36 +606,54 @@ static int event_queue_insert(Manager *manager, struct udev_device *dev) {
assert(manager->pid == getpid_cached());
- event = new0(struct event, 1);
- if (!event)
- return -ENOMEM;
+ /* We only accepts devices received by device monitor. */
+ r = sd_device_get_property_value(dev, "SEQNUM", &val);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(val, &seqnum);
+ if (r < 0)
+ return r;
+
+ if (seqnum == 0)
+ return -EINVAL;
+
+ /* Save original device to restore the state on failures. */
+ r = device_shallow_clone(dev, &clone);
+ if (r < 0)
+ return r;
- event->udev = udev_device_get_udev(dev);
- event->manager = manager;
- event->dev = dev;
- event->dev_kernel = udev_device_shallow_clone(dev);
- udev_device_copy_properties(event->dev_kernel, dev);
- event->seqnum = udev_device_get_seqnum(dev);
- event->devpath = udev_device_get_devpath(dev);
- event->devpath_len = strlen(event->devpath);
- event->devpath_old = udev_device_get_devpath_old(dev);
- event->devnum = udev_device_get_devnum(dev);
- event->is_block = streq("block", udev_device_get_subsystem(dev));
- event->ifindex = udev_device_get_ifindex(dev);
+ r = device_copy_properties(clone, dev);
+ if (r < 0)
+ return r;
- log_debug("seq %llu queued, '%s' '%s'", udev_device_get_seqnum(dev),
- udev_device_get_action(dev), udev_device_get_subsystem(dev));
+ event = new(struct event, 1);
+ if (!event)
+ return -ENOMEM;
- event->state = EVENT_QUEUED;
+ *event = (struct event) {
+ .manager = manager,
+ .dev = sd_device_ref(dev),
+ .dev_kernel = TAKE_PTR(clone),
+ .seqnum = seqnum,
+ .state = EVENT_QUEUED,
+ };
if (LIST_IS_EMPTY(manager->events)) {
r = touch("/run/udev/queue");
if (r < 0)
- log_warning_errno(r, "could not touch /run/udev/queue: %m");
+ log_warning_errno(r, "Failed to touch /run/udev/queue: %m");
}
LIST_APPEND(event, manager->events, event);
+ if (DEBUG_LOGGING) {
+ if (sd_device_get_property_value(dev, "ACTION", &val) < 0)
+ val = NULL;
+
+ log_device_debug(dev, "Device (SEQNUM=%"PRIu64", ACTION=%s) is queued", seqnum, strnull(val));
+ }
+
return 0;
}
@@ -632,17 +668,48 @@ static void manager_kill_workers(Manager *manager) {
continue;
worker->state = WORKER_KILLED;
- kill(worker->pid, SIGTERM);
+ (void) kill(worker->pid, SIGTERM);
}
}
/* lookup event for identical, parent, child device */
-static bool is_devpath_busy(Manager *manager, struct event *event) {
+static int is_device_busy(Manager *manager, struct event *event) {
+ const char *subsystem, *devpath, *devpath_old = NULL;
+ dev_t devnum = makedev(0, 0);
struct event *loop_event;
- size_t common;
+ size_t devpath_len;
+ int r, ifindex = 0;
+ bool is_block;
+
+ r = sd_device_get_subsystem(event->dev, &subsystem);
+ if (r < 0)
+ return r;
+
+ is_block = streq(subsystem, "block");
+
+ r = sd_device_get_devpath(event->dev, &devpath);
+ if (r < 0)
+ return r;
+
+ devpath_len = strlen(devpath);
+
+ r = sd_device_get_property_value(event->dev, "DEVPATH_OLD", &devpath_old);
+ if (r < 0 && r != -ENOENT)
+ return r;
+
+ r = sd_device_get_devnum(event->dev, &devnum);
+ if (r < 0 && r != -ENOENT)
+ return r;
+
+ r = sd_device_get_ifindex(event->dev, &ifindex);
+ if (r < 0 && r != -ENOENT)
+ return r;
/* check if queue contains events we depend on */
LIST_FOREACH(event, loop_event, manager->events) {
+ size_t loop_devpath_len, common;
+ const char *loop_devpath;
+
/* we already found a later event, earlier cannot block us, no need to check again */
if (loop_event->seqnum < event->delaying_seqnum)
continue;
@@ -656,51 +723,65 @@ static bool is_devpath_busy(Manager *manager, struct event *event) {
break;
/* check major/minor */
- if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block)
- return true;
+ if (major(devnum) != 0) {
+ const char *s;
+ dev_t d;
+
+ if (sd_device_get_subsystem(loop_event->dev, &s) < 0)
+ continue;
+
+ if (sd_device_get_devnum(loop_event->dev, &d) >= 0 &&
+ devnum == d && is_block == streq(s, "block"))
+ return true;
+ }
/* check network device ifindex */
- if (event->ifindex != 0 && event->ifindex == loop_event->ifindex)
- return true;
+ if (ifindex > 0) {
+ int i;
+
+ if (sd_device_get_ifindex(loop_event->dev, &i) >= 0 &&
+ ifindex == i)
+ return true;
+ }
+
+ if (sd_device_get_devpath(loop_event->dev, &loop_devpath) < 0)
+ continue;
/* check our old name */
- if (event->devpath_old != NULL && streq(loop_event->devpath, event->devpath_old)) {
+ if (devpath_old && streq(devpath_old, loop_devpath)) {
event->delaying_seqnum = loop_event->seqnum;
return true;
}
+ loop_devpath_len = strlen(loop_devpath);
+
/* compare devpath */
- common = MIN(loop_event->devpath_len, event->devpath_len);
+ common = MIN(devpath_len, loop_devpath_len);
/* one devpath is contained in the other? */
- if (memcmp(loop_event->devpath, event->devpath, common) != 0)
+ if (!strneq(devpath, loop_devpath, common))
continue;
/* identical device event found */
- if (loop_event->devpath_len == event->devpath_len) {
+ if (devpath_len == loop_devpath_len) {
/* devices names might have changed/swapped in the meantime */
- if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block))
- continue;
- if (event->ifindex != 0 && event->ifindex != loop_event->ifindex)
+ if (major(devnum) != 0 || ifindex > 0)
continue;
event->delaying_seqnum = loop_event->seqnum;
return true;
}
/* parent device event found */
- if (event->devpath[common] == '/') {
+ if (devpath[common] == '/') {
event->delaying_seqnum = loop_event->seqnum;
return true;
}
/* child device event found */
- if (loop_event->devpath[common] == '/') {
+ if (loop_devpath[common] == '/') {
event->delaying_seqnum = loop_event->seqnum;
return true;
}
-
- /* no matching device */
- continue;
}
return false;
@@ -711,8 +792,7 @@ static int on_exit_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
assert(manager);
- log_error_errno(ETIMEDOUT, "giving up waiting for workers to finish");
-
+ log_error("Giving up waiting for workers to finish.");
sd_event_exit(manager->event, -ETIMEDOUT);
return 1;
@@ -738,7 +818,7 @@ static void manager_exit(Manager *manager) {
manager->fd_inotify = safe_close(manager->fd_inotify);
manager->uevent_event = sd_event_source_unref(manager->uevent_event);
- manager->monitor = udev_monitor_unref(manager->monitor);
+ manager->monitor = sd_device_monitor_unref(manager->monitor);
/* discard queued events and kill workers */
event_queue_cleanup(manager, EVENT_QUEUED);
@@ -762,17 +842,29 @@ static void manager_reload(Manager *manager) {
"STATUS=Flushing configuration...");
manager_kill_workers(manager);
- manager->rules = udev_rules_unref(manager->rules);
- udev_builtin_exit(manager->udev);
+ manager->rules = udev_rules_free(manager->rules);
+ udev_builtin_exit();
sd_notifyf(false,
"READY=1\n"
"STATUS=Processing with %u children at max", arg_children_max);
}
+static int on_kill_workers_event(sd_event_source *s, uint64_t usec, void *userdata) {
+ Manager *manager = userdata;
+
+ assert(manager);
+
+ log_debug("Cleanup idle workers");
+ manager_kill_workers(manager);
+
+ return 1;
+}
+
static void event_queue_start(Manager *manager) {
struct event *event;
usec_t usec;
+ int r;
assert(manager);
@@ -785,26 +877,32 @@ static void event_queue_start(Manager *manager) {
if (manager->last_usec == 0 ||
(usec - manager->last_usec) > 3 * USEC_PER_SEC) {
if (udev_rules_check_timestamp(manager->rules) ||
- udev_builtin_validate(manager->udev))
+ udev_builtin_validate())
manager_reload(manager);
manager->last_usec = usec;
}
- udev_builtin_init(manager->udev);
+ r = event_source_disable(manager->kill_workers_event);
+ if (r < 0)
+ log_warning_errno(r, "Failed to disable event source for cleaning up idle workers, ignoring: %m");
+
+ udev_builtin_init();
if (!manager->rules) {
- manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
- if (!manager->rules)
+ r = udev_rules_new(&manager->rules, arg_resolve_name_timing);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read udev rules: %m");
return;
+ }
}
- LIST_FOREACH(event,event,manager->events) {
+ LIST_FOREACH(event, event, manager->events) {
if (event->state != EVENT_QUEUED)
continue;
/* do not start event if parent or child event is still running */
- if (is_devpath_busy(manager, event))
+ if (is_device_busy(manager, event) != 0)
continue;
event_run(manager, event);
@@ -856,9 +954,9 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
/* nothing more to read */
break;
- return log_error_errno(errno, "failed to receive message: %m");
+ return log_error_errno(errno, "Failed to receive message: %m");
} else if (size != sizeof(struct worker_message)) {
- log_warning_errno(EIO, "ignoring worker message with invalid size %zi bytes", size);
+ log_warning("Ignoring worker message with invalid size %zi bytes", size);
continue;
}
@@ -870,14 +968,14 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
}
if (!ucred || ucred->pid <= 0) {
- log_warning_errno(EIO, "ignoring worker message without valid PID");
+ log_warning("Ignoring worker message without valid PID");
continue;
}
/* lookup worker who sent the signal */
worker = hashmap_get(manager->workers, PID_TO_PTR(ucred->pid));
if (!worker) {
- log_debug("worker ["PID_FMT"] returned, but is no longer tracked", ucred->pid);
+ log_debug("Worker ["PID_FMT"] returned, but is no longer tracked", ucred->pid);
continue;
}
@@ -894,24 +992,23 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
return 1;
}
-static int on_uevent(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
Manager *manager = userdata;
- struct udev_device *dev;
int r;
assert(manager);
- dev = udev_monitor_receive_device(manager->monitor);
- if (dev) {
- udev_device_ensure_usec_initialized(dev, NULL);
- r = event_queue_insert(manager, dev);
- if (r < 0)
- udev_device_unref(dev);
- else
- /* we have fresh events, try to schedule them */
- event_queue_start(manager);
+ device_ensure_usec_initialized(dev, NULL);
+
+ r = event_queue_insert(manager, dev);
+ if (r < 0) {
+ log_device_error_errno(dev, r, "Failed to insert device into event queue: %m");
+ return 1;
}
+ /* we have fresh events, try to schedule them */
+ event_queue_start(manager);
+
return 1;
}
@@ -921,7 +1018,7 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd
_cleanup_(udev_ctrl_connection_unrefp) struct udev_ctrl_connection *ctrl_conn = NULL;
_cleanup_(udev_ctrl_msg_unrefp) struct udev_ctrl_msg *ctrl_msg = NULL;
const char *str;
- int i;
+ int i, r;
assert(manager);
@@ -935,55 +1032,84 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd
i = udev_ctrl_get_set_log_level(ctrl_msg);
if (i >= 0) {
- log_debug("udevd message (SET_LOG_LEVEL) received, log_priority=%i", i);
+ log_debug("Received udev control message (SET_LOG_LEVEL), setting log_priority=%i", i);
log_set_max_level(i);
manager_kill_workers(manager);
}
if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) {
- log_debug("udevd message (STOP_EXEC_QUEUE) received");
+ log_debug("Received udev control message (STOP_EXEC_QUEUE)");
manager->stop_exec_queue = true;
}
if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) {
- log_debug("udevd message (START_EXEC_QUEUE) received");
+ log_debug("Received udev control message (START_EXEC_QUEUE)");
manager->stop_exec_queue = false;
event_queue_start(manager);
}
if (udev_ctrl_get_reload(ctrl_msg) > 0) {
- log_debug("udevd message (RELOAD) received");
+ log_debug("Received udev control message (RELOAD)");
manager_reload(manager);
}
str = udev_ctrl_get_set_env(ctrl_msg);
- if (str != NULL) {
- _cleanup_free_ char *key = NULL;
-
- key = strdup(str);
- if (key) {
- char *val;
-
- val = strchr(key, '=');
- if (val != NULL) {
- val[0] = '\0';
- val = &val[1];
- if (val[0] == '\0') {
- log_debug("udevd message (ENV) received, unset '%s'", key);
- udev_list_entry_add(&manager->properties, key, NULL);
- } else {
- log_debug("udevd message (ENV) received, set '%s=%s'", key, val);
- udev_list_entry_add(&manager->properties, key, val);
- }
- } else
- log_error("wrong key format '%s'", key);
+ if (str) {
+ _cleanup_free_ char *key = NULL, *val = NULL, *old_key = NULL, *old_val = NULL;
+ char *eq;
+
+ eq = strchr(str, '=');
+ if (!eq) {
+ log_error("Invalid key format '%s'", str);
+ return 1;
+ }
+
+ key = strndup(str, eq - str);
+ if (!key) {
+ log_oom();
+ return 1;
}
+
+ old_val = hashmap_remove2(manager->properties, key, (void **) &old_key);
+
+ r = hashmap_ensure_allocated(&manager->properties, &string_hash_ops);
+ if (r < 0) {
+ log_oom();
+ return 1;
+ }
+
+ eq++;
+ if (!isempty(eq)) {
+ log_debug("Received udev control message (ENV), unsetting '%s'", key);
+
+ r = hashmap_put(manager->properties, key, NULL);
+ if (r < 0) {
+ log_oom();
+ return 1;
+ }
+ } else {
+ val = strdup(eq);
+ if (!val) {
+ log_oom();
+ return 1;
+ }
+
+ log_debug("Received udev control message (ENV), setting '%s=%s'", key, val);
+
+ r = hashmap_put(manager->properties, key, val);
+ if (r < 0) {
+ log_oom();
+ return 1;
+ }
+ }
+
+ key = val = NULL;
manager_kill_workers(manager);
}
i = udev_ctrl_get_set_children_max(ctrl_msg);
if (i >= 0) {
- log_debug("udevd message (SET_MAX_CHILDREN) received, children_max=%i", i);
+ log_debug("Receivd udev control message (SET_MAX_CHILDREN), setting children_max=%i", i);
arg_children_max = i;
(void) sd_notifyf(false,
@@ -992,10 +1118,10 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd
}
if (udev_ctrl_get_ping(ctrl_msg) > 0)
- log_debug("udevd message (SYNC) received");
+ log_debug("Received udev control message (SYNC)");
if (udev_ctrl_get_exit(ctrl_msg) > 0) {
- log_debug("udevd message (EXIT) received");
+ log_debug("Received udev control message (EXIT)");
manager_exit(manager);
/* keep reference to block the client until we exit
TODO: deal with several blocking exit requests */
@@ -1005,19 +1131,38 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd
return 1;
}
-static int synthesize_change(struct udev_device *dev) {
- char filename[UTIL_PATH_SIZE];
+static int synthesize_change(sd_device *dev) {
+ const char *subsystem, *sysname, *devname, *syspath, *devtype;
+ char filename[PATH_MAX];
int r;
- if (streq_ptr("block", udev_device_get_subsystem(dev)) &&
- streq_ptr("disk", udev_device_get_devtype(dev)) &&
- !startswith(udev_device_get_sysname(dev), "dm-")) {
- bool part_table_read = false;
- bool has_partitions = false;
+ r = sd_device_get_subsystem(dev, &subsystem);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_sysname(dev, &sysname);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_devname(dev, &devname);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_syspath(dev, &syspath);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_devtype(dev, &devtype);
+ if (r < 0)
+ return r;
+
+ if (streq_ptr("block", subsystem) &&
+ streq_ptr("disk", devtype) &&
+ !startswith(sysname, "dm-")) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ bool part_table_read = false, has_partitions = false;
+ sd_device *d;
int fd;
- struct udev *udev = udev_device_get_udev(dev);
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
- struct udev_list_entry *item;
/*
* Try to re-read the partition table. This only succeeds if
@@ -1025,7 +1170,7 @@ static int synthesize_change(struct udev_device *dev) {
* partition table is found, and we will not get an event for
* the disk.
*/
- fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
+ fd = open(devname, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
if (fd >= 0) {
r = flock(fd, LOCK_EX|LOCK_NB);
if (r >= 0)
@@ -1037,30 +1182,27 @@ static int synthesize_change(struct udev_device *dev) {
}
/* search for partitions */
- e = udev_enumerate_new(udev);
- if (!e)
- return -ENOMEM;
-
- r = udev_enumerate_add_match_parent(e, dev);
+ r = sd_device_enumerator_new(&e);
if (r < 0)
return r;
- r = udev_enumerate_add_match_subsystem(e, "block");
+ r = sd_device_enumerator_allow_uninitialized(e);
if (r < 0)
return r;
- r = udev_enumerate_scan_devices(e);
+ r = sd_device_enumerator_add_match_parent(e, dev);
if (r < 0)
return r;
- udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+ r = sd_device_enumerator_add_match_subsystem(e, "block", true);
+ if (r < 0)
+ return r;
- d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!d)
- continue;
+ FOREACH_DEVICE(e, d) {
+ const char *t;
- if (!streq_ptr("partition", udev_device_get_devtype(d)))
+ if (sd_device_get_devtype(d, &t) < 0 ||
+ !streq("partition", t))
continue;
has_partitions = true;
@@ -1079,32 +1221,32 @@ static int synthesize_change(struct udev_device *dev) {
* We have partitions but re-reading the partition table did not
* work, synthesize "change" for the disk and all partitions.
*/
- log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev));
- strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
- write_string_file(filename, "change", WRITE_STRING_FILE_CREATE);
+ log_debug("Device '%s' is closed, synthesising 'change'", devname);
+ strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
+ write_string_file(filename, "change", WRITE_STRING_FILE_DISABLE_BUFFER);
- udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) {
- _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
+ FOREACH_DEVICE(e, d) {
+ const char *t, *n, *s;
- d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
- if (!d)
+ if (sd_device_get_devtype(d, &t) < 0 ||
+ !streq("partition", t))
continue;
- if (!streq_ptr("partition", udev_device_get_devtype(d)))
+ if (sd_device_get_devname(d, &n) < 0 ||
+ sd_device_get_syspath(d, &s) < 0)
continue;
- log_debug("device %s closed, synthesising partition '%s' 'change'",
- udev_device_get_devnode(dev), udev_device_get_devnode(d));
- strscpyl(filename, sizeof(filename), udev_device_get_syspath(d), "/uevent", NULL);
- write_string_file(filename, "change", WRITE_STRING_FILE_CREATE);
+ log_debug("Device '%s' is closed, synthesising partition '%s' 'change'", devname, n);
+ strscpyl(filename, sizeof(filename), s, "/uevent", NULL);
+ write_string_file(filename, "change", WRITE_STRING_FILE_DISABLE_BUFFER);
}
return 0;
}
- log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev));
- strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
- write_string_file(filename, "change", WRITE_STRING_FILE_CREATE);
+ log_debug("Device %s is closed, synthesising 'change'", devname);
+ strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
+ write_string_file(filename, "change", WRITE_STRING_FILE_DISABLE_BUFFER);
return 0;
}
@@ -1114,9 +1256,14 @@ static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userda
union inotify_event_buffer buffer;
struct inotify_event *e;
ssize_t l;
+ int r;
assert(manager);
+ r = event_source_disable(manager->kill_workers_event);
+ if (r < 0)
+ log_warning_errno(r, "Failed to disable event source for cleaning up idle workers, ignoring: %m");
+
l = read(fd, &buffer, sizeof(buffer));
if (l < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
@@ -1126,24 +1273,20 @@ static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userda
}
FOREACH_INOTIFY_EVENT(e, buffer, l) {
- _cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ const char *devnode;
- dev = udev_watch_lookup(manager->udev, e->wd);
- if (!dev)
+ if (udev_watch_lookup(e->wd, &dev) <= 0)
continue;
- log_debug("inotify event: %x for %s", e->mask, udev_device_get_devnode(dev));
- if (e->mask & IN_CLOSE_WRITE) {
- synthesize_change(dev);
+ if (sd_device_get_devname(dev, &devnode) < 0)
+ continue;
- /* settle might be waiting on us to determine the queue
- * state. If we just handled an inotify event, we might have
- * generated a "change" event, but we won't have queued up
- * the resultant uevent yet. Do that.
- */
- on_uevent(NULL, -1, 0, manager);
- } else if (e->mask & IN_IGNORED)
- udev_watch_end(manager->udev, dev);
+ log_device_debug(dev, "Inotify event: %x for %s", e->mask, devnode);
+ if (e->mask & IN_CLOSE_WRITE)
+ synthesize_change(dev);
+ else if (e->mask & IN_IGNORED)
+ udev_watch_end(dev);
}
return 1;
@@ -1171,6 +1314,7 @@ static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void
static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
Manager *manager = userdata;
+ int r;
assert(manager);
@@ -1185,35 +1329,37 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi
worker = hashmap_get(manager->workers, PID_TO_PTR(pid));
if (!worker) {
- log_warning("worker ["PID_FMT"] is unknown, ignoring", pid);
+ log_warning("Worker ["PID_FMT"] is unknown, ignoring", pid);
continue;
}
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 0)
- log_debug("worker ["PID_FMT"] exited", pid);
+ log_debug("Worker ["PID_FMT"] exited", pid);
else
- log_warning("worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status));
+ log_warning("Worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
- log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), signal_to_string(WTERMSIG(status)));
+ log_warning("Worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), signal_to_string(WTERMSIG(status)));
} else if (WIFSTOPPED(status)) {
- log_info("worker ["PID_FMT"] stopped", pid);
+ log_info("Worker ["PID_FMT"] stopped", pid);
continue;
} else if (WIFCONTINUED(status)) {
- log_info("worker ["PID_FMT"] continued", pid);
+ log_info("Worker ["PID_FMT"] continued", pid);
continue;
} else
- log_warning("worker ["PID_FMT"] exit with status 0x%04x", pid, status);
-
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- if (worker->event) {
- log_error("worker ["PID_FMT"] failed while handling '%s'", pid, worker->event->devpath);
- /* delete state from disk */
- udev_device_delete_db(worker->event->dev);
- udev_device_tag_index(worker->event->dev, NULL, false);
- /* forward kernel event without amending it */
- udev_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel);
- }
+ log_warning("Worker ["PID_FMT"] exit with status 0x%04x", pid, status);
+
+ if ((!WIFEXITED(status) || WEXITSTATUS(status) != 0) && worker->event) {
+ log_device_error(worker->event->dev, "Worker ["PID_FMT"] failed", pid);
+
+ /* delete state from disk */
+ device_delete_db(worker->event->dev);
+ device_tag_index(worker->event->dev, NULL, false);
+
+ /* forward kernel event without amending it */
+ r = device_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel);
+ if (r < 0)
+ log_device_error_errno(worker->event->dev_kernel, r, "Failed to send back device to kernel: %m");
}
worker_free(worker);
@@ -1222,57 +1368,66 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi
/* we can start new workers, try to schedule events */
event_queue_start(manager);
+ /* Disable unnecessary cleanup event */
+ if (hashmap_isempty(manager->workers)) {
+ r = event_source_disable(manager->kill_workers_event);
+ if (r < 0)
+ log_warning_errno(r, "Failed to disable event source for cleaning up idle workers, ignoring: %m");
+ }
+
return 1;
}
static int on_post(sd_event_source *s, void *userdata) {
Manager *manager = userdata;
- int r;
assert(manager);
- if (LIST_IS_EMPTY(manager->events)) {
- /* no pending events */
- if (!hashmap_isempty(manager->workers)) {
- /* there are idle workers */
- log_debug("cleanup idle workers");
- manager_kill_workers(manager);
- } else {
- /* we are idle */
- if (manager->exit) {
- r = sd_event_exit(manager->event, 0);
- if (r < 0)
- return r;
- } else if (manager->cgroup)
- /* cleanup possible left-over processes in our cgroup */
- cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, NULL, NULL, NULL);
- }
+ if (!LIST_IS_EMPTY(manager->events))
+ return 1;
+
+ /* There are no pending events. Let's cleanup idle process. */
+
+ if (!hashmap_isempty(manager->workers)) {
+ /* There are idle workers */
+ (void) event_reset_time(manager->event, &manager->kill_workers_event, CLOCK_MONOTONIC,
+ now(CLOCK_MONOTONIC) + 3 * USEC_PER_SEC, USEC_PER_SEC,
+ on_kill_workers_event, manager, 0, "kill-workers-event", false);
+ return 1;
}
+ /* There are no idle workers. */
+
+ if (manager->exit)
+ return sd_event_exit(manager->event, 0);
+
+ if (manager->cgroup)
+ /* cleanup possible left-over processes in our cgroup */
+ (void) cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, NULL, NULL, NULL);
+
return 1;
}
-static int listen_fds(int *rctrl, int *rnetlink) {
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
+static int listen_fds(int *ret_ctrl, int *ret_netlink) {
int ctrl_fd = -1, netlink_fd = -1;
- int fd, n, r;
+ int fd, n;
- assert(rctrl);
- assert(rnetlink);
+ assert(ret_ctrl);
+ assert(ret_netlink);
n = sd_listen_fds(true);
if (n < 0)
return n;
for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) {
- if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) {
+ if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1) > 0) {
if (ctrl_fd >= 0)
return -EINVAL;
ctrl_fd = fd;
continue;
}
- if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) {
+ if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) {
if (netlink_fd >= 0)
return -EINVAL;
netlink_fd = fd;
@@ -1282,60 +1437,8 @@ static int listen_fds(int *rctrl, int *rnetlink) {
return -EINVAL;
}
- if (ctrl_fd < 0) {
- _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *ctrl = NULL;
-
- udev = udev_new();
- if (!udev)
- return -ENOMEM;
-
- ctrl = udev_ctrl_new(udev);
- if (!ctrl)
- return log_error_errno(EINVAL, "error initializing udev control socket");
-
- r = udev_ctrl_enable_receiving(ctrl);
- if (r < 0)
- return log_error_errno(EINVAL, "error binding udev control socket");
-
- fd = udev_ctrl_get_fd(ctrl);
- if (fd < 0)
- return log_error_errno(EIO, "could not get ctrl fd");
-
- ctrl_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (ctrl_fd < 0)
- return log_error_errno(errno, "could not dup ctrl fd: %m");
- }
-
- if (netlink_fd < 0) {
- _cleanup_(udev_monitor_unrefp) struct udev_monitor *monitor = NULL;
-
- if (!udev) {
- udev = udev_new();
- if (!udev)
- return -ENOMEM;
- }
-
- monitor = udev_monitor_new_from_netlink(udev, "kernel");
- if (!monitor)
- return log_error_errno(EINVAL, "error initializing netlink socket");
-
- (void) udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024);
-
- r = udev_monitor_enable_receiving(monitor);
- if (r < 0)
- return log_error_errno(EINVAL, "error binding netlink socket");
-
- fd = udev_monitor_get_fd(monitor);
- if (fd < 0)
- return log_error_errno(netlink_fd, "could not get uevent fd: %m");
-
- netlink_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- if (netlink_fd < 0)
- return log_error_errno(errno, "could not dup netlink fd: %m");
- }
-
- *rctrl = ctrl_fd;
- *rnetlink = netlink_fd;
+ *ret_ctrl = ctrl_fd;
+ *ret_netlink = netlink_fd;
return 0;
}
@@ -1360,7 +1463,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (proc_cmdline_value_missing(key, value))
return 0;
- r = util_log_priority(value);
+ r = log_level_from_string(value);
if (r >= 0)
log_set_max_level(r);
@@ -1369,11 +1472,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (proc_cmdline_value_missing(key, value))
return 0;
- r = safe_atou64(value, &arg_event_timeout_usec);
- if (r >= 0) {
- arg_event_timeout_usec *= USEC_PER_SEC;
- arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
- }
+ r = parse_sec(value, &arg_event_timeout_usec);
} else if (proc_cmdline_key_streq(key, "udev.children_max")) {
@@ -1387,10 +1486,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (proc_cmdline_value_missing(key, value))
return 0;
- r = safe_atoi(value, &arg_exec_delay);
+ r = parse_sec(value, &arg_exec_delay_usec);
} else if (startswith(key, "udev."))
- log_warning("Unknown udev kernel command line option \"%s\"", key);
+ log_warning("Unknown udev kernel command line option \"%s\", ignoring", key);
if (r < 0)
log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value);
@@ -1398,7 +1497,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-udevd.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"Manages devices.\n\n"
" -h --help Print this message\n"
@@ -1410,7 +1516,12 @@ static void help(void) {
" -t --event-timeout=SECONDS Seconds to wait before terminating an event\n"
" -N --resolve-names=early|late|never\n"
" When to resolve users and groups\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -1426,14 +1537,12 @@ static int parse_argv(int argc, char *argv[]) {
{}
};
- int c;
+ int c, r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "c:de:Dt:N:hV", options, NULL)) >= 0) {
- int r;
-
switch (c) {
case 'd':
@@ -1442,40 +1551,33 @@ static int parse_argv(int argc, char *argv[]) {
case 'c':
r = safe_atou(optarg, &arg_children_max);
if (r < 0)
- log_warning("Invalid --children-max ignored: %s", optarg);
+ log_warning_errno(r, "Failed to parse --children-max= value '%s', ignoring: %m", optarg);
break;
case 'e':
- r = safe_atoi(optarg, &arg_exec_delay);
+ r = parse_sec(optarg, &arg_exec_delay_usec);
if (r < 0)
- log_warning("Invalid --exec-delay ignored: %s", optarg);
+ log_warning_errno(r, "Failed to parse --exec-delay= value '%s', ignoring: %m", optarg);
break;
case 't':
- r = safe_atou64(optarg, &arg_event_timeout_usec);
+ r = parse_sec(optarg, &arg_event_timeout_usec);
if (r < 0)
- log_warning("Invalid --event-timeout ignored: %s", optarg);
- else {
- arg_event_timeout_usec *= USEC_PER_SEC;
- arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
- }
+ log_warning_errno(r, "Failed to parse --event-timeout= value '%s', ignoring: %m", optarg);
break;
case 'D':
arg_debug = true;
break;
- case 'N':
- if (streq(optarg, "early")) {
- arg_resolve_names = 1;
- } else if (streq(optarg, "late")) {
- arg_resolve_names = 0;
- } else if (streq(optarg, "never")) {
- arg_resolve_names = -1;
- } else {
- log_error("resolve-names must be early, late or never");
- return 0;
- }
+ case 'N': {
+ ResolveNameTiming t;
+
+ t = resolve_name_timing_from_string(optarg);
+ if (t < 0)
+ log_warning("Invalid --resolve-names= value '%s', ignoring.", optarg);
+ else
+ arg_resolve_name_timing = t;
break;
+ }
case 'h':
- help();
- return 0;
+ return help();
case 'V':
printf("%s\n", PACKAGE_VERSION);
return 0;
@@ -1492,90 +1594,94 @@ static int parse_argv(int argc, char *argv[]) {
static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cgroup) {
_cleanup_(manager_freep) Manager *manager = NULL;
- int r, fd_worker, one = 1;
+ int r, fd_worker;
assert(ret);
- assert(fd_ctrl >= 0);
- assert(fd_uevent >= 0);
- manager = new0(Manager, 1);
+ manager = new(Manager, 1);
if (!manager)
return log_oom();
- manager->fd_inotify = -1;
- manager->worker_watch[WRITE_END] = -1;
- manager->worker_watch[READ_END] = -1;
-
- manager->udev = udev_new();
- if (!manager->udev)
- return log_error_errno(errno, "could not allocate udev context: %m");
+ *manager = (Manager) {
+ .fd_inotify = -1,
+ .worker_watch = { -1, -1 },
+ .cgroup = cgroup,
+ };
- udev_builtin_init(manager->udev);
+ udev_builtin_init();
- manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
+ r = udev_rules_new(&manager->rules, arg_resolve_name_timing);
if (!manager->rules)
- return log_error_errno(ENOMEM, "error reading rules");
+ return log_error_errno(r, "Failed to read udev rules: %m");
- LIST_HEAD_INIT(manager->events);
- udev_list_init(manager->udev, &manager->properties, true);
+ manager->ctrl = udev_ctrl_new_from_fd(fd_ctrl);
+ if (!manager->ctrl)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize udev control socket");
- manager->cgroup = cgroup;
+ if (fd_ctrl < 0) {
+ r = udev_ctrl_enable_receiving(manager->ctrl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind udev control socket: %m");
+ }
- manager->ctrl = udev_ctrl_new_from_fd(manager->udev, fd_ctrl);
- if (!manager->ctrl)
- return log_error_errno(EINVAL, "error taking over udev control socket");
+ fd_ctrl = udev_ctrl_get_fd(manager->ctrl);
+ if (fd_ctrl < 0)
+ return log_error_errno(fd_ctrl, "Failed to get udev control socket fd: %m");
- manager->monitor = udev_monitor_new_from_netlink_fd(manager->udev, "kernel", fd_uevent);
- if (!manager->monitor)
- return log_error_errno(EINVAL, "error taking over netlink socket");
+ r = device_monitor_new_full(&manager->monitor, MONITOR_GROUP_KERNEL, fd_uevent);
+ 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);
/* unnamed socket from workers to the main daemon */
r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch);
if (r < 0)
- return log_error_errno(errno, "error creating socketpair: %m");
+ return log_error_errno(errno, "Failed to create socketpair for communicating with workers: %m");
fd_worker = manager->worker_watch[READ_END];
- r = setsockopt(fd_worker, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ r = setsockopt_int(fd_worker, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- return log_error_errno(errno, "could not enable SO_PASSCRED: %m");
+ return log_error_errno(r, "Failed to enable SO_PASSCRED: %m");
- manager->fd_inotify = udev_watch_init(manager->udev);
- if (manager->fd_inotify < 0)
- return log_error_errno(ENOMEM, "error initializing inotify");
+ r = udev_watch_init();
+ if (r < 0)
+ return log_error_errno(r, "Failed to create inotify descriptor: %m");
+ manager->fd_inotify = r;
- udev_watch_restore(manager->udev);
+ udev_watch_restore();
/* block and listen to all signals on signalfd */
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0);
r = sd_event_default(&manager->event);
if (r < 0)
- return log_error_errno(r, "could not allocate event loop: %m");
+ return log_error_errno(r, "Failed to allocate event loop: %m");
r = sd_event_add_signal(manager->event, NULL, SIGINT, on_sigterm, manager);
if (r < 0)
- return log_error_errno(r, "error creating sigint event source: %m");
+ return log_error_errno(r, "Failed to create SIGINT event source: %m");
r = sd_event_add_signal(manager->event, NULL, SIGTERM, on_sigterm, manager);
if (r < 0)
- return log_error_errno(r, "error creating sigterm event source: %m");
+ return log_error_errno(r, "Failed to create SIGTERM event source: %m");
r = sd_event_add_signal(manager->event, NULL, SIGHUP, on_sighup, manager);
if (r < 0)
- return log_error_errno(r, "error creating sighup event source: %m");
+ return log_error_errno(r, "Failed to create SIGHUP event source: %m");
r = sd_event_add_signal(manager->event, NULL, SIGCHLD, on_sigchld, manager);
if (r < 0)
- return log_error_errno(r, "error creating sigchld event source: %m");
+ return log_error_errno(r, "Failed to create SIGCHLD event source: %m");
r = sd_event_set_watchdog(manager->event, true);
if (r < 0)
- return log_error_errno(r, "error creating watchdog event source: %m");
+ return log_error_errno(r, "Failed to create watchdog event source: %m");
r = sd_event_add_io(manager->event, &manager->ctrl_event, fd_ctrl, EPOLLIN, on_ctrl_msg, manager);
if (r < 0)
- return log_error_errno(r, "error creating ctrl event source: %m");
+ return log_error_errno(r, "Failed to create udev control event source: %m");
/* This needs to be after the inotify and uevent handling, to make sure
* that the ping is send back after fully processing the pending uevents
@@ -1583,42 +1689,48 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg
*/
r = sd_event_source_set_priority(manager->ctrl_event, SD_EVENT_PRIORITY_IDLE);
if (r < 0)
- return log_error_errno(r, "cold not set IDLE event priority for ctrl event source: %m");
+ return log_error_errno(r, "Failed to set IDLE event priority for udev control event source: %m");
r = sd_event_add_io(manager->event, &manager->inotify_event, manager->fd_inotify, EPOLLIN, on_inotify, manager);
if (r < 0)
- return log_error_errno(r, "error creating inotify event source: %m");
+ return log_error_errno(r, "Failed to create inotify event source: %m");
- r = sd_event_add_io(manager->event, &manager->uevent_event, fd_uevent, EPOLLIN, on_uevent, manager);
+ r = sd_device_monitor_attach_event(manager->monitor, manager->event);
if (r < 0)
- return log_error_errno(r, "error creating uevent event source: %m");
+ return log_error_errno(r, "Failed to attach event to device monitor: %m");
+
+ r = sd_device_monitor_start(manager->monitor, on_uevent, manager);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start device monitor: %m");
+
+ (void) sd_event_source_set_description(sd_device_monitor_get_event_source(manager->monitor), "device-monitor");
r = sd_event_add_io(manager->event, NULL, fd_worker, EPOLLIN, on_worker, manager);
if (r < 0)
- return log_error_errno(r, "error creating worker event source: %m");
+ return log_error_errno(r, "Failed to create worker event source: %m");
r = sd_event_add_post(manager->event, NULL, on_post, manager);
if (r < 0)
- return log_error_errno(r, "error creating post event source: %m");
+ return log_error_errno(r, "Failed to create post event source: %m");
*ret = TAKE_PTR(manager);
return 0;
}
-static int run(int fd_ctrl, int fd_uevent, const char *cgroup) {
+static int main_loop(int fd_ctrl, int fd_uevent, const char *cgroup) {
_cleanup_(manager_freep) Manager *manager = NULL;
int r;
r = manager_new(&manager, fd_ctrl, fd_uevent, cgroup);
if (r < 0) {
- r = log_error_errno(r, "failed to allocate manager object: %m");
+ r = log_error_errno(r, "Failed to allocate manager object: %m");
goto exit;
}
r = udev_rules_apply_static_dev_perms(manager->rules);
if (r < 0)
- log_error_errno(r, "failed to apply permissions on static device nodes: %m");
+ log_error_errno(r, "Failed to apply permissions on static device nodes: %m");
(void) sd_notifyf(false,
"READY=1\n"
@@ -1626,7 +1738,7 @@ static int run(int fd_ctrl, int fd_uevent, const char *cgroup) {
r = sd_event_loop(manager->event);
if (r < 0) {
- log_error_errno(r, "event loop failed: %m");
+ log_error_errno(r, "Event loop failed: %m");
goto exit;
}
@@ -1641,32 +1753,34 @@ exit:
return r;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_free_ char *cgroup = NULL;
int fd_ctrl = -1, fd_uevent = -1;
int r;
log_set_target(LOG_TARGET_AUTO);
- udev_parse_config();
+ udev_parse_config_full(&arg_children_max, &arg_exec_delay_usec, &arg_event_timeout_usec, &arg_resolve_name_timing);
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
- goto exit;
+ return r;
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
- log_warning_errno(r, "failed to parse kernel command line, ignoring: %m");
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
if (arg_debug) {
log_set_target(LOG_TARGET_CONSOLE);
log_set_max_level(LOG_DEBUG);
}
+ log_set_max_level_realm(LOG_REALM_SYSTEMD, log_get_max_level());
+
r = must_be_root();
if (r < 0)
- goto exit;
+ return r;
if (arg_children_max == 0) {
cpu_set_t cpu_set;
@@ -1675,34 +1789,28 @@ int main(int argc, char *argv[]) {
arg_children_max = 8;
if (sched_getaffinity(0, sizeof(cpu_set), &cpu_set) == 0)
- arg_children_max += CPU_COUNT(&cpu_set) * 2;
+ arg_children_max += CPU_COUNT(&cpu_set) * 8;
mem_limit = physical_memory() / (128LU*1024*1024);
arg_children_max = MAX(10U, MIN(arg_children_max, mem_limit));
- log_debug("set children_max to %u", arg_children_max);
+ log_debug("Set children_max to %u", arg_children_max);
}
/* set umask before creating any file/directory */
r = chdir("/");
- if (r < 0) {
- r = log_error_errno(errno, "could not change dir to /: %m");
- goto exit;
- }
+ if (r < 0)
+ return log_error_errno(errno, "Failed to change dir to '/': %m");
umask(022);
r = mac_selinux_init();
- if (r < 0) {
- log_error_errno(r, "could not initialize labelling: %m");
- goto exit;
- }
+ if (r < 0)
+ return log_error_errno(r, "Could not initialize labelling: %m");
r = mkdir_errno_wrapper("/run/udev", 0755);
- if (r < 0 && r != -EEXIST) {
- log_error_errno(r, "could not create /run/udev: %m");
- goto exit;
- }
+ if (r < 0 && r != -EEXIST)
+ return log_error_errno(r, "Failed to create /run/udev: %m");
dev_setup(NULL, UID_INVALID, GID_INVALID);
@@ -1713,17 +1821,15 @@ int main(int argc, char *argv[]) {
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
if (r < 0) {
if (IN_SET(r, -ENOENT, -ENOMEDIUM))
- log_debug_errno(r, "did not find dedicated cgroup: %m");
+ log_debug_errno(r, "Dedicated cgroup not found: %m");
else
- log_warning_errno(r, "failed to get cgroup: %m");
+ log_warning_errno(r, "Failed to get cgroup: %m");
}
}
r = listen_fds(&fd_ctrl, &fd_uevent);
- if (r < 0) {
- r = log_error_errno(r, "could not listen on fds: %m");
- goto exit;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to listen on fds: %m");
if (arg_daemonize) {
pid_t pid;
@@ -1738,27 +1844,21 @@ int main(int argc, char *argv[]) {
}
pid = fork();
- switch (pid) {
- case 0:
- break;
- case -1:
- r = log_error_errno(errno, "fork of daemon failed: %m");
- goto exit;
- default:
- mac_selinux_finish();
- log_close();
- _exit(EXIT_SUCCESS);
- }
+ if (pid < 0)
+ return log_error_errno(errno, "Failed to fork daemon: %m");
+ if (pid > 0)
+ /* parent */
+ return 0;
- setsid();
+ /* child */
+ (void) setsid();
- write_string_file("/proc/self/oom_score_adj", "-1000", 0);
+ r = set_oom_score_adjust(-1000);
+ if (r < 0)
+ log_debug_errno(r, "Failed to adjust OOM score, ignoring: %m");
}
- r = run(fd_ctrl, fd_uevent, cgroup);
-
-exit:
- mac_selinux_finish();
- log_close();
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return main_loop(fd_ctrl, fd_uevent, cgroup);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c
index 22faf19fa3..c76c2d1f51 100644
--- a/src/update-done/update-done.c
+++ b/src/update-done/update-done.c
@@ -37,9 +37,7 @@ int main(int argc, char *argv[]) {
struct stat st;
int r, q = 0;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
if (stat("/usr", &st) < 0) {
log_error_errno(errno, "Failed to stat /usr: %m");
@@ -49,12 +47,11 @@ int main(int argc, char *argv[]) {
r = mac_selinux_init();
if (r < 0) {
log_error_errno(r, "SELinux setup failed: %m");
- goto finish;
+ return EXIT_FAILURE;
}
r = apply_timestamp("/etc/.updated", &st.st_mtim);
q = apply_timestamp("/var/.updated", &st.st_mtim);
-finish:
return r < 0 || q < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c
index 70ed723a8e..7d66ed3357 100644
--- a/src/update-utmp/update-utmp.c
+++ b/src/update-utmp/update-utmp.c
@@ -30,6 +30,17 @@ typedef struct Context {
#endif
} Context;
+static void context_clear(Context *c) {
+ assert(c);
+
+ c->bus = sd_bus_flush_close_unref(c->bus);
+#if HAVE_AUDIT
+ if (c->audit_fd >= 0)
+ audit_close(c->audit_fd);
+ c->audit_fd = -1;
+#endif
+}
+
static usec_t get_startup_time(Context *c) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
usec_t t = 0;
@@ -205,7 +216,7 @@ static int on_runlevel(Context *c) {
}
int main(int argc, char *argv[]) {
- Context c = {
+ _cleanup_(context_clear) Context c = {
#if HAVE_AUDIT
.audit_fd = -1
#endif
@@ -222,9 +233,7 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
@@ -238,8 +247,7 @@ int main(int argc, char *argv[]) {
r = bus_connect_system_systemd(&c.bus);
if (r < 0) {
log_error_errno(r, "Failed to get D-Bus connection: %m");
- r = -EIO;
- goto finish;
+ return EXIT_FAILURE;
}
log_debug("systemd-update-utmp running as pid "PID_FMT, getpid_cached());
@@ -252,17 +260,10 @@ int main(int argc, char *argv[]) {
r = on_runlevel(&c);
else {
log_error("Unknown command %s", argv[1]);
- r = -EINVAL;
+ return EXIT_FAILURE;
}
log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid_cached());
-finish:
-#if HAVE_AUDIT
- if (c.audit_fd >= 0)
- audit_close(c.audit_fd);
-#endif
-
- sd_bus_flush_close_unref(c.bus);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/user-sessions/user-sessions.c b/src/user-sessions/user-sessions.c
index 89c4b0005d..490d9f01a9 100644
--- a/src/user-sessions/user-sessions.c
+++ b/src/user-sessions/user-sessions.c
@@ -6,22 +6,20 @@
#include "fileio.h"
#include "fileio-label.h"
#include "fs-util.h"
+#include "main-func.h"
#include "log.h"
#include "selinux-util.h"
#include "string-util.h"
#include "util.h"
-int main(int argc, char*argv[]) {
+static int run(int argc, char *argv[]) {
int r, k;
- if (argc != 2) {
- log_error("This program requires one argument.");
- return EXIT_FAILURE;
- }
+ if (argc != 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "This program requires one argument.");
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
@@ -30,16 +28,14 @@ int main(int argc, char*argv[]) {
if (streq(argv[1], "start")) {
r = unlink_or_warn("/run/nologin");
k = unlink_or_warn("/etc/nologin");
- if (k < 0 && r >= 0)
- r = k;
+ if (r < 0)
+ return r;
+ return k;
} else if (streq(argv[1], "stop"))
- r = create_shutdown_run_nologin_or_warn();
- else {
- log_error("Unknown verb '%s'.", argv[1]);
- r = -EINVAL;
- }
-
- mac_selinux_finish();
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return create_shutdown_run_nologin_or_warn();
+
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb '%s'.", argv[1]);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/vconsole/90-vconsole.rules.in b/src/vconsole/90-vconsole.rules.in
index 11137163ed..70679b6dd2 100644
--- a/src/vconsole/90-vconsole.rules.in
+++ b/src/vconsole/90-vconsole.rules.in
@@ -9,4 +9,4 @@
# Each vtcon keeps its own state of fonts.
#
-ACTION=="add", SUBSYSTEM=="vtconsole", KERNEL=="vtcon*", ATTR{name}!="*dummy device", RUN+="@rootlibexecdir@/systemd-vconsole-setup"
+ACTION=="add", SUBSYSTEM=="vtconsole", KERNEL=="vtcon*", RUN+="@rootlibexecdir@/systemd-vconsole-setup"
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
index f162d29220..ebdeba3e8e 100644
--- a/src/vconsole/vconsole-setup.c
+++ b/src/vconsole/vconsole-setup.c
@@ -18,11 +18,13 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
#include "io-util.h"
#include "locale-util.h"
#include "log.h"
+#include "proc-cmdline.h"
#include "process-util.h"
#include "signal-util.h"
#include "stdio-util.h"
@@ -112,7 +114,7 @@ static int toggle_utf8(const char *name, int fd, bool utf8) {
static int toggle_utf8_sysfs(bool utf8) {
int r;
- r = write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8), 0);
+ r = write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8), WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return log_warning_errno(r, "Failed to %s sysfs UTF-8 flag: %m", enable_disable(utf8));
@@ -148,7 +150,7 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m
log_debug("Executing \"%s\"...", strnull(cmd));
}
- r = safe_fork("(loadkeys)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ r = safe_fork("(loadkeys)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {
@@ -191,7 +193,7 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map,
log_debug("Executing \"%s\"...", strnull(cmd));
}
- r = safe_fork("(setfont)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ r = safe_fork("(setfont)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {
@@ -296,14 +298,28 @@ static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) {
r = ioctl(fd_d, KDFONTOP, &cfo);
if (r < 0) {
- log_warning_errno(errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i);
+ int last_errno, mode;
+
+ /* The fonts couldn't have been copied. It might be due to the
+ * terminal being in graphical mode. In this case the kernel
+ * returns -EINVAL which is too generic for distinguishing this
+ * specific case. So we need to retrieve the terminal mode and if
+ * the graphical mode is in used, let's assume that something else
+ * is using the terminal and the failure was expected as we
+ * shouldn't have tried to copy the fonts. */
+
+ last_errno = errno;
+ if (ioctl(fd_d, KDGETMODE, &mode) >= 0 && mode != KD_TEXT)
+ log_debug("KD_FONT_OP_SET skipped: tty%u is not in text mode", i);
+ else
+ log_warning_errno(last_errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i);
+
continue;
}
/*
- * copy unicode translation table
- * unimapd is a ushort count and a pointer to an
- * array of struct unipair { ushort, ushort }
+ * copy unicode translation table unimapd is a ushort count and a pointer
+ * to an array of struct unipair { ushort, ushort }
*/
r = ioctl(fd_d, PIO_UNIMAPCLR, &adv);
if (r < 0) {
@@ -402,9 +418,7 @@ int main(int argc, char **argv) {
unsigned idx = 0;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
@@ -417,32 +431,29 @@ int main(int argc, char **argv) {
utf8 = is_locale_utf8();
- r = parse_env_file(NULL, "/etc/vconsole.conf", NEWLINE,
+ r = parse_env_file(NULL, "/etc/vconsole.conf",
"KEYMAP", &vc_keymap,
"KEYMAP_TOGGLE", &vc_keymap_toggle,
"FONT", &vc_font,
"FONT_MAP", &vc_font_map,
- "FONT_UNIMAP", &vc_font_unimap,
- NULL);
+ "FONT_UNIMAP", &vc_font_unimap);
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
/* Let the kernel command line override /etc/vconsole.conf */
- if (detect_container() <= 0) {
- r = parse_env_file(NULL, "/proc/cmdline", WHITESPACE,
- "vconsole.keymap", &vc_keymap,
- "vconsole.keymap_toggle", &vc_keymap_toggle,
- "vconsole.font", &vc_font,
- "vconsole.font_map", &vc_font_map,
- "vconsole.font_unimap", &vc_font_unimap,
- /* compatibility with obsolete multiple-dot scheme */
- "vconsole.keymap.toggle", &vc_keymap_toggle,
- "vconsole.font.map", &vc_font_map,
- "vconsole.font.unimap", &vc_font_unimap,
- NULL);
- if (r < 0 && r != -ENOENT)
- log_warning_errno(r, "Failed to read /proc/cmdline: %m");
- }
+ r = proc_cmdline_get_key_many(
+ PROC_CMDLINE_STRIP_RD_PREFIX,
+ "vconsole.keymap", &vc_keymap,
+ "vconsole.keymap_toggle", &vc_keymap_toggle,
+ "vconsole.font", &vc_font,
+ "vconsole.font_map", &vc_font_map,
+ "vconsole.font_unimap", &vc_font_unimap,
+ /* compatibility with obsolete multiple-dot scheme */
+ "vconsole.keymap.toggle", &vc_keymap_toggle,
+ "vconsole.font.map", &vc_font_map,
+ "vconsole.font.unimap", &vc_font_unimap);
+ if (r < 0 && r != -ENOENT)
+ log_warning_errno(r, "Failed to read /proc/cmdline: %m");
(void) toggle_utf8_sysfs(utf8);
(void) toggle_utf8(vc, fd, utf8);
diff --git a/src/veritysetup/veritysetup-generator.c b/src/veritysetup/veritysetup-generator.c
index 0c63c5eaad..65a4e7b0fd 100644
--- a/src/veritysetup/veritysetup-generator.c
+++ b/src/veritysetup/veritysetup-generator.c
@@ -13,6 +13,7 @@
#include "generator.h"
#include "hexdecoct.h"
#include "id128-util.h"
+#include "main-func.h"
#include "mkdir.h"
#include "parse-util.h"
#include "proc-cmdline.h"
@@ -22,12 +23,16 @@
#define SYSTEMD_VERITYSETUP_SERVICE "systemd-veritysetup@root.service"
-static char *arg_dest = NULL;
+static const char *arg_dest = NULL;
static bool arg_enabled = true;
static char *arg_root_hash = NULL;
static char *arg_data_what = NULL;
static char *arg_hash_what = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_data_what, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_hash_what, freep);
+
static int create_device(void) {
_cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL, *u_escaped = NULL, *v_escaped = NULL, *root_hash_escaped = NULL;
_cleanup_fclose_ FILE *f = NULL;
@@ -118,7 +123,7 @@ static int create_device(void) {
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
- if (streq(key, "systemd.verity")) {
+ if (proc_cmdline_key_streq(key, "systemd.verity")) {
r = value ? parse_boolean(value) : 1;
if (r < 0)
@@ -126,7 +131,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
else
arg_enabled = r;
- } else if (streq(key, "roothash")) {
+ } else if (proc_cmdline_key_streq(key, "roothash")) {
if (proc_cmdline_value_missing(key, value))
return 0;
@@ -135,7 +140,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
return log_oom();
- } else if (streq(key, "systemd.verity_root_data")) {
+ } else if (proc_cmdline_key_streq(key, "systemd.verity_root_data")) {
if (proc_cmdline_value_missing(key, value))
return 0;
@@ -144,7 +149,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (r < 0)
return log_oom();
- } else if (streq(key, "systemd.verity_root_hash")) {
+ } else if (proc_cmdline_key_streq(key, "systemd.verity_root_hash")) {
if (proc_cmdline_value_missing(key, value))
return 0;
@@ -199,52 +204,26 @@ static int determine_devices(void) {
return 1;
}
-int main(int argc, char *argv[]) {
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
int r;
- if (argc > 1 && argc != 4) {
- log_error("This program takes three or no arguments.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1)
- arg_dest = argv[1];
-
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
+ assert_se(arg_dest = dest);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
- if (r < 0) {
- log_warning_errno(r, "Failed to parse kernel command line: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse kernel command line: %m");
/* For now we only support the root device on verity. Later on we might want to add support for /etc/veritytab
* or similar to define additional mappings */
- if (!arg_enabled) {
- r = 0;
- goto finish;
- }
+ if (!arg_enabled)
+ return 0;
r = determine_devices();
if (r < 0)
- goto finish;
-
- r = create_device();
- if (r < 0)
- goto finish;
-
- r = 0;
-
-finish:
- free(arg_root_hash);
- free(arg_data_what);
- free(arg_hash_what);
+ return r;
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return create_device();
}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c
index 795af77aa6..9c2fe9a1b4 100644
--- a/src/veritysetup/veritysetup.c
+++ b/src/veritysetup/veritysetup.c
@@ -8,40 +8,50 @@
#include "crypt-util.h"
#include "hexdecoct.h"
#include "log.h"
+#include "main-func.h"
+#include "pretty-print.h"
#include "string-util.h"
+#include "terminal-util.h"
static char *arg_root_hash = NULL;
static char *arg_data_what = NULL;
static char *arg_hash_what = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_data_what, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_hash_what, freep);
+
static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-veritysetup@.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH\n"
"%s detach VOLUME\n\n"
- "Attaches or detaches an integrity protected block device.\n",
- program_invocation_short_name,
- program_invocation_short_name);
+ "Attaches or detaches an integrity protected block device.\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
int r;
- if (argc <= 1) {
- r = help();
- goto finish;
- }
+ if (argc <= 1)
+ return help();
- if (argc < 3) {
- log_error("This program requires at least two arguments.");
- r = -EINVAL;
- goto finish;
- }
+ if (argc < 3)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires at least two arguments.");
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
umask(0022);
@@ -50,82 +60,57 @@ int main(int argc, char *argv[]) {
crypt_status_info status;
size_t l;
- if (argc < 6) {
- log_error("attach requires at least two arguments.");
- r = -EINVAL;
- goto finish;
- }
+ if (argc < 6)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least two arguments.");
r = unhexmem(argv[5], strlen(argv[5]), &m, &l);
- if (r < 0) {
- log_error("Failed to parse root hash.");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse root hash: %m");
r = crypt_init(&cd, argv[4]);
- if (r < 0) {
- log_error_errno(r, "Failed to open verity device %s: %m", argv[4]);
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to open verity device %s: %m", argv[4]);
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
status = crypt_status(cd, argv[2]);
if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) {
log_info("Volume %s already active.", argv[2]);
- r = 0;
- goto finish;
+ return 0;
}
r = crypt_load(cd, CRYPT_VERITY, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to load verity superblock: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to load verity superblock: %m");
r = crypt_set_data_device(cd, argv[3]);
- if (r < 0) {
- log_error_errno(r, "Failed to configure data device: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to configure data device: %m");
r = crypt_activate_by_volume_key(cd, argv[2], m, l, CRYPT_ACTIVATE_READONLY);
- if (r < 0) {
- log_error_errno(r, "Failed to set up verity device: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up verity device: %m");
} else if (streq(argv[1], "detach")) {
r = crypt_init_by_name(&cd, argv[2]);
if (r == -ENODEV) {
log_info("Volume %s already inactive.", argv[2]);
- goto finish;
- } else if (r < 0) {
- log_error_errno(r, "crypt_init_by_name() failed: %m");
- goto finish;
+ return 0;
}
+ if (r < 0)
+ return log_error_errno(r, "crypt_init_by_name() failed: %m");
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
r = crypt_deactivate(cd, argv[2]);
- if (r < 0) {
- log_error_errno(r, "Failed to deactivate: %m");
- goto finish;
- }
-
- } else {
- log_error("Unknown verb %s.", argv[1]);
- r = -EINVAL;
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to deactivate: %m");
- r = 0;
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", argv[1]);
-finish:
- free(arg_root_hash);
- free(arg_data_what);
- free(arg_hash_what);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return 0;
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/volatile-root/volatile-root.c b/src/volatile-root/volatile-root.c
index bc786c9734..5da9ce1681 100644
--- a/src/volatile-root/volatile-root.c
+++ b/src/volatile-root/volatile-root.c
@@ -4,12 +4,14 @@
#include "alloc-util.h"
#include "fs-util.h"
+#include "main-func.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "path-util.h"
#include "stat-util.h"
-#include "volatile-util.h"
#include "string-util.h"
-#include "path-util.h"
+#include "volatile-util.h"
static int make_volatile(const char *path) {
_cleanup_free_ char *old_usr = NULL;
@@ -18,10 +20,9 @@ static int make_volatile(const char *path) {
r = path_is_mount_point(path, NULL, AT_SYMLINK_FOLLOW);
if (r < 0)
return log_error_errno(r, "Couldn't determine whether %s is a mount point: %m", path);
- if (r == 0) {
- log_error("%s is not a mount point.", path);
- return -EINVAL;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s is not a mount point.", path);
r = path_is_temporary_fs(path);
if (r < 0)
@@ -76,33 +77,26 @@ finish_rmdir:
return r;
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
VolatileMode m = _VOLATILE_MODE_INVALID;
const char *path;
int r;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ log_setup_service();
- if (argc > 3) {
- log_error("Too many arguments. Expected directory and mode.");
- r = -EINVAL;
- goto finish;
- }
+ if (argc > 3)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many arguments. Expected directory and mode.");
r = query_volatile_mode(&m);
- if (r < 0) {
- log_error_errno(r, "Failed to determine volatile mode from kernel command line.");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine volatile mode from kernel command line.");
if (r == 0 && argc >= 2) {
/* The kernel command line always wins. However if nothing was set there, the argument passed here wins instead. */
m = volatile_mode_from_string(argv[1]);
if (m < 0) {
log_error("Couldn't parse volatile mode: %s", argv[1]);
r = -EINVAL;
- goto finish;
}
}
@@ -111,30 +105,21 @@ int main(int argc, char *argv[]) {
else {
path = argv[2];
- if (isempty(path)) {
- log_error("Directory name cannot be empty.");
- r = -EINVAL;
- goto finish;
- }
- if (!path_is_absolute(path)) {
- log_error("Directory must be specified as absolute path.");
- r = -EINVAL;
- goto finish;
- }
- if (path_equal(path, "/")) {
- log_error("Directory cannot be the root directory.");
- r = -EINVAL;
- goto finish;
- }
+ if (isempty(path))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Directory name cannot be empty.");
+ if (!path_is_absolute(path))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Directory must be specified as absolute path.");
+ if (path_equal(path, "/"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Directory cannot be the root directory.");
}
- if (m != VOLATILE_YES) {
- r = 0;
- goto finish;
- }
-
- r = make_volatile(path);
+ if (m != VOLATILE_YES)
+ return 0;
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return make_volatile(path);
}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/sysctl.d/50-default.conf b/sysctl.d/50-default.conf
index b67ae87ca6..b0645f33e7 100644
--- a/sysctl.d/50-default.conf
+++ b/sysctl.d/50-default.conf
@@ -22,7 +22,7 @@ kernel.sysrq = 16
kernel.core_uses_pid = 1
# Source route verification
-net.ipv4.conf.all.rp_filter = 1
+net.ipv4.conf.all.rp_filter = 2
# Do not accept source routing
net.ipv4.conf.all.accept_source_route = 0
@@ -33,9 +33,6 @@ net.ipv4.conf.all.promote_secondaries = 1
# Fair Queue CoDel packet scheduler to fight bufferbloat
net.core.default_qdisc = fq_codel
-# Request Explicit Congestion Notification (ECN) on both in and outgoing connections
-net.ipv4.tcp_ecn = 1
-
# Enable hard and soft link protection
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
diff --git a/sysusers.d/systemd.conf.m4 b/sysusers.d/systemd.conf.m4
index 2315b56e3f..ef5a3cb619 100644
--- a/sysusers.d/systemd.conf.m4
+++ b/sysusers.d/systemd.conf.m4
@@ -6,6 +6,15 @@
# (at your option) any later version.
g systemd-journal - -
+m4_ifdef(`ENABLE_NETWORKD',
+u systemd-network - "systemd Network Management"
+)m4_dnl
+m4_ifdef(`ENABLE_RESOLVE',
+u systemd-resolve - "systemd Resolver"
+)m4_dnl
+m4_ifdef(`ENABLE_TIMESYNCD',
+u systemd-timesync - "systemd Time Synchronization"
+)m4_dnl
m4_ifdef(`ENABLE_COREDUMP',
u systemd-coredump - "systemd Core Dumper"
)m4_dnl
diff --git a/test/README.testsuite b/test/README.testsuite
index 58f67f50fd..2edcf6a70a 100644
--- a/test/README.testsuite
+++ b/test/README.testsuite
@@ -1,46 +1,67 @@
-The extended testsuite only works with uid=0. It contains of several
+The extended testsuite only works with UID=0. It contains of several
subdirectories named "test/TEST-??-*", which are run one by one.
To run the extended testsuite do the following:
-$ make all # Avoid the "sudo make" below building anything as root
-$ cd test
-$ sudo make clean check
-...
-make[1]: Entering directory `/mnt/data/harald/git/systemd/test/TEST-01-BASIC'
-Making all in .
-Making all in po
-TEST: Basic systemd setup [OK]
-make[1]: Leaving directory `/mnt/data/harald/git/systemd/test/TEST-01-BASIC'
+$ ninja -C build # Avoid building anything as root later
+$ sudo test/run-integration-tests.sh
+ninja: Entering directory `/home/zbyszek/src/systemd/build'
+ninja: no work to do.
+--x-- Running TEST-01-BASIC --x--
++ make -C TEST-01-BASIC BUILD_DIR=/home/zbyszek/src/systemd/build clean setup run
+make: Entering directory '/home/zbyszek/src/systemd/test/TEST-01-BASIC'
+TEST CLEANUP: Basic systemd setup
+TEST SETUP: Basic systemd setup
...
+TEST RUN: Basic systemd setup [OK]
+make: Leaving directory '/home/zbyszek/src/systemd/test/TEST-01-BASIC'
+--x-- Result of TEST-01-BASIC: 0 --x--
+--x-- Running TEST-02-CRYPTSETUP --x--
++ make -C TEST-02-CRYPTSETUP BUILD_DIR=/home/zbyszek/src/systemd/build clean setup run
If one of the tests fails, then $subdir/test.log contains the log file of
the test.
-To debug a special testcase of the testsuite do:
+To run just one of the cases:
-$ make all
-$ cd test/TEST-01-BASIC
-$ sudo make clean setup run
+$ sudo make -C test/TEST-01-BASIC clean setup run
-QEMU
-====
+Specifying the build directory
+==============================
-If you want to log in the testsuite virtual machine, you can specify
-additional kernel command line parameter with $KERNEL_APPEND.
+If the build directory is not detected automatically, it can be specified
+with BUILD_DIR=:
-$ sudo make KERNEL_APPEND="systemd.unit=multi-user.target" clean setup run
+$ sudo BUILD_DIR=some-other-build/ test/run-integration-tests
-you can even skip the "clean" and "setup" if you want to run the machine again.
+or
-$ sudo make KERNEL_APPEND="systemd.unit=multi-user.target" run
+$ sudo make -C test/TEST-01-BASIC BUILD_DIR=../../some-other-build/ ...
-You can specify a different kernel and initramfs with $KERNEL_BIN and $INITRD.
-(Fedora's or Debian's default kernel path and initramfs are used by default)
+Note that in the second case, the path is relative to the test case directory.
+An absolute path may also be used in both cases.
+
+Configuration variables
+=======================
+
+TEST_NO_QEMU=1 can be used to disable qemu tests.
+
+TEST_NO_NSPAWN=1 can be used to disable nspawn tests.
-$ sudo make KERNEL_BIN=/boot/vmlinuz-foo INITRD=/boot/initramfs-bar clean check
+KERNEL_APPEND='...' can be used to add additional kernel parameters for the QEMU runs.
+
+The kernel and initramfs can be specified with $KERNEL_BIN and $INITRD.
+(Fedora's or Debian's default kernel path and initramfs are used by default)
A script will try to find your QEMU binary. If you want to specify a different
-one you can use $QEMU_BIN.
+one with $QEMU_BIN.
+
+Debugging the qemu image
+========================
+
+If you want to log in the testsuite virtual machine, you can specify additional
+kernel command line parameter with $KERNEL_APPEND and then log in as root.
+
+$ sudo make -C test/TEST-01-BASIC KERNEL_APPEND="systemd.unit=multi-user.target" run
-$ sudo make QEMU_BIN=/path/to/qemu/qemu-kvm clean check
+Root password is empty.
diff --git a/test/TEST-01-BASIC/Makefile b/test/TEST-01-BASIC/Makefile
index 34d7cc6cdf..45e9bfc67c 100644
--- a/test/TEST-01-BASIC/Makefile
+++ b/test/TEST-01-BASIC/Makefile
@@ -1,4 +1,9 @@
BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
-all setup clean run:
+all setup run:
@basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
+
+clean clean-again:
+ @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --clean
+
+.PHONY: all setup run clean clean-again
diff --git a/test/TEST-01-BASIC/test.sh b/test/TEST-01-BASIC/test.sh
index 8b21ba05d3..1d2f833478 100755
--- a/test/TEST-01-BASIC/test.sh
+++ b/test/TEST-01-BASIC/test.sh
@@ -3,6 +3,7 @@
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
TEST_DESCRIPTION="Basic systemd setup"
+RUN_IN_UNPRIVILEGED_CONTAINER=yes
. $TEST_BASE_DIR/test-functions
diff --git a/test/TEST-02-CRYPTSETUP/test.sh b/test/TEST-02-CRYPTSETUP/test.sh
index 545602e17a..71709f7d58 100755
--- a/test/TEST-02-CRYPTSETUP/test.sh
+++ b/test/TEST-02-CRYPTSETUP/test.sh
@@ -45,6 +45,14 @@ test_setup() {
setup_basic_environment
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
diff --git a/test/TEST-03-JOBS/test.sh b/test/TEST-03-JOBS/test.sh
index 08e5cfe6c8..93a387df59 100755
--- a/test/TEST-03-JOBS/test.sh
+++ b/test/TEST-03-JOBS/test.sh
@@ -19,6 +19,14 @@ test_setup() {
setup_basic_environment
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
diff --git a/test/TEST-04-JOURNAL/test.sh b/test/TEST-04-JOURNAL/test.sh
index 30e7b181b2..33bfcbf681 100755
--- a/test/TEST-04-JOURNAL/test.sh
+++ b/test/TEST-04-JOURNAL/test.sh
@@ -18,11 +18,18 @@ test_setup() {
setup_basic_environment
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
ExecStart=/test-journal.sh
diff --git a/test/TEST-05-RLIMITS/test.sh b/test/TEST-05-RLIMITS/test.sh
index a1b855c5fb..3eb2165796 100755
--- a/test/TEST-05-RLIMITS/test.sh
+++ b/test/TEST-05-RLIMITS/test.sh
@@ -18,6 +18,14 @@ test_setup() {
setup_basic_environment
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
cat >$initdir/etc/systemd/system.conf <<EOF
[Manager]
DefaultLimitNOFILE=10000:16384
@@ -27,7 +35,6 @@ EOF
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
ExecStart=/test-rlimits.sh
diff --git a/test/TEST-06-SELINUX/test.sh b/test/TEST-06-SELINUX/test.sh
index 604ede5b2b..0c924ecbbd 100755
--- a/test/TEST-06-SELINUX/test.sh
+++ b/test/TEST-06-SELINUX/test.sh
@@ -33,7 +33,6 @@ test_setup() {
cat <<EOF >$initdir/etc/systemd/system/testsuite.service
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
ExecStart=/test-selinux-checks.sh
diff --git a/test/TEST-07-ISSUE-1981/test.sh b/test/TEST-07-ISSUE-1981/test.sh
index 88d143e479..499a36038c 100755
--- a/test/TEST-07-ISSUE-1981/test.sh
+++ b/test/TEST-07-ISSUE-1981/test.sh
@@ -21,11 +21,18 @@ test_setup() {
setup_basic_environment
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
ExecStart=/test-segfault.sh
diff --git a/test/TEST-08-ISSUE-2730/test.sh b/test/TEST-08-ISSUE-2730/test.sh
index 68159c331f..b01df3656b 100755
--- a/test/TEST-08-ISSUE-2730/test.sh
+++ b/test/TEST-08-ISSUE-2730/test.sh
@@ -6,7 +6,6 @@ TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2730"
TEST_NO_NSPAWN=1
. $TEST_BASE_DIR/test-functions
-SKIP_INITRD=yes
QEMU_TIMEOUT=180
FSTYPE=ext4
@@ -26,7 +25,6 @@ test_setup() {
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
ExecStart=/bin/sh -x -c 'mount -o remount,rw /dev/sda1 && echo OK > /testok; systemctl poweroff'
diff --git a/test/TEST-09-ISSUE-2691/test.sh b/test/TEST-09-ISSUE-2691/test.sh
index 4c3e9496b4..01eb4dbac8 100755
--- a/test/TEST-09-ISSUE-2691/test.sh
+++ b/test/TEST-09-ISSUE-2691/test.sh
@@ -6,8 +6,7 @@ TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2691"
TEST_NO_NSPAWN=1
. $TEST_BASE_DIR/test-functions
-SKIP_INITRD=yes
-QEMU_TIMEOUT=90
+QEMU_TIMEOUT=180
test_setup() {
create_empty_image
@@ -25,14 +24,13 @@ test_setup() {
cat >$initdir/etc/systemd/system/testsuite.service <<'EOF'
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c '>/testok'
RemainAfterExit=yes
ExecStop=/bin/sh -c 'kill -SEGV $$$$'
-TimeoutStopSec=180s
+TimeoutStopSec=270s
EOF
setup_testsuite
diff --git a/test/TEST-10-ISSUE-2467/test.sh b/test/TEST-10-ISSUE-2467/test.sh
index 2f95e9062d..d8959187a1 100755
--- a/test/TEST-10-ISSUE-2467/test.sh
+++ b/test/TEST-10-ISSUE-2467/test.sh
@@ -5,7 +5,6 @@ set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2467"
. $TEST_BASE_DIR/test-functions
-SKIP_INITRD=yes
test_setup() {
create_empty_image
@@ -24,7 +23,6 @@ test_setup() {
cat >$initdir/etc/systemd/system/testsuite.service <<'EOF'
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
Type=oneshot
diff --git a/test/TEST-11-ISSUE-3166/test.sh b/test/TEST-11-ISSUE-3166/test.sh
index 4602bdfc98..6cde7fd7ea 100755
--- a/test/TEST-11-ISSUE-3166/test.sh
+++ b/test/TEST-11-ISSUE-3166/test.sh
@@ -6,7 +6,6 @@ TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3166"
TEST_NO_NSPAWN=1
. $TEST_BASE_DIR/test-functions
-SKIP_INITRD=yes
test_setup() {
create_empty_image
@@ -21,11 +20,18 @@ test_setup() {
setup_basic_environment
dracut_install false touch
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
ExecStart=/test-fail-on-restart.sh
diff --git a/test/TEST-12-ISSUE-3171/test.sh b/test/TEST-12-ISSUE-3171/test.sh
index 559fa469cd..0c267e11a6 100755
--- a/test/TEST-12-ISSUE-3171/test.sh
+++ b/test/TEST-12-ISSUE-3171/test.sh
@@ -20,6 +20,14 @@ test_setup() {
setup_basic_environment
dracut_install cat mv stat nc
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
@@ -31,7 +39,6 @@ ExecStart=/test-socket-group.sh
Type=oneshot
EOF
-
cat >$initdir/test-socket-group.sh <<'EOF'
#!/bin/bash
set -x
diff --git a/test/TEST-13-NSPAWN-SMOKE/Makefile b/test/TEST-13-NSPAWN-SMOKE/Makefile
index 7d74b1343a..e5e3350211 100644
--- a/test/TEST-13-NSPAWN-SMOKE/Makefile
+++ b/test/TEST-13-NSPAWN-SMOKE/Makefile
@@ -2,6 +2,9 @@ BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
all setup run:
@basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
-clean:
+
+clean clean-again:
@basedir=../.. TEST_BASE_DIR=../ ./test.sh --clean
@rm -f has-overflow
+
+.PHONY: all setup run clean clean-again
diff --git a/test/TEST-13-NSPAWN-SMOKE/test.sh b/test/TEST-13-NSPAWN-SMOKE/test.sh
index 6a0cb42eaf..774b7cbed5 100755
--- a/test/TEST-13-NSPAWN-SMOKE/test.sh
+++ b/test/TEST-13-NSPAWN-SMOKE/test.sh
@@ -4,7 +4,7 @@
set -e
TEST_DESCRIPTION="systemd-nspawn smoke test"
TEST_NO_NSPAWN=1
-SKIP_INITRD=yes
+
. $TEST_BASE_DIR/test-functions
test_setup() {
@@ -18,7 +18,15 @@ test_setup() {
eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
setup_basic_environment
- dracut_install busybox chmod rmdir unshare ip
+ dracut_install busybox chmod rmdir unshare ip sysctl
+
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
cp create-busybox-container $initdir/
@@ -29,7 +37,6 @@ test_setup() {
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
ExecStart=/test-nspawn.sh
@@ -63,6 +70,11 @@ if [[ -f /proc/1/ns/cgroup ]]; then
fi
is_user_ns_supported=no
+# On some systems (e.g. CentOS 7) the default limit for user namespaces
+# is set to 0, which causes the following unshare syscall to fail, even
+# with enabled user namespaces support. By setting this value explicitly
+# we can ensure the user namespaces support to be detected correctly.
+sysctl -w user.max_user_namespaces=10000
if unshare -U sh -c :; then
is_user_ns_supported=yes
fi
diff --git a/test/TEST-14-MACHINE-ID/test.sh b/test/TEST-14-MACHINE-ID/test.sh
index 7342645bc5..9435834204 100755
--- a/test/TEST-14-MACHINE-ID/test.sh
+++ b/test/TEST-14-MACHINE-ID/test.sh
@@ -4,7 +4,7 @@
set -e
TEST_DESCRIPTION="/etc/machine-id testing"
TEST_NO_NSPAWN=1
-SKIP_INITRD=yes
+
. $TEST_BASE_DIR/test-functions
test_setup() {
@@ -25,7 +25,6 @@ test_setup() {
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
ExecStart=/bin/sh -e -x -c '/test-machine-id-setup.sh; systemctl --state=failed --no-legend --no-pager > /failed ; echo OK > /testok'
diff --git a/test/TEST-15-DROPIN/testsuite.service b/test/TEST-15-DROPIN/testsuite.service
index d9790c2610..4c9f65e2b1 100644
--- a/test/TEST-15-DROPIN/testsuite.service
+++ b/test/TEST-15-DROPIN/testsuite.service
@@ -1,6 +1,5 @@
[Unit]
Description=Testsuite service
-After=multi-user.target
[Service]
ExecStart=/test-dropin.sh
diff --git a/test/TEST-17-UDEV-WANTS/Makefile b/test/TEST-17-UDEV-WANTS/Makefile
index 34d7cc6cdf..e9f93b1104 100644..120000
--- a/test/TEST-17-UDEV-WANTS/Makefile
+++ b/test/TEST-17-UDEV-WANTS/Makefile
@@ -1,4 +1 @@
-BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
-
-all setup clean run:
- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-17-UDEV-WANTS/test.sh b/test/TEST-17-UDEV-WANTS/test.sh
index 24989ebcf6..074771ab75 100755
--- a/test/TEST-17-UDEV-WANTS/test.sh
+++ b/test/TEST-17-UDEV-WANTS/test.sh
@@ -6,7 +6,7 @@ TEST_DESCRIPTION="UDEV SYSTEMD_WANTS property"
TEST_NO_NSPAWN=1
. $TEST_BASE_DIR/test-functions
-QEMU_TIMEOUT=180
+QEMU_TIMEOUT=300
test_setup() {
create_empty_image
diff --git a/test/TEST-18-FAILUREACTION/Makefile b/test/TEST-18-FAILUREACTION/Makefile
index 34d7cc6cdf..e9f93b1104 100644..120000
--- a/test/TEST-18-FAILUREACTION/Makefile
+++ b/test/TEST-18-FAILUREACTION/Makefile
@@ -1,4 +1 @@
-BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
-
-all setup clean run:
- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-18-FAILUREACTION/test.sh b/test/TEST-18-FAILUREACTION/test.sh
index e48ba9bac3..783b3aac6e 100755
--- a/test/TEST-18-FAILUREACTION/test.sh
+++ b/test/TEST-18-FAILUREACTION/test.sh
@@ -35,6 +35,13 @@ EOF
) || return 1
setup_nspawn_root
+ # mask some services that we do not want to run in these tests
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+
ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
}
diff --git a/test/TEST-19-DELEGATE/Makefile b/test/TEST-19-DELEGATE/Makefile
index 34d7cc6cdf..e9f93b1104 100644..120000
--- a/test/TEST-19-DELEGATE/Makefile
+++ b/test/TEST-19-DELEGATE/Makefile
@@ -1,4 +1 @@
-BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
-
-all setup clean run:
- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-19-DELEGATE/test.sh b/test/TEST-19-DELEGATE/test.sh
index 841a29c06f..bb0c5057b2 100755
--- a/test/TEST-19-DELEGATE/test.sh
+++ b/test/TEST-19-DELEGATE/test.sh
@@ -2,7 +2,7 @@
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
-TEST_DESCRIPTION="test cgroup delegation in the unifier hierarchy"
+TEST_DESCRIPTION="test cgroup delegation in the unified hierarchy"
TEST_NO_NSPAWN=1
. $TEST_BASE_DIR/test-functions
@@ -20,6 +20,14 @@ test_setup() {
setup_basic_environment
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
diff --git a/test/TEST-19-DELEGATE/testsuite.sh b/test/TEST-19-DELEGATE/testsuite.sh
index c738bea10e..abfcee3cd2 100755
--- a/test/TEST-19-DELEGATE/testsuite.sh
+++ b/test/TEST-19-DELEGATE/testsuite.sh
@@ -11,10 +11,27 @@ if grep -q cgroup2 /proc/filesystems ; then
-w /sys/fs/cgroup/system.slice/test0.service/cgroup.subtree_control
systemd-run --wait --unit=test1.service -p "DynamicUser=1" -p "Delegate=memory pids" \
- grep memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers
+ grep -q memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers
systemd-run --wait --unit=test2.service -p "DynamicUser=1" -p "Delegate=memory pids" \
- grep pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers
+ grep -q pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers
+
+ # "io" is not among the controllers enabled by default for all units, verify that
+ grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+
+ # Run a service with "io" enabled, and verify it works
+ systemd-run --wait --unit=test3.service -p "IOAccounting=yes" -p "Slice=system-foo-bar-baz.slice" \
+ grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test3.service/cgroup.controllers
+
+ # We want to check if "io" is removed again from the controllers
+ # list. However, PID 1 (rightfully) does this asynchronously. In order
+ # to force synchronization on this, let's start a short-lived service
+ # which requires PID 1 to refresh the cgroup tree, so that we can
+ # verify that this all works.
+ systemd-run --wait --unit=test4.service true
+
+ # And now check again, "io" should have vanished
+ grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
else
echo "Skipping TEST-19-DELEGATE, as the kernel doesn't actually support cgroupsv2" >&2
fi
diff --git a/test/TEST-20-MAINPIDGAMES/Makefile b/test/TEST-20-MAINPIDGAMES/Makefile
index 34d7cc6cdf..e9f93b1104 100644..120000
--- a/test/TEST-20-MAINPIDGAMES/Makefile
+++ b/test/TEST-20-MAINPIDGAMES/Makefile
@@ -1,4 +1 @@
-BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
-
-all setup clean run:
- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-20-MAINPIDGAMES/test.sh b/test/TEST-20-MAINPIDGAMES/test.sh
index b14083a256..4ec8081478 100755
--- a/test/TEST-20-MAINPIDGAMES/test.sh
+++ b/test/TEST-20-MAINPIDGAMES/test.sh
@@ -17,6 +17,14 @@ test_setup() {
setup_basic_environment
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
diff --git a/test/TEST-21-SYSUSERS/Makefile b/test/TEST-21-SYSUSERS/Makefile
index 34d7cc6cdf..e9f93b1104 100644..120000
--- a/test/TEST-21-SYSUSERS/Makefile
+++ b/test/TEST-21-SYSUSERS/Makefile
@@ -1,4 +1 @@
-BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
-
-all setup clean run:
- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-21-SYSUSERS/test.sh b/test/TEST-21-SYSUSERS/test.sh
index 73cbe10b69..b1049e720d 100755
--- a/test/TEST-21-SYSUSERS/test.sh
+++ b/test/TEST-21-SYSUSERS/test.sh
@@ -15,6 +15,7 @@ prepare_testdir() {
for i in $1.initial-{passwd,group,shadow}; do
test -f $i && cp $i $TESTDIR/etc/${i#*.initial-}
done
+ return 0
}
preprocess() {
diff --git a/test/TEST-22-TMPFILES/Makefile b/test/TEST-22-TMPFILES/Makefile
index 34d7cc6cdf..e9f93b1104 100644..120000
--- a/test/TEST-22-TMPFILES/Makefile
+++ b/test/TEST-22-TMPFILES/Makefile
@@ -1,4 +1 @@
-BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
-
-all setup clean run:
- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-22-TMPFILES/test-02.sh b/test/TEST-22-TMPFILES/test-02.sh
new file mode 100755
index 0000000000..fe8b903298
--- /dev/null
+++ b/test/TEST-22-TMPFILES/test-02.sh
@@ -0,0 +1,95 @@
+#! /bin/bash
+#
+# Basic tests for types creating directories
+#
+
+set -e
+set -x
+
+rm -fr /tmp/{d,D,e}
+mkdir /tmp/{d,D,e}
+
+#
+# 'd'
+#
+mkdir /tmp/d/2
+chmod 777 /tmp/d/2
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/d/1 0755 daemon daemon - -
+d /tmp/d/2 0755 daemon daemon - -
+EOF
+
+test -d /tmp/d/1
+test $(stat -c %U:%G:%a /tmp/d/1) = "daemon:daemon:755"
+
+test -d /tmp/d/2
+test $(stat -c %U:%G:%a /tmp/d/2) = "daemon:daemon:755"
+
+#
+# 'D'
+#
+mkdir /tmp/D/2
+chmod 777 /tmp/D/2
+touch /tmp/D/2/foo
+
+systemd-tmpfiles --create - <<EOF
+D /tmp/D/1 0755 daemon daemon - -
+D /tmp/D/2 0755 daemon daemon - -
+EOF
+
+test -d /tmp/D/1
+test $(stat -c %U:%G:%a /tmp/D/1) = "daemon:daemon:755"
+
+test -d /tmp/D/2
+test $(stat -c %U:%G:%a /tmp/D/2) = "daemon:daemon:755"
+
+systemd-tmpfiles --remove - <<EOF
+D /tmp/D/2 0755 daemon daemon - -
+EOF
+
+# the content of '2' should be removed
+test "$(echo /tmp/D/2/*)" = "/tmp/D/2/*"
+
+#
+# 'e'
+#
+mkdir -p /tmp/e/2/{d1,d2}
+chmod 777 /tmp/e/2
+chmod 777 /tmp/e/2/d*
+
+systemd-tmpfiles --create - <<EOF
+e /tmp/e/1 0755 daemon daemon - -
+e /tmp/e/2/* 0755 daemon daemon - -
+EOF
+
+! test -d /tmp/e/1
+
+test -d /tmp/e/2
+test $(stat -c %U:%G:%a /tmp/e/2) = "root:root:777"
+
+test -d /tmp/e/2/d1
+test $(stat -c %U:%G:%a /tmp/e/2/d1) = "daemon:daemon:755"
+test -d /tmp/e/2/d2
+test $(stat -c %U:%G:%a /tmp/e/2/d2) = "daemon:daemon:755"
+
+# 'e' operates on directories only
+mkdir -p /tmp/e/3/{d1,d2}
+chmod 777 /tmp/e/3
+chmod 777 /tmp/e/3/d*
+touch /tmp/e/3/f1
+chmod 644 /tmp/e/3/f1
+
+! systemd-tmpfiles --create - <<EOF
+e /tmp/e/3/* 0755 daemon daemon - -
+EOF
+
+# the directories should have been processed although systemd-tmpfiles failed
+# previously due to the presence of a file.
+test -d /tmp/e/3/d1
+test $(stat -c %U:%G:%a /tmp/e/3/d1) = "daemon:daemon:755"
+test -d /tmp/e/3/d2
+test $(stat -c %U:%G:%a /tmp/e/3/d2) = "daemon:daemon:755"
+
+test -f /tmp/e/3/f1
+test $(stat -c %U:%G:%a /tmp/e/3/f1) = "root:root:644"
diff --git a/test/TEST-22-TMPFILES/test-03.sh b/test/TEST-22-TMPFILES/test-03.sh
new file mode 100755
index 0000000000..39a4badc38
--- /dev/null
+++ b/test/TEST-22-TMPFILES/test-03.sh
@@ -0,0 +1,236 @@
+#! /bin/bash
+#
+# Basic tests for types creating/writing files
+#
+
+set -e
+set -x
+
+rm -fr /tmp/{f,F,w}
+mkdir /tmp/{f,F,w}
+touch /tmp/file-owned-by-root
+
+#
+# 'f'
+#
+systemd-tmpfiles --create - <<EOF
+f /tmp/f/1 0644 - - - -
+f /tmp/f/2 0644 - - - This string should be written
+EOF
+
+### '1' should exist and be empty
+test -f /tmp/f/1; ! test -s /tmp/f/1
+test $(stat -c %U:%G:%a /tmp/f/1) = "root:root:644"
+
+test $(stat -c %U:%G:%a /tmp/f/2) = "root:root:644"
+test "$(< /tmp/f/2)" = "This string should be written"
+
+### The perms are supposed to be updated even if the file already exists.
+systemd-tmpfiles --create - <<EOF
+f /tmp/f/1 0666 daemon daemon - This string should not be written
+EOF
+
+# file should be empty
+! test -s /tmp/f/1
+test $(stat -c %U:%G:%a /tmp/f/1) = "daemon:daemon:666"
+
+### But we shouldn't try to set perms on an existing file which is not a
+### regular one.
+mkfifo /tmp/f/fifo
+chmod 644 /tmp/f/fifo
+
+! systemd-tmpfiles --create - <<EOF
+f /tmp/f/fifo 0666 daemon daemon - This string should not be written
+EOF
+
+test -p /tmp/f/fifo
+test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
+
+### 'f' should not follow symlinks.
+ln -s missing /tmp/f/dangling
+ln -s /tmp/file-owned-by-root /tmp/f/symlink
+
+! systemd-tmpfiles --create - <<EOF
+f /tmp/f/dangling 0644 daemon daemon - -
+f /tmp/f/symlink 0644 daemon daemon - -
+EOF
+! test -e /tmp/f/missing
+test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
+
+### Handle read-only filesystem gracefully: we shouldn't fail if the target
+### already exists and have the correct perms.
+mkdir /tmp/f/rw-fs
+mkdir /tmp/f/ro-fs
+
+touch /tmp/f/rw-fs/foo
+chmod 644 /tmp/f/rw-fs/foo
+
+mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs
+
+systemd-tmpfiles --create - <<EOF
+f /tmp/f/ro-fs/foo 0644 - - - - This string should not be written
+EOF
+test -f /tmp/f/ro-fs/foo; ! test -s /tmp/f/ro-fs/foo
+
+! systemd-tmpfiles --create - <<EOF
+f /tmp/f/ro-fs/foo 0666 - - - -
+EOF
+test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
+
+! systemd-tmpfiles --create - <<EOF
+f /tmp/f/ro-fs/bar 0644 - - - -
+EOF
+! test -e /tmp/f/ro-fs/bar
+
+### 'f' shouldn't follow unsafe paths.
+mkdir /tmp/f/daemon
+ln -s /root /tmp/f/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/f/daemon
+
+! systemd-tmpfiles --create - <<EOF
+f /tmp/f/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
+EOF
+! test -e /tmp/f/daemon/unsafe-symlink/exploit
+
+#
+# 'F'
+#
+echo "This should be truncated" >/tmp/F/truncated
+echo "This should be truncated" >/tmp/F/truncated-with-content
+
+systemd-tmpfiles --create - <<EOF
+F /tmp/F/created 0644 - - - -
+F /tmp/F/created-with-content 0644 - - - new content
+F /tmp/F/truncated 0666 daemon daemon - -
+F /tmp/F/truncated-with-content 0666 daemon daemon - new content
+EOF
+
+test -f /tmp/F/created; ! test -s /tmp/F/created
+test -f /tmp/F/created-with-content
+test "$(< /tmp/F/created-with-content)" = "new content"
+test -f /tmp/F/truncated; ! test -s /tmp/F/truncated
+test $(stat -c %U:%G:%a /tmp/F/truncated) = "daemon:daemon:666"
+test -s /tmp/F/truncated-with-content
+test $(stat -c %U:%G:%a /tmp/F/truncated-with-content) = "daemon:daemon:666"
+
+### We shouldn't try to truncate anything but regular files since the behavior is
+### unspecified in the other cases.
+mkfifo /tmp/F/fifo
+
+! systemd-tmpfiles --create - <<EOF
+F /tmp/F/fifo 0644 - - - -
+EOF
+
+test -p /tmp/F/fifo
+
+### 'F' should not follow symlinks.
+ln -s missing /tmp/F/dangling
+ln -s /tmp/file-owned-by-root /tmp/F/symlink
+
+! systemd-tmpfiles --create - <<EOF
+f /tmp/F/dangling 0644 daemon daemon - -
+f /tmp/F/symlink 0644 daemon daemon - -
+EOF
+! test -e /tmp/F/missing
+test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
+
+### Handle read-only filesystem gracefully: we shouldn't fail if the target
+### already exists and is empty.
+mkdir /tmp/F/rw-fs
+mkdir /tmp/F/ro-fs
+
+touch /tmp/F/rw-fs/foo
+chmod 644 /tmp/F/rw-fs/foo
+
+mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs
+
+systemd-tmpfiles --create - <<EOF
+F /tmp/F/ro-fs/foo 0644 - - - -
+EOF
+test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
+
+echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo
+! systemd-tmpfiles --create - <<EOF
+F /tmp/F/ro-fs/foo 0644 - - - -
+EOF
+
+! systemd-tmpfiles --create - <<EOF
+F /tmp/F/ro-fs/foo 0644 - - - - This string should not be written
+EOF
+test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
+
+# Trying to change the perms should fail.
+>/tmp/F/rw-fs/foo
+! systemd-tmpfiles --create - <<EOF
+F /tmp/F/ro-fs/foo 0666 - - - -
+EOF
+test $(stat -c %U:%G:%a /tmp/F/ro-fs/foo) = "root:root:644"
+
+### Try to create a new file.
+! systemd-tmpfiles --create - <<EOF
+F /tmp/F/ro-fs/bar 0644 - - - -
+EOF
+! test -e /tmp/F/ro-fs/bar
+
+### 'F' shouldn't follow unsafe paths.
+mkdir /tmp/F/daemon
+ln -s /root /tmp/F/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/F/daemon
+
+! systemd-tmpfiles --create - <<EOF
+F /tmp/F/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
+EOF
+! test -e /tmp/F/daemon/unsafe-symlink/exploit
+
+#
+# 'w'
+#
+touch /tmp/w/overwritten
+
+### nop if the target does not exist.
+systemd-tmpfiles --create - <<EOF
+w /tmp/w/unexistent 0644 - - - new content
+EOF
+! test -e /tmp/w/unexistent
+
+### no argument given -> fails.
+! systemd-tmpfiles --create - <<EOF
+w /tmp/w/unexistent 0644 - - - -
+EOF
+
+### write into an empty file.
+systemd-tmpfiles --create - <<EOF
+w /tmp/w/overwritten 0644 - - - old content
+EOF
+test -f /tmp/w/overwritten
+test "$(< /tmp/w/overwritten)" = "old content"
+
+### new content is overwritten
+systemd-tmpfiles --create - <<EOF
+w /tmp/w/overwritten 0644 - - - new content
+EOF
+test -f /tmp/w/overwritten
+test "$(< /tmp/w/overwritten)" = "new content"
+
+### writing into an 'exotic' file sould be allowed.
+systemd-tmpfiles --create - <<EOF
+w /dev/null - - - - new content
+EOF
+
+### 'w' follows symlinks
+ln -s ./overwritten /tmp/w/symlink
+systemd-tmpfiles --create - <<EOF
+w /tmp/w/symlink - - - - $(readlink -e /tmp/w/symlink)
+EOF
+readlink -e /tmp/w/symlink
+test "$(< /tmp/w/overwritten)" = "/tmp/w/overwritten"
+
+### 'w' shouldn't follow unsafe paths.
+mkdir /tmp/w/daemon
+ln -s /root /tmp/w/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/w/daemon
+
+! systemd-tmpfiles --create - <<EOF
+f /tmp/w/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
+EOF
+! test -e /tmp/w/daemon/unsafe-symlink/exploit
diff --git a/test/TEST-22-TMPFILES/test-04.sh b/test/TEST-22-TMPFILES/test-04.sh
new file mode 100755
index 0000000000..f916086b1e
--- /dev/null
+++ b/test/TEST-22-TMPFILES/test-04.sh
@@ -0,0 +1,44 @@
+#! /bin/bash
+#
+# Basic tests for types creating fifos
+#
+
+set -e
+set -x
+
+rm -fr /tmp/p
+mkdir /tmp/p
+touch /tmp/p/f1
+
+systemd-tmpfiles --create - <<EOF
+p /tmp/p/fifo1 0666 - - - -
+EOF
+
+test -p /tmp/p/fifo1
+test $(stat -c %U:%G:%a /tmp/p/fifo1) = "root:root:666"
+
+# it should refuse to overwrite an existing file
+! systemd-tmpfiles --create - <<EOF
+p /tmp/p/f1 0666 - - - -
+EOF
+
+test -f /tmp/p/f1
+
+# unless '+' prefix is used
+systemd-tmpfiles --create - <<EOF
+p+ /tmp/p/f1 0666 - - - -
+EOF
+
+test -p /tmp/p/f1
+test $(stat -c %U:%G:%a /tmp/p/f1) = "root:root:666"
+
+#
+# Must be fixed
+#
+# mkdir /tmp/p/daemon
+# #ln -s /root /tmp/F/daemon/unsafe-symlink
+# chown -R --no-dereference daemon:daemon /tmp/p/daemon
+#
+# systemd-tmpfiles --create - <<EOF
+# p /tmp/p/daemon/fifo2 0666 daemon daemon - -
+# EOF
diff --git a/test/TEST-22-TMPFILES/test-05.sh b/test/TEST-22-TMPFILES/test-05.sh
new file mode 100755
index 0000000000..13c4ac80fc
--- /dev/null
+++ b/test/TEST-22-TMPFILES/test-05.sh
@@ -0,0 +1,45 @@
+#! /bin/bash
+
+set -e
+set -x
+
+rm -fr /tmp/{z,Z}
+mkdir /tmp/{z,Z}
+
+#
+# 'z'
+#
+mkdir /tmp/z/d{1,2}
+touch /tmp/z/f1 /tmp/z/d1/f11 /tmp/z/d2/f21
+
+systemd-tmpfiles --create - <<EOF
+z /tmp/z/f1 0755 daemon daemon - -
+z /tmp/z/d1 0755 daemon daemon - -
+EOF
+
+test $(stat -c %U:%G /tmp/z/f1) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/z/d1) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/z/d1/f11) = "root:root"
+
+systemd-tmpfiles --create - <<EOF
+z /tmp/z/d2/* 0755 daemon daemon - -
+EOF
+
+test $(stat -c %U:%G /tmp/z/d2/f21) = "daemon:daemon"
+
+#
+# 'Z'
+#
+mkdir /tmp/Z/d1 /tmp/Z/d1/d11
+touch /tmp/Z/f1 /tmp/Z/d1/f11 /tmp/Z/d1/d11/f111
+
+systemd-tmpfiles --create - <<EOF
+Z /tmp/Z/f1 0755 daemon daemon - -
+Z /tmp/Z/d1 0755 daemon daemon - -
+EOF
+
+test $(stat -c %U:%G /tmp/Z/f1) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/Z/d1) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/Z/d1/d11) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/Z/d1/f11) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/Z/d1/d11/f111) = "daemon:daemon"
diff --git a/test/TEST-22-TMPFILES/test-06.sh b/test/TEST-22-TMPFILES/test-06.sh
new file mode 100755
index 0000000000..cd65ba6726
--- /dev/null
+++ b/test/TEST-22-TMPFILES/test-06.sh
@@ -0,0 +1,38 @@
+#! /bin/bash
+#
+# Inspired by https://github.com/systemd/systemd/issues/9508
+#
+
+set -e
+
+test_snippet() {
+ systemd-tmpfiles "$@" - <<EOF
+d /var/tmp/foobar-test-06
+d /var/tmp/foobar-test-06/important
+R /var/tmp/foobar-test-06
+EOF
+}
+
+test_snippet --create --remove
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+
+test_snippet --remove
+! test -f /var/tmp/foobar-test-06
+! test -f /var/tmp/foobar-test-06/important
+
+test_snippet --create
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+
+touch /var/tmp/foobar-test-06/something-else
+
+test_snippet --create
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+test -f /var/tmp/foobar-test-06/something-else
+
+test_snippet --create --remove
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+! test -f /var/tmp/foobar-test-06/something-else
diff --git a/test/TEST-22-TMPFILES/test-07.sh b/test/TEST-22-TMPFILES/test-07.sh
new file mode 100755
index 0000000000..39c04b925c
--- /dev/null
+++ b/test/TEST-22-TMPFILES/test-07.sh
@@ -0,0 +1,31 @@
+#! /bin/bash
+#
+# Verifies the issues described by https://github.com/systemd/systemd/issues/10191
+#
+
+set -e
+set -x
+
+rm -rf /tmp/test-prefix
+
+mkdir /tmp/test-prefix
+touch /tmp/test-prefix/file
+
+systemd-tmpfiles --remove - <<EOF
+r /tmp/test-prefix
+r /tmp/test-prefix/file
+EOF
+
+! test -f /tmp/test-prefix/file
+! test -f /tmp/test-prefix
+
+mkdir /tmp/test-prefix
+touch /tmp/test-prefix/file
+
+systemd-tmpfiles --remove - <<EOF
+r /tmp/test-prefix/file
+r /tmp/test-prefix
+EOF
+
+! test -f /tmp/test-prefix/file
+! test -f /tmp/test-prefix
diff --git a/test/TEST-22-TMPFILES/test.sh b/test/TEST-22-TMPFILES/test.sh
index 5b8abbfff5..13e191bd92 100755
--- a/test/TEST-22-TMPFILES/test.sh
+++ b/test/TEST-22-TMPFILES/test.sh
@@ -14,6 +14,8 @@ test_setup() {
inst_binary stat
inst_binary seq
inst_binary xargs
+ inst_binary mkfifo
+ inst_binary readlink
# mask some services that we do not want to run in these tests
ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
diff --git a/test/TEST-22-TMPFILES/testsuite.service b/test/TEST-22-TMPFILES/testsuite.service
index 3a44b41989..a19174e795 100644
--- a/test/TEST-22-TMPFILES/testsuite.service
+++ b/test/TEST-22-TMPFILES/testsuite.service
@@ -1,8 +1,10 @@
[Unit]
Description=Testsuite service
-After=multi-user.target
+After=systemd-tmpfiles-setup.service
[Service]
WorkingDirectory=/testsuite
ExecStart=/testsuite/run-tmpfiles-tests.sh
Type=oneshot
+StandardOutput=tty
+StandardError=tty
diff --git a/test/TEST-23-TYPE-EXEC/Makefile b/test/TEST-23-TYPE-EXEC/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-23-TYPE-EXEC/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-23-TYPE-EXEC/test.sh b/test/TEST-23-TYPE-EXEC/test.sh
new file mode 100755
index 0000000000..2e76451f5b
--- /dev/null
+++ b/test/TEST-23-TYPE-EXEC/test.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+TEST_DESCRIPTION="test Type=exec"
+
+. $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 some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/testsuite.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
+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-23-TYPE-EXEC/testsuite.sh b/test/TEST-23-TYPE-EXEC/testsuite.sh
new file mode 100755
index 0000000000..80734bbbdc
--- /dev/null
+++ b/test/TEST-23-TYPE-EXEC/testsuite.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+set -o pipefail
+
+systemd-analyze set-log-level debug
+systemd-analyze set-log-target console
+
+# Create a binary for which execve() will fail
+touch /tmp/brokenbinary
+chmod +x /tmp/brokenbinary
+
+# These three commands should succeed.
+systemd-run --unit=one -p Type=simple /bin/sleep infinity
+systemd-run --unit=two -p Type=simple -p User=idontexist /bin/sleep infinity
+systemd-run --unit=three -p Type=simple /tmp/brokenbinary
+
+# And now, do the same with Type=exec, where the latter two should fail
+systemd-run --unit=four -p Type=exec /bin/sleep infinity
+! systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity
+! systemd-run --unit=six -p Type=exec /tmp/brokenbinary
+
+systemd-analyze set-log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/TEST-24-UNIT-TESTS/Makefile b/test/TEST-24-UNIT-TESTS/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-24-UNIT-TESTS/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-24-UNIT-TESTS/test.sh b/test/TEST-24-UNIT-TESTS/test.sh
new file mode 100755
index 0000000000..014ee52277
--- /dev/null
+++ b/test/TEST-24-UNIT-TESTS/test.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+TEST_DESCRIPTION="Run unit tests under containers"
+RUN_IN_UNPRIVILEGED_CONTAINER=yes
+
+. $TEST_BASE_DIR/test-functions
+
+check_result_nspawn() {
+ local _ret=1
+ [[ -e $TESTDIR/$1/testok ]] && _ret=0
+ if [[ -s $TESTDIR/$1/failed ]]; then
+ _ret=$(($_ret+1))
+ echo "=== Failed test log ==="
+ cat $TESTDIR/$1/failed
+ else
+ if [[ -s $TESTDIR/$1/skipped ]]; then
+ echo "=== Skipped test log =="
+ cat $TESTDIR/$1/skipped
+ fi
+ if [[ -s $TESTDIR/$1/testok ]]; then
+ echo "=== Passed tests ==="
+ cat $TESTDIR/$1/testok
+ fi
+ fi
+ cp -a $TESTDIR/$1/var/log/journal $TESTDIR
+ [[ -n "$TIMED_OUT" ]] && _ret=$(($_ret+1))
+ return $_ret
+}
+
+check_result_qemu() {
+ local _ret=1
+ mkdir -p $TESTDIR/root
+ mount ${LOOPDEV}p1 $TESTDIR/root
+ [[ -e $TESTDIR/root/testok ]] && _ret=0
+ if [[ -s $TESTDIR/root/failed ]]; then
+ _ret=$(($_ret+1))
+ echo "=== Failed test log ==="
+ cat $TESTDIR/root/failed
+ else
+ if [[ -s $TESTDIR/root/skipped ]]; then
+ echo "=== Skipped test log =="
+ cat $TESTDIR/root/skipped
+ fi
+ if [[ -s $TESTDIR/root/testok ]]; then
+ echo "=== Passed tests ==="
+ cat $TESTDIR/root/testok
+ fi
+ fi
+ cp -a $TESTDIR/root/var/log/journal $TESTDIR
+ umount $TESTDIR/root
+ [[ -n "$TIMED_OUT" ]] && _ret=$(($_ret+1))
+ return $_ret
+}
+
+test_setup() {
+ if type -P meson && [[ "$(meson configure $BUILD_DIR | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
+ dfatal "Needs to be built with -Dinstall-tests=true"
+ exit 1
+ fi
+
+ create_empty_image
+ mkdir -p $TESTDIR/root
+ mount ${LOOPDEV}p1 $TESTDIR/root
+
+ # Create what will eventually be our root filesystem onto an overlay
+ (
+ LOG_LEVEL=5
+ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+ for i in getfacl dirname basename capsh cut rev stat mktemp rmdir ionice unshare uname tr awk getent diff xzcat lz4cat; do
+ inst_binary $i
+ done
+
+ inst /etc/hosts
+
+ setup_basic_environment
+ install_keymaps yes
+ install_zoneinfo
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/testsuite.sh
+Type=oneshot
+EOF
+ cp testsuite.sh $initdir/
+
+ setup_testsuite
+ ) || return 1
+ setup_nspawn_root
+
+ # mask some services that we do not want to run in these tests
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -s /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+
+ ddebug "umount $TESTDIR/root"
+ umount $TESTDIR/root
+}
+
+do_test "$@"
diff --git a/test/TEST-24-UNIT-TESTS/testsuite.sh b/test/TEST-24-UNIT-TESTS/testsuite.sh
new file mode 100755
index 0000000000..7c0e495dbd
--- /dev/null
+++ b/test/TEST-24-UNIT-TESTS/testsuite.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+#set -ex
+#set -o pipefail
+
+for i in /usr/lib/systemd/tests/test-*; do
+ if [[ ! -x $i ]]; then continue; fi
+ NAME=${i##*/}
+ echo "Running $NAME"
+ $i > /$NAME.log 2>&1
+ ret=$?
+ if (( $ret && $ret != 77 )); then
+ echo "$NAME failed with $ret"
+ echo $NAME >> /failed-tests
+ echo "--- $NAME begin ---" >> /failed
+ cat /$NAME.log >> /failed
+ echo "--- $NAME end ---" >> /failed
+ elif (( $ret == 77 )); then
+ echo "$NAME skipped"
+ echo $NAME >> /skipped-tests
+ echo "--- $NAME begin ---" >> /skipped
+ cat /$NAME.log >> /skipped
+ echo "--- $NAME end ---" >> /skipped
+ else
+ echo "$NAME OK"
+ echo $NAME >> /testok
+ fi
+
+ systemd-cat echo "--- $NAME ---"
+ systemd-cat cat /$NAME.log
+done
+
+exit 0
diff --git a/test/TEST-25-IMPORT/Makefile b/test/TEST-25-IMPORT/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-25-IMPORT/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-25-IMPORT/test.sh b/test/TEST-25-IMPORT/test.sh
new file mode 100755
index 0000000000..188e7233bb
--- /dev/null
+++ b/test/TEST-25-IMPORT/test.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+TEST_DESCRIPTION="test importd"
+
+. $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
+ dracut_install dd gunzip mv tar diff
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/testsuite.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
+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-25-IMPORT/testsuite.sh b/test/TEST-25-IMPORT/testsuite.sh
new file mode 100755
index 0000000000..2bb96f137d
--- /dev/null
+++ b/test/TEST-25-IMPORT/testsuite.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+set -o pipefail
+
+export SYSTEMD_PAGER=cat
+
+dd if=/dev/urandom of=/var/tmp/testimage.raw bs=$((1024*1024+7)) count=5
+
+# Test import
+machinectl import-raw /var/tmp/testimage.raw
+machinectl image-status testimage
+test -f /var/lib/machines/testimage.raw
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
+
+# Test export
+machinectl export-raw testimage /var/tmp/testimage2.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
+rm /var/tmp/testimage2.raw
+
+# Test compressed export (gzip)
+machinectl export-raw testimage /var/tmp/testimage2.raw.gz
+gunzip /var/tmp/testimage2.raw.gz
+cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
+rm /var/tmp/testimage2.raw
+
+# Test clone
+machinectl clone testimage testimage3
+test -f /var/lib/machines/testimage3.raw
+machinectl image-status testimage3
+test -f /var/lib/machines/testimage.raw
+machinectl image-status testimage
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage3.raw
+
+# Test removal
+machinectl remove testimage
+! test -f /var/lib/machines/testimage.raw
+! machinectl image-status testimage
+
+# Test export of clone
+machinectl export-raw testimage3 /var/tmp/testimage3.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage3.raw
+rm /var/tmp/testimage3.raw
+
+# Test rename
+machinectl rename testimage3 testimage4
+test -f /var/lib/machines/testimage4.raw
+machinectl image-status testimage4
+! test -f /var/lib/machines/testimage3.raw
+! machinectl image-status testimage3
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw
+
+# Test export of rename
+machinectl export-raw testimage4 /var/tmp/testimage4.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage4.raw
+rm /var/tmp/testimage4.raw
+
+# Test removal
+machinectl remove testimage4
+! test -f /var/lib/machines/testimage4.raw
+! machinectl image-status testimage4
+
+# → And now, let's test directory trees ↠#
+
+# Set up a directory we can import
+mkdir /var/tmp/scratch
+mv /var/tmp/testimage.raw /var/tmp/scratch/
+touch /var/tmp/scratch/anotherfile
+mkdir /var/tmp/scratch/adirectory
+echo "piep" > /var/tmp/scratch/adirectory/athirdfile
+
+# Test import-fs
+machinectl import-fs /var/tmp/scratch/
+test -d /var/lib/machines/scratch
+machinectl image-status scratch
+
+# Test export-tar
+machinectl export-tar scratch /var/tmp/scratch.tar.gz
+test -f /var/tmp/scratch.tar.gz
+mkdir /var/tmp/extract
+(cd /var/tmp/extract ; tar xzf /var/tmp/scratch.tar.gz)
+diff -r /var/tmp/scratch/ /var/tmp/extract/
+rm -rf /var/tmp/extract
+
+# Test import-tar
+machinectl import-tar /var/tmp/scratch.tar.gz scratch2
+test -d /var/lib/machines/scratch2
+machinectl image-status scratch2
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch2
+
+# Test removal
+machinectl remove scratch
+! test -f /var/lib/machines/scratch
+! machinectl image-status scratch
+
+# Test clone
+machinectl clone scratch2 scratch3
+test -d /var/lib/machines/scratch2
+machinectl image-status scratch2
+test -d /var/lib/machines/scratch3
+machinectl image-status scratch3
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch3
+
+# Test removal
+machinectl remove scratch2
+! test -f /var/lib/machines/scratch2
+! machinectl image-status scratch2
+
+# Test rename
+machinectl rename scratch3 scratch4
+test -d /var/lib/machines/scratch4
+machinectl image-status scratch4
+! test -f /var/lib/machines/scratch3
+! machinectl image-status scratch3
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch4
+
+# Test removal
+machinectl remove scratch4
+! test -f /var/lib/machines/scratch4
+! machinectl image-status scratch4
+
+rm -rf /var/tmp/scratch
+
+echo OK > /testok
+
+exit 0
diff --git a/test/TEST-26-SETENV/Makefile b/test/TEST-26-SETENV/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-26-SETENV/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-26-SETENV/test.sh b/test/TEST-26-SETENV/test.sh
new file mode 100755
index 0000000000..fb9c18926f
--- /dev/null
+++ b/test/TEST-26-SETENV/test.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+TEST_DESCRIPTION="test setenv"
+
+. $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
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/bin/bash -x /testsuite.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
+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-26-SETENV/testsuite.sh b/test/TEST-26-SETENV/testsuite.sh
new file mode 100755
index 0000000000..2d93cb49c9
--- /dev/null
+++ b/test/TEST-26-SETENV/testsuite.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+set -o pipefail
+
+# Make sure PATH is set
+systemctl show-environment | grep -q '^PATH='
+
+# Let's add an entry and override a built-in one
+systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR
+
+# Check that both are set
+systemctl show-environment | grep -q '^PATH=.*testaddition$'
+systemctl show-environment | grep -q '^FOO=BAR$'
+
+systemctl daemon-reload
+
+# Check again after the reload
+systemctl show-environment | grep -q '^PATH=.*testaddition$'
+systemctl show-environment | grep -q '^FOO=BAR$'
+
+# Drop both
+systemctl unset-environment FOO PATH
+
+# Check that one is gone and the other reverted to the built-in
+! (systemctl show-environment | grep -q '^FOO=$')
+! (systemctl show-environment | grep -q '^PATH=.*testaddition$')
+systemctl show-environment | grep -q '^PATH='
+
+echo OK > /testok
+
+exit 0
diff --git a/test/TEST-27-STDOUTFILE/Makefile b/test/TEST-27-STDOUTFILE/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-27-STDOUTFILE/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-27-STDOUTFILE/test.sh b/test/TEST-27-STDOUTFILE/test.sh
new file mode 100755
index 0000000000..724dbef231
--- /dev/null
+++ b/test/TEST-27-STDOUTFILE/test.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+TEST_DESCRIPTION="test StandardOutput=file:"
+
+. $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)
+
+ inst_binary cmp
+
+ setup_basic_environment
+
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/testsuite.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
+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-27-STDOUTFILE/testsuite.sh b/test/TEST-27-STDOUTFILE/testsuite.sh
new file mode 100755
index 0000000000..b8fcc46e33
--- /dev/null
+++ b/test/TEST-27-STDOUTFILE/testsuite.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+set -o pipefail
+
+systemd-analyze set-log-level debug
+systemd-analyze set-log-target console
+
+systemd-run --wait --unit=one -p StandardOutput=file:/tmp/stdout -p StandardError=file:/tmp/stderr -p Type=exec sh -c 'echo x ; echo y >&2'
+cmp /tmp/stdout <<EOF
+x
+EOF
+cmp /tmp/stderr <<EOF
+y
+EOF
+
+systemd-run --wait --unit=two -p StandardOutput=file:/tmp/stdout -p StandardError=file:/tmp/stderr -p Type=exec sh -c 'echo z ; echo a >&2'
+cmp /tmp/stdout <<EOF
+z
+EOF
+cmp /tmp/stderr <<EOF
+a
+EOF
+
+systemd-run --wait --unit=three -p StandardOutput=append:/tmp/stdout -p StandardError=append:/tmp/stderr -p Type=exec sh -c 'echo b ; echo c >&2'
+cmp /tmp/stdout <<EOF
+z
+b
+EOF
+cmp /tmp/stderr <<EOF
+a
+c
+EOF
+
+systemd-analyze set-log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/daughter.service b/test/daughter.service
index aebedca348..c790b9d04b 100644
--- a/test/daughter.service
+++ b/test/daughter.service
@@ -5,3 +5,4 @@ Description=Daughter Service
Slice=parent.slice
Type=oneshot
ExecStart=/bin/true
+CPUAccounting=true
diff --git a/test/fuzz-regressions/.gitattributes b/test/fuzz-regressions/.gitattributes
deleted file mode 100644
index 7b1b3e1835..0000000000
--- a/test/fuzz-regressions/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-/*/* -whitespace
diff --git a/test/fuzz-regressions/meson.build b/test/fuzz-regressions/meson.build
deleted file mode 100644
index 3a2c306492..0000000000
--- a/test/fuzz-regressions/meson.build
+++ /dev/null
@@ -1,34 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1+
-
-sanitize_address = custom_target(
- 'sanitize-address-fuzzers',
- output : 'sanitize-address-fuzzers',
- command : [meson_build_sh,
- meson.source_root(),
- '@OUTPUT@',
- 'fuzzers',
- '-Db_lundef=false -Db_sanitize=address'])
-
-sanitizers = [['address', sanitize_address]]
-
-fuzz_regression_tests = '''
- fuzz-dns-packet/issue-7888
- fuzz-dns-packet/oss-fuzz-5465
- fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76
- fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45
- fuzz-journal-remote/oss-fuzz-8659
- fuzz-journal-remote/oss-fuzz-8686
- fuzz-unit-file/oss-fuzz-6884
- fuzz-unit-file/oss-fuzz-6885
- fuzz-unit-file/oss-fuzz-6886
- fuzz-unit-file/oss-fuzz-6892
- fuzz-unit-file/oss-fuzz-6897
- fuzz-unit-file/oss-fuzz-6897-evverx
- fuzz-unit-file/oss-fuzz-6908
- fuzz-unit-file/oss-fuzz-6917
- fuzz-unit-file/oss-fuzz-6977
- fuzz-unit-file/oss-fuzz-6977-unminimized
- fuzz-unit-file/oss-fuzz-7004
- fuzz-unit-file/oss-fuzz-8064
- fuzz-unit-file/oss-fuzz-8827
-'''.split()
diff --git a/test/fuzz-corpus/.gitattributes b/test/fuzz/.gitattributes
index 7b1b3e1835..7b1b3e1835 100644
--- a/test/fuzz-corpus/.gitattributes
+++ b/test/fuzz/.gitattributes
diff --git a/test/fuzz/fuzz-bus-message/crash-26bba7182dedc8848939931d9fcefcb7922f2e56 b/test/fuzz/fuzz-bus-message/crash-26bba7182dedc8848939931d9fcefcb7922f2e56
new file mode 100644
index 0000000000..f1bf3229ef
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-26bba7182dedc8848939931d9fcefcb7922f2e56
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/crash-29ed3c202e0ffade3cad42c8bbeb6cc68a21eb8e b/test/fuzz/fuzz-bus-message/crash-29ed3c202e0ffade3cad42c8bbeb6cc68a21eb8e
new file mode 100644
index 0000000000..4488f0a6c6
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-29ed3c202e0ffade3cad42c8bbeb6cc68a21eb8e
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/crash-32bf69483cbd4f2e6d46c25a2f92a472109aee45 b/test/fuzz/fuzz-bus-message/crash-32bf69483cbd4f2e6d46c25a2f92a472109aee45
new file mode 100644
index 0000000000..aa0c6ff7f7
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-32bf69483cbd4f2e6d46c25a2f92a472109aee45
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/crash-37449529b1ad867f0c2671fa80aca5d7812a2b70 b/test/fuzz/fuzz-bus-message/crash-37449529b1ad867f0c2671fa80aca5d7812a2b70
new file mode 100644
index 0000000000..6a20265a39
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-37449529b1ad867f0c2671fa80aca5d7812a2b70
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/crash-4162a61a79e4c5a832ca5232212f75fa560a1f75 b/test/fuzz/fuzz-bus-message/crash-4162a61a79e4c5a832ca5232212f75fa560a1f75
new file mode 100644
index 0000000000..5faf3308e7
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-4162a61a79e4c5a832ca5232212f75fa560a1f75
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/crash-4f0211eb269e28db941961061494bfdbf3345e54 b/test/fuzz/fuzz-bus-message/crash-4f0211eb269e28db941961061494bfdbf3345e54
new file mode 100644
index 0000000000..865b29507c
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-4f0211eb269e28db941961061494bfdbf3345e54
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/crash-603dfd98252375ac7dbced53c2ec312671939a36 b/test/fuzz/fuzz-bus-message/crash-603dfd98252375ac7dbced53c2ec312671939a36
new file mode 100644
index 0000000000..b3fee9e07a
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-603dfd98252375ac7dbced53c2ec312671939a36
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/crash-b88ad9ecf4aacf4a0caca5b5543953265367f084 b/test/fuzz/fuzz-bus-message/crash-b88ad9ecf4aacf4a0caca5b5543953265367f084
new file mode 100644
index 0000000000..52469650b5
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-b88ad9ecf4aacf4a0caca5b5543953265367f084
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/crash-c1b37b4729b42c0c05b23cba4eed5d8102498a1e b/test/fuzz/fuzz-bus-message/crash-c1b37b4729b42c0c05b23cba4eed5d8102498a1e
new file mode 100644
index 0000000000..2ae1a8715a
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-c1b37b4729b42c0c05b23cba4eed5d8102498a1e
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/crash-d8f3941c74219b4c03532c9b244d5ea539c61af5 b/test/fuzz/fuzz-bus-message/crash-d8f3941c74219b4c03532c9b244d5ea539c61af5
new file mode 100644
index 0000000000..26262e1149
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-d8f3941c74219b4c03532c9b244d5ea539c61af5
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/crash-e1b811da5ca494e494b77c6bd8e1c2f2989425c5 b/test/fuzz/fuzz-bus-message/crash-e1b811da5ca494e494b77c6bd8e1c2f2989425c5
new file mode 100644
index 0000000000..9d3fa0035f
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/crash-e1b811da5ca494e494b77c6bd8e1c2f2989425c5
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/leak-c09c0e2256d43bc5e2d02748c8d8760e7bc25d20 b/test/fuzz/fuzz-bus-message/leak-c09c0e2256d43bc5e2d02748c8d8760e7bc25d20
new file mode 100644
index 0000000000..c371824ffb
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/leak-c09c0e2256d43bc5e2d02748c8d8760e7bc25d20
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/message1 b/test/fuzz/fuzz-bus-message/message1
new file mode 100644
index 0000000000..2df70fd7cb
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/message1
Binary files differ
diff --git a/test/fuzz/fuzz-bus-message/timeout-08ee8f6446a4064db064e8e0b3d220147f7d0b5b b/test/fuzz/fuzz-bus-message/timeout-08ee8f6446a4064db064e8e0b3d220147f7d0b5b
new file mode 100644
index 0000000000..c975f906ee
--- /dev/null
+++ b/test/fuzz/fuzz-bus-message/timeout-08ee8f6446a4064db064e8e0b3d220147f7d0b5b
Binary files differ
diff --git a/test/fuzz/fuzz-catalog/clusterfuzz-testcase-minimized-fuzz-catalog-5674475278827520 b/test/fuzz/fuzz-catalog/clusterfuzz-testcase-minimized-fuzz-catalog-5674475278827520
new file mode 100644
index 0000000000..2594f49df0
--- /dev/null
+++ b/test/fuzz/fuzz-catalog/clusterfuzz-testcase-minimized-fuzz-catalog-5674475278827520
@@ -0,0 +1,2 @@
+-- ae2f7b866b0347b9af31fe1c80b127c0 ÿ
+ \ No newline at end of file
diff --git a/test/fuzz/fuzz-catalog/systemd.pl.catalog b/test/fuzz/fuzz-catalog/systemd.pl.catalog
new file mode 100644
index 0000000000..901541fd32
--- /dev/null
+++ b/test/fuzz/fuzz-catalog/systemd.pl.catalog
@@ -0,0 +1,390 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
+# Message catalog for systemd's own messages
+# Polish translation
+
+# The catalog format is documented on
+# https://www.freedesktop.org/wiki/Software/systemd/catalog
+
+# For an explanation why we do all this, see https://xkcd.com/1024/
+
+-- f77379a8490b408bbe5f6940505a777b
+Subject: Uruchomiono dziennik
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Systemowy proces dziennika został uruchomiony, otworzył pliki dziennika
+do zapisu i jest gotowy do przetwarzania żądań.
+
+-- d93fb3c9c24d451a97cea615ce59c00b
+Subject: Zatrzymano dziennik
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Systemowy proces dziennika został wyłączony i zamknął wszystkie obecnie
+aktywne pliki dziennika.
+
+-- ec387f577b844b8fa948f33cad9a75e6
+Subject: Miejsce na dysku używane przez dziennik
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+@JOURNAL_NAME@ (@JOURNAL_PATH@) obecnie używa @CURRENT_USE_PRETTY@.
+Maksymalnie może używać @MAX_USE_PRETTY@.
+Zostawianie co najmniej @DISK_KEEP_FREE_PRETTY@ wolnego (z obecnie dostępnego @DISK_AVAILABLE_PRETTY@ miejsca na dysku).
+Wymuszone ograniczenie użycia wynosi więc @LIMIT_PRETTY@, z czego @AVAILABLE_PRETTY@ jest nadal dostępne.
+
+Ograniczenia kontrolujące ilość miejsca na dysku używanego przez dziennik
+można konfigurować za pomocą ustawień SystemMaxUse=, SystemKeepFree=,
+SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize=
+w pliku /etc/systemd/journald.conf. Strona journald.conf(5) zawiera więcej
+informacji.
+
+-- a596d6fe7bfa4994828e72309e95d61e
+Subject: Ograniczono komunikaty z usługi
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:journald.conf(5)
+
+Usługa zapisała za dużo komunikatów w określonym czasie.
+Komunikaty z usługi zostały pominięte.
+
+Proszę zauważyć, że tylko komunikaty z danej usługi zostały pominięte.
+Nie ma to wpływu na komunikaty innych usług.
+
+Ograniczenia kontrolujące pomijanie komunikatów mogą być konfigurowane
+za pomocą opcji RateLimitIntervalSec= i RateLimitBurst= w pliku
+/etc/systemd/journald.conf lub LogRateLimitIntervalSec= i LogRateLimitBurst=
+w pliku jednostki. Strony journald.conf(5) i systemd.exec(5) zawierają więcej
+informacji.
+
+-- e9bf28e6e834481bb6f48f548ad13606
+Subject: Utracono komunikaty dziennika
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Komunikaty jądra zostały utracone, ponieważ system dziennika nie mógł
+przetworzyć ich odpowiednio szybko.
+
+-- fc2e22bc6ee647b6b90729ab34a250b1
+Subject: Proces @COREDUMP_PID@ (@COREDUMP_COMM@) zrzucił plik core
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:core(5)
+
+Proces @COREDUMP_PID@ (@COREDUMP_COMM@) uległ awarii i zrzucił plik core.
+
+Zwykle wskazuje to na błąd programistyczny w danym programie i powinno zostać
+zgłoszone jego producentowi jako błąd.
+
+-- 5aadd8e954dc4b1a8c954d63fd9e1137
+Subject: Plik core został skrócony do @SIZE_LIMIT@ B.
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:coredump.conf(5)
+
+Proces miał więcej zmapowanej pamięci niż maksimum dla przetwarzania i miejsca
+skonfigurowane przez systemd-coredump(8). Tylko pierwsze @SIZE_LIMIT@ B
+zostało zapisanych. Ten plik core może nadal być używalny, ale narzędzia typu
+gdb(1) będą ostrzegały o skróceniu pliku.
+
+-- 8d45620c1a4348dbb17410da57c60c66
+Subject: Utworzono nową sesję @SESSION_ID@ dla użytkownika @USER_ID@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: https://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Nowa sesja o identyfikatorze @SESSION_ID@ została utworzona dla użytkownika
+@USER_ID@.
+
+Proces prowadzÄ…cy sesji: @LEADER@.
+
+-- 3354939424b4456d9802ca8333ed424a
+Subject: Zakończono sesję @SESSION_ID@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: https://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Sesja o identyfikatorze @SESSION_ID@ została zakończona.
+
+-- fcbefc5da23d428093f97c82a9290f7b
+Subject: Dostępne jest nowe stanowisko @SEAT_ID@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: https://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Nowe stanowisko @SEAT_ID@ zostało skonfigurowane i jest teraz dostępne.
+
+-- e7852bfe46784ed0accde04bc864c2d5
+Subject: Usunięto stanowisko @SEAT_ID@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: https://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Stanowisko @SEAT_ID@ zostało usunięte i nie jest już dostępne.
+
+-- c7a787079b354eaaa9e77b371893cd27
+Subject: Zmiana czasu
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Zegar systemowy został zmieniony na @REALTIME@ μs po 1 stycznia 1970.
+
+-- 45f82f4aef7a4bbf942ce861d1f20990
+Subject: Zmiana strefy czasowej na @TIMEZONE@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Systemowa strefa czasowa została zmieniona na @TIMEZONE@.
+
+-- b07a249cd024414a82dd00cd181378ff
+Subject: Ukończono uruchamianie systemu
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Wszystkie usługi systemowe obowiązkowo zakolejkowane do włączenia podczas
+uruchamiania systemu zostały uruchomione. Proszę zauważyć, że nie oznacza
+to, że komputer jest bezczynny, jako że usługi mogą wciąż kończyć proces
+uruchamiania.
+
+Uruchamianie jądra zajęło @KERNEL_USEC@ μs.
+
+Uruchamianie początkowego dysku RAM zajęło @INITRD_USEC@ μs.
+
+Uruchamianie przestrzeni użytkownika zajęło @USERSPACE_USEC@ μs.
+
+-- eed00a68ffd84e31882105fd973abdd1
+Subject: Ukończono uruchamianie menedżera użytkownika
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Wystąpienie menedżera dla użytkownika @_UID@ zostało uruchomione.
+Wszystkie usługi zakolejkowane do włączenia zostały uruchomione.
+Proszę zauważyć, że inne usługi mogą być nadal uruchamiane
+lub zostać uruchomione później.
+
+Uruchamianie menedżera zajęło @USERSPACE_USEC@ μs.
+
+-- 6bbd95ee977941e497c48be27c254128
+Subject: Przejście do stanu uśpienia @SLEEP@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+System przeszedł do stanu uśpienia @SLEEP@.
+
+-- 8811e6df2a8e40f58a94cea26f8ebf14
+Subject: Wyjście ze stanu uśpienia @SLEEP@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+System wyszedł ze stanu uśpienia @SLEEP@.
+
+-- 98268866d1d54a499c4e98921d93bc40
+Subject: Zainicjowano wyłączenie systemu
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Zainicjowano wyłączenie systemu. Wyłączenie zostało rozpoczęte i wszystkie
+usługi systemowe zostały zakończone, a wszystkie systemy plików odmontowane.
+
+-- 7d4958e842da4a758f6c1cdc7b36dcc5
+Subject: Rozpoczęto wykonywanie zadania uruchamiania dla jednostki @UNIT@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Rozpoczęto wykonywanie zadania uruchamiania dla jednostki @UNIT@.
+
+Identyfikator zadania: @JOB_ID@.
+
+-- 39f53479d3a045ac8e11786248231fbf
+Subject: Pomyślnie ukończono zadanie uruchamiania dla jednostki @UNIT@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Pomyślnie ukończono zadanie uruchamiania dla jednostki @UNIT@.
+
+Identyfikator zadania: @JOB_ID@.
+
+-- be02cf6855d2428ba40df7e9d022f03d
+Subject: Zadanie uruchamiania dla jednostki @UNIT@ się nie powiodło
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Zadanie uruchamiania dla jednostki @UNIT@ zostało ukończone z niepowodzeniem.
+
+Identyfikator zadania: @JOB_ID@, wynik zadania: @JOB_RESULT@.
+
+-- de5b426a63be47a7b6ac3eaac82e2f6f
+Subject: Rozpoczęto wykonywanie zadania zatrzymania dla jednostki @UNIT@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Rozpoczęto wykonywanie zadania zatrzymania dla jednostki @UNIT@.
+
+Identyfikator zadania: @JOB_ID@.
+
+-- 9d1aaa27d60140bd96365438aad20286
+Subject: Ukończono zadanie zatrzymania dla jednostki @UNIT@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Ukończono zadanie zatrzymania dla jednostki @UNIT@.
+
+Identyfikator zadania: @JOB_ID@, wynik zadania: @JOB_RESULT@.
+
+-- d34d037fff1847e6ae669a370e694725
+Subject: Rozpoczęto wykonywanie zadania ponownego wczytania dla jednostki @UNIT@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Rozpoczęto wykonywanie zadania ponownego wczytania dla jednostki @UNIT@.
+
+Identyfikator zadania: @JOB_ID@.
+
+-- 7b05ebc668384222baa8881179cfda54
+Subject: Ukończono zadanie ponownego wczytania dla jednostki @UNIT@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Ukończono zadanie ponownego wczytania dla jednostki @UNIT@.
+
+Identyfikator zadania: @JOB_ID@, wynik zadania: @JOB_RESULT@.
+
+-- 641257651c1b4ec9a8624d7a40a9e1e7
+Subject: Nie można wykonać procesu @EXECUTABLE@
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Proces @EXECUTABLE@ nie mógł zostać wykonany i się nie powiódł.
+
+Numer błędu zwrócony przez ten proces: @ERRNO@.
+
+-- 0027229ca0644181a76c4e92458afa2e
+Subject: Nie można przekazać jednego lub więcej komunikatów do syslog
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jeden lub więcej komunikatów nie może zostać przekazanych do usługi syslog
+uruchomionej obok journald. Zwykle oznacza to, że implementacja syslog nie
+jest w stanie nadążyć za prędkością kolejki komunikatów.
+
+-- 1dee0369c7fc4736b7099b38ecb46ee7
+Subject: Punkt montowania nie jest pusty
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Katalog @WHERE@ został podany jako punkt montowania (drugie pole w pliku
+/etc/fstab lub pole Where= w pliku jednostki systemd) i nie jest pusty. Nie
+wpływa to na montowanie, ale wcześniej istniejące pliki w tym katalogu stają
+się niedostępne. Aby zobaczyć te pliki, proszę ręcznie zamontować system
+plików w innym położeniu.
+
+-- 24d8d4452573402496068381a6312df2
+Subject: Uruchomiono maszynÄ™ wirtualnÄ… lub kontener
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Maszyna wirtualna @NAME@ (PID prowadzący @LEADER@) została uruchomiona i jest
+gotowa do użycia.
+
+-- 58432bd3bace477cb514b56381b8a758
+Subject: Zakończono maszynę wirtualną lub kontener
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Maszyna wirtualna @NAME@ (PID prowadzący @LEADER@) została wyłączona.
+
+-- 36db2dfa5a9045e1bd4af5f93e1cf057
+Subject: Wyłączono tryb DNSSEC, ponieważ serwer go nie obsługuje
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8) resolved.conf(5)
+
+Usługa resolver (systemd-resolved.service) wykryła, że skonfigurowany serwer
+DNS nie obsługuje DNSSEC, w wyniku czego walidacja DNSSEC została wyłączona.
+
+To zdarzenie będzie miało miejsce, jeśli skonfigurowano DNSSEC=allow-downgrade
+w pliku resolved.conf, a skonfigurowany serwer DNS jest niezgodny z DNSSEC.
+Proszę zauważyć, że używanie tego trybu umożliwia ataki wyłączające DNSSEC,
+ponieważ atakujący będzie mógł wyłączyć walidację DNSSEC na komputerze przez
+umieszczenie odpowiednich odpowiedzi DNS w kanale komunikacji.
+
+To zdarzenie może wskazywać, że serwer DNS jest faktycznie niezgodny z DNSSEC,
+albo że atakującemu udało się upozorować atak tego typu.
+
+-- 1675d7f172174098b1108bf8c7dc8f5d
+Subject: Walidacja DNSSEC się nie powiodła
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8)
+
+Zapytanie DNS lub ustawiony wpis zasobu nie przeszedł walidacji DNSSEC.
+Zwykle wskazuje to, że ktoś manipulował używanym kanałem komunikacji.
+
+-- 4d4408cfd0d144859184d1e65d7c8a65
+Subject: Unieważniono kotwicę zaufania DNSSEC
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8)
+
+Kotwica zaufania DNSSEC została unieważniona. Należy skonfigurować nową, albo
+system operacyjny musi zostać zaktualizowany, aby dostarczyć zaktualizowaną
+kotwicÄ™ zaufania DNSSEC.
+
+-- 5eb03494b6584870a536b337290809b3
+Subject: Zaplanowano automatyczne ponowne uruchamianie jednostki
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+W wyniki skonfigurowania ustawienia Restart= zaplanowano automatyczne ponowne
+uruchamianie jednostki @UNIT@.
+
+-- ae8f7b866b0347b9af31fe1c80b127c0
+Subject: Zasoby zużyte przez uruchomienie jednostki
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jednostka @UNIT@ została ukończona, zużywając wskazane zasoby.
+
+-- 7ad2d189f7e94e70a38c781354912448
+Subject: Jednostka się powiodła
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jednostka @UNIT@ pomyślnie przeszła do stanu „dead†(martwego).
+
+-- d9b373ed55a64feb8242e02dbe79a49c
+Subject: Jednostka się nie powiodła
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Jednostka @UNIT@ przeszła do stanu „failed†(niepowodzenia)
+z wynikiem „@UNIT_RESULT@â€.
+
+-- 98e322203f7a4ed290d09fe03c09fe15
+Subject: Proces jednostki zakończył działanie
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Proces @COMMAND@= należący do jednostki @UNIT@ zakończył działanie.
+
+Kod wyjÅ›cia procesu: „@EXIT_CODE@â€, jego stan wyjÅ›cia: @EXIT_STATUS@.
+
+-- 50876a9db00f4c40bde1a2ad381c3a1b
+Subject: System jest skonfigurowany w sposób, który może powodować problemy
+Defined-By: systemd
+Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Możliwe sÄ… nastÄ™pujÄ…ce „etykietyâ€:
+• „split-usr†— /usr jest oddzielnym systemem plików, który nie był
+ zamontowany w czasie uruchomienia systemd,
+• „cgroups-missing†— jądro zostało skompilowane bez obsługi cgroups
+ lub dostęp do oczekiwanych plików interfejsu jest ograniczony,
+• „var-run-bad†— /var/run nie jest dowiązaniem symbolicznym do /run,
+• „overflowuid-not-65534†— identyfikator użytkownika dla „nieznanychâ€
+ użytkowników (przy wykorzystaniu przestrzeni nazw użytkowników lub NFS)
+ nie wynosi 65534,
+• „overflowgid-not-65534†— identyfikator grupy dla „nieznanychâ€
+ użytkowników (przy wykorzystaniu przestrzeni nazw użytkowników lub NFS)
+ nie wynosi 65534.
+Obecny system ma etykietÄ™ „@TAINT@â€.
diff --git a/test/fuzz-corpus/dhcp-server/discover-existing b/test/fuzz/fuzz-dhcp-server/discover-existing
index 1e26bf09e2..1e26bf09e2 100644
--- a/test/fuzz-corpus/dhcp-server/discover-existing
+++ b/test/fuzz/fuzz-dhcp-server/discover-existing
Binary files differ
diff --git a/test/fuzz-corpus/dhcp-server/discover-new b/test/fuzz/fuzz-dhcp-server/discover-new
index feeae55e5e..feeae55e5e 100644
--- a/test/fuzz-corpus/dhcp-server/discover-new
+++ b/test/fuzz/fuzz-dhcp-server/discover-new
Binary files differ
diff --git a/test/fuzz-corpus/dhcp-server/release b/test/fuzz/fuzz-dhcp-server/release
index 4f3eadb2f2..4f3eadb2f2 100644
--- a/test/fuzz-corpus/dhcp-server/release
+++ b/test/fuzz/fuzz-dhcp-server/release
Binary files differ
diff --git a/test/fuzz-corpus/dhcp-server/request-existing b/test/fuzz/fuzz-dhcp-server/request-existing
index 9f7a0d88f7..9f7a0d88f7 100644
--- a/test/fuzz-corpus/dhcp-server/request-existing
+++ b/test/fuzz/fuzz-dhcp-server/request-existing
Binary files differ
diff --git a/test/fuzz-corpus/dhcp-server/request-new b/test/fuzz/fuzz-dhcp-server/request-new
index fc6f5864a7..fc6f5864a7 100644
--- a/test/fuzz-corpus/dhcp-server/request-new
+++ b/test/fuzz/fuzz-dhcp-server/request-new
Binary files differ
diff --git a/test/fuzz-corpus/dhcp-server/request-reboot b/test/fuzz/fuzz-dhcp-server/request-reboot
index fde74b2b91..fde74b2b91 100644
--- a/test/fuzz-corpus/dhcp-server/request-reboot
+++ b/test/fuzz/fuzz-dhcp-server/request-reboot
Binary files differ
diff --git a/test/fuzz-corpus/dhcp-server/request-renew b/test/fuzz/fuzz-dhcp-server/request-renew
index 8dcda2ad88..8dcda2ad88 100644
--- a/test/fuzz-corpus/dhcp-server/request-renew
+++ b/test/fuzz/fuzz-dhcp-server/request-renew
Binary files differ
diff --git a/test/fuzz/fuzz-dhcp6-client/crash-4003c06fce43a11fbd22f02584df2807ac333eae b/test/fuzz/fuzz-dhcp6-client/crash-4003c06fce43a11fbd22f02584df2807ac333eae
new file mode 100644
index 0000000000..630ee6632c
--- /dev/null
+++ b/test/fuzz/fuzz-dhcp6-client/crash-4003c06fce43a11fbd22f02584df2807ac333eae
Binary files differ
diff --git a/test/fuzz/fuzz-dhcp6-client/crash-6e88fcb6b85c9436bcbe05219aa8e550194645ef b/test/fuzz/fuzz-dhcp6-client/crash-6e88fcb6b85c9436bcbe05219aa8e550194645ef
new file mode 100644
index 0000000000..44883be9f0
--- /dev/null
+++ b/test/fuzz/fuzz-dhcp6-client/crash-6e88fcb6b85c9436bcbe05219aa8e550194645ef
Binary files differ
diff --git a/test/fuzz/fuzz-dhcp6-client/oss-fuzz-10746 b/test/fuzz/fuzz-dhcp6-client/oss-fuzz-10746
new file mode 100644
index 0000000000..41c0da0db4
--- /dev/null
+++ b/test/fuzz/fuzz-dhcp6-client/oss-fuzz-10746
Binary files differ
diff --git a/test/fuzz/fuzz-dhcp6-client/oss-fuzz-11019 b/test/fuzz/fuzz-dhcp6-client/oss-fuzz-11019
new file mode 100644
index 0000000000..542b08521c
--- /dev/null
+++ b/test/fuzz/fuzz-dhcp6-client/oss-fuzz-11019
Binary files differ
diff --git a/test/fuzz-regressions/fuzz-dns-packet/issue-7888 b/test/fuzz/fuzz-dns-packet/issue-7888
index 19e7eedf51..19e7eedf51 100644
--- a/test/fuzz-regressions/fuzz-dns-packet/issue-7888
+++ b/test/fuzz/fuzz-dns-packet/issue-7888
Binary files differ
diff --git a/test/fuzz-regressions/fuzz-dns-packet/oss-fuzz-5465 b/test/fuzz/fuzz-dns-packet/oss-fuzz-5465
index ccd8a4fd6b..ccd8a4fd6b 100644
--- a/test/fuzz-regressions/fuzz-dns-packet/oss-fuzz-5465
+++ b/test/fuzz/fuzz-dns-packet/oss-fuzz-5465
Binary files differ
diff --git a/test/fuzz-regressions/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76 b/test/fuzz/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76
index e6a7316805..e6a7316805 100644
--- a/test/fuzz-regressions/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76
+++ b/test/fuzz/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76
Binary files differ
diff --git a/test/fuzz-regressions/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45 b/test/fuzz/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45
index 535d49ea7a..535d49ea7a 100644
--- a/test/fuzz-regressions/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45
+++ b/test/fuzz/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45
Binary files differ
diff --git a/test/fuzz-corpus/journal-remote/invalid-ts.txt b/test/fuzz/fuzz-journal-remote/invalid-ts.txt
index bc036fdcb0..bc036fdcb0 100644
--- a/test/fuzz-corpus/journal-remote/invalid-ts.txt
+++ b/test/fuzz/fuzz-journal-remote/invalid-ts.txt
Binary files differ
diff --git a/test/fuzz/fuzz-journal-remote/oss-fuzz-8658 b/test/fuzz/fuzz-journal-remote/oss-fuzz-8658
new file mode 100644
index 0000000000..c78d65c569
--- /dev/null
+++ b/test/fuzz/fuzz-journal-remote/oss-fuzz-8658
Binary files differ
diff --git a/test/fuzz-regressions/fuzz-journal-remote/oss-fuzz-8659 b/test/fuzz/fuzz-journal-remote/oss-fuzz-8659
index acc6a0f5e1..acc6a0f5e1 100644
--- a/test/fuzz-regressions/fuzz-journal-remote/oss-fuzz-8659
+++ b/test/fuzz/fuzz-journal-remote/oss-fuzz-8659
diff --git a/test/fuzz-regressions/fuzz-journal-remote/oss-fuzz-8686 b/test/fuzz/fuzz-journal-remote/oss-fuzz-8686
index 7c73c8cd9b..7c73c8cd9b 100644
--- a/test/fuzz-regressions/fuzz-journal-remote/oss-fuzz-8686
+++ b/test/fuzz/fuzz-journal-remote/oss-fuzz-8686
diff --git a/test/fuzz-corpus/journal-remote/sample.txt b/test/fuzz/fuzz-journal-remote/sample.txt
index 891c0003ef..891c0003ef 100644
--- a/test/fuzz-corpus/journal-remote/sample.txt
+++ b/test/fuzz/fuzz-journal-remote/sample.txt
diff --git a/test/fuzz/fuzz-journald-audit/basic b/test/fuzz/fuzz-journald-audit/basic
new file mode 100644
index 0000000000..d1ce8cc5f0
--- /dev/null
+++ b/test/fuzz/fuzz-journald-audit/basic
@@ -0,0 +1 @@
+audit(1542398162.211:744): pid=7376 uid=1000 auid=1000 ses=6 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='op=PAM:accounting grantors=pam_unix,pam_localuser acct="vagrant" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/1 res=success' \ No newline at end of file
diff --git a/test/fuzz/fuzz-journald-audit/crash b/test/fuzz/fuzz-journald-audit/crash
new file mode 100644
index 0000000000..91bd85ca6e
--- /dev/null
+++ b/test/fuzz/fuzz-journald-audit/crash
@@ -0,0 +1 @@
+audit(1542398162.211:744) pid=7376 uid=1000 auid=1000 ses=6 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='op=PAM:accounting grantors=pam_unix,pam_localuser acct="vagrant" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/1 res=success'
diff --git a/test/fuzz/fuzz-journald-kmsg/basic b/test/fuzz/fuzz-journald-kmsg/basic
new file mode 100644
index 0000000000..1299cd0869
--- /dev/null
+++ b/test/fuzz/fuzz-journald-kmsg/basic
@@ -0,0 +1 @@
+29,456,292891883,-;systemd[1]: Reexecuting.
diff --git a/test/fuzz/fuzz-journald-kmsg/crash-c6c04d83e73f3d1417bc0afce8fa81b99f955963 b/test/fuzz/fuzz-journald-kmsg/crash-c6c04d83e73f3d1417bc0afce8fa81b99f955963
new file mode 100644
index 0000000000..19887a1fec
--- /dev/null
+++ b/test/fuzz/fuzz-journald-kmsg/crash-c6c04d83e73f3d1417bc0afce8fa81b99f955963
Binary files differ
diff --git a/test/fuzz/fuzz-journald-kmsg/dev-null b/test/fuzz/fuzz-journald-kmsg/dev-null
new file mode 100644
index 0000000000..de039588b5
--- /dev/null
+++ b/test/fuzz/fuzz-journald-kmsg/dev-null
@@ -0,0 +1,2 @@
+12,460,1322026586,-;hey
+ DEVICE=c1:3
diff --git a/test/fuzz/fuzz-journald-kmsg/leak-ab161e601e82f1ec31d11e2cbae2747834ce9e43 b/test/fuzz/fuzz-journald-kmsg/leak-ab161e601e82f1ec31d11e2cbae2747834ce9e43
new file mode 100644
index 0000000000..424ae5cb01
--- /dev/null
+++ b/test/fuzz/fuzz-journald-kmsg/leak-ab161e601e82f1ec31d11e2cbae2747834ce9e43
Binary files differ
diff --git a/test/fuzz/fuzz-journald-kmsg/loopback b/test/fuzz/fuzz-journald-kmsg/loopback
new file mode 100644
index 0000000000..ca320177b7
--- /dev/null
+++ b/test/fuzz/fuzz-journald-kmsg/loopback
@@ -0,0 +1,2 @@
+12,460,1322026586,-;hey
+ DEVICE=n1
diff --git a/test/fuzz/fuzz-journald-kmsg/subsystem-loopback b/test/fuzz/fuzz-journald-kmsg/subsystem-loopback
new file mode 100644
index 0000000000..af9c0d91e5
--- /dev/null
+++ b/test/fuzz/fuzz-journald-kmsg/subsystem-loopback
@@ -0,0 +1,2 @@
+12,460,1322026586,-;hey
+ DEVICE=+net:lo
diff --git a/test/fuzz/fuzz-journald-native-fd/basic b/test/fuzz/fuzz-journald-native-fd/basic
new file mode 100644
index 0000000000..65f89705a6
--- /dev/null
+++ b/test/fuzz/fuzz-journald-native-fd/basic
Binary files differ
diff --git a/test/fuzz/fuzz-journald-stream/basic b/test/fuzz/fuzz-journald-stream/basic
new file mode 100644
index 0000000000..a088f1a539
--- /dev/null
+++ b/test/fuzz/fuzz-journald-stream/basic
@@ -0,0 +1,8 @@
+
+
+6
+1
+0
+0
+0
+hey \ No newline at end of file
diff --git a/test/fuzz/fuzz-journald-syslog/github-9795 b/test/fuzz/fuzz-journald-syslog/github-9795
new file mode 100644
index 0000000000..0519ecba6e
--- /dev/null
+++ b/test/fuzz/fuzz-journald-syslog/github-9795
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/test/fuzz/fuzz-journald-syslog/github-9820 b/test/fuzz/fuzz-journald-syslog/github-9820
new file mode 100644
index 0000000000..55e1bb5967
--- /dev/null
+++ b/test/fuzz/fuzz-journald-syslog/github-9820
@@ -0,0 +1 @@
+<13>Aug 4 04:08:03 something-is-about-to-go-wrong: \ No newline at end of file
diff --git a/test/fuzz/fuzz-journald-syslog/github-9827 b/test/fuzz/fuzz-journald-syslog/github-9827
new file mode 100644
index 0000000000..6787e487a4
--- /dev/null
+++ b/test/fuzz/fuzz-journald-syslog/github-9827
@@ -0,0 +1 @@
+<> \ No newline at end of file
diff --git a/test/fuzz/fuzz-journald-syslog/github-9829 b/test/fuzz/fuzz-journald-syslog/github-9829
new file mode 100644
index 0000000000..22ded55aa2
--- /dev/null
+++ b/test/fuzz/fuzz-journald-syslog/github-9829
@@ -0,0 +1 @@
+: \ No newline at end of file
diff --git a/test/fuzz/fuzz-json/crash-5639441482252288 b/test/fuzz/fuzz-json/crash-5639441482252288
new file mode 100644
index 0000000000..c6cf59a51e
--- /dev/null
+++ b/test/fuzz/fuzz-json/crash-5639441482252288
@@ -0,0 +1,9 @@
+[[],[],[ ],[[],[[],[[ ],[[],[],[],[[],[],[[ ],[],[],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]],[],[[],[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],[],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+
+
+
+
+
+[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],[],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]],[],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],0]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+
+]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
diff --git a/test/fuzz/fuzz-json/oss-fuzz-10908 b/test/fuzz/fuzz-json/oss-fuzz-10908
new file mode 100644
index 0000000000..5aee8e7ce2
--- /dev/null
+++ b/test/fuzz/fuzz-json/oss-fuzz-10908
@@ -0,0 +1,9 @@
+[[],[],[ ],[[],[[],[[ ],[[],[],[],[[],[],[[ ],[],[],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]],[],[[],[],[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],[],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+
+
+
+
+
+[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],[],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]],[],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]],[],""]]]],[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[],[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],0]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+
+]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] \ No newline at end of file
diff --git a/test/fuzz/fuzz-lldp/basic b/test/fuzz/fuzz-lldp/basic
new file mode 100644
index 0000000000..43debea6e4
--- /dev/null
+++ b/test/fuzz/fuzz-lldp/basic
Binary files differ
diff --git a/test/fuzz/fuzz-lldp/incomplete b/test/fuzz/fuzz-lldp/incomplete
new file mode 100644
index 0000000000..53dce6001b
--- /dev/null
+++ b/test/fuzz/fuzz-lldp/incomplete
Binary files differ
diff --git a/test/fuzz/fuzz-lldp/oui b/test/fuzz/fuzz-lldp/oui
new file mode 100644
index 0000000000..9c76c400cb
--- /dev/null
+++ b/test/fuzz/fuzz-lldp/oui
Binary files differ
diff --git a/test/fuzz/fuzz-ndisc-rs/oss-fuzz-10734 b/test/fuzz/fuzz-ndisc-rs/oss-fuzz-10734
new file mode 100644
index 0000000000..c964745249
--- /dev/null
+++ b/test/fuzz/fuzz-ndisc-rs/oss-fuzz-10734
Binary files differ
diff --git a/test/fuzz/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 b/test/fuzz/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1
new file mode 100644
index 0000000000..410cf38c1e
--- /dev/null
+++ b/test/fuzz/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1
Binary files differ
diff --git a/test/fuzz/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b b/test/fuzz/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b
new file mode 100644
index 0000000000..04e871fbcb
--- /dev/null
+++ b/test/fuzz/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b
Binary files differ
diff --git a/test/fuzz/fuzz-netdev-parser/11-dummy.netdev b/test/fuzz/fuzz-netdev-parser/11-dummy.netdev
new file mode 100644
index 0000000000..6797eb4b09
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/11-dummy.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=test1
+Kind=dummy
diff --git a/test/fuzz/fuzz-netdev-parser/12-dummy.netdev b/test/fuzz/fuzz-netdev-parser/12-dummy.netdev
new file mode 100644
index 0000000000..a7fdc0f7e0
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/12-dummy.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=dummy98
+Kind=dummy
diff --git a/test/fuzz/fuzz-netdev-parser/21-macvlan.netdev b/test/fuzz/fuzz-netdev-parser/21-macvlan.netdev
new file mode 100644
index 0000000000..e9a3c5b347
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/21-macvlan.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=macvlan99
+Kind=macvlan
diff --git a/test/fuzz/fuzz-netdev-parser/21-macvtap.netdev b/test/fuzz/fuzz-netdev-parser/21-macvtap.netdev
new file mode 100644
index 0000000000..2c23aacfb2
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/21-macvtap.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=macvtap99
+Kind=macvtap
diff --git a/test/fuzz/fuzz-netdev-parser/21-vlan.netdev b/test/fuzz/fuzz-netdev-parser/21-vlan.netdev
new file mode 100644
index 0000000000..fe9801c8a2
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/21-vlan.netdev
@@ -0,0 +1,10 @@
+[NetDev]
+Name=vlan99
+Kind=vlan
+
+[VLAN]
+Id=99
+GVRP=true
+MVRP=true
+LooseBinding=true
+ReorderHeader=true
diff --git a/test/fuzz/fuzz-netdev-parser/25-6rd-tunnel.netdev b/test/fuzz/fuzz-netdev-parser/25-6rd-tunnel.netdev
new file mode 100644
index 0000000000..252abf5278
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-6rd-tunnel.netdev
@@ -0,0 +1,8 @@
+[NetDev]
+Name=6rdtun99
+Kind=sit
+
+[Tunnel]
+Local=10.65.223.238
+Remote=10.65.223.239
+IPv6RapidDeploymentPrefix=2602::/24
diff --git a/test/fuzz/fuzz-netdev-parser/25-bond-active-backup-slave.netdev b/test/fuzz/fuzz-netdev-parser/25-bond-active-backup-slave.netdev
new file mode 100644
index 0000000000..1bbbf75570
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-bond-active-backup-slave.netdev
@@ -0,0 +1,6 @@
+[NetDev]
+Name=bond199
+Kind=bond
+
+[Bond]
+Mode=active-backup
diff --git a/test/fuzz/fuzz-netdev-parser/25-bond.netdev b/test/fuzz/fuzz-netdev-parser/25-bond.netdev
new file mode 100644
index 0000000000..4e4885c44c
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-bond.netdev
@@ -0,0 +1,18 @@
+[NetDev]
+Name=bond99
+Kind=bond
+
+[Bond]
+Mode=802.3ad
+TransmitHashPolicy=layer3+4
+MIIMonitorSec=1s
+LACPTransmitRate=fast
+UpDelaySec=2s
+DownDelaySec=2s
+ResendIGMP=4
+MinLinks=1
+AdActorSystemPriority=1218
+AdUserPortKey=811
+AdActorSystem=00:11:22:33:44:55
+# feed the sanitizer
+AdActorSystem=00:11:22:33:44:55
diff --git a/test/fuzz/fuzz-netdev-parser/25-bridge.netdev b/test/fuzz/fuzz-netdev-parser/25-bridge.netdev
new file mode 100644
index 0000000000..e23abd5368
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-bridge.netdev
@@ -0,0 +1,13 @@
+[NetDev]
+Name=bridge99
+Kind=bridge
+
+[Bridge]
+HelloTimeSec=9
+MaxAgeSec=9
+ForwardDelaySec=9
+AgeingTimeSec=9
+Priority=9
+MulticastQuerier= true
+MulticastSnooping=true
+STP=true
diff --git a/test/fuzz/fuzz-netdev-parser/25-erspan-tunnel.netdev b/test/fuzz/fuzz-netdev-parser/25-erspan-tunnel.netdev
new file mode 100644
index 0000000000..0fd8b92b2f
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-erspan-tunnel.netdev
@@ -0,0 +1,11 @@
+[NetDev]
+Name=erspan-test
+Kind=erspan
+
+[Tunnel]
+Independent=true
+ERSPANIndex=123
+Local=172.16.1.200
+Remote=172.16.1.100
+Key=101
+SerializeTunneledPackets=true
diff --git a/test/fuzz/fuzz-netdev-parser/25-geneve.netdev b/test/fuzz/fuzz-netdev-parser/25-geneve.netdev
new file mode 100644
index 0000000000..279d71324e
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-geneve.netdev
@@ -0,0 +1,12 @@
+[NetDev]
+Name=geneve99
+Kind=geneve
+
+[GENEVE]
+Id=99
+Remote=192.168.22.1
+TTL=1
+UDPChecksum=true
+UDP6ZeroChecksumTx=true
+UDP6ZeroChecksumRx=true
+DestinationPort=6082
diff --git a/test/fuzz/fuzz-netdev-parser/25-gre-tunnel.netdev b/test/fuzz/fuzz-netdev-parser/25-gre-tunnel.netdev
new file mode 100644
index 0000000000..94d9320cdb
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-gre-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=gretun99
+Kind=gre
+
+[Tunnel]
+Local=10.65.223.238
+Remote=10.65.223.239
diff --git a/test/fuzz/fuzz-netdev-parser/25-gretap-tunnel.netdev b/test/fuzz/fuzz-netdev-parser/25-gretap-tunnel.netdev
new file mode 100644
index 0000000000..769e7653e4
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-gretap-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=gretap99
+Kind=gretap
+
+[Tunnel]
+Local=10.65.223.238
+Remote=10.65.223.239
diff --git a/test/fuzz/fuzz-netdev-parser/25-ip6gre-tunnel.netdev b/test/fuzz/fuzz-netdev-parser/25-ip6gre-tunnel.netdev
new file mode 100644
index 0000000000..b16e0b4969
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-ip6gre-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=ip6gretap99
+Kind=ip6gretap
+
+[Tunnel]
+Local=2a00:ffde:4567:edde::4987
+Remote=2001:473:fece:cafe::5179
diff --git a/test/fuzz/fuzz-netdev-parser/25-ip6tnl-tunnel.netdev b/test/fuzz/fuzz-netdev-parser/25-ip6tnl-tunnel.netdev
new file mode 100644
index 0000000000..713e685ea1
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-ip6tnl-tunnel.netdev
@@ -0,0 +1,8 @@
+[NetDev]
+Name=ip6tnl99
+Kind=ip6tnl
+
+[Tunnel]
+Mode=ip6ip6
+Local=2a00:ffde:4567:edde::4987
+Remote=2001:473:fece:cafe::5179
diff --git a/test/fuzz/fuzz-netdev-parser/25-ipip-tunnel-independent.netdev b/test/fuzz/fuzz-netdev-parser/25-ipip-tunnel-independent.netdev
new file mode 100644
index 0000000000..36ff8d9429
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-ipip-tunnel-independent.netdev
@@ -0,0 +1,9 @@
+[NetDev]
+Name=ipiptun99
+Kind=ipip
+MTUBytes=1480
+
+[Tunnel]
+Local=192.168.223.238
+Remote=192.169.224.239
+Independent=true
diff --git a/test/fuzz/fuzz-netdev-parser/25-ipip-tunnel.netdev b/test/fuzz/fuzz-netdev-parser/25-ipip-tunnel.netdev
new file mode 100644
index 0000000000..159ac72703
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-ipip-tunnel.netdev
@@ -0,0 +1,8 @@
+[NetDev]
+Name=ipiptun99
+Kind=ipip
+MTUBytes=1480
+
+[Tunnel]
+Local=192.168.223.238
+Remote=192.169.224.239
diff --git a/test/fuzz/fuzz-netdev-parser/25-ipvlan.netdev b/test/fuzz/fuzz-netdev-parser/25-ipvlan.netdev
new file mode 100644
index 0000000000..9921b787a1
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-ipvlan.netdev
@@ -0,0 +1,6 @@
+[NetDev]
+Name=ipvlan99
+Kind=ipvlan
+
+[IPVLAN]
+Mode=L2
diff --git a/test/fuzz/fuzz-netdev-parser/25-sit-tunnel.netdev b/test/fuzz/fuzz-netdev-parser/25-sit-tunnel.netdev
new file mode 100644
index 0000000000..406d74bcf0
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-sit-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=sittun99
+Kind=sit
+
+[Tunnel]
+Local=10.65.223.238
+Remote=10.65.223.239
diff --git a/test/fuzz/fuzz-netdev-parser/25-tap.netdev b/test/fuzz/fuzz-netdev-parser/25-tap.netdev
new file mode 100644
index 0000000000..bf5e7fe52c
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-tap.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=tap99
+Kind=tap
+
+[Tap]
+MultiQueue=true
+PacketInfo=true
diff --git a/test/fuzz/fuzz-netdev-parser/25-tun.netdev b/test/fuzz/fuzz-netdev-parser/25-tun.netdev
new file mode 100644
index 0000000000..380ab21552
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-tun.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=tun99
+Kind=tun
+
+[Tun]
+MultiQueue=true
+PacketInfo=true
diff --git a/test/fuzz/fuzz-netdev-parser/25-vcan.netdev b/test/fuzz/fuzz-netdev-parser/25-vcan.netdev
new file mode 100644
index 0000000000..ff1979536a
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-vcan.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=vcan99
+Kind=vcan
diff --git a/test/fuzz/fuzz-netdev-parser/25-veth.netdev b/test/fuzz/fuzz-netdev-parser/25-veth.netdev
new file mode 100644
index 0000000000..9ae4ad53b8
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-veth.netdev
@@ -0,0 +1,8 @@
+[NetDev]
+Name=veth99
+Kind=veth
+MACAddress=12:34:56:78:9a:bc
+
+[Peer]
+Name=veth-peer
+MACAddress=12:34:56:78:9a:bd
diff --git a/test/fuzz/fuzz-netdev-parser/25-vrf.netdev b/test/fuzz/fuzz-netdev-parser/25-vrf.netdev
new file mode 100644
index 0000000000..bf949ec293
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-vrf.netdev
@@ -0,0 +1,6 @@
+[NetDev]
+Name=vrf99
+Kind=vrf
+
+[VRF]
+TableId=42
diff --git a/test/fuzz/fuzz-netdev-parser/25-vti-tunnel.netdev b/test/fuzz/fuzz-netdev-parser/25-vti-tunnel.netdev
new file mode 100644
index 0000000000..cec6259781
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-vti-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=vtitun99
+Kind=vti
+
+[Tunnel]
+Local=10.65.223.238
+Remote=10.65.223.239
diff --git a/test/fuzz/fuzz-netdev-parser/25-vti6-tunnel.netdev b/test/fuzz/fuzz-netdev-parser/25-vti6-tunnel.netdev
new file mode 100644
index 0000000000..d150c9ce86
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-vti6-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=vti6tun99
+Kind=vti6
+
+[Tunnel]
+Local=2a00:ffde:4567:edde::4987
+Remote=2001:473:fece:cafe::5179
diff --git a/test/fuzz/fuzz-netdev-parser/25-vxlan.netdev b/test/fuzz/fuzz-netdev-parser/25-vxlan.netdev
new file mode 100644
index 0000000000..819a58356f
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-vxlan.netdev
@@ -0,0 +1,16 @@
+[NetDev]
+Name=vxlan99
+Kind=vxlan
+
+[VXLAN]
+Id=999
+L2MissNotification=true
+L3MissNotification=true
+RouteShortCircuit=true
+UDPChecksum=true
+UDP6ZeroChecksumTx=true
+UDP6ZeroChecksumRx=true
+RemoteChecksumTx=true
+RemoteChecksumRx=true
+GroupPolicyExtension=true
+DestinationPort=5555
diff --git a/test/fuzz/fuzz-netdev-parser/25-wireguard.netdev b/test/fuzz/fuzz-netdev-parser/25-wireguard.netdev
new file mode 100644
index 0000000000..e07d685129
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/25-wireguard.netdev
@@ -0,0 +1,12 @@
+[NetDev]
+Name=wg99
+Kind=wireguard
+
+[WireGuard]
+PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
+ListenPort=51820
+
+[WireGuardPeer]
+PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=
+AllowedIPs=fd31:bf08:57cb::/48,192.168.26.0/24
+Endpoint=wireguard.example.com:51820
diff --git a/test/fuzz/fuzz-netdev-parser/26-bridge.netdev b/test/fuzz/fuzz-netdev-parser/26-bridge.netdev
new file mode 100644
index 0000000000..9b31e06b5b
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/26-bridge.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=bridge99
+Kind=bridge
diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev
new file mode 100644
index 0000000000..cd7c3aaded
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/directives.netdev
@@ -0,0 +1,158 @@
+[VLAN]
+MVRP=
+LooseBinding=
+ReorderHeader=
+Id=
+GVRP=
+[MACVLAN]
+Mode=
+[WireGuard]
+ListenPort=
+PrivateKey=
+FwMark=
+[MACVTAP]
+Mode=
+[Match]
+Architecture=
+Host=
+KernelVersion=
+Virtualization=
+KernelCommandLine=
+[GENEVE]
+DestinationPort=
+TTL=
+UDP6ZeroCheckSumTx=
+TOS=
+Id=
+UDPChecksum=
+UDP6ZeroChecksumTx=
+FlowLabel=
+UDP6ZeroChecksumRx=
+Remote=
+UDP6ZeroCheckSumRx=
+[Bridge]
+ForwardDelaySec=
+HelloTimeSec=
+MaxAgeSec=
+STP=
+MulticastSnooping=
+DefaultPVID=
+MulticastQuerier=
+AgeingTimeSec=
+Priority=
+GroupForwardMask=
+VLANFiltering=
+[VRF]
+TableId=
+Table=
+[Peer]
+MACAddress=
+Name=
+[WireGuardPeer]
+Endpoint=
+PresharedKey=
+PersistentKeepalive=
+PublicKey=
+AllowedIPs=
+[Tunnel]
+FooOverUDP=
+IPv6FlowLabel=
+Remote=
+AllowLocalRemote=
+Local=
+TOS=
+Independent=
+Key=
+InputKey=
+Encapsulation=
+Mode=
+OutputKey=
+DiscoverPathMTU=
+FOUDestinationPort=
+CopyDSCP=
+EncapsulationLimit=
+TTL=
+FOUSourcePort=
+IPv6RapidDeploymentPrefix=
+ERSPANIndex=
+SerializeTunneledPackets=
+ISATAP=
+[VXLAN]
+UDP6ZeroChecksumRx=
+ARPProxy=
+FlowLabel=
+Local=
+RemoteChecksumTx=
+RouteShortCircuit=
+Remote=
+L3MissNotification=
+Group=
+FDBAgeingSec=
+MacLearning=
+Id=
+RemoteChecksumRx=
+TOS=
+L2MissNotification=
+UDP6ZeroChecksumTx=
+UDP6ZeroCheckSumRx=
+UDPCheckSum=
+GroupPolicyExtension=
+MaximumFDBEntries=
+TTL=
+DestinationPort=
+ReduceARPProxy=
+PortRange=
+UDPChecksum=
+UDP6ZeroCheckSumTx=
+[VXCAN]
+Peer=
+[Bond]
+LearnPacketIntervalSec=
+TransmitHashPolicy=
+MIIMonitorSec=
+PacketsPerSlave=
+DownDelaySec=
+PrimaryReselectPolicy=
+ResendIGMP=
+ARPAllTargets=
+AdActorSystemPriority=
+UpDelaySec=
+AdSelect=
+AdActorSystem=
+AdUserPortKey=
+FailOverMACPolicy=
+ARPValidate=
+Mode=
+GratuitousARP=
+ARPIPTargets=
+MinLinks=
+LACPTransmitRate=
+ARPIntervalSec=
+AllSlavesActive=
+DynamicTransmitLoadBalancing=
+[FooOverUDP]
+Protocol=
+Port=
+Encapsulation=
+[Tap]
+MultiQueue=
+OneQueue=
+User=
+Group=
+PacketInfo=
+VNetHeader=
+[IPVLAN]
+Mode=
+Flags=
+[Tun]
+OneQueue=
+MultiQueue=
+PacketInfo=
+Group=
+User=
+[NetDev]
+Kind=
+MACAddress=
+MTUBytes=
+Description=
+Name=
diff --git a/test/fuzz/fuzz-netdev-parser/github-10615 b/test/fuzz/fuzz-netdev-parser/github-10615
new file mode 100644
index 0000000000..1151ebfb47
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/github-10615
@@ -0,0 +1,5 @@
+[NetDev]
+Name=veth99
+Kind=veth
+MACAddress=12:34:56:78:9a:bc
+MACAddress=12:34:56:78:9a:bc
diff --git a/test/fuzz/fuzz-netdev-parser/github-10629 b/test/fuzz/fuzz-netdev-parser/github-10629
new file mode 100644
index 0000000000..e269e3b285
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/github-10629
@@ -0,0 +1,10 @@
+[NetDev]
+Name=vlan99
+Kind=vcan
+
+[VLAN]
+Id=99
+GVRP=true
+MVRP=true
+LooseBinding=true
+ReorderHeader=true
diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-11279 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11279
new file mode 100644
index 0000000000..f7a99bdaa1
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11279
Binary files differ
diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-11280 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11280
new file mode 100644
index 0000000000..33d24990b7
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11280
Binary files differ
diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-11286 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11286
new file mode 100644
index 0000000000..cde4ee8e43
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11286
Binary files differ
diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-11287 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11287
new file mode 100644
index 0000000000..03ab20694d
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11287
Binary files differ
diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-11296 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11296
new file mode 100644
index 0000000000..05d57f702b
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11296
Binary files differ
diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-11297 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11297
new file mode 100644
index 0000000000..f4baa0bf7a
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11297
Binary files differ
diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-11299 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11299
new file mode 100644
index 0000000000..295a1d08ad
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11299
Binary files differ
diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-11324 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11324
new file mode 100644
index 0000000000..dea1dcf6e4
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11324
Binary files differ
diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-11344 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11344
new file mode 100644
index 0000000000..e3744c608c
--- /dev/null
+++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-11344
@@ -0,0 +1,6 @@
+[NetDev]
+Name=v
+Kind=vti
+[Tunnel]
+Local=::
+Local=any \ No newline at end of file
diff --git a/test/fuzz/fuzz-network-parser/21-vlan.network b/test/fuzz/fuzz-network-parser/21-vlan.network
new file mode 100644
index 0000000000..a4fa1decde
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/21-vlan.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+VLAN=vlan99
diff --git a/test/fuzz/fuzz-network-parser/23-active-slave.network b/test/fuzz/fuzz-network-parser/23-active-slave.network
new file mode 100644
index 0000000000..59a65960e9
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/23-active-slave.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Network]
+Bond=bond199
+ActiveSlave=true
diff --git a/test/fuzz/fuzz-network-parser/23-bond199.network b/test/fuzz/fuzz-network-parser/23-bond199.network
new file mode 100644
index 0000000000..31e5d12f75
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/23-bond199.network
@@ -0,0 +1,2 @@
+[Match]
+Name=bond199
diff --git a/test/fuzz/fuzz-network-parser/23-emit-lldp.network b/test/fuzz/fuzz-network-parser/23-emit-lldp.network
new file mode 100644
index 0000000000..de35045388
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/23-emit-lldp.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth-peer
+
+[Network]
+EmitLLDP=yes
diff --git a/test/fuzz/fuzz-network-parser/23-primary-slave.network b/test/fuzz/fuzz-network-parser/23-primary-slave.network
new file mode 100644
index 0000000000..380ae267a2
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/23-primary-slave.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Network]
+Bond=bond199
+PrimarySlave=true
diff --git a/test/fuzz/fuzz-network-parser/23-test1-bond199.network b/test/fuzz/fuzz-network-parser/23-test1-bond199.network
new file mode 100644
index 0000000000..6e7c28dfe7
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/23-test1-bond199.network
@@ -0,0 +1,6 @@
+[Match]
+Name=test1
+
+[Network]
+Bond=bond199
+PrimarySlave=true
diff --git a/test/fuzz/fuzz-network-parser/24-lldp.network b/test/fuzz/fuzz-network-parser/24-lldp.network
new file mode 100644
index 0000000000..fbdfb1b672
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/24-lldp.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth99
+
+[Network]
+LLDP=yes
diff --git a/test/fuzz/fuzz-network-parser/24-search-domain.network b/test/fuzz/fuzz-network-parser/24-search-domain.network
new file mode 100644
index 0000000000..970b130ab0
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/24-search-domain.network
@@ -0,0 +1,7 @@
+[Match]
+Name=dummy98
+
+[Network]
+Address=192.168.42.100
+DNS=192.168.42.1
+Domains= one two three four five six seven eight nine ten
diff --git a/test/fuzz/fuzz-network-parser/25-address-link-section.network b/test/fuzz/fuzz-network-parser/25-address-link-section.network
new file mode 100644
index 0000000000..759e83c325
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-address-link-section.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Link]
+MACAddress=00:01:02:aa:bb:cc
diff --git a/test/fuzz/fuzz-network-parser/25-address-section-miscellaneous.network b/test/fuzz/fuzz-network-parser/25-address-section-miscellaneous.network
new file mode 100644
index 0000000000..3a37d036ce
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-address-section-miscellaneous.network
@@ -0,0 +1,10 @@
+[Match]
+Name=dummy98
+
+[Address]
+Address=10.2.3.4/16
+PreferredLifetime=0
+Scope=link
+
+[Address]
+Address=2001:0db8:0:f101::1/64
diff --git a/test/fuzz/fuzz-network-parser/25-address-section.network b/test/fuzz/fuzz-network-parser/25-address-section.network
new file mode 100644
index 0000000000..d0fae69361
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-address-section.network
@@ -0,0 +1,11 @@
+[Match]
+Name=dummy98
+
+[Address]
+Address=10.2.3.4/16
+Peer=10.2.3.5/16
+Label=32
+
+[Address]
+Address=10.6.7.8/16
+Label=33
diff --git a/test/fuzz/fuzz-network-parser/25-fibrule-port-range.network b/test/fuzz/fuzz-network-parser/25-fibrule-port-range.network
new file mode 100644
index 0000000000..36646ec0ff
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-fibrule-port-range.network
@@ -0,0 +1,11 @@
+[Match]
+Name=test1
+
+[RoutingPolicyRule]
+TypeOfService=0x08
+Table=7
+From= 192.168.100.18
+Priority=111
+SourcePort = 1123-1150
+DestinationPort = 3224-3290
+IPProtocol = tcp
diff --git a/test/fuzz/fuzz-network-parser/25-ipv6-address-label-section.network b/test/fuzz/fuzz-network-parser/25-ipv6-address-label-section.network
new file mode 100644
index 0000000000..945b7dcc45
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-ipv6-address-label-section.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[IPv6AddressLabel]
+Label=4444
+Prefix=2004:da8:1:0::/64
diff --git a/test/fuzz/fuzz-network-parser/25-link-section-unmanaged.network b/test/fuzz/fuzz-network-parser/25-link-section-unmanaged.network
new file mode 100644
index 0000000000..4fe4916031
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-link-section-unmanaged.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Link]
+MACAddress=00:01:02:aa:bb:cc
+Unmanaged=true
diff --git a/test/fuzz/fuzz-network-parser/25-neighbor-section.network b/test/fuzz/fuzz-network-parser/25-neighbor-section.network
new file mode 100644
index 0000000000..dd750dd566
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-neighbor-section.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Neighbor]
+Address=2004:da8:1:0::
+MACAddress=00:00:5e:00:02:00
diff --git a/test/fuzz/fuzz-network-parser/25-route-section.network b/test/fuzz/fuzz-network-parser/25-route-section.network
new file mode 100644
index 0000000000..c9c7a722c5
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-route-section.network
@@ -0,0 +1,8 @@
+[Match]
+Name=dummy98
+
+[Network]
+Address=192.168.0.15/24
+
+[Route]
+Gateway=192.168.0.1
diff --git a/test/fuzz/fuzz-network-parser/25-route-tcp-window-settings.network b/test/fuzz/fuzz-network-parser/25-route-tcp-window-settings.network
new file mode 100644
index 0000000000..e77a721740
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-route-tcp-window-settings.network
@@ -0,0 +1,10 @@
+[Match]
+Name=test1
+
+[Route]
+Destination=192.168.1.1
+InitialCongestionWindow=20
+
+[Route]
+Destination=192.168.1.2
+InitialAdvertisedReceiveWindow=30
diff --git a/test/fuzz/fuzz-network-parser/25-route-type.network b/test/fuzz/fuzz-network-parser/25-route-type.network
new file mode 100644
index 0000000000..9a104137de
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-route-type.network
@@ -0,0 +1,14 @@
+[Match]
+Name=dummy98
+
+[Route]
+Type=blackhole
+Destination=202.54.1.2
+
+[Route]
+Type=unreachable
+Destination=202.54.1.3
+
+[Route]
+Type=prohibit
+Destination=202.54.1.4
diff --git a/test/fuzz/fuzz-network-parser/25-sysctl.network b/test/fuzz/fuzz-network-parser/25-sysctl.network
new file mode 100644
index 0000000000..2452fb7e85
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/25-sysctl.network
@@ -0,0 +1,10 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPForward=true
+IPv6PrivacyExtensions=true
+IPv6DuplicateAddressDetection=3
+IPv6HopLimit=5
+IPv4ProxyARP=true
+IPv6ProxyNDP=true
diff --git a/test/fuzz/fuzz-network-parser/26-bridge-slave-interface-1.network b/test/fuzz/fuzz-network-parser/26-bridge-slave-interface-1.network
new file mode 100644
index 0000000000..81b372fb6d
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/26-bridge-slave-interface-1.network
@@ -0,0 +1,12 @@
+[Match]
+Name=dummy98
+
+[Network]
+Bridge=bridge99
+
+[Bridge]
+Cost=400
+HairPin = true
+FastLeave = true
+UnicastFlood = true
+MulticastToUnicast = true
diff --git a/test/fuzz/fuzz-network-parser/26-bridge-slave-interface-2.network b/test/fuzz/fuzz-network-parser/26-bridge-slave-interface-2.network
new file mode 100644
index 0000000000..45ec2de999
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/26-bridge-slave-interface-2.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+Bridge=bridge99
diff --git a/test/fuzz/fuzz-network-parser/bridge99.network b/test/fuzz/fuzz-network-parser/bridge99.network
new file mode 100644
index 0000000000..39e48ce2e1
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/bridge99.network
@@ -0,0 +1,6 @@
+[Match]
+Name=bridge99
+
+[Network]
+Address=192.168.0.15/24
+Gateway=192.168.0.1
diff --git a/test/fuzz/fuzz-network-parser/configure-without-carrier.network b/test/fuzz/fuzz-network-parser/configure-without-carrier.network
new file mode 100644
index 0000000000..5bd9d7e84a
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/configure-without-carrier.network
@@ -0,0 +1,7 @@
+[Match]
+Name=test1
+
+[Network]
+Address=192.168.0.15/24
+Gateway=192.168.0.1
+ConfigureWithoutCarrier=true
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-anonymize.network b/test/fuzz/fuzz-network-parser/dhcp-client-anonymize.network
new file mode 100644
index 0000000000..a910cba0f7
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-anonymize.network
@@ -0,0 +1,16 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCP]
+Anonymize=true
+UseMTU=true
+UseRoutes=true
+SendHostname=true
+UseHostname=true
+Hostname=test-hostname
+ClientIdentifier=mac
+VendorClassIdentifier=SusantVendorTest
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-critical-connection.network b/test/fuzz/fuzz-network-parser/dhcp-client-critical-connection.network
new file mode 100644
index 0000000000..0e65dec0ae
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-critical-connection.network
@@ -0,0 +1,9 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCP]
+CriticalConnection=true
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-ipv4-dhcp-settings.network b/test/fuzz/fuzz-network-parser/dhcp-client-ipv4-dhcp-settings.network
new file mode 100644
index 0000000000..5c4ca22ab7
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-ipv4-dhcp-settings.network
@@ -0,0 +1,15 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCP]
+UseMTU=true
+UseRoutes=true
+SendHostname=true
+UseHostname=true
+Hostname=test-hostname
+ClientIdentifier=mac
+VendorClassIdentifier=SusantVendorTest
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-ipv4-only-ipv6-disabled.network b/test/fuzz/fuzz-network-parser/dhcp-client-ipv4-only-ipv6-disabled.network
new file mode 100644
index 0000000000..c980bf9fca
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-ipv4-only-ipv6-disabled.network
@@ -0,0 +1,6 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-ipv4-only.network b/test/fuzz/fuzz-network-parser/dhcp-client-ipv4-only.network
new file mode 100644
index 0000000000..9bc019a674
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-ipv4-only.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-ipv6-only.network b/test/fuzz/fuzz-network-parser/dhcp-client-ipv6-only.network
new file mode 100644
index 0000000000..1f70c3b86d
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-ipv6-only.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv6
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-ipv6-rapid-commit.network b/test/fuzz/fuzz-network-parser/dhcp-client-ipv6-rapid-commit.network
new file mode 100644
index 0000000000..52fa58729e
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-ipv6-rapid-commit.network
@@ -0,0 +1,8 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv6
+
+[DHCP]
+RapidCommit=false
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-listen-port.network b/test/fuzz/fuzz-network-parser/dhcp-client-listen-port.network
new file mode 100644
index 0000000000..73ac364630
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-listen-port.network
@@ -0,0 +1,8 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
+
+[DHCP]
+ListenPort=5555
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-route-metric.network b/test/fuzz/fuzz-network-parser/dhcp-client-route-metric.network
new file mode 100644
index 0000000000..aa04aaef33
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-route-metric.network
@@ -0,0 +1,10 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCP]
+UseRoutes=true
+RouteMetric=24
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-route-table.network b/test/fuzz/fuzz-network-parser/dhcp-client-route-table.network
new file mode 100644
index 0000000000..75f16aa22d
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-route-table.network
@@ -0,0 +1,10 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCP]
+UseRoutes=true
+RouteTable=12
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client-timezone-router.network b/test/fuzz/fuzz-network-parser/dhcp-client-timezone-router.network
new file mode 100644
index 0000000000..6316f9cf59
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client-timezone-router.network
@@ -0,0 +1,8 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=false
+DHCP=ipv4
+UseRoutes=true
+UseTimezone=true
diff --git a/test/fuzz/fuzz-network-parser/dhcp-client.network b/test/fuzz/fuzz-network-parser/dhcp-client.network
new file mode 100644
index 0000000000..5629bc48e4
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-client.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
diff --git a/test/fuzz/fuzz-network-parser/dhcp-server-timezone-router.network b/test/fuzz/fuzz-network-parser/dhcp-server-timezone-router.network
new file mode 100644
index 0000000000..3ebbf05d3e
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-server-timezone-router.network
@@ -0,0 +1,13 @@
+[Match]
+Name=veth-peer
+
+[Network]
+IPv6AcceptRA=false
+Address=192.168.5.1/24
+DHCPServer=yes
+
+[DHCPServer]
+PoolOffset=10
+PoolSize=50
+EmitRouter=yes
+Timezone=Europe/Berlin
diff --git a/test/fuzz/fuzz-network-parser/dhcp-server-veth-peer.network b/test/fuzz/fuzz-network-parser/dhcp-server-veth-peer.network
new file mode 100644
index 0000000000..7b38e72b7e
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-server-veth-peer.network
@@ -0,0 +1,6 @@
+[Match]
+Name=veth-peer
+
+[Network]
+Address=2600::1
+Address=192.168.5.1/24
diff --git a/test/fuzz/fuzz-network-parser/dhcp-server.network b/test/fuzz/fuzz-network-parser/dhcp-server.network
new file mode 100644
index 0000000000..9e49691a9b
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-server.network
@@ -0,0 +1,12 @@
+[Match]
+Name=veth-peer
+
+[Network]
+Address=192.168.5.1/24
+DHCPServer=yes
+
+[DHCPServer]
+PoolOffset=10
+PoolSize=50
+DNS=192.168.5.1
+NTP=192.168.5.1
diff --git a/test/fuzz/fuzz-network-parser/dhcp-v4-server-veth-peer.network b/test/fuzz/fuzz-network-parser/dhcp-v4-server-veth-peer.network
new file mode 100644
index 0000000000..5c91d65432
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/dhcp-v4-server-veth-peer.network
@@ -0,0 +1,6 @@
+[Match]
+Name=veth-peer
+
+[Network]
+Address=192.168.0.1
+Address=192.168.5.1
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
new file mode 100644
index 0000000000..209132f239
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -0,0 +1,196 @@
+[Bridge]
+Cost=
+UseBPDU=
+HairPin=
+UnicastFlood=
+FastLeave=
+Priority=
+AllowPortToBeRoot=
+MulticastToUnicast=
+[Match]
+KernelVersion=
+Type=
+Driver=
+Architecture=
+Path=
+Name=
+Virtualization=
+KernelCommandLine=
+Host=
+MACAddress=
+[Link]
+RequiredForOnline=
+ARP=
+AllMulticast=
+Unmanaged=
+MTUBytes=
+Multicast=
+MACAddress=
+[BridgeFDB]
+VLANId=
+MACAddress=
+[DHCP]
+UseDomains=
+UseRoutes=
+IAID=
+UserClass=
+UseNTP=
+RapidCommit=
+ForceDHCPv6PDOtherInformation=
+UseMTU=
+UseDomainName=
+RouteMetric=
+SendHostname=
+Anonymize=
+VendorClassIdentifier=
+Hostname=
+DUIDType=
+UseHostname=
+UseDNS=
+CriticalConnection=
+DUIDRawData=
+RequestBroadcast=
+ClientIdentifier=
+ListenPort=
+UseTimezone=
+RouteTable=
+[Route]
+Destination=
+Protocol=
+Table=
+Gateway=
+InitialAdvertisedReceiveWindow=
+GatewayOnlink=
+Type=
+InitialCongestionWindow=
+IPv6Preference=
+PreferredSource=
+Scope=
+MTUBytes=
+QuickAck=
+Source=
+Metric=
+[Network]
+IPv6DuplicateAddressDetection=
+IPMasquerade=
+ProxyARP=
+PrimarySlave=
+IPv4LLRoute=
+Address=
+IPv6ProxyNDPAddress=
+IPv6AcceptRA=
+IPv6AcceptRouterAdvertisements=
+DNSSECNegativeTrustAnchors=
+MACVTAP=
+IPv6PrivacyExtensions=
+MACVLAN=
+MulticastDNS=
+LLDP=
+Tunnel=
+Gateway=
+IPv4LL=
+IPVLAN=
+EmitLLDP=
+IPv6MTUBytes=
+IPv4ProxyARP=
+Bridge=
+DNSOverTLS=
+Bond=
+IPv6ProxyNDP=
+DNS=
+ActiveSlave=
+LLMNR=
+DNSSEC=
+IPv6HopLimit=
+IPForward=
+IPv6Token=
+Description=
+VXLAN=
+LinkLocalAddressing=
+ConfigureWithoutCarrier=
+NTP=
+DHCP=
+Domains=
+IPv6PrefixDelegation=
+VLAN=
+DHCPServer=
+BindCarrier=
+VRF=
+[IPv6Prefix]
+Prefix=
+OnLink=
+PreferredLifetimeSec=
+AddressAutoconfiguration=
+ValidLifetimeSec=
+[BridgeVLAN]
+EgressUntagged=
+VLAN=
+PVID=
+[CAN]
+SamplePoint=
+BitRate=
+RestartSec=
+[Address]
+DuplicateAddressDetection=
+AutoJoin=
+PreferredLifetime=
+Address=
+Scope=
+HomeAddress=
+PrefixRoute=
+ManageTemporaryAddress=
+Broadcast=
+Peer=
+Label=
+[RoutingPolicyRule]
+Table=
+IncomingInterface=
+To=
+OutgoingInterface=
+From=
+TypeOfService=
+Priority=
+FirewallMark=
+SourcePort=
+DestinationPort=
+IPProtocol=
+InvertRule=
+[IPv6PrefixDelegation]
+RouterPreference=
+DNSLifetimeSec=
+DNS=
+DNSDefaultRoute=
+RouterLifetimeSec=
+Domains=
+EmitDNS=
+EmitDomains=
+Managed=
+OtherInformation=
+[Neighbor]
+Address=
+MacAddress=
+[IPv6AddressLabel]
+Label=
+Prefix=
+[IPv6AcceptRA]
+UseDomains=
+RouteTable=
+UseDNS=
+[DHCPServer]
+EmitNTP=
+PoolSize=
+PoolOffset=
+Timezone=
+EmitDNS=
+NTP=
+EmitRouter=
+MaxLeaseTimeSec=
+DefaultLeaseTimeSec=
+EmitTimezone=
+DNS=
+[DHCPv4]
+UseHostname=
+UseMTU=
+UseDomainName=
+CriticalConnection=
+UseDNS=
diff --git a/test/fuzz/fuzz-network-parser/github-10639 b/test/fuzz/fuzz-network-parser/github-10639
new file mode 100644
index 0000000000..530a29b241
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/github-10639
@@ -0,0 +1,8 @@
+[Match]
+Name=dummy98
+
+[Network]
+Address=192.168.0.15/2424
+
+[Rou]
+Gateway=192.136.0.1
diff --git a/test/fuzz/fuzz-network-parser/gretap.network b/test/fuzz/fuzz-network-parser/gretap.network
new file mode 100644
index 0000000000..88b9250349
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/gretap.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=gretap99
diff --git a/test/fuzz/fuzz-network-parser/gretun.network b/test/fuzz/fuzz-network-parser/gretun.network
new file mode 100644
index 0000000000..376074cb1b
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/gretun.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=gretun99
diff --git a/test/fuzz/fuzz-network-parser/ip6gretap.network b/test/fuzz/fuzz-network-parser/ip6gretap.network
new file mode 100644
index 0000000000..cad0bae04a
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/ip6gretap.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=ip6gretap99
diff --git a/test/fuzz/fuzz-network-parser/ip6tnl.network b/test/fuzz/fuzz-network-parser/ip6tnl.network
new file mode 100644
index 0000000000..41e3448495
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/ip6tnl.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=ip6tnl99
diff --git a/test/fuzz/fuzz-network-parser/ipip.network b/test/fuzz/fuzz-network-parser/ipip.network
new file mode 100644
index 0000000000..4ce6714904
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/ipip.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=ipiptun99
diff --git a/test/fuzz/fuzz-network-parser/ipv6-prefix-veth.network b/test/fuzz/fuzz-network-parser/ipv6-prefix-veth.network
new file mode 100644
index 0000000000..3d2acecde2
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/ipv6-prefix-veth.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
diff --git a/test/fuzz/fuzz-network-parser/ipv6-prefix.network b/test/fuzz/fuzz-network-parser/ipv6-prefix.network
new file mode 100644
index 0000000000..c1932a84d3
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/ipv6-prefix.network
@@ -0,0 +1,10 @@
+[Match]
+Name=veth-peer
+
+[Network]
+IPv6PrefixDelegation=yes
+
+[IPv6Prefix]
+Prefix=2002:da8:1:0::/64
+PreferredLifetimeSec=1000s
+ValidLifetimeSec=2100s
diff --git a/test/fuzz/fuzz-network-parser/ipvlan.network b/test/fuzz/fuzz-network-parser/ipvlan.network
new file mode 100644
index 0000000000..d053220550
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/ipvlan.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+IPVLAN=ipvlan99
diff --git a/test/fuzz/fuzz-network-parser/macvlan.network b/test/fuzz/fuzz-network-parser/macvlan.network
new file mode 100644
index 0000000000..a41c1f916c
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/macvlan.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+MACVLAN=macvlan99
diff --git a/test/fuzz/fuzz-network-parser/macvtap.network b/test/fuzz/fuzz-network-parser/macvtap.network
new file mode 100644
index 0000000000..6ee99ab9ce
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/macvtap.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+MACVTAP=macvtap99
diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-11285 b/test/fuzz/fuzz-network-parser/oss-fuzz-11285
new file mode 100644
index 0000000000..e0aad02cdb
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/oss-fuzz-11285
@@ -0,0 +1,2 @@
+[IPv6PrefixDelegation]
+Domains=m \ No newline at end of file
diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-11302 b/test/fuzz/fuzz-network-parser/oss-fuzz-11302
new file mode 100644
index 0000000000..0ef0dccbd0
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/oss-fuzz-11302
@@ -0,0 +1,2 @@
+[Network]
+IPv6MTUBytes=7K \ No newline at end of file
diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-11314 b/test/fuzz/fuzz-network-parser/oss-fuzz-11314
new file mode 100644
index 0000000000..2fb68e9862
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/oss-fuzz-11314
@@ -0,0 +1,5 @@
+
+[Network]
+IPv6MTUBytes=6M
+
+Bond=
diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-11345 b/test/fuzz/fuzz-network-parser/oss-fuzz-11345
new file mode 100644
index 0000000000..b097fcd05f
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/oss-fuzz-11345
@@ -0,0 +1,2 @@
+[IPv6PrefixDelegation]
+DNS=:: \ No newline at end of file
diff --git a/test/fuzz/fuzz-network-parser/routing-policy-rule.network b/test/fuzz/fuzz-network-parser/routing-policy-rule.network
new file mode 100644
index 0000000000..46b87c5a9a
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/routing-policy-rule.network
@@ -0,0 +1,10 @@
+[Match]
+Name=test1
+
+[RoutingPolicyRule]
+TypeOfService=0x08
+Table=7
+From= 192.168.100.18
+Priority=111
+IncomingInterface=test1
+OutgoingInterface=test1
diff --git a/test/fuzz/fuzz-network-parser/sit.network b/test/fuzz/fuzz-network-parser/sit.network
new file mode 100644
index 0000000000..84e5af0ff0
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/sit.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=sittun99
diff --git a/test/fuzz/fuzz-network-parser/test-static.network b/test/fuzz/fuzz-network-parser/test-static.network
new file mode 100644
index 0000000000..636c55c8e3
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/test-static.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Network]
+Address=192.168.0.15/24
+Gateway=192.168.0.1
diff --git a/test/fuzz/fuzz-network-parser/vti.network b/test/fuzz/fuzz-network-parser/vti.network
new file mode 100644
index 0000000000..7fbad6a82d
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/vti.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=vtitun99
diff --git a/test/fuzz/fuzz-network-parser/vti6.network b/test/fuzz/fuzz-network-parser/vti6.network
new file mode 100644
index 0000000000..49a9d11fff
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/vti6.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=vti6tun99
diff --git a/test/fuzz/fuzz-network-parser/vxlan.network b/test/fuzz/fuzz-network-parser/vxlan.network
new file mode 100644
index 0000000000..80b405574c
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/vxlan.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+VXLAN=vxlan99
diff --git a/test/fuzz/fuzz-udev-rules/50-udev-default.rules b/test/fuzz/fuzz-udev-rules/50-udev-default.rules
new file mode 100644
index 0000000000..8e06c13388
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/50-udev-default.rules
@@ -0,0 +1,86 @@
+# do not edit this file, it will be overwritten on update
+
+# run a command on remove events
+ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}"
+ACTION=="remove", GOTO="default_end"
+
+SUBSYSTEM=="virtio-ports", KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-ports/$attr{name}"
+
+# select "system RTC" or just use the first one
+SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc"
+SUBSYSTEM=="rtc", KERNEL=="rtc0", SYMLINK+="rtc", OPTIONS+="link_priority=-100"
+
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
+ENV{MODALIAS}!="", IMPORT{builtin}="hwdb --subsystem=$env{SUBSYSTEM}"
+
+ACTION!="add", GOTO="default_end"
+
+SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666"
+SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666"
+SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620"
+SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620"
+SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620"
+SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620"
+SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty"
+KERNEL=="tty[A-Z]*[0-9]|ttymxc[0-9]*|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout"
+
+SUBSYSTEM=="mem", KERNEL=="mem|kmem|port", GROUP="kmem", MODE="0640"
+
+SUBSYSTEM=="input", GROUP="input"
+SUBSYSTEM=="input", KERNEL=="js[0-9]*", MODE="0664"
+
+SUBSYSTEM=="video4linux", GROUP="video"
+SUBSYSTEM=="graphics", GROUP="video"
+SUBSYSTEM=="drm", KERNEL!="renderD*", GROUP="video"
+SUBSYSTEM=="dvb", GROUP="video"
+SUBSYSTEM=="media", GROUP="video"
+SUBSYSTEM=="cec", GROUP="video"
+
+SUBSYSTEM=="drm", KERNEL=="renderD*", GROUP="render", MODE="0666"
+SUBSYSTEM=="kfd", GROUP="render", MODE="0666"
+
+SUBSYSTEM=="sound", GROUP="audio", \
+ OPTIONS+="static_node=snd/seq", OPTIONS+="static_node=snd/timer"
+
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664"
+
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", GROUP="video"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", GROUP="video"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", GROUP="video"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", GROUP="video"
+
+KERNEL=="parport[0-9]*", GROUP="lp"
+SUBSYSTEM=="printer", KERNEL=="lp*", GROUP="lp"
+SUBSYSTEM=="ppdev", GROUP="lp"
+KERNEL=="lp[0-9]*", GROUP="lp"
+KERNEL=="irlpt[0-9]*", GROUP="lp"
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", GROUP="lp"
+
+SUBSYSTEM=="block", GROUP="disk"
+SUBSYSTEM=="block", KERNEL=="sr[0-9]*", GROUP="cdrom"
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", GROUP="cdrom"
+KERNEL=="sch[0-9]*", GROUP="cdrom"
+KERNEL=="pktcdvd[0-9]*", GROUP="cdrom"
+KERNEL=="pktcdvd", GROUP="cdrom"
+
+SUBSYSTEM=="scsi_generic|scsi_tape", SUBSYSTEMS=="scsi", ATTRS{type}=="1|8", GROUP="tape"
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="0", GROUP="disk"
+KERNEL=="qft[0-9]*|nqft[0-9]*|zqft[0-9]*|nzqft[0-9]*|rawqft[0-9]*|nrawqft[0-9]*", GROUP="disk"
+KERNEL=="loop-control", GROUP="disk", OPTIONS+="static_node=loop-control"
+KERNEL=="btrfs-control", GROUP="disk"
+KERNEL=="rawctl", GROUP="disk"
+SUBSYSTEM=="raw", KERNEL=="raw[0-9]*", GROUP="disk"
+SUBSYSTEM=="aoe", GROUP="disk", MODE="0220"
+SUBSYSTEM=="aoe", KERNEL=="err", MODE="0440"
+
+KERNEL=="rfkill", MODE="0664"
+KERNEL=="tun", MODE="0666", OPTIONS+="static_node=net/tun"
+
+KERNEL=="fuse", MODE="0666", OPTIONS+="static_node=fuse"
+
+# The static_node is required on s390x and ppc (they are using MODULE_ALIAS)
+KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"
+
+SUBSYSTEM=="ptp", ATTR{clock_name}=="KVM virtual PTP", SYMLINK += "ptp_kvm"
+
+LABEL="default_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-block.rules b/test/fuzz/fuzz-udev-rules/60-block.rules
new file mode 100644
index 0000000000..343fc06f85
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-block.rules
@@ -0,0 +1,11 @@
+# do not edit this file, it will be overwritten on update
+
+# enable in-kernel media-presence polling
+ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_poll_msecs}=="0", \
+ ATTR{parameters/events_dfl_poll_msecs}="2000"
+
+# forward scsi device event to corresponding block device
+ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change"
+
+# watch metadata changes, caused by tools closing the device node which was opened for writing
+ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*|pmem*|mmcblk*", OPTIONS+="watch"
diff --git a/test/fuzz/fuzz-udev-rules/60-cdrom_id.rules b/test/fuzz/fuzz-udev-rules/60-cdrom_id.rules
new file mode 100644
index 0000000000..288f8ce2f9
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-cdrom_id.rules
@@ -0,0 +1,29 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="cdrom_end"
+SUBSYSTEM!="block", GOTO="cdrom_end"
+KERNEL!="sr[0-9]*|vdisk*|xvd*", GOTO="cdrom_end"
+ENV{DEVTYPE}!="disk", GOTO="cdrom_end"
+
+# unconditionally tag device as CDROM
+KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1"
+
+# stop automatically any mount units bound to the device if the media eject
+# button is pressed.
+ENV{ID_CDROM}=="1", ENV{SYSTEMD_MOUNT_DEVICE_BOUND}="1"
+
+# media eject button pressed
+ENV{DISK_EJECT_REQUEST}=="?*", RUN+="cdrom_id --eject-media $devnode", GOTO="cdrom_end"
+
+# import device and media properties and lock tray to
+# enable the receiving of media eject button events
+IMPORT{program}="cdrom_id --lock-media $devnode"
+
+# ejecting a CD does not remove the device node, so mark the systemd device
+# unit as inactive while there is no medium; this automatically cleans up of
+# stale mounts after ejecting
+ENV{DISK_MEDIA_CHANGE}=="?*", ENV{ID_CDROM_MEDIA}!="?*", ENV{SYSTEMD_READY}="0"
+
+KERNEL=="sr0", SYMLINK+="cdrom", OPTIONS+="link_priority=-100"
+
+LABEL="cdrom_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-drm.rules b/test/fuzz/fuzz-udev-rules/60-drm.rules
new file mode 100644
index 0000000000..f7f3435d50
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-drm.rules
@@ -0,0 +1,8 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="remove", SUBSYSTEM=="drm", SUBSYSTEMS=="pci|usb|platform", IMPORT{builtin}="path_id"
+
+# by-path
+ENV{ID_PATH}=="?*", KERNEL=="card*", SYMLINK+="dri/by-path/$env{ID_PATH}-card"
+ENV{ID_PATH}=="?*", KERNEL=="controlD*", SYMLINK+="dri/by-path/$env{ID_PATH}-control"
+ENV{ID_PATH}=="?*", KERNEL=="renderD*", SYMLINK+="dri/by-path/$env{ID_PATH}-render"
diff --git a/test/fuzz/fuzz-udev-rules/60-evdev.rules b/test/fuzz/fuzz-udev-rules/60-evdev.rules
new file mode 100644
index 0000000000..e5e608acd3
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-evdev.rules
@@ -0,0 +1,23 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="evdev_end"
+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"
+
+# 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"
+
+# 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"
+
+# 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"
+
+LABEL="evdev_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-input-id.rules b/test/fuzz/fuzz-udev-rules/60-input-id.rules
new file mode 100644
index 0000000000..bb8a812d1b
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-input-id.rules
@@ -0,0 +1,8 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="id_input_end"
+
+SUBSYSTEM=="input", ENV{ID_INPUT}=="", IMPORT{builtin}="input_id"
+SUBSYSTEM=="input", IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=id-input:modalias:"
+
+LABEL="id_input_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-alsa.rules b/test/fuzz/fuzz-udev-rules/60-persistent-alsa.rules
new file mode 100644
index 0000000000..8154e2dbb5
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-persistent-alsa.rules
@@ -0,0 +1,14 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="persistent_alsa_end"
+SUBSYSTEM!="sound", GOTO="persistent_alsa_end"
+KERNEL!="controlC[0-9]*", GOTO="persistent_alsa_end"
+
+SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id"
+ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}"
+ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
+
+IMPORT{builtin}="path_id"
+ENV{ID_PATH}=="?*", SYMLINK+="snd/by-path/$env{ID_PATH}"
+
+LABEL="persistent_alsa_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-input.rules b/test/fuzz/fuzz-udev-rules/60-persistent-input.rules
new file mode 100644
index 0000000000..255547d906
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-persistent-input.rules
@@ -0,0 +1,42 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="persistent_input_end"
+SUBSYSTEM!="input", GOTO="persistent_input_end"
+SUBSYSTEMS=="bluetooth", ENV{ID_BUS}="bluetooth", GOTO="persistent_input_end"
+# Bluetooth devices don't always have the bluetooth subsystem
+ATTRS{id/bustype}=="0005", ENV{ID_BUS}="bluetooth", GOTO="persistent_input_end"
+SUBSYSTEMS=="rmi4", ENV{ID_BUS}="rmi"
+SUBSYSTEMS=="serio", ENV{ID_BUS}="i8042"
+
+SUBSYSTEMS=="usb", ENV{ID_BUS}=="", IMPORT{builtin}="usb_id"
+
+# determine class name for persistent symlinks
+ENV{ID_INPUT_KEYBOARD}=="?*", ENV{.INPUT_CLASS}="kbd"
+ENV{ID_INPUT_MOUSE}=="?*", ENV{.INPUT_CLASS}="mouse"
+ENV{ID_INPUT_TOUCHPAD}=="?*", ENV{.INPUT_CLASS}="mouse"
+ENV{ID_INPUT_TABLET}=="?*", ENV{.INPUT_CLASS}="mouse"
+ENV{ID_INPUT_JOYSTICK}=="?*", ENV{.INPUT_CLASS}="joystick"
+DRIVERS=="pcspkr", ENV{.INPUT_CLASS}="spkr"
+ATTRS{name}=="*dvb*|*DVB*|* IR *", ENV{.INPUT_CLASS}="ir"
+
+# fill empty serial number
+ENV{.INPUT_CLASS}=="?*", ENV{ID_SERIAL}=="", ENV{ID_SERIAL}="noserial"
+
+# by-id links
+KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="|00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{.INPUT_CLASS}"
+KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-$env{.INPUT_CLASS}"
+KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="|00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-$env{.INPUT_CLASS}"
+KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-event-$env{.INPUT_CLASS}"
+# allow empty class for USB devices, by appending the interface number
+SUBSYSTEMS=="usb", ENV{ID_BUS}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", ATTRS{bInterfaceNumber}=="?*", \
+ SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-if$attr{bInterfaceNumber}"
+
+# by-path
+SUBSYSTEMS=="pci|usb|platform|acpi", IMPORT{builtin}="path_id"
+ENV{ID_PATH}=="?*", KERNEL=="mouse*|js*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-$env{.INPUT_CLASS}"
+ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-event-$env{.INPUT_CLASS}"
+# allow empty class for platform and usb devices; platform supports only a single interface that way
+SUBSYSTEMS=="usb|platform", ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", \
+ SYMLINK+="input/by-path/$env{ID_PATH}-event"
+
+LABEL="persistent_input_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules b/test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules
new file mode 100644
index 0000000000..0136140a46
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules
@@ -0,0 +1,36 @@
+# do not edit this file, it will be overwritten on update
+
+# persistent storage links: /dev/tape/{by-id,by-path}
+
+ACTION=="remove", GOTO="persistent_storage_tape_end"
+ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_tape_end"
+
+# type 8 devices are "Medium Changers"
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \
+ SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}"
+
+# iSCSI devices from the same host have all the same ID_SERIAL,
+# but additionally a property named ID_SCSI_SERIAL.
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", ENV{ID_SCSI_SERIAL}=="?*", \
+ SYMLINK+="tape/by-id/scsi-$env{ID_SCSI_SERIAL}"
+
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{builtin}="path_id", \
+ SYMLINK+="tape/by-path/$env{ID_PATH}-changer"
+
+SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end"
+
+KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394"
+KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id"
+KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi"
+KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
+KERNEL=="st*[0-9]", ENV{ID_SCSI_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SCSI_SERIAL}"
+KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst"
+KERNEL=="nst*[0-9]", ENV{ID_SCSI_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SCSI_SERIAL}-nst"
+
+# by-path (parent device path)
+KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{builtin}="path_id"
+KERNEL=="st*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}"
+KERNEL=="nst*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}-nst"
+
+LABEL="persistent_storage_tape_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-storage.rules b/test/fuzz/fuzz-udev-rules/60-persistent-storage.rules
new file mode 100644
index 0000000000..1d8880ef02
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-persistent-storage.rules
@@ -0,0 +1,109 @@
+# do not edit this file, it will be overwritten on update
+
+# persistent storage links: /dev/disk/{by-id,by-uuid,by-label,by-path}
+# scheme based on "Linux persistent device names", 2004, Hannes Reinecke <hare@suse.de>
+
+ACTION=="remove", GOTO="persistent_storage_end"
+ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_end"
+
+SUBSYSTEM!="block", GOTO="persistent_storage_end"
+KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|scm*|pmem*|nbd*|zd*", GOTO="persistent_storage_end"
+
+# ignore partitions that span the entire disk
+TEST=="whole_disk", GOTO="persistent_storage_end"
+
+# for partitions import parent information
+ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*"
+
+# NVMe
+KERNEL=="nvme*[0-9]n*[0-9]", ATTR{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}-part%n"
+
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{wwid}=="?*", ENV{ID_WWN}="$attr{wwid}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{model}=="?*", ENV{ID_MODEL}="$attr{model}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{firmware_rev}=="?*", ENV{ID_REVISION}="$attr{firmware_rev}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", \
+ ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}"
+
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{model}=="?*", ENV{ID_MODEL}="$attr{model}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{firmware_rev}=="?*", ENV{ID_REVISION}="$attr{firmware_rev}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", \
+ ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}-part%n"
+
+# virtio-blk
+KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}"
+KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n"
+
+# ATA
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode"
+
+# ATAPI devices (SPC-3 or later)
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode"
+
+# Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures)
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode"
+
+# Fall back usb_id for USB devices
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+
+# SCSI devices
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi"
+KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss"
+KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
+KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n"
+
+# PMEM devices
+KERNEL=="pmem*", ENV{DEVTYPE}=="disk", ATTRS{uuid}=="?*", SYMLINK+="disk/by-id/pmem-$attr{uuid}"
+
+# FireWire
+KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}"
+KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}-part%n"
+
+# MMC
+KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", \
+ ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}"
+KERNEL=="mmcblk[0-9]p[0-9]*", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n"
+
+# UBI-MTD
+SUBSYSTEM=="ubi", KERNEL=="ubi*_*", ATTRS{mtd_num}=="*", SYMLINK+="ubi_mtd%s{mtd_num}_%s{name}"
+
+# Memstick
+KERNEL=="msblk[0-9]|mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", \
+ ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}"
+KERNEL=="msblk[0-9]p[0-9]|mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n"
+
+# by-path
+ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id"
+KERNEL=="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-boot%n"
+KERNEL!="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}"
+ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n"
+
+# legacy virtio-pci by-path links (deprecated)
+KERNEL=="vd*[!0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}"
+KERNEL=="vd*[0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}-part%n"
+
+# probe filesystem metadata of optical drives which have a media inserted
+KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \
+ IMPORT{builtin}="blkid --offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}"
+# single-session CDs do not have ID_CDROM_MEDIA_SESSION_LAST_OFFSET
+KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="", \
+ IMPORT{builtin}="blkid --noraid"
+
+# probe filesystem metadata of disks
+KERNEL!="sr*", IMPORT{builtin}="blkid"
+
+# by-label/by-uuid links (filesystem metadata)
+ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
+ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
+
+# by-id (World Wide Name)
+ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}"
+ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n"
+
+# by-partlabel/by-partuuid links (partition metadata)
+ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}"
+ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}"
+
+LABEL="persistent_storage_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-v4l.rules b/test/fuzz/fuzz-udev-rules/60-persistent-v4l.rules
new file mode 100644
index 0000000000..93c5ee8c27
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-persistent-v4l.rules
@@ -0,0 +1,20 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="persistent_v4l_end"
+SUBSYSTEM!="video4linux", GOTO="persistent_v4l_end"
+ENV{MAJOR}=="", GOTO="persistent_v4l_end"
+
+IMPORT{program}="v4l_id $devnode"
+
+SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+KERNEL=="video*", ENV{ID_SERIAL}=="?*", SYMLINK+="v4l/by-id/$env{ID_BUS}-$env{ID_SERIAL}-video-index$attr{index}"
+
+# check for valid "index" number
+TEST!="index", GOTO="persistent_v4l_end"
+ATTR{index}!="?*", GOTO="persistent_v4l_end"
+
+IMPORT{builtin}="path_id"
+ENV{ID_PATH}=="?*", KERNEL=="video*|vbi*", SYMLINK+="v4l/by-path/$env{ID_PATH}-video-index$attr{index}"
+ENV{ID_PATH}=="?*", KERNEL=="audio*", SYMLINK+="v4l/by-path/$env{ID_PATH}-audio-index$attr{index}"
+
+LABEL="persistent_v4l_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-sensor.rules b/test/fuzz/fuzz-udev-rules/60-sensor.rules
new file mode 100644
index 0000000000..7ad2c36be3
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-sensor.rules
@@ -0,0 +1,18 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="sensor_end"
+
+# device matching the sensor's name and the machine's DMI data for IIO devices
+SUBSYSTEM=="iio", KERNEL=="iio*", SUBSYSTEMS=="usb|i2c", \
+ IMPORT{builtin}="hwdb 'sensor:modalias:$attr{modalias}:$attr{[dmi/id]modalias}'", \
+ GOTO="sensor_end"
+
+SUBSYSTEM=="input", ENV{ID_INPUT_ACCELEROMETER}=="1", SUBSYSTEMS=="acpi", \
+ IMPORT{builtin}="hwdb 'sensor:modalias:acpi:$attr{hid}:$attr{[dmi/id]modalias}'", \
+ GOTO="sensor_end"
+
+SUBSYSTEM=="input", ENV{ID_INPUT_ACCELEROMETER}=="1", SUBSYSTEMS=="platform", \
+ IMPORT{builtin}="hwdb 'sensor:modalias:platform:$id:$attr{[dmi/id]modalias}'", \
+ GOTO="sensor_end"
+
+LABEL="sensor_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-serial.rules b/test/fuzz/fuzz-udev-rules/60-serial.rules
new file mode 100644
index 0000000000..f303e27fd5
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/60-serial.rules
@@ -0,0 +1,26 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="serial_end"
+SUBSYSTEM!="tty", GOTO="serial_end"
+
+SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
+SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
+SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
+
+# /dev/serial/by-path/, /dev/serial/by-id/ for USB devices
+KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end"
+
+SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}"
+
+IMPORT{builtin}="path_id"
+ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="serial/by-path/$env{ID_PATH}"
+ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-path/$env{ID_PATH}-port$env{.ID_PORT}"
+
+IMPORT{builtin}="usb_id"
+ENV{ID_SERIAL}=="", GOTO="serial_end"
+SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}"
+ENV{ID_USB_INTERFACE_NUM}=="", GOTO="serial_end"
+ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}"
+ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}"
+
+LABEL="serial_end"
diff --git a/test/fuzz/fuzz-udev-rules/64-btrfs.rules b/test/fuzz/fuzz-udev-rules/64-btrfs.rules
new file mode 100644
index 0000000000..0fa79df840
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/64-btrfs.rules
@@ -0,0 +1,17 @@
+# do not edit this file, it will be overwritten on update
+
+SUBSYSTEM!="block", GOTO="btrfs_end"
+ACTION=="remove", GOTO="btrfs_end"
+ENV{ID_FS_TYPE}!="btrfs", GOTO="btrfs_end"
+ENV{SYSTEMD_READY}=="0", GOTO="btrfs_end"
+
+# let the kernel know about this btrfs filesystem, and check if it is complete
+IMPORT{builtin}="btrfs ready $devnode"
+
+# mark the device as not ready to be used by the system
+ENV{ID_BTRFS_READY}=="0", ENV{SYSTEMD_READY}="0"
+
+# reconsider pending devices in case when multidevice volume awaits
+ENV{ID_BTRFS_READY}=="1", RUN+="/usr/bin/udevadm trigger -s block -p ID_BTRFS_READY=0"
+
+LABEL="btrfs_end"
diff --git a/test/fuzz/fuzz-udev-rules/70-joystick.rules b/test/fuzz/fuzz-udev-rules/70-joystick.rules
new file mode 100644
index 0000000000..b80d203670
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/70-joystick.rules
@@ -0,0 +1,12 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="joystick_end"
+ENV{ID_INPUT_JOYSTICK}=="", GOTO="joystick_end"
+KERNEL!="event*", GOTO="joystick_end"
+
+# joystick:<bustype>:v<vid>p<pid>:name:<name>:*
+KERNELS=="input*", ENV{ID_BUS}!="", \
+ IMPORT{builtin}="hwdb 'joystick:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
+ GOTO="joystick_end"
+
+LABEL="joystick_end"
diff --git a/test/fuzz/fuzz-udev-rules/70-mouse.rules b/test/fuzz/fuzz-udev-rules/70-mouse.rules
new file mode 100644
index 0000000000..3ea743aff9
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/70-mouse.rules
@@ -0,0 +1,18 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="mouse_end"
+KERNEL!="event*", GOTO="mouse_end"
+ENV{ID_INPUT_MOUSE}=="", GOTO="mouse_end"
+
+# mouse:<subsystem>:v<vid>p<pid>:name:<name>:*
+KERNELS=="input*", ENV{ID_BUS}=="usb", \
+ IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
+ GOTO="mouse_end"
+KERNELS=="input*", ENV{ID_BUS}=="bluetooth", \
+ IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
+ GOTO="mouse_end"
+DRIVERS=="psmouse", SUBSYSTEMS=="serio", \
+ IMPORT{builtin}="hwdb 'mouse:ps2::name:$attr{device/name}:'", \
+ GOTO="mouse_end"
+
+LABEL="mouse_end"
diff --git a/test/fuzz/fuzz-udev-rules/70-touchpad.rules b/test/fuzz/fuzz-udev-rules/70-touchpad.rules
new file mode 100644
index 0000000000..7bede02dec
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/70-touchpad.rules
@@ -0,0 +1,13 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="touchpad_end"
+ENV{ID_INPUT}=="", GOTO="touchpad_end"
+ENV{ID_INPUT_TOUCHPAD}=="", GOTO="touchpad_end"
+KERNEL!="event*", GOTO="touchpad_end"
+
+# touchpad:<subsystem>:v<vid>p<pid>:name:<name>:*
+KERNELS=="input*", ENV{ID_BUS}!="", \
+ IMPORT{builtin}="hwdb 'touchpad:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
+ GOTO="touchpad_end"
+
+LABEL="touchpad_end"
diff --git a/test/fuzz/fuzz-udev-rules/75-net-description.rules b/test/fuzz/fuzz-udev-rules/75-net-description.rules
new file mode 100644
index 0000000000..7e62f8b26b
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/75-net-description.rules
@@ -0,0 +1,14 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="net_end"
+SUBSYSTEM!="net", GOTO="net_end"
+
+IMPORT{builtin}="net_id"
+
+SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
+SUBSYSTEMS=="usb", GOTO="net_end"
+
+SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
+SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
+
+LABEL="net_end"
diff --git a/test/fuzz/fuzz-udev-rules/75-probe_mtd.rules b/test/fuzz/fuzz-udev-rules/75-probe_mtd.rules
new file mode 100644
index 0000000000..8848aeeaed
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/75-probe_mtd.rules
@@ -0,0 +1,7 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add", GOTO="mtd_probe_end"
+
+KERNEL=="mtd*ro", IMPORT{program}="mtd_probe $devnode"
+
+LABEL="mtd_probe_end"
diff --git a/test/fuzz/fuzz-udev-rules/78-sound-card.rules b/test/fuzz/fuzz-udev-rules/78-sound-card.rules
new file mode 100644
index 0000000000..f2fc277396
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/78-sound-card.rules
@@ -0,0 +1,96 @@
+# do not edit this file, it will be overwritten on update
+
+SUBSYSTEM!="sound", GOTO="sound_end"
+
+ACTION=="add|change", KERNEL=="controlC*", ATTR{../uevent}="change"
+ACTION!="change", GOTO="sound_end"
+
+# Ok, we probably need a little explanation here for what the two lines above
+# are good for.
+#
+# The story goes like this: when ALSA registers a new sound card it emits a
+# series of 'add' events to userspace, for the main card device and for all the
+# child device nodes that belong to it. udev relays those to applications,
+# however only maintains the order between father and child, but not between
+# the siblings. The control device node creation can be used as synchronization
+# point. All other devices that belong to a card are created in the kernel
+# before it. However unfortunately due to the fact that siblings are forwarded
+# out of order by udev this fact is lost to applications.
+#
+# OTOH before an application can open a device it needs to make sure that all
+# its device nodes are completely created and set up.
+#
+# As a workaround for this issue we have added the udev rule above which will
+# generate a 'change' event on the main card device from the 'add' event of the
+# card's control device. Due to the ordering semantics of udev this event will
+# only be relayed after all child devices have finished processing properly.
+# When an application needs to listen for appearing devices it can hence look
+# for 'change' events only, and ignore the actual 'add' events.
+#
+# When the application is initialized at the same time as a device is plugged
+# in it may need to figure out if the 'change' event has already been triggered
+# or not for a card. To find that out we store the flag environment variable
+# SOUND_INITIALIZED on the device which simply tells us if the card 'change'
+# event has already been processed.
+
+KERNEL!="card*", GOTO="sound_end"
+
+ENV{SOUND_INITIALIZED}="1"
+
+IMPORT{builtin}="hwdb"
+SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+SUBSYSTEMS=="usb", GOTO="skip_pci"
+
+SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", \
+ ENV{ID_BUS}="firewire", ENV{ID_SERIAL}="$attr{guid}", ENV{ID_SERIAL_SHORT}="$attr{guid}", \
+ ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{model}", \
+ ENV{ID_VENDOR}="$attr{vendor_name}", ENV{ID_MODEL}="$attr{model_name}"
+SUBSYSTEMS=="firewire", GOTO="skip_pci"
+
+SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
+SUBSYSTEMS=="pci", GOTO="skip_pci"
+
+# If we reach here, the device nor any of its parents are USB/PCI/firewire bus devices.
+# If we now find a parent that is a platform device, assume that we're working with
+# an internal sound card.
+SUBSYSTEMS=="platform", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end"
+
+LABEL="skip_pci"
+
+# Define ID_ID if ID_BUS and ID_SERIAL are set. This will work for both
+# USB and firewire.
+ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}"
+ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}"
+
+IMPORT{builtin}="path_id"
+
+# The values used here for $SOUND_FORM_FACTOR and $SOUND_CLASS should be kept
+# in sync with those defined for PulseAudio's src/pulse/proplist.h
+# PA_PROP_DEVICE_FORM_FACTOR, PA_PROP_DEVICE_CLASS properties.
+
+# If the first PCM device of this card has the pcm class 'modem', then the card is a modem
+ATTR{pcmC%nD0p/pcm_class}=="modem", ENV{SOUND_CLASS}="modem", GOTO="sound_end"
+
+# Identify cards on the internal PCI bus as internal
+SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:??.?/sound/*", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end"
+
+# Devices that also support Image/Video interfaces are most likely webcams
+SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACES}=="*:0e????:*", ENV{SOUND_FORM_FACTOR}="webcam", GOTO="sound_end"
+
+# Matching on the model strings is a bit ugly, I admit
+ENV{ID_MODEL}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end"
+ENV{ID_MODEL_FROM_DATABASE}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end"
+
+ENV{ID_MODEL}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end"
+ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end"
+
+ENV{ID_MODEL}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end"
+ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end"
+
+ENV{ID_MODEL}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end"
+ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end"
+
+ENV{ID_MODEL}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end"
+ENV{ID_MODEL_FROM_DATABASE}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end"
+
+LABEL="sound_end"
diff --git a/test/fuzz/fuzz-udev-rules/80-drivers.rules b/test/fuzz/fuzz-udev-rules/80-drivers.rules
new file mode 100644
index 0000000000..16fa5d8e32
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/80-drivers.rules
@@ -0,0 +1,13 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add", GOTO="drivers_end"
+
+ENV{MODALIAS}=="?*", RUN{builtin}+="kmod load $env{MODALIAS}"
+SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", RUN{builtin}+="kmod load tifm_sd"
+SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="MS", RUN{builtin}+="kmod load tifm_ms"
+SUBSYSTEM=="memstick", RUN{builtin}+="kmod load ms_block mspro_block"
+SUBSYSTEM=="i2o", RUN{builtin}+="kmod load i2o_block"
+SUBSYSTEM=="module", KERNEL=="parport_pc", RUN{builtin}+="kmod load ppdev"
+KERNEL=="mtd*ro", ENV{MTD_FTL}=="smartmedia", RUN{builtin}+="kmod load sm_ftl"
+
+LABEL="drivers_end"
diff --git a/test/fuzz/fuzz-udev-rules/80-net-setup-link.rules b/test/fuzz/fuzz-udev-rules/80-net-setup-link.rules
new file mode 100644
index 0000000000..6e411a91f0
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/80-net-setup-link.rules
@@ -0,0 +1,13 @@
+# do not edit this file, it will be overwritten on update
+
+SUBSYSTEM!="net", GOTO="net_setup_link_end"
+
+IMPORT{builtin}="path_id"
+
+ACTION!="add", GOTO="net_setup_link_end"
+
+IMPORT{builtin}="net_setup_link"
+
+NAME=="", ENV{ID_NET_NAME}!="", NAME="$env{ID_NET_NAME}"
+
+LABEL="net_setup_link_end"
diff --git a/test/fuzz/fuzz-udev-rules/99-systemd.rules b/test/fuzz/fuzz-udev-rules/99-systemd.rules
new file mode 100644
index 0000000000..6ae9898103
--- /dev/null
+++ b/test/fuzz/fuzz-udev-rules/99-systemd.rules
@@ -0,0 +1,76 @@
+# 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.
+
+ACTION=="remove", GOTO="systemd_end"
+
+SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*|ttysclp*|sclp_line*|3270/tty[0-9]*", TAG+="systemd"
+KERNEL=="vport*", TAG+="systemd"
+
+SUBSYSTEM=="ubi", TAG+="systemd"
+
+SUBSYSTEM=="block", TAG+="systemd"
+SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0"
+
+# Ignore encrypted devices with no identified superblock on it, since
+# we are probably still calling mke2fs or mkswap on it.
+SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
+
+# add symlink to GPT root disk
+SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root"
+SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks"
+SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", SYMLINK+="gpt-auto-root"
+
+# Ignore raid devices that are not yet assembled and started
+SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", TEST!="md/array_state", ENV{SYSTEMD_READY}="0"
+SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", ATTR{md/array_state}=="|clear|inactive", ENV{SYSTEMD_READY}="0"
+
+# Ignore loop devices that don't have any file attached
+SUBSYSTEM=="block", KERNEL=="loop[0-9]*", ENV{DEVTYPE}=="disk", TEST!="loop/backing_file", ENV{SYSTEMD_READY}="0"
+
+# Ignore nbd devices until the PID file exists (which signals a connected device)
+SUBSYSTEM=="block", KERNEL=="nbd*", ENV{DEVTYPE}=="disk", TEST!="pid", ENV{SYSTEMD_READY}="0"
+
+# We need a hardware independent way to identify network devices. We
+# use the /sys/subsystem/ path for this. Kernel "bus" and "class" names
+# should be treated as one namespace, like udev handles it. This is mostly
+# just an identification string for systemd, so whether the path actually is
+# accessible or not does not matter as long as it is unique and in the
+# filesystem namespace.
+#
+# http://cgit.freedesktop.org/systemd/systemd/tree/src/libudev/libudev-enumerate.c#n955
+
+SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/$name"
+SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k"
+
+SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}+="bluetooth.target"
+ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target"
+SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target"
+
+SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target"
+SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target"
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target"
+
+# Apply sysctl variables to network devices (and only to those) as they appear.
+ACTION=="add", SUBSYSTEM=="net", KERNEL!="lo", RUN+="/usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/$name --prefix=/net/ipv4/neigh/$name --prefix=/net/ipv6/conf/$name --prefix=/net/ipv6/neigh/$name"
+
+# Pull in backlight save/restore for all backlight devices and
+# keyboard backlights
+SUBSYSTEM=="backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@backlight:$name.service"
+SUBSYSTEM=="leds", KERNEL=="*kbd_backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@leds:$name.service"
+
+# Pull in rfkill save/restore for all rfkill devices
+SUBSYSTEM=="rfkill", ENV{SYSTEMD_RFKILL}="1"
+SUBSYSTEM=="rfkill", IMPORT{builtin}="path_id"
+SUBSYSTEM=="misc", KERNEL=="rfkill", TAG+="systemd", ENV{SYSTEMD_WANTS}+="systemd-rfkill.socket"
+
+# Asynchronously mount file systems implemented by these modules as soon as they are loaded.
+SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount"
+SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-kernel-config.mount"
+
+LABEL="systemd_end"
diff --git a/test/fuzz-corpus/unit-file/dev-mapper-fedora_krowka\x2dswap.swap b/test/fuzz/fuzz-unit-file/dev-mapper-fedora_krowka\x2dswap.swap
index 2886021b1a..2886021b1a 100644
--- a/test/fuzz-corpus/unit-file/dev-mapper-fedora_krowka\x2dswap.swap
+++ b/test/fuzz/fuzz-unit-file/dev-mapper-fedora_krowka\x2dswap.swap
diff --git a/test/fuzz-corpus/unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service
index c2334d3b19..4d7526f636 100644
--- a/test/fuzz-corpus/unit-file/directives.service
+++ b/test/fuzz/fuzz-unit-file/directives.service
@@ -417,6 +417,7 @@ Group=
GroupForwardMask=
GroupPolicyExtension=
HairPin=
+MulticastToUnicast=
HelloTimeSec=
HomeAddress=
Host=
@@ -442,6 +443,7 @@ Independent=
InitialAdvertisedReceiveWindow=
InitialCongestionWindow=
InputKey=
+InvertRule=
KernelCommandLine=
KernelVersion=
Key=
@@ -690,7 +692,6 @@ HibernateMode=
HibernateState=
HybridSleepMode=
HybridSleepState=
-JoinControllers=
LogColor=
LogLevel=
LogLocation=
@@ -769,6 +770,7 @@ KeyringMode=
KillExcludeUsers=
KillOnlyUsers=
KillSignal=
+WatchdogSignal=
KillUserProcesses=
LOCATION=
LidSwitchIgnoreInhibited=
@@ -792,6 +794,8 @@ LineMax=
LockPersonality=
LogExtraFields=
LogLevelMax=
+LogRateLimitIntervalSec=
+LogRateLimitBurst=
LogsDirectory=
LogsDirectoryMode=
MACVLAN=
diff --git a/test/fuzz-corpus/unit-file/empty.scope b/test/fuzz/fuzz-unit-file/empty.scope
index 8df7245f62..8df7245f62 100644
--- a/test/fuzz-corpus/unit-file/empty.scope
+++ b/test/fuzz/fuzz-unit-file/empty.scope
diff --git a/test/fuzz-corpus/unit-file/machine.slice b/test/fuzz/fuzz-unit-file/machine.slice
index bf8c6bfc3e..bf8c6bfc3e 100644
--- a/test/fuzz-corpus/unit-file/machine.slice
+++ b/test/fuzz/fuzz-unit-file/machine.slice
diff --git a/test/fuzz/fuzz-unit-file/oss-fuzz-10007 b/test/fuzz/fuzz-unit-file/oss-fuzz-10007
new file mode 100644
index 0000000000..893630c83f
--- /dev/null
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-10007
@@ -0,0 +1,6 @@
+socket
+ #
+[Socket]
+ListenStream=vsock u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H5%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%HHs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%HHs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fus-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0s-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H23372036708:255
+ListenStream=vsock:34843013755:210
+Bis0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H32767%Hs-fu%H%H0%Hs-fu%H%H0%Hs-f \ No newline at end of file
diff --git a/test/fuzz/fuzz-unit-file/oss-fuzz-11569 b/test/fuzz/fuzz-unit-file/oss-fuzz-11569
new file mode 100644
index 0000000000..c49b8c6b1c
--- /dev/null
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-11569
Binary files differ
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6884 b/test/fuzz/fuzz-unit-file/oss-fuzz-6884
index 00d105ade5..00d105ade5 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6884
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-6884
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6885 b/test/fuzz/fuzz-unit-file/oss-fuzz-6885
index 1859136fdc..1859136fdc 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6885
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-6885
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6886 b/test/fuzz/fuzz-unit-file/oss-fuzz-6886
index 1fbe5ffd99..1fbe5ffd99 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6886
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-6886
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6892 b/test/fuzz/fuzz-unit-file/oss-fuzz-6892
index 31f746d034..31f746d034 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6892
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-6892
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6897 b/test/fuzz/fuzz-unit-file/oss-fuzz-6897
index 742fd9bfeb..742fd9bfeb 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6897
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-6897
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6897-evverx b/test/fuzz/fuzz-unit-file/oss-fuzz-6897-evverx
index 126678e76c..126678e76c 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6897-evverx
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-6897-evverx
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6908 b/test/fuzz/fuzz-unit-file/oss-fuzz-6908
index 8f2404b136..8f2404b136 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6908
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-6908
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6917 b/test/fuzz/fuzz-unit-file/oss-fuzz-6917
index 9a79cf00b7..9a79cf00b7 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6917
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-6917
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6977 b/test/fuzz/fuzz-unit-file/oss-fuzz-6977
index 3d844e6f08..3d844e6f08 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6977
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-6977
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6977-unminimized b/test/fuzz/fuzz-unit-file/oss-fuzz-6977-unminimized
index 718f94f0d7..718f94f0d7 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6977-unminimized
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-6977-unminimized
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-7004 b/test/fuzz/fuzz-unit-file/oss-fuzz-7004
index 77a5e5e8d6..77a5e5e8d6 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-7004
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-7004
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-8064 b/test/fuzz/fuzz-unit-file/oss-fuzz-8064
index 2c6c1eaee7..2c6c1eaee7 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-8064
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-8064
diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-8827 b/test/fuzz/fuzz-unit-file/oss-fuzz-8827
index c71b75e134..c71b75e134 100644
--- a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-8827
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-8827
diff --git a/test/fuzz-corpus/unit-file/proc-sys-fs-binfmt_misc.automount b/test/fuzz/fuzz-unit-file/proc-sys-fs-binfmt_misc.automount
index 777a123ef4..777a123ef4 100644
--- a/test/fuzz-corpus/unit-file/proc-sys-fs-binfmt_misc.automount
+++ b/test/fuzz/fuzz-unit-file/proc-sys-fs-binfmt_misc.automount
diff --git a/test/fuzz-corpus/unit-file/syslog.socket b/test/fuzz/fuzz-unit-file/syslog.socket
index 2eb316fcaa..2eb316fcaa 100644
--- a/test/fuzz-corpus/unit-file/syslog.socket
+++ b/test/fuzz/fuzz-unit-file/syslog.socket
diff --git a/test/fuzz-corpus/unit-file/systemd-ask-password-console.path b/test/fuzz/fuzz-unit-file/systemd-ask-password-console.path
index 3e12c752de..3e12c752de 100644
--- a/test/fuzz-corpus/unit-file/systemd-ask-password-console.path
+++ b/test/fuzz/fuzz-unit-file/systemd-ask-password-console.path
diff --git a/test/fuzz-corpus/unit-file/systemd-machined.service b/test/fuzz/fuzz-unit-file/systemd-machined.service
index 448f062ecf..448f062ecf 100644
--- a/test/fuzz-corpus/unit-file/systemd-machined.service
+++ b/test/fuzz/fuzz-unit-file/systemd-machined.service
diff --git a/test/fuzz-corpus/unit-file/systemd-resolved.service b/test/fuzz/fuzz-unit-file/systemd-resolved.service
index 0854c5f841..0854c5f841 100644
--- a/test/fuzz-corpus/unit-file/systemd-resolved.service
+++ b/test/fuzz/fuzz-unit-file/systemd-resolved.service
diff --git a/test/fuzz-corpus/unit-file/systemd-tmpfiles-clean.timer b/test/fuzz/fuzz-unit-file/systemd-tmpfiles-clean.timer
index 7db361cd69..7db361cd69 100644
--- a/test/fuzz-corpus/unit-file/systemd-tmpfiles-clean.timer
+++ b/test/fuzz/fuzz-unit-file/systemd-tmpfiles-clean.timer
diff --git a/test/fuzz-corpus/unit-file/timers.target b/test/fuzz/fuzz-unit-file/timers.target
index 171226c680..171226c680 100644
--- a/test/fuzz-corpus/unit-file/timers.target
+++ b/test/fuzz/fuzz-unit-file/timers.target
diff --git a/test/fuzz-corpus/unit-file/var-lib-machines.mount b/test/fuzz/fuzz-unit-file/var-lib-machines.mount
index 9c257d1191..9c257d1191 100644
--- a/test/fuzz-corpus/unit-file/var-lib-machines.mount
+++ b/test/fuzz/fuzz-unit-file/var-lib-machines.mount
diff --git a/test/fuzz/meson.build b/test/fuzz/meson.build
new file mode 100644
index 0000000000..daec2ead88
--- /dev/null
+++ b/test/fuzz/meson.build
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
+sanitize_address = custom_target(
+ 'sanitize-address-fuzzers',
+ output : 'sanitize-address-fuzzers',
+ command : [meson_build_sh,
+ meson.source_root(),
+ '@OUTPUT@',
+ 'fuzzers',
+ '-Db_lundef=false -Db_sanitize=address',
+ ' '.join(cc.cmd_array()),
+ cxx_cmd])
+
+sanitizers = [['address', sanitize_address]]
+
+if git.found()
+ out = run_command(
+ git,
+ '--git-dir=@0@/.git'.format(meson.source_root()),
+ 'ls-files', ':/test/fuzz/*/*')
+else
+ out = run_command(
+ 'sh', '-c', 'ls @0@/*/*'.format(meson.current_source_dir()))
+endif
+
+fuzz_regression_tests = []
+foreach p : out.stdout().split()
+ # Remove the last entry which is ''.
+ #
+ # Also, backslashes get mangled, so skip test. See
+ # https://github.com/mesonbuild/meson/issues/1564.
+ if not p.contains('\\')
+ fuzz_regression_tests += p
+ endif
+endforeach
diff --git a/test/meson.build b/test/meson.build
index 826e684e59..d98bfb80d2 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -18,6 +18,8 @@ test_data_files = '''
hwdb/10-bad.hwdb
journal-data/journal-1.txt
journal-data/journal-2.txt
+ nomem.slice
+ nomemleaf.service
parent-deep.slice
parent.slice
sched_idle_bad.service
@@ -45,6 +47,8 @@ test_data_files = '''
test-execute/exec-cpuaffinity1.service
test-execute/exec-cpuaffinity2.service
test-execute/exec-cpuaffinity3.service
+ test-execute/exec-dynamicuser-fixeduser-adm.service
+ test-execute/exec-dynamicuser-fixeduser-games.service
test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service
test-execute/exec-dynamicuser-fixeduser.service
test-execute/exec-dynamicuser-statedir-migrate-step1.service
@@ -115,6 +119,8 @@ test_data_files = '''
test-execute/exec-specifier@.service
test-execute/exec-standardinput-data.service
test-execute/exec-standardinput-file.service
+ test-execute/exec-standardoutput-file.service
+ test-execute/exec-standardoutput-append.service
test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
test-execute/exec-supplementarygroups-multiple-groups-withgid.service
test-execute/exec-supplementarygroups-multiple-groups-withuid.service
@@ -130,6 +136,7 @@ test_data_files = '''
test-execute/exec-systemcallfilter-system-user-nfsnobody.service
test-execute/exec-systemcallfilter-system-user-nobody.service
test-execute/exec-systemcallfilter-system-user.service
+ test-execute/exec-systemcallfilter-with-errno-multi.service
test-execute/exec-systemcallfilter-with-errno-name.service
test-execute/exec-systemcallfilter-with-errno-number.service
test-execute/exec-temporaryfilesystem-options.service
@@ -207,16 +214,28 @@ endif
############################################################
rule_syntax_check_py = find_program('rule-syntax-check.py')
-test('rule-syntax-check',
- rule_syntax_check_py,
- args : all_rules)
+if want_tests != 'false'
+ test('rule-syntax-check',
+ rule_syntax_check_py,
+ args : all_rules)
+endif
############################################################
if conf.get('HAVE_SYSV_COMPAT') == 1
sysv_generator_test_py = find_program('sysv-generator-test.py')
- test('sysv-generator-test',
- sysv_generator_test_py)
+ if want_tests != 'false'
+ test('sysv-generator-test',
+ sysv_generator_test_py)
+ endif
+endif
+
+############################################################
+
+if install_tests
+ install_data('run-unit-tests.py',
+ install_mode : 'rwxr-xr-x',
+ install_dir : testsdir)
endif
############################################################
@@ -227,21 +246,25 @@ custom_target(
'sys',
command : [sys_script_py, meson.current_build_dir()],
output : 'sys',
- build_by_default : true)
+ build_by_default : want_tests != 'false')
if perl.found()
udev_test_pl = find_program('udev-test.pl')
- test('udev-test',
- udev_test_pl)
+ if want_tests != 'false'
+ test('udev-test',
+ udev_test_pl)
+ endif
else
message('Skipping udev-test because perl is not available')
endif
if conf.get('ENABLE_HWDB') == 1
hwdb_test_sh = find_program('hwdb-test.sh')
- test('hwdb-test',
- hwdb_test_sh,
- timeout : 90)
+ if want_tests != 'false'
+ test('hwdb-test',
+ hwdb_test_sh,
+ timeout : 90)
+ endif
endif
-subdir('fuzz-regressions')
+subdir('fuzz')
diff --git a/test/mkosi.build.networkd-test b/test/mkosi.build.networkd-test
new file mode 100755
index 0000000000..cdaa4302f7
--- /dev/null
+++ b/test/mkosi.build.networkd-test
@@ -0,0 +1,24 @@
+#!/bin/sh
+set -ex
+
+# First, source in the main build script
+. "$SRCDIR"/mkosi.build
+
+mkdir -p "$DESTDIR"/usr/local/bin
+cp "$SRCDIR"/test/networkd-test.py "$DESTDIR"/usr/local/bin/networkd-test.py
+
+mkdir -p "$DESTDIR"/etc/systemd/system
+cat > "$DESTDIR"/etc/systemd/system/networkd-test.service <<EOF
+[Unit]
+Description=networkd test service
+SuccessAction=exit
+FailureAction=exit
+
+[Service]
+ExecStart=/usr/local/bin/networkd-test.py
+EOF
+
+mkdir -p "$DESTDIR"/etc/systemd/system/multi-user.target.wants/
+ln -s ../networkd-test.service "$DESTDIR"/etc/systemd/system/multi-user.target.wants/
+
+systemctl --root="$DESTDIR" disable systemd-networkd.service
diff --git a/test/mkosi.default.networkd-test b/test/mkosi.default.networkd-test
new file mode 100644
index 0000000000..9630d3ec42
--- /dev/null
+++ b/test/mkosi.default.networkd-test
@@ -0,0 +1,80 @@
+# Puts together an nspawn container and runs networkd-test.py in it, inside a
+# network namespace and everything. Run this with "mkosi
+# --default=mkosi.default.networkd-test boot". This will start the test and
+# eventually exit with sucess in case the test succeeded.
+
+[Distribution]
+Distribution=fedora
+Release=29
+
+[Output]
+Format=raw_btrfs
+Bootable=yes
+KernelCommandLine=printk.devkmsg=on
+OutputDirectory=../mkosi.output
+Output=networkd-test.raw
+
+[Partitions]
+RootSize=3G
+
+[Packages]
+BuildPackages=
+ audit-libs-devel
+ bzip2-devel
+ cryptsetup-devel
+ dbus-devel
+ diffutils
+ docbook-style-xsl
+ elfutils-devel
+ gcc
+ gettext
+ git
+ gnu-efi
+ gnu-efi-devel
+ gnutls-devel
+ gperf
+ hostname
+ iptables-devel
+ kmod-devel
+ libacl-devel
+ libblkid-devel
+ libcap-devel
+ libcurl-devel
+ libgcrypt-devel
+ libidn2-devel
+ libmicrohttpd-devel
+ libmount-devel
+ libseccomp-devel
+ libselinux-devel
+ libtool
+ libxkbcommon-devel
+ libxslt
+ lz4
+ lz4-devel
+ m4
+ meson
+ pam-devel
+ pcre2-devel
+ pkgconfig
+ python3-devel
+ python3-lxml
+ qrencode-devel
+ tree
+ xz-devel
+
+Packages=
+ dnsmasq
+ iproute
+ libidn2
+ polkit
+ python3
+
+# Share caches with the top-level mkosi
+BuildDirectory=../mkosi.builddir
+Cache=../mkosi.cache
+
+# Run our own script
+BuildScript=mkosi.build.networkd-test
+
+BuildSources=..
+NSpawnSettings=mkosi.nspawn.networkd-test
diff --git a/test/mkosi.nspawn.networkd-test b/test/mkosi.nspawn.networkd-test
new file mode 100644
index 0000000000..a23aed6eda
--- /dev/null
+++ b/test/mkosi.nspawn.networkd-test
@@ -0,0 +1,2 @@
+[Network]
+Private=yes
diff --git a/test/networkd-test.py b/test/networkd-test.py
index 387cdb6400..7011abc965 100755
--- a/test/networkd-test.py
+++ b/test/networkd-test.py
@@ -37,16 +37,47 @@ NETWORKD_WAIT_ONLINE = shutil.which('systemd-networkd-wait-online',
RESOLV_CONF = '/run/systemd/resolve/resolv.conf'
+tmpmounts = []
+running_units = []
+stopped_units = []
+
def setUpModule():
+ global tmpmounts
+
"""Initialize the environment, and perform sanity checks on it."""
if NETWORKD_WAIT_ONLINE is None:
raise OSError(errno.ENOENT, 'systemd-networkd-wait-online not found')
- # Do not run any tests if the system is using networkd already.
- if subprocess.call(['systemctl', 'is-active', '--quiet',
- 'systemd-networkd.service']) == 0:
- raise unittest.SkipTest('networkd is already active')
+ # Do not run any tests if the system is using networkd already and it's not virtualized
+ if (subprocess.call(['systemctl', 'is-active', '--quiet', 'systemd-networkd.service']) == 0 and
+ subprocess.call(['systemd-detect-virt', '--quiet']) != 0):
+ raise unittest.SkipTest('not virtualized and networkd is already active')
+
+ # Ensure we don't mess with an existing networkd config
+ for u in ['systemd-networkd.socket', 'systemd-networkd', 'systemd-resolved']:
+ if subprocess.call(['systemctl', 'is-active', '--quiet', u]) == 0:
+ subprocess.call(['systemctl', 'stop', u])
+ running_units.append(u)
+ else:
+ stopped_units.append(u)
+
+ # create static systemd-network user for networkd-test-router.service (it
+ # needs to do some stuff as root and can't start as user; but networkd
+ # still insists on the user)
+ subprocess.call(['adduser', '--system', '--no-create-home', 'systemd-network'])
+
+ for d in ['/etc/systemd/network', '/run/systemd/network',
+ '/run/systemd/netif', '/run/systemd/resolve']:
+ if os.path.isdir(d):
+ subprocess.check_call(["mount", "-t", "tmpfs", "none", d])
+ tmpmounts.append(d)
+ if os.path.isdir('/run/systemd/resolve'):
+ os.chmod('/run/systemd/resolve', 0o755)
+ shutil.chown('/run/systemd/resolve', 'systemd-resolve', 'systemd-resolve')
+ if os.path.isdir('/run/systemd/netif'):
+ os.chmod('/run/systemd/netif', 0o755)
+ shutil.chown('/run/systemd/netif', 'systemd-network', 'systemd-network')
# Avoid "Failed to open /dev/tty" errors in containers.
os.environ['SYSTEMD_LOG_TARGET'] = 'journal'
@@ -55,6 +86,16 @@ def setUpModule():
os.makedirs(NETWORK_UNITDIR, exist_ok=True)
+def tearDownModule():
+ global tmpmounts
+ for d in tmpmounts:
+ subprocess.check_call(["umount", d])
+ for u in stopped_units:
+ subprocess.call(["systemctl", "stop", u])
+ for u in running_units:
+ subprocess.call(["systemctl", "restart", u])
+
+
class NetworkdTestingUtilities:
"""Provide a set of utility functions to facilitate networkd tests.
@@ -70,13 +111,17 @@ class NetworkdTestingUtilities:
list(peer_options))
self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'dev', peer])
+ def write_config(self, path, contents):
+ """"Write a configuration file, and queue it to be removed."""
+
+ with open(path, 'w') as f:
+ f.write(contents)
+
+ self.addCleanup(os.remove, path)
+
def write_network(self, unit_name, contents):
"""Write a network unit file, and queue it to be removed."""
- unit_path = os.path.join(NETWORK_UNITDIR, unit_name)
-
- with open(unit_path, 'w') as unit:
- unit.write(contents)
- self.addCleanup(os.remove, unit_path)
+ self.write_config(os.path.join(NETWORK_UNITDIR, unit_name), contents)
def write_network_dropin(self, unit_name, dropin_name, contents):
"""Write a network unit drop-in, and queue it to be removed."""
@@ -172,6 +217,7 @@ Name=mybridge
DNS=192.168.250.1
Address=192.168.250.33/24
Gateway=192.168.250.1''')
+ subprocess.call(['systemctl', 'reset-failed', 'systemd-networkd', 'systemd-resolved'])
subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
def tearDown(self):
@@ -257,6 +303,8 @@ class ClientTestBase(NetworkdTestingUtilities):
self.assertTrue(out.startswith('-- cursor:'))
self.journal_cursor = out.split()[-1]
+ subprocess.call(['systemctl', 'reset-failed', 'systemd-networkd', 'systemd-resolved'])
+
def tearDown(self):
self.shutdown_iface()
subprocess.call(['systemctl', 'stop', 'systemd-networkd'])
@@ -572,13 +620,13 @@ Domains= ~company ~lab''')
# test vpnclient specific domains; these should *not* be answered by
# the general DNS
- out = subprocess.check_output(['systemd-resolve', 'math.lab'])
+ out = subprocess.check_output(['resolvectl', 'query', 'math.lab'])
self.assertIn(b'math.lab: 10.241.3.3', out)
- out = subprocess.check_output(['systemd-resolve', 'kettle.cantina.company'])
+ out = subprocess.check_output(['resolvectl', 'query', 'kettle.cantina.company'])
self.assertIn(b'kettle.cantina.company: 10.241.4.4', out)
# test general domains
- out = subprocess.check_output(['systemd-resolve', 'megasearch.net'])
+ out = subprocess.check_output(['resolvectl', 'query', 'megasearch.net'])
self.assertIn(b'megasearch.net: 192.168.42.1', out)
with open(self.dnsmasq_log) as f:
@@ -604,7 +652,7 @@ Domains= ~company ~lab''')
conf = '/run/systemd/resolved.conf.d/test-disable-dnssec.conf'
os.makedirs(os.path.dirname(conf), exist_ok=True)
with open(conf, 'w') as f:
- f.write('[Resolve]\nDNSSEC=no')
+ f.write('[Resolve]\nDNSSEC=no\nLLMNR=no\nMulticastDNS=no\n')
self.addCleanup(os.remove, conf)
# create /etc/hosts bind mount which resolves my.example for IPv4
@@ -625,26 +673,26 @@ Domains= ~company ~lab''')
try:
# family specific queries
- out = subprocess.check_output(['systemd-resolve', '-4', 'my.example'])
+ out = subprocess.check_output(['resolvectl', 'query', '-4', 'my.example'])
self.assertIn(b'my.example: 172.16.99.99', out)
# we don't expect an IPv6 answer; if /etc/hosts has any IP address,
# it's considered a sufficient source
- self.assertNotEqual(subprocess.call(['systemd-resolve', '-6', 'my.example']), 0)
+ self.assertNotEqual(subprocess.call(['resolvectl', 'query', '-6', 'my.example']), 0)
# "any family" query; IPv4 should come from /etc/hosts
- out = subprocess.check_output(['systemd-resolve', 'my.example'])
+ out = subprocess.check_output(['resolvectl', 'query', 'my.example'])
self.assertIn(b'my.example: 172.16.99.99', out)
# IP → name lookup; again, takes the /etc/hosts one
- out = subprocess.check_output(['systemd-resolve', '172.16.99.99'])
+ out = subprocess.check_output(['resolvectl', 'query', '172.16.99.99'])
self.assertIn(b'172.16.99.99: my.example', out)
# non-address RRs should fall back to DNS
- out = subprocess.check_output(['systemd-resolve', '--type=MX', 'example'])
+ out = subprocess.check_output(['resolvectl', 'query', '--type=MX', 'example'])
self.assertIn(b'example IN MX 1 mail.example', out)
# other domains query DNS
- out = subprocess.check_output(['systemd-resolve', 'other.example'])
+ out = subprocess.check_output(['resolvectl', 'query', 'other.example'])
self.assertIn(b'172.16.0.42', out)
- out = subprocess.check_output(['systemd-resolve', '172.16.0.42'])
+ out = subprocess.check_output(['resolvectl', 'query', '172.16.0.42'])
self.assertIn(b'172.16.0.42: other.example', out)
except (AssertionError, subprocess.CalledProcessError):
self.show_journal('systemd-resolved.service')
@@ -661,6 +709,7 @@ Domains= ~company ~lab''')
subprocess.check_call(['mount', '--bind', '/dev/null', '/etc/hostname'])
self.addCleanup(subprocess.call, ['umount', '/etc/hostname'])
subprocess.check_call(['systemctl', 'stop', 'systemd-hostnamed.service'])
+ self.addCleanup(subprocess.call, ['systemctl', 'stop', 'systemd-hostnamed.service'])
self.create_iface(dnsmasq_opts=['--dhcp-host={},192.168.5.210,testgreen'.format(self.iface_mac)])
self.do_test(coldplug=None, extra_opts='IPv6AcceptRA=False', dhcp_mode='ipv4')
@@ -693,9 +742,18 @@ Domains= ~company ~lab''')
orig_hostname = socket.gethostname()
self.addCleanup(socket.sethostname, orig_hostname)
+
if not os.path.exists('/etc/hostname'):
- self.writeConfig('/etc/hostname', orig_hostname)
+ self.write_config('/etc/hostname', "foobarqux")
+ else:
+ self.write_config('/run/hostname.tmp', "foobarqux")
+ subprocess.check_call(['mount', '--bind', '/run/hostname.tmp', '/etc/hostname'])
+ self.addCleanup(subprocess.call, ['umount', '/etc/hostname'])
+
+ socket.sethostname("foobarqux");
+
subprocess.check_call(['systemctl', 'stop', 'systemd-hostnamed.service'])
+ self.addCleanup(subprocess.call, ['systemctl', 'stop', 'systemd-hostnamed.service'])
self.create_iface(dnsmasq_opts=['--dhcp-host={},192.168.5.210,testgreen'.format(self.iface_mac)])
self.do_test(coldplug=None, extra_opts='IPv6AcceptRA=False', dhcp_mode='ipv4')
@@ -705,7 +763,7 @@ Domains= ~company ~lab''')
out = subprocess.check_output(['ip', '-4', 'a', 'show', 'dev', self.iface])
self.assertRegex(out, b'inet 192.168.5.210/24 .* scope global dynamic')
# static hostname wins over transient one, thus *not* applied
- self.assertEqual(socket.gethostname(), orig_hostname)
+ self.assertEqual(socket.gethostname(), "foobarqux")
except AssertionError:
self.show_journal('systemd-networkd.service')
self.show_journal('systemd-hostnamed.service')
diff --git a/test/nomem.slice b/test/nomem.slice
new file mode 100644
index 0000000000..9c5d208cb4
--- /dev/null
+++ b/test/nomem.slice
@@ -0,0 +1,5 @@
+[Unit]
+Description=Nomem Parent Slice
+
+[Slice]
+DisableControllers=memory
diff --git a/test/nomemleaf.service b/test/nomemleaf.service
new file mode 100644
index 0000000000..3cbaccb82f
--- /dev/null
+++ b/test/nomemleaf.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=Nomem Leaf Service
+
+[Service]
+Slice=nomem.slice
+Type=oneshot
+ExecStart=/bin/true
+IOWeight=200
+MemoryAccounting=true
diff --git a/test/run-integration-tests.sh b/test/run-integration-tests.sh
index 94df346c28..0d2377128e 100755
--- a/test/run-integration-tests.sh
+++ b/test/run-integration-tests.sh
@@ -4,7 +4,7 @@ BUILD_DIR="$($(dirname "$0")/../tools/find-build-dir.sh)"
if [ $# -gt 0 ]; then
args="$@"
else
- args="clean setup run"
+ args="clean setup run clean-again"
fi
ninja -C "$BUILD_DIR"
diff --git a/test/run-unit-tests.py b/test/run-unit-tests.py
new file mode 100755
index 0000000000..9a75cd421e
--- /dev/null
+++ b/test/run-unit-tests.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+
+import argparse
+import dataclasses
+import glob
+import os
+import subprocess
+import sys
+try:
+ import colorama as c
+ GREEN = c.Fore.GREEN
+ YELLOW = c.Fore.YELLOW
+ RED = c.Fore.RED
+ RESET_ALL = c.Style.RESET_ALL
+ BRIGHT = c.Style.BRIGHT
+except ImportError:
+ GREEN = YELLOW = RED = RESET_ALL = BRIGHT = ''
+
+@dataclasses.dataclass
+class Total:
+ total:int
+ good:int = 0
+ skip:int = 0
+ fail:int = 0
+
+def argument_parser():
+ p = argparse.ArgumentParser()
+ p.add_argument('-u', '--unsafe', action='store_true',
+ help='run "unsafe" tests too')
+ return p
+
+opts = argument_parser().parse_args()
+
+tests = glob.glob('/usr/lib/systemd/tests/test-*')
+if opts.unsafe:
+ tests += glob.glob('/usr/lib/systemd/tests/unsafe/test-*')
+
+total = Total(total=len(tests))
+for test in tests:
+ name = os.path.basename(test)
+
+ ex = subprocess.run(test, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ if ex.returncode == 0:
+ print(f'{GREEN}PASS: {name}{RESET_ALL}')
+ total.good += 1
+ elif ex.returncode == 77:
+ print(f'{YELLOW}SKIP: {name}{RESET_ALL}')
+ total.skip += 1
+ else:
+ print(f'{RED}FAIL: {name}{RESET_ALL}')
+ total.fail += 1
+
+ # stdout/stderr might not be valid unicode, let's just dump it to the terminal.
+ # Also let's reset the style afterwards, in case our output sets something.
+ sys.stdout.buffer.write(ex.stdout)
+ print(f'{RESET_ALL}{BRIGHT}')
+ sys.stdout.buffer.write(ex.stderr)
+ print(f'{RESET_ALL}')
+
+print(f'{BRIGHT}OK: {total.good} SKIP: {total.skip} FAIL: {total.fail}{RESET_ALL}')
+sys.exit(total.fail > 0)
diff --git a/test/test-execute/exec-basic.service b/test/test-execute/exec-basic.service
index 456f06951a..8b672fcecb 100644
--- a/test/test-execute/exec-basic.service
+++ b/test/test-execute/exec-basic.service
@@ -2,7 +2,7 @@
Description=Test for basic execution
[Service]
-ExecStart=touch /tmp/a ; /bin/touch /tmp/b ; touch /tmp/c
+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
diff --git a/test/test-execute/exec-dynamicuser-fixeduser-adm.service b/test/test-execute/exec-dynamicuser-fixeduser-adm.service
new file mode 100644
index 0000000000..90040ee533
--- /dev/null
+++ b/test/test-execute/exec-dynamicuser-fixeduser-adm.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Test DynamicUser with static User= whose uid and gid are different
+# On Fedora, user adm has uid==3 and gid==4.
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
+# Multiple ExecStart= lines causes the issue #9702.
+ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
+DynamicUser=yes
+User=adm
diff --git a/test/test-execute/exec-dynamicuser-fixeduser-games.service b/test/test-execute/exec-dynamicuser-fixeduser-games.service
new file mode 100644
index 0000000000..1cc9518fc6
--- /dev/null
+++ b/test/test-execute/exec-dynamicuser-fixeduser-games.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Test DynamicUser with static User= whose uid and gid are different
+# On Ubuntu or Debian, user games has uid==5 and gid==60.
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
+# Multiple ExecStart= lines causes the issue #9702.
+ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
+DynamicUser=yes
+User=games
diff --git a/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service b/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service
index de1a6e7303..bd7881d286 100644
--- a/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service
+++ b/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service
@@ -2,7 +2,8 @@
Description=Test DynamicUser with User= and SupplementaryGroups=
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -G)" = "1" && test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "1" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
Type=oneshot
User=1
DynamicUser=yes
diff --git a/test/test-execute/exec-dynamicuser-fixeduser.service b/test/test-execute/exec-dynamicuser-fixeduser.service
index 1d84af02ed..f28078f242 100644
--- a/test/test-execute/exec-dynamicuser-fixeduser.service
+++ b/test/test-execute/exec-dynamicuser-fixeduser.service
@@ -2,7 +2,8 @@
Description=Test DynamicUser with User=
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -G)" = "1" && test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "1" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
Type=oneshot
User=1
DynamicUser=yes
diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service
index 72e6d7686f..5efc5483b8 100644
--- a/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service
+++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service
@@ -10,6 +10,7 @@ ExecStart=test -d /var/lib/test-dynamicuser-migrate
ExecStart=test -d /var/lib/test-dynamicuser-migrate2/hoge
ExecStart=touch /var/lib/test-dynamicuser-migrate/yay
ExecStart=touch /var/lib/test-dynamicuser-migrate2/hoge/yayyay
+ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
Type=oneshot
DynamicUser=no
diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service
index edb0be7ef8..c72302ffd5 100644
--- a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service
+++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service
@@ -18,6 +18,7 @@ ExecStart=touch /var/lib/test-dynamicuser-migrate/yay
ExecStart=touch /var/lib/test-dynamicuser-migrate2/hoge/yayyay
ExecStart=touch /var/lib/private/test-dynamicuser-migrate/yay
ExecStart=touch /var/lib/private/test-dynamicuser-migrate2/hoge/yayyay
+ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
Type=oneshot
DynamicUser=yes
diff --git a/test/test-execute/exec-dynamicuser-statedir.service b/test/test-execute/exec-dynamicuser-statedir.service
index f459f3c1eb..2fb7b8660b 100644
--- a/test/test-execute/exec-dynamicuser-statedir.service
+++ b/test/test-execute/exec-dynamicuser-statedir.service
@@ -10,6 +10,7 @@ ExecStart=test -f /var/lib/waldo/yay
ExecStart=test -f /var/lib/quux/pief/yayyay
ExecStart=test -f /var/lib/private/waldo/yay
ExecStart=test -f /var/lib/private/quux/pief/yayyay
+ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/waldo:%S/quux/pief"'
# Make sure that /var/lib/private/waldo is really the only writable directory besides the obvious candidates
ExecStart=sh -x -c 'test $$(find / \( -path /var/tmp -o -path /tmp -o -path /proc -o -path /dev/mqueue -o -path /dev/shm -o -path /sys/fs/bpf \) -prune -o -type d -writable -print 2>/dev/null | sort -u | tr -d '\\\\n') = /var/lib/private/quux/pief/var/lib/private/waldo'
diff --git a/test/test-execute/exec-dynamicuser-supplementarygroups.service b/test/test-execute/exec-dynamicuser-supplementarygroups.service
index a47b7fab78..e3549c22c9 100644
--- a/test/test-execute/exec-dynamicuser-supplementarygroups.service
+++ b/test/test-execute/exec-dynamicuser-supplementarygroups.service
@@ -2,7 +2,9 @@
Description=Test DynamicUser with SupplementaryGroups=
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -G | cut -d " " --complement -f 1)" = "1 2 3"'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "1" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "2" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "3" && HAVE=1; done; test "$$HAVE" -eq 1'
Type=oneshot
DynamicUser=yes
SupplementaryGroups=1 2 3
diff --git a/test/test-execute/exec-privatenetwork-yes.service b/test/test-execute/exec-privatenetwork-yes.service
index a38d24912f..ded8d55126 100644
--- a/test/test-execute/exec-privatenetwork-yes.service
+++ b/test/test-execute/exec-privatenetwork-yes.service
@@ -2,6 +2,6 @@
Description=Test for PrivateNetwork
[Service]
-ExecStart=/bin/sh -x -c '! ip link | grep ": " | grep -Ev ": (lo|sit0@.*):"'
+ExecStart=/bin/sh -x -c '! ip link | grep ": " | grep -Ev ": (lo|(sit0|ip6tnl0|ip6gre0)@.*):"'
Type=oneshot
PrivateNetwork=yes
diff --git a/test/test-execute/exec-readonlypaths.service b/test/test-execute/exec-readonlypaths.service
index 6866fdc700..a0ca68f67d 100644
--- a/test/test-execute/exec-readonlypaths.service
+++ b/test/test-execute/exec-readonlypaths.service
@@ -2,6 +2,8 @@
Description=Test for ReadOnlyPaths=
[Service]
-ReadOnlyPaths=/etc -/i-dont-exist /usr
-ExecStart=/bin/sh -x -c 'test ! -w /etc && test ! -w /usr && test ! -e /i-dont-exist && test -w /var'
+ReadOnlyPaths=/usr /etc /sys /dev -/i-dont-exist
+PrivateDevices=yes
+ExecStart=/bin/sh -x -c 'test ! -w /usr && test ! -w /etc && test ! -w /sys && test ! -w /sys/fs/cgroup'
+ExecStart=/bin/sh -x -c 'test ! -w /dev && test ! -w /dev/shm && test ! -e /i-dont-exist && test -w /var'
Type=oneshot
diff --git a/test/test-execute/exec-runtimedirectory-mode.service b/test/test-execute/exec-runtimedirectory-mode.service
index 480f904155..85ae5161c4 100644
--- a/test/test-execute/exec-runtimedirectory-mode.service
+++ b/test/test-execute/exec-runtimedirectory-mode.service
@@ -3,6 +3,7 @@ Description=Test for RuntimeDirectoryMode
[Service]
ExecStart=/bin/sh -x -c 'mode=$$(stat -c %%a %t/test-exec_runtimedirectory-mode); test "$$mode" = "750"'
+ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory-mode"'
Type=oneshot
RuntimeDirectory=test-exec_runtimedirectory-mode
RuntimeDirectoryMode=0750
diff --git a/test/test-execute/exec-runtimedirectory.service b/test/test-execute/exec-runtimedirectory.service
index 6a4383110f..a33044d23c 100644
--- a/test/test-execute/exec-runtimedirectory.service
+++ b/test/test-execute/exec-runtimedirectory.service
@@ -4,6 +4,7 @@ Description=Test for RuntimeDirectory
[Service]
ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory'
ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory2/hogehoge'
+ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory:%t/test-exec_runtimedirectory2/hogehoge"'
Type=oneshot
RuntimeDirectory=test-exec_runtimedirectory
RuntimeDirectory=./test-exec_runtimedirectory2///./hogehoge/.
diff --git a/test/test-execute/exec-specifier.service b/test/test-execute/exec-specifier.service
index 13ba859879..7c3f81f2b5 100644
--- a/test/test-execute/exec-specifier.service
+++ b/test/test-execute/exec-specifier.service
@@ -19,9 +19,11 @@ ExecStart=test %L = /var/log
ExecStart=test %E = /etc
ExecStart=test %T = /tmp
ExecStart=test %V = /var/tmp
-ExecStart=sh -c 'test %u = $$(id -un 0)'
-ExecStart=test %U = 0
-ExecStart=sh -c 'test %h = $$(getent passwd 0 | cut -d: -f 6)'
+ExecStart=sh -c 'test %u = $$(id -un)'
+ExecStart=sh -c 'test %U = $$(id -u)'
+ExecStart=sh -c 'test %g = $$(id -gn)'
+ExecStart=sh -c 'test %G = $$(id -g)'
+ExecStart=test %h = /root
ExecStart=sh -c 'test %s = /bin/sh'
ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)'
ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')'
diff --git a/test/test-execute/exec-specifier@.service b/test/test-execute/exec-specifier@.service
index 174559dbd1..a388926846 100644
--- a/test/test-execute/exec-specifier@.service
+++ b/test/test-execute/exec-specifier@.service
@@ -17,9 +17,11 @@ ExecStart=test %S = /var/lib
ExecStart=test %C = /var/cache
ExecStart=test %L = /var/log
ExecStart=test %E = /etc
-ExecStart=sh -c 'test %u = $$(id -un 0)'
-ExecStart=test %U = 0
-ExecStart=sh -c 'test %h = $$(getent passwd 0 | cut -d: -f 6)'
+ExecStart=sh -c 'test %u = $$(id -un)'
+ExecStart=sh -c 'test %U = $$(id -u)'
+ExecStart=sh -c 'test %g = $$(id -gn)'
+ExecStart=sh -c 'test %G = $$(id -g)'
+ExecStart=test %h = /root
ExecStart=sh -c 'test %s = /bin/sh'
ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)'
ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')'
diff --git a/test/test-execute/exec-standardoutput-append.service b/test/test-execute/exec-standardoutput-append.service
new file mode 100644
index 0000000000..8983bb056b
--- /dev/null
+++ b/test/test-execute/exec-standardoutput-append.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Test for StandardOutput=append:
+
+[Service]
+ExecStartPre=sh -c 'printf "hello\n" > /tmp/test-exec-standardoutput-output'
+ExecStartPre=sh -c 'printf "hello\nhello\n" > /tmp/test-exec-standardoutput-expected'
+StandardInput=data
+StandardInputText=hello
+StandardOutput=append:/tmp/test-exec-standardoutput-output
+StandardError=null
+ExecStart=cat
+ExecStart=cmp /tmp/test-exec-standardoutput-output /tmp/test-exec-standardoutput-expected
+Type=oneshot
diff --git a/test/test-execute/exec-standardoutput-file.service b/test/test-execute/exec-standardoutput-file.service
new file mode 100644
index 0000000000..71e2604b94
--- /dev/null
+++ b/test/test-execute/exec-standardoutput-file.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Test for StandardOutput=file:
+
+[Service]
+ExecStartPre=sh -c 'printf "nooo\nhello\n" > /tmp/test-exec-standardoutput-output'
+ExecStartPre=sh -c 'printf "hello\nello\n" > /tmp/test-exec-standardoutput-expected'
+StandardInput=data
+StandardInputText=hello
+StandardOutput=file:/tmp/test-exec-standardoutput-output
+StandardError=null
+ExecStart=cat
+ExecStart=cmp /tmp/test-exec-standardoutput-expected /tmp/test-exec-standardoutput-output
+Type=oneshot
diff --git a/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service b/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
index a49c9d26a1..2a7ce87bb4 100644
--- a/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
+++ b/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
@@ -2,6 +2,10 @@
Description=Test for Supplementary Group with multiple groups without Group and User
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -G)" = "0 1 2 3" && test "$$(id -g)" = "0" && test "$$(id -u)" = "0"'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "%G" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "1" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "2" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "3" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "%G" && test "$$(id -u)" = "%U"'
Type=oneshot
SupplementaryGroups=1 2 3
diff --git a/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service b/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service
index 5c62c1d639..aae20fbf95 100644
--- a/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service
+++ b/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service
@@ -2,7 +2,10 @@
Description=Test for Supplementary Group with multiple groups and Group=1
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -G)" = "1 2 3" && test "$$(id -g)" = "1" && test "$$(id -u)" = "0"'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "1" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "2" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "3" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "%U"'
Type=oneshot
Group=1
SupplementaryGroups=1 2 3
diff --git a/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service b/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service
index 00523e383b..2714235be6 100644
--- a/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service
+++ b/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service
@@ -2,7 +2,9 @@
Description=Test for Supplementary Group with multiple groups and Uid=1
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -G)" = "1 2 3" && test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "1" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "2" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "3" && HAVE=1; done; test "$$HAVE" -eq 1'
Type=oneshot
User=1
SupplementaryGroups=1 2 3
diff --git a/test/test-execute/exec-supplementarygroups-single-group-user.service b/test/test-execute/exec-supplementarygroups-single-group-user.service
index ed6276d303..405c5f9bfe 100644
--- a/test/test-execute/exec-supplementarygroups-single-group-user.service
+++ b/test/test-execute/exec-supplementarygroups-single-group-user.service
@@ -2,7 +2,8 @@
Description=Test for Supplementary Group with only one group and uid 1
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -G)" = "1" && test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "1" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
Type=oneshot
User=1
Group=1
diff --git a/test/test-execute/exec-supplementarygroups-single-group.service b/test/test-execute/exec-supplementarygroups-single-group.service
index ee502b3d37..f9b721696b 100644
--- a/test/test-execute/exec-supplementarygroups-single-group.service
+++ b/test/test-execute/exec-supplementarygroups-single-group.service
@@ -2,7 +2,8 @@
Description=Test for Supplementary Group with only one group
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -G)" = "1" && test "$$(id -g)" = "1" && test "$$(id -u)" = "0"'
+ExecStart=/bin/sh -x -c 'HAVE=; for g in $$(id -G); do test "$$g" = "1" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "0"'
Type=oneshot
Group=1
SupplementaryGroups=1
diff --git a/test/test-execute/exec-supplementarygroups.service b/test/test-execute/exec-supplementarygroups.service
index 43a9a981f2..6f6e2ba822 100644
--- a/test/test-execute/exec-supplementarygroups.service
+++ b/test/test-execute/exec-supplementarygroups.service
@@ -2,6 +2,7 @@
Description=Test for Supplementary Group
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -G)" = "0 1"'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "%G" && HAVE=1; done; test "$$HAVE" -eq 1'
+ExecStart=/bin/sh -x -c 'HAVE=0; for g in $$(id -G); do test "$$g" = "1" && HAVE=1; done; test "$$HAVE" -eq 1'
Type=oneshot
SupplementaryGroups=1
diff --git a/test/test-execute/exec-systemcallfilter-with-errno-multi.service b/test/test-execute/exec-systemcallfilter-with-errno-multi.service
new file mode 100644
index 0000000000..951e7ac36b
--- /dev/null
+++ b/test/test-execute/exec-systemcallfilter-with-errno-multi.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=Test for SystemCallFilter updating errno
+# test for issue #9939 which is fixed by a5404992cc7724ebf7572a0aa89d9fdb26ce0b62 (#9942)
+
+[Service]
+ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+Type=oneshot
+SystemCallFilter=~uname:ENOENT uname:EILSEQ
+SystemCallErrorNumber=EACCES
diff --git a/test/test-execute/exec-temporaryfilesystem-options.service b/test/test-execute/exec-temporaryfilesystem-options.service
index b7a5baf93a..371e5674b1 100644
--- a/test/test-execute/exec-temporaryfilesystem-options.service
+++ b/test/test-execute/exec-temporaryfilesystem-options.service
@@ -5,11 +5,10 @@ Description=Test for TemporaryFileSystem with mount options
Type=oneshot
# The mount options default to "mode=0755,nodev,strictatime".
-# Let's override some of them, and test the behaviour of "ro".
+# Let's override some of them, and test "ro".
TemporaryFileSystem=/var:ro,mode=0700,nostrictatime
# Check /proc/self/mountinfo
-ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$11 !~ /(^|,)ro(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$11 !~ /(^|,)mode=700(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)ro(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
diff --git a/test/test-execute/exec-temporaryfilesystem-ro.service b/test/test-execute/exec-temporaryfilesystem-ro.service
index c0e3721a01..c161aecc30 100644
--- a/test/test-execute/exec-temporaryfilesystem-ro.service
+++ b/test/test-execute/exec-temporaryfilesystem-ro.service
@@ -10,6 +10,9 @@ ExecStart=/bin/sh -c 'test -d /var/test-exec-temporaryfilesystem/rw && test -d /
# Check TemporaryFileSystem= are empty
ExecStart=/bin/sh -c 'for i in $$(ls -A /var); do test $$i = test-exec-temporaryfilesystem || false; done'
+# Check default mode
+ExecStart=sh -x -c 'test "$$(stat -c %%a /var)" = "755"'
+
# Cannot create a file in /var
ExecStart=/bin/sh -c '! touch /var/hoge'
diff --git a/test/test-execute/exec-temporaryfilesystem-rw.service b/test/test-execute/exec-temporaryfilesystem-rw.service
index 379ad066fb..bb830595bc 100644
--- a/test/test-execute/exec-temporaryfilesystem-rw.service
+++ b/test/test-execute/exec-temporaryfilesystem-rw.service
@@ -10,6 +10,9 @@ ExecStart=test -d /var/test-exec-temporaryfilesystem/rw -a -d /var/test-exec-tem
# Check TemporaryFileSystem= are empty
ExecStart=sh -c 'for i in $$(ls -A /var); do test $$i = test-exec-temporaryfilesystem || false; done'
+# Check default mode
+ExecStart=sh -x -c 'test "$$(stat -c %%a /var)" = "755"'
+
# Create a file in /var
ExecStart=touch /var/hoge
diff --git a/test/test-execute/exec-umask-0177.service b/test/test-execute/exec-umask-0177.service
index a5e8fc4dbc..c18293e403 100644
--- a/test/test-execute/exec-umask-0177.service
+++ b/test/test-execute/exec-umask-0177.service
@@ -2,7 +2,7 @@
Description=Test for UMask
[Service]
-ExecStart=/bin/sh -x -c 'touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "600"'
+ExecStart=/bin/sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "600"'
Type=oneshot
UMask=0177
PrivateTmp=yes
diff --git a/test/test-execute/exec-umask-default.service b/test/test-execute/exec-umask-default.service
index 487f5e9b94..bf0573dd42 100644
--- a/test/test-execute/exec-umask-default.service
+++ b/test/test-execute/exec-umask-default.service
@@ -2,6 +2,6 @@
Description=Test for UMask default
[Service]
-ExecStart=/bin/sh -x -c 'touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "644"'
+ExecStart=/bin/sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "644"'
Type=oneshot
PrivateTmp=yes
diff --git a/test/test-functions b/test/test-functions
index e69420aeca..37069396b7 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -21,13 +21,38 @@ if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
ROOTLIBDIR=/usr/lib/systemd
fi
-BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln"
+PATH_TO_INIT=$ROOTLIBDIR/systemd
+
+BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs"
DEBUGTOOLS="df free ls stty cat ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find"
STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"
STATEFILE="$STATEDIR/.testdir"
TESTLOG="$STATEDIR/test.log"
+is_built_with_asan() {
+ if ! type -P objdump >/dev/null; then
+ ddebug "Failed to find objdump. Assuming systemd hasn't been built with ASAN."
+ return 1
+ fi
+
+ # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
+ local _asan_calls=$(objdump -dC $BUILD_DIR/systemd | egrep "callq\s+[0-9a-f]+\s+<__asan" -c)
+ if (( $_asan_calls < 1000 )); then
+ return 1
+ else
+ return 0
+ fi
+}
+
+IS_BUILT_WITH_ASAN=$(is_built_with_asan && echo yes || echo no)
+
+if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
+ STRIP_BINARIES=no
+ SKIP_INITRD=yes
+ PATH_TO_INIT=$ROOTLIBDIR/systemd-under-asan
+fi
+
function find_qemu_bin() {
# SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
# Either way, only use this version if we aren't running in KVM, because
@@ -121,7 +146,7 @@ KERNEL_APPEND="$PARAMS \
root=/dev/sda1 \
raid=noautodetect \
loglevel=2 \
-init=$ROOTLIBDIR/systemd \
+init=$PATH_TO_INIT \
console=ttyS0 \
selinux=0 \
printk.devkmsg=on \
@@ -165,7 +190,7 @@ $KERNEL_APPEND \
run_nspawn() {
[[ -d /run/systemd/system ]] || return 1
- local _nspawn_cmd="$BUILD_DIR/systemd-nspawn --register=no --kill-signal=SIGKILL --directory=$TESTDIR/nspawn-root $ROOTLIBDIR/systemd $KERNEL_APPEND"
+ local _nspawn_cmd="$BUILD_DIR/systemd-nspawn $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/$1 $PATH_TO_INIT $KERNEL_APPEND"
if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
_nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd"
fi
@@ -217,6 +242,9 @@ setup_basic_environment() {
strip_binaries
install_depmod_files
generate_module_dependencies
+ if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
+ create_asan_wrapper
+ fi
}
setup_selinux() {
@@ -293,6 +321,46 @@ EOF
chmod 0755 $_valgrind_wrapper
}
+create_asan_wrapper() {
+ local _asan_wrapper=$initdir/$ROOTLIBDIR/systemd-under-asan
+ ddebug "Create $_asan_wrapper"
+ cat >$_asan_wrapper <<EOF
+#!/bin/bash
+
+set -x
+
+DEFAULT_ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
+DEFAULT_UBSAN_OPTIONS=print_stacktrace=1:print_summary=1
+DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS:halt_on_error=1"
+
+mount -t proc proc /proc
+mount -t sysfs sysfs /sys
+mount -o remount,rw /
+
+PATH_TO_ASAN=\$(find / -name '*libasan*' | sed 1q)
+if [[ "\$PATH_TO_ASAN" ]]; then
+ # A lot of services (most notably dbus) won't start without preloading libasan
+ # See https://github.com/systemd/systemd/issues/5004
+ DEFAULT_ENVIRONMENT="\$DEFAULT_ENVIRONMENT LD_PRELOAD=\$PATH_TO_ASAN"
+fi
+echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
+
+# ASAN and syscall filters aren't compatible with each other.
+find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/'
+
+# The redirection of ASAN reports to a file prevents them from ending up in /dev/null.
+# But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886.
+JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d
+mkdir -p "\$JOURNALD_CONF_DIR"
+printf "[Service]\nEnvironment=ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd-journald.asan.log\n" >"\$JOURNALD_CONF_DIR/env.conf"
+
+export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
+exec $ROOTLIBDIR/systemd "\$@"
+EOF
+
+ chmod 0755 $_asan_wrapper
+}
+
create_strace_wrapper() {
local _strace_wrapper=$initdir/$ROOTLIBDIR/systemd-under-strace
ddebug "Create $_strace_wrapper"
@@ -353,20 +421,24 @@ get_ldpath() {
install_missing_libraries() {
# install possible missing libraries
- for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/*; do
+ for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do
LD_LIBRARY_PATH=$(get_ldpath $i) inst_libs $i
done
}
create_empty_image() {
+ local _size=500
+ if [[ "$STRIP_BINARIES" = "no" ]]; then
+ _size=$((2*_size))
+ fi
rm -f "$TESTDIR/rootdisk.img"
# Create the blank file to use as a root filesystem
- dd if=/dev/null of="$TESTDIR/rootdisk.img" bs=1M seek=400
+ dd if=/dev/null of="$TESTDIR/rootdisk.img" bs=1M seek="$_size"
LOOPDEV=$(losetup --show -P -f $TESTDIR/rootdisk.img)
[ -b "$LOOPDEV" ] || return 1
echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
sfdisk "$LOOPDEV" <<EOF
-,390M
+,$((_size-10))M
,
EOF
@@ -382,10 +454,10 @@ EOF
}
check_result_nspawn() {
- ret=1
- [[ -e $TESTDIR/nspawn-root/testok ]] && ret=0
- [[ -f $TESTDIR/nspawn-root/failed ]] && cp -a $TESTDIR/nspawn-root/failed $TESTDIR
- cp -a $TESTDIR/nspawn-root/var/log/journal $TESTDIR
+ local ret=1
+ [[ -e $TESTDIR/$1/testok ]] && ret=0
+ [[ -f $TESTDIR/$1/failed ]] && cp -a $TESTDIR/$1/failed $TESTDIR
+ cp -a $TESTDIR/$1/var/log/journal $TESTDIR
[[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
ls -l $TESTDIR/journal/*/*.journal
test -s $TESTDIR/failed && ret=$(($ret+1))
@@ -395,7 +467,7 @@ check_result_nspawn() {
# can be overridden in specific test
check_result_qemu() {
- ret=1
+ local ret=1
mkdir -p $TESTDIR/root
mount ${LOOPDEV}p1 $TESTDIR/root
[[ -e $TESTDIR/root/testok ]] && ret=0
@@ -433,9 +505,8 @@ install_execs() {
export PKG_CONFIG_PATH=$BUILD_DIR/src/core/
systemdsystemunitdir=$(pkg-config --variable=systemdsystemunitdir systemd)
systemduserunitdir=$(pkg-config --variable=systemduserunitdir systemd)
- egrep -ho '^Exec[^ ]*=[^ ]+' $initdir/{$systemdsystemunitdir,$systemduserunitdir}/*.service \
- | while read i; do
- i=${i##Exec*=}; i=${i##[@+\!-]}; i=${i##\!}
+ sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' $initdir/{$systemdsystemunitdir,$systemduserunitdir}/*.service \
+ | sort -u | while read i; do
# some {rc,halt}.local scripts and programs are okay to not exist, the rest should
inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ]
done
@@ -519,7 +590,15 @@ install_libnss() {
install_dbus() {
inst $ROOTLIBDIR/system/dbus.socket
- inst $ROOTLIBDIR/system/dbus.service
+
+ # Fedora rawhide replaced dbus.service with dbus-daemon.service
+ if [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
+ inst $ROOTLIBDIR/system/dbus-daemon.service
+ # Alias symlink
+ inst_symlink /etc/systemd/system/dbus.service
+ else
+ inst $ROOTLIBDIR/system/dbus.service
+ fi
find \
/etc/dbus-1 /usr/share/dbus-1 -xtype f \
@@ -552,12 +631,33 @@ install_pam() {
}
install_keymaps() {
+ # The first three paths may be deprecated.
+ # It seems now the last two paths are used by many distributions.
for i in \
/usr/lib/kbd/keymaps/include/* \
/usr/lib/kbd/keymaps/i386/include/* \
- /usr/lib/kbd/keymaps/i386/qwerty/us.*; do
+ /usr/lib/kbd/keymaps/i386/qwerty/us.* \
+ /usr/lib/kbd/keymaps/legacy/include/* \
+ /usr/lib/kbd/keymaps/legacy/i386/qwerty/us.*; do
+ [[ -f $i ]] || continue
+ inst $i
+ done
+
+ # When it takes any argument, then install more keymaps.
+ if [[ -n $1 ]]; then
+ for i in \
+ /usr/lib/kbd/keymaps/i386/*/* \
+ /usr/lib/kbd/keymaps/legacy/i386/*/*; do
[[ -f $i ]] || continue
inst $i
+ done
+ fi
+}
+
+install_zoneinfo() {
+ for i in /usr/share/zoneinfo/{,*/,*/*/}*; do
+ [[ -f $i ]] || continue
+ inst $i
done
}
@@ -595,6 +695,9 @@ setup_nspawn_root() {
cp -ar $initdir $TESTDIR/nspawn-root
# we don't mount in the nspawn root
rm -f $TESTDIR/nspawn-root/etc/fstab
+ if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
+ cp -ar $TESTDIR/nspawn-root $TESTDIR/unprivileged-nspawn-root
+ fi
}
setup_basic_dirs() {
@@ -687,7 +790,7 @@ _lvl2char() {
# This enables:
# dwarn "This is a warning"
# echo "This is a warning" | dwarn
-LOG_LEVEL=4
+LOG_LEVEL=${LOG_LEVEL:-4}
dlog() {
[ -z "$LOG_LEVEL" ] && return 0
@@ -1411,11 +1514,19 @@ test_run() {
fi
fi
if [ -z "$TEST_NO_NSPAWN" ]; then
- if run_nspawn; then
- check_result_nspawn || return 1
+ if run_nspawn "nspawn-root"; then
+ check_result_nspawn "nspawn-root" || return 1
else
dwarn "can't run systemd-nspawn, skipping"
fi
+
+ if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
+ if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "unprivileged-nspawn-root"; then
+ check_result_nspawn "unprivileged-nspawn-root" || return 1
+ else
+ dwarn "can't run systemd-nspawn, skipping"
+ fi
+ fi
fi
return 0
}
@@ -1444,7 +1555,9 @@ do_test() {
case $1 in
--run)
echo "TEST RUN: $TEST_DESCRIPTION"
- if test_run; then
+ test_run
+ ret=$?
+ if (( $ret == 0 )); then
echo "TEST RUN: $TEST_DESCRIPTION [OK]"
else
echo "TEST RUN: $TEST_DESCRIPTION [FAILED]"
diff --git a/test/test-network/conf/10-dropin-test.netdev b/test/test-network/conf/10-dropin-test.netdev
new file mode 100644
index 0000000000..d85ea5bb72
--- /dev/null
+++ b/test/test-network/conf/10-dropin-test.netdev
@@ -0,0 +1,4 @@
+[NetDev]
+Name=hoge
+Kind=dummy
+MACAddress=00:50:56:c0:00:18
diff --git a/test/test-network/conf/10-dropin-test.netdev.d/mac.conf b/test/test-network/conf/10-dropin-test.netdev.d/mac.conf
new file mode 100644
index 0000000000..0b3f765932
--- /dev/null
+++ b/test/test-network/conf/10-dropin-test.netdev.d/mac.conf
@@ -0,0 +1,2 @@
+[NetDev]
+MACAddress=00:50:56:c0:00:28
diff --git a/test/test-network/conf/10-dropin-test.netdev.d/name.conf b/test/test-network/conf/10-dropin-test.netdev.d/name.conf
new file mode 100644
index 0000000000..99059320a2
--- /dev/null
+++ b/test/test-network/conf/10-dropin-test.netdev.d/name.conf
@@ -0,0 +1,2 @@
+[NetDev]
+Name=dropin-test
diff --git a/test/test-network/conf/11-dummy.netdev b/test/test-network/conf/11-dummy.netdev
new file mode 100644
index 0000000000..6797eb4b09
--- /dev/null
+++ b/test/test-network/conf/11-dummy.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=test1
+Kind=dummy
diff --git a/test/test-network/conf/12-dummy.netdev b/test/test-network/conf/12-dummy.netdev
new file mode 100644
index 0000000000..a7fdc0f7e0
--- /dev/null
+++ b/test/test-network/conf/12-dummy.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=dummy98
+Kind=dummy
diff --git a/test/test-network/conf/21-macvlan.netdev b/test/test-network/conf/21-macvlan.netdev
new file mode 100644
index 0000000000..e9a3c5b347
--- /dev/null
+++ b/test/test-network/conf/21-macvlan.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=macvlan99
+Kind=macvlan
diff --git a/test/test-network/conf/21-macvtap.netdev b/test/test-network/conf/21-macvtap.netdev
new file mode 100644
index 0000000000..2c23aacfb2
--- /dev/null
+++ b/test/test-network/conf/21-macvtap.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=macvtap99
+Kind=macvtap
diff --git a/test/test-network/conf/21-vlan.netdev b/test/test-network/conf/21-vlan.netdev
new file mode 100644
index 0000000000..af39404b5f
--- /dev/null
+++ b/test/test-network/conf/21-vlan.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=vlan99
+Kind=vlan
diff --git a/test/test-network/conf/21-vlan.netdev.d/override.conf b/test/test-network/conf/21-vlan.netdev.d/override.conf
new file mode 100644
index 0000000000..cd61be5310
--- /dev/null
+++ b/test/test-network/conf/21-vlan.netdev.d/override.conf
@@ -0,0 +1,6 @@
+[VLAN]
+Id=99
+GVRP=true
+MVRP=true
+LooseBinding=true
+ReorderHeader=true
diff --git a/test/test-network/conf/21-vlan.network b/test/test-network/conf/21-vlan.network
new file mode 100644
index 0000000000..afe1debe08
--- /dev/null
+++ b/test/test-network/conf/21-vlan.network
@@ -0,0 +1,2 @@
+[Match]
+Name=test1
diff --git a/test/test-network/conf/21-vlan.network.d/override.conf b/test/test-network/conf/21-vlan.network.d/override.conf
new file mode 100644
index 0000000000..363fc90d7b
--- /dev/null
+++ b/test/test-network/conf/21-vlan.network.d/override.conf
@@ -0,0 +1,2 @@
+[Network]
+VLAN=vlan99
diff --git a/test/test-network/conf/23-active-slave.network b/test/test-network/conf/23-active-slave.network
new file mode 100644
index 0000000000..59a65960e9
--- /dev/null
+++ b/test/test-network/conf/23-active-slave.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Network]
+Bond=bond199
+ActiveSlave=true
diff --git a/test/test-network/conf/23-bond199.network b/test/test-network/conf/23-bond199.network
new file mode 100644
index 0000000000..31e5d12f75
--- /dev/null
+++ b/test/test-network/conf/23-bond199.network
@@ -0,0 +1,2 @@
+[Match]
+Name=bond199
diff --git a/test/test-network/conf/23-emit-lldp.network b/test/test-network/conf/23-emit-lldp.network
new file mode 100644
index 0000000000..de35045388
--- /dev/null
+++ b/test/test-network/conf/23-emit-lldp.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth-peer
+
+[Network]
+EmitLLDP=yes
diff --git a/test/test-network/conf/23-primary-slave.network b/test/test-network/conf/23-primary-slave.network
new file mode 100644
index 0000000000..380ae267a2
--- /dev/null
+++ b/test/test-network/conf/23-primary-slave.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Network]
+Bond=bond199
+PrimarySlave=true
diff --git a/test/test-network/conf/23-test1-bond199.network b/test/test-network/conf/23-test1-bond199.network
new file mode 100644
index 0000000000..6e7c28dfe7
--- /dev/null
+++ b/test/test-network/conf/23-test1-bond199.network
@@ -0,0 +1,6 @@
+[Match]
+Name=test1
+
+[Network]
+Bond=bond199
+PrimarySlave=true
diff --git a/test/test-network/conf/24-lldp.network b/test/test-network/conf/24-lldp.network
new file mode 100644
index 0000000000..fbdfb1b672
--- /dev/null
+++ b/test/test-network/conf/24-lldp.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth99
+
+[Network]
+LLDP=yes
diff --git a/test/test-network/conf/24-search-domain.network b/test/test-network/conf/24-search-domain.network
new file mode 100644
index 0000000000..970b130ab0
--- /dev/null
+++ b/test/test-network/conf/24-search-domain.network
@@ -0,0 +1,7 @@
+[Match]
+Name=dummy98
+
+[Network]
+Address=192.168.42.100
+DNS=192.168.42.1
+Domains= one two three four five six seven eight nine ten
diff --git a/test/test-network/conf/25-6rd-tunnel.netdev b/test/test-network/conf/25-6rd-tunnel.netdev
new file mode 100644
index 0000000000..756beccb70
--- /dev/null
+++ b/test/test-network/conf/25-6rd-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=sittun99
+Kind=sit
+
+[Tunnel]
+Local=10.65.223.238
+IPv6RapidDeploymentPrefix=2602::/24
diff --git a/test/test-network/conf/25-address-link-section.network b/test/test-network/conf/25-address-link-section.network
new file mode 100644
index 0000000000..759e83c325
--- /dev/null
+++ b/test/test-network/conf/25-address-link-section.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Link]
+MACAddress=00:01:02:aa:bb:cc
diff --git a/test/test-network/conf/25-address-section-miscellaneous.network b/test/test-network/conf/25-address-section-miscellaneous.network
new file mode 100644
index 0000000000..3a37d036ce
--- /dev/null
+++ b/test/test-network/conf/25-address-section-miscellaneous.network
@@ -0,0 +1,10 @@
+[Match]
+Name=dummy98
+
+[Address]
+Address=10.2.3.4/16
+PreferredLifetime=0
+Scope=link
+
+[Address]
+Address=2001:0db8:0:f101::1/64
diff --git a/test/test-network/conf/25-address-section.network b/test/test-network/conf/25-address-section.network
new file mode 100644
index 0000000000..d0fae69361
--- /dev/null
+++ b/test/test-network/conf/25-address-section.network
@@ -0,0 +1,11 @@
+[Match]
+Name=dummy98
+
+[Address]
+Address=10.2.3.4/16
+Peer=10.2.3.5/16
+Label=32
+
+[Address]
+Address=10.6.7.8/16
+Label=33
diff --git a/test/test-network/conf/25-bond-active-backup-slave.netdev b/test/test-network/conf/25-bond-active-backup-slave.netdev
new file mode 100644
index 0000000000..1bbbf75570
--- /dev/null
+++ b/test/test-network/conf/25-bond-active-backup-slave.netdev
@@ -0,0 +1,6 @@
+[NetDev]
+Name=bond199
+Kind=bond
+
+[Bond]
+Mode=active-backup
diff --git a/test/test-network/conf/25-bond-balanced-tlb.netdev b/test/test-network/conf/25-bond-balanced-tlb.netdev
new file mode 100644
index 0000000000..439ddf2809
--- /dev/null
+++ b/test/test-network/conf/25-bond-balanced-tlb.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=bond99
+Kind=bond
+
+[Bond]
+Mode=balance-tlb
+DynamicTransmitLoadBalancing=true
diff --git a/test/test-network/conf/25-bond.netdev b/test/test-network/conf/25-bond.netdev
new file mode 100644
index 0000000000..4e4885c44c
--- /dev/null
+++ b/test/test-network/conf/25-bond.netdev
@@ -0,0 +1,18 @@
+[NetDev]
+Name=bond99
+Kind=bond
+
+[Bond]
+Mode=802.3ad
+TransmitHashPolicy=layer3+4
+MIIMonitorSec=1s
+LACPTransmitRate=fast
+UpDelaySec=2s
+DownDelaySec=2s
+ResendIGMP=4
+MinLinks=1
+AdActorSystemPriority=1218
+AdUserPortKey=811
+AdActorSystem=00:11:22:33:44:55
+# feed the sanitizer
+AdActorSystem=00:11:22:33:44:55
diff --git a/test/test-network/conf/25-bridge.netdev b/test/test-network/conf/25-bridge.netdev
new file mode 100644
index 0000000000..e23abd5368
--- /dev/null
+++ b/test/test-network/conf/25-bridge.netdev
@@ -0,0 +1,13 @@
+[NetDev]
+Name=bridge99
+Kind=bridge
+
+[Bridge]
+HelloTimeSec=9
+MaxAgeSec=9
+ForwardDelaySec=9
+AgeingTimeSec=9
+Priority=9
+MulticastQuerier= true
+MulticastSnooping=true
+STP=true
diff --git a/test/test-network/conf/25-erspan-tunnel.netdev b/test/test-network/conf/25-erspan-tunnel.netdev
new file mode 100644
index 0000000000..746b7ac64f
--- /dev/null
+++ b/test/test-network/conf/25-erspan-tunnel.netdev
@@ -0,0 +1,11 @@
+[NetDev]
+Name=erspan-test
+Kind=erspan
+
+[Tunnel]
+Independent=true
+ERSPANIndex=123
+Local = 172.16.1.200
+Remote = 172.16.1.100
+Key=101
+SerializeTunneledPackets=true
diff --git a/test/test-network/conf/25-fibrule-invert.network b/test/test-network/conf/25-fibrule-invert.network
new file mode 100644
index 0000000000..bcca0c27ab
--- /dev/null
+++ b/test/test-network/conf/25-fibrule-invert.network
@@ -0,0 +1,10 @@
+[Match]
+Name=test1
+
+[RoutingPolicyRule]
+TypeOfService=0x08
+Table=7
+From= 192.168.100.18
+Priority=111
+IPProtocol = tcp
+InvertRule=true
diff --git a/test/test-network/conf/25-fibrule-port-range.network b/test/test-network/conf/25-fibrule-port-range.network
new file mode 100644
index 0000000000..36646ec0ff
--- /dev/null
+++ b/test/test-network/conf/25-fibrule-port-range.network
@@ -0,0 +1,11 @@
+[Match]
+Name=test1
+
+[RoutingPolicyRule]
+TypeOfService=0x08
+Table=7
+From= 192.168.100.18
+Priority=111
+SourcePort = 1123-1150
+DestinationPort = 3224-3290
+IPProtocol = tcp
diff --git a/test/test-network/conf/25-geneve.netdev b/test/test-network/conf/25-geneve.netdev
new file mode 100644
index 0000000000..279d71324e
--- /dev/null
+++ b/test/test-network/conf/25-geneve.netdev
@@ -0,0 +1,12 @@
+[NetDev]
+Name=geneve99
+Kind=geneve
+
+[GENEVE]
+Id=99
+Remote=192.168.22.1
+TTL=1
+UDPChecksum=true
+UDP6ZeroChecksumTx=true
+UDP6ZeroChecksumRx=true
+DestinationPort=6082
diff --git a/test/test-network/conf/25-gre-tunnel.netdev b/test/test-network/conf/25-gre-tunnel.netdev
new file mode 100644
index 0000000000..94d9320cdb
--- /dev/null
+++ b/test/test-network/conf/25-gre-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=gretun99
+Kind=gre
+
+[Tunnel]
+Local=10.65.223.238
+Remote=10.65.223.239
diff --git a/test/test-network/conf/25-gretap-tunnel.netdev b/test/test-network/conf/25-gretap-tunnel.netdev
new file mode 100644
index 0000000000..769e7653e4
--- /dev/null
+++ b/test/test-network/conf/25-gretap-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=gretap99
+Kind=gretap
+
+[Tunnel]
+Local=10.65.223.238
+Remote=10.65.223.239
diff --git a/test/test-network/conf/25-ip6gre-tunnel.netdev b/test/test-network/conf/25-ip6gre-tunnel.netdev
new file mode 100644
index 0000000000..b16e0b4969
--- /dev/null
+++ b/test/test-network/conf/25-ip6gre-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=ip6gretap99
+Kind=ip6gretap
+
+[Tunnel]
+Local=2a00:ffde:4567:edde::4987
+Remote=2001:473:fece:cafe::5179
diff --git a/test/test-network/conf/25-ip6tnl-tunnel.netdev b/test/test-network/conf/25-ip6tnl-tunnel.netdev
new file mode 100644
index 0000000000..713e685ea1
--- /dev/null
+++ b/test/test-network/conf/25-ip6tnl-tunnel.netdev
@@ -0,0 +1,8 @@
+[NetDev]
+Name=ip6tnl99
+Kind=ip6tnl
+
+[Tunnel]
+Mode=ip6ip6
+Local=2a00:ffde:4567:edde::4987
+Remote=2001:473:fece:cafe::5179
diff --git a/test/test-network/conf/25-ipip-tunnel-independent.netdev b/test/test-network/conf/25-ipip-tunnel-independent.netdev
new file mode 100644
index 0000000000..36ff8d9429
--- /dev/null
+++ b/test/test-network/conf/25-ipip-tunnel-independent.netdev
@@ -0,0 +1,9 @@
+[NetDev]
+Name=ipiptun99
+Kind=ipip
+MTUBytes=1480
+
+[Tunnel]
+Local=192.168.223.238
+Remote=192.169.224.239
+Independent=true
diff --git a/test/test-network/conf/25-ipip-tunnel.netdev b/test/test-network/conf/25-ipip-tunnel.netdev
new file mode 100644
index 0000000000..159ac72703
--- /dev/null
+++ b/test/test-network/conf/25-ipip-tunnel.netdev
@@ -0,0 +1,8 @@
+[NetDev]
+Name=ipiptun99
+Kind=ipip
+MTUBytes=1480
+
+[Tunnel]
+Local=192.168.223.238
+Remote=192.169.224.239
diff --git a/test/test-network/conf/25-ipv6-address-label-section.network b/test/test-network/conf/25-ipv6-address-label-section.network
new file mode 100644
index 0000000000..945b7dcc45
--- /dev/null
+++ b/test/test-network/conf/25-ipv6-address-label-section.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[IPv6AddressLabel]
+Label=4444
+Prefix=2004:da8:1:0::/64
diff --git a/test/test-network/conf/25-ipvlan.netdev b/test/test-network/conf/25-ipvlan.netdev
new file mode 100644
index 0000000000..9921b787a1
--- /dev/null
+++ b/test/test-network/conf/25-ipvlan.netdev
@@ -0,0 +1,6 @@
+[NetDev]
+Name=ipvlan99
+Kind=ipvlan
+
+[IPVLAN]
+Mode=L2
diff --git a/test/test-network/conf/25-isatap-tunnel.netdev b/test/test-network/conf/25-isatap-tunnel.netdev
new file mode 100644
index 0000000000..3aa882a261
--- /dev/null
+++ b/test/test-network/conf/25-isatap-tunnel.netdev
@@ -0,0 +1,8 @@
+[NetDev]
+Name=isataptun99
+Kind=sit
+
+[Tunnel]
+Local=10.65.223.238
+Remote=10.65.223.239
+ISATAP=true
diff --git a/test/test-network/conf/25-link-section-unmanaged.network b/test/test-network/conf/25-link-section-unmanaged.network
new file mode 100644
index 0000000000..4fe4916031
--- /dev/null
+++ b/test/test-network/conf/25-link-section-unmanaged.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Link]
+MACAddress=00:01:02:aa:bb:cc
+Unmanaged=true
diff --git a/test/test-network/conf/25-neighbor-section.network b/test/test-network/conf/25-neighbor-section.network
new file mode 100644
index 0000000000..d90802f44f
--- /dev/null
+++ b/test/test-network/conf/25-neighbor-section.network
@@ -0,0 +1,10 @@
+[Match]
+Name=dummy98
+
+[Neighbor]
+Address=192.168.10.1
+MACAddress=00:00:5e:00:02:65
+
+[Neighbor]
+Address=2004:da8:1:0::1
+MACAddress=00:00:5e:00:02:66
diff --git a/test/test-network/conf/25-route-gateway-on-link.network b/test/test-network/conf/25-route-gateway-on-link.network
new file mode 100644
index 0000000000..aa64c25516
--- /dev/null
+++ b/test/test-network/conf/25-route-gateway-on-link.network
@@ -0,0 +1,13 @@
+[Match]
+Name=dummy98
+
+[Address]
+Address=149.10.124.58/28
+
+[Route]
+Destination=149.10.124.64
+Scope=link
+
+[Route]
+Gateway=149.10.125.65
+GatewayOnlink=true
diff --git a/test/test-network/conf/25-route-gateway.network b/test/test-network/conf/25-route-gateway.network
new file mode 100644
index 0000000000..29d07f182a
--- /dev/null
+++ b/test/test-network/conf/25-route-gateway.network
@@ -0,0 +1,14 @@
+# test for issue #5430
+
+[Match]
+Name=dummy98
+
+[Address]
+Address=149.10.124.58/28
+
+[Route]
+Destination=149.10.124.64
+Scope=link
+
+[Route]
+Gateway=149.10.124.64
diff --git a/test/test-network/conf/25-route-reverse-order.network b/test/test-network/conf/25-route-reverse-order.network
new file mode 100644
index 0000000000..d1b47ad434
--- /dev/null
+++ b/test/test-network/conf/25-route-reverse-order.network
@@ -0,0 +1,15 @@
+[Match]
+Name=dummy98
+
+[Network]
+LinkLocalAddressing=ipv6
+Address=2001:1234:5:8f63::1/128
+IPv6AcceptRA=no
+
+[Route]
+Destination=2001:1234:5:8fff:ff:ff:ff:ff/128
+Scope=link
+
+[Route]
+Destination=::/0
+Gateway=2001:1234:5:8fff:ff:ff:ff:ff
diff --git a/test/test-network/conf/25-route-section.network b/test/test-network/conf/25-route-section.network
new file mode 100644
index 0000000000..c9c7a722c5
--- /dev/null
+++ b/test/test-network/conf/25-route-section.network
@@ -0,0 +1,8 @@
+[Match]
+Name=dummy98
+
+[Network]
+Address=192.168.0.15/24
+
+[Route]
+Gateway=192.168.0.1
diff --git a/test/test-network/conf/25-route-tcp-window-settings.network b/test/test-network/conf/25-route-tcp-window-settings.network
new file mode 100644
index 0000000000..e77a721740
--- /dev/null
+++ b/test/test-network/conf/25-route-tcp-window-settings.network
@@ -0,0 +1,10 @@
+[Match]
+Name=test1
+
+[Route]
+Destination=192.168.1.1
+InitialCongestionWindow=20
+
+[Route]
+Destination=192.168.1.2
+InitialAdvertisedReceiveWindow=30
diff --git a/test/test-network/conf/25-route-type.network b/test/test-network/conf/25-route-type.network
new file mode 100644
index 0000000000..9a104137de
--- /dev/null
+++ b/test/test-network/conf/25-route-type.network
@@ -0,0 +1,14 @@
+[Match]
+Name=dummy98
+
+[Route]
+Type=blackhole
+Destination=202.54.1.2
+
+[Route]
+Type=unreachable
+Destination=202.54.1.3
+
+[Route]
+Type=prohibit
+Destination=202.54.1.4
diff --git a/test/test-network/conf/25-sit-tunnel.netdev b/test/test-network/conf/25-sit-tunnel.netdev
new file mode 100644
index 0000000000..406d74bcf0
--- /dev/null
+++ b/test/test-network/conf/25-sit-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=sittun99
+Kind=sit
+
+[Tunnel]
+Local=10.65.223.238
+Remote=10.65.223.239
diff --git a/test/test-network/conf/25-sysctl.network b/test/test-network/conf/25-sysctl.network
new file mode 100644
index 0000000000..2452fb7e85
--- /dev/null
+++ b/test/test-network/conf/25-sysctl.network
@@ -0,0 +1,10 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPForward=true
+IPv6PrivacyExtensions=true
+IPv6DuplicateAddressDetection=3
+IPv6HopLimit=5
+IPv4ProxyARP=true
+IPv6ProxyNDP=true
diff --git a/test/test-network/conf/25-tap.netdev b/test/test-network/conf/25-tap.netdev
new file mode 100644
index 0000000000..bf5e7fe52c
--- /dev/null
+++ b/test/test-network/conf/25-tap.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=tap99
+Kind=tap
+
+[Tap]
+MultiQueue=true
+PacketInfo=true
diff --git a/test/test-network/conf/25-tun.netdev b/test/test-network/conf/25-tun.netdev
new file mode 100644
index 0000000000..380ab21552
--- /dev/null
+++ b/test/test-network/conf/25-tun.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=tun99
+Kind=tun
+
+[Tun]
+MultiQueue=true
+PacketInfo=true
diff --git a/test/test-network/conf/25-vcan.netdev b/test/test-network/conf/25-vcan.netdev
new file mode 100644
index 0000000000..ff1979536a
--- /dev/null
+++ b/test/test-network/conf/25-vcan.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=vcan99
+Kind=vcan
diff --git a/test/test-network/conf/25-veth.netdev b/test/test-network/conf/25-veth.netdev
new file mode 100644
index 0000000000..9ae4ad53b8
--- /dev/null
+++ b/test/test-network/conf/25-veth.netdev
@@ -0,0 +1,8 @@
+[NetDev]
+Name=veth99
+Kind=veth
+MACAddress=12:34:56:78:9a:bc
+
+[Peer]
+Name=veth-peer
+MACAddress=12:34:56:78:9a:bd
diff --git a/test/test-network/conf/25-vrf.netdev b/test/test-network/conf/25-vrf.netdev
new file mode 100644
index 0000000000..bf949ec293
--- /dev/null
+++ b/test/test-network/conf/25-vrf.netdev
@@ -0,0 +1,6 @@
+[NetDev]
+Name=vrf99
+Kind=vrf
+
+[VRF]
+TableId=42
diff --git a/test/test-network/conf/25-vti-tunnel.netdev b/test/test-network/conf/25-vti-tunnel.netdev
new file mode 100644
index 0000000000..cec6259781
--- /dev/null
+++ b/test/test-network/conf/25-vti-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=vtitun99
+Kind=vti
+
+[Tunnel]
+Local=10.65.223.238
+Remote=10.65.223.239
diff --git a/test/test-network/conf/25-vti6-tunnel.netdev b/test/test-network/conf/25-vti6-tunnel.netdev
new file mode 100644
index 0000000000..d150c9ce86
--- /dev/null
+++ b/test/test-network/conf/25-vti6-tunnel.netdev
@@ -0,0 +1,7 @@
+[NetDev]
+Name=vti6tun99
+Kind=vti6
+
+[Tunnel]
+Local=2a00:ffde:4567:edde::4987
+Remote=2001:473:fece:cafe::5179
diff --git a/test/test-network/conf/25-vxlan.netdev b/test/test-network/conf/25-vxlan.netdev
new file mode 100644
index 0000000000..819a58356f
--- /dev/null
+++ b/test/test-network/conf/25-vxlan.netdev
@@ -0,0 +1,16 @@
+[NetDev]
+Name=vxlan99
+Kind=vxlan
+
+[VXLAN]
+Id=999
+L2MissNotification=true
+L3MissNotification=true
+RouteShortCircuit=true
+UDPChecksum=true
+UDP6ZeroChecksumTx=true
+UDP6ZeroChecksumRx=true
+RemoteChecksumTx=true
+RemoteChecksumRx=true
+GroupPolicyExtension=true
+DestinationPort=5555
diff --git a/test/test-network/conf/25-wireguard.netdev b/test/test-network/conf/25-wireguard.netdev
new file mode 100644
index 0000000000..01c5f2a28d
--- /dev/null
+++ b/test/test-network/conf/25-wireguard.netdev
@@ -0,0 +1,15 @@
+[NetDev]
+Name=wg99
+Kind=wireguard
+
+[WireGuard]
+PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
+ListenPort=51820
+FwMark=1234
+
+[WireGuardPeer]
+PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=
+AllowedIPs=fd31:bf08:57cb::/48,192.168.26.0/24
+Endpoint=wireguard.example.com:51820
+PresharedKey=IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=
+PersistentKeepalive=20
diff --git a/test/test-network/conf/26-bridge-slave-interface-1.network b/test/test-network/conf/26-bridge-slave-interface-1.network
new file mode 100644
index 0000000000..81b372fb6d
--- /dev/null
+++ b/test/test-network/conf/26-bridge-slave-interface-1.network
@@ -0,0 +1,12 @@
+[Match]
+Name=dummy98
+
+[Network]
+Bridge=bridge99
+
+[Bridge]
+Cost=400
+HairPin = true
+FastLeave = true
+UnicastFlood = true
+MulticastToUnicast = true
diff --git a/test/test-network/conf/26-bridge-slave-interface-2.network b/test/test-network/conf/26-bridge-slave-interface-2.network
new file mode 100644
index 0000000000..45ec2de999
--- /dev/null
+++ b/test/test-network/conf/26-bridge-slave-interface-2.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+Bridge=bridge99
diff --git a/test/test-network/conf/26-bridge.netdev b/test/test-network/conf/26-bridge.netdev
new file mode 100644
index 0000000000..9b31e06b5b
--- /dev/null
+++ b/test/test-network/conf/26-bridge.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Name=bridge99
+Kind=bridge
diff --git a/test/test-network/conf/6rd.network b/test/test-network/conf/6rd.network
new file mode 100644
index 0000000000..84e5af0ff0
--- /dev/null
+++ b/test/test-network/conf/6rd.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=sittun99
diff --git a/test/test-network/conf/bridge99.network b/test/test-network/conf/bridge99.network
new file mode 100644
index 0000000000..39e48ce2e1
--- /dev/null
+++ b/test/test-network/conf/bridge99.network
@@ -0,0 +1,6 @@
+[Match]
+Name=bridge99
+
+[Network]
+Address=192.168.0.15/24
+Gateway=192.168.0.1
diff --git a/test/test-network/conf/configure-without-carrier.network b/test/test-network/conf/configure-without-carrier.network
new file mode 100644
index 0000000000..5bd9d7e84a
--- /dev/null
+++ b/test/test-network/conf/configure-without-carrier.network
@@ -0,0 +1,7 @@
+[Match]
+Name=test1
+
+[Network]
+Address=192.168.0.15/24
+Gateway=192.168.0.1
+ConfigureWithoutCarrier=true
diff --git a/test/test-network/conf/dhcp-client-anonymize.network b/test/test-network/conf/dhcp-client-anonymize.network
new file mode 100644
index 0000000000..a910cba0f7
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-anonymize.network
@@ -0,0 +1,16 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCP]
+Anonymize=true
+UseMTU=true
+UseRoutes=true
+SendHostname=true
+UseHostname=true
+Hostname=test-hostname
+ClientIdentifier=mac
+VendorClassIdentifier=SusantVendorTest
diff --git a/test/test-network/conf/dhcp-client-critical-connection.network b/test/test-network/conf/dhcp-client-critical-connection.network
new file mode 100644
index 0000000000..0e65dec0ae
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-critical-connection.network
@@ -0,0 +1,9 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCP]
+CriticalConnection=true
diff --git a/test/test-network/conf/dhcp-client-ipv4-dhcp-settings.network b/test/test-network/conf/dhcp-client-ipv4-dhcp-settings.network
new file mode 100644
index 0000000000..5c4ca22ab7
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv4-dhcp-settings.network
@@ -0,0 +1,15 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCP]
+UseMTU=true
+UseRoutes=true
+SendHostname=true
+UseHostname=true
+Hostname=test-hostname
+ClientIdentifier=mac
+VendorClassIdentifier=SusantVendorTest
diff --git a/test/test-network/conf/dhcp-client-ipv4-only-ipv6-disabled.network b/test/test-network/conf/dhcp-client-ipv4-only-ipv6-disabled.network
new file mode 100644
index 0000000000..c980bf9fca
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv4-only-ipv6-disabled.network
@@ -0,0 +1,6 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
diff --git a/test/test-network/conf/dhcp-client-ipv4-only.network b/test/test-network/conf/dhcp-client-ipv4-only.network
new file mode 100644
index 0000000000..9bc019a674
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv4-only.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
diff --git a/test/test-network/conf/dhcp-client-ipv6-only.network b/test/test-network/conf/dhcp-client-ipv6-only.network
new file mode 100644
index 0000000000..1f70c3b86d
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv6-only.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv6
diff --git a/test/test-network/conf/dhcp-client-ipv6-rapid-commit.network b/test/test-network/conf/dhcp-client-ipv6-rapid-commit.network
new file mode 100644
index 0000000000..52fa58729e
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv6-rapid-commit.network
@@ -0,0 +1,8 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv6
+
+[DHCP]
+RapidCommit=false
diff --git a/test/test-network/conf/dhcp-client-listen-port.network b/test/test-network/conf/dhcp-client-listen-port.network
new file mode 100644
index 0000000000..73ac364630
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-listen-port.network
@@ -0,0 +1,8 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
+
+[DHCP]
+ListenPort=5555
diff --git a/test/test-network/conf/dhcp-client-route-metric.network b/test/test-network/conf/dhcp-client-route-metric.network
new file mode 100644
index 0000000000..aa04aaef33
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-route-metric.network
@@ -0,0 +1,10 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCP]
+UseRoutes=true
+RouteMetric=24
diff --git a/test/test-network/conf/dhcp-client-route-table.network b/test/test-network/conf/dhcp-client-route-table.network
new file mode 100644
index 0000000000..75f16aa22d
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-route-table.network
@@ -0,0 +1,10 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCP]
+UseRoutes=true
+RouteTable=12
diff --git a/test/test-network/conf/dhcp-client-timezone-router.network b/test/test-network/conf/dhcp-client-timezone-router.network
new file mode 100644
index 0000000000..23e04649de
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-timezone-router.network
@@ -0,0 +1,10 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=false
+DHCP=ipv4
+
+[DHCP]
+UseRoutes=true
+UseTimezone=true
diff --git a/test/test-network/conf/dhcp-client.network b/test/test-network/conf/dhcp-client.network
new file mode 100644
index 0000000000..5629bc48e4
--- /dev/null
+++ b/test/test-network/conf/dhcp-client.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
diff --git a/test/test-network/conf/dhcp-server-timezone-router.network b/test/test-network/conf/dhcp-server-timezone-router.network
new file mode 100644
index 0000000000..3ebbf05d3e
--- /dev/null
+++ b/test/test-network/conf/dhcp-server-timezone-router.network
@@ -0,0 +1,13 @@
+[Match]
+Name=veth-peer
+
+[Network]
+IPv6AcceptRA=false
+Address=192.168.5.1/24
+DHCPServer=yes
+
+[DHCPServer]
+PoolOffset=10
+PoolSize=50
+EmitRouter=yes
+Timezone=Europe/Berlin
diff --git a/test/test-network/conf/dhcp-server-veth-peer.network b/test/test-network/conf/dhcp-server-veth-peer.network
new file mode 100644
index 0000000000..7b38e72b7e
--- /dev/null
+++ b/test/test-network/conf/dhcp-server-veth-peer.network
@@ -0,0 +1,6 @@
+[Match]
+Name=veth-peer
+
+[Network]
+Address=2600::1
+Address=192.168.5.1/24
diff --git a/test/test-network/conf/dhcp-server.network b/test/test-network/conf/dhcp-server.network
new file mode 100644
index 0000000000..9e49691a9b
--- /dev/null
+++ b/test/test-network/conf/dhcp-server.network
@@ -0,0 +1,12 @@
+[Match]
+Name=veth-peer
+
+[Network]
+Address=192.168.5.1/24
+DHCPServer=yes
+
+[DHCPServer]
+PoolOffset=10
+PoolSize=50
+DNS=192.168.5.1
+NTP=192.168.5.1
diff --git a/test/test-network/conf/dhcp-v4-server-veth-peer.network b/test/test-network/conf/dhcp-v4-server-veth-peer.network
new file mode 100644
index 0000000000..5c91d65432
--- /dev/null
+++ b/test/test-network/conf/dhcp-v4-server-veth-peer.network
@@ -0,0 +1,6 @@
+[Match]
+Name=veth-peer
+
+[Network]
+Address=192.168.0.1
+Address=192.168.5.1
diff --git a/test/test-network/conf/gretap.network b/test/test-network/conf/gretap.network
new file mode 100644
index 0000000000..88b9250349
--- /dev/null
+++ b/test/test-network/conf/gretap.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=gretap99
diff --git a/test/test-network/conf/gretun.network b/test/test-network/conf/gretun.network
new file mode 100644
index 0000000000..376074cb1b
--- /dev/null
+++ b/test/test-network/conf/gretun.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=gretun99
diff --git a/test/test-network/conf/ip6gretap.network b/test/test-network/conf/ip6gretap.network
new file mode 100644
index 0000000000..cad0bae04a
--- /dev/null
+++ b/test/test-network/conf/ip6gretap.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=ip6gretap99
diff --git a/test/test-network/conf/ip6tnl.network b/test/test-network/conf/ip6tnl.network
new file mode 100644
index 0000000000..41e3448495
--- /dev/null
+++ b/test/test-network/conf/ip6tnl.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=ip6tnl99
diff --git a/test/test-network/conf/ipip.network b/test/test-network/conf/ipip.network
new file mode 100644
index 0000000000..4ce6714904
--- /dev/null
+++ b/test/test-network/conf/ipip.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=ipiptun99
diff --git a/test/test-network/conf/ipv6-prefix-veth.network b/test/test-network/conf/ipv6-prefix-veth.network
new file mode 100644
index 0000000000..3d2acecde2
--- /dev/null
+++ b/test/test-network/conf/ipv6-prefix-veth.network
@@ -0,0 +1,5 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
diff --git a/test/test-network/conf/ipv6-prefix.network b/test/test-network/conf/ipv6-prefix.network
new file mode 100644
index 0000000000..c1932a84d3
--- /dev/null
+++ b/test/test-network/conf/ipv6-prefix.network
@@ -0,0 +1,10 @@
+[Match]
+Name=veth-peer
+
+[Network]
+IPv6PrefixDelegation=yes
+
+[IPv6Prefix]
+Prefix=2002:da8:1:0::/64
+PreferredLifetimeSec=1000s
+ValidLifetimeSec=2100s
diff --git a/test/test-network/conf/ipvlan.network b/test/test-network/conf/ipvlan.network
new file mode 100644
index 0000000000..d053220550
--- /dev/null
+++ b/test/test-network/conf/ipvlan.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+IPVLAN=ipvlan99
diff --git a/test/test-network/conf/isatap.network b/test/test-network/conf/isatap.network
new file mode 100644
index 0000000000..e8d03ed6bd
--- /dev/null
+++ b/test/test-network/conf/isatap.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=isataptun99
diff --git a/test/test-network/conf/macvlan.network b/test/test-network/conf/macvlan.network
new file mode 100644
index 0000000000..a41c1f916c
--- /dev/null
+++ b/test/test-network/conf/macvlan.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+MACVLAN=macvlan99
diff --git a/test/test-network/conf/macvtap.network b/test/test-network/conf/macvtap.network
new file mode 100644
index 0000000000..6ee99ab9ce
--- /dev/null
+++ b/test/test-network/conf/macvtap.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+MACVTAP=macvtap99
diff --git a/test/test-network/conf/routing-policy-rule.network b/test/test-network/conf/routing-policy-rule.network
new file mode 100644
index 0000000000..46b87c5a9a
--- /dev/null
+++ b/test/test-network/conf/routing-policy-rule.network
@@ -0,0 +1,10 @@
+[Match]
+Name=test1
+
+[RoutingPolicyRule]
+TypeOfService=0x08
+Table=7
+From= 192.168.100.18
+Priority=111
+IncomingInterface=test1
+OutgoingInterface=test1
diff --git a/test/test-network/conf/sit.network b/test/test-network/conf/sit.network
new file mode 100644
index 0000000000..84e5af0ff0
--- /dev/null
+++ b/test/test-network/conf/sit.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=sittun99
diff --git a/test/test-network/conf/test-static.network b/test/test-network/conf/test-static.network
new file mode 100644
index 0000000000..636c55c8e3
--- /dev/null
+++ b/test/test-network/conf/test-static.network
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Network]
+Address=192.168.0.15/24
+Gateway=192.168.0.1
diff --git a/test/test-network/conf/vti.network b/test/test-network/conf/vti.network
new file mode 100644
index 0000000000..7fbad6a82d
--- /dev/null
+++ b/test/test-network/conf/vti.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=vtitun99
diff --git a/test/test-network/conf/vti6.network b/test/test-network/conf/vti6.network
new file mode 100644
index 0000000000..49a9d11fff
--- /dev/null
+++ b/test/test-network/conf/vti6.network
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+Tunnel=vti6tun99
diff --git a/test/test-network/conf/vxlan.network b/test/test-network/conf/vxlan.network
new file mode 100644
index 0000000000..80b405574c
--- /dev/null
+++ b/test/test-network/conf/vxlan.network
@@ -0,0 +1,5 @@
+[Match]
+Name=test1
+
+[Network]
+VXLAN=vxlan99
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
new file mode 100755
index 0000000000..19572be151
--- /dev/null
+++ b/test/test-network/systemd-networkd-tests.py
@@ -0,0 +1,1179 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1+
+# systemd-networkd tests
+
+import os
+import re
+import shutil
+import signal
+import socket
+import subprocess
+import sys
+import threading
+import time
+import unittest
+from shutil import copytree
+
+network_unit_file_path='/run/systemd/network'
+networkd_runtime_directory='/run/systemd/netif'
+networkd_ci_path='/run/networkd-ci'
+network_sysctl_ipv6_path='/proc/sys/net/ipv6/conf'
+network_sysctl_ipv4_path='/proc/sys/net/ipv4/conf'
+
+dnsmasq_config_file='/run/networkd-ci/test-dnsmasq.conf'
+dnsmasq_pid_file='/run/networkd-ci/test-test-dnsmasq.pid'
+dnsmasq_log_file='/run/networkd-ci/test-dnsmasq-log-file'
+
+def is_module_available(module_name):
+ lsmod_output = subprocess.check_output('lsmod', universal_newlines=True)
+ module_re = re.compile(r'^{0}\b'.format(re.escape(module_name)), re.MULTILINE)
+ return module_re.search(lsmod_output) or not subprocess.call(["modprobe", module_name])
+
+def expectedFailureIfModuleIsNotAvailable(module_name):
+ def f(func):
+ if not is_module_available(module_name):
+ return unittest.expectedFailure(func)
+ return func
+
+ return f
+
+def setUpModule():
+
+ os.makedirs(network_unit_file_path, exist_ok=True)
+ os.makedirs(networkd_ci_path, exist_ok=True)
+
+ shutil.rmtree(networkd_ci_path)
+ copytree(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_path)
+
+ subprocess.check_call('systemctl stop systemd-networkd.socket', shell=True)
+
+def tearDownModule():
+ shutil.rmtree(networkd_ci_path)
+
+ subprocess.check_call('systemctl stop systemd-networkd.service', shell=True)
+ subprocess.check_call('systemctl start systemd-networkd.socket', shell=True)
+ subprocess.check_call('systemctl start systemd-networkd.service', shell=True)
+
+class Utilities():
+ dhcp_server_data = []
+
+ def read_link_attr(self, link, dev, attribute):
+ with open(os.path.join(os.path.join(os.path.join('/sys/class/net/', link), dev), attribute)) as f:
+ return f.readline().strip()
+
+ def read_bridge_port_attr(self, bridge, link, attribute):
+
+ path_bridge = os.path.join('/sys/devices/virtual/net', bridge)
+ path_port = 'lower_' + link + '/brport'
+ path = os.path.join(path_bridge, path_port)
+
+ with open(os.path.join(path, attribute)) as f:
+ return f.readline().strip()
+
+ def link_exits(self, link):
+ return os.path.exists(os.path.join('/sys/class/net', link))
+
+ def link_remove(self, links):
+ for link in links:
+ if os.path.exists(os.path.join('/sys/class/net', link)):
+ subprocess.call(['ip', 'link', 'del', 'dev', link])
+ time.sleep(1)
+
+ def read_ipv6_sysctl_attr(self, link, attribute):
+ with open(os.path.join(os.path.join(network_sysctl_ipv6_path, link), attribute)) as f:
+ return f.readline().strip()
+
+ def read_ipv4_sysctl_attr(self, link, attribute):
+ with open(os.path.join(os.path.join(network_sysctl_ipv4_path, link), attribute)) as f:
+ return f.readline().strip()
+
+ def copy_unit_to_networkd_unit_path(self, *units):
+ for unit in units:
+ shutil.copy(os.path.join(networkd_ci_path, unit), network_unit_file_path)
+ if (os.path.exists(os.path.join(networkd_ci_path, unit + '.d'))):
+ copytree(os.path.join(networkd_ci_path, unit + '.d'), os.path.join(network_unit_file_path, unit + '.d'))
+
+ def remove_unit_from_networkd_path(self, units):
+ for unit in units:
+ if (os.path.exists(os.path.join(network_unit_file_path, unit))):
+ os.remove(os.path.join(network_unit_file_path, unit))
+ if (os.path.exists(os.path.join(network_unit_file_path, unit + '.d'))):
+ shutil.rmtree(os.path.join(network_unit_file_path, unit + '.d'))
+
+ def start_dnsmasq(self):
+ subprocess.check_call('dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range=2600::10,2600::20 --dhcp-range=192.168.5.10,192.168.5.200 -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5', shell=True)
+
+ time.sleep(10)
+
+ def stop_dnsmasq(self, pid_file):
+ if os.path.exists(pid_file):
+ with open(pid_file, 'r') as f:
+ pid = f.read().rstrip(' \t\r\n\0')
+ os.kill(int(pid), signal.SIGTERM)
+
+ os.remove(pid_file)
+
+ def search_words_in_dnsmasq_log(self, words, show_all=False):
+ if os.path.exists(dnsmasq_log_file):
+ with open (dnsmasq_log_file) as in_file:
+ contents = in_file.read()
+ if show_all:
+ print(contents)
+ for line in contents.split('\n'):
+ if words in line:
+ in_file.close()
+ print("%s, %s" % (words, line))
+ return True
+ return False
+
+ def remove_lease_file(self):
+ if os.path.exists(os.path.join(networkd_ci_path, 'lease')):
+ os.remove(os.path.join(networkd_ci_path, 'lease'))
+
+ def remove_log_file(self):
+ if os.path.exists(dnsmasq_log_file):
+ os.remove(dnsmasq_log_file)
+
+ def start_networkd(self):
+ if (os.path.exists(os.path.join(networkd_runtime_directory, 'state'))):
+ subprocess.check_call('systemctl stop systemd-networkd', shell=True)
+ os.remove(os.path.join(networkd_runtime_directory, 'state'))
+ subprocess.check_call('systemctl start systemd-networkd', shell=True)
+ else:
+ subprocess.check_call('systemctl restart systemd-networkd', shell=True)
+ time.sleep(5)
+ print()
+
+global ip
+global port
+
+class DHCPServer(threading.Thread):
+ def __init__(self, name):
+ threading.Thread.__init__(self)
+ self.name = name
+
+ def run(self):
+ self.start_dhcp_server()
+
+ def start_dhcp_server(self):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+ server_address = ('0.0.0.0', 67)
+ sock.bind(server_address)
+
+ print('Starting DHCP Server ...\n')
+ data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
+
+ global ip
+ ip = addr[0]
+
+ global port
+ port = addr[1]
+ sock.close()
+
+class NetworkdNetDevTests(unittest.TestCase, Utilities):
+
+ links =[
+ '6rdtun99',
+ 'bond99',
+ 'bridge99',
+ 'dropin-test',
+ 'dummy98',
+ 'erspan-test',
+ 'geneve99',
+ 'gretap99',
+ 'gretun99',
+ 'ip6gretap99',
+ 'ip6tnl99',
+ 'ipiptun99',
+ 'ipvlan99',
+ 'isataptun99',
+ 'macvlan99',
+ 'macvtap99',
+ 'sittun99',
+ 'tap99',
+ 'test1',
+ 'tun99',
+ 'vcan99',
+ 'veth99',
+ 'vlan99',
+ 'vrf99',
+ 'vti6tun99',
+ 'vtitun99',
+ 'vxlan99',
+ 'wg99']
+
+ units = [
+ '10-dropin-test.netdev',
+ '11-dummy.netdev',
+ '12-dummy.netdev',
+ '21-macvlan.netdev',
+ '21-macvtap.netdev',
+ '21-vlan.netdev',
+ '21-vlan.network',
+ '25-6rd-tunnel.netdev',
+ '25-bond.netdev',
+ '25-bond-balanced-tlb.netdev',
+ '25-bridge.netdev',
+ '25-erspan-tunnel.netdev',
+ '25-geneve.netdev',
+ '25-gretap-tunnel.netdev',
+ '25-gre-tunnel.netdev',
+ '25-ip6gre-tunnel.netdev',
+ '25-ip6tnl-tunnel.netdev',
+ '25-ipip-tunnel-independent.netdev',
+ '25-ipip-tunnel.netdev',
+ '25-ipvlan.netdev',
+ '25-isatap-tunnel.netdev',
+ '25-sit-tunnel.netdev',
+ '25-tap.netdev',
+ '25-tun.netdev',
+ '25-vcan.netdev',
+ '25-veth.netdev',
+ '25-vrf.netdev',
+ '25-vti6-tunnel.netdev',
+ '25-vti-tunnel.netdev',
+ '25-vxlan.netdev',
+ '25-wireguard.netdev',
+ '6rd.network',
+ 'gre.network',
+ 'gretap.network',
+ 'gretun.network',
+ 'ip6gretap.network',
+ 'ip6tnl.network',
+ 'ipip.network',
+ 'ipvlan.network',
+ 'isatap.network',
+ 'macvlan.network',
+ 'macvtap.network',
+ 'sit.network',
+ 'vti6.network',
+ 'vti.network',
+ 'vxlan.network']
+
+ def setUp(self):
+ self.link_remove(self.links)
+
+ def tearDown(self):
+ self.link_remove(self.links)
+ self.remove_unit_from_networkd_path(self.units)
+
+ def test_dropin(self):
+ self.copy_unit_to_networkd_unit_path('10-dropin-test.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dropin-test'))
+
+ output = subprocess.check_output(['ip', 'link', 'show', 'dropin-test']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '00:50:56:c0:00:28')
+
+ def test_bridge(self):
+ self.copy_unit_to_networkd_unit_path('25-bridge.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('bridge99'))
+
+ self.assertEqual('900', self.read_link_attr('bridge99', 'bridge', 'hello_time'))
+ self.assertEqual('900', self.read_link_attr('bridge99', 'bridge', 'max_age'))
+ self.assertEqual('900', self.read_link_attr('bridge99', 'bridge','forward_delay'))
+ self.assertEqual('900', self.read_link_attr('bridge99', 'bridge','ageing_time'))
+ self.assertEqual('9', self.read_link_attr('bridge99', 'bridge','priority'))
+ self.assertEqual('1', self.read_link_attr('bridge99', 'bridge','multicast_querier'))
+ self.assertEqual('1', self.read_link_attr('bridge99', 'bridge','multicast_snooping'))
+ self.assertEqual('1', self.read_link_attr('bridge99', 'bridge','stp_state'))
+
+ def test_bond(self):
+ self.copy_unit_to_networkd_unit_path('25-bond.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('bond99'))
+
+ self.assertEqual('802.3ad 4', self.read_link_attr('bond99', 'bonding', 'mode'))
+ self.assertEqual('layer3+4 1', self.read_link_attr('bond99', 'bonding', 'xmit_hash_policy'))
+ self.assertEqual('1000', self.read_link_attr('bond99', 'bonding', 'miimon'))
+ self.assertEqual('fast 1', self.read_link_attr('bond99', 'bonding', 'lacp_rate'))
+ self.assertEqual('2000', self.read_link_attr('bond99', 'bonding', 'updelay'))
+ self.assertEqual('2000', self.read_link_attr('bond99', 'bonding', 'downdelay'))
+ self.assertEqual('4', self.read_link_attr('bond99', 'bonding', 'resend_igmp'))
+ self.assertEqual('1', self.read_link_attr('bond99', 'bonding', 'min_links'))
+ self.assertEqual('1218', self.read_link_attr('bond99', 'bonding', 'ad_actor_sys_prio'))
+ self.assertEqual('811', self.read_link_attr('bond99', 'bonding', 'ad_user_port_key'))
+ self.assertEqual('00:11:22:33:44:55', self.read_link_attr('bond99', 'bonding', 'ad_actor_system'))
+
+ def test_bond_balanced_tlb(self):
+ self.copy_unit_to_networkd_unit_path('25-bond-balanced-tlb.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('bond99'))
+
+ self.assertEqual('balance-tlb 5', self.read_link_attr('bond99', 'bonding', 'mode'))
+ self.assertEqual('1', self.read_link_attr('bond99', 'bonding', 'tlb_dynamic_lb'))
+
+ def test_vlan(self):
+ self.copy_unit_to_networkd_unit_path('21-vlan.netdev', '11-dummy.netdev', '21-vlan.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('vlan99'))
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vlan99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertTrue(output, 'REORDER_HDR')
+ self.assertTrue(output, 'LOOSE_BINDING')
+ self.assertTrue(output, 'GVRP')
+ self.assertTrue(output, 'MVRP')
+ self.assertTrue(output, '99')
+
+ def test_macvtap(self):
+ self.copy_unit_to_networkd_unit_path('21-macvtap.netdev', '11-dummy.netdev', 'macvtap.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('macvtap99'))
+
+ def test_macvlan(self):
+ self.copy_unit_to_networkd_unit_path('21-macvlan.netdev', '11-dummy.netdev', 'macvlan.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('macvlan99'))
+
+ @expectedFailureIfModuleIsNotAvailable('ipvlan')
+ def test_ipvlan(self):
+ self.copy_unit_to_networkd_unit_path('25-ipvlan.netdev', '11-dummy.netdev', 'ipvlan.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('ipvlan99'))
+
+ def test_veth(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ def test_dummy(self):
+ self.copy_unit_to_networkd_unit_path('11-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('test1'))
+
+ def test_tun(self):
+ self.copy_unit_to_networkd_unit_path('25-tun.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('tun99'))
+
+ def test_tap(self):
+ self.copy_unit_to_networkd_unit_path('25-tap.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('tap99'))
+
+ @expectedFailureIfModuleIsNotAvailable('vrf')
+ def test_vrf(self):
+ self.copy_unit_to_networkd_unit_path('25-vrf.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('vrf99'))
+
+ @expectedFailureIfModuleIsNotAvailable('vcan')
+ def test_vcan(self):
+ self.copy_unit_to_networkd_unit_path('25-vcan.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('vcan99'))
+
+ @expectedFailureIfModuleIsNotAvailable('wireguard')
+ def test_wireguard(self):
+ self.copy_unit_to_networkd_unit_path('25-wireguard.netdev')
+ self.start_networkd()
+
+ if shutil.which('wg'):
+ subprocess.call('wg')
+
+ self.assertTrue(self.link_exits('wg99'))
+
+ def test_geneve(self):
+ self.copy_unit_to_networkd_unit_path('25-geneve.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('geneve99'))
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'geneve99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertTrue(output, '192.168.22.1')
+ self.assertTrue(output, '6082')
+ self.assertTrue(output, 'udpcsum')
+ self.assertTrue(output, 'udp6zerocsumrx')
+
+ def test_ipip_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ipip-tunnel.netdev', 'ipip.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('ipiptun99'))
+
+ def test_gre_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-gre-tunnel.netdev', 'gretun.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('gretun99'))
+
+ def test_gretap_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-gretap-tunnel.netdev', 'gretap.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('gretap99'))
+
+ def test_ip6gretap_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6gre-tunnel.netdev', 'ip6gretap.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('ip6gretap99'))
+
+ def test_vti_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-vti-tunnel.netdev', 'vti.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('vtitun99'))
+
+ def test_vti6_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-vti6-tunnel.netdev', 'vti6.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('vti6tun99'))
+
+ def test_ip6tnl_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6tnl-tunnel.netdev', 'ip6tnl.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('ip6tnl99'))
+
+ def test_sit_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-sit-tunnel.netdev', 'sit.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('sittun99'))
+
+ def test_isatap_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-isatap-tunnel.netdev', 'isatap.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('isataptun99'))
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'isataptun99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, "isatap ")
+
+ def test_6rd_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-6rd-tunnel.netdev', '6rd.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('sittun99'))
+
+ def test_erspan_tunnel(self):
+ self.copy_unit_to_networkd_unit_path('25-erspan-tunnel.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('erspan-test'))
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'erspan-test']).rstrip().decode('utf-8')
+ print(output)
+ self.assertTrue(output, '172.16.1.200')
+ self.assertTrue(output, '172.16.1.100')
+ self.assertTrue(output, '101')
+
+ def test_tunnel_independent(self):
+ self.copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('ipiptun99'))
+
+ def test_vxlan(self):
+ self.copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network','11-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('vxlan99'))
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vxlan99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, "999")
+ self.assertRegex(output, '5555')
+ self.assertRegex(output, 'l2miss')
+ self.assertRegex(output, 'l3miss')
+ self.assertRegex(output, 'udpcsum')
+ self.assertRegex(output, 'udp6zerocsumtx')
+ self.assertRegex(output, 'udp6zerocsumrx')
+ self.assertRegex(output, 'remcsumtx')
+ self.assertRegex(output, 'remcsumrx')
+ self.assertRegex(output, 'gbp')
+
+class NetworkdNetWorkTests(unittest.TestCase, Utilities):
+ links = [
+ 'bond199',
+ 'dummy98',
+ 'test1']
+
+ units = [
+ '11-dummy.netdev',
+ '12-dummy.netdev',
+ '23-active-slave.network',
+ '23-bond199.network',
+ '23-primary-slave.network',
+ '23-test1-bond199.network',
+ '25-address-link-section.network',
+ '25-address-section-miscellaneous.network',
+ '25-address-section.network',
+ '25-bond-active-backup-slave.netdev',
+ '25-fibrule-invert.network',
+ '25-fibrule-port-range.network',
+ '25-ipv6-address-label-section.network',
+ '25-neighbor-section.network',
+ '25-link-section-unmanaged.network',
+ '25-route-gateway.network',
+ '25-route-gateway-on-link.network',
+ '25-route-reverse-order.network',
+ '25-route-section.network',
+ '25-route-tcp-window-settings.network',
+ '25-route-type.network',
+ '25-sysctl.network',
+ 'configure-without-carrier.network',
+ 'routing-policy-rule.network',
+ 'test-static.network']
+
+ def setUp(self):
+ self.link_remove(self.links)
+
+ def tearDown(self):
+ self.link_remove(self.links)
+ self.remove_unit_from_networkd_path(self.units)
+
+ def test_static_address(self):
+ self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'test-static.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '192.168.0.15')
+ self.assertRegex(output, '192.168.0.1')
+ self.assertRegex(output, 'routable')
+
+ def test_configure_without_carrier(self):
+ self.copy_unit_to_networkd_unit_path('configure-without-carrier.network', '11-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('test1'))
+
+ output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '192.168.0.15')
+ self.assertRegex(output, '192.168.0.1')
+ self.assertRegex(output, 'routable')
+
+ def test_bond_active_slave(self):
+ self.copy_unit_to_networkd_unit_path('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('bond199'))
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond199']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'active_slave dummy98')
+
+ def test_bond_primary_slave(self):
+ self.copy_unit_to_networkd_unit_path('23-primary-slave.network', '23-test1-bond199.network', '25-bond-active-backup-slave.netdev', '11-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('test1'))
+ self.assertTrue(self.link_exits('bond199'))
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond199']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'primary test1')
+
+ def test_routing_policy_rule(self):
+ self.copy_unit_to_networkd_unit_path('routing-policy-rule.network', '11-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('test1'))
+
+ output = subprocess.check_output(['ip', 'rule']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '111')
+ self.assertRegex(output, 'from 192.168.100.18')
+ self.assertRegex(output, r'tos (?:0x08|throughput)\s')
+ self.assertRegex(output, 'iif test1')
+ self.assertRegex(output, 'oif test1')
+ self.assertRegex(output, 'lookup 7')
+
+ subprocess.call(['ip', 'rule', 'del', 'table', '7'])
+
+ def test_routing_policy_rule_port_range(self):
+ self.copy_unit_to_networkd_unit_path('25-fibrule-port-range.network', '11-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('test1'))
+
+ output = subprocess.check_output(['ip', 'rule']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '111')
+ self.assertRegex(output, 'from 192.168.100.18')
+ self.assertRegex(output, '1123-1150')
+ self.assertRegex(output, '3224-3290')
+ self.assertRegex(output, 'tcp')
+ self.assertRegex(output, 'lookup 7')
+
+ subprocess.call(['ip', 'rule', 'del', 'table', '7'])
+
+ def test_routing_policy_rule_invert(self):
+ self.copy_unit_to_networkd_unit_path('25-fibrule-invert.network', '11-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('test1'))
+
+ output = subprocess.check_output(['ip', 'rule']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '111')
+ self.assertRegex(output, 'not.*?from.*?192.168.100.18')
+ self.assertRegex(output, 'tcp')
+ self.assertRegex(output, 'lookup 7')
+
+ subprocess.call(['ip', 'rule', 'del', 'table', '7'])
+
+ def test_address_preferred_lifetime_zero_ipv6(self):
+ self.copy_unit_to_networkd_unit_path('25-address-section-miscellaneous.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['ip', 'address', 'show', 'dummy98']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope link deprecated dummy98')
+ self.assertRegex(output, 'inet6 2001:db8:0:f101::1/64 scope global')
+
+ def test_ip_route(self):
+ self.copy_unit_to_networkd_unit_path('25-route-section.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'dummy98']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '192.168.0.1')
+ self.assertRegex(output, 'static')
+ self.assertRegex(output, '192.168.0.0/24')
+
+ def test_ip_route_reverse(self):
+ self.copy_unit_to_networkd_unit_path('25-route-reverse-order.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['ip', '-6', 'route', 'show', 'dev', 'dummy98']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff')
+ self.assertRegex(output, '2001:1234:5:8f63::1')
+
+ def test_ip_route_blackhole_unreachable_prohibit(self):
+ self.copy_unit_to_networkd_unit_path('25-route-type.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['ip', 'route', 'list']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'blackhole')
+ self.assertRegex(output, 'unreachable')
+ self.assertRegex(output, 'prohibit')
+
+ subprocess.call(['ip', 'route', 'del', 'blackhole', '202.54.1.2'])
+ subprocess.call(['ip', 'route', 'del', 'unreachable', '202.54.1.3'])
+ subprocess.call(['ip', 'route', 'del', 'prohibit', '202.54.1.4'])
+
+ def test_ip_route_tcp_window(self):
+ self.copy_unit_to_networkd_unit_path('25-route-tcp-window-settings.network', '11-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('test1'))
+
+ output = subprocess.check_output(['ip', 'route', 'list']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'initcwnd 20')
+ self.assertRegex(output, 'initrwnd 30')
+
+ def test_ip_route_gateway(self):
+ self.copy_unit_to_networkd_unit_path('25-route-gateway.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'dummy98', 'default']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'default')
+ self.assertRegex(output, 'via')
+ self.assertRegex(output, '149.10.124.64')
+ self.assertRegex(output, 'proto')
+ self.assertRegex(output, 'static')
+
+ output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'dummy98', 'src', '149.10.124.58']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '149.10.124.48/28')
+ self.assertRegex(output, 'proto')
+ self.assertRegex(output, 'kernel')
+ self.assertRegex(output, 'scope')
+ self.assertRegex(output, 'link')
+
+ def test_ip_route_gateway_on_link(self):
+ self.copy_unit_to_networkd_unit_path('25-route-gateway-on-link.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'dummy98', 'default']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'default')
+ self.assertRegex(output, 'via')
+ self.assertRegex(output, '149.10.125.65')
+ self.assertRegex(output, 'proto')
+ self.assertRegex(output, 'static')
+ self.assertRegex(output, 'onlink')
+
+ output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'dummy98', 'src', '149.10.124.58']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '149.10.124.48/28')
+ self.assertRegex(output, 'proto')
+ self.assertRegex(output, 'kernel')
+ self.assertRegex(output, 'scope')
+ self.assertRegex(output, 'link')
+
+ def test_ip_link_mac_address(self):
+ self.copy_unit_to_networkd_unit_path('25-address-link-section.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['ip', 'link', 'show', 'dummy98']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '00:01:02:aa:bb:cc')
+
+ def test_ip_link_unmanaged(self):
+ self.copy_unit_to_networkd_unit_path('25-link-section-unmanaged.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'unmanaged')
+
+ def test_ipv6_address_label(self):
+ self.copy_unit_to_networkd_unit_path('25-ipv6-address-label-section.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['ip', 'addrlabel', 'list']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '2004:da8:1::/64')
+
+ def test_ipv6_neighbor(self):
+ self.copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['ip', 'neigh', 'list']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT')
+ self.assertRegex(output, '2004:da8:1:0::1.*00:00:5e:00:02:66.*PERMANENT')
+
+ def test_sysctl(self):
+ self.copy_unit_to_networkd_unit_path('25-sysctl.network', '12-dummy.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'forwarding'), '1')
+ self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'use_tempaddr'), '2')
+ self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'dad_transmits'), '3')
+ self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'hop_limit'), '5')
+ self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'proxy_ndp'), '1')
+ self.assertEqual(self.read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1')
+ self.assertEqual(self.read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1')
+
+class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities):
+ links = [
+ 'bridge99',
+ 'dummy98',
+ 'test1']
+
+ units = [
+ '11-dummy.netdev',
+ '12-dummy.netdev',
+ '26-bridge.netdev',
+ '26-bridge-slave-interface-1.network',
+ '26-bridge-slave-interface-2.network',
+ 'bridge99.network']
+
+ def setUp(self):
+ self.link_remove(self.links)
+
+ def tearDown(self):
+ self.link_remove(self.links)
+ self.remove_unit_from_networkd_path(self.units)
+
+ def test_bridge_property(self):
+ self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
+ '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
+ 'bridge99.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+ self.assertTrue(self.link_exits('test1'))
+ self.assertTrue(self.link_exits('bridge99'))
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'master')
+ self.assertRegex(output, 'bridge')
+
+ output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dummy98']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'master')
+ self.assertRegex(output, 'bridge')
+
+ output = subprocess.check_output(['ip', 'addr', 'show', 'bridge99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '192.168.0.15')
+ self.assertRegex(output, '192.168.0.1')
+
+ output = subprocess.check_output(['bridge', '-d', 'link', 'show', 'dummy98']).rstrip().decode('utf-8')
+ print(output)
+ self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode'), '1')
+ self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'path_cost'), '400')
+ self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood'), '1')
+ self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave'), '1')
+
+ # CONFIG_BRIDGE_IGMP_SNOOPING=y
+ if (os.path.exists('/sys/devices/virtual/net/bridge00/lower_dummy98/brport/multicast_to_unicast')):
+ self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast'), '1')
+
+class NetworkdNetWorkLLDPTests(unittest.TestCase, Utilities):
+ links = ['veth99']
+
+ units = [
+ '23-emit-lldp.network',
+ '24-lldp.network',
+ '25-veth.netdev']
+
+ def setUp(self):
+ self.link_remove(self.links)
+
+ def tearDown(self):
+ self.link_remove(self.links)
+ self.remove_unit_from_networkd_path(self.units)
+
+ def test_lldp(self):
+ self.copy_unit_to_networkd_unit_path('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ output = subprocess.check_output(['networkctl', 'lldp']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'veth-peer')
+ self.assertRegex(output, 'veth99')
+
+class NetworkdNetworkRATests(unittest.TestCase, Utilities):
+ links = ['veth99']
+
+ units = [
+ '25-veth.netdev',
+ 'ipv6-prefix.network',
+ 'ipv6-prefix-veth.network']
+
+ def setUp(self):
+ self.link_remove(self.links)
+
+ def tearDown(self):
+ self.link_remove(self.links)
+ self.remove_unit_from_networkd_path(self.units)
+
+ def test_ipv6_prefix_delegation(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '2002:da8:1:0')
+
+class NetworkdNetworkDHCPServerTests(unittest.TestCase, Utilities):
+ links = [
+ 'dummy98',
+ 'veth99']
+
+ units = [
+ '12-dummy.netdev',
+ '24-search-domain.network',
+ '25-veth.netdev',
+ 'dhcp-client.network',
+ 'dhcp-client-timezone-router.network',
+ 'dhcp-server.network',
+ 'dhcp-server-timezone-router.network']
+
+ def setUp(self):
+ self.link_remove(self.links)
+
+ def tearDown(self):
+ self.link_remove(self.links)
+ self.remove_unit_from_networkd_path(self.units)
+
+ def test_dhcp_server(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client.network', 'dhcp-server.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '192.168.5.*')
+ self.assertRegex(output, 'Gateway: 192.168.5.1')
+ self.assertRegex(output, 'DNS: 192.168.5.1')
+ self.assertRegex(output, 'NTP: 192.168.5.1')
+
+ def test_domain(self):
+ self.copy_unit_to_networkd_unit_path( '12-dummy.netdev', '24-search-domain.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('dummy98'))
+
+ output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'Address: 192.168.42.100')
+ self.assertRegex(output, 'DNS: 192.168.42.1')
+ self.assertRegex(output, 'Search Domains: one')
+
+ def test_emit_router_timezone(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client-timezone-router.network', 'dhcp-server-timezone-router.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'Gateway: 192.168.5.*')
+ self.assertRegex(output, '192.168.5.*')
+ self.assertRegex(output, 'Europe/Berlin')
+
+class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities):
+ links = [
+ 'dummy98',
+ 'veth99']
+
+ units = [
+ '25-veth.netdev',
+ 'dhcp-client-anonymize.network',
+ 'dhcp-client-critical-connection.network',
+ 'dhcp-client-ipv4-dhcp-settings.network',
+ 'dhcp-client-ipv4-only-ipv6-disabled.network',
+ 'dhcp-client-ipv4-only.network',
+ 'dhcp-client-ipv6-only.network',
+ 'dhcp-client-ipv6-rapid-commit.network',
+ 'dhcp-client-listen-port.network',
+ 'dhcp-client-route-metric.network',
+ 'dhcp-client-route-table.network',
+ 'dhcp-server-veth-peer.network',
+ 'dhcp-v4-server-veth-peer.network']
+
+ def setUp(self):
+ self.link_remove(self.links)
+ self.stop_dnsmasq(dnsmasq_pid_file)
+
+ def tearDown(self):
+ self.link_remove(self.links)
+ self.remove_unit_from_networkd_path(self.units)
+ self.stop_dnsmasq(dnsmasq_pid_file)
+ self.remove_lease_file()
+ self.remove_log_file()
+
+ def test_dhcp_client_ipv6_only(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network','dhcp-client-ipv6-only.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ self.start_dnsmasq()
+
+ output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '2600::')
+ self.assertNotRegex(output, '192.168.5')
+
+ def test_dhcp_client_ipv4_only(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network','dhcp-client-ipv4-only-ipv6-disabled.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ self.start_dnsmasq()
+
+ output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertNotRegex(output, '2600::')
+ self.assertRegex(output, '192.168.5')
+
+ def test_dhcp_client_ipv4_ipv6(self):
+ 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')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ self.start_dnsmasq()
+
+ output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '2600::')
+ self.assertRegex(output, '192.168.5')
+
+ def test_dhcp_client_settings(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-dhcp-settings.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ self.start_dnsmasq()
+
+ output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '12:34:56:78:9a:bc')
+ self.assertRegex(output, '192.168.5')
+ self.assertRegex(output, '1492')
+
+ output = subprocess.check_output(['ip', 'route']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'default.*dev veth99 proto dhcp')
+
+ self.assertTrue(self.search_words_in_dnsmasq_log('vendor class: SusantVendorTest', True))
+ self.assertTrue(self.search_words_in_dnsmasq_log('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc'))
+ self.assertTrue(self.search_words_in_dnsmasq_log('client provides name: test-hostname'))
+ self.assertTrue(self.search_words_in_dnsmasq_log('26:mtu'))
+
+ def test_dhcp6_client_settings_rapidcommit_true(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ self.start_dnsmasq()
+
+ output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '12:34:56:78:9a:bc')
+ self.assertTrue(self.search_words_in_dnsmasq_log('14:rapid-commit', True))
+
+ def test_dhcp6_client_settings_rapidcommit_false(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-rapid-commit.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ self.start_dnsmasq()
+
+ output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '12:34:56:78:9a:bc')
+ self.assertFalse(self.search_words_in_dnsmasq_log('14:rapid-commit', True))
+
+ def test_dhcp_client_settings_anonymize(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-anonymize.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ self.start_dnsmasq()
+
+ self.assertFalse(self.search_words_in_dnsmasq_log('VendorClassIdentifier=SusantVendorTest', True))
+ self.assertFalse(self.search_words_in_dnsmasq_log('test-hostname'))
+ self.assertFalse(self.search_words_in_dnsmasq_log('26:mtu'))
+
+ def test_dhcp_client_listen_port(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-listen-port.network')
+
+ dh_server = DHCPServer("dhcp_server")
+ dh_server.start()
+
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ global port
+ global ip
+
+ self.assertRegex(str(port), '5555')
+ self.assertRegex(str(ip), '0.0.0.0')
+
+ dh_server.join()
+
+ def test_dhcp_route_table_id(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-table.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ self.start_dnsmasq()
+
+ output = subprocess.check_output(['ip', 'route', 'show', 'table', '12']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'veth99 proto dhcp')
+ self.assertRegex(output, '192.168.5.1')
+
+ def test_dhcp_route_metric(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-metric.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ self.start_dnsmasq()
+
+ output = subprocess.check_output(['ip', 'route', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, 'metric 24')
+
+ def test_dhcp_route_criticalconnection_true(self):
+ self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-critical-connection.network')
+ self.start_networkd()
+
+ self.assertTrue(self.link_exits('veth99'))
+
+ self.start_dnsmasq()
+
+ output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '192.168.5.*')
+
+ # Stoping dnsmasq as networkd won't be allowed to renew the DHCP lease.
+ self.stop_dnsmasq(dnsmasq_pid_file)
+
+ # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
+ time.sleep(125)
+
+ output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8')
+ print(output)
+ self.assertRegex(output, '192.168.5.*')
+
+if __name__ == '__main__':
+ unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
+ verbosity=3))
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 0433629c7c..957cda541c 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1537,7 +1537,11 @@ sub udev_setup {
system("umount", $udev_tmpfs);
rmdir($udev_tmpfs);
mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n";
- system("mount", "-o", "rw,mode=755,nosuid,noexec,nodev", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs";
+
+ if (system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs)) {
+ warn "unable to mount tmpfs";
+ return 0;
+ }
mkdir($udev_dev) || die "unable to create udev_dev: $udev_dev\n";
# setting group and mode of udev_dev ensures the tests work
@@ -1545,9 +1549,29 @@ sub udev_setup {
chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n";
chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n";
+ if (system("mknod", $udev_dev . "/null", "c", "1", "3")) {
+ warn "unable to create $udev_dev/null";
+ return 0;
+ }
+
+ # check if we are permitted to create block device nodes
+ my $block_device_filename = $udev_dev . "/sda";
+ if (system("mknod", $block_device_filename, "b", "8", "0")) {
+ warn "unable to create $block_device_filename";
+ return 0;
+ }
+ unlink $block_device_filename;
+
system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys";
system("rm", "-rf", "$udev_run");
+
+ if (!mkdir($udev_run)) {
+ warn "unable to create directory $udev_run";
+ return 0;
+ }
+
+ return 1;
}
sub run_test {
@@ -1645,14 +1669,15 @@ if ($? >> 8 == 0) {
exit($EXIT_TEST_SKIP);
}
-# skip the test when running in a container
-system("systemd-detect-virt", "-c", "-q");
-if ($? >> 8 == 0) {
- print "Running in a container, skipping the test.\n";
+if (!udev_setup()) {
+ warn "Failed to set up the environment, skipping the test";
exit($EXIT_TEST_SKIP);
}
-udev_setup();
+if (system($udev_bin, "check")) {
+ warn "$udev_bin failed to set up the environment, skipping the test";
+ exit($EXIT_TEST_SKIP);
+}
my $test_num = 1;
my @list;
diff --git a/tmpfiles.d/systemd.conf.m4 b/tmpfiles.d/systemd.conf.m4
index 0992f74209..d39c9cbbcf 100644
--- a/tmpfiles.d/systemd.conf.m4
+++ b/tmpfiles.d/systemd.conf.m4
@@ -17,9 +17,9 @@ d /run/systemd/users 0755 root root -
d /run/systemd/machines 0755 root root -
d /run/systemd/shutdown 0755 root root -
m4_ifdef(`ENABLE_NETWORKD',
-d /run/systemd/netif - - - -
-d /run/systemd/netif/links - - - -
-d /run/systemd/netif/leases - - - -
+d /run/systemd/netif 0755 systemd-network systemd-network -
+d /run/systemd/netif/links 0755 systemd-network systemd-network -
+d /run/systemd/netif/leases 0755 systemd-network systemd-network -
)m4_dnl
d /run/log 0755 root root -
diff --git a/tools/catalog-report.py b/tools/catalog-report.py
index b65869d02b..ca1e13df9a 100755
--- a/tools/catalog-report.py
+++ b/tools/catalog-report.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
# SPDX-License-Identifier: MIT
#
# This file is distributed under the MIT license, see below.
diff --git a/tools/check-directives.sh b/tools/check-directives.sh
new file mode 100755
index 0000000000..e2fd38898f
--- /dev/null
+++ b/tools/check-directives.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -e
+
+function generate_directives() {
+ perl -aF'/[\s,]+/' -ne '
+ if (my ($s, $d) = ($F[0] =~ /^([^\s\.]+)\.([^\s\.]+)$/)) { $d{$s}{"$d="} = 1; }
+ END { while (my ($key, $value) = each %d) {
+ printf "[%s]\n%s\n", $key, join("\n", keys(%$value))
+ }}' "$1"
+}
+
+if [[ $(generate_directives src/network/networkd-network-gperf.gperf | wc -l) -ne $(wc -l <test/fuzz/fuzz-network-parser/directives.network) ]]; then
+ echo "Looks like test/fuzz/fuzz-network-parser/directives.network hasn't been updated"
+ exit 1
+fi
+
+if [[ $(generate_directives src/network/netdev/netdev-gperf.gperf | wc -l) -ne $(wc -l <test/fuzz/fuzz-netdev-parser/directives.netdev) ]]; then
+ echo "Looks like test/fuzz/fuzz-netdev-parser/directives.netdev hasn't been updated"
+ exit 1
+fi
diff --git a/tools/check-includes.pl b/tools/check-includes.pl
index 6aae7c1534..c8bfcba8c0 100755
--- a/tools/check-includes.pl
+++ b/tools/check-includes.pl
@@ -1,7 +1,7 @@
+# SPDX-License-Identifier: CC0-1.0
#!/usr/bin/env perl
#
# checkincludes: Find files included more than once in (other) files.
-# Copyright abandoned, 2000, Niels Kristian Bech Jensen <nkbj@image.dk>.
foreach $file (@ARGV) {
open(FILE, $file) or die "Cannot open $file: $!.\n";
diff --git a/tools/coverity.sh b/tools/coverity.sh
index 561d127d1e..af4c920cd2 100755
--- a/tools/coverity.sh
+++ b/tools/coverity.sh
@@ -42,13 +42,13 @@ if [ "$AUTH_RES" = "Access denied" ]; then
echo -e "\033[33;1mCoverity Scan API access denied. Check COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN.\033[0m"
exit 1
else
- AUTH=`echo $AUTH_RES | python -c "import sys, json; print json.load(sys.stdin)['upload_permitted']"`
+ AUTH=`echo $AUTH_RES | python -c "import sys, json; print(json.load(sys.stdin)['upload_permitted'])"`
if [ "$AUTH" = "True" ]; then
echo -e "\033[33;1mCoverity Scan analysis authorized per quota.\033[0m"
else
- WHEN=`echo $AUTH_RES | python -c "import sys; json; print json.load(sys.stdin)['next_upload_permitted_at']"`
+ WHEN=`echo $AUTH_RES | python -c "import sys, json; print(json.load(sys.stdin)['next_upload_permitted_at'])"`
echo -e "\033[33;1mCoverity Scan analysis NOT authorized until $WHEN.\033[0m"
- exit 0
+ exit 1
fi
fi
diff --git a/tools/gdb-sd_dump_hashmaps.py b/tools/gdb-sd_dump_hashmaps.py
index e6ddd14ea7..4e8593f320 100644
--- a/tools/gdb-sd_dump_hashmaps.py
+++ b/tools/gdb-sd_dump_hashmaps.py
@@ -1,7 +1,8 @@
#!/usr/bin/env python3
-# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
# SPDX-License-Identifier: LGPL-2.1+
+from __future__ import print_function
+
import gdb
class sd_dump_hashmaps(gdb.Command):
@@ -14,12 +15,11 @@ class sd_dump_hashmaps(gdb.Command):
d = gdb.parse_and_eval("hashmap_debug_list")
all_entry_sizes = gdb.parse_and_eval("all_entry_sizes")
all_direct_buckets = gdb.parse_and_eval("all_direct_buckets")
- hashmap_base_t = gdb.lookup_type("HashmapBase")
uchar_t = gdb.lookup_type("unsigned char")
ulong_t = gdb.lookup_type("unsigned long")
debug_offset = gdb.parse_and_eval("(unsigned long)&((HashmapBase*)0)->debug")
- print "type, hash, indirect, entries, max_entries, buckets, creator"
+ print("type, hash, indirect, entries, max_entries, buckets, creator")
while d:
h = gdb.parse_and_eval("(HashmapBase*)((char*)%d - %d)" % (int(d.cast(ulong_t)), debug_offset))
@@ -34,7 +34,7 @@ class sd_dump_hashmaps(gdb.Command):
t = ["plain", "ordered", "set"][int(h["type"])]
- print "{}, {}, {}, {}, {}, {}, {} ({}:{})".format(t, h["hash_ops"], bool(h["has_indirect"]), n_entries, d["max_entries"], n_buckets, d["func"], d["file"], d["line"])
+ print("{}, {}, {}, {}, {}, {}, {} ({}:{})".format(t, h["hash_ops"], bool(h["has_indirect"]), n_entries, d["max_entries"], n_buckets, d["func"], d["file"], d["line"]))
if arg != "" and n_entries > 0:
dib_raw_addr = storage_ptr + (all_entry_sizes[h["type"]] * n_buckets)
@@ -46,10 +46,10 @@ class sd_dump_hashmaps(gdb.Command):
for dib in sorted(iter(histogram)):
if dib != 255:
- print "{:>3} {:>8} {} of entries".format(dib, histogram[dib], 100.0*histogram[dib]/n_entries)
+ print("{:>3} {:>8} {} of entries".format(dib, histogram[dib], 100.0*histogram[dib]/n_entries))
else:
- print "{:>3} {:>8} {} of slots".format(dib, histogram[dib], 100.0*histogram[dib]/n_buckets)
- print "mean DIB of entries: {}".format(sum([dib*histogram[dib] for dib in iter(histogram) if dib != 255])*1.0/n_entries)
+ print("{:>3} {:>8} {} of slots".format(dib, histogram[dib], 100.0*histogram[dib]/n_buckets))
+ print("mean DIB of entries: {}".format(sum([dib*histogram[dib] for dib in iter(histogram) if dib != 255])*1.0/n_entries))
blocks = []
current_len = 1
@@ -70,9 +70,9 @@ class sd_dump_hashmaps(gdb.Command):
if len(blocks) > 1 and blocks[0][0] == blocks[0][1] and blocks[-1][0] == n_buckets - 1:
blocks[0][1] += blocks[-1][1]
blocks = blocks[0:-1]
- print "max block: {}".format(max(blocks, key=lambda a: a[1]))
- print "sum block lens: {}".format(sum(b[1] for b in blocks))
- print "mean block len: {}".format((1.0 * sum(b[1] for b in blocks) / len(blocks)))
+ print("max block: {}".format(max(blocks, key=lambda a: a[1])))
+ print("sum block lens: {}".format(sum(b[1] for b in blocks)))
+ print("mean block len: {}".format((1.0 * sum(b[1] for b in blocks) / len(blocks))))
d = d["debug_list_next"]
diff --git a/src/basic/generate-gperfs.py b/tools/generate-gperfs.py
index aca9ab1fe9..5392df0ebb 100755
--- a/src/basic/generate-gperfs.py
+++ b/tools/generate-gperfs.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1+
-"""Generate %-from-name.gperf from %-list.txt
+"""
+Generate %-from-name.gperf from %-list.txt
"""
import sys
diff --git a/tools/make-directive-index.py b/tools/make-directive-index.py
index 8b85ef40f3..8703c8a37b 100755
--- a/tools/make-directive-index.py
+++ b/tools/make-directive-index.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
# SPDX-License-Identifier: LGPL-2.1+
import sys
diff --git a/tools/make-index-md.sh b/tools/make-index-md.sh
new file mode 100755
index 0000000000..78506cbf39
--- /dev/null
+++ b/tools/make-index-md.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -eu
+
+cd "$@"/docs/
+(
+ echo -e "# systemd Documentation\n"
+
+ for f in *.md ; do
+ if [ "x$f" != "xindex.md" ] ; then
+ t=`grep "^# " "$f" | head -n 1 | sed -e 's/^#\s*//'`
+
+ if [ "x$f" = "xCODE_OF_CONDUCT.md" -o "x$f" = "xCONTRIBUTING.md" ] ; then
+ # For some reason GitHub refuses to generate
+ # HTML versions of these two documents,
+ # probably because they are in some way special
+ # in GitHub behaviour (as they are shown as
+ # links in the issue submission form). Let's
+ # work around this limitation by linking to
+ # their repository browser version
+ # instead. This might not even be such a bad
+ # thing, given that the issue submission form
+ # and our index file thus link to the same
+ # version.
+ u="https://github.com/systemd/systemd/blob/master/docs/$f"
+ else
+ u="https://systemd.io/"`echo "$f" | sed -e 's/.md$//'`
+ fi
+ echo "* [$t]($u)"
+ fi
+ done
+) > index.md
diff --git a/tools/make-man-index.py b/tools/make-man-index.py
index 7ed98cb4e0..66027af02e 100755
--- a/tools/make-man-index.py
+++ b/tools/make-man-index.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
# SPDX-License-Identifier: LGPL-2.1+
import collections
diff --git a/tools/make-man-rules.py b/tools/make-man-rules.py
index 42a48bc98c..c4551c6f61 100755
--- a/tools/make-man-rules.py
+++ b/tools/make-man-rules.py
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
-# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
-# SPDX-License-Identifier: LGPL-2.1+
+# SPDX-License-Identifier: LGPL-2.1+
from __future__ import print_function
import collections
diff --git a/tools/meson-build.sh b/tools/meson-build.sh
index 304a755676..dea554177d 100755
--- a/tools/meson-build.sh
+++ b/tools/meson-build.sh
@@ -5,8 +5,10 @@ src="$1"
dst="$2"
target="$3"
options="$4"
+CC="$5"
+CXX="$6"
-[ -d "$dst" ] || meson "$src" "$dst" $options
+[ -f "$dst/ninja.build" ] || CC="$CC" CXX="$CXX" meson "$src" "$dst" $options
# Locate ninja binary, on CentOS 7 it is called ninja-build, so
# use that name if available.
diff --git a/tools/meson-check-api-docs.sh b/tools/meson-check-api-docs.sh
index 5bc808c1e4..a654368f9e 100755
--- a/tools/meson-check-api-docs.sh
+++ b/tools/meson-check-api-docs.sh
@@ -2,10 +2,33 @@
set -eu
+sd_good=0
+sd_total=0
+udev_good=0
+udev_total=0
+
for symbol in `nm -g --defined-only "$@" | grep " T " | cut -d" " -f3 | sort -u` ; do
if test -f ${MESON_BUILD_ROOT}/man/$symbol.3 ; then
echo "✓ Symbol $symbol() is documented."
+ good=1
else
printf " \x1b[1;31mSymbol $symbol() lacks documentation.\x1b[0m\n"
+ good=0
fi
+
+ case $symbol in
+ sd_*)
+ ((sd_good+=good))
+ ((sd_total+=1))
+ ;;
+ udev_*)
+ ((udev_good+=good))
+ ((udev_total+=1))
+ ;;
+ *)
+ echo 'unknown symbol prefix'
+ exit 1
+ esac
done
+
+echo "libsystemd: $sd_good/$sd_total libudev: $udev_good/$udev_total"
diff --git a/tools/meson-link-test.c b/tools/meson-link-test.c
deleted file mode 100644
index 825bbff05f..0000000000
--- a/tools/meson-link-test.c
+++ /dev/null
@@ -1 +0,0 @@
-int main(void) {return 0;}
diff --git a/tools/oss-fuzz.sh b/tools/oss-fuzz.sh
index 200407fcca..9a116be114 100755
--- a/tools/oss-fuzz.sh
+++ b/tools/oss-fuzz.sh
@@ -35,8 +35,10 @@ fi
meson $build -D$fuzzflag -Db_lundef=false
ninja -C $build fuzzers
-for d in "$(dirname "$0")/../test/fuzz-corpus/"*; do
- zip -jqr $OUT/fuzz-$(basename "$d")_seed_corpus.zip "$d"
+# The seed corpus is a separate flat archive for each fuzzer,
+# with a fixed name ${fuzzer}_seed_corpus.zip.
+for d in "$(dirname "$0")/../test/fuzz/fuzz-"*; do
+ zip -jqr $OUT/$(basename "$d")_seed_corpus.zip "$d"
done
# get fuzz-dns-packet corpus
@@ -44,8 +46,11 @@ df=$build/dns-fuzzing
git clone --depth 1 https://github.com/CZ-NIC/dns-fuzzing $df
zip -jqr $OUT/fuzz-dns-packet_seed_corpus.zip $df/packet
-# install the private shared library without executable permissions
-install -Dt $OUT/src/shared/ -m 0644 $build/src/shared/libsystemd-shared-*.so
+install -Dt $OUT/src/shared/ $build/src/shared/libsystemd-shared-*.so
+
+wget -O $OUT/fuzz-json_seed_corpus.zip https://storage.googleapis.com/skia-fuzzer/oss-fuzz/skjson_seed_corpus.zip
+wget -O $OUT/fuzz-json.dict https://raw.githubusercontent.com/rc0r/afl-fuzz/master/dictionaries/json.dict
find $build -maxdepth 1 -type f -executable -name "fuzz-*" -exec mv {} $OUT \;
+find src -type f -name "fuzz-*.dict" -exec cp {} $OUT \;
cp src/fuzz/*.options $OUT
diff --git a/tools/xml_helper.py b/tools/xml_helper.py
index 19e343b4e3..f399e7493c 100755
--- a/tools/xml_helper.py
+++ b/tools/xml_helper.py
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
-# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
-# SPDX-License-Identifier: LGPL-2.1+
+# SPDX-License-Identifier: LGPL-2.1+
from lxml import etree as tree
diff --git a/travis-ci/managers/debian.sh b/travis-ci/managers/debian.sh
new file mode 100755
index 0000000000..af2f0dab08
--- /dev/null
+++ b/travis-ci/managers/debian.sh
@@ -0,0 +1,83 @@
+#!/bin/bash
+
+# Run this script from the root of the systemd's git repository
+# or set REPO_ROOT to a correct path.
+#
+# Example execution on Fedora:
+# dnf install docker
+# systemctl start docker
+# export CONT_NAME="my-fancy-container"
+# travis-ci/managers/debian.sh SETUP RUN CLEANUP
+
+PHASES=(${@:-SETUP RUN RUN_ASAN CLEANUP})
+DEBIAN_RELEASE="${DEBIAN_RELEASE:-testing}"
+CONT_NAME="${CONT_NAME:-debian-$DEBIAN_RELEASE-$RANDOM}"
+DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}"
+DOCKER_RUN="${DOCKER_RUN:-docker run}"
+REPO_ROOT="${REPO_ROOT:-$PWD}"
+ADDITIONAL_DEPS=(python3-libevdev python3-pyparsing clang)
+
+function info() {
+ echo -e "\033[33;1m$1\033[0m"
+}
+
+set -e
+
+source "$(dirname $0)/travis_wait.bash"
+
+for phase in "${PHASES[@]}"; do
+ case $phase in
+ SETUP)
+ info "Setup phase"
+ info "Using Debian $DEBIAN_RELEASE"
+ printf "FROM debian:$DEBIAN_RELEASE\nRUN bash -c 'apt-get -y update && apt-get install -y systemd'\n" | docker build -t debian-with-systemd/latest -
+ info "Starting container $CONT_NAME"
+ $DOCKER_RUN -v $REPO_ROOT:/build:rw \
+ -w /build --privileged=true --name $CONT_NAME \
+ -dit --net=host debian-with-systemd/latest /usr/bin/systemd
+ $DOCKER_EXEC bash -c "echo deb-src http://deb.debian.org/debian $DEBIAN_RELEASE main >>/etc/apt/sources.list"
+ $DOCKER_EXEC apt-get -y update
+ $DOCKER_EXEC apt-get -y build-dep systemd
+ $DOCKER_EXEC apt-get -y install "${ADDITIONAL_DEPS[@]}"
+ # overlayfs on TravisCI is having trouble delivering inotify events to test-path and test-event.
+ # Let's use tmpfs instead for now.
+ $DOCKER_EXEC mount -t tmpfs tmpfs /tmp
+ ;;
+ RUN)
+ info "Run phase"
+ $DOCKER_EXEC meson --werror -Dtests=unsafe -Dslow-tests=true -Dsplit-usr=true build
+ $DOCKER_EXEC ninja -v -C build
+ $DOCKER_EXEC ninja -C build test
+ $DOCKER_EXEC tools/check-directives.sh
+ ;;
+ RUN_CLANG)
+ docker exec -e CC=clang -e CXX=clang++ -it $CONT_NAME meson --werror -Dtests=unsafe -Dslow-tests=true -Dsplit-usr=true build
+ $DOCKER_EXEC ninja -v -C build
+ $DOCKER_EXEC ninja -C build test
+ ;;
+ RUN_ASAN|RUN_CLANG_ASAN)
+ if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then
+ ENV_VARS="-e CC=clang -e CXX=clang++"
+ MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764
+ fi
+ docker exec $ENV_VARS -it $CONT_NAME meson --werror -Dtests=unsafe -Db_sanitize=address,undefined -Dsplit-usr=true $MESON_ARGS build
+ $DOCKER_EXEC ninja -v -C build
+
+ # Never remove halt_on_error from UBSAN_OPTIONS. See https://github.com/systemd/systemd/commit/2614d83aa06592aedb.
+ travis_wait docker exec --interactive=false \
+ -e UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 \
+ -e ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 \
+ -e "TRAVIS=$TRAVIS" \
+ -t $CONT_NAME \
+ meson test --timeout-multiplier=3 -C ./build/ --print-errorlogs
+ ;;
+ CLEANUP)
+ info "Cleanup phase"
+ docker stop $CONT_NAME
+ docker rm -f $CONT_NAME
+ ;;
+ *)
+ echo >&2 "Unknown phase '$phase'"
+ exit 1
+ esac
+done
diff --git a/travis-ci/managers/fedora.sh b/travis-ci/managers/fedora.sh
new file mode 100755
index 0000000000..760ed5b1ea
--- /dev/null
+++ b/travis-ci/managers/fedora.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+# Run this script from the root of the systemd's git repository
+# or set REPO_ROOT to a correct path.
+#
+# Example execution on Fedora:
+# dnf install docker
+# systemctl start docker
+# export CONT_NAME="my-fancy-container"
+# travis-ci/managers/fedora.sh SETUP RUN CLEANUP
+
+PHASES=(${@:-SETUP RUN RUN_ASAN CLEANUP})
+FEDORA_RELEASE="${FEDORA_RELEASE:-rawhide}"
+CONT_NAME="${CONT_NAME:-fedora-$FEDORA_RELEASE-$RANDOM}"
+DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}"
+DOCKER_RUN="${DOCKER_RUN:-docker run}"
+REPO_ROOT="${REPO_ROOT:-$PWD}"
+ADDITIONAL_DEPS=(dnf-plugins-core python2 iputils hostname libasan python3-pyparsing python3-evdev libubsan clang llvm)
+
+function info() {
+ echo -e "\033[33;1m$1\033[0m"
+}
+
+set -e
+
+source "$(dirname $0)/travis_wait.bash"
+
+for phase in "${PHASES[@]}"; do
+ case $phase in
+ SETUP)
+ info "Setup phase"
+ info "Using Fedora $FEDORA_RELEASE"
+ # Pull a Docker image and start a new container
+ docker pull fedora:$FEDORA_RELEASE
+ info "Starting container $CONT_NAME"
+ $DOCKER_RUN -v $REPO_ROOT:/build:rw \
+ -w /build --privileged=true --name $CONT_NAME \
+ -dit --net=host fedora:$FEDORA_RELEASE /sbin/init
+ # Beautiful workaround for Fedora's version of Docker
+ sleep 1
+ $DOCKER_EXEC dnf makecache
+ # Install necessary build/test requirements
+ $DOCKER_EXEC dnf -y --exclude selinux-policy\* upgrade
+ $DOCKER_EXEC dnf -y install "${ADDITIONAL_DEPS[@]}"
+ $DOCKER_EXEC dnf -y builddep systemd
+ ;;
+ RUN)
+ info "Run phase"
+ # Build systemd
+ $DOCKER_EXEC meson --werror -Dtests=unsafe -Dslow-tests=true build
+ $DOCKER_EXEC ninja -v -C build
+ $DOCKER_EXEC ninja -C build test
+ $DOCKER_EXEC tools/check-directives.sh
+ ;;
+ RUN_CLANG)
+ docker exec -e CC=clang -e CXX=clang++ -it $CONT_NAME meson --werror -Dtests=unsafe -Dslow-tests=true build
+ $DOCKER_EXEC ninja -v -C build
+ $DOCKER_EXEC ninja -C build test
+ ;;
+ RUN_ASAN|RUN_CLANG_ASAN)
+ if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then
+ ENV_VARS="-e CC=clang -e CXX=clang++"
+ MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764
+ fi
+ docker exec $ENV_VARS -it $CONT_NAME meson --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS build
+ $DOCKER_EXEC ninja -v -C build
+
+ # Never remove halt_on_error from UBSAN_OPTIONS. See https://github.com/systemd/systemd/commit/2614d83aa06592aedb.
+ travis_wait docker exec --interactive=false \
+ -e UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 \
+ -e ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 \
+ -e "TRAVIS=$TRAVIS" \
+ -t $CONT_NAME \
+ meson test --timeout-multiplier=3 -C ./build/ --print-errorlogs
+ ;;
+ CLEANUP)
+ info "Cleanup phase"
+ docker stop $CONT_NAME
+ docker rm -f $CONT_NAME
+ ;;
+ *)
+ echo >&2 "Unknown phase '$phase'"
+ exit 1
+ esac
+done
diff --git a/travis-ci/managers/travis_wait.bash b/travis-ci/managers/travis_wait.bash
new file mode 100644
index 0000000000..acf6ad15e4
--- /dev/null
+++ b/travis-ci/managers/travis_wait.bash
@@ -0,0 +1,61 @@
+# This was borrowed from https://github.com/travis-ci/travis-build/tree/master/lib/travis/build/bash
+# to get around https://github.com/travis-ci/travis-ci/issues/9979. It should probably be removed
+# as soon as Travis CI has started to provide an easy way to export the functions to bash scripts.
+
+travis_jigger() {
+ local cmd_pid="${1}"
+ shift
+ local timeout="${1}"
+ shift
+ local count=0
+
+ echo -e "\\n"
+
+ while [[ "${count}" -lt "${timeout}" ]]; do
+ count="$((count + 1))"
+ echo -ne "Still running (${count} of ${timeout}): ${*}\\r"
+ sleep 60
+ done
+
+ echo -e "\\n${ANSI_RED}Timeout (${timeout} minutes) reached. Terminating \"${*}\"${ANSI_RESET}\\n"
+ kill -9 "${cmd_pid}"
+}
+
+travis_wait() {
+ local timeout="${1}"
+
+ if [[ "${timeout}" =~ ^[0-9]+$ ]]; then
+ shift
+ else
+ timeout=20
+ fi
+
+ local cmd=("${@}")
+ local log_file="travis_wait_${$}.log"
+
+ "${cmd[@]}" &>"${log_file}" &
+ local cmd_pid="${!}"
+
+ travis_jigger "${!}" "${timeout}" "${cmd[@]}" &
+ local jigger_pid="${!}"
+ local result
+
+ {
+ set +e
+ wait "${cmd_pid}" 2>/dev/null
+ result="${?}"
+ ps -p"${jigger_pid}" &>/dev/null && kill "${jigger_pid}"
+ set -e
+ }
+
+ if [[ "${result}" -eq 0 ]]; then
+ echo -e "\\n${ANSI_GREEN}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}"
+ else
+ echo -e "\\n${ANSI_RED}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}"
+ fi
+
+ echo -e "\\n${ANSI_GREEN}Log:${ANSI_RESET}\\n"
+ cat "${log_file}"
+
+ return "${result}"
+}
diff --git a/units/busnames.target b/units/boot-complete.target
index 9e2d7c3194..f0b9e57e7c 100644
--- a/units/busnames.target
+++ b/units/boot-complete.target
@@ -8,5 +8,7 @@
# (at your option) any later version.
[Unit]
-Description=Bus Names
+Description=Boot Completion Check
Documentation=man:systemd.special(7)
+Requires=sysinit.target
+After=sysinit.target
diff --git a/units/meson-add-wants.sh b/units/meson-add-wants.sh
index 70f7172ae9..e2b260398c 100755
--- a/units/meson-add-wants.sh
+++ b/units/meson-add-wants.sh
@@ -18,10 +18,10 @@ unitpath="${DESTDIR:-}${unitdir}/${unit}"
case "$target" in
*/)
- mkdir -p -m 0755 "$dir"
+ mkdir -vp -m 0755 "$dir"
;;
*)
- mkdir -p -m 0755 "$(basename "$dir")"
+ mkdir -vp -m 0755 "$(dirname "$dir")"
;;
esac
diff --git a/units/meson.build b/units/meson.build
index e4ac6ced64..d69508467f 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -3,6 +3,7 @@
units = [
['basic.target', ''],
['bluetooth.target', ''],
+ ['boot-complete.target', ''],
['cryptsetup-pre.target', 'HAVE_LIBCRYPTSETUP'],
['cryptsetup.target', 'HAVE_LIBCRYPTSETUP',
'sysinit.target.wants/'],
@@ -85,6 +86,7 @@ units = [
'multi-user.target.wants/'],
['systemd-coredump.socket', 'ENABLE_COREDUMP',
'sockets.target.wants/'],
+ ['systemd-exit.service', ''],
['systemd-initctl.socket', '',
'sockets.target.wants/'],
['systemd-journal-gatewayd.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'],
@@ -97,6 +99,8 @@ units = [
'sockets.target.wants/'],
['systemd-networkd.socket', 'ENABLE_NETWORKD',
join_paths(pkgsysconfdir, 'system/sockets.target.wants/')],
+ ['systemd-poweroff.service', ''],
+ ['systemd-reboot.service', ''],
['systemd-rfkill.socket', 'ENABLE_RFKILL'],
['systemd-tmpfiles-clean.timer', '',
'timers.target.wants/'],
@@ -132,8 +136,9 @@ in_units = [
['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-coredump@.service', 'ENABLE_COREDUMP'],
- ['systemd-exit.service', ''],
['systemd-firstboot.service', 'ENABLE_FIRSTBOOT',
'sysinit.target.wants/'],
['systemd-fsck-root.service', ''],
@@ -178,11 +183,9 @@ in_units = [
['systemd-nspawn@.service', ''],
['systemd-portabled.service', 'ENABLE_PORTABLED',
'dbus-org.freedesktop.portable1.service'],
- ['systemd-poweroff.service', ''],
['systemd-quotacheck.service', 'ENABLE_QUOTACHECK'],
['systemd-random-seed.service', 'ENABLE_RANDOMSEED',
'sysinit.target.wants/'],
- ['systemd-reboot.service', ''],
['systemd-remount-fs.service', '',
'local-fs.target.wants/'],
['systemd-resolved.service', 'ENABLE_RESOLVE',
diff --git a/units/systemd-ask-password-console.path b/units/systemd-ask-password-console.path
index d686ca634c..248d597540 100644
--- a/units/systemd-ask-password-console.path
+++ b/units/systemd-ask-password-console.path
@@ -11,7 +11,7 @@
Description=Dispatch Password Requests to Console Directory Watch
Documentation=man:systemd-ask-password-console.service(8)
DefaultDependencies=no
-Conflicts=shutdown.target
+Conflicts=shutdown.target emergency.service
After=plymouth-start.service
Before=paths.target shutdown.target cryptsetup.target
ConditionPathExists=!/run/plymouth/pid
diff --git a/units/systemd-ask-password-console.service.in b/units/systemd-ask-password-console.service.in
index 6923d68df0..60fa7c3200 100644
--- a/units/systemd-ask-password-console.service.in
+++ b/units/systemd-ask-password-console.service.in
@@ -11,7 +11,7 @@
Description=Dispatch Password Requests to Console
Documentation=man:systemd-ask-password-console.service(8)
DefaultDependencies=no
-Conflicts=shutdown.target
+Conflicts=shutdown.target emergency.service
After=plymouth-start.service systemd-vconsole-setup.service
Before=shutdown.target
ConditionPathExists=!/run/plymouth/pid
diff --git a/units/systemd-ask-password-wall.path b/units/systemd-ask-password-wall.path
index 5a356b97d0..193a9b311e 100644
--- a/units/systemd-ask-password-wall.path
+++ b/units/systemd-ask-password-wall.path
@@ -11,7 +11,7 @@
Description=Forward Password Requests to Wall Directory Watch
Documentation=man:systemd-ask-password-console.service(8)
DefaultDependencies=no
-Conflicts=shutdown.target
+Conflicts=shutdown.target emergency.service
Before=paths.target shutdown.target cryptsetup.target
[Path]
diff --git a/units/systemd-bless-boot.service.in b/units/systemd-bless-boot.service.in
new file mode 100644
index 0000000000..511d991d3b
--- /dev/null
+++ b/units/systemd-bless-boot.service.in
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Mark the Current Boot Loader Entry as Good
+Documentation=man:systemd-bless-boot.service(8)
+DefaultDependencies=no
+Requires=boot-complete.target
+After=local-fs.target boot-complete.target
+Conflicts=shutdown.target
+Before=shutdown.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=@rootlibexecdir@/systemd-bless-boot good
diff --git a/units/systemd-boot-check-no-failures.service.in b/units/systemd-boot-check-no-failures.service.in
new file mode 100644
index 0000000000..27e898b85b
--- /dev/null
+++ b/units/systemd-boot-check-no-failures.service.in
@@ -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=Check if Any System Units Failed
+Documentation=man:systemd-boot-check-no-failures.service(8)
+After=default.target graphical.target multi-user.target
+Before=boot-complete.target
+Conflicts=shutdown.target
+Before=shutdown.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=@rootlibexecdir@/systemd-boot-check-no-failures
+
+[Install]
+RequiredBy=boot-complete.target
diff --git a/units/systemd-coredump@.service.in b/units/systemd-coredump@.service.in
index 215696ecd1..ffcb5f36ca 100644
--- a/units/systemd-coredump@.service.in
+++ b/units/systemd-coredump@.service.in
@@ -18,24 +18,25 @@ Before=shutdown.target
[Service]
ExecStart=-@rootlibexecdir@/systemd-coredump
+IPAddressDeny=any
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
Nice=9
+NoNewPrivileges=yes
OOMScoreAdjust=500
-RuntimeMaxSec=5min
-PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
-ProtectSystem=strict
-ProtectHome=yes
+PrivateTmp=yes
ProtectControlGroups=yes
-ProtectKernelTunables=yes
+ProtectHome=yes
ProtectKernelModules=yes
-MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+ProtectKernelTunables=yes
+ProtectSystem=strict
RestrictAddressFamilies=AF_UNIX
-SystemCallFilter=@system-service
-SystemCallErrorNumber=EPERM
-SystemCallArchitectures=native
-LockPersonality=yes
-IPAddressDeny=any
+RestrictNamespaces=yes
+RestrictRealtime=yes
+RuntimeMaxSec=5min
StateDirectory=systemd/coredump
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service
diff --git a/units/systemd-exit.service b/units/systemd-exit.service
new file mode 100644
index 0000000000..773c400b47
--- /dev/null
+++ b/units/systemd-exit.service
@@ -0,0 +1,16 @@
+# 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=Exit the Container
+Documentation=man:systemd.special(7)
+DefaultDependencies=no
+Requires=shutdown.target umount.target final.target
+After=shutdown.target umount.target final.target
+SuccessAction=exit-force
diff --git a/units/systemd-fsck-root.service.in b/units/systemd-fsck-root.service.in
index 25aca1943f..042081ccee 100644
--- a/units/systemd-fsck-root.service.in
+++ b/units/systemd-fsck-root.service.in
@@ -11,6 +11,7 @@
Description=File System Check on Root Device
Documentation=man:systemd-fsck-root.service(8)
DefaultDependencies=no
+Conflicts=shutdown.target
Before=local-fs.target shutdown.target
ConditionPathIsReadWrite=!/
diff --git a/units/systemd-fsck@.service.in b/units/systemd-fsck@.service.in
index 078edcc01a..332208352d 100644
--- a/units/systemd-fsck@.service.in
+++ b/units/systemd-fsck@.service.in
@@ -12,6 +12,7 @@ Description=File System Check on %f
Documentation=man:systemd-fsck@.service(8)
DefaultDependencies=no
BindsTo=%i.device
+Conflicts=shutdown.target
After=%i.device systemd-fsck-root.service local-fs-pre.target
Before=systemd-quotacheck.service shutdown.target
diff --git a/units/systemd-hostnamed.service.in b/units/systemd-hostnamed.service.in
index da74b4fe8b..9c925e80d9 100644
--- a/units/systemd-hostnamed.service.in
+++ b/units/systemd-hostnamed.service.in
@@ -13,25 +13,26 @@ Documentation=man:systemd-hostnamed.service(8) man:hostname(5) man:machine-info(
Documentation=https://www.freedesktop.org/wiki/Software/systemd/hostnamed
[Service]
-ExecStart=@rootlibexecdir@/systemd-hostnamed
BusName=org.freedesktop.hostname1
-WatchdogSec=3min
CapabilityBoundingSet=CAP_SYS_ADMIN
-PrivateTmp=yes
+ExecStart=@rootlibexecdir@/systemd-hostnamed
+IPAddressDeny=any
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
PrivateDevices=yes
PrivateNetwork=yes
-ProtectSystem=strict
-ProtectHome=yes
+PrivateTmp=yes
ProtectControlGroups=yes
-ProtectKernelTunables=yes
+ProtectHome=yes
ProtectKernelModules=yes
-MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+ProtectKernelTunables=yes
+ProtectSystem=strict
+ReadWritePaths=/etc
RestrictAddressFamilies=AF_UNIX
-SystemCallFilter=@system-service sethostname
-SystemCallErrorNumber=EPERM
+RestrictNamespaces=yes
+RestrictRealtime=yes
SystemCallArchitectures=native
-LockPersonality=yes
-IPAddressDeny=any
-ReadWritePaths=/etc
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service sethostname
+WatchdogSec=3min
diff --git a/units/systemd-initctl.service.in b/units/systemd-initctl.service.in
index 2b4b957dce..c276283908 100644
--- a/units/systemd-initctl.service.in
+++ b/units/systemd-initctl.service.in
@@ -13,6 +13,7 @@ Documentation=man:systemd-initctl.service(8)
DefaultDependencies=no
[Service]
-NotifyAccess=all
ExecStart=@rootlibexecdir@/systemd-initctl
+NoNewPrivileges=yes
+NotifyAccess=all
SystemCallArchitectures=native
diff --git a/units/systemd-journal-flush.service.in b/units/systemd-journal-flush.service.in
index 439f5f3f76..bacfe51d6f 100644
--- a/units/systemd-journal-flush.service.in
+++ b/units/systemd-journal-flush.service.in
@@ -12,9 +12,8 @@ Description=Flush Journal to Persistent Storage
Documentation=man:systemd-journald.service(8) man:journald.conf(5)
DefaultDependencies=no
Requires=systemd-journald.service
-After=systemd-journald.service
-After=systemd-remount-fs.service
-Before=systemd-user-sessions.service systemd-tmpfiles-setup.service
+After=systemd-journald.service systemd-remount-fs.service
+Before=systemd-tmpfiles-setup.service
RequiresMountsFor=/var/log/journal
[Service]
diff --git a/units/systemd-journal-gatewayd.service.in b/units/systemd-journal-gatewayd.service.in
index 9768928c57..ebc8bf9a25 100644
--- a/units/systemd-journal-gatewayd.service.in
+++ b/units/systemd-journal-gatewayd.service.in
@@ -13,26 +13,27 @@ Documentation=man:systemd-journal-gatewayd(8)
Requires=systemd-journal-gatewayd.socket
[Service]
-ExecStart=@rootlibexecdir@/systemd-journal-gatewayd
-User=systemd-journal-gateway
-SupplementaryGroups=systemd-journal
DynamicUser=yes
+ExecStart=@rootlibexecdir@/systemd-journal-gatewayd
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
PrivateDevices=yes
PrivateNetwork=yes
-ProtectHome=yes
ProtectControlGroups=yes
-ProtectKernelTunables=yes
+ProtectHome=yes
ProtectKernelModules=yes
-MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+ProtectKernelTunables=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
+RestrictNamespaces=yes
+RestrictRealtime=yes
+SupplementaryGroups=systemd-journal
SystemCallArchitectures=native
-LockPersonality=yes
+User=systemd-journal-gateway
-# If there are many split upjournal files we need a lot of fds to
-# access them all and combine
-LimitNOFILE=16384
+# If there are many split up journal files we need a lot of fds to access them
+# all in parallel.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
[Install]
Also=systemd-journal-gatewayd.socket
diff --git a/units/systemd-journal-remote.service.in b/units/systemd-journal-remote.service.in
index a94265f215..29a99aaec1 100644
--- a/units/systemd-journal-remote.service.in
+++ b/units/systemd-journal-remote.service.in
@@ -14,23 +14,28 @@ Requires=systemd-journal-remote.socket
[Service]
ExecStart=@rootlibexecdir@/systemd-journal-remote --listen-https=-3 --output=/var/log/journal/remote/
-User=systemd-journal-remote
-WatchdogSec=3min
-PrivateTmp=yes
+LockPersonality=yes
+LogsDirectory=journal/remote
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
PrivateDevices=yes
PrivateNetwork=yes
-ProtectSystem=strict
-ProtectHome=yes
+PrivateTmp=yes
ProtectControlGroups=yes
-ProtectKernelTunables=yes
+ProtectHome=yes
ProtectKernelModules=yes
-MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+ProtectKernelTunables=yes
+ProtectSystem=strict
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
+RestrictNamespaces=yes
+RestrictRealtime=yes
SystemCallArchitectures=native
-LockPersonality=yes
-LogsDirectory=journal/remote
+User=systemd-journal-remote
+WatchdogSec=3min
+
+# If there are many split up journal files we need a lot of fds to access them
+# all in parallel.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
[Install]
Also=systemd-journal-remote.socket
diff --git a/units/systemd-journal-upload.service.in b/units/systemd-journal-upload.service.in
index 42da70f473..92cd4e5259 100644
--- a/units/systemd-journal-upload.service.in
+++ b/units/systemd-journal-upload.service.in
@@ -14,27 +14,28 @@ Wants=network-online.target
After=network-online.target
[Service]
-ExecStart=@rootlibexecdir@/systemd-journal-upload --save-state
-User=systemd-journal-upload
DynamicUser=yes
-SupplementaryGroups=systemd-journal
-WatchdogSec=3min
+ExecStart=@rootlibexecdir@/systemd-journal-upload --save-state
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
PrivateDevices=yes
-ProtectHome=yes
ProtectControlGroups=yes
-ProtectKernelTunables=yes
+ProtectHome=yes
ProtectKernelModules=yes
-MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+ProtectKernelTunables=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
-SystemCallArchitectures=native
-LockPersonality=yes
+RestrictNamespaces=yes
+RestrictRealtime=yes
StateDirectory=systemd/journal-upload
+SupplementaryGroups=systemd-journal
+SystemCallArchitectures=native
+User=systemd-journal-upload
+WatchdogSec=3min
-# If there are many split up journal files we need a lot of fds to
-# access them all and combine
-LimitNOFILE=16384
+# If there are many split up journal files we need a lot of fds to access them
+# all in parallel.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
[Install]
WantedBy=multi-user.target
diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in
index 52939e6820..4684f095c0 100644
--- a/units/systemd-journald.service.in
+++ b/units/systemd-journald.service.in
@@ -16,27 +16,26 @@ After=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-a
Before=sysinit.target
[Service]
-Type=notify
-Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket
+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
ExecStart=@rootlibexecdir@/systemd-journald
-Restart=always
-RestartSec=0
-StandardOutput=null
-WatchdogSec=3min
FileDescriptorStoreMax=4224
-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
+IPAddressDeny=any
+LockPersonality=yes
MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+NoNewPrivileges=yes
+Restart=always
+RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_NETLINK
-SystemCallFilter=@system-service
-SystemCallErrorNumber=EPERM
+RestrictNamespaces=yes
+RestrictRealtime=yes
+Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket
+StandardOutput=null
SystemCallArchitectures=native
-LockPersonality=yes
-IPAddressDeny=any
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service
+Type=notify
+WatchdogSec=3min
-# Increase the default a bit in order to allow many simultaneous
-# services being run since we keep one fd open per service. Also, when
-# flushing journal files to disk, we might need a lot of fds when many
-# journal files are combined.
-LimitNOFILE=16384
+# 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-localed.service.in b/units/systemd-localed.service.in
index a24e61a0cd..01e0703d0e 100644
--- a/units/systemd-localed.service.in
+++ b/units/systemd-localed.service.in
@@ -13,25 +13,26 @@ Documentation=man:systemd-localed.service(8) man:locale.conf(5) man:vconsole.con
Documentation=https://www.freedesktop.org/wiki/Software/systemd/localed
[Service]
-ExecStart=@rootlibexecdir@/systemd-localed
BusName=org.freedesktop.locale1
-WatchdogSec=3min
CapabilityBoundingSet=
-PrivateTmp=yes
+ExecStart=@rootlibexecdir@/systemd-localed
+IPAddressDeny=any
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
PrivateDevices=yes
PrivateNetwork=yes
-ProtectSystem=strict
-ProtectHome=yes
+PrivateTmp=yes
ProtectControlGroups=yes
-ProtectKernelTunables=yes
+ProtectHome=yes
ProtectKernelModules=yes
-MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+ProtectKernelTunables=yes
+ProtectSystem=strict
+ReadWritePaths=/etc
RestrictAddressFamilies=AF_UNIX
-SystemCallFilter=@system-service
-SystemCallErrorNumber=EPERM
+RestrictNamespaces=yes
+RestrictRealtime=yes
SystemCallArchitectures=native
-LockPersonality=yes
-IPAddressDeny=any
-ReadWritePaths=/etc
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service
+WatchdogSec=3min
diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in
index 5e090bcf23..38a7f269ac 100644
--- a/units/systemd-logind.service.in
+++ b/units/systemd-logind.service.in
@@ -20,23 +20,24 @@ Wants=dbus.socket
After=dbus.socket
[Service]
-ExecStart=@rootlibexecdir@/systemd-logind
-Restart=always
-RestartSec=0
BusName=org.freedesktop.login1
-WatchdogSec=3min
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG
+ExecStart=@rootlibexecdir@/systemd-logind
+FileDescriptorStoreMax=512
+IPAddressDeny=any
+LockPersonality=yes
MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+NoNewPrivileges=yes
+Restart=always
+RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_NETLINK
-SystemCallFilter=@system-service
-SystemCallErrorNumber=EPERM
+RestrictNamespaces=yes
+RestrictRealtime=yes
SystemCallArchitectures=native
-LockPersonality=yes
-IPAddressDeny=any
-FileDescriptorStoreMax=512
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service
+WatchdogSec=3min
-# Increase the default a bit in order to allow many simultaneous
-# logins since we keep one fd open per session.
-LimitNOFILE=16384
+# Increase the default a bit in order to allow many simultaneous logins since
+# we keep one fd open per session.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
diff --git a/units/systemd-machined.service.in b/units/systemd-machined.service.in
index 1200a90a61..9f1476814d 100644
--- a/units/systemd-machined.service.in
+++ b/units/systemd-machined.service.in
@@ -16,18 +16,19 @@ After=machine.slice
RequiresMountsFor=/var/lib/machines
[Service]
-ExecStart=@rootlibexecdir@/systemd-machined
BusName=org.freedesktop.machine1
-WatchdogSec=3min
CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD
+ExecStart=@rootlibexecdir@/systemd-machined
+IPAddressDeny=any
+LockPersonality=yes
MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
+NoNewPrivileges=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
-SystemCallFilter=@system-service @mount
-SystemCallErrorNumber=EPERM
+RestrictRealtime=yes
SystemCallArchitectures=native
-LockPersonality=yes
-IPAddressDeny=any
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service @mount
+WatchdogSec=3min
# Note that machined cannot be placed in a mount namespace, since it
# needs access to the host's mount namespace in order to implement the
diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in
index 371ab3a9cf..472ef045de 100644
--- a/units/systemd-networkd.service.in
+++ b/units/systemd-networkd.service.in
@@ -13,34 +13,35 @@ Documentation=man:systemd-networkd.service(8)
ConditionCapability=CAP_NET_ADMIN
DefaultDependencies=no
# systemd-udevd.service can be dropped once tuntap is moved to netlink
-After=systemd-udevd.service network-pre.target systemd-sysctl.service
+After=systemd-udevd.service network-pre.target systemd-sysusers.service systemd-sysctl.service
Before=network.target multi-user.target shutdown.target
Conflicts=shutdown.target
Wants=network.target
[Service]
-Type=notify
-Restart=on-failure
-RestartSec=0
-ExecStart=!!@rootlibexecdir@/systemd-networkd
-WatchdogSec=3min
-User=systemd-network
-DynamicUser=yes
-CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW
-ProtectHome=yes
+CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW
+ExecStart=!!@rootlibexecdir@/systemd-networkd
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
ProtectControlGroups=yes
+ProtectHome=yes
ProtectKernelModules=yes
-MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+ProtectSystem=strict
+Restart=on-failure
+RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET
-SystemCallFilter=@system-service
-SystemCallErrorNumber=EPERM
-SystemCallArchitectures=native
-LockPersonality=yes
+RestrictNamespaces=yes
+RestrictRealtime=yes
RuntimeDirectory=systemd/netif
RuntimeDirectoryPreserve=yes
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service
+Type=notify
+User=systemd-network
+WatchdogSec=3min
[Install]
WantedBy=multi-user.target
diff --git a/units/systemd-portabled.service.in b/units/systemd-portabled.service.in
index a868f61dba..a44cdb30a4 100644
--- a/units/systemd-portabled.service.in
+++ b/units/systemd-portabled.service.in
@@ -20,7 +20,7 @@ CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_C
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
-SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io @reboot @swap
+SystemCallFilter=@system-service @mount
SystemCallArchitectures=native
LockPersonality=yes
IPAddressDeny=any
diff --git a/units/systemd-poweroff.service.in b/units/systemd-poweroff.service
index e9fd655508..8d1d54389b 100644
--- a/units/systemd-poweroff.service.in
+++ b/units/systemd-poweroff.service
@@ -13,7 +13,4 @@ Documentation=man:systemd-halt.service(8)
DefaultDependencies=no
Requires=shutdown.target umount.target final.target
After=shutdown.target umount.target final.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --force poweroff
+SuccessAction=poweroff-force
diff --git a/units/systemd-reboot.service.in b/units/systemd-reboot.service
index 4763ccfdca..505f60aabf 100644
--- a/units/systemd-reboot.service.in
+++ b/units/systemd-reboot.service
@@ -13,7 +13,4 @@ Documentation=man:systemd-halt.service(8)
DefaultDependencies=no
Requires=shutdown.target umount.target final.target
After=shutdown.target umount.target final.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --force reboot
+SuccessAction=reboot-force
diff --git a/units/systemd-resolved.service.in b/units/systemd-resolved.service.in
index 9982ecebff..3144b70063 100644
--- a/units/systemd-resolved.service.in
+++ b/units/systemd-resolved.service.in
@@ -14,36 +14,38 @@ Documentation=https://www.freedesktop.org/wiki/Software/systemd/resolved
Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-network-configuration-managers
Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients
DefaultDependencies=no
-After=systemd-networkd.service
+After=systemd-sysusers.service systemd-networkd.service
Before=network.target nss-lookup.target shutdown.target
Conflicts=shutdown.target
Wants=nss-lookup.target
[Service]
-Type=notify
-Restart=always
-RestartSec=0
-ExecStart=!!@rootlibexecdir@/systemd-resolved
-WatchdogSec=3min
-User=systemd-resolve
-DynamicUser=yes
-CapabilityBoundingSet=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE
+CapabilityBoundingSet=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE
+ExecStart=!!@rootlibexecdir@/systemd-resolved
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
PrivateDevices=yes
-ProtectHome=yes
+PrivateTmp=yes
ProtectControlGroups=yes
-ProtectKernelTunables=yes
+ProtectHome=yes
ProtectKernelModules=yes
-MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+ProtectKernelTunables=yes
+ProtectSystem=strict
+Restart=always
+RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
-SystemCallFilter=@system-service
-SystemCallErrorNumber=EPERM
-SystemCallArchitectures=native
-LockPersonality=yes
+RestrictNamespaces=yes
+RestrictRealtime=yes
RuntimeDirectory=systemd/resolve
RuntimeDirectoryPreserve=yes
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service
+Type=notify
+User=systemd-resolve
+WatchdogSec=3min
[Install]
WantedBy=multi-user.target
diff --git a/units/systemd-rfkill.service.in b/units/systemd-rfkill.service.in
index 4b68f0b5a7..3abb958310 100644
--- a/units/systemd-rfkill.service.in
+++ b/units/systemd-rfkill.service.in
@@ -17,7 +17,8 @@ After=sys-devices-virtual-misc-rfkill.device systemd-remount-fs.service
Before=shutdown.target
[Service]
-Type=notify
ExecStart=@rootlibexecdir@/systemd-rfkill
-TimeoutSec=30s
+NoNewPrivileges=yes
StateDirectory=systemd/rfkill
+TimeoutSec=30s
+Type=notify
diff --git a/units/systemd-rfkill.socket b/units/systemd-rfkill.socket
index c8e9f71bf6..9d408903bb 100644
--- a/units/systemd-rfkill.socket
+++ b/units/systemd-rfkill.socket
@@ -12,7 +12,8 @@ Description=Load/Save RF Kill Switch Status /dev/rfkill Watch
Documentation=man:systemd-rfkill.socket(8)
DefaultDependencies=no
BindsTo=sys-devices-virtual-misc-rfkill.device
-After=sys-devices-virtual-misc-rfkill.device
+After=sys-devices-virtual-misc-rfkill.device systemd-remount-fs.service
+RequiresMountsFor=/var/lib/systemd/rfkill
Conflicts=shutdown.target
Before=shutdown.target
diff --git a/units/systemd-timedated.service.in b/units/systemd-timedated.service.in
index 906bb4326c..6d53024195 100644
--- a/units/systemd-timedated.service.in
+++ b/units/systemd-timedated.service.in
@@ -13,23 +13,24 @@ Documentation=man:systemd-timedated.service(8) man:localtime(5)
Documentation=https://www.freedesktop.org/wiki/Software/systemd/timedated
[Service]
-ExecStart=@rootlibexecdir@/systemd-timedated
BusName=org.freedesktop.timedate1
-WatchdogSec=3min
CapabilityBoundingSet=CAP_SYS_TIME
+ExecStart=@rootlibexecdir@/systemd-timedated
+IPAddressDeny=any
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
PrivateTmp=yes
-ProtectSystem=strict
-ProtectHome=yes
ProtectControlGroups=yes
-ProtectKernelTunables=yes
+ProtectHome=yes
ProtectKernelModules=yes
-MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+ProtectKernelTunables=yes
+ProtectSystem=strict
+ReadWritePaths=/etc
RestrictAddressFamilies=AF_UNIX
-SystemCallFilter=@system-service @clock
-SystemCallErrorNumber=EPERM
+RestrictNamespaces=yes
+RestrictRealtime=yes
SystemCallArchitectures=native
-LockPersonality=yes
-IPAddressDeny=any
-ReadWritePaths=/etc
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service @clock
+WatchdogSec=3min
diff --git a/units/systemd-timesyncd.service.in b/units/systemd-timesyncd.service.in
index 4a490b6e16..03ade45d08 100644
--- a/units/systemd-timesyncd.service.in
+++ b/units/systemd-timesyncd.service.in
@@ -13,36 +13,38 @@ Documentation=man:systemd-timesyncd.service(8)
ConditionCapability=CAP_SYS_TIME
ConditionVirtualization=!container
DefaultDependencies=no
-After=systemd-remount-fs.service
+After=systemd-remount-fs.service systemd-sysusers.service
Before=time-sync.target sysinit.target shutdown.target
Conflicts=shutdown.target
Wants=time-sync.target
[Service]
-Type=notify
-Restart=always
-RestartSec=0
-ExecStart=!!@rootlibexecdir@/systemd-timesyncd
-WatchdogSec=3min
-User=systemd-timesync
-DynamicUser=yes
-CapabilityBoundingSet=CAP_SYS_TIME
AmbientCapabilities=CAP_SYS_TIME
+CapabilityBoundingSet=CAP_SYS_TIME
+ExecStart=!!@rootlibexecdir@/systemd-timesyncd
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
PrivateDevices=yes
-ProtectHome=yes
+PrivateTmp=yes
ProtectControlGroups=yes
-ProtectKernelTunables=yes
+ProtectHome=yes
ProtectKernelModules=yes
-MemoryDenyWriteExecute=yes
-RestrictRealtime=yes
-RestrictNamespaces=yes
+ProtectKernelTunables=yes
+ProtectSystem=strict
+Restart=always
+RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
+RestrictNamespaces=yes
+RestrictRealtime=yes
RuntimeDirectory=systemd/timesync
-SystemCallFilter=@system-service @clock
-SystemCallErrorNumber=EPERM
-SystemCallArchitectures=native
-LockPersonality=yes
StateDirectory=systemd/timesync
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service @clock
+Type=notify
+User=systemd-timesync
+WatchdogSec=3min
[Install]
WantedBy=sysinit.target
diff --git a/units/systemd-tmpfiles-setup.service.in b/units/systemd-tmpfiles-setup.service.in
index 384be59481..b02bbcd61b 100644
--- a/units/systemd-tmpfiles-setup.service.in
+++ b/units/systemd-tmpfiles-setup.service.in
@@ -12,7 +12,7 @@ Description=Create Volatile Files and Directories
Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8)
DefaultDependencies=no
Conflicts=shutdown.target
-After=local-fs.target systemd-sysusers.service
+After=local-fs.target systemd-sysusers.service systemd-journald.service
Before=sysinit.target shutdown.target
RefuseManualStop=yes
diff --git a/units/user-.slice.d/10-defaults.conf b/units/user-.slice.d/10-defaults.conf
index 95ab11b30b..c81a00e050 100644
--- a/units/user-.slice.d/10-defaults.conf
+++ b/units/user-.slice.d/10-defaults.conf
@@ -9,7 +9,9 @@
[Unit]
Description=User Slice of UID %j
+Documentation=man:user@.service(5)
After=systemd-user-sessions.service
+StopWhenUnneeded=yes
[Slice]
TasksMax=33%
diff --git a/units/user-runtime-dir@.service.in b/units/user-runtime-dir@.service.in
index 8c02beda3b..c168b89f98 100644
--- a/units/user-runtime-dir@.service.in
+++ b/units/user-runtime-dir@.service.in
@@ -8,10 +8,15 @@
# (at your option) any later version.
[Unit]
-Description=/run/user/%i mount wrapper
+Description=User Runtime Directory /run/user/%i
+Documentation=man:user@.service(5)
+After=systemd-user-sessions.service dbus.service
StopWhenUnneeded=yes
+IgnoreOnIsolate=yes
[Service]
ExecStart=@rootlibexecdir@/systemd-user-runtime-dir start %i
ExecStop=@rootlibexecdir@/systemd-user-runtime-dir stop %i
-RemainAfterExit=true
+Type=oneshot
+RemainAfterExit=yes
+Slice=user-%i.slice
diff --git a/units/user/bluetooth.target b/units/user/bluetooth.target
index 72e74be0a1..62407d363e 120000..100644
--- a/units/user/bluetooth.target
+++ b/units/user/bluetooth.target
@@ -1 +1,13 @@
-../bluetooth.target \ No newline at end of file
+# 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=Bluetooth
+Documentation=man:systemd.special(7)
+StopWhenUnneeded=yes
diff --git a/units/user/busnames.target b/units/user/busnames.target
deleted file mode 120000
index 04f4ba1345..0000000000
--- a/units/user/busnames.target
+++ /dev/null
@@ -1 +0,0 @@
-../busnames.target \ No newline at end of file
diff --git a/units/user/meson.build b/units/user/meson.build
index b1c2e95597..36341a42f5 100644
--- a/units/user/meson.build
+++ b/units/user/meson.build
@@ -14,6 +14,7 @@ units = [
'sockets.target',
'sound.target',
'timers.target',
+ 'systemd-exit.service',
'systemd-tmpfiles-clean.timer',
]
@@ -23,7 +24,6 @@ foreach file : units
endforeach
in_units = [
- 'systemd-exit.service',
'systemd-tmpfiles-clean.service',
'systemd-tmpfiles-setup.service',
]
diff --git a/units/user/paths.target b/units/user/paths.target
index 33545d24f3..9b6ed1c13f 120000..100644
--- a/units/user/paths.target
+++ b/units/user/paths.target
@@ -1 +1,12 @@
-../paths.target \ No newline at end of file
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Paths
+Documentation=man:systemd.special(7)
diff --git a/units/user/printer.target b/units/user/printer.target
index 8b8d5511cd..e1fb0d4e34 120000..100644
--- a/units/user/printer.target
+++ b/units/user/printer.target
@@ -1 +1,13 @@
-../printer.target \ No newline at end of file
+# 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=Printer
+Documentation=man:systemd.special(7)
+StopWhenUnneeded=yes
diff --git a/units/user/shutdown.target b/units/user/shutdown.target
index a9de83782f..d48e6d6494 120000..100644
--- a/units/user/shutdown.target
+++ b/units/user/shutdown.target
@@ -1 +1,14 @@
-../shutdown.target \ No newline at end of file
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Shutdown
+Documentation=man:systemd.special(7)
+DefaultDependencies=no
+RefuseManualStart=yes
diff --git a/units/user/smartcard.target b/units/user/smartcard.target
index f7a23b6b6d..717ea2311d 120000..100644
--- a/units/user/smartcard.target
+++ b/units/user/smartcard.target
@@ -1 +1,13 @@
-../smartcard.target \ No newline at end of file
+# 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=Smart Card
+Documentation=man:systemd.special(7)
+StopWhenUnneeded=yes
diff --git a/units/user/sockets.target b/units/user/sockets.target
index a9e4b97184..9af67fdb1f 120000..100644
--- a/units/user/sockets.target
+++ b/units/user/sockets.target
@@ -1 +1,12 @@
-../sockets.target \ No newline at end of file
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Sockets
+Documentation=man:systemd.special(7)
diff --git a/units/user/sound.target b/units/user/sound.target
index 17c8e9d6e1..19afc2a637 120000..100644
--- a/units/user/sound.target
+++ b/units/user/sound.target
@@ -1 +1,13 @@
-../sound.target \ No newline at end of file
+# 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=Sound Card
+Documentation=man:systemd.special(7)
+StopWhenUnneeded=yes
diff --git a/units/systemd-exit.service.in b/units/user/systemd-exit.service
index 2fb6ebd767..1d3b61e3ab 100644
--- a/units/systemd-exit.service.in
+++ b/units/user/systemd-exit.service
@@ -13,7 +13,4 @@ Documentation=man:systemd.special(7)
DefaultDependencies=no
Requires=shutdown.target
After=shutdown.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --force exit
+SuccessAction=exit-force
diff --git a/units/user/systemd-exit.service.in b/units/user/systemd-exit.service.in
deleted file mode 100644
index d69273f6b3..0000000000
--- a/units/user/systemd-exit.service.in
+++ /dev/null
@@ -1,19 +0,0 @@
-# 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=Exit the Session
-Documentation=man:systemd.special(7)
-DefaultDependencies=no
-Requires=shutdown.target
-After=shutdown.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --user --force exit
diff --git a/units/user/timers.target b/units/user/timers.target
index f98b68a84d..b1aa8c797c 120000..100644
--- a/units/user/timers.target
+++ b/units/user/timers.target
@@ -1 +1,15 @@
-../timers.target \ No newline at end of file
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Timers
+Documentation=man:systemd.special(7)
+
+DefaultDependencies=no
+Conflicts=shutdown.target
diff --git a/units/user@.service.in b/units/user@.service.in
index b88108e1b7..ca98c0515d 100644
--- a/units/user@.service.in
+++ b/units/user@.service.in
@@ -9,9 +9,10 @@
[Unit]
Description=User Manager for UID %i
-After=systemd-user-sessions.service
-After=user-runtime-dir@%i.service
+Documentation=man:user@.service(5)
+After=systemd-user-sessions.service user-runtime-dir@%i.service dbus.service
Requires=user-runtime-dir@%i.service
+IgnoreOnIsolate=yes
[Service]
User=%i
diff --git a/units/var-lib-machines.mount b/units/var-lib-machines.mount
index 5da0c6fa32..3658199cc7 100644
--- a/units/var-lib-machines.mount
+++ b/units/var-lib-machines.mount
@@ -7,8 +7,13 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
+# This unit is required for pre-240 versions of systemd that automatically set
+# up /var/lib/machines.raw as loopback-mounted btrfs file system. Later
+# versions don't do that anymore, but let's keep minimal compatibility by
+# mounting the image still, if it exists.
+
[Unit]
-Description=Virtual Machine and Container Storage
+Description=Virtual Machine and Container Storage (Compatibility)
ConditionPathExists=/var/lib/machines.raw
[Mount]